diff --git a/.gitignore b/.gitignore index 0c69c0c4..a6549e51 100644 --- a/.gitignore +++ b/.gitignore @@ -131,9 +131,7 @@ celerybeat.pid # Environments .env .venv -env/ venv/ -ENV/ env.bak/ venv.bak/ @@ -349,5 +347,6 @@ web/package-lock.json .gitignore_backup web/static/*.png -# Local VSCode project settings -.vscode/ +# Local tmp directory +.tmp/ +tmp/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..6bf11629 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["davidanson.vscode-markdownlint"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..a9e016ce --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,143 @@ +{ + "cSpell.words": [ + "addextension", + "AIML", + "anthropics", + "badfile", + "Behrens", + "blindspots", + "Bombal", + "Cerebras", + "compinit", + "creatordate", + "custompatterns", + "danielmiessler", + "davidanson", + "Debugf", + "dedup", + "deepseek", + "direnv", + "dryrun", + "dsrp", + "editability", + "Eisler", + "elif", + "envrc", + "eugeis", + "Eugen", + "excalidraw", + "exolab", + "fabriclogo", + "fpath", + "frequencypenalty", + "fsdb", + "gantt", + "genai", + "githelper", + "gjson", + "GOARCH", + "godotenv", + "gofmt", + "goimports", + "gomod", + "gonic", + "goopenai", + "GOPATH", + "gopkg", + "GOROOT", + "Graphviz", + "grokai", + "Groq", + "hackerone", + "Haddix", + "hasura", + "hormozi", + "Hormozi's", + "HTMLURL", + "jaredmontoya", + "jessevdk", + "Jina", + "joho", + "ksylvan", + "Langdock", + "ldflags", + "libexec", + "listcontexts", + "listextensions", + "listmodels", + "listpatterns", + "listsessions", + "liststrategies", + "listvendors", + "lmstudio", + "Makefiles", + "markmap", + "matplotlib", + "mattn", + "Miessler", + "nometa", + "numpy", + "ollama", + "opencode", + "openrouter", + "otiai", + "pdflatex", + "pipx", + "PKCE", + "pkgs", + "presencepenalty", + "printcontext", + "printsession", + "pycache", + "pyperclip", + "readystream", + "restapi", + "rmextension", + "samber", + "sashabaranov", + "sdist", + "seaborn", + "semgrep", + "sess", + "Streamlit", + "stretchr", + "talkpanel", + "Telos", + "Thacker", + "tidwall", + "topp", + "ttrc", + "unalias", + "unmarshalling", + "updatepatterns", + "videoid", + "webp", + "wipecontext", + "wipesession", + "writeups", + "xclip", + "yourpatternname" + ], + "cSpell.ignorePaths": ["go.mod", ".gitignore", "CHANGELOG.md"], + "markdownlint.config": { + "MD004": false, + "MD011": false, + "MD024": false, + "MD025": false, + "M032": false, + "MD033": { + "allowed_elements": [ + "a", + "br", + "code", + "div", + "em", + "h4", + "img", + "module", + "p" + ] + }, + "MD041": false + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..13028563 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,2339 @@ +# Changelog + +## v1.4.246 (2025-07-13) + +### Direct commits + +- Add AI-powered changelog generation with high-performance Go tool and comprehensive caching +- Implement high-performance Go changelog generator with GraphQL and SQLite-based persistent caching for incremental updates +- Add one-pass git history walking algorithm with concurrent GitHub API processing and batching +- Create comprehensive CLI with cobra framework integration and tag-based caching for unreleased content detection +- Add content hashing for change detection optimization and AI summarization using Fabric CLI integration + +## v1.4.245 (2025-07-11) + +### PR [#1603](https://github.com/danielmiessler/Fabric/pull/1603) by [ksylvan](https://github.com/ksylvan): Together AI Support with OpenAI Fallback Mechanism Added + +- Add direct model fetching support for non-standard providers +- Add `DirectlyGetModels` function to handle non-standard API responses +- Implement fallback to direct model fetching when standard method fails +- Enhance error messages in OpenAI compatible models endpoint with response body details +- Add context support to DirectlyGetModels method + +### PR [#1599](https://github.com/danielmiessler/Fabric/pull/1599) by [ksylvan](https://github.com/ksylvan): Update file paths to reflect new data directory structure + +- Update file paths to reflect new data directory structure +- Move fabric logo image path to docs directory +- Update patterns directory reference to data/patterns location +- Update strategies directory reference to data/strategies location +- Fix create_coding_feature README path reference + +### Direct commits + +- Broken image link + +## v1.4.244 (2025-07-09) + +### PR [#1598](https://github.com/danielmiessler/Fabric/pull/1598) by [jaredmontoya](https://github.com/jaredmontoya): flake: fixes and enhancements + +- Updated Nix package to use self reference for better dependency management +- Renamed shell command for improved clarity +- Fixed generation path in update-mod functionality +- Corrected typo in shell configuration + +## v1.4.243 (2025-07-09) + +### PR [#1597](https://github.com/danielmiessler/Fabric/pull/1597) by [ksylvan](https://github.com/ksylvan): CLI Refactoring: Modular Command Processing and Pattern Loading Improvements + +- Refactor CLI to modularize command handling with specialized handlers for setup, configuration, listing, management, and extensions +- Extract chat processing logic into separate function and improve patterns loader with migration support +- Add tool processing for YouTube and web scraping functionality with enhanced error handling +- Implement `handled` boolean return system across all command handlers for better control flow +- Improve error handling with proper wrapping, secure temporary directory creation, and context information + +### Direct commits + +- Nix:pkgs:fabric: use self reference +- Update-mod: fix generation path +- Shell: rename command + +## v1.4.242 (2025-07-09) + +### PR [#1596](https://github.com/danielmiessler/Fabric/pull/1596) by [ksylvan](https://github.com/ksylvan): Fix patterns zipping workflow + +- Update workflow paths to reflect directory structure change +- Modify trigger path to `data/patterns/**` +- Update `git diff` command to new path +- Change zip command to include `data/patterns/` directory + +## v1.4.241 (2025-07-09) + +### PR [#1595](https://github.com/danielmiessler/Fabric/pull/1595) by [ksylvan](https://github.com/ksylvan): Restructure project to align with standard Go layout + +- Restructured project to follow standard Go layout with `cmd` directory for binaries +- Moved all Go packages into `internal` directory and renamed `restapi` to `server` +- Consolidated patterns/strategies into `data` directory and scripts into `scripts` directory +- Updated all import paths and CI/CD workflows for new structure +- Added new patterns for content tagging and cognitive bias analysis + +### PR [#1594](https://github.com/danielmiessler/Fabric/pull/1594) by [amancioandre](https://github.com/amancioandre): Adds check Dunning-Kruger Telos self-evaluation pattern + +- Added pattern for Dunning-Kruger cognitive bias evaluation + +## v1.4.240 (2025-07-07) + +### PR [#1593](https://github.com/danielmiessler/Fabric/pull/1593) by [ksylvan](https://github.com/ksylvan): Refactor: Generalize OAuth flow for improved token handling + +- Replace hardcoded "claude" with configurable `authTokenIdentifier` parameter for better flexibility +- Update `RunOAuthFlow`, `RefreshToken`, and `exchangeToken` functions to accept token identifier parameter +- Add token refresh attempt before full OAuth flow and improve existing token validation +- Create comprehensive OAuth testing suite with 434 lines coverage including mock token server +- Implement PKCE generation, token expiration logic, and performance benchmark tests + +## v1.4.239 (2025-07-07) + +### PR [#1592](https://github.com/danielmiessler/Fabric/pull/1592) by [ksylvan](https://github.com/ksylvan): Fix Streaming Error Handling in Chatter + +- Improve error handling in streaming chat functionality with dedicated error channel +- Add proper goroutine synchronization using `done` channel for completion tracking +- Rename variables (`channel` to `responseChan`, `doneChan` to `done`) for better clarity +- Implement comprehensive testing with mock vendor for error propagation validation +- Streamline response aggregation and ensure proper resource cleanup in streaming operations + +## v1.4.238 (2025-07-07) + +### PR [#1591](https://github.com/danielmiessler/Fabric/pull/1591) by [ksylvan](https://github.com/ksylvan): Improved Anthropic Plugin Configuration Logic + +- Add vendor configuration validation and OAuth auto-authentication +- Implement IsConfigured method for Anthropic client validation +- Add automatic OAuth flow when no valid token +- Add token expiration checking with 5-minute buffer +- Extract vendor token identifier constant and remove redundant configure call + +## v1.4.237 (2025-07-07) + +### PR [#1590](https://github.com/danielmiessler/Fabric/pull/1590) by [ksylvan](https://github.com/ksylvan): Do not pass non-default TopP values + +- Add conditional check for TopP parameter in OpenAI client +- Add zero-value check before setting TopP parameter +- Prevent sending TopP when value is zero +- Apply fix to both chat completions method +- Apply fix to response parameters method + +## v1.4.236 (2025-07-06) + +### PR [#1587](https://github.com/danielmiessler/Fabric/pull/1587) by [ksylvan](https://github.com/ksylvan): Enhance bug report template + +- Enhanced bug report template with detailed system info and installation method fields +- Added detailed instructions for bug reproduction steps +- Included operating system dropdown with specific architectures +- Added OS version textarea with command examples +- Created installation method dropdown with all options + +## v1.4.235 (2025-07-06) + +### PR [#1586](https://github.com/danielmiessler/Fabric/pull/1586) by [ksylvan](https://github.com/ksylvan): Fix to persist the CUSTOM_PATTERNS_DIRECTORY variable + +- Make custom patterns persist correctly + +## v1.4.234 (2025-07-06) + +### PR [#1581](https://github.com/danielmiessler/Fabric/pull/1581) by [ksylvan](https://github.com/ksylvan): Fix Custom Patterns Directory Creation Logic + +- Improve directory creation logic in `configure` method +- Add `fmt` package for logging errors +- Check directory existence before creating +- Log error without clearing directory value + +## v1.4.233 (2025-07-06) + +### PR [#1580](https://github.com/danielmiessler/Fabric/pull/1580) by [ksylvan](https://github.com/ksylvan): Alphabetical Pattern Sorting and Configuration Refactor + +- Move custom patterns directory initialization to Configure method +- Add alphabetical sorting to pattern names retrieval +- Override ListNames method for PatternsEntity class +- Improve pattern listing with proper error handling +- Ensure custom patterns loaded after environment configuration + +### PR [#1578](https://github.com/danielmiessler/Fabric/pull/1578) by [ksylvan](https://github.com/ksylvan): Document Custom Patterns Directory Support + +- Add comprehensive custom patterns setup and usage guide +- Document priority system for custom vs built-in patterns +- Include step-by-step custom pattern creation workflow +- Explain update-safe custom pattern storage +- Document seamless integration with existing fabric commands + +## v1.4.232 (2025-07-06) + +### PR [#1577](https://github.com/danielmiessler/Fabric/pull/1577) by [ksylvan](https://github.com/ksylvan): Add Custom Patterns Directory Support + +- Add custom patterns directory support with environment variable configuration +- Implement custom patterns plugin with registry integration +- Override main patterns with custom directory patterns +- Expand home directory paths in custom patterns config +- Add comprehensive test coverage for custom patterns functionality + +## v1.4.231 (2025-07-05) + +### PR [#1565](https://github.com/danielmiessler/Fabric/pull/1565) by [ksylvan](https://github.com/ksylvan): OAuth Authentication Support for Anthropic + +- Add OAuth authentication support for Anthropic Claude with PKCE flow and browser integration +- Implement automatic OAuth token refresh and persistent storage for seamless authentication +- Support both API key and OAuth authentication methods with fallback re-authentication +- Extract OAuth functionality to separate module for cleaner code organization +- Standardize all API calls to use v2 endpoint and simplify base URL configuration + +## v1.4.230 (2025-07-05) + +### PR [#1575](https://github.com/danielmiessler/Fabric/pull/1575) by [ksylvan](https://github.com/ksylvan): Advanced image generation parameters for OpenAI models + +- Add four new image generation CLI flags for enhanced control +- Implement validation for image parameter combinations +- Support size, quality, compression, and background controls +- Add comprehensive test coverage for new parameters +- Update shell completions and README with detailed examples + +## v1.4.229 (2025-07-05) + +### PR [#1574](https://github.com/danielmiessler/Fabric/pull/1574) by [ksylvan](https://github.com/ksylvan): Add Model Validation for Image Generation and Fix CLI Flag Mapping + +- Add model validation for image generation support with `supportsImageGeneration` function +- Add model field to `BuildChatOptions` method for proper CLI flag mapping +- Extract supported models list to shared constant `ImageGenerationSupportedModels` for reusability +- Implement validation in `sendResponses` to ensure model supports image generation +- Add comprehensive tests for model validation logic in `TestModelValidationLogic` + +## v1.4.228 (2025-07-05) + +### PR [#1573](https://github.com/danielmiessler/Fabric/pull/1573) by [ksylvan](https://github.com/ksylvan): Add Image File Validation and Dynamic Format Support + +- Add image file validation and format detection for image generation +- Implement dynamic output format detection from file extensions +- Add comprehensive test coverage for image file validation +- Upgrade YAML library from v2 to v3 +- Support PNG, JPEG, JPG, and WEBP image formats + +### Direct commits + +- Added tutorial as a tag + +## v1.4.227 (2025-07-04) + +### PR [#1572](https://github.com/danielmiessler/Fabric/pull/1572) by [ksylvan](https://github.com/ksylvan): Add Image Generation Support to Fabric + +- Add image generation support with OpenAI image generation model +- Add `--image-file` flag for saving generated images +- Implement image generation tool integration with OpenAI +- Add web search tool for Anthropic and OpenAI models +- Support PNG, JPG, JPEG, GIF, BMP image formats + +### Direct commits + +- Fixed ul tag applier +- Updated ul tag prompt +- Added the UL tags pattern + +## v1.4.226 (2025-07-04) + +### PR [#1569](https://github.com/danielmiessler/Fabric/pull/1569) by [ksylvan](https://github.com/ksylvan): OpenAI Plugin Now Supports Web Search Functionality + +- Add web search tool support for OpenAI models with citation formatting +- Enable web search tool for OpenAI models with location parameter support +- Extract and format citations from search responses with deduplication +- Implement comprehensive test coverage for search functionality +- Update CLI flag description and README with new web search feature details + +## v1.4.225 (2025-07-04) + +### PR [#1568](https://github.com/danielmiessler/Fabric/pull/1568) by [ksylvan](https://github.com/ksylvan): Runtime Web Search Control via Command-Line Flag + +- Add web search tool support for Anthropic models with --search flag +- Add --search-location for timezone-based results through ChatOptions struct +- Implement web search tool in Anthropic client with formatted citations +- Add comprehensive tests and remove plugin-level web search configuration +- Extract web search tool constants and optimize string building with sources header + +### Direct commits + +- Merge branch 'main' of +- Sections as heading 1, typos +- Merge branch 'danielmiessler:main' into main +- Adds pattern telos check dunning kruger + +## v1.4.224 (2025-07-01) + +### PR [#1564](https://github.com/danielmiessler/Fabric/pull/1564) by [ksylvan](https://github.com/ksylvan): Add code_review pattern and updates in Pattern_Descriptions + +- Add comprehensive code review pattern with systematic analysis framework and principal engineer reviewer role +- Add new patterns: `review_code`, `extract_alpha`, and `extract_mcp_servers` for enhanced functionality +- Refactor pattern extraction script with improved error handling and docstrings for better clarity +- Add JSONDecodeError handling in `load_existing_file` with graceful fallback to empty list +- Fix typo in `analyze_bill_short` pattern description and improve formatting in pattern management README + +## v1.4.223 (2025-07-01) + +### PR [#1563](https://github.com/danielmiessler/Fabric/pull/1563) by [ksylvan](https://github.com/ksylvan): Fix Cross-Platform Compatibility in Release Workflow + +- Update GitHub Actions to use bash shell in release job +- Adjust repository_dispatch type spacing for consistency +- Use bash shell for creating release if absent + +## v1.4.222 (2025-07-01) + +### PR [#1559](https://github.com/danielmiessler/Fabric/pull/1559) by [ksylvan](https://github.com/ksylvan): OpenAI Plugin Migrates to New Responses API + +- Migrate OpenAI plugin to use new responses API instead of chat completions +- Add chat completions API fallback for non-Responses API providers +- Implement `sendChatCompletions` and `sendStreamChatCompletions` methods +- Add `ImplementsResponses` flag to track provider API capabilities +- Extract common message conversion logic to reduce duplication + +### Direct commits + +- Updated alpha post +- Updated extract alpha +- Added extract_alpha as kind of an experiment + +## v1.4.221 (2025-06-28) + +### PR [#1556](https://github.com/danielmiessler/Fabric/pull/1556) by [ksylvan](https://github.com/ksylvan): feat: Migrate to official openai-go SDK + +- Abstract chat message structs and migrate to official openai-go SDK +- Introduce local `chat` package for message abstraction +- Replace sashabaranov/go-openai with official openai-go SDK +- Update OpenAI, Azure, and Exolab plugins for new client +- Refactor all AI providers to use internal chat types + +## v1.4.220 (2025-06-28) + +### PR [#1555](https://github.com/danielmiessler/Fabric/pull/1555) by [ksylvan](https://github.com/ksylvan): fix: Race condition in GitHub actions release flow + +- Improve release creation to gracefully handle pre-existing tags +- Check if a release exists before attempting creation +- Suppress error output from `gh release view` command +- Add an informative log when release already exists + +## v1.4.219 (2025-06-28) + +### PR [#1553](https://github.com/danielmiessler/Fabric/pull/1553) by [ksylvan](https://github.com/ksylvan): docs: add DeepWiki badge and fix minor typos in README + +- Add DeepWiki badge to README header +- Fix typo "chatbots" to "chat-bots" and "Perlexity" to "Perplexity" +- Correct "distro" to "Linux distribution" +- Add alt text to contributor images +- Update dependency versions in go.mod and remove unused soup dependency + +### PR [#1552](https://github.com/danielmiessler/Fabric/pull/1552) by [nawarajshahi](https://github.com/nawarajshahi): Fix typos in README.md + +- Fix typos on README.md + +## v1.4.218 (2025-06-27) + +### PR [#1550](https://github.com/danielmiessler/Fabric/pull/1550) by [ksylvan](https://github.com/ksylvan): Add Support for OpenAI Search and Research Model Variants + +- Add support for new OpenAI search and research model variants +- Add slices import for array operations +- Define new search preview model names and mini search preview variants +- Include deep research model support with June 2025 dated model versions +- Replace hardcoded check with slices.Contains for both prefix and exact model matching + +## v1.4.217 (2025-06-26) + +### PR [#1546](https://github.com/danielmiessler/Fabric/pull/1546) by [ksylvan](https://github.com/ksylvan): New YouTube Transcript Endpoint Added to REST API + +- Add dedicated YouTube transcript API endpoint +- Create `/youtube/transcript` POST endpoint route +- Add request/response types for YouTube API +- Support language and timestamp options +- Update frontend to use new endpoint + +### Direct commits + +- Add extract_mcp_servers pattern +New pattern to extract mentions of MCP (Model Context Protocol) servers from content. Identifies server names, features, capabilities, and usage examples. +🤖 Generated with [Claude Code]( +Co-Authored-By: Claude + +## v1.4.216 (2025-06-26) + +### PR [#1545](https://github.com/danielmiessler/Fabric/pull/1545) by [ksylvan](https://github.com/ksylvan): Update Message Handling for Attachments and Multi-Modal content + +- Allow combining user messages and attachments with patterns +- Refactor chat request builder for improved clarity and enhanced dryrun client to display multi-content user messages +- Handle multi-content messages for user role and display image URLs from user messages in output +- Fix duplicate user message issue when applying patterns and ensure multi-part content is always included in session +- Extract message and option formatting logic into reusable methods to reduce code duplication and improve maintainability + +## v1.4.215 (2025-06-25) + +### PR [#1543](https://github.com/danielmiessler/Fabric/pull/1543) by [ksylvan](https://github.com/ksylvan): fix: Revert multiline tags in generated json files + +- Reformat `pattern_descriptions.json` to improve readability +- Reformat JSON `tags` array to display on new lines +- Update `write_essay` pattern description for clarity +- Apply consistent formatting to both data files + +## v1.4.214 (2025-06-25) + +### PR [#1542](https://github.com/danielmiessler/Fabric/pull/1542) by [ksylvan](https://github.com/ksylvan): Add `write_essay_by_author` and update Pattern metadata + +- Refactor ProviderMap for dynamic URL template handling with environment variables +- Add new patterns: `analyze_terraform_plan`, `write_essay_by_author`, `summarize_board_meeting`, `create_mnemonic_phrases` +- Rename `write_essay` to `write_essay_pg` for Paul Graham style specificity +- Update pattern metadata files with tags and descriptions for new analytical patterns +- Sort pattern explanations alphabetically and clean up duplicate entries + +## v1.4.213 (2025-06-23) + +### PR [#1538](https://github.com/danielmiessler/Fabric/pull/1538) by [andrewsjg](https://github.com/andrewsjg): Bug/bedrock region handling + +- Updated hasAWSCredentials to check for AWS_DEFAULT_REGION when access keys are configured +- Fixed bedrock region handling with correct pointer reference and region value setting +- Refactored Bedrock client with improved error handling and ai.Vendor interface compliance +- Added AWS region validation logic and enhanced resource cleanup in SendStream +- Improved code documentation and added user agent constants with proper context usage + +## v1.4.212 (2025-06-23) + +### PR [#1540](https://github.com/danielmiessler/Fabric/pull/1540) by [ksylvan](https://github.com/ksylvan): Add Langdock AI and enhance generic OpenAI compatible support + +- Refactor ProviderMap for dynamic URL template handling with environment variables +- Add `os` and `strings` packages to imports for template processing +- Implement dynamic URL handling using environment variables or default values +- Reorder providers for consistent key order in ProviderMap +- Extract and parse template variables from BaseURL + +### Direct commits + +- Refactor Bedrock client with improved error handling and interface compliance +- Add AWS region validation logic and fix resource cleanup in SendStream +- Enhanced code documentation and user agent constants +- Fixed Bedrock region handling with proper pointer reference resolution +- Updated paper analyzer functionality + +## v1.4.211 (2025-06-19) + +### PR [#1533](https://github.com/danielmiessler/Fabric/pull/1533) by [ksylvan](https://github.com/ksylvan): REST API and Web UI Now Support Dynamic Pattern Variables + +- Add pattern variables support to REST API chat endpoint with Variables field in PromptRequest struct +- Add pattern variables UI in web interface with JSON textarea for variable input +- Add `ApplyPattern` route for applying patterns with variables via POST /patterns/:name/apply +- Refactor ChatService to clean up message stream and pattern output methods +- Remove unnecessary raycast scripts directory from patterns/ folder + +### Direct commits + +- Updated paper analyzer format and sanitization instructions +- Updated markdown cleaner functionality + +## v1.4.210 (2025-06-18) + +### PR [#1530](https://github.com/danielmiessler/Fabric/pull/1530) by [ksylvan](https://github.com/ksylvan): Add Citation Support to Perplexity Response + +- Add citation support to perplexity AI responses +- Add citation extraction from API responses +- Append citations section to response content +- Format citations as numbered markdown list +- Handle citations in streaming responses + +### Direct commits + +- Update README.md +- Updated readme and intro text describing Fabric's utility + +## v1.4.208 (2025-06-17) + +### PR [#1527](https://github.com/danielmiessler/Fabric/pull/1527) by [ksylvan](https://github.com/ksylvan): Add Perplexity AI Provider with Token Limits Support + +- Add Perplexity AI provider support with token limits and streaming +- Add `MaxTokens` field to `ChatOptions` struct for response control +- Integrate Perplexity client into core plugin registry initialization +- Implement stream handling in Perplexity client using sync.WaitGroup +- Update README with Perplexity AI support instructions + +### PR [#1526](https://github.com/danielmiessler/Fabric/pull/1526) by [ConnorKirk](https://github.com/ConnorKirk): Check for AWS_PROFILE or AWS_ROLE_SESSION_NAME environment variables + +- Check for AWS_PROFILE or AWS_ROLE_SESSION_NAME environment variables + +## v1.4.207 (2025-06-17) + +### PR [#1525](https://github.com/danielmiessler/Fabric/pull/1525) by [ksylvan](https://github.com/ksylvan): Refactor yt-dlp Transcript Logic and Fix Language Bug + +- Extract common yt-dlp logic to reduce code duplication in YouTube plugin +- Add processVTTFileFunc parameter for flexible VTT processing +- Implement language matching for 2-char language codes +- Refactor transcript methods to use new helper function +- Maintain existing functionality with cleaner structure + +### Direct commits + +- Updated extract insights + +## v1.4.206 (2025-06-16) + +### PR [#1523](https://github.com/danielmiessler/Fabric/pull/1523) by [ksylvan](https://github.com/ksylvan): Conditional AWS Bedrock Plugin Initialization + +- Add AWS credential detection for Bedrock client initialization +- Add hasAWSCredentials helper function to check for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY +- Look for AWS shared credentials file with support for custom AWS_SHARED_CREDENTIALS_FILE path +- Default to ~/.aws/credentials location for credential detection +- Only initialize Bedrock client if credentials exist to prevent AWS SDK credential search failures + +### Direct commits + +- Updated prompt. + +## v1.4.205 (2025-06-16) + +### PR [#1519](https://github.com/danielmiessler/Fabric/pull/1519) by [ConnorKirk](https://github.com/ConnorKirk): feat: Dynamically list AWS Bedrock models + +- Dynamically fetch and list available foundation models and inference profiles + +### PR [#1518](https://github.com/danielmiessler/Fabric/pull/1518) by [ksylvan](https://github.com/ksylvan): chore: remove duplicate/outdated patterns + +- Remove duplicate/outdated patterns + +### Direct commits + +- Updated markdown sanitizer +- Updated markdown cleaner + +## v1.4.204 (2025-06-15) + +### PR [#1517](https://github.com/danielmiessler/Fabric/pull/1517) by [ksylvan](https://github.com/ksylvan): Fix: Prevent race conditions in versioning workflow + +- Improve version update workflow to prevent race conditions +- Add concurrency control to prevent simultaneous runs +- Pull latest main branch changes before tagging +- Fetch all remote tags before calculating version + +## v1.4.203 (2025-06-14) + +### PR [#1512](https://github.com/danielmiessler/Fabric/pull/1512) by [ConnorKirk](https://github.com/ConnorKirk): feat:Add support for Amazon Bedrock + +- Add Bedrock plugin for Amazon Bedrock integration within fabric + +### PR [#1513](https://github.com/danielmiessler/Fabric/pull/1513) by [marcas756](https://github.com/marcas756): feat: create mnemonic phrase pattern + +- Create mnemonic phrase pattern for generating phrases from diceware words +- Add markdown files with user guide and system implementation details + +### PR [#1516](https://github.com/danielmiessler/Fabric/pull/1516) by [ksylvan](https://github.com/ksylvan): Fix REST API pattern creation + +- Add Save method to PatternsEntity for persisting patterns to filesystem +- Create pattern directory with proper permissions and write content to system files +- Add comprehensive tests for Save functionality with error handling + +## v1.4.202 (2025-06-12) + +### PR [#1510](https://github.com/danielmiessler/Fabric/pull/1510) by [ksylvan](https://github.com/ksylvan): Cross-Platform fix for Youtube Transcript extraction + +- Replace hardcoded `/tmp` with `os.TempDir()` for cross-platform compatibility +- Use `filepath.Join()` instead of string concatenation for proper path handling +- Remove Unix `find` command dependency completely +- Add new `findVTTFiles()` method using `filepath.Walk()` for Windows support +- Improve error handling for file operations while maintaining backward compatibility + +## v1.4.201 (2025-06-12) + +### PR [#1503](https://github.com/danielmiessler/Fabric/pull/1503) by [dependabot[bot]](https://github.com/apps/dependabot): chore(deps): bump brace-expansion from 1.1.11 to 1.1.12 in /web in the npm_and_yarn group across 1 directory + +- Updated brace-expansion dependency from 1.1.11 to 1.1.12 in /web directory +- Indirect dependency update in npm_and_yarn group + +### PR [#1508](https://github.com/danielmiessler/Fabric/pull/1508) by [ksylvan](https://github.com/ksylvan): feat: cleanup after `yt-dlp` addition + +- Updated README with yt-dlp requirement for transcripts +- Improved error messages for better clarity and actionability +- General cleanup following yt-dlp integration + +## v1.4.200 (2025-06-11) + +### PR [#1507](https://github.com/danielmiessler/Fabric/pull/1507) by [ksylvan](https://github.com/ksylvan): Refactor: No more web scraping, just use yt-dlp + +- Replace web scraping with yt-dlp for YouTube transcript extraction +- Remove unreliable YouTube API scraping methods +- Add yt-dlp integration for transcript extraction +- Implement VTT subtitle parsing functionality +- Add timestamp preservation for transcripts + +## v1.4.199 (2025-06-11) + +### PR [#1506](https://github.com/danielmiessler/Fabric/pull/1506) by [eugeis](https://github.com/eugeis): fix: fix web search tool location + +- Fix web search tool location + +## v1.4.198 (2025-06-11) + +### PR [#1504](https://github.com/danielmiessler/Fabric/pull/1504) by [marcas756](https://github.com/marcas756): fix: Add configurable HTTP timeout for Ollama client + +- Added configurable HTTP timeout for Ollama client +- Introduced new setup question to configure timeout duration for Ollama requests +- Set default timeout value to 20 minutes +- Improved request handling reliability for Ollama integration + +## v1.4.197 (2025-06-11) + +### PR [#1502](https://github.com/danielmiessler/Fabric/pull/1502) by [eugeis](https://github.com/eugeis): Feat/antropic tool + +- Search tool working +- Search tool result collection + +### PR [#1499](https://github.com/danielmiessler/Fabric/pull/1499) by [noamsiegel](https://github.com/noamsiegel): feat: Enhance the PRD Generator's identity and purpose + +- Enhanced PRD Generator identity and purpose for better clarity +- Added structured output format with Markdown, sections, and bullet points +- Defined key PRD sections: Overview, Objectives, Features, User Stories, Requirements +- Improved instructions for highlighting priorities and MVP features + +### PR [#1497](https://github.com/danielmiessler/Fabric/pull/1497) by [ksylvan](https://github.com/ksylvan): feat: add Terraform plan analyzer pattern for infrastructure changes + +- Added Terraform plan analyzer pattern for infrastructure change assessment +- Included security, cost, and compliance focus areas +- Created structured output with summaries, critical changes, and key takeaways +- Required numbered lists and specific word limits for sections + +### Direct commits + +- Dependency update: bumped brace-expansion from 1.1.11 to 1.1.12 +- Added configurable HTTP timeout for Ollama client with 20-minute default + +## v1.4.196 (2025-06-07) + +### PR [#1495](https://github.com/danielmiessler/Fabric/pull/1495) by [ksylvan](https://github.com/ksylvan): Add AIML provider configuration + +- Add AIML provider to OpenAI compatible providers configuration +- Set AIML base URL to api.aimlapi.com/v1 +- Expand supported OpenAI compatible providers list +- Enable AIML API integration support +- Added simpler paper analyzer with updated output + +## v1.4.195 (2025-05-24) + +### PR [#1487](https://github.com/danielmiessler/Fabric/pull/1487) by [ksylvan](https://github.com/ksylvan): Dependency Updates and PDF Worker Refactoring + +- Upgrade PDF.js to v4.2 and refactor worker initialization +- Add `.browserslistrc` to define target browser versions +- Upgrade `pdfjs-dist` dependency from v2.16 to v4.2.67 +- Upgrade `nanoid` dependency from v4.0.2 to v5.0.9 +- Introduce `pdf-config.ts` for centralized PDF.js worker setup + +## v1.4.194 (2025-05-24) + +### PR [#1485](https://github.com/danielmiessler/Fabric/pull/1485) by [ksylvan](https://github.com/ksylvan): Web UI: Centralize Environment Configuration and Make Fabric Base URL Configurable + +- Add centralized environment configuration for Fabric base URL +- Create environment config module for URL handling +- Add getFabricBaseUrl() function with server/client support +- Add getFabricApiUrl() helper for API endpoints +- Configure Vite to inject FABRIC_BASE_URL client-side + +## v1.4.193 (2025-05-24) + +### PR [#1484](https://github.com/danielmiessler/Fabric/pull/1484) by [ksylvan](https://github.com/ksylvan): Web UI update all packages, reorganize docs, add install scripts + +- Reorganize web documentation and add installation scripts +- Update all package dependencies to latest versions +- Add PDF-to-Markdown installation steps to README +- Move legacy documentation files to web/legacy/ +- Add convenience scripts for npm and pnpm installation + +### PR [#1481](https://github.com/danielmiessler/Fabric/pull/1481) by [skibum1869](https://github.com/skibum1869): Add board meeting summary pattern template + +- Add board meeting summary pattern template +- Update meeting summary template with word count requirement +- Add minimum word count for context section in board summary + +### Direct commits + +- Add centralized environment configuration for Fabric base URL +- Create getFabricBaseUrl() function with server/client support +- Configure Vite to inject FABRIC_BASE_URL client-side +- Support FABRIC_BASE_URL env var with fallback +- Add TypeScript definitions for window config + +## v1.4.192 (2025-05-23) + +### PR [#1480](https://github.com/danielmiessler/Fabric/pull/1480) by [ksylvan](https://github.com/ksylvan): Automatic setting of "raw mode" for some models + +- Add NeedsRawMode method to AI vendor interface for automatic raw mode detection +- Implement NeedsRawMode in all AI clients with model-specific detection logic +- Enable automatic raw mode for Ollama llama2/llama3 models and OpenAI o1/o3/o4 models +- Auto-enable raw mode when vendor requires it based on model configuration +- Import strings package for prefix matching functionality + +## v1.4.191 (2025-05-22) + +### PR [#1478](https://github.com/danielmiessler/Fabric/pull/1478) by [ksylvan](https://github.com/ksylvan): Claude 4 Integration and README Updates + +- Add support for Anthropic Claude 4 models and update SDK to v1.2.0 +- Upgrade `anthropic-sdk-go` dependency to version `v1.2.0` +- Integrate new Anthropic Claude 4 Opus and Sonnet models +- Remove deprecated Claude 2.0 and 2.1 models from list +- Adjust model type casting for `anthropic-sdk-go v1.2.0` compatibility + +## v1.4.190 (2025-05-20) + +### PR [#1475](https://github.com/danielmiessler/Fabric/pull/1475) by [ksylvan](https://github.com/ksylvan): refactor: improve raw mode handling in BuildSession + +- Improved raw mode handling in BuildSession with better system message processing +- Fixed duplicate inputs when using patterns in raw mode +- Added conditional logic to distinguish between pattern and non-pattern scenarios +- Simplified message construction with clearer variable names +- Enhanced code comments for better readability + +## v1.4.189 (2025-05-19) + +### PR [#1473](https://github.com/danielmiessler/Fabric/pull/1473) by [roumy](https://github.com/roumy): add authentification for ollama instance + +- Add authentification for ollama instance + +## v1.4.188 (2025-05-19) + +### PR [#1474](https://github.com/danielmiessler/Fabric/pull/1474) by [ksylvan](https://github.com/ksylvan): feat: update `BuildSession` to handle message appending logic + +- Improve message handling for raw mode and Anthropic client +- Fix pattern-based message handling in non-raw mode +- Add proper handling for empty message arrays +- Implement user/assistant message alternation for Anthropic +- Preserve system messages in Anthropic conversations + +### PR [#1467](https://github.com/danielmiessler/Fabric/pull/1467) by [joshuafuller](https://github.com/joshuafuller): Typos, spelling, grammar and other minor updates + +- Fix spelling in pattern management guide +- Correct Anthropic spelling in notes +- Fix typos in web README +- Fix grammar in nuclei template instructions +- Fix spelling in PR 1284 update notes + +### PR [#1468](https://github.com/danielmiessler/Fabric/pull/1468) by [NavNab](https://github.com/NavNab): Refactor content structure in create_hormozi_offer system.md for clarity and readability + +- Improved formatting of introduction and content summary sections +- Consolidated repetitive sentences and enhanced text coherence +- Adjusted bullet points and numbering for consistency +- Enhanced visual distinction of key concepts +- Ensured clear articulation of important information + +### Direct commits + +- Add authentification for ollama instance + +## v1.4.187 (2025-05-10) + +### PR [#1463](https://github.com/danielmiessler/Fabric/pull/1463) by [CodeCorrupt](https://github.com/CodeCorrupt): Add completion to the build output for Nix + +- Add completion files to the build output for Nix + +## v1.4.186 (2025-05-06) + +### PR [#1459](https://github.com/danielmiessler/Fabric/pull/1459) by [ksylvan](https://github.com/ksylvan): chore: Repository cleanup and .gitignore Update + +- Added `coverage.out` to `.gitignore` for ignoring coverage output +- Removed `Alma.md` documentation file from the repository +- Deleted `rate_ai_result.txt` stitch script from `stitches` folder +- Removed `readme.md` for `rate_ai_result` stitch documentation +- Updated `.gitignore` and removed obsolete files + +## v1.4.185 (2025-04-28) + +### PR [#1453](https://github.com/danielmiessler/Fabric/pull/1453) by [ksylvan](https://github.com/ksylvan): Fix for default model setting + +- Introduce `getSortedGroupsItems` for consistent sorting logic +- Add centralized sorting method for groups and items (alphabetical, case-insensitive) +- Replace inline sorting in `Print` with new method +- Update `GetGroupAndItemByItemNumber` to use sorted data +- Ensure original `GroupsItems` remains unmodified + +## v1.4.184 (2025-04-25) + +### PR [#1447](https://github.com/danielmiessler/Fabric/pull/1447) by [ksylvan](https://github.com/ksylvan): More shell completion scripts: Zsh, Bash, and Fish + +- Add shell completion scripts for Zsh, Bash, and Fish +- Create standardized completion scripts in completions/ directory +- Add --shell-complete-list flag for machine-readable output +- Update Print() methods to support plain output format +- Document installation steps for each shell in README + +## v1.4.183 (2025-04-23) + +### PR [#1431](https://github.com/danielmiessler/Fabric/pull/1431) by [KenMacD](https://github.com/KenMacD): Add a completion script for fish + +- Add a completion script for fish + +## v1.4.182 (2025-04-23) + +### PR [#1441](https://github.com/danielmiessler/Fabric/pull/1441) by [ksylvan](https://github.com/ksylvan): Update go toolchain and go module packages to latest versions + +- Updated Go to version 1.24.2 across Dockerfile and Nix configurations +- Refreshed Go module dependencies and updated go.mod/go.sum files +- Updated Nix flake lock file inputs and configured packages for Go 1.24 +- Centralized Go version definition with `getGoVersion` function in flake.nix +- Fixed "nix flake check" errors and removed redundant Go version definitions + +## v1.4.181 (2025-04-22) + +### PR [#1433](https://github.com/danielmiessler/Fabric/pull/1433) by [ksylvan](https://github.com/ksylvan): chore: update Anthropic SDK to v0.2.0-beta.3 and migrate to V2 API + +- Upgrade Anthropic SDK from alpha.11 to beta.3 +- Update API endpoint from v1 to v2 +- Replace anthropic.F() with direct assignment and anthropic.Opt() for optional params +- Simplify event delta handling in streaming +- Change client type from pointer to value type + +## v1.4.180 (2025-04-22) + +### PR [#1435](https://github.com/danielmiessler/Fabric/pull/1435) by [ksylvan](https://github.com/ksylvan): chore: Fix user input handling when using raw mode and `--strategy` flag + +- Unify raw mode message handling and preserve env vars in extension executor +- Refactor BuildSession raw mode to prepend system to user content +- Ensure raw mode messages always have User role +- Append systemMessage separately in non-raw mode sessions +- Store original cmd.Env before context-based exec command creation + +### Direct commits + +- Update Anthropic SDK to v0.2.0-beta.3 and migrate to V2 API +- Replace anthropic.F() with direct assignment and anthropic.Opt() for optional params +- Change client type from pointer to value type +- Update API endpoint from v1 to v2 +- Simplify event delta handling in streaming + +## v1.4.179 (2025-04-21) + +### PR [#1432](https://github.com/danielmiessler/Fabric/pull/1432) by [ksylvan](https://github.com/ksylvan): chore: fix fabric setup mess-up introduced by sorting lists (tools and models) + +- Alphabetize the order of plugin tools +- Sort AI models alphabetically for consistent listing +- Import `sort` and `strings` packages for sorting functionality +- Sort retrieved AI model names alphabetically, ignoring case +- Ensure consistent ordering of AI models in lists + +### Direct commits + +- Add a completion script for fish + +## v1.4.178 (2025-04-21) + +### PR [#1427](https://github.com/danielmiessler/Fabric/pull/1427) by [ksylvan](https://github.com/ksylvan): Refactor OpenAI-compatible AI providers and add `--listvendors` flag + +- Add `--listvendors` command to list AI vendors +- Introduce `--listvendors` flag to display all AI vendors +- Refactor OpenAI-compatible providers into a unified configuration +- Remove individual vendor packages for streamlined management +- Add sorting for consistent vendor listing output + +## v1.4.177 (2025-04-21) + +### PR [#1428](https://github.com/danielmiessler/Fabric/pull/1428) by [ksylvan](https://github.com/ksylvan): feat: Alphabetical case-insensitive sorting for groups and items + +- Added alphabetical sorting to groups and items in Print method +- Imported `sort` and `strings` packages for sorting functionality +- Implemented case-insensitive sorting for both groups and items +- Created stable copies of groups and items before sorting +- Enhanced display iteration to use sorted collections + +## v1.4.176 (2025-04-21) + +### PR [#1429](https://github.com/danielmiessler/Fabric/pull/1429) by [ksylvan](https://github.com/ksylvan): feat: enhance StrategyMeta with Prompt field and dynamic naming + +- Add `Prompt` field to `StrategyMeta` struct for storing JSON prompt data +- Implement dynamic strategy naming using filename with `strings.TrimSuffix` +- Add alphabetical sorting to groups and items in Print method with case-insensitive ordering +- Introduce `--listvendors` command to display all AI vendors with consistent output +- Refactor OpenAI-compatible providers into unified configuration, removing individual vendor packages + +## v1.4.175 (2025-04-19) + +### PR [#1418](https://github.com/danielmiessler/Fabric/pull/1418) by [dependabot[bot]](https://github.com/apps/dependabot): chore(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 in the go_modules group across 1 directory + +- Updated golang.org/x/net dependency from version 0.36.0 to 0.38.0 +- Dependency update applied to go_modules group in root directory +- Indirect dependency type update managed by dependabot +- Security and performance improvements included in newer version +- Automated dependency maintenance to keep project current + +## v1.4.174 (2025-04-19) + +### PR [#1425](https://github.com/danielmiessler/Fabric/pull/1425) by [ksylvan](https://github.com/ksylvan): feat: add Cerebras AI plugin to plugin registry + +- Add Cerebras AI plugin to plugin registry +- Introduce Cerebras AI plugin import in plugin registry +- Register Cerebras client in the NewPluginRegistry function + +## v1.4.173 (2025-04-18) + +### PR [#1420](https://github.com/danielmiessler/Fabric/pull/1420) by [sherif-fanous](https://github.com/sherif-fanous): Fix error in deleting patterns due to non empty directory + +- Fix error in deleting patterns due to non empty directory + +### PR [#1421](https://github.com/danielmiessler/Fabric/pull/1421) by [ksylvan](https://github.com/ksylvan): feat: add Atom-of-Thought (AoT) strategy and prompt definition + +- Add Atom-of-Thought (AoT) strategy and prompt definition +- Add new aot.json for Atom-of-Thought (AoT) prompting +- Define AoT strategy description and detailed prompt instructions +- Update strategies.json to include AoT in available strategies list +- Ensure AoT strategy appears alongside CoD, CoT, and LTM options + +### Direct commits + +- Chore(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 + +## v1.4.172 (2025-04-16) + +### PR [#1415](https://github.com/danielmiessler/Fabric/pull/1415) by [ksylvan](https://github.com/ksylvan): feat: add Grok AI provider support + +- Add Grok AI provider support for AI model interactions +- Integrate Grok AI client into plugin registry +- Include Grok AI API key in REST API configuration endpoints +- Update README with Grok documentation + +### PR [#1411](https://github.com/danielmiessler/Fabric/pull/1411) by [ksylvan](https://github.com/ksylvan): docs: add contributors section to README with contrib.rocks image + +- Add contributors section with visual representation to README +- Include link to project contributors page +- Add attribution to contrib.rocks tool + +## v1.4.171 (2025-04-15) + +### PR [#1407](https://github.com/danielmiessler/Fabric/pull/1407) by [sherif-fanous](https://github.com/sherif-fanous): Update Dockerfile so that Go image version matches go.mod version + +- Bump golang version to match go.mod + +### Direct commits + +- Multiple README.md updates (12 commits) + +## v1.4.170 (2025-04-13) + +### PR [#1406](https://github.com/danielmiessler/Fabric/pull/1406) by [jmd1010](https://github.com/jmd1010): Fix chat history LLM response sequence in ChatInput.svelte + +- Fix chat history LLM response sequence in ChatInput.svelte +- Finalize WEB UI V2 loose ends fixes +- Update pattern_descriptions.json +- Bump golang version to match go.mod + +## v1.4.169 (2025-04-11) + +### PR [#1403](https://github.com/danielmiessler/Fabric/pull/1403) by [jmd1010](https://github.com/jmd1010): Strategy flag enhancement - Web UI implementation + +- Integrated strategy flag enhancement from fabric CLI into web UI +- Updated strategies.json configuration +- Added new excalidraw pattern for visual documentation +- Implemented bill analyzer functionality with shorter version +- Enhanced analyze bill pattern for improved processing + +## v1.4.168 (2025-04-02) + +### PR [#1399](https://github.com/danielmiessler/Fabric/pull/1399) by [HaroldFinchIFT](https://github.com/HaroldFinchIFT): feat: add simple optional api key management for protect routes in --serve mode + +- Add simple optional API key management for protected routes in --serve mode +- Refactor API key middleware based on code review feedback +- Fix formatting issues + +## v1.4.167 (2025-03-31) + +### PR [#1397](https://github.com/danielmiessler/Fabric/pull/1397) by [HaroldFinchIFT](https://github.com/HaroldFinchIFT): feat: add it lang to the chat drop down menu lang in web gui + +- Add it lang to the chat drop down menu lang in web gui + +## v1.4.166 (2025-03-29) + +### PR [#1392](https://github.com/danielmiessler/Fabric/pull/1392) by [ksylvan](https://github.com/ksylvan): chore: enhance argument validation in `code_helper` tool + +- Streamline code_helper CLI interface and require explicit instructions +- Require exactly two arguments: directory and instructions +- Remove dedicated help flag, use flag.Usage instead +- Improve directory validation to check if it's a directory +- Inline pattern parsing, removing separate function + +### PR [#1390](https://github.com/danielmiessler/Fabric/pull/1390) by [PatrickCLee](https://github.com/PatrickCLee): docs: improve README link + +- Fix broken what-and-why link reference + +## v1.4.165 (2025-03-26) + +### PR [#1389](https://github.com/danielmiessler/Fabric/pull/1389) by [ksylvan](https://github.com/ksylvan): Create Coding Feature + +- Add `code_helper` tool (renamed from `fabric_code`) for AI-driven codebase modifications +- Implement `create_coding_feature` pattern with file management API for code changes +- Add secure file parsing and validation system with JSON escape sequence handling +- Update README with installation instructions and usage examples +- Replace deprecated io/ioutil with modern alternatives and improve error handling + +### Direct commits + +- Improve README link +- Fix broken what-and-why link reference + +## v1.4.164 (2025-03-22) + +### PR [#1380](https://github.com/danielmiessler/Fabric/pull/1380) by [jmd1010](https://github.com/jmd1010): Add flex windows sizing to web interface + raw text input fix + +- Add flex windows sizing to web interface +- Fixed processing message not stopping after pattern output completion + +### PR [#1379](https://github.com/danielmiessler/Fabric/pull/1379) by [guilhermechapiewski](https://github.com/guilhermechapiewski): Fix typo on fallacies instruction + +- Fix typo on fallacies instruction + +### PR [#1382](https://github.com/danielmiessler/Fabric/pull/1382) by [ksylvan](https://github.com/ksylvan): docs: improve README formatting and fix some broken links + +- Improve README formatting and add clipboard support section +- Fix broken installation link reference and environment variables link +- Replace code tags with backticks and improve code block formatting + +### PR [#1376](https://github.com/danielmiessler/Fabric/pull/1376) by [vaygr](https://github.com/vaygr): Add installation instructions for OS package managers + +- Add installation instructions for OS package managers + +### Direct commits + +- Added find_female_life_partner pattern +- Updated find prompt multiple times + +## v1.4.163 (2025-03-19) + +### PR [#1362](https://github.com/danielmiessler/Fabric/pull/1362) by [dependabot[bot]](https://github.com/apps/dependabot): Bump golang.org/x/net from 0.35.0 to 0.36.0 in the go_modules group across 1 directory + +- Updated golang.org/x/net dependency from version 0.35.0 to 0.36.0 + +### PR [#1372](https://github.com/danielmiessler/Fabric/pull/1372) by [rube-de](https://github.com/rube-de): fix: set percentEncoded to false + +- Fixed YouTube link encoding issue by setting percentEncoded to false +- Prevents URL encoding errors when using YouTube links like youtu.be/sHIlFKKaq0A + +### PR [#1373](https://github.com/danielmiessler/Fabric/pull/1373) by [ksylvan](https://github.com/ksylvan): Remove unnecessary `system.md` file at top level + +- Removed redundant system.md file from top-level directory +- File was an RPG session summarization prompt superseded by create_rpg_summary and summarize_rpg_session patterns + +## v1.4.162 (2025-03-19) + +### PR [#1374](https://github.com/danielmiessler/Fabric/pull/1374) by [ksylvan](https://github.com/ksylvan): Fix Default Model Change Functionality + +- Improve error handling in ChangeDefaultModel flow and save environment file +- Add early return on setup error +- Save environment file after successful setup +- Maintain proper error propagation + +### Direct commits + +- Remove redundant file system.md at top level +- Set percentEncoded to false for YouTube links to prevent encoding errors +- Removed RPG session summarization prompt (system.md) - replaced by create_rpg_summary and summarize_rpg_session patterns +- Fix YouTube link processing to avoid URL encoding issues that caused fabric errors + +## v1.4.161 (2025-03-17) + +### PR [#1363](https://github.com/danielmiessler/Fabric/pull/1363) by [garkpit](https://github.com/garkpit): clipboard operations now work on Mac and PC + +- Clipboard operations now work on Mac and PC + +## v1.4.160 (2025-03-17) + +### PR [#1368](https://github.com/danielmiessler/Fabric/pull/1368) by [vaygr](https://github.com/vaygr): Standardize sections for no repeat guidelines + +- Standardize sections for no repeat guidelines + +### Direct commits + +- Moved system file to proper directory +- Added activity extractor +- Merge branch 'main' of github.com:danielmiessler/fabric + +## v1.4.159 (2025-03-16) + +### Direct commits + +- Added flashcard generator. + +## v1.4.158 (2025-03-16) + +### PR [#1367](https://github.com/danielmiessler/Fabric/pull/1367) by [ksylvan](https://github.com/ksylvan): Remove Generic Type Parameters from StorageHandler Initialization + +- Remove generic type parameters from NewStorageHandler calls +- Remove explicit type parameters from StorageHandler initialization +- Update contexts handler constructor implementation +- Update patterns handler constructor implementation +- Update sessions handler constructor implementation + +## v1.4.157 (2025-03-16) + +### PR [#1365](https://github.com/danielmiessler/Fabric/pull/1365) by [ksylvan](https://github.com/ksylvan): Implement Prompt Strategies in Fabric + +- Add prompt strategies like Chain of Thought (CoT) with `--strategy` flag +- Implement `--liststrategies` command to view available strategies +- Support applying strategies to system prompts +- Improve README with platform-specific installation instructions +- Refactor git operations with new githelper package + +### Direct commits + +- Clipboard operations now work on Mac and PC +- Bump golang.org/x/net from 0.35.0 to 0.36.0 in go_modules group + +## v1.4.156 (2025-03-11) + +### PR [#1356](https://github.com/danielmiessler/Fabric/pull/1356) by [ksylvan](https://github.com/ksylvan): chore: add .vscode to `.gitignore` and fix typos and markdown linting in `Alma.md` + +- Add .vscode to `.gitignore` and fix typos and markdown linting in `Alma.md` + +### PR [#1352](https://github.com/danielmiessler/Fabric/pull/1352) by [matmilbury](https://github.com/matmilbury): pattern_explanations.md: fix typo + +- Pattern_explanations.md: fix typo + +### PR [#1354](https://github.com/danielmiessler/Fabric/pull/1354) by [jmd1010](https://github.com/jmd1010): Fix Chat history window scrolling behavior + +- Fix Chat history window sizing +- Update Web V2 Install Guide with improved instructions + +## v1.4.155 (2025-03-09) + +### PR [#1350](https://github.com/danielmiessler/Fabric/pull/1350) by [jmd1010](https://github.com/jmd1010): Implement Pattern Tile search functionality + +- Implement Pattern Tile search functionality +- Implement column resize functionnality + +## v1.4.154 (2025-03-09) + +### PR [#1349](https://github.com/danielmiessler/Fabric/pull/1349) by [ksylvan](https://github.com/ksylvan): Fix: v1.4.153 does not compile because of extra version declaration + +- Remove unnecessary `version` variable from `main.go` +- Update Azure client API version access path in tests +- Implement column resize functionality +- Implement Pattern Tile search functionality + +## v1.4.153 (2025-03-08) + +### PR [#1348](https://github.com/danielmiessler/Fabric/pull/1348) by [liyuankui](https://github.com/liyuankui): feat: Add LiteLLM AI plugin support with local endpoint configuration + +- Add LiteLLM AI plugin support with local endpoint configuration + +## v1.4.152 (2025-03-07) + +### Direct commits + +- Fix pipe handling + +## v1.4.151 (2025-03-07) + +### PR [#1339](https://github.com/danielmiessler/Fabric/pull/1339) by [Eckii24](https://github.com/Eckii24): Feature/add azure api version + +- Update azure.go +- Update azure_test.go +- Update openai.go + +## v1.4.150 (2025-03-07) + +### PR [#1343](https://github.com/danielmiessler/Fabric/pull/1343) by [jmd1010](https://github.com/jmd1010): Rename input.svelte to Input.svelte for proper component naming convention + +- Rename input.svelte to Input.svelte for proper component naming convention + +## v1.4.149 (2025-03-05) + +### PR [#1340](https://github.com/danielmiessler/Fabric/pull/1340) by [ksylvan](https://github.com/ksylvan): Fix for youtube live links plus new youtube_summary pattern + +- Update YouTube regex to support live URLs and timestamped transcripts +- Add argument validation and -t flag for transcript with timestamps +- Refactor PowerShell yt function with parameter switch +- Introduce youtube_summary pattern with documentation +- Update README to dynamically select transcript option + +### PR [#1338](https://github.com/danielmiessler/Fabric/pull/1338) by [jmd1010](https://github.com/jmd1010): Update Web V2 Install Guide layout + +- Update Web V2 Install Guide layout improvements + +### PR [#1330](https://github.com/danielmiessler/Fabric/pull/1330) by [jmd1010](https://github.com/jmd1010): Fixed ALL CAP DIR as requested and processed minor updates to documentation + +- Reorganize documentation with consistent directory naming + +### PR [#1333](https://github.com/danielmiessler/Fabric/pull/1333) by [asasidh](https://github.com/asasidh): Update QUOTES section to include speaker names for clarity + +- Update QUOTES section to include speaker names for clarity + +### Direct commits + +- Update azure.go, openai.go, and azure_test.go files + +## v1.4.148 (2025-03-03) + +## Summary of Changes + +### Direct commits + +- Rework LM Studio plugin +- Update QUOTES section to include speaker names for clarity +- Update Web V2 Install Guide with improved instructions V2 +- Update Web V2 Install Guide with improved instructions +- Reorganize documentation with consistent directory naming and updated guides + +## v1.4.147 (2025-02-28) + +### PR [#1326](https://github.com/danielmiessler/Fabric/pull/1326) by [pavdmyt](https://github.com/pavdmyt): fix: continue fetching models even if some vendors fail + +- Continue fetching models even if some vendors fail +- Remove cancellation of remaining goroutines when vendor collection fails +- Ensure other vendor collections continue even if one fails +- Fix model listing via `fabric -L` when localhost models are down +- Fix using non-default models via `fabric -m custom_model` + +### PR [#1329](https://github.com/danielmiessler/Fabric/pull/1329) by [jmd1010](https://github.com/jmd1010): Svelte Web V2 Installation Guide + +- Add Web V2 Installation Guide +- Update install guide with Plain Text instructions + +## v1.4.146 (2025-02-27) + +### PR [#1319](https://github.com/danielmiessler/Fabric/pull/1319) by [jmd1010](https://github.com/jmd1010): Enhancement: PDF to Markdown Conversion Functionality to the Web Svelte Chat Interface + +- Add PDF to Markdown conversion functionality to the web svelte chat interface +- Add PDF to Markdown integration documentation +- Add Svelte implementation files for PDF integration +- Update README files directory structure and naming convention +- Add required UI image assets for feature implementation + +## v1.4.145 (2025-02-26) + +### PR [#1324](https://github.com/danielmiessler/Fabric/pull/1324) by [jaredmontoya](https://github.com/jaredmontoya): flake: fix/update and enhance + +- Flake: fix/update + +## v1.4.144 (2025-02-26) + +### Direct commits + +- Upgrade upload artifacts to v4 + +## v1.4.143 (2025-02-26) + +### PR [#1264](https://github.com/danielmiessler/Fabric/pull/1264) by [eugeis](https://github.com/eugeis): feat: implement support for exolab + +- Implement support for +- Merge branch 'main' into feat/exolab + +## v1.4.142 (2025-02-25) + +### Direct commits + +- Build problems + +## v1.4.141 (2025-02-25) + +### PR [#1260](https://github.com/danielmiessler/Fabric/pull/1260) by [bluPhy](https://github.com/bluPhy): Fixing typo + +- Fixed typos in codebase +- Updated version to v1.4.80 +- Reverted previous v1.4.79 version update +- Merged changes from main branch +- Applied version control corrections + +## v1.4.140 (2025-02-25) + +### PR [#1313](https://github.com/danielmiessler/Fabric/pull/1313) by [cx-ken-swain](https://github.com/cx-ken-swain): Updated ollama.go to fix a couple of potential DoS issues + +- Fixed security vulnerabilities in ollama.go to prevent potential DoS attacks +- Resolved multiple medium-severity vulnerabilities +- Updated application version to v..1 +- Removed version.nix and version.go files +- Merged changes from main branch + +## v1.4.139 (2025-02-25) + +### PR [#1321](https://github.com/danielmiessler/Fabric/pull/1321) by [jmd1010](https://github.com/jmd1010): Update demo video link in PR-1309 documentation + +- Updated demo video link in PR-1284 documentation +- Added complete PDF to Markdown conversion functionality +- Implemented Svelte integration files for PDF processing +- Added comprehensive PDF to Markdown documentation +- Enhanced web Svelte chat interface with PDF conversion capabilities + +## v1.4.138 (2025-02-24) + +### PR [#1317](https://github.com/danielmiessler/Fabric/pull/1317) by [ksylvan](https://github.com/ksylvan): chore: update Anthropic SDK and add Claude 3.7 Sonnet model support + +- Updated anthropic-sdk-go from v0.2.0-alpha.4 to v0.2.0-alpha.11 +- Added Claude 3.7 Sonnet models to available model list +- Added ModelClaude3_7SonnetLatest to model options +- Added ModelClaude3_7Sonnet20250219 to model options +- Removed ModelClaude_Instant_1_2 from available models + +## v1.4.80 (2025-02-24) + +### Direct commits + +- Impl. multi-model / attachments, images + +## v1.4.79 (2025-02-24) + +### PR [#1257](https://github.com/danielmiessler/Fabric/pull/1257) by [jessefmoore](https://github.com/jessefmoore): Create analyze_threat_report_cmds + +- Create system.md +Create pattern to extract commands from videos and threat reports to obtain commands so pentesters or red teams or Threat hunters can use to either threat hunt or simulate the threat actor. + +### PR [#1256](https://github.com/danielmiessler/Fabric/pull/1256) by [JOduMonT](https://github.com/JOduMonT): Update README.md + +- Update README.md + +1. Windows Command: Because actually curl does not exist natively on Windows +2. Syntax: Because like this; it makes the "click, cut and paste" easier + +### PR [#1247](https://github.com/danielmiessler/Fabric/pull/1247) by [kevnk](https://github.com/kevnk): Update suggest_pattern: refine summaries and add recently added patterns + +- Update summaries and add recently added patterns + +### PR [#1252](https://github.com/danielmiessler/Fabric/pull/1252) by [jeffmcjunkin](https://github.com/jeffmcjunkin): Update README.md: Add PowerShell aliases + +- Update README.md: Add PowerShell aliases + +### PR [#1253](https://github.com/danielmiessler/Fabric/pull/1253) by [abassel](https://github.com/abassel): Fixed few typos that I could find + +- Fixed few typos that I could find + +**Key Changes:** + +- Added threat analysis pattern for extracting commands from security reports +- Improved Windows compatibility with PowerShell aliases and curl alternatives +- Enhanced pattern suggestions with updated summaries +- Added markdown callout pattern and prediction generator +- Implemented multi-model support with image attachments + +## v1.4.137 (2025-02-24) + +### PR [#1296](https://github.com/danielmiessler/Fabric/pull/1296) by [dependabot[bot]](https://github.com/apps/dependabot): Bump github.com/go-git/go-git/v5 from 5.12.0 to 5.13.0 in the go_modules group across 1 directory + +- Updated github.com/go-git/go-git/v5 dependency from version 5.12.0 to 5.13.0 +- Automated dependency update in go_modules group +- Direct production dependency upgrade +- Includes release notes and commit history links for transparency +- Signed-off by dependabot bot for automated maintenance + +## v1.4.136 (2025-02-24) + +### Direct commits + +- Update to upload-artifact@v4 because upload-artifact@v3 is deprecated +- Merge branch 'danielmiessler:main' into main +- Update Anthropic SDK and add Claude 3.7 Sonnet model support +- Updated anthropic-sdk-go from v0.2.0-alpha.4 to v0.2.0-alpha.11 +- Added Claude 3.7 Sonnet models to available model list + +## v1.4.135 (2025-02-24) + +### PR [#1309](https://github.com/danielmiessler/Fabric/pull/1309) by [jmd1010](https://github.com/jmd1010): Feature/Web Svelte GUI Enhancements: Pattern Descriptions, Tags, Favorites, Search Bar, Language Integration, PDF file conversion, etc + +- Enhanced Web UI with pattern descriptions, tags, favorites, and search functionality +- Improved chat interface and pattern handling capabilities +- Updated dependencies and backup configuration +- Cleaned up sensitive files and improved .gitignore structure + +### PR [#1312](https://github.com/danielmiessler/Fabric/pull/1312) by [junaid18183](https://github.com/junaid18183): Added Create LOE Document Prompt + +- Added create_loe_document prompt for Level of Effort documentation + +### PR [#1302](https://github.com/danielmiessler/Fabric/pull/1302) by [verebes1](https://github.com/verebes1): feat: Add LM Studio compatibility + +- Added LM Studio as new plugin with base URL configuration +- Updated plugin registry to include LM Studio integration + +### PR [#1297](https://github.com/danielmiessler/Fabric/pull/1297) by [Perchycs](https://github.com/Perchycs): Create pattern_explanations.md + +- Created comprehensive pattern explanations with one-line summaries + +### Direct commits + +- Fixed security vulnerabilities in ollama.go and updated version to v1.1 +- Added and updated extract_domains functionality + +## v1.4.134 (2025-02-11) + +### PR [#1289](https://github.com/danielmiessler/Fabric/pull/1289) by [thevops](https://github.com/thevops): Add the ability to grab YouTube video transcript with timestamps + +- Added new `--transcript-with-timestamps` flag for YouTube video processing +- Timestamps formatted as HH:MM:SS and prepended to each transcript line +- Enables quick navigation to specific video segments in summaries +- Similar functionality to existing `--transcript` flag but with time markers +- Useful for creating timestamped video summaries and references + +## v1.4.133 (2025-02-11) + +### PR [#1294](https://github.com/danielmiessler/Fabric/pull/1294) by [TvisharajiK](https://github.com/TvisharajiK): Improved unit-test coverage from 0 to 100 (AI module) using Keploy's agent + +- Increased unit test coverage from 0 to 100% in the AI module using Keploy's Agent +- Added YouTube video transcript with timestamps feature via `--transcript-with-timestamps` flag +- Bumped github.com/go-git/go-git/v5 from 5.12.0 to 5.13.0 +- Added multiple new TELOS patterns and challenge handling pattern +- Added panel topic extractor and intro sentences pattern + +## v1.4.132 (2025-02-02) + +### PR [#1278](https://github.com/danielmiessler/Fabric/pull/1278) by [aicharles](https://github.com/aicharles): feat(anthropic): enable custom API base URL support + +- Enable custom API base URL configuration for Anthropic integration +- Add proper handling of v1 endpoint for UUID-containing URLs +- Implement URL formatting logic for consistent endpoint structure +- Clean up commented code and improve configuration flow +- Enhance API flexibility for different deployment environments + +## v1.4.131 (2025-01-30) + +### PR [#1270](https://github.com/danielmiessler/Fabric/pull/1270) by [wmahfoudh](https://github.com/wmahfoudh): Added output filename support for to_pdf + +- Added output filename support for to_pdf + +### PR [#1271](https://github.com/danielmiessler/Fabric/pull/1271) by [wmahfoudh](https://github.com/wmahfoudh): Adding deepseek support + +- Added Deepseek AI integration + +### PR [#1258](https://github.com/danielmiessler/Fabric/pull/1258) by [tuergeist](https://github.com/tuergeist): Minor README fix and additional Example + +- Doc: Custom patterns also work with Claude models +- Doc: Add scrape URL example. Fix Example 4 + +### Direct commits + +- Implement support for + +## v1.4.130 (2025-01-03) + +### PR [#1240](https://github.com/danielmiessler/Fabric/pull/1240) by [johnconnor-sec](https://github.com/johnconnor-sec): Updates: ./web + +- Moved pattern loader to ModelConfig and added page fly transitions +- Updated UI components and improved responsive layout for chat interface +- Added NotesDrawer component that saves notes to lib/content/inbox +- Centered chat and NotesDrawer in viewport for better user experience +- Restructured project organization: moved types to lib/interfaces and lib/api + +## v1.4.129 (2025-01-03) + +### PR [#1242](https://github.com/danielmiessler/Fabric/pull/1242) by [CuriouslyCory](https://github.com/CuriouslyCory): Adding youtube --metadata flag + +- Added metadata lookup to youtube helper +- Better metadata + +### PR [#1230](https://github.com/danielmiessler/Fabric/pull/1230) by [iqbalabd](https://github.com/iqbalabd): Update translate pattern to use curly braces + +- Update translate pattern to use curly braces + +### Direct commits + +- Chat and NotesDrawer now centered in viewport +- Enhanced enrich pattern and added enrich_blog_post +- Major file reorganization: moved types to lib/interfaces, components restructured +- Updated Post page styling and NotesDrawer saves to lib/content/inbox +- Version updates to v..1 with corresponding .nix and .go files + +## v1.4.128 (2024-12-26) + +### PR [#1227](https://github.com/danielmiessler/Fabric/pull/1227) by [mattjoyce](https://github.com/mattjoyce): Feature/template extensions + +- Implemented stdout template extensions with path-based registry storage and hash verification +- Added file-based output handling with proper cleanup of temporary files for local and remote operations +- Fixed pattern file usage without stdin by initializing empty message for template processing +- Added extension manager tests, registration, execution validation, and example files with tutorial +- Enhanced extension listing with better error messages when hash verification fails + +### Direct commits + +- Updated story format to shorter bullets and improved notes drawer with rocket theme +- Updated POSTS for main 24-12-08 release and fixed import statements + +## v1.4.127 (2024-12-23) + +### PR [#1218](https://github.com/danielmiessler/Fabric/pull/1218) by [sosacrazy126](https://github.com/sosacrazy126): streamlit ui + +- Added comprehensive Streamlit application for managing and executing patterns +- Implemented pattern creation, execution, and analysis with advanced UI components +- Enhanced logging configuration with color-coded console and detailed file handlers +- Added pattern chain execution functionality for sequential pattern processing +- Integrated output management with starring/favoriting and persistent storage features + +### PR [#1225](https://github.com/danielmiessler/Fabric/pull/1225) by [wmahfoudh](https://github.com/wmahfoudh): Added Humanize Pattern + +- Added Humanize Pattern + +## v1.4.126 (2024-12-22) + +### PR [#1212](https://github.com/danielmiessler/Fabric/pull/1212) by [wrochow](https://github.com/wrochow): Significant updates to Duke and Socrates + +- Significant thematic rewrite incorporating classical philosophical texts +- Ingested 8 key documents including Plato's Apology, Phaedrus, Symposium, and Republic +- Added Xenophon's works: The Economist, Memorabilia, Memorable Thoughts, and Symposium +- Enhanced with specific steps for research, analysis, and code reviews +- Version updates and branch merging activities + +## v1.4.125 (2024-12-22) + +### PR [#1222](https://github.com/danielmiessler/Fabric/pull/1222) by [wmahfoudh](https://github.com/wmahfoudh): Fix cross-filesystem file move in to_pdf plugin (issue 1221) + +- Fix cross-filesystem file move in to_pdf plugin (issue 1221) + +### Direct commits + +- Update version to v..1 and commit +- Don't quite know how I screwed this up, I wasn't even working there. +- Update version to v..1 and commit +- Merge branch 'main' into main +- Merge branch 'main' into main + +## v1.4.124 (2024-12-21) + +### PR [#1215](https://github.com/danielmiessler/Fabric/pull/1215) by [infosecwatchman](https://github.com/infosecwatchman): Add Endpoints to facilitate Ollama based chats + +- Add endpoints to facilitate Ollama based chats +- Built to use with Open WebUI + +### PR [#1214](https://github.com/danielmiessler/Fabric/pull/1214) by [iliaross](https://github.com/iliaross): Fix the typo in the sentence + +- Fix typo in sentence + +### PR [#1213](https://github.com/danielmiessler/Fabric/pull/1213) by [AnirudhG07](https://github.com/AnirudhG07): Spelling Fixes + +- Spelling fixes in patterns and README +- Spelling fixes in create_quiz pattern + +### Direct commits + +- Enhanced pattern management with improved creation, editing, and deletion +- Improved logging configuration and error handling for better debugging + +## v1.4.123 (2024-12-20) + +### PR [#1208](https://github.com/danielmiessler/Fabric/pull/1208) by [mattjoyce](https://github.com/mattjoyce): Fix: Issue with the custom message and added example config file + +- Fixed custom message issue and added example config file + +### Direct commits + +- Added Streamlit application for pattern management and execution with logging, session state, and UI components +- Added Ollama chat endpoints for Open WebUI integration +- Fixed multiple spelling errors across patterns and documentation +- Updated version to v1.1 with significant research and analysis workflow improvements +- Major Socrates pattern rewrite incorporating Plato and Xenophon source materials from Project Gutenberg + +## v1.4.122 (2024-12-14) + +### PR [#1201](https://github.com/danielmiessler/Fabric/pull/1201) by [mattjoyce](https://github.com/mattjoyce): feat: Add YAML configuration support + +- Add YAML configuration support for persistent settings +- Add --config flag for specifying YAML config file path +- Support standard option precedence (CLI > YAML > defaults) +- Add type-safe YAML parsing with reflection +- Add tests for YAML config functionality + +## v1.4.121 (2024-12-13) + +### PR [#1200](https://github.com/danielmiessler/Fabric/pull/1200) by [mattjoyce](https://github.com/mattjoyce): Fix: Mask input token to prevent var substitution in patterns + +- Mask input token to prevent var substitution in patterns + +### Direct commits + +- Added new instruction trick. + +## v1.4.120 (2024-12-10) + +### PR [#1189](https://github.com/danielmiessler/Fabric/pull/1189) by [mattjoyce](https://github.com/mattjoyce): Add --input-has-vars flag to control variable substitution in input + +- Add --input-has-vars flag to control variable substitution in input +- Add InputHasVars field to ChatRequest struct +- Only process template variables in user input when flag is set +- Fixes issue with Ansible/Jekyll templates that use {{var}} syntax +- Makes template variable substitution opt-in, preserving literal curly braces by default + +### PR [#1182](https://github.com/danielmiessler/Fabric/pull/1182) by [jessefmoore](https://github.com/jessefmoore): analyze_risk pattern + +- Created analyze_risk pattern for 3rd party vendor risk analysis + +## v1.4.119 (2024-12-07) + +### PR [#1181](https://github.com/danielmiessler/Fabric/pull/1181) by [mattjoyce](https://github.com/mattjoyce): Bugfix/1169 symlinks + +- Fix #1169: Add robust handling for paths and symlinks in GetAbsolutePath +- Update version to v..1 and commit +- Revert "Update version to v..1 and commit" + +### Direct commits + +- Added tutorial and example files +- Add cards component and update packages, main page, styles +- Check extension names don't have spaces +- Added test pattern + +## v1.4.118 (2024-12-05) + +### PR [#1174](https://github.com/danielmiessler/Fabric/pull/1174) by [mattjoyce](https://github.com/mattjoyce): Curly brace templates + +- Fixed pattern file usage without stdin to prevent segfault by initializing empty message +- Removed redundant template processing of message content +- Simplified template processing flow for both stdin and non-stdin use cases +- Added proper handling for patterns with variables but no stdin input + +### PR [#1179](https://github.com/danielmiessler/Fabric/pull/1179) by [sluosapher](https://github.com/sluosapher): added a new pattern create_newsletter_entry + +- Added new pattern create_newsletter_entry + +## v1.4.117 (2024-11-30) + +### Direct commits + +- Close #1173 + +## v1.4.116 (2024-11-28) + +### Direct commits + +- Cleanup style + +## v1.4.115 (2024-11-28) + +### PR [#1168](https://github.com/danielmiessler/Fabric/pull/1168) by [johnconnor-sec](https://github.com/johnconnor-sec): Update README.md + +- Updated README.md documentation +- Cleaned up code styling +- Enhanced message handling to use custom messages with piped input +- Improved overall project documentation and formatting +- Streamlined user experience with better message processing + +## v1.4.114 (2024-11-26) + +### PR [#1164](https://github.com/danielmiessler/Fabric/pull/1164) by [MegaGrindStone](https://github.com/MegaGrindStone): fix: provide default message content to avoid nil pointer dereference + +- Provide default message content to avoid nil pointer dereference + +## v1.4.113 (2024-11-26) + +### PR [#1166](https://github.com/danielmiessler/Fabric/pull/1166) by [dependabot[bot]](https://github.com/apps/dependabot): build(deps-dev): bump @sveltejs/kit from 2.6.1 to 2.8.4 in /web in the npm_and_yarn group across 1 directory + +- Updated @sveltejs/kit from version 2.6.1 to 2.8.4 in the /web directory +- Dependency update in the npm_and_yarn group for development dependencies +- Automated security and maintenance update by dependabot +- Direct development dependency type update +- Includes release notes and changelog references for the SvelteKit framework + +## v1.4.112 (2024-11-26) + +### PR [#1165](https://github.com/danielmiessler/Fabric/pull/1165) by [johnconnor-sec](https://github.com/johnconnor-sec): feat: Fabric Web UI + +- Update version to v..1 and commit +- Update Obsidian.md documentation +- Update README.md file +- Multiple commits by John on 2024-11-26 + +### Direct commits + +- Provide default message content to avoid nil pointer dereference + +## v1.4.111 (2024-11-26) + +### Direct commits + +- Integrate code formating + +## v1.4.110 (2024-11-26) + +### PR [#1135](https://github.com/danielmiessler/Fabric/pull/1135) by [mrtnrdl](https://github.com/mrtnrdl): Add `extract_recipe` + +- Update version to v..1 and commit +- Add extract_recipe to easily extract the necessary information from cooking-videos +- Merge branch 'main' into main + +## v1.4.109 (2024-11-24) + +### PR [#1157](https://github.com/danielmiessler/Fabric/pull/1157) by [mattjoyce](https://github.com/mattjoyce): fix: process template variables in raw input + +- Process template variables ({{var}}) consistently in both pattern files and raw input messages +- Add template variable processing for raw input in BuildSession with explicit messageContent initialization +- Remove errantly committed build artifact (fabric binary) +- Fix template.go to handle missing variables in stdin input with proper error messages +- Fix raw mode doubling user input by streamlining context staging + +### Direct commits + +- Added analyze_mistakes + +## v1.4.108 (2024-11-21) + +### PR [#1155](https://github.com/danielmiessler/Fabric/pull/1155) by [mattjoyce](https://github.com/mattjoyce): Curly brace templates and plugins + +- Introduced template package for variable substitution with {{variable}} syntax +- Moved substitution logic from patterns to centralized template system +- Updated patterns.go and chatter.go to use new template package +- Added support for special {{input}} handling and nested variables +- Implemented core plugin system with utility plugins (datetime, fetch, file, sys, text) + +## v1.4.107 (2024-11-19) + +### PR [#1149](https://github.com/danielmiessler/Fabric/pull/1149) by [mathisto](https://github.com/mathisto): Fix typo in md_callout + +- Fixed typo in md_callout pattern +- Updated patterns zip workflow +- Removed patterns zip workflow + +## v1.4.106 (2024-11-19) + +### Direct commits + +- Migrate to official anthropics Go SDK + +## v1.4.105 (2024-11-19) + +### PR [#1147](https://github.com/danielmiessler/Fabric/pull/1147) by [mattjoyce](https://github.com/mattjoyce): refactor: unify pattern loading and variable handling + +- Unify pattern loading and variable handling +- Stronger separation of concerns between chatter.go and patterns.go +- Consolidate pattern loading logic into GetPattern method +- Support both file and database patterns through single interface +- Handle variable substitution in one place + +### PR [#1146](https://github.com/danielmiessler/Fabric/pull/1146) by [mrwadams](https://github.com/mrwadams): Add summarize_meeting + +- Add summarize_meeting pattern for creating meeting summaries from audio transcripts +- Outputs Key Points, Tasks, Decisions, and Next Steps sections +- Provides structured format for meeting documentation +- Supports audio transcript processing +- Enables organized meeting follow-up workflow + +### Direct commits + +- Introduce template package for variable substitution with {{variable}} syntax +- Move substitution logic from patterns to centralized template system +- Support special {{input}} handling for pattern content +- Enable multiple passes to handle nested variables +- Report errors for missing required variables + +## v1.4.104 (2024-11-18) + +### PR [#1142](https://github.com/danielmiessler/Fabric/pull/1142) by [mattjoyce](https://github.com/mattjoyce): feat: add file-based pattern support + +- Add file-based pattern support for loading patterns directly from files using path prefixes +- Supports relative paths (./pattern.txt), home directory expansion (~/patterns/test.txt), and absolute paths +- Maintains backwards compatibility with named patterns +- Requires explicit path markers to distinguish from pattern names +- Example usage: `fabric --pattern ./draft-pattern.txt` or `fabric --pattern ~/patterns/my-pattern.txt` + +### Direct commits + +- Add summarize_meeting pattern to create meeting summaries from audio transcripts with sections for Key Points, Tasks, Decisions, and Next Steps + +## v1.4.103 (2024-11-18) + +### PR [#1133](https://github.com/danielmiessler/Fabric/pull/1133) by [igophper](https://github.com/igophper): fix: fix default gin + +- Fix default gin configuration + +### PR [#1129](https://github.com/danielmiessler/Fabric/pull/1129) by [xyb](https://github.com/xyb): add a screenshot of fabric + +- Add a screenshot of fabric to documentation + +## v1.4.102 (2024-11-18) + +### PR [#1143](https://github.com/danielmiessler/Fabric/pull/1143) by [mariozig](https://github.com/mariozig): Update docker image + +- Update docker image + +### Direct commits + +- Add file-based pattern support for loading patterns directly from files using explicit path prefixes +- Support relative paths (./pattern.txt, ../pattern.txt) +- Support home directory expansion (~/patterns/test.txt) +- Support absolute paths with backwards compatibility for named patterns +- Require explicit path markers to distinguish from pattern names + +## v1.4.101 (2024-11-15) + +### Direct commits + +- Improve logging for missing setup steps +- Add extract_recipe to easily extract the necessary information from cooking-videos +- Fix default gin +- Update version to v..1 and commit +- Add a screenshot of fabric + +## v1.4.100 (2024-11-13) + +### Direct commits + +- Added our first formal stitch +- Upgraded AI result rater (multiple iterations) + +## v1.4.99 (2024-11-10) + +### PR [#1126](https://github.com/danielmiessler/Fabric/pull/1126) by [jaredmontoya](https://github.com/jaredmontoya): flake: add gomod2nix auto-update + +- Added gomod2nix auto-update functionality to flake + +### Direct commits + +- Upgraded AI result rater (multiple iterations) + +## v1.4.98 (2024-11-09) + +### Direct commits + +- Zip patterns + +## v1.4.97 (2024-11-09) + +### Direct commits + +- Update dependencies; improve vendors setup/default model + +## v1.4.96 (2024-11-09) + +### PR [#1060](https://github.com/danielmiessler/Fabric/pull/1060) by [noamsiegel](https://github.com/noamsiegel): Analyze Candidates Pattern + +- Added system and user prompts + +### Direct commits + +- Add claude-3-5-haiku-latest model + +## v1.4.95 (2024-11-09) + +### PR [#1123](https://github.com/danielmiessler/Fabric/pull/1123) by [polyglotdev](https://github.com/polyglotdev): :sparkles: Added unaliasing to pattern setup + +- Added unaliasing step to pattern setup process +- Prevents conflicts between dynamically defined functions and existing aliases + +### PR [#1119](https://github.com/danielmiessler/Fabric/pull/1119) by [verebes1](https://github.com/verebes1): Add auto save functionality + +- Added auto save functionality to aliases +- Updated README with autogenerating aliases information for Obsidian integration +- Updated table of contents + +### Direct commits + +- Merged main branch updates +- Updated README documentation + +## v1.4.94 (2024-11-06) + +### PR [#1108](https://github.com/danielmiessler/Fabric/pull/1108) by [butterflyx](https://github.com/butterflyx): [add] RegEx for YT shorts + +- Added VideoID support for YouTube shorts +- Merged branch 'main' into fix/yt-shorts + +### PR [#1117](https://github.com/danielmiessler/Fabric/pull/1117) by [verebes1](https://github.com/verebes1): Add alias generation information + +- Added alias generation documentation to README +- Updated table of contents +- Included information about generating aliases for prompts including YouTube transcripts +- Merged branch 'main' into add-aliases-for-patterns + +### PR [#1115](https://github.com/danielmiessler/Fabric/pull/1115) by [ignacio-arce](https://github.com/ignacio-arce): Added create_diy + +- Added create_diy functionality + +## v1.4.93 (2024-11-06) + +### Direct commits + +- Short YouTube url pattern +- Add alias generation information +- Updated the readme with information about generating aliases for each prompt including one for youtube transcripts +- Updated the table of contents +- Added create_diy +- [add] VideoID for YT shorts + +## v1.4.92 (2024-11-05) + +### PR [#1109](https://github.com/danielmiessler/Fabric/pull/1109) by [leonsgithub](https://github.com/leonsgithub): Add docker + +- Add docker + +## v1.4.91 (2024-11-05) + +### Direct commits + +- Bufio.Scanner message too long +- Add docker + +## v1.4.90 (2024-11-04) + +### Direct commits + +- Impl. Youtube PlayList support +- Close #1103, Update Readme hpt to install to_pdf + +## v1.4.89 (2024-11-04) + +### PR [#1102](https://github.com/danielmiessler/Fabric/pull/1102) by [jholsgrove](https://github.com/jholsgrove): Create user story pattern + +- Create user story pattern + +### Direct commits + +- Close #1106, fix pipe reading +- YouTube PlayList support + +## v1.4.88 (2024-10-30) + +### PR [#1098](https://github.com/danielmiessler/Fabric/pull/1098) by [jaredmontoya](https://github.com/jaredmontoya): Fix nix package update workflow + +- Fix nix package version auto update workflow + +## v1.4.87 (2024-10-30) + +### PR [#1096](https://github.com/danielmiessler/Fabric/pull/1096) by [jaredmontoya](https://github.com/jaredmontoya): Implement automated ci nix package version update + +- Modularize nix flake +- Automate nix package version update + +## v1.4.86 (2024-10-30) + +### PR [#1088](https://github.com/danielmiessler/Fabric/pull/1088) by [jaredmontoya](https://github.com/jaredmontoya): feat: add DEFAULT_CONTEXT_LENGTH setting + +- Add model context length setting + +## v1.4.85 (2024-10-30) + +### Direct commits + +- Write tools output also to output file if defined; fix XouTube transcript ' character + +## v1.4.84 (2024-10-30) + +### Direct commits + +- Deactivate build triggering at changes of patterns or docu + +## v1.4.83 (2024-10-30) + +### PR [#1089](https://github.com/danielmiessler/Fabric/pull/1089) by [jaredmontoya](https://github.com/jaredmontoya): Introduce Nix to the project + +- Add trailing newline +- Add Nix Flake + +## v1.4.82 (2024-10-30) + +### PR [#1094](https://github.com/danielmiessler/Fabric/pull/1094) by [joshmedeski](https://github.com/joshmedeski): feat: add md_callout pattern + +- Add md_callout pattern +Add a pattern that can convert text into an appropriate markdown callout + +## v1.4.81 (2024-10-29) + +### Direct commits + +- Split tools messages from use message + +## v1.4.78 (2024-10-28) + +### PR [#1059](https://github.com/danielmiessler/Fabric/pull/1059) by [noamsiegel](https://github.com/noamsiegel): Analyze Proposition Pattern + +- Added system and user prompts + +## v1.4.77 (2024-10-28) + +### PR [#1073](https://github.com/danielmiessler/Fabric/pull/1073) by [mattjoyce](https://github.com/mattjoyce): Five patterns to explore a project, opportunity or brief + +- Added five new DSRP patterns for project exploration and strategic analysis +- Enhanced prompts to increase divergent thinking capabilities +- Implemented identify_job_stories pattern for user story identification +- Created S7 Strategy profiling with headwinds and tailwinds analysis +- Added comprehensive metadata and style guide documentation + +### Direct commits + +- Add Nix Flake + +## v1.4.76 (2024-10-28) + +### Direct commits + +- Simplify isChatRequest + +## v1.4.75 (2024-10-28) + +### PR [#1090](https://github.com/danielmiessler/Fabric/pull/1090) by [wrochow](https://github.com/wrochow): A couple of patterns + +- Added "Dialog with Socrates" pattern for deep philosophical conversations with a modern-day philosopher +- Added "Ask uncle Duke" pattern for Java development expertise, specializing in Spring Framework and Maven +- Fixed formatting by adding trailing newline to files + +### Direct commits + +- Add trailing newline + +## v1.4.74 (2024-10-27) + +### PR [#1077](https://github.com/danielmiessler/Fabric/pull/1077) by [xvnpw](https://github.com/xvnpw): feat: add pattern refine_design_document + +- Add pattern refine_design_document + +## v1.4.73 (2024-10-27) + +### PR [#1086](https://github.com/danielmiessler/Fabric/pull/1086) by [NuCl34R](https://github.com/NuCl34R): Create a basic translator pattern, edit file to add desired language + +- Create system.md + +### Direct commits + +- Added metadata and styleguide +- Added structure to prompt +- Added headwinds and tailwinds +- Initial draft of s7 Strategy profiling +- Merge operations from main branch + +## v1.4.72 (2024-10-25) + +### PR [#1070](https://github.com/danielmiessler/Fabric/pull/1070) by [xvnpw](https://github.com/xvnpw): feat: create create_design_document pattern + +- Create create_design_document pattern + +## v1.4.71 (2024-10-25) + +### PR [#1072](https://github.com/danielmiessler/Fabric/pull/1072) by [xvnpw](https://github.com/xvnpw): feat: add review_design pattern + +- Add review_design pattern + +## v1.4.70 (2024-10-25) + +### PR [#1064](https://github.com/danielmiessler/Fabric/pull/1064) by [rprouse](https://github.com/rprouse): Update README.md with pbpaste section + +- Update README.md with pbpaste section + +### Direct commits + +- Add pattern refine_design_document +- Added identify_job_stories +- Add review_design pattern +- Create create_design_document pattern +- Added system and user prompts + +## v1.4.69 (2024-10-21) + +### Direct commits + +- Updated the Alma.md file. + +## v1.4.68 (2024-10-21) + +### Direct commits + +- Setup does not overwrites old values + +## v1.4.67 (2024-10-19) + +### Direct commits + +- Merge remote-tracking branch 'origin/main' +- Plugins arch., new setup procedure + +## v1.4.66 (2024-10-19) + +### Direct commits + +- Plugins arch., new setup procedure + +## v1.4.65 (2024-10-16) + +### PR [#1045](https://github.com/danielmiessler/Fabric/pull/1045) by [Fenicio](https://github.com/Fenicio): Update patterns/analyze_answers/system.md - Fixed a bunch of typos + +- Update patterns/analyze_answers/system.md - Fixed a bunch of typos + +## v1.4.64 (2024-10-14) + +### Direct commits + +- Updated readme + +## v1.4.63 (2024-10-13) + +### PR [#862](https://github.com/danielmiessler/Fabric/pull/862) by [Thepathakarpit](https://github.com/Thepathakarpit): Create setup_fabric.bat, a batch script to automate setup and running… + +- Created setup_fabric.bat batch script to automate Fabric setup and execution on Windows +- Merged branch 'main' into patch-1 + +## v1.4.62 (2024-10-13) + +### PR [#1044](https://github.com/danielmiessler/Fabric/pull/1044) by [eugeis](https://github.com/eugeis): Feat/rest api + +- Work on Rest API +- Restructure for better reuse +- Merge branch 'main' into feat/rest-api + +## v1.4.61 (2024-10-13) + +### Direct commits + +- Updated extract sponsors. +- Merge branch 'main' into feat/rest-api +- Restructure for better reuse +- Restructure for better reuse +- Restructure for better reuse + +## v1.4.60 (2024-10-12) + +### Direct commits + +- IsChatRequest rule; Close #1042 is + +## v1.4.59 (2024-10-11) + +### Direct commits + +- Added ctw to Raycast. + +## v1.4.58 (2024-10-11) + +### Direct commits + +- We don't need tp configure DryRun vendor +- Close #1040. Configure vendors separately that were not configured yet + +## v1.4.57 (2024-10-11) + +### Direct commits + +- Close #1035, provide better example for pattern variables + +## v1.4.56 (2024-10-11) + +### PR [#1039](https://github.com/danielmiessler/Fabric/pull/1039) by [hallelujah-shih](https://github.com/hallelujah-shih): Feature/set default lang + +- Support set default output language +- Updated cli/cli.go +- Modified core/fabric.go with formatting changes + +### Direct commits + +- Updated all dsrp prompts to increase divergent thinking +- Fixed mix up with system +- Initial dsrp prompts + +## v1.4.55 (2024-10-09) + +### Direct commits + +- Close #1036 + +## v1.4.54 (2024-10-07) + +### PR [#1021](https://github.com/danielmiessler/Fabric/pull/1021) by [joshuafuller](https://github.com/joshuafuller): Corrected spelling and grammatical errors for consistency and clarity for transcribe_minutes + +- Fixed grammatical accuracy: "agreed within" → "agreed upon within" +- Added missing periods for consistency across list items +- Corrected spelling: "highliting" → "highlighting" +- Fixed spelling: "exxactly" → "exactly" +- Updated phrasing: "Write NEXT STEPS a 2-3 sentences" → "Write NEXT STEPS as 2-3 sentences" + +## v1.4.53 (2024-10-07) + +### Direct commits + +- Fix NP if response is empty, close #1026, #1027 + +## v1.4.52 (2024-10-06) + +### Direct commits + +- Merge branch 'main' of github.com:danielmiessler/fabric +- Added extract_core_message functionality +- Extensive work on Rest API development and implementation +- Corrected spelling and grammatical errors for consistency and clarity: +- Fixed "agreed within" to "agreed upon within" +- Added missing periods for consistency +- Corrected "highliting" to "highlighting" +- Fixed "exxactly" to "exactly" +- Updated "Write NEXT STEPS a 2-3 sentences" to "Write NEXT STEPS as 2-3 sentences" + +These changes improve document readability and API functionality. + +## v1.4.51 (2024-10-05) + +### Direct commits + +- Tests + +## v1.4.50 (2024-10-05) + +### Direct commits + +- Windows release + +## v1.4.49 (2024-10-05) + +### Direct commits + +- Windows release + +## v1.4.48 (2024-10-05) + +### Direct commits + +- Add 'meta' role to store meta info to session, like source of input content. + +## v1.4.47 (2024-10-05) + +### Direct commits + +- Add 'meta' role to store meta info to session, like source of input content. +- Add 'meta' role to store meta info to session, like source of input content. + +## v1.4.46 (2024-10-04) + +### Direct commits + +- Close #1018 +- Implement print session and context +- Implement print session and context + +## v1.4.45 (2024-10-04) + +### Direct commits + +- Setup for specific vendor, e.g. --setup-vendor=OpenAI + +## v1.4.44 (2024-10-03) + +### Direct commits + +- Use the latest tag by date + +## v1.4.43 (2024-10-03) + +### Direct commits + +- Use the latest tag by date + +## v1.4.42 (2024-10-03) + +### Direct commits + +- Use the latest tag by date +- Use the latest tag by date + +## v1.4.41 (2024-10-03) + +### Direct commits + +- Trigger release workflow ony tag_created + +## v1.4.40 (2024-10-03) + +### Direct commits + +- Create repo dispatch + +## v1.4.39 (2024-10-03) + +### Direct commits + +- Test tag creation + +## v1.4.38 (2024-10-03) + +### Direct commits + +- Test tag creation +- Commit version changes only if it changed +- Use TAG_PAT instead of secrets.GITHUB_TOKEN for tag push +- Merge branch 'main' of github.com:danielmiessler/fabric +- Updated predictions pattern + +## v1.4.36 (2024-10-03) + +### Direct commits + +- Merge branch 'main' of github.com:danielmiessler/fabric +- Added redeeming thing. + +## v1.4.35 (2024-10-02) + +### Direct commits + +- Clean up html readability; add autm. tag creation + +## v1.4.34 (2024-10-02) + +### Direct commits + +- Clean up html readability; add autm. tag creation + +## v1.4.33 (2024-10-02) + +### Direct commits + +- Clean up html readability; add autm. tag creation +- Clean up html readability; add autm. tag creation +- Clean up html readability; add autm. tag creation + +## v1.5.0 (2024-10-02) + +### Direct commits + +- Clean up html readability; add autm. tag creation + +## v1.4.32 (2024-10-02) + +### PR [#1007](https://github.com/danielmiessler/Fabric/pull/1007) by [hallelujah-shih](https://github.com/hallelujah-shih): support turn any web page into clean view content + +- Support turn any web page into clean view content + +### PR [#1005](https://github.com/danielmiessler/Fabric/pull/1005) by [fn5](https://github.com/fn5): Update patterns/solve_with_cot/system.md typos + +- Fixed multiple typos in solve_with_cot pattern +- Corrected opening/closing brackets format + +### PR [#962](https://github.com/danielmiessler/Fabric/pull/962) by [alucarded](https://github.com/alucarded): Update prompt in agility_story + +- Updated agility_story pattern to make topic more logical + +### PR [#994](https://github.com/danielmiessler/Fabric/pull/994) by [OddDuck11](https://github.com/OddDuck11): Add pattern analyze_military_strategy + +- Added new pattern for analyzing historic or fictional battle strategies + +### PR [#1008](https://github.com/danielmiessler/Fabric/pull/1008) by [MattBash17](https://github.com/MattBash17): Update system.md in transcribe_minutes + +- Updated transcribe_minutes pattern system file + +## v1.4.31 (2024-10-01) + +### PR [#987](https://github.com/danielmiessler/Fabric/pull/987) by [joshmedeski](https://github.com/joshmedeski): feat: remove cli list label and indentation + +- Remove cli list label and indentation + +### PR [#1011](https://github.com/danielmiessler/Fabric/pull/1011) by [fooman[org]](https://github.com/fooman): Grab transcript from youtube matching the user's language + +- Grab transcript from youtube matching the user's language instead of the first one + +### Direct commits + +- Added epp pattern +- Added create_story_explanation pattern +- Add version updater bot +- Update system.md in transcribe_minutes +- Support turn any web page into clean view content + +## v1.4.30 (2024-09-29) + +### Direct commits + +- Add version updater bot + +## v1.4.29 (2024-09-29) + +### PR [#996](https://github.com/danielmiessler/Fabric/pull/996) by [hallelujah-shih](https://github.com/hallelujah-shih): add wipe flag for ctx and session + +- Add wipe flag for ctx and session + +### PR [#984](https://github.com/danielmiessler/Fabric/pull/984) by [riccardo1980](https://github.com/riccardo1980): adding flag for pinning seed in openai and compatible APIs + +- Adding flag for pinning seed in openai and compatible APIs +- Updating README with the new flag + +### PR [#991](https://github.com/danielmiessler/Fabric/pull/991) by [aculich](https://github.com/aculich): Fix GOROOT path for Apple Silicon Macs + +- Fix GOROOT path for Apple Silicon Macs in setup instructions +- Updated instructions to dynamically determine correct GOROOT path using Homebrew + +### PR [#861](https://github.com/danielmiessler/Fabric/pull/861) by [noamsiegel](https://github.com/noamsiegel): Scrape url + +- Add ScrapeURL flag for CLI to scrape website URL to markdown using Jina AI +- Add Jina AI integration for web scraping and question search +- Made jina api key optional + +### PR [#970](https://github.com/danielmiessler/Fabric/pull/970) by [mark-kazakov](https://github.com/mark-kazakov): add mistral vendor + +- Add mistral vendor diff --git a/cmd/generate_changelog/PRD.md b/cmd/generate_changelog/PRD.md new file mode 100644 index 00000000..0f96cad7 --- /dev/null +++ b/cmd/generate_changelog/PRD.md @@ -0,0 +1,151 @@ +# Product Requirements Document: Changelog Generator + +## Overview + +The Changelog Generator is a high-performance Go tool that automatically generates comprehensive changelogs from git history and GitHub pull requests. + +## Goals + +1. **Performance**: Very fast. Efficient enough to be used in CI/CD as part of release process. +2. **Completeness**: Capture ALL commits including unreleased changes +3. **Efficiency**: Minimize API calls through caching and batch operations +4. **Reliability**: Handle errors gracefully with proper Go error handling +5. **Simplicity**: Single binary with no runtime dependencies + +## Key Features + +### 1. One-Pass Git History Algorithm + +- Walk git history once from newest to oldest +- Start with "Unreleased" bucket for all new commits +- Switch buckets when encountering version commits +- No need to calculate ranges between versions + +### 2. Native Library Integration + +- **go-git**: Pure Go git implementation (no git binary required) +- **go-github**: Official GitHub Go client library +- Benefits: Type safety, better error handling, no subprocess overhead + +### 3. Smart Caching System + +- SQLite-based persistent cache +- Stores: versions, commits, PR details, last processed commit +- Enables incremental updates on subsequent runs +- Instant changelog regeneration from cache + +### 4. Concurrent Processing + +- Parallel GitHub API calls (up to 10 concurrent) +- Batch PR fetching with deduplication +- Rate limiting awareness + +### 5. Enhanced Output + +- "Unreleased" section for commits since last version +- Clean markdown formatting +- Configurable version limiting +- Direct commit tracking (non-PR commits) + +## Technical Architecture + +### Module Structure + +```text +cmd/generate_changelog/ +├── main.go # CLI entry point with cobra +├── internal/ +│ ├── git/ # Git operations (go-git) +│ ├── github/ # GitHub API client (go-github) +│ ├── cache/ # SQLite caching layer +│ ├── changelog/ # Core generation logic +│ └── config/ # Configuration management +└── changelog.db # SQLite cache (generated) +``` + +### Data Flow + +1. Git walker collects all commits in one pass +2. Commits bucketed by version (starting with "Unreleased") +3. PR numbers extracted from merge commits +4. GitHub API batch-fetches PR details +5. Cache stores everything for future runs +6. Formatter generates markdown output + +### Cache Schema + +- **metadata**: Last processed commit SHA +- **versions**: Version names, dates, commit SHAs +- **commits**: Full commit details with version associations +- **pull_requests**: PR details including commits +- Indexes on version and PR number for fast lookups + +### Features + +- **Unreleased section**: Shows all new commits +- **Better caching**: SQLite vs JSON, incremental updates +- **Smarter deduplication**: Removes consecutive duplicate commits +- **Direct commit tracking**: Shows non-PR commits + +### Reliability + +- **No subprocess errors**: Direct library usage +- **Type safety**: Compile-time checking +- **Better error handling**: Go's explicit error returns + +### Deployment + +- **Single binary**: No Python/pip/dependencies +- **Cross-platform**: Compile for any OS/architecture +- **No git CLI required**: Uses go-git library + +## Configuration + +### Environment Variables + +- `GITHUB_TOKEN`: GitHub API authentication token + +### Command Line Flags + +- `--repo, -r`: Repository path (default: current directory) +- `--output, -o`: Output file (default: stdout) +- `--limit, -l`: Version limit (default: all) +- `--version, -v`: Target specific version +- `--save-data`: Export debug JSON +- `--cache`: Cache file location +- `--no-cache`: Disable caching +- `--rebuild-cache`: Force cache rebuild +- `--token`: GitHub token override + +## Success Metrics + +1. **Performance**: Generate full changelog in <5 seconds for fabric repo +2. **Completeness**: 100% commit coverage including unreleased +3. **Accuracy**: Correct PR associations and change extraction +4. **Reliability**: Handle network failures gracefully +5. **Usability**: Simple CLI with sensible defaults + +## Future Enhancements + +1. **Multiple output formats**: JSON, HTML, etc. +2. **Custom version patterns**: Configurable regex +3. **Change categorization**: feat/fix/docs auto-grouping +4. **Conventional commits**: Full support for semantic versioning +5. **GitLab/Bitbucket**: Support other platforms +6. **Web UI**: Interactive changelog browser +7. **Incremental updates**: Update existing CHANGELOG.md file +8. **Breaking change detection**: Highlight breaking changes + +## Implementation Status + +- ✅ Core architecture and modules +- ✅ One-pass git walking algorithm +- ✅ GitHub API integration with concurrency +- ✅ SQLite caching system +- ✅ Changelog formatting and generation +- ✅ CLI with all planned flags +- ✅ Documentation (README and PRD) + +## Conclusion + +This Go implementation provides a modern, efficient, and feature-rich changelog generator. diff --git a/cmd/generate_changelog/README.md b/cmd/generate_changelog/README.md new file mode 100644 index 00000000..9aed132a --- /dev/null +++ b/cmd/generate_changelog/README.md @@ -0,0 +1,178 @@ +# Changelog Generator + +A high-performance changelog generator for Git repositories that automatically creates comprehensive, well-formatted changelogs from your git history and GitHub pull requests. + +## Features + +- **One-pass git history walking**: Efficiently processes entire repository history in a single pass +- **Automatic PR detection**: Extracts pull request information from merge commits +- **GitHub API integration**: Fetches detailed PR information including commits, authors, and descriptions +- **Smart caching**: SQLite-based caching for instant incremental updates +- **Unreleased changes**: Tracks all commits since the last release +- **Concurrent processing**: Parallel GitHub API calls for improved performance +- **Flexible output**: Generate complete changelogs or target specific versions +- **Optimized PR fetching**: Batch fetches all merged PRs using GitHub Search API (drastically reduces API calls) +- **Intelligent sync**: Automatically syncs new PRs every 24 hours or when missing PRs are detected + +## Installation + +```bash +go install github.com/danielmiessler/fabric/cmd/generate_changelog@latest +``` + +## Usage + +### Basic usage (generate complete changelog) +```bash +generate_changelog +``` + +### Save to file +```bash +generate_changelog -o CHANGELOG.md +``` + +### Generate for specific version +```bash +generate_changelog -v v1.4.244 +``` + +### Limit to recent versions +```bash +generate_changelog -l 10 +``` + +### Using GitHub token for private repos or higher rate limits +```bash +export GITHUB_TOKEN=your_token_here +generate_changelog + +# Or pass directly +generate_changelog --token your_token_here +``` + +### Cache management +```bash +# Rebuild cache from scratch +generate_changelog --rebuild-cache + +# Force a full PR sync from GitHub +generate_changelog --force-pr-sync + +# Disable cache usage +generate_changelog --no-cache + +# Use custom cache location +generate_changelog --cache /path/to/cache.db +``` + +## Command Line Options + +| Flag | Short | Description | Default | +|------|-------|-------------|---------| +| `--repo` | `-r` | Repository path | `.` (current directory) | +| `--output` | `-o` | Output file | stdout | +| `--limit` | `-l` | Limit number of versions | 0 (all) | +| `--version` | `-v` | Generate for specific version | | +| `--save-data` | | Save version data to JSON | false | +| `--cache` | | Cache database file | `./cmd/generate_changelog/changelog.db` | +| `--no-cache` | | Disable cache usage | false | +| `--rebuild-cache` | | Rebuild cache from scratch | false | +| `--force-pr-sync` | | Force a full PR sync from GitHub | false | +| `--token` | | GitHub API token | `$GITHUB_TOKEN` | + +## Output Format + +The generated changelog follows this structure: + +```markdown +# Changelog + +## Unreleased + +### PR [#1601](url) by [author](profile): PR Title +- Change description 1 +- Change description 2 + +### Direct commits +- Direct commit message 1 +- Direct commit message 2 + +## v1.4.244 (2025-07-09) + +### PR [#1598](url) by [author](profile): PR Title +- Change description +... +``` + +## How It Works + +1. **Git History Walking**: The tool walks through your git history from newest to oldest commits +2. **Version Detection**: Identifies version bump commits (pattern: "Update version to vX.Y.Z") +3. **PR Extraction**: Detects merge commits and extracts PR numbers +4. **GitHub API Calls**: Fetches detailed PR information in parallel batches +5. **Change Extraction**: Extracts changes from PR commit messages or PR body +6. **Formatting**: Generates clean, organized markdown output + +## Performance + +- **Native Go libraries**: Uses go-git and go-github for maximum performance +- **Concurrent API calls**: Processes up to 10 GitHub API requests in parallel +- **Smart caching**: SQLite cache eliminates redundant API calls +- **Incremental updates**: Only processes new commits on subsequent runs +- **Batch PR fetching**: Uses GitHub Search API to fetch all merged PRs in minimal API calls + +### Major Optimization: Batch PR Fetching + +The tool has been optimized to drastically reduce GitHub API calls: + +**Before**: Individual API calls for each PR (2 API calls per PR - one for PR details, one for commits) +- For a repo with 500 PRs: 1,000 API calls + +**After**: Batch fetching using GitHub Search API +- For a repo with 500 PRs: ~10 API calls (search) + 500 API calls (details) = ~510 API calls +- **50% reduction in API calls!** + +The optimization includes: +1. **Batch Search**: Uses GitHub's Search API to find all merged PRs in paginated batches +2. **Smart Caching**: Stores complete PR data and tracks last sync timestamp +3. **Incremental Sync**: Only fetches PRs merged after the last sync +4. **Automatic Refresh**: PRs are synced every 24 hours or when missing PRs are detected +5. **Fallback Support**: If batch fetch fails, falls back to individual PR fetching + +## Requirements + +- Go 1.24+ (for installation from source) +- Git repository +- GitHub token (optional, for private repos or higher rate limits) + +## Authentication + +The tool supports GitHub authentication via: +1. Environment variable: `export GITHUB_TOKEN=your_token` +2. Command line flag: `--token your_token` + +Without authentication, the tool is limited to 60 GitHub API requests per hour. + +## Caching + +The SQLite cache stores: +- Version information and commit associations +- Pull request details (title, body, commits, authors) +- Last processed commit SHA for incremental updates +- Last PR sync timestamp for intelligent refresh + +Cache benefits: +- Instant changelog regeneration +- Drastically reduced GitHub API usage (50%+ reduction) +- Offline changelog generation (after initial cache build) +- Automatic PR data refresh every 24 hours +- Batch database transactions for better performance + +## Contributing + +This tool is part of the Fabric project. Contributions are welcome! + +## License + +Same as the Fabric project. \ No newline at end of file diff --git a/cmd/generate_changelog/changelog.db b/cmd/generate_changelog/changelog.db new file mode 100644 index 00000000..d184f2b5 Binary files /dev/null and b/cmd/generate_changelog/changelog.db differ diff --git a/cmd/generate_changelog/internal/cache/cache.go b/cmd/generate_changelog/internal/cache/cache.go new file mode 100644 index 00000000..f1e21989 --- /dev/null +++ b/cmd/generate_changelog/internal/cache/cache.go @@ -0,0 +1,448 @@ +package cache + +import ( + "database/sql" + "encoding/json" + "fmt" + "time" + + "github.com/danielmiessler/fabric/cmd/generate_changelog/internal/git" + "github.com/danielmiessler/fabric/cmd/generate_changelog/internal/github" + _ "github.com/mattn/go-sqlite3" +) + +type Cache struct { + db *sql.DB +} + +func New(dbPath string) (*Cache, error) { + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return nil, fmt.Errorf("failed to open database: %w", err) + } + + cache := &Cache{db: db} + if err := cache.createTables(); err != nil { + return nil, fmt.Errorf("failed to create tables: %w", err) + } + + return cache, nil +} + +func (c *Cache) Close() error { + return c.db.Close() +} + +func (c *Cache) createTables() error { + queries := []string{ + `CREATE TABLE IF NOT EXISTS metadata ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + )`, + `CREATE TABLE IF NOT EXISTS versions ( + name TEXT PRIMARY KEY, + date DATETIME, + commit_sha TEXT, + pr_numbers TEXT, + ai_summary TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + )`, + `CREATE TABLE IF NOT EXISTS commits ( + sha TEXT PRIMARY KEY, + version TEXT NOT NULL, + message TEXT, + author TEXT, + email TEXT, + date DATETIME, + is_merge BOOLEAN, + pr_number INTEGER, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (version) REFERENCES versions(name) + )`, + `CREATE TABLE IF NOT EXISTS pull_requests ( + number INTEGER PRIMARY KEY, + title TEXT, + body TEXT, + author TEXT, + author_url TEXT, + author_type TEXT DEFAULT 'user', + url TEXT, + merged_at DATETIME, + merge_commit TEXT, + commits TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + )`, + `CREATE INDEX IF NOT EXISTS idx_commits_version ON commits(version)`, + `CREATE INDEX IF NOT EXISTS idx_commits_pr_number ON commits(pr_number)`, + `CREATE TABLE IF NOT EXISTS commit_pr_mapping ( + commit_sha TEXT PRIMARY KEY, + pr_number INTEGER NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (pr_number) REFERENCES pull_requests(number) + )`, + `CREATE INDEX IF NOT EXISTS idx_commit_pr_mapping_sha ON commit_pr_mapping(commit_sha)`, + } + + for _, query := range queries { + if _, err := c.db.Exec(query); err != nil { + return fmt.Errorf("failed to execute query: %w", err) + } + } + + return nil +} + +func (c *Cache) GetLastProcessedTag() (string, error) { + var tag string + err := c.db.QueryRow("SELECT value FROM metadata WHERE key = 'last_processed_tag'").Scan(&tag) + if err == sql.ErrNoRows { + return "", nil + } + return tag, err +} + +func (c *Cache) SetLastProcessedTag(tag string) error { + _, err := c.db.Exec(` + INSERT OR REPLACE INTO metadata (key, value, updated_at) + VALUES ('last_processed_tag', ?, CURRENT_TIMESTAMP) + `, tag) + return err +} + +func (c *Cache) SaveVersion(v *git.Version) error { + prNumbers, _ := json.Marshal(v.PRNumbers) + + _, err := c.db.Exec(` + INSERT OR REPLACE INTO versions (name, date, commit_sha, pr_numbers, ai_summary) + VALUES (?, ?, ?, ?, ?) + `, v.Name, v.Date, v.CommitSHA, string(prNumbers), v.AISummary) + + return err +} + +// UpdateVersionAISummary updates only the AI summary for a specific version +func (c *Cache) UpdateVersionAISummary(versionName, aiSummary string) error { + _, err := c.db.Exec(` + UPDATE versions SET ai_summary = ? WHERE name = ? + `, aiSummary, versionName) + return err +} + +func (c *Cache) SaveCommit(commit *git.Commit, version string) error { + _, err := c.db.Exec(` + INSERT OR REPLACE INTO commits + (sha, version, message, author, email, date, is_merge, pr_number) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `, commit.SHA, version, commit.Message, commit.Author, commit.Email, + commit.Date, commit.IsMerge, commit.PRNumber) + + return err +} + +func (c *Cache) SavePR(pr *github.PR) error { + commits, _ := json.Marshal(pr.Commits) + + _, err := c.db.Exec(` + INSERT OR REPLACE INTO pull_requests + (number, title, body, author, author_url, author_type, url, merged_at, merge_commit, commits) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, pr.Number, pr.Title, pr.Body, pr.Author, pr.AuthorURL, pr.AuthorType, + pr.URL, pr.MergedAt, pr.MergeCommit, string(commits)) + + return err +} + +func (c *Cache) GetPR(number int) (*github.PR, error) { + var pr github.PR + var commitsJSON string + + err := c.db.QueryRow(` + SELECT number, title, body, author, author_url, COALESCE(author_type, 'user'), url, merged_at, merge_commit, commits + FROM pull_requests WHERE number = ? + `, number).Scan( + &pr.Number, &pr.Title, &pr.Body, &pr.Author, &pr.AuthorURL, &pr.AuthorType, + &pr.URL, &pr.MergedAt, &pr.MergeCommit, &commitsJSON, + ) + + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, err + } + + if err := json.Unmarshal([]byte(commitsJSON), &pr.Commits); err != nil { + return nil, fmt.Errorf("failed to unmarshal commits: %w", err) + } + + return &pr, nil +} + +func (c *Cache) GetVersions() (map[string]*git.Version, error) { + rows, err := c.db.Query(` + SELECT name, date, commit_sha, pr_numbers, ai_summary FROM versions + `) + if err != nil { + return nil, err + } + defer rows.Close() + + versions := make(map[string]*git.Version) + + for rows.Next() { + var v git.Version + var dateStr sql.NullString + var prNumbersJSON string + var aiSummary sql.NullString + + if err := rows.Scan(&v.Name, &dateStr, &v.CommitSHA, &prNumbersJSON, &aiSummary); err != nil { + return nil, err + } + + if dateStr.Valid { + v.Date, _ = time.Parse(time.RFC3339, dateStr.String) + } + + if prNumbersJSON != "" { + json.Unmarshal([]byte(prNumbersJSON), &v.PRNumbers) + } + + if aiSummary.Valid { + v.AISummary = aiSummary.String + } + + v.Commits, err = c.getCommitsForVersion(v.Name) + if err != nil { + return nil, err + } + + versions[v.Name] = &v + } + + return versions, rows.Err() +} + +func (c *Cache) getCommitsForVersion(version string) ([]*git.Commit, error) { + rows, err := c.db.Query(` + SELECT sha, message, author, email, date, is_merge, pr_number + FROM commits WHERE version = ? + ORDER BY date DESC + `, version) + if err != nil { + return nil, err + } + defer rows.Close() + + var commits []*git.Commit + + for rows.Next() { + var commit git.Commit + if err := rows.Scan( + &commit.SHA, &commit.Message, &commit.Author, &commit.Email, + &commit.Date, &commit.IsMerge, &commit.PRNumber, + ); err != nil { + return nil, err + } + commits = append(commits, &commit) + } + + return commits, rows.Err() +} + +func (c *Cache) Clear() error { + tables := []string{"metadata", "versions", "commits", "pull_requests"} + for _, table := range tables { + if _, err := c.db.Exec("DELETE FROM " + table); err != nil { + return err + } + } + return nil +} + +// GetLastPRSync returns the timestamp of the last PR sync +func (c *Cache) GetLastPRSync() (time.Time, error) { + var timestamp string + err := c.db.QueryRow("SELECT value FROM metadata WHERE key = 'last_pr_sync'").Scan(×tamp) + if err == sql.ErrNoRows { + return time.Time{}, nil + } + if err != nil { + return time.Time{}, err + } + + return time.Parse(time.RFC3339, timestamp) +} + +// SetLastPRSync updates the timestamp of the last PR sync +func (c *Cache) SetLastPRSync(timestamp time.Time) error { + _, err := c.db.Exec(` + INSERT OR REPLACE INTO metadata (key, value, updated_at) + VALUES ('last_pr_sync', ?, CURRENT_TIMESTAMP) + `, timestamp.Format(time.RFC3339)) + return err +} + +// SavePRBatch saves multiple PRs in a single transaction for better performance +func (c *Cache) SavePRBatch(prs []*github.PR) error { + tx, err := c.db.Begin() + if err != nil { + return fmt.Errorf("failed to begin transaction: %w", err) + } + defer tx.Rollback() + + stmt, err := tx.Prepare(` + INSERT OR REPLACE INTO pull_requests + (number, title, body, author, author_url, author_type, url, merged_at, merge_commit, commits) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `) + if err != nil { + return fmt.Errorf("failed to prepare statement: %w", err) + } + defer stmt.Close() + + for _, pr := range prs { + commits, _ := json.Marshal(pr.Commits) + _, err := stmt.Exec( + pr.Number, pr.Title, pr.Body, pr.Author, pr.AuthorURL, pr.AuthorType, + pr.URL, pr.MergedAt, pr.MergeCommit, string(commits), + ) + if err != nil { + return fmt.Errorf("failed to save PR #%d: %w", pr.Number, err) + } + } + + return tx.Commit() +} + +// GetAllPRs returns all cached PRs +func (c *Cache) GetAllPRs() (map[int]*github.PR, error) { + rows, err := c.db.Query(` + SELECT number, title, body, author, author_url, COALESCE(author_type, 'user'), url, merged_at, merge_commit, commits + FROM pull_requests + `) + if err != nil { + return nil, err + } + defer rows.Close() + + prs := make(map[int]*github.PR) + + for rows.Next() { + var pr github.PR + var commitsJSON string + + if err := rows.Scan( + &pr.Number, &pr.Title, &pr.Body, &pr.Author, &pr.AuthorURL, &pr.AuthorType, + &pr.URL, &pr.MergedAt, &pr.MergeCommit, &commitsJSON, + ); err != nil { + return nil, err + } + + if err := json.Unmarshal([]byte(commitsJSON), &pr.Commits); err != nil { + return nil, fmt.Errorf("failed to unmarshal commits for PR #%d: %w", pr.Number, err) + } + + prs[pr.Number] = &pr + } + + return prs, rows.Err() +} + +// MarkPRAsNonExistent marks a PR number as non-existent to avoid future fetches +func (c *Cache) MarkPRAsNonExistent(prNumber int) error { + _, err := c.db.Exec(` + INSERT OR REPLACE INTO metadata (key, value, updated_at) + VALUES (?, 'non_existent', CURRENT_TIMESTAMP) + `, fmt.Sprintf("pr_non_existent_%d", prNumber)) + return err +} + +// IsPRMarkedAsNonExistent checks if a PR is marked as non-existent +func (c *Cache) IsPRMarkedAsNonExistent(prNumber int) bool { + var value string + err := c.db.QueryRow("SELECT value FROM metadata WHERE key = ?", + fmt.Sprintf("pr_non_existent_%d", prNumber)).Scan(&value) + return err == nil && value == "non_existent" +} + +// SaveCommitPRMappings saves SHA→PR mappings for all commits in PRs +func (c *Cache) SaveCommitPRMappings(prs []*github.PR) error { + tx, err := c.db.Begin() + if err != nil { + return fmt.Errorf("failed to begin transaction: %w", err) + } + defer tx.Rollback() + + stmt, err := tx.Prepare(` + INSERT OR REPLACE INTO commit_pr_mapping (commit_sha, pr_number) + VALUES (?, ?) + `) + if err != nil { + return fmt.Errorf("failed to prepare statement: %w", err) + } + defer stmt.Close() + + for _, pr := range prs { + for _, commit := range pr.Commits { + _, err := stmt.Exec(commit.SHA, pr.Number) + if err != nil { + return fmt.Errorf("failed to save commit mapping %s→%d: %w", commit.SHA, pr.Number, err) + } + } + } + + return tx.Commit() +} + +// GetPRNumberBySHA returns the PR number for a given commit SHA +func (c *Cache) GetPRNumberBySHA(sha string) (int, bool) { + var prNumber int + err := c.db.QueryRow("SELECT pr_number FROM commit_pr_mapping WHERE commit_sha = ?", sha).Scan(&prNumber) + if err == sql.ErrNoRows { + return 0, false + } + if err != nil { + return 0, false + } + return prNumber, true +} + +// GetCommitSHAsForPR returns all commit SHAs for a given PR number +func (c *Cache) GetCommitSHAsForPR(prNumber int) ([]string, error) { + rows, err := c.db.Query("SELECT commit_sha FROM commit_pr_mapping WHERE pr_number = ?", prNumber) + if err != nil { + return nil, err + } + defer rows.Close() + + var shas []string + for rows.Next() { + var sha string + if err := rows.Scan(&sha); err != nil { + return nil, err + } + shas = append(shas, sha) + } + + return shas, rows.Err() +} + +// GetUnreleasedContentHash returns the cached content hash for Unreleased +func (c *Cache) GetUnreleasedContentHash() (string, error) { + var hash string + err := c.db.QueryRow("SELECT value FROM metadata WHERE key = 'unreleased_content_hash'").Scan(&hash) + if err == sql.ErrNoRows { + return "", fmt.Errorf("no content hash found") + } + return hash, err +} + +// SetUnreleasedContentHash stores the content hash for Unreleased +func (c *Cache) SetUnreleasedContentHash(hash string) error { + _, err := c.db.Exec(` + INSERT OR REPLACE INTO metadata (key, value, updated_at) + VALUES ('unreleased_content_hash', ?, CURRENT_TIMESTAMP) + `, hash) + return err +} diff --git a/cmd/generate_changelog/internal/changelog/generator.go b/cmd/generate_changelog/internal/changelog/generator.go new file mode 100644 index 00000000..83ef51eb --- /dev/null +++ b/cmd/generate_changelog/internal/changelog/generator.go @@ -0,0 +1,643 @@ +package changelog + +import ( + "crypto/sha256" + "fmt" + "os" + "regexp" + "sort" + "strings" + "time" + + "github.com/danielmiessler/fabric/cmd/generate_changelog/internal/cache" + "github.com/danielmiessler/fabric/cmd/generate_changelog/internal/config" + "github.com/danielmiessler/fabric/cmd/generate_changelog/internal/git" + "github.com/danielmiessler/fabric/cmd/generate_changelog/internal/github" +) + +type Generator struct { + cfg *config.Config + gitWalker *git.Walker + ghClient *github.Client + cache *cache.Cache + versions map[string]*git.Version + prs map[int]*github.PR +} + +func New(cfg *config.Config) (*Generator, error) { + gitWalker, err := git.NewWalker(cfg.RepoPath) + if err != nil { + return nil, fmt.Errorf("failed to create git walker: %w", err) + } + + owner, repo, err := gitWalker.GetRepoInfo() + if err != nil { + return nil, fmt.Errorf("failed to get repo info: %w", err) + } + + ghClient := github.NewClient(cfg.GitHubToken, owner, repo) + + var c *cache.Cache + if !cfg.NoCache { + c, err = cache.New(cfg.CacheFile) + if err != nil { + return nil, fmt.Errorf("failed to create cache: %w", err) + } + + if cfg.RebuildCache { + if err := c.Clear(); err != nil { + return nil, fmt.Errorf("failed to clear cache: %w", err) + } + } + } + + return &Generator{ + cfg: cfg, + gitWalker: gitWalker, + ghClient: ghClient, + cache: c, + prs: make(map[int]*github.PR), + }, nil +} + +func (g *Generator) Generate() (string, error) { + if err := g.collectData(); err != nil { + return "", fmt.Errorf("failed to collect data: %w", err) + } + + if err := g.fetchPRs(); err != nil { + return "", fmt.Errorf("failed to fetch PRs: %w", err) + } + + return g.formatChangelog(), nil +} + +func (g *Generator) collectData() error { + if g.cache != nil && !g.cfg.RebuildCache { + cachedTag, err := g.cache.GetLastProcessedTag() + if err != nil { + return fmt.Errorf("failed to get last processed tag: %w", err) + } + + if cachedTag != "" { + // Get the current latest tag from git + currentTag, err := g.gitWalker.GetLatestTag() + if err == nil && currentTag == cachedTag { + // Same tag - load cached data and walk commits since tag for "Unreleased" + cachedVersions, err := g.cache.GetVersions() + if err == nil && len(cachedVersions) > 0 { + g.versions = cachedVersions + + // Load cached PRs + for _, version := range g.versions { + for _, prNum := range version.PRNumbers { + if pr, err := g.cache.GetPR(prNum); err == nil && pr != nil { + g.prs[prNum] = pr + } + } + } + + // Walk commits since the latest tag to get new unreleased commits + unreleasedVersion, err := g.gitWalker.WalkCommitsSinceTag(currentTag) + if err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to walk commits since tag %s: %v\n", currentTag, err) + } else if unreleasedVersion != nil { + // Preserve existing AI summary if available + if existingUnreleased, exists := g.versions["Unreleased"]; exists { + unreleasedVersion.AISummary = existingUnreleased.AISummary + } + // Replace or add the unreleased version + g.versions["Unreleased"] = unreleasedVersion + } + + return nil + } + } + } + } + + versions, err := g.gitWalker.WalkHistory() + if err != nil { + return fmt.Errorf("failed to walk history: %w", err) + } + + g.versions = versions + + if g.cache != nil { + for _, version := range versions { + if err := g.cache.SaveVersion(version); err != nil { + return fmt.Errorf("failed to save version to cache: %w", err) + } + + for _, commit := range version.Commits { + if err := g.cache.SaveCommit(commit, version.Name); err != nil { + return fmt.Errorf("failed to save commit to cache: %w", err) + } + } + } + + // Save the latest tag as our cache anchor point + if latestTag, err := g.gitWalker.GetLatestTag(); err == nil && latestTag != "" { + if err := g.cache.SetLastProcessedTag(latestTag); err != nil { + return fmt.Errorf("failed to save last processed tag: %w", err) + } + } + } + + return nil +} + +func (g *Generator) fetchPRs() error { + // First, load all cached PRs + if g.cache != nil { + cachedPRs, err := g.cache.GetAllPRs() + if err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to load cached PRs: %v\n", err) + } else { + g.prs = cachedPRs + } + } + + // Check if we need to fetch new PRs + var lastSync time.Time + if g.cache != nil { + lastSync, _ = g.cache.GetLastPRSync() + } + + // If we have never synced or it's been more than 24 hours, do a full sync + needsSync := lastSync.IsZero() || time.Since(lastSync) > 24*time.Hour || g.cfg.ForcePRSync + + if !needsSync { + fmt.Fprintf(os.Stderr, "Using cached PR data (last sync: %s)\n", lastSync.Format("2006-01-02 15:04:05")) + return nil + } + + fmt.Fprintf(os.Stderr, "Fetching merged PRs from GitHub using GraphQL...\n") + + // Use GraphQL for ultimate performance - gets everything in ~5-10 calls + prs, err := g.ghClient.FetchAllMergedPRsGraphQL(lastSync) + if err != nil { + fmt.Fprintf(os.Stderr, "GraphQL fetch failed, falling back to REST API: %v\n", err) + // Fall back to REST API + prs, err = g.ghClient.FetchAllMergedPRs(lastSync) + if err != nil { + return fmt.Errorf("both GraphQL and REST API failed: %w", err) + } + } + + // Update our PR map with new data + for _, pr := range prs { + g.prs[pr.Number] = pr + } + + // Save all PRs to cache in a batch transaction + if g.cache != nil && len(prs) > 0 { + // Save PRs + if err := g.cache.SavePRBatch(prs); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to cache PRs: %v\n", err) + } + + // Save SHA→PR mappings for lightning-fast git operations + if err := g.cache.SaveCommitPRMappings(prs); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to cache commit mappings: %v\n", err) + } + + // Update last sync timestamp + if err := g.cache.SetLastPRSync(time.Now()); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to update last sync timestamp: %v\n", err) + } + } + + if len(prs) > 0 { + fmt.Fprintf(os.Stderr, "Fetched %d PRs with commits (total cached: %d)\n", len(prs), len(g.prs)) + } + + return nil +} + +func (g *Generator) formatChangelog() string { + var sb strings.Builder + sb.WriteString("# Changelog\n") + + versionList := g.getSortedVersions() + + for _, version := range versionList { + if g.cfg.Version != "" && version.Name != g.cfg.Version { + continue + } + + versionText := g.formatVersion(version) + if versionText != "" { + sb.WriteString("\n") + sb.WriteString(versionText) + } + } + + return sb.String() +} + +func (g *Generator) getSortedVersions() []*git.Version { + var versions []*git.Version + var releasedVersions []*git.Version + + // Collect all released versions (non-"Unreleased") + for name, version := range g.versions { + if name != "Unreleased" { + releasedVersions = append(releasedVersions, version) + } + } + + // Sort released versions by date (newest first) + sort.Slice(releasedVersions, func(i, j int) bool { + return releasedVersions[i].Date.After(releasedVersions[j].Date) + }) + + // Add "Unreleased" first if it exists and has commits + if unreleased, exists := g.versions["Unreleased"]; exists && len(unreleased.Commits) > 0 { + versions = append(versions, unreleased) + } + + // Add sorted released versions + versions = append(versions, releasedVersions...) + + if g.cfg.Limit > 0 && len(versions) > g.cfg.Limit { + versions = versions[:g.cfg.Limit] + } + + return versions +} + +func (g *Generator) formatVersion(version *git.Version) string { + var sb strings.Builder + + // Generate raw content + rawContent := g.generateRawVersionContent(version) + if rawContent == "" { + return "" + } + + header := g.formatVersionHeader(version) + sb.WriteString(("\n")) + sb.WriteString(header) + + // If AI summarization is enabled, enhance with AI + if g.cfg.EnableAISummary { + // For "Unreleased", check if content has changed since last AI summary + if version.Name == "Unreleased" && version.AISummary != "" && g.cache != nil { + // Get cached content hash + cachedHash, err := g.cache.GetUnreleasedContentHash() + if err == nil { + // Calculate current content hash + currentHash := hashContent(rawContent) + if cachedHash == currentHash { + // Content unchanged, use cached summary + fmt.Fprintf(os.Stderr, "✅ %s content unchanged (skipping AI)\n", version.Name) + sb.WriteString(version.AISummary) + return fixMarkdown(sb.String()) + } + } + } + + if version.Name != "Unreleased" && version.AISummary != "" { + fmt.Fprintf(os.Stderr, "✅ %s already summarized (skipping)\n", version.Name) + sb.WriteString(version.AISummary) + return fixMarkdown(sb.String()) + } + + fmt.Fprintf(os.Stderr, "🤖 AI summarizing %s...", version.Name) + + aiSummary, err := SummarizeVersionContent(rawContent) + if err != nil { + fmt.Fprintf(os.Stderr, " Failed: %v\n", err) + sb.WriteString((rawContent)) + return fixMarkdown(sb.String()) + } + if checkForAIError(aiSummary) { + fmt.Fprintf(os.Stderr, " AI error detected, using raw content instead\n") + sb.WriteString(rawContent) + fmt.Fprintf(os.Stderr, "Raw Content was: (%d bytes) %s \n", len(rawContent), rawContent) + fmt.Fprintf(os.Stderr, "AI Summary was: (%d bytes) %s\n", len(aiSummary), aiSummary) + return fixMarkdown(sb.String()) + } + + fmt.Fprintf(os.Stderr, " Done!\n") + aiSummary = strings.TrimSpace(aiSummary) + + // Cache the AI summary and content hash + version.AISummary = aiSummary + if g.cache != nil { + if err := g.cache.UpdateVersionAISummary(version.Name, aiSummary); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to cache AI summary: %v\n", err) + } + // Cache content hash for "Unreleased" to detect changes + if version.Name == "Unreleased" { + if err := g.cache.SetUnreleasedContentHash(hashContent(rawContent)); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to cache content hash: %v\n", err) + } + } + } + + sb.WriteString(aiSummary) + return fixMarkdown(sb.String()) + } + + sb.WriteString(rawContent) + return fixMarkdown(sb.String()) +} + +func checkForAIError(summary string) bool { + // Check for common AI error patterns + errorPatterns := []string{ + "I don't see any", "please provide", + "content you've provided appears to be incomplete", + } + + for _, pattern := range errorPatterns { + if strings.Contains(summary, pattern) { + return true + } + } + + return false +} + +// formatVersionHeader formats just the version header (## ...) +func (g *Generator) formatVersionHeader(version *git.Version) string { + if version.Name == "Unreleased" { + return "## Unreleased\n\n" + } + return fmt.Sprintf("\n## %s (%s)\n\n", version.Name, version.Date.Format("2006-01-02")) +} + +// generateRawVersionContent generates the raw content (PRs + commits) for a version +func (g *Generator) generateRawVersionContent(version *git.Version) string { + var sb strings.Builder + + // Build a set of commit SHAs that are part of fetched PRs + prCommitSHAs := make(map[string]bool) + for _, prNum := range version.PRNumbers { + if pr, exists := g.prs[prNum]; exists { + for _, prCommit := range pr.Commits { + prCommitSHAs[prCommit.SHA] = true + } + } + } + + prCommits := make(map[int][]*git.Commit) + directCommits := []*git.Commit{} + + for _, commit := range version.Commits { + // Skip version bump commits from output + if commit.IsVersion { + continue + } + + // If this commit is part of a fetched PR, don't include it in direct commits + if prCommitSHAs[commit.SHA] { + continue + } + + if commit.PRNumber > 0 { + prCommits[commit.PRNumber] = append(prCommits[commit.PRNumber], commit) + } else { + directCommits = append(directCommits, commit) + } + } + + // There are occasionally no PRs or direct commits other than version bumps, so we handle that gracefully + if len(prCommits) == 0 && len(directCommits) == 0 { + return "" + } + + prependNewline := "" + for _, prNum := range version.PRNumbers { + if pr, exists := g.prs[prNum]; exists { + sb.WriteString(prependNewline) + sb.WriteString(g.formatPR(pr)) + prependNewline = "\n" + } + } + + if len(directCommits) > 0 { + // Sort direct commits by date (newest first) for consistent ordering + sort.Slice(directCommits, func(i, j int) bool { + return directCommits[i].Date.After(directCommits[j].Date) + }) + + sb.WriteString(prependNewline + "### Direct commits\n\n") + for _, commit := range directCommits { + message := g.formatCommitMessage(strings.TrimSpace(commit.Message)) + if message != "" && !g.isDuplicateMessage(message, directCommits) { + sb.WriteString(fmt.Sprintf("- %s\n", message)) + } + } + } + + return fixMarkdown( + strings.ReplaceAll(sb.String(), "\n-\n", "\n"), // Remove empty list items + ) +} + +func fixMarkdown(content string) string { + + // Fix MD032/blank-around-lists: Lists should be surrounded by blank lines + lines := strings.Split(content, "\n") + inList := false + preListNewline := false + for i := range lines { + line := strings.TrimSpace(lines[i]) + if strings.HasPrefix(line, "- ") || strings.HasPrefix(line, "* ") { + if !inList { + inList = true + // Ensure there's a blank line before the list starts + if !preListNewline && i > 0 && lines[i-1] != "" { + line = "\n" + line + preListNewline = true + } + } + } else { + if inList { + inList = false + preListNewline = false + } + } + lines[i] = strings.TrimRight(line, " \t") + } + + fixedContent := strings.TrimSpace(strings.Join(lines, "\n")) + + return fixedContent + "\n" +} + +func (g *Generator) formatPR(pr *github.PR) string { + var sb strings.Builder + + pr.Title = strings.TrimRight(strings.TrimSpace(pr.Title), ".") + + // Add type indicator for non-users + authorName := pr.Author + switch pr.AuthorType { + case "bot": + authorName += "[bot]" + case "organization": + authorName += "[org]" + } + + sb.WriteString(fmt.Sprintf("### PR [#%d](%s) by [%s](%s): %s\n\n", + pr.Number, pr.URL, authorName, pr.AuthorURL, strings.TrimSpace(pr.Title))) + + changes := g.extractChanges(pr) + for _, change := range changes { + if change != "" { + sb.WriteString(fmt.Sprintf("- %s\n", change)) + } + } + + return sb.String() +} + +func (g *Generator) extractChanges(pr *github.PR) []string { + var changes []string + seen := make(map[string]bool) + + for _, commit := range pr.Commits { + message := g.formatCommitMessage(commit.Message) + if message != "" && !seen[message] { + seen[message] = true + changes = append(changes, message) + } + } + + if len(changes) == 0 && pr.Body != "" { + lines := strings.Split(pr.Body, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "- ") || strings.HasPrefix(line, "* ") { + change := strings.TrimPrefix(strings.TrimPrefix(line, "- "), "* ") + if change != "" { + changes = append(changes, change) + } + } + } + } + + return changes +} + +func normalizeLineEndings(content string) string { + return strings.ReplaceAll(content, "\r\n", "\n") +} + +func (g *Generator) formatCommitMessage(message string) string { + prefixes := []string{"fix:", "feat:", "docs:", "style:", "refactor:", + "test:", "chore:", "perf:", "ci:", "build:", "revert:", "# docs:"} + strings_to_remove := []string{ + "### CHANGES\n", "## CHANGES\n", "# CHANGES\n", + "...\n", "---\n", "## Changes\n", "## Change", + "Update version to v..1 and commit\n", + "# What this Pull Request (PR) does\n", + "# Conflicts:", + } + + message = normalizeLineEndings(message) + // No hard tabs + message = strings.ReplaceAll(message, "\t", " ") + + for _, prefix := range prefixes { + if strings.HasPrefix(strings.ToLower(message), prefix) { + message = strings.TrimSpace(message[len(prefix):]) + break + } + } + + if len(message) > 0 { + message = strings.ToUpper(message[:1]) + message[1:] + } + + for _, str := range strings_to_remove { + if strings.Contains(message, str) { + message = strings.ReplaceAll(message, str, "") + } + } + + message = fixFormatting(message) + + return message +} + +func fixFormatting(message string) string { + // Turn "*"" lists into "-" lists" + message = strings.ReplaceAll(message, "* ", "- ") + // Remove extra spaces around dashes + message = strings.ReplaceAll(message, "- ", "- ") + message = strings.ReplaceAll(message, "- ", "- ") + // turn bare URL into + if strings.Contains(message, "http://") || strings.Contains(message, "https://") { + // Use regex to wrap bare URLs with angle brackets + urlRegex := regexp.MustCompile(`\b(https?://[^\s<>]+)`) + message = urlRegex.ReplaceAllString(message, "<$1>") + } + + // Replace "## LINKS\n" with "- " + message = strings.ReplaceAll(message, "## LINKS\n", "- ") + // Dependabot messages: "- [Commits]" should become "\n- [Commits]" + message = strings.TrimSpace(message) + // Turn multiple newlines into a single newline + message = strings.TrimSpace(strings.ReplaceAll(message, "\n\n", "\n")) + // Fix inline trailing spaces + message = strings.ReplaceAll(message, " \n", "\n") + // Fix weird indent before list, + message = strings.ReplaceAll(message, "\n - ", "\n- ") + + // blanks-around-lists MD032 fix + // Use regex to ensure blank line before list items that don't already have one + listRegex := regexp.MustCompile(`(?m)([^\n-].*[^:\n])\n([-*] .*)`) + message = listRegex.ReplaceAllString(message, "$1\n\n$2") + + // Change random first-level "#" to 4th level "####" + // This is a hack to fix spurious first-level headings that are not actual headings + // but rather just comments or notes in the commit message. + message = strings.ReplaceAll(message, "# ", "\n#### ") + message = strings.ReplaceAll(message, "\n\n\n", "\n\n") + + // Wrap any non-wrapped Emails with angle brackets + emailRegex := regexp.MustCompile(`([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})`) + message = emailRegex.ReplaceAllString(message, "<$1>") + + // Wrap any non-wrapped URLs with angle brackets + urlRegex := regexp.MustCompile(`(https?://[^\s<]+)`) + message = urlRegex.ReplaceAllString(message, "<$1>") + + message = strings.ReplaceAll(message, "<<", "<") + message = strings.ReplaceAll(message, ">>", ">") + + // Fix some spurious Issue/PR links at the beginning of a commit message line + prOrIssueLinkRegex := regexp.MustCompile("\n" + `(#\d+)`) + message = prOrIssueLinkRegex.ReplaceAllString(message, " $1") + + // Remove leading/trailing whitespace + message = strings.TrimSpace(message) + return message +} + +func (g *Generator) isDuplicateMessage(message string, commits []*git.Commit) bool { + if message == "." || strings.ToLower(message) == "fix" { + count := 0 + for _, commit := range commits { + formatted := g.formatCommitMessage(commit.Message) + if formatted == message { + count++ + if count > 1 { + return true + } + } + } + } + return false +} + +// hashContent generates a SHA256 hash of the content for change detection +func hashContent(content string) string { + hash := sha256.Sum256([]byte(content)) + return fmt.Sprintf("%x", hash) +} diff --git a/cmd/generate_changelog/internal/changelog/summarize.go b/cmd/generate_changelog/internal/changelog/summarize.go new file mode 100644 index 00000000..93f8558b --- /dev/null +++ b/cmd/generate_changelog/internal/changelog/summarize.go @@ -0,0 +1,58 @@ +package changelog + +import ( + "fmt" + "os" + "os/exec" + "strings" +) + +const DefaultSummarizeModel = "claude-sonnet-4-20250514" +const MinContentLength = 256 // Minimum content length to consider for summarization + +// getSummarizeModel returns the model to use for AI summarization +func getSummarizeModel() string { + if model := os.Getenv("FABRIC_CHANGELOG_SUMMARIZE_MODEL"); model != "" { + return model + } + return DefaultSummarizeModel +} + +// SummarizeVersionContent takes raw version content and returns AI-enhanced summary +func SummarizeVersionContent(content string) (string, error) { + if strings.TrimSpace(content) == "" { + return "", fmt.Errorf("no content to summarize") + } + if len(content) < MinContentLength { + // If content is too brief, return it as is + return content, nil + } + + model := getSummarizeModel() + + prompt := `Summarize the changes extracted from Git commit logs in a concise, professional way. +Pay particular attention to the following rules: +- Preserve the PR headers verbatim to your summary. +- I REPEAT: Do not change the PR headers in any way. They contain links to the PRs and Author Profiles. +- Use bullet points for lists and key changes (rendered using "-") +- Focus on the main changes and improvements. +- Avoid unnecessary details or preamble. +- Keep it under 800 characters. +- Be brief. List only the 5 most important changes along with the PR information which should be kept intact. +- If the content is too brief or you do not see any PR headers, return the content as is.` + + cmd := exec.Command("fabric", "-m", model, prompt) + cmd.Stdin = strings.NewReader(content) + + output, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("fabric command failed: %w", err) + } + + summary := strings.TrimSpace(string(output)) + if summary == "" { + return "", fmt.Errorf("fabric returned empty summary") + } + + return summary, nil +} diff --git a/cmd/generate_changelog/internal/config/config.go b/cmd/generate_changelog/internal/config/config.go new file mode 100644 index 00000000..6c7f9d60 --- /dev/null +++ b/cmd/generate_changelog/internal/config/config.go @@ -0,0 +1,15 @@ +package config + +type Config struct { + RepoPath string + OutputFile string + Limit int + Version string + SaveData bool + CacheFile string + NoCache bool + RebuildCache bool + GitHubToken string + ForcePRSync bool + EnableAISummary bool +} diff --git a/cmd/generate_changelog/internal/git/types.go b/cmd/generate_changelog/internal/git/types.go new file mode 100644 index 00000000..d6105534 --- /dev/null +++ b/cmd/generate_changelog/internal/git/types.go @@ -0,0 +1,26 @@ +package git + +import ( + "time" +) + +type Commit struct { + SHA string + Message string + Author string + Email string + Date time.Time + IsMerge bool + PRNumber int + IsVersion bool + Version string +} + +type Version struct { + Name string + Date time.Time + CommitSHA string + Commits []*Commit + PRNumbers []int + AISummary string +} diff --git a/cmd/generate_changelog/internal/git/walker.go b/cmd/generate_changelog/internal/git/walker.go new file mode 100644 index 00000000..693753f0 --- /dev/null +++ b/cmd/generate_changelog/internal/git/walker.go @@ -0,0 +1,295 @@ +package git + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" +) + +var ( + versionPattern = regexp.MustCompile(`Update version to (v\d+\.\d+\.\d+)`) + prPattern = regexp.MustCompile(`Merge pull request #(\d+)`) +) + +type Walker struct { + repo *git.Repository +} + +func NewWalker(repoPath string) (*Walker, error) { + repo, err := git.PlainOpen(repoPath) + if err != nil { + return nil, fmt.Errorf("failed to open repository: %w", err) + } + + return &Walker{repo: repo}, nil +} + +// GetLatestTag returns the name of the most recent tag by committer date +func (w *Walker) GetLatestTag() (string, error) { + tagRefs, err := w.repo.Tags() + if err != nil { + return "", err + } + + var latestTagCommit *object.Commit + var latestTagName string + + err = tagRefs.ForEach(func(tagRef *plumbing.Reference) error { + revision := plumbing.Revision(tagRef.Name().String()) + tagCommitHash, err := w.repo.ResolveRevision(revision) + if err != nil { + return err + } + + commit, err := w.repo.CommitObject(*tagCommitHash) + if err != nil { + return err + } + + if latestTagCommit == nil { + latestTagCommit = commit + latestTagName = tagRef.Name().Short() // Get short name like "v1.4.245" + } + + if commit.Committer.When.After(latestTagCommit.Committer.When) { + latestTagCommit = commit + latestTagName = tagRef.Name().Short() + } + + return nil + }) + if err != nil { + return "", err + } + + return latestTagName, nil +} + +// WalkCommitsSinceTag walks commits from the specified tag to HEAD and returns only "Unreleased" version +func (w *Walker) WalkCommitsSinceTag(tagName string) (*Version, error) { + // Get the tag reference + tagRef, err := w.repo.Tag(tagName) + if err != nil { + return nil, fmt.Errorf("failed to find tag %s: %w", tagName, err) + } + + // Get the commit that the tag points to + tagCommit, err := w.repo.CommitObject(tagRef.Hash()) + if err != nil { + return nil, fmt.Errorf("failed to get tag commit: %w", err) + } + + // Get HEAD + headRef, err := w.repo.Head() + if err != nil { + return nil, fmt.Errorf("failed to get HEAD: %w", err) + } + + // Walk from HEAD back to the tag commit (exclusive) + commitIter, err := w.repo.Log(&git.LogOptions{ + From: headRef.Hash(), + Order: git.LogOrderCommitterTime, + }) + if err != nil { + return nil, fmt.Errorf("failed to get commit log: %w", err) + } + + version := &Version{ + Name: "Unreleased", + Commits: []*Commit{}, + } + + prNumbers := []int{} + + err = commitIter.ForEach(func(c *object.Commit) error { + // Stop when we reach the tag commit (don't include it) + if c.Hash == tagCommit.Hash { + return fmt.Errorf("reached tag commit") // Use error to break out of iteration + } + + commit := &Commit{ + SHA: c.Hash.String(), + Message: strings.TrimSpace(c.Message), + Date: c.Committer.When, + } + + // Check for version patterns + if versionMatch := versionPattern.FindStringSubmatch(commit.Message); versionMatch != nil { + commit.IsVersion = true + } + + // Check for PR merge patterns + if prMatch := prPattern.FindStringSubmatch(commit.Message); prMatch != nil { + if prNumber, err := strconv.Atoi(prMatch[1]); err == nil { + commit.PRNumber = prNumber + prNumbers = append(prNumbers, prNumber) + } + } + + version.Commits = append(version.Commits, commit) + return nil + }) + + // Ignore the "reached tag commit" error - it's expected + if err != nil && !strings.Contains(err.Error(), "reached tag commit") { + return nil, fmt.Errorf("failed to walk commits: %w", err) + } + + // Remove duplicates from prNumbers and set them + prNumbersMap := make(map[int]bool) + for _, prNum := range prNumbers { + prNumbersMap[prNum] = true + } + + version.PRNumbers = make([]int, 0, len(prNumbersMap)) + for prNum := range prNumbersMap { + version.PRNumbers = append(version.PRNumbers, prNum) + } + + return version, nil +} + +func (w *Walker) WalkHistory() (map[string]*Version, error) { + ref, err := w.repo.Head() + if err != nil { + return nil, fmt.Errorf("failed to get HEAD: %w", err) + } + + commitIter, err := w.repo.Log(&git.LogOptions{ + From: ref.Hash(), + Order: git.LogOrderCommitterTime, + }) + if err != nil { + return nil, fmt.Errorf("failed to get commit log: %w", err) + } + + versions := make(map[string]*Version) + currentVersion := "Unreleased" + versions[currentVersion] = &Version{ + Name: currentVersion, + Commits: []*Commit{}, + } + + prNumbers := make(map[string][]int) + + err = commitIter.ForEach(func(c *object.Commit) error { + // c.Message = Summarize(c.Message) + commit := &Commit{ + SHA: c.Hash.String(), + Message: strings.TrimSpace(c.Message), + Author: c.Author.Name, + Email: c.Author.Email, + Date: c.Author.When, + IsMerge: len(c.ParentHashes) > 1, + } + + if matches := versionPattern.FindStringSubmatch(commit.Message); len(matches) > 1 { + commit.IsVersion = true + commit.Version = matches[1] + currentVersion = commit.Version + + if _, exists := versions[currentVersion]; !exists { + versions[currentVersion] = &Version{ + Name: currentVersion, + Date: commit.Date, + CommitSHA: commit.SHA, + Commits: []*Commit{}, + } + } + return nil + } + + if matches := prPattern.FindStringSubmatch(commit.Message); len(matches) > 1 { + prNumber := 0 + fmt.Sscanf(matches[1], "%d", &prNumber) + commit.PRNumber = prNumber + + prNumbers[currentVersion] = append(prNumbers[currentVersion], prNumber) + } + + versions[currentVersion].Commits = append(versions[currentVersion].Commits, commit) + + return nil + }) + + if err != nil { + return nil, fmt.Errorf("failed to walk commits: %w", err) + } + + for version, prs := range prNumbers { + versions[version].PRNumbers = dedupInts(prs) + } + + return versions, nil +} + +func (w *Walker) GetRepoInfo() (owner string, name string, err error) { + remotes, err := w.repo.Remotes() + if err != nil { + return "", "", fmt.Errorf("failed to get remotes: %w", err) + } + + // First try upstream (preferred for forks) + for _, remote := range remotes { + if remote.Config().Name == "upstream" { + urls := remote.Config().URLs + if len(urls) > 0 { + owner, name = parseGitHubURL(urls[0]) + if owner != "" && name != "" { + return owner, name, nil + } + } + } + } + + // Then try origin + for _, remote := range remotes { + if remote.Config().Name == "origin" { + urls := remote.Config().URLs + if len(urls) > 0 { + owner, name = parseGitHubURL(urls[0]) + if owner != "" && name != "" { + return owner, name, nil + } + } + } + } + + return "danielmiessler", "fabric", nil +} + +func parseGitHubURL(url string) (owner, repo string) { + patterns := []string{ + `github\.com[:/]([^/]+)/([^/.]+)`, + `github\.com[:/]([^/]+)/([^/]+)\.git$`, + } + + for _, pattern := range patterns { + re := regexp.MustCompile(pattern) + matches := re.FindStringSubmatch(url) + if len(matches) > 2 { + return matches[1], matches[2] + } + } + + return "", "" +} + +func dedupInts(ints []int) []int { + seen := make(map[int]bool) + result := []int{} + + for _, i := range ints { + if !seen[i] { + seen[i] = true + result = append(result, i) + } + } + + return result +} diff --git a/cmd/generate_changelog/internal/github/client.go b/cmd/generate_changelog/internal/github/client.go new file mode 100644 index 00000000..6ebbbcf8 --- /dev/null +++ b/cmd/generate_changelog/internal/github/client.go @@ -0,0 +1,354 @@ +package github + +import ( + "context" + "fmt" + "net/http" + "os" + "strings" + "sync" + "time" + + "github.com/google/go-github/v66/github" + "github.com/hasura/go-graphql-client" + "golang.org/x/oauth2" +) + +type Client struct { + client *github.Client + graphqlClient *graphql.Client + owner string + repo string + token string +} + +func NewClient(token, owner, repo string) *Client { + var githubClient *github.Client + var httpClient *http.Client + var gqlClient *graphql.Client + + if token != "" { + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + httpClient = oauth2.NewClient(context.Background(), ts) + githubClient = github.NewClient(httpClient) + gqlClient = graphql.NewClient("https://api.github.com/graphql", httpClient) + } else { + httpClient = http.DefaultClient + githubClient = github.NewClient(nil) + gqlClient = graphql.NewClient("https://api.github.com/graphql", httpClient) + } + + return &Client{ + client: githubClient, + graphqlClient: gqlClient, + owner: owner, + repo: repo, + token: token, + } +} + +func (c *Client) FetchPRs(prNumbers []int) ([]*PR, error) { + if len(prNumbers) == 0 { + return []*PR{}, nil + } + + ctx := context.Background() + prs := make([]*PR, 0, len(prNumbers)) + prsChan := make(chan *PR, len(prNumbers)) + errChan := make(chan error, len(prNumbers)) + + var wg sync.WaitGroup + semaphore := make(chan struct{}, 10) + + for _, prNumber := range prNumbers { + wg.Add(1) + go func(num int) { + defer wg.Done() + + semaphore <- struct{}{} + defer func() { <-semaphore }() + + pr, err := c.fetchSinglePR(ctx, num) + if err != nil { + errChan <- fmt.Errorf("failed to fetch PR #%d: %w", num, err) + return + } + prsChan <- pr + }(prNumber) + } + + go func() { + wg.Wait() + close(prsChan) + close(errChan) + }() + + var errors []error + for pr := range prsChan { + prs = append(prs, pr) + } + for err := range errChan { + errors = append(errors, err) + } + + if len(errors) > 0 { + return prs, fmt.Errorf("some PRs failed to fetch: %v", errors) + } + + return prs, nil +} + +func (c *Client) fetchSinglePR(ctx context.Context, prNumber int) (*PR, error) { + pr, _, err := c.client.PullRequests.Get(ctx, c.owner, c.repo, prNumber) + if err != nil { + return nil, err + } + + commits, _, err := c.client.PullRequests.ListCommits(ctx, c.owner, c.repo, prNumber, nil) + if err != nil { + return nil, fmt.Errorf("failed to fetch commits: %w", err) + } + + result := &PR{ + Number: prNumber, + Title: getString(pr.Title), + Body: getString(pr.Body), + URL: getString(pr.HTMLURL), + Commits: make([]PRCommit, 0, len(commits)), + } + + if pr.MergedAt != nil { + result.MergedAt = pr.MergedAt.Time + } + + if pr.User != nil { + result.Author = getString(pr.User.Login) + result.AuthorURL = getString(pr.User.HTMLURL) + userType := getString(pr.User.Type) // GitHub API returns "User", "Organization", or "Bot" + + // Convert GitHub API type to lowercase + switch userType { + case "User": + result.AuthorType = "user" + case "Organization": + result.AuthorType = "organization" + case "Bot": + result.AuthorType = "bot" + default: + result.AuthorType = "user" // Default fallback + } + } + + if pr.MergeCommitSHA != nil { + result.MergeCommit = *pr.MergeCommitSHA + } + + for _, commit := range commits { + if commit.Commit != nil { + prCommit := PRCommit{ + SHA: getString(commit.SHA), + Message: strings.TrimSpace(getString(commit.Commit.Message)), + } + if commit.Commit.Author != nil { + prCommit.Author = getString(commit.Commit.Author.Name) + } + result.Commits = append(result.Commits, prCommit) + } + } + + return result, nil +} + +func getString(s *string) string { + if s == nil { + return "" + } + return *s +} + +// FetchAllMergedPRs fetches all merged PRs using GitHub's search API +// This is much more efficient than fetching PRs individually +func (c *Client) FetchAllMergedPRs(since time.Time) ([]*PR, error) { + ctx := context.Background() + var allPRs []*PR + + // Build search query for merged PRs + query := fmt.Sprintf("repo:%s/%s is:pr is:merged", c.owner, c.repo) + if !since.IsZero() { + query += fmt.Sprintf(" merged:>=%s", since.Format("2006-01-02")) + } + + opts := &github.SearchOptions{ + Sort: "created", + Order: "desc", + ListOptions: github.ListOptions{ + PerPage: 100, // Maximum allowed + }, + } + + for { + result, resp, err := c.client.Search.Issues(ctx, query, opts) + if err != nil { + return allPRs, fmt.Errorf("failed to search PRs: %w", err) + } + + // Process PRs in parallel + prsChan := make(chan *PR, len(result.Issues)) + errChan := make(chan error, len(result.Issues)) + var wg sync.WaitGroup + semaphore := make(chan struct{}, 10) // Limit concurrent requests + + for _, issue := range result.Issues { + if issue.PullRequestLinks == nil { + continue // Not a PR + } + + wg.Add(1) + go func(prNumber int) { + defer wg.Done() + + semaphore <- struct{}{} + defer func() { <-semaphore }() + + pr, err := c.fetchSinglePR(ctx, prNumber) + if err != nil { + errChan <- fmt.Errorf("failed to fetch PR #%d: %w", prNumber, err) + return + } + prsChan <- pr + }(*issue.Number) + } + + go func() { + wg.Wait() + close(prsChan) + close(errChan) + }() + + // Collect results + for pr := range prsChan { + allPRs = append(allPRs, pr) + } + + // Check for errors + for err := range errChan { + // Log error but continue processing + fmt.Fprintf(os.Stderr, "Warning: %v\n", err) + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + + return allPRs, nil +} + +// FetchAllMergedPRsGraphQL fetches all merged PRs with their commits using GraphQL +// This is the ultimate optimization - gets everything in ~5-10 API calls +func (c *Client) FetchAllMergedPRsGraphQL(since time.Time) ([]*PR, error) { + ctx := context.Background() + var allPRs []*PR + var after *string + totalFetched := 0 + + for { + // Prepare variables + variables := map[string]interface{}{ + "owner": graphql.String(c.owner), + "repo": graphql.String(c.repo), + "after": (*graphql.String)(after), + } + + // Execute GraphQL query + var query PullRequestsQuery + err := c.graphqlClient.Query(ctx, &query, variables) + if err != nil { + return allPRs, fmt.Errorf("GraphQL query failed: %w", err) + } + + prs := query.Repository.PullRequests.Nodes + fmt.Fprintf(os.Stderr, "Fetched %d PRs via GraphQL (page %d)\n", len(prs), (totalFetched/100)+1) + + // Convert GraphQL PRs to our PR struct + for _, gqlPR := range prs { + // If we have a since filter, stop when we reach older PRs + if !since.IsZero() && gqlPR.MergedAt.Before(since) { + fmt.Fprintf(os.Stderr, "Reached PRs older than %s, stopping\n", since.Format("2006-01-02")) + return allPRs, nil + } + + pr := &PR{ + Number: gqlPR.Number, + Title: gqlPR.Title, + Body: gqlPR.Body, + URL: gqlPR.URL, + MergedAt: gqlPR.MergedAt, + Commits: make([]PRCommit, 0, len(gqlPR.Commits.Nodes)), + } + + // Handle author - check if it's nil first + if gqlPR.Author != nil { + pr.Author = gqlPR.Author.Login + pr.AuthorURL = gqlPR.Author.URL + + switch gqlPR.Author.Typename { + case "Bot": + pr.AuthorType = "bot" + case "Organization": + pr.AuthorType = "organization" + case "User": + pr.AuthorType = "user" + default: + pr.AuthorType = "user" // fallback + if gqlPR.Author.Typename != "" { + fmt.Fprintf(os.Stderr, "PR #%d: Unknown author typename '%s'\n", gqlPR.Number, gqlPR.Author.Typename) + } + } + } else { + // Author is nil - try to fetch from REST API as fallback + fmt.Fprintf(os.Stderr, "PR #%d: Author is nil in GraphQL response, fetching from REST API\n", gqlPR.Number) + + // Fetch this specific PR from REST API + restPR, err := c.fetchSinglePR(ctx, gqlPR.Number) + if err == nil && restPR != nil && restPR.Author != "" { + pr.Author = restPR.Author + pr.AuthorURL = restPR.AuthorURL + pr.AuthorType = restPR.AuthorType + } else { + // Fallback if REST API also fails + pr.Author = "[unknown]" + pr.AuthorURL = "" + pr.AuthorType = "user" + } + } + + // Convert commits + for _, commitNode := range gqlPR.Commits.Nodes { + commit := PRCommit{ + SHA: commitNode.Commit.OID, + Message: strings.TrimSpace(commitNode.Commit.Message), + Author: commitNode.Commit.Author.Name, + } + pr.Commits = append(pr.Commits, commit) + } + + allPRs = append(allPRs, pr) + } + + totalFetched += len(prs) + + // Check if we need to fetch more pages + if !query.Repository.PullRequests.PageInfo.HasNextPage { + break + } + + after = &query.Repository.PullRequests.PageInfo.EndCursor + } + + fmt.Fprintf(os.Stderr, "Total PRs fetched via GraphQL: %d\n", len(allPRs)) + return allPRs, nil +} diff --git a/cmd/generate_changelog/internal/github/types.go b/cmd/generate_changelog/internal/github/types.go new file mode 100644 index 00000000..473a074a --- /dev/null +++ b/cmd/generate_changelog/internal/github/types.go @@ -0,0 +1,57 @@ +package github + +import "time" + +type PR struct { + Number int + Title string + Body string + Author string + AuthorURL string + AuthorType string // "user", "organization", or "bot" + URL string + MergedAt time.Time + Commits []PRCommit + MergeCommit string +} + +type PRCommit struct { + SHA string + Message string + Author string +} + +// GraphQL query structures for hasura client +type PullRequestsQuery struct { + Repository struct { + PullRequests struct { + PageInfo struct { + HasNextPage bool + EndCursor string + } + Nodes []struct { + Number int + Title string + Body string + URL string + MergedAt time.Time + Author *struct { + Typename string `graphql:"__typename"` + Login string `graphql:"login"` + URL string `graphql:"url"` + } + Commits struct { + Nodes []struct { + Commit struct { + OID string `graphql:"oid"` + Message string + Author struct { + Name string + } + } + } + } `graphql:"commits(first: 250)"` + } + } `graphql:"pullRequests(first: 100, after: $after, states: MERGED, orderBy: {field: UPDATED_AT, direction: DESC})"` + } `graphql:"repository(owner: $owner, name: $repo)"` +} diff --git a/cmd/generate_changelog/main.go b/cmd/generate_changelog/main.go new file mode 100644 index 00000000..6865a2ef --- /dev/null +++ b/cmd/generate_changelog/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/danielmiessler/fabric/cmd/generate_changelog/internal/changelog" + "github.com/danielmiessler/fabric/cmd/generate_changelog/internal/config" + "github.com/joho/godotenv" + "github.com/spf13/cobra" +) + +var ( + cfg = &config.Config{} +) + +var rootCmd = &cobra.Command{ + Use: "generate_changelog", + Short: "Generate changelog from git history and GitHub PRs", + Long: `A high-performance changelog generator that walks git history, +collects version information and pull requests, and generates a +comprehensive changelog in markdown format.`, + RunE: run, +} + +func init() { + rootCmd.Flags().StringVarP(&cfg.RepoPath, "repo", "r", ".", "Repository path") + rootCmd.Flags().StringVarP(&cfg.OutputFile, "output", "o", "", "Output file (default: stdout)") + rootCmd.Flags().IntVarP(&cfg.Limit, "limit", "l", 0, "Limit number of versions (0 = all)") + rootCmd.Flags().StringVarP(&cfg.Version, "version", "v", "", "Generate changelog for specific version") + rootCmd.Flags().BoolVar(&cfg.SaveData, "save-data", false, "Save version data to JSON for debugging") + rootCmd.Flags().StringVar(&cfg.CacheFile, "cache", "./cmd/generate_changelog/changelog.db", "Cache database file") + rootCmd.Flags().BoolVar(&cfg.NoCache, "no-cache", false, "Disable cache usage") + rootCmd.Flags().BoolVar(&cfg.RebuildCache, "rebuild-cache", false, "Rebuild cache from scratch") + rootCmd.Flags().StringVar(&cfg.GitHubToken, "token", "", "GitHub API token (or set GITHUB_TOKEN env var)") + rootCmd.Flags().BoolVar(&cfg.ForcePRSync, "force-pr-sync", false, "Force a full PR sync from GitHub (ignores cache age)") + rootCmd.Flags().BoolVar(&cfg.EnableAISummary, "ai-summarize", false, "Generate AI-enhanced summaries using Fabric") +} + +func run(cmd *cobra.Command, args []string) error { + if cfg.GitHubToken == "" { + cfg.GitHubToken = os.Getenv("GITHUB_TOKEN") + } + + generator, err := changelog.New(cfg) + if err != nil { + return fmt.Errorf("failed to create changelog generator: %w", err) + } + + output, err := generator.Generate() + if err != nil { + return fmt.Errorf("failed to generate changelog: %w", err) + } + + if cfg.OutputFile != "" { + if err := os.WriteFile(cfg.OutputFile, []byte(output), 0644); err != nil { + return fmt.Errorf("failed to write output file: %w", err) + } + fmt.Printf("Changelog written to %s\n", cfg.OutputFile) + } else { + fmt.Print(output) + } + + return nil +} + +func main() { + // Load .env file from the same directory as the binary + if exePath, err := os.Executable(); err == nil { + envPath := filepath.Join(filepath.Dir(exePath), ".env") + if _, err := os.Stat(envPath); err == nil { + // .env file exists, load it + if err := godotenv.Load(envPath); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to load .env file: %v\n", err) + } + } + } + + if err := rootCmd.Execute(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index 7d0d5e58..6ba13a0d 100644 --- a/go.mod +++ b/go.mod @@ -16,17 +16,21 @@ require ( github.com/go-git/go-git/v5 v5.16.2 github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612 github.com/google/generative-ai-go v0.20.1 + github.com/google/go-github/v66 v66.0.0 + github.com/hasura/go-graphql-client v0.14.4 github.com/jessevdk/go-flags v1.6.1 github.com/joho/godotenv v1.5.1 + github.com/mattn/go-sqlite3 v1.14.28 github.com/ollama/ollama v0.9.0 github.com/openai/openai-go v1.8.2 github.com/otiai10/copy v1.14.1 github.com/pkg/errors v0.9.1 github.com/samber/lo v1.50.0 github.com/sgaunet/perplexity-go/v2 v2.8.0 + github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 golang.org/x/oauth2 v0.30.0 - golang.org/x/text v0.26.0 + golang.org/x/text v0.27.0 google.golang.org/api v0.236.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -59,6 +63,7 @@ require ( github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/cloudwego/base64x v0.1.5 // indirect + github.com/coder/websocket v1.8.13 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect @@ -75,10 +80,12 @@ require ( github.com/goccy/go-json v0.10.5 // indirect github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -89,10 +96,11 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/pjbgf/sha1cd v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/skeema/knownhosts v1.3.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -108,9 +116,10 @@ require ( go.opentelemetry.io/otel/trace v1.36.0 // indirect golang.org/x/arch v0.18.0 // indirect golang.org/x/crypto v0.39.0 // indirect + golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect golang.org/x/net v0.41.0 // indirect - golang.org/x/sync v0.15.0 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.34.0 // indirect golang.org/x/time v0.12.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect diff --git a/go.sum b/go.sum index 8ce3fa93..adc68afd 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,9 @@ github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZ github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= +github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -125,9 +128,14 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/generative-ai-go v0.20.1 h1:6dEIujpgN2V0PgLhr6c/M1ynRdc7ARtiIDPFzj45uNQ= github.com/google/generative-ai-go v0.20.1/go.mod h1:TjOnZJmZKzarWbjUJgy+r3Ee7HGBRVLhOIgupnwR4Bg= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-github/v66 v66.0.0 h1:ADJsaXj9UotwdgK8/iFZtv7MLc8E8WBl62WLd/D/9+M= +github.com/google/go-github/v66 v66.0.0/go.mod h1:+4SO9Zkuyf8ytMj0csN1NR/5OTR+MfqPp8P8dVlcvY4= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= @@ -137,6 +145,10 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= +github.com/hasura/go-graphql-client v0.14.4 h1:bYU7/+V50T2YBGdNQXt6l4f2cMZPECPUd8cyCR+ixtw= +github.com/hasura/go-graphql-client v0.14.4/go.mod h1:jfSZtBER3or+88Q9vFhWHiFMPppfYILRyl+0zsgPIIw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= @@ -163,6 +175,8 @@ github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjS github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -180,8 +194,8 @@ github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= -github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pjbgf/sha1cd v0.4.0 h1:NXzbL1RvjTUi6kgYZCX3fPwwl27Q1LJndxtUDVfJGRY= +github.com/pjbgf/sha1cd v0.4.0/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -189,6 +203,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= @@ -199,6 +214,10 @@ github.com/sgaunet/perplexity-go/v2 v2.8.0/go.mod h1:MSks4RNuivCi0GqJyylhFdgSJFV github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -255,8 +274,8 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= -golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= +golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA= +golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -283,8 +302,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -301,8 +320,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -324,8 +343,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -335,6 +354,7 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0= google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4= google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= diff --git a/internal/plugins/ai/anthropic/oauth.go b/internal/plugins/ai/anthropic/oauth.go index adf301af..330eeee5 100644 --- a/internal/plugins/ai/anthropic/oauth.go +++ b/internal/plugins/ai/anthropic/oauth.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net/http" + "os" "os/exec" "strings" "time" @@ -71,7 +72,7 @@ func (t *OAuthTransport) getValidToken(tokenIdentifier string) (string, error) { } // If no token exists, run OAuth flow if token == nil { - fmt.Println("No OAuth token found, initiating authentication...") + fmt.Fprintln(os.Stderr, "No OAuth token found, initiating authentication...") newAccessToken, err := RunOAuthFlow(tokenIdentifier) if err != nil { return "", fmt.Errorf("failed to authenticate: %w", err) @@ -81,11 +82,11 @@ func (t *OAuthTransport) getValidToken(tokenIdentifier string) (string, error) { // Check if token needs refresh (5 minute buffer) if token.IsExpired(5) { - fmt.Println("OAuth token expired, refreshing...") + fmt.Fprintln(os.Stderr, "OAuth token expired, refreshing...") newAccessToken, err := RefreshToken(tokenIdentifier) if err != nil { // If refresh fails, try re-authentication - fmt.Println("Token refresh failed, re-authenticating...") + fmt.Fprintln(os.Stderr, "Token refresh failed, re-authenticating...") newAccessToken, err = RunOAuthFlow(tokenIdentifier) if err != nil { return "", fmt.Errorf("failed to refresh or re-authenticate: %w", err) @@ -137,13 +138,13 @@ func RunOAuthFlow(tokenIdentifier string) (token string, err error) { if err == nil && existingToken != nil { // If token exists but is expired, try refreshing first if existingToken.IsExpired(5) { - fmt.Println("Found expired OAuth token, attempting refresh...") + fmt.Fprintln(os.Stderr, "Found expired OAuth token, attempting refresh...") refreshedToken, refreshErr := RefreshToken(tokenIdentifier) if refreshErr == nil { - fmt.Println("Token refresh successful") + fmt.Fprintln(os.Stderr, "Token refresh successful") return refreshedToken, nil } - fmt.Printf("Token refresh failed (%v), proceeding with full OAuth flow...\n", refreshErr) + fmt.Fprintf(os.Stderr, "Token refresh failed (%v), proceeding with full OAuth flow...\n", refreshErr) } else { // Token exists and is still valid return existingToken.AccessToken, nil @@ -170,10 +171,10 @@ func RunOAuthFlow(tokenIdentifier string) (token string, err error) { oauth2.SetAuthURLParam("state", verifier), ) - fmt.Println("Open the following URL in your browser. Fabric would like to authorize:") - fmt.Println(authURL) + fmt.Fprintln(os.Stderr, "Open the following URL in your browser. Fabric would like to authorize:") + fmt.Fprintln(os.Stderr, authURL) openBrowser(authURL) - fmt.Print("Paste the authorization code here: ") + fmt.Fprint(os.Stderr, "Paste the authorization code here: ") var code string fmt.Scanln(&code) parts := strings.SplitN(code, "#", 2)