Compare commits

...

54 Commits

Author SHA1 Message Date
github-actions[bot]
7af6817bac chore(release): Update version to v1.4.293 2025-08-19 11:29:38 +00:00
Kayvan Sylvan
50ecc32d85 Merge pull request #1718 from ksylvan/0819-debug-log-levels
Implement Configurable Debug Logging Levels
2025-08-19 04:27:08 -07:00
Kayvan Sylvan
ff1ef380a7 feat: add --debug flag with levels and centralized logging
CHANGES
- Add --debug flag controlling runtime logging verbosity levels
- Introduce internal/log package with Off, Basic, Detailed, Trace
- Replace ad-hoc Debugf and globals with centralized debug logger
- Wire debug level during early CLI argument parsing
- Add bash, zsh, fish completions for --debug levels
- Document debug levels in README with usage examples
- Add comprehensive STT guide covering models, flags, workflows
- Simplify splitAudioFile signature and log ffmpeg chunking operations
- Remove FABRIC_STT_DEBUG environment variable and related code
- Clean minor code paths in vendors and template modules
2025-08-19 04:23:40 -07:00
github-actions[bot]
6a3a7e82d1 chore(release): Update version to v1.4.292 2025-08-19 00:55:22 +00:00
Kayvan Sylvan
34bc0b5e31 Merge pull request #1717 from ksylvan/0818-feature-default-model-indicator
Highlight default vendor/model in model listing
2025-08-18 17:52:57 -07:00
Kayvan Sylvan
ce59999503 feat: highlight default vendor/model in listings, pass registry defaults
CHANGES
- Update PrintWithVendor signature to accept default vendor and model
- Mark default vendor/model with asterisk in non-shell output
- Compare vendor and model case-insensitively when marking
- Pass registry defaults to PrintWithVendor from CLI
- Add test ensuring default selection appears with asterisk
- Keep shell completion output unchanged without default markers
2025-08-18 16:58:25 -07:00
Kayvan Sylvan
9bb4ccf740 docs: update version number in README updates section from v1.4.290 to v1.4.291 2025-08-18 08:13:55 -07:00
github-actions[bot]
900b13f08c chore(release): Update version to v1.4.291 2025-08-18 15:05:02 +00:00
Kayvan Sylvan
6824f0c0a7 Merge pull request #1715 from ksylvan/0818-openai-transcribe-using-openai-models
Add speech-to-text via OpenAI with transcription flags and completions
2025-08-18 08:02:36 -07:00
Kayvan Sylvan
a2481406db feat: add speech-to-text via OpenAI with transcription flags and completions
CHANGES
- Add --transcribe-file flag to transcribe audio or video
- Add --transcribe-model flag with model listing and completion
- Add --split-media-file flag to chunk files over 25MB
- Implement OpenAI transcription using Whisper and GPT-4o Transcribe
- Integrate transcription pipeline into CLI before readability processing
- Provide zsh, bash, fish completions for new transcription flags
- Validate media extensions and enforce 25MB upload limits
- Update README with release and corrected pattern link path
2025-08-18 07:59:50 -07:00
github-actions[bot]
171f7eb3ab chore(release): Update version to v1.4.290 2025-08-17 23:52:24 +00:00
Kayvan Sylvan
dccc70c433 Merge pull request #1714 from ksylvan/0817-simple-pattern-to-model-mapping-via-env-vars
Add Per-Pattern Model Mapping via Environment Variables
2025-08-17 16:49:46 -07:00
Kayvan Sylvan
e5ec9acfac feat: add per-pattern model mapping support via environment variables
• Add per-pattern model mapping documentation section
• Implement environment variable lookup for pattern-specific models
• Support vendor|model format in environment variable specification
• Check pattern-specific model when no model explicitly set
• Transform pattern names to uppercase environment variable format
• Add table of contents entry for new feature
• Enable shell startup file configuration for patterns
2025-08-17 16:15:23 -07:00
github-actions[bot]
f0eb9f90a3 chore(release): Update version to v1.4.289 2025-08-16 21:22:43 +00:00
Kayvan Sylvan
758425f98a Merge pull request #1710 from ksylvan/0816-no-variable-replacement-flag
Add `--no-variable-replacement` Flag for Literal Pattern Handling
2025-08-16 14:20:18 -07:00
Kayvan Sylvan
b4b5b0a4d9 feat: add --no-variable-replacement flag to disable pattern variable substitution
- Introduce CLI flag to skip pattern variable replacement.
- Wire flag into domain request and session builder.
- Avoid applying input variables when replacement is disabled.
- Provide PatternsEntity.GetWithoutVariables for input-only pattern processing support.
- Refactor patterns code into reusable load and apply helpers.
- Update bash, zsh, fish completions with new flag.
- Document flag in README and CLI help output.
- Add unit tests covering GetWithoutVariables path and behavior.
- Ensure {{input}} placeholder appends when missing in patterns.
2025-08-16 14:12:06 -07:00
github-actions[bot]
81a47ecab7 chore(release): Update version to v1.4.288 2025-08-16 16:19:42 +00:00
Kayvan Sylvan
0bce5c7b6e Merge pull request #1709 from ksylvan/0816-fix-youtube-transcripts
Enhanced YouTube Subtitle Language Fallback Handling
2025-08-16 09:17:09 -07:00
Kayvan Sylvan
992936dbd8 fix: improve YouTube subtitle language fallback handling in yt-dlp integration
- Fix typo "Gemmini" to "Gemini" in README
- Add "kballard" and "shellquote" to VSCode dictionary
- Add "YTDLP" to VSCode spell checker
- Enhance subtitle language options with fallback variants
- Build language options string with comma-separated alternatives
2025-08-16 09:14:03 -07:00
github-actions[bot]
48d74290f3 chore(release): Update version to v1.4.287 2025-08-16 07:29:23 +00:00
Kayvan Sylvan
3d4e967b92 Merge pull request #1706 from ksylvan/0814-readme-updates
Gemini Thinking Support and README (New Features) automation
2025-08-16 00:26:55 -07:00
Kayvan Sylvan
d8690c7cec feat: add release updates section and Gemini thinking support
- Add comprehensive "Recent Major Features" section to README
- Introduce new readme_updates Python script for automation
- Enable Gemini thinking configuration with token budgets
- Update CLI help text for Gemini thinking support
- Add comprehensive test coverage for Gemini thinking
- Create documentation for README update automation
- Reorganize README navigation structure with changelog section
2025-08-16 00:21:12 -07:00
github-actions[bot]
7eed9c3c64 chore(release): Update version to v1.4.286 2025-08-14 14:18:00 +00:00
Kayvan Sylvan
97b75cb153 Merge pull request #1700 from ksylvan/0813-thinking-flag-plus-suggest-pattern-overhault
Introduce Thinking Config Across Anthropic and OpenAI Providers
2025-08-14 07:15:40 -07:00
Kayvan Sylvan
b485a4584f refactor: extract token budget constants for thinking levels with validation bounds
## CHANGES

- Extract hardcoded token values into named constants
- Add comprehensive documentation for token budget purposes
- Implement token validation bounds (1-10000) in parsing
- Replace magic numbers with semantic constant references
- Improve code maintainability through constant extraction
2025-08-14 07:11:04 -07:00
Kayvan Sylvan
f4dbafc638 feat: add cross-provider --thinking flag mapping to Anthropic/OpenAI
CHANGES
- Add --thinking flag to set reasoning level cross-vendors
- Map Anthropic thinking levels and token budgets appropriately
- Translate OpenAI reasoning effort from thinking levels
- Propagate Thinking through ChatOptions, server, and dry-run output
- Update zsh, bash, fish completions with thinking choices
- Expand suggest_pattern docs with categories, workflows, usage examples
- Remove outdated suggest_pattern user files to avoid duplication
- Add VSCode dictionary terms: Anki, DMARC, wireframes
- Extend tests to include Thinking defaults in ChatOptions
2025-08-14 07:06:31 -07:00
github-actions[bot]
eae56e0038 chore(release): Update version to v1.4.285 2025-08-13 13:35:14 +00:00
Kayvan Sylvan
72a5e49855 Merge pull request #1698 from ksylvan/0812-claude-sonnet-1m-context
Enable One Million Token Context Beta Feature for Sonnet-4
2025-08-13 06:32:50 -07:00
Kayvan Sylvan
17b7d96da1 chore: upgrade anthropic-sdk-go to v1.9.1 and add beta feature support for context-1m
## CHANGES

- Upgrade anthropic-sdk-go from v1.7.0 to v1.9.1
- Upgrade golang.org/x/crypto from v0.39.0 to v0.40.0
- Add modelBetas map for beta feature configuration
- Implement context-1m-2025-08-07 beta for Claude Sonnet 4
- Add beta header support in streaming requests
- Add beta header support in standard requests
- Implement fallback mechanism when beta features fail
- Preserve existing beta headers in OAuth transport
- Add test coverage for model beta configuration
2025-08-12 22:30:27 -07:00
github-actions[bot]
1b2d9ec0ed chore(release): Update version to v1.4.284 2025-08-12 18:51:24 +00:00
Kayvan Sylvan
63fe320b16 Merge pull request #1695 from ksylvan/0812-make-installing-completions-super-easy
Introduce One-Liner Curl Install for Completions
2025-08-12 11:48:57 -07:00
Changelog Bot
aafca303ad chore: incoming 1695 changelog entry 2025-08-12 11:48:21 -07:00
Kayvan Sylvan
41821efd27 refactor: standardize obtain_completion_files logging; use stderr-only printf
CHANGES
- Replace print_info with tagged printf directed to stderr.
- Replace print_dry_run with tagged printf directed to stderr.
- Add comment enforcing stderr-only output inside this function.
- Preserve dry-run behavior by echoing path only on stdout.
- Retain error handling using print_error for directory creation.
- Normalize log message prefixes to [INFO] and [DRY-RUN].
- Avoid stdout pollution by routing informational messages to stderr.
2025-08-12 11:16:25 -07:00
Kayvan Sylvan
3a4082a1f3 fix: convert GitHub blob/tree URLs to raw and validate completion downloads
CHANGES
- Add helper to translate GitHub blob/tree to raw URLs
- Use effective URL in curl and wget download paths
- Validate downloaded files are non-empty and not HTML
- Redirect info and dry-run messages to standard error
- Relocate temporary directory cleanup trap into main execution
- Improve error messages when completion download sources appear invalid
2025-08-12 10:49:55 -07:00
Kayvan Sylvan
b6fa44d003 docs: add quick install method for shell completions without cloning repo
## CHANGES

- Add one-liner curl install for completions
- Support downloading completions when files missing locally
- Add dry-run option for preview changes
- Enable custom download source via environment variable
- Create temp directory for downloaded completion files
- Add automatic cleanup of temporary files
- Update documentation with new installation methods
2025-08-12 10:00:11 -07:00
github-actions[bot]
09d2d7efc5 chore(release): Update version to v1.4.283 2025-08-12 14:15:16 +00:00
Kayvan Sylvan
4c2ebf25fa Merge pull request #1692 from ksylvan/0812-specify-vendor-when-ambiguous
Add Vendor Selection Support for Models
2025-08-12 07:12:40 -07:00
Changelog Bot
b1b748dc9c chore: incoming 1692 changelog entry 2025-08-12 07:06:45 -07:00
Kayvan Sylvan
cc3e4226d7 feat: add -V/--vendor flag and vendor-aware model selection
CHANGES
- Add -V/--vendor flag to specify model vendor
- Implement vendor-aware model resolution and availability validation
- Warn on ambiguous models; suggest --vendor to disambiguate
- Update bash, zsh, fish completions with vendor suggestions
- Extend --listmodels to print vendor|model when interactive
- Add VendorsModels.PrintWithVendor; sort vendors and models alphabetically
- Pass vendor through API; update server chat handler
- Standardize docs and errors to --yt-dlp-args="..." syntax
- Add test covering ambiguous model warning across multiple vendors
- Promote go-shellquote to direct dependency in go.mod
2025-08-12 06:39:02 -07:00
github-actions[bot]
0f994d8136 chore(release): Update version to v1.4.282 2025-08-11 18:26:48 +00:00
Kayvan Sylvan
298a9007ad Merge pull request #1689 from ksylvan/0811-fix-completions-for-fabric-ai
Enhanced Shell Completions for Fabric CLI Binaries
2025-08-11 11:24:26 -07:00
Kayvan Sylvan
b36e5d3372 feat: enhance completions with 'fabric-ai' alias, dynamic exec, installer
CHANGES
- Support 'fabric-ai' alias across Zsh, Bash, and Fish
- Use invoked command for dynamic completion list queries
- Refactor Fish completions into reusable registrar for multiple commands
- Update Bash completion to reference executable via COMP_WORDS[0]
- Extend Zsh compdef to register fabric and fabric-ai
- Add cross-shell installer script with autodetection and dry-run mode
- Document installation, features, troubleshooting in new completions guide
2025-08-11 11:21:53 -07:00
github-actions[bot]
d1b8eb10ce chore(release): Update version to v1.4.281 2025-08-11 03:16:33 +00:00
Kayvan Sylvan
6000e7469e Merge pull request #1687 from ksylvan/0810-enable-gemini-search
Add Web Search Tool Support for Gemini Models
2025-08-10 20:13:57 -07:00
Changelog Bot
88d3fe65f3 chore: incoming 1687 changelog entry 2025-08-10 20:07:03 -07:00
Kayvan Sylvan
558e7f877d feat(gemini): enable web search, citations, and search-location validation
CHANGES
- Enable Gemini models to use web search tool
- Validate search-location timezone or language code formats
- Normalize language codes from underscores to hyphenated form
- Inject Google Search tool when --search flag enabled
- Append deduplicated web citations under standardized Sources section
- Improve robustness for nil candidates and content parts
- Factor generation config builder for reuse in streaming
- Update CLI help and completions to include Gemini
2025-08-10 19:56:02 -07:00
github-actions[bot]
f33d27f836 chore(release): Update version to v1.4.280 2025-08-10 12:34:52 +00:00
Kayvan Sylvan
1694324261 Merge pull request #1686 from ksylvan/0810-fix-openai-streaming-bug
Prevent duplicate text output in OpenAI streaming responses
2025-08-10 05:32:17 -07:00
Changelog Bot
3a3f5c50a8 chore: incoming 1686 changelog entry 2025-08-10 05:27:29 -07:00
Kayvan Sylvan
b1abfd71c2 fix: prevent duplicate text output in OpenAI streaming responses
## CHANGES

- Skip processing of ResponseOutputTextDone events
- Prevent doubled text in stream output
- Add clarifying comment about API behavior
- Maintain delta chunk streaming functionality
- Fix duplicate content issue in responses
2025-08-10 05:24:30 -07:00
github-actions[bot]
f5b7279225 chore(release): Update version to v1.4.279 2025-08-10 12:13:45 +00:00
Kayvan Sylvan
b974e1bfd5 Merge pull request #1685 from ksylvan/0810-fix-gemini-roles-in-sessions
Fix Gemini Role Mapping for API Compatibility
2025-08-10 05:11:16 -07:00
Changelog Bot
8dda68b3b9 chore: incoming 1685 changelog entry 2025-08-10 05:07:06 -07:00
Kayvan Sylvan
33c24e0cb2 fix(gemini): map chat roles to Gemini user/model in convertMessages
CHANGES
- Map assistant role to model per Gemini constraints
- Map system, developer, function, tool roles to user
- Default unrecognized roles to user to preserve instruction context
- Add unit test validating convertMessages role mapping logic
- Import chat package in tests for role constants
2025-08-10 04:55:33 -07:00
56 changed files with 3774 additions and 2054 deletions

View File

@@ -4,6 +4,7 @@
"addextension",
"adduser",
"AIML",
"Anki",
"anthropics",
"Aoede",
"atotto",
@@ -28,6 +29,7 @@
"deepseek",
"Despina",
"direnv",
"DMARC",
"dryrun",
"dsrp",
"editability",
@@ -73,6 +75,7 @@
"jessevdk",
"Jina",
"joho",
"kballard",
"Keploy",
"Kore",
"ksylvan",
@@ -96,6 +99,7 @@
"mbed",
"metacharacters",
"Miessler",
"mpga",
"nometa",
"numpy",
"ollama",
@@ -127,6 +131,7 @@
"seaborn",
"semgrep",
"sess",
"shellquote",
"storer",
"Streamlit",
"stretchr",
@@ -149,11 +154,13 @@
"WEBVTT",
"wipecontext",
"wipesession",
"wireframes",
"Worktree",
"writeups",
"xclip",
"yourpatternname",
"youtu"
"youtu",
"YTDLP"
],
"cSpell.ignorePaths": ["go.mod", ".gitignore", "CHANGELOG.md"],
"markdownlint.config": {

View File

@@ -1,5 +1,158 @@
# Changelog
## v1.4.293 (2025-08-19)
### PR [#1718](https://github.com/danielmiessler/Fabric/pull/1718) by [ksylvan](https://github.com/ksylvan): Implement Configurable Debug Logging Levels
- Add --debug flag controlling runtime logging verbosity levels
- Introduce internal/log package with Off, Basic, Detailed, Trace
- Replace ad-hoc Debugf and globals with centralized debug logger
- Wire debug level during early CLI argument parsing
- Add bash, zsh, fish completions for --debug levels
## v1.4.292 (2025-08-18)
### PR [#1717](https://github.com/danielmiessler/Fabric/pull/1717) by [ksylvan](https://github.com/ksylvan): Highlight default vendor/model in model listing
- Update PrintWithVendor signature to accept default vendor and model
- Mark default vendor/model with asterisk in non-shell output
- Compare vendor and model case-insensitively when marking
- Pass registry defaults to PrintWithVendor from CLI
- Add test ensuring default selection appears with asterisk
### Direct commits
- Docs: update version number in README updates section from v1.4.290 to v1.4.291
## v1.4.291 (2025-08-18)
### PR [#1715](https://github.com/danielmiessler/Fabric/pull/1715) by [ksylvan](https://github.com/ksylvan): feat: add speech-to-text via OpenAI with transcription flags and comp…
- Add --transcribe-file flag to transcribe audio or video
- Add --transcribe-model flag with model listing and completion
- Add --split-media-file flag to chunk files over 25MB
- Implement OpenAI transcription using Whisper and GPT-4o Transcribe
- Integrate transcription pipeline into CLI before readability processing
## v1.4.290 (2025-08-17)
### PR [#1714](https://github.com/danielmiessler/Fabric/pull/1714) by [ksylvan](https://github.com/ksylvan): feat: add per-pattern model mapping support via environment variables
- Add per-pattern model mapping support via environment variables
- Implement environment variable lookup for pattern-specific models
- Support vendor|model format in environment variable specification
- Enable shell startup file configuration for patterns
- Transform pattern names to uppercase environment variable format
## v1.4.289 (2025-08-16)
### PR [#1710](https://github.com/danielmiessler/Fabric/pull/1710) by [ksylvan](https://github.com/ksylvan): feat: add --no-variable-replacement flag to disable pattern variable …
- Add --no-variable-replacement flag to disable pattern variable substitution
- Introduce CLI flag to skip pattern variable replacement and wire it into domain request and session builder
- Provide PatternsEntity.GetWithoutVariables for input-only pattern processing support
- Refactor patterns code into reusable load and apply helpers
- Update bash, zsh, fish completions with new flag and document in README and CLI help output
## v1.4.288 (2025-08-16)
### PR [#1709](https://github.com/danielmiessler/Fabric/pull/1709) by [ksylvan](https://github.com/ksylvan): Enhanced YouTube Subtitle Language Fallback Handling
- Fix: improve YouTube subtitle language fallback handling in yt-dlp integration
- Fix typo "Gemmini" to "Gemini" in README
- Add "kballard" and "shellquote" to VSCode dictionary
- Add "YTDLP" to VSCode spell checker
- Enhance subtitle language options with fallback variants
## v1.4.287 (2025-08-14)
### PR [#1706](https://github.com/danielmiessler/Fabric/pull/1706) by [ksylvan](https://github.com/ksylvan): Gemini Thinking Support and README (New Features) automation
- Add comprehensive "Recent Major Features" section to README
- Introduce new readme_updates Python script for automation
- Enable Gemini thinking configuration with token budgets
- Update CLI help text for Gemini thinking support
- Add comprehensive test coverage for Gemini thinking
## v1.4.286 (2025-08-14)
### PR [#1700](https://github.com/danielmiessler/Fabric/pull/1700) by [ksylvan](https://github.com/ksylvan): Introduce Thinking Config Across Anthropic and OpenAI Providers
- Add --thinking CLI flag for configurable reasoning levels across providers
- Implement Anthropic ThinkingConfig with standardized budgets and tokens
- Map OpenAI reasoning effort from thinking levels
- Show thinking level in dry-run formatted options
- Overhaul suggest_pattern docs with categories, workflows, usage examples
## v1.4.285 (2025-08-13)
### PR [#1698](https://github.com/danielmiessler/Fabric/pull/1698) by [ksylvan](https://github.com/ksylvan): Enable One Million Token Context Beta Feature for Sonnet-4
- Chore: upgrade anthropic-sdk-go to v1.9.1 and add beta feature support for context-1m
- Add modelBetas map for beta feature configuration
- Implement context-1m-2025-08-07 beta for Claude Sonnet 4
- Add beta header support with fallback handling
- Preserve existing beta headers in OAuth transport
## v1.4.284 (2025-08-12)
### PR [#1695](https://github.com/danielmiessler/Fabric/pull/1695) by [ksylvan](https://github.com/ksylvan): Introduce One-Liner Curl Install for Completions
- Add one-liner curl install method for shell completions without requiring repository cloning
- Support downloading completions when files are missing locally with dry-run option for previewing changes
- Enable custom download source via environment variable and create temporary directory for downloaded completion files
- Add automatic cleanup of temporary files and validate downloaded files are non-empty and not HTML
- Improve error handling and standardize logging by routing informational messages to stderr to avoid stdout pollution
## v1.4.283 (2025-08-12)
### PR [#1692](https://github.com/danielmiessler/Fabric/pull/1692) by [ksylvan](https://github.com/ksylvan): Add Vendor Selection Support for Models
- Add -V/--vendor flag to specify model vendor
- Implement vendor-aware model resolution and availability validation
- Warn on ambiguous models; suggest --vendor to disambiguate
- Update bash, zsh, fish completions with vendor suggestions
- Extend --listmodels to print vendor|model when interactive
## v1.4.282 (2025-08-11)
### PR [#1689](https://github.com/danielmiessler/Fabric/pull/1689) by [ksylvan](https://github.com/ksylvan): Enhanced Shell Completions for Fabric CLI Binaries
- Add 'fabric-ai' alias support across all shell completions
- Use invoked command name for dynamic completion list queries
- Refactor fish completions into reusable registrar for multiple commands
- Update Bash completion to reference executable via COMP_WORDS[0]
- Install completions automatically with new cross-shell setup script
## v1.4.281 (2025-08-11)
### PR [#1687](https://github.com/danielmiessler/Fabric/pull/1687) by [ksylvan](https://github.com/ksylvan): Add Web Search Tool Support for Gemini Models
- Enable Gemini models to use web search tool with --search flag
- Add validation for search-location timezone and language code formats
- Normalize language codes from underscores to hyphenated form
- Append deduplicated web citations under standardized Sources section
- Improve robustness for nil candidates and content parts
## v1.4.280 (2025-08-10)
### PR [#1686](https://github.com/danielmiessler/Fabric/pull/1686) by [ksylvan](https://github.com/ksylvan): Prevent duplicate text output in OpenAI streaming responses
- Fix: prevent duplicate text output in OpenAI streaming responses
- Skip processing of ResponseOutputTextDone events
- Prevent doubled text in stream output
- Add clarifying comment about API behavior
- Maintain delta chunk streaming functionality
## v1.4.279 (2025-08-10)
### PR [#1685](https://github.com/danielmiessler/Fabric/pull/1685) by [ksylvan](https://github.com/ksylvan): Fix Gemini Role Mapping for API Compatibility
- Fix Gemini role mapping to ensure proper API compatibility by converting chat roles to Gemini's user/model format
- Map assistant role to model role per Gemini API constraints
- Map system, developer, function, and tool roles to user role for proper handling
- Default unrecognized roles to user role to preserve instruction context
- Add comprehensive unit tests to validate convertMessages role mapping logic
## v1.4.278 (2025-08-09)
### PR [#1681](https://github.com/danielmiessler/Fabric/pull/1681) by [ksylvan](https://github.com/ksylvan): Enhance YouTube Support with Custom yt-dlp Arguments

100
README.md
View File

@@ -47,6 +47,53 @@ It's all really exciting and powerful, but _it's not easy to integrate this func
Fabric organizes prompts by real-world task, allowing people to create, collect, and organize their most important AI solutions in a single place for use in their favorite tools. And if you're command-line focused, you can use Fabric itself as the interface!
## Updates
Dear Users,
We've been doing so many exciting things here at Fabric, I wanted to give a quick summary here to give you a sense of our development velocity!
Below are the **new features and capabilities** we've added (newest first):
### Recent Major Features
- [v1.4.291](https://github.com/danielmiessler/fabric/releases/tag/v1.4.291) (Aug 18, 2025) — **Speech To Text**: Add OpenAI speech-to-text support with `--transcribe-file`, `--transcribe-model`, and `--split-media-file` flags.
- [v1.4.287](https://github.com/danielmiessler/fabric/releases/tag/v1.4.287) (Aug 16, 2025) — **AI Reasoning**: Add Thinking to Gemini models and introduce `readme_updates` python script
- [v1.4.286](https://github.com/danielmiessler/fabric/releases/tag/v1.4.286) (Aug 14, 2025) — **AI Reasoning**: Introduce Thinking Config Across Anthropic and OpenAI Providers
- [v1.4.285](https://github.com/danielmiessler/fabric/releases/tag/v1.4.285) (Aug 13, 2025) — **Extended Context**: Enable One Million Token Context Beta Feature for Sonnet-4
- [v1.4.284](https://github.com/danielmiessler/fabric/releases/tag/v1.4.284) (Aug 12, 2025) — **Easy Shell Completions Setup**: Introduce One-Liner Curl Install for Completions
- [v1.4.283](https://github.com/danielmiessler/fabric/releases/tag/v1.4.283) (Aug 12, 2025) — **Model Management**: Add Vendor Selection Support for Models
- [v1.4.282](https://github.com/danielmiessler/fabric/releases/tag/v1.4.282) (Aug 11, 2025) — **Enhanced Shell Completions**: Enhanced Shell Completions for Fabric CLI Binaries
- [v1.4.281](https://github.com/danielmiessler/fabric/releases/tag/v1.4.281) (Aug 11, 2025) — **Gemini Search Tool**: Add Web Search Tool Support for Gemini Models
- [v1.4.278](https://github.com/danielmiessler/fabric/releases/tag/v1.4.278) (Aug 9, 2025) — **Enhance YouTube Transcripts**: Enhance YouTube Support with Custom yt-dlp Arguments
- [v1.4.277](https://github.com/danielmiessler/fabric/releases/tag/v1.4.277) (Aug 8, 2025) — **Desktop Notifications**: Add cross-platform desktop notifications to Fabric CLI
- [v1.4.274](https://github.com/danielmiessler/fabric/releases/tag/v1.4.274) (Aug 7, 2025) — **Claude 4.1 Added**: Add Support for Claude Opus 4.1 Model
- [v1.4.271](https://github.com/danielmiessler/fabric/releases/tag/v1.4.271) (Jul 28, 2025) — **AI Summarized Release Notes**: Enable AI summary updates for GitHub releases
- [v1.4.268](https://github.com/danielmiessler/fabric/releases/tag/v1.4.268) (Jul 26, 2025) — **Gemini TTS Voice Selection**: add Gemini TTS voice selection and listing functionality
- [v1.4.267](https://github.com/danielmiessler/fabric/releases/tag/v1.4.267) (Jul 26, 2025) — **Text-to-Speech**: Update Gemini Plugin to New SDK with TTS Support
- [v1.4.258](https://github.com/danielmiessler/fabric/releases/tag/v1.4.258) (Jul 17, 2025) — **Onboarding Improved**: Add startup check to initialize config and .env file automatically
- [v1.4.257](https://github.com/danielmiessler/fabric/releases/tag/v1.4.257) (Jul 17, 2025) — **OpenAI Routing Control**: Introduce CLI Flag to Disable OpenAI Responses API
- [v1.4.252](https://github.com/danielmiessler/fabric/releases/tag/v1.4.252) (Jul 16, 2025) — **Hide Thinking Block**: Optional Hiding of Model Thinking Process with Configurable Tags
- [v1.4.246](https://github.com/danielmiessler/fabric/releases/tag/v1.4.246) (Jul 14, 2025) — **Automatic ChangeLog Updates**: Add AI-powered changelog generation with high-performance Go tool and comprehensive caching
- [v1.4.245](https://github.com/danielmiessler/fabric/releases/tag/v1.4.245) (Jul 11, 2025) — **Together AI**: Together AI Support with OpenAI Fallback Mechanism Added
- [v1.4.232](https://github.com/danielmiessler/fabric/releases/tag/v1.4.232) (Jul 6, 2025) — **Add Custom**: Add Custom Patterns Directory Support
- [v1.4.231](https://github.com/danielmiessler/fabric/releases/tag/v1.4.231) (Jul 5, 2025) — **OAuth Auto-Auth**: OAuth Authentication Support for Anthropic (Use your Max Subscription)
- [v1.4.230](https://github.com/danielmiessler/fabric/releases/tag/v1.4.230) (Jul 5, 2025) — **Model Management**: Add advanced image generation parameters for OpenAI models with four new CLI flags
- [v1.4.227](https://github.com/danielmiessler/fabric/releases/tag/v1.4.227) (Jul 4, 2025) — **Add Image**: Add Image Generation Support to Fabric
- [v1.4.226](https://github.com/danielmiessler/fabric/releases/tag/v1.4.226) (Jul 4, 2025) — **Web Search**: OpenAI Plugin Now Supports Web Search Functionality
- [v1.4.225](https://github.com/danielmiessler/fabric/releases/tag/v1.4.225) (Jul 4, 2025) — **Web Search**: Runtime Web Search Control via Command-Line `--search` Flag
- [v1.4.224](https://github.com/danielmiessler/fabric/releases/tag/v1.4.224) (Jul 1, 2025) — **Add code_review**: Add code_review pattern and updates in Pattern_Descriptions
- [v1.4.222](https://github.com/danielmiessler/fabric/releases/tag/v1.4.222) (Jul 1, 2025) — **OpenAI Plugin**: OpenAI Plugin Migrates to New Responses API
- [v1.4.218](https://github.com/danielmiessler/fabric/releases/tag/v1.4.218) (Jun 27, 2025) — **Model Management**: Add Support for OpenAI Search and Research Model Variants
- [v1.4.217](https://github.com/danielmiessler/fabric/releases/tag/v1.4.217) (Jun 26, 2025) — **New YouTube**: New YouTube Transcript Endpoint Added to REST API
- [v1.4.212](https://github.com/danielmiessler/fabric/releases/tag/v1.4.212) (Jun 23, 2025) — **Add Langdock**: Add Langdock AI and enhance generic OpenAI compatible support
- [v1.4.211](https://github.com/danielmiessler/fabric/releases/tag/v1.4.211) (Jun 19, 2025) — **REST API**: REST API and Web UI Now Support Dynamic Pattern Variables
- [v1.4.210](https://github.com/danielmiessler/fabric/releases/tag/v1.4.210) (Jun 18, 2025) — **Add Citations**: Add Citation Support to Perplexity Response
- [v1.4.208](https://github.com/danielmiessler/fabric/releases/tag/v1.4.208) (Jun 17, 2025) — **Add Perplexity**: Add Perplexity AI Provider with Token Limits Support
- [v1.4.203](https://github.com/danielmiessler/fabric/releases/tag/v1.4.203) (Jun 14, 2025) — **Add Amazon Bedrock**: Add support for Amazon Bedrock
These features represent our commitment to making Fabric the most powerful and flexible AI augmentation framework available!
## Intro videos
Keep in mind that many of these were recorded when Fabric was Python-based, so remember to use the current [install instructions](#installation) below.
@@ -60,9 +107,11 @@ Keep in mind that many of these were recorded when Fabric was Python-based, so r
- [`fabric`](#fabric)
- [What and why](#what-and-why)
- [Updates](#updates)
- [Recent Major Features](#recent-major-features)
- [Intro videos](#intro-videos)
- [Navigation](#navigation)
- [Updates](#updates)
- [Changelog](#changelog)
- [Philosophy](#philosophy)
- [Breaking problems into components](#breaking-problems-into-components)
- [Too many prompts](#too-many-prompts)
@@ -79,11 +128,13 @@ Keep in mind that many of these were recorded when Fabric was Python-based, so r
- [From Source](#from-source)
- [Environment Variables](#environment-variables)
- [Setup](#setup)
- [Per-Pattern Model Mapping](#per-pattern-model-mapping)
- [Add aliases for all patterns](#add-aliases-for-all-patterns)
- [Save your files in markdown using aliases](#save-your-files-in-markdown-using-aliases)
- [Migration](#migration)
- [Upgrading](#upgrading)
- [Shell Completions](#shell-completions)
- [Quick install (no clone required)](#quick-install-no-clone-required)
- [Zsh Completion](#zsh-completion)
- [Bash Completion](#bash-completion)
- [Fish Completion](#fish-completion)
@@ -111,7 +162,7 @@ Keep in mind that many of these were recorded when Fabric was Python-based, so r
<br />
## Updates
## Changelog
Fabric is evolving rapidly.
@@ -235,6 +286,13 @@ fabric --setup
If everything works you are good to go.
### Per-Pattern Model Mapping
You can configure specific models for individual patterns using environment variables
like `FABRIC_MODEL_PATTERN_NAME=vendor|model`
This makes it easy to maintain these per-pattern model mappings in your shell startup files.
### Add aliases for all patterns
In order to add aliases for all your patterns and use them directly as commands ie. `summarize` instead of `fabric --pattern summarize`
@@ -428,6 +486,25 @@ Fabric provides shell completion scripts for Zsh, Bash, and Fish
shells, making it easier to use the CLI by providing tab completion
for commands and options.
#### Quick install (no clone required)
You can install completions directly via a one-liner:
```bash
curl -fsSL https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions/setup-completions.sh | sh
```
Optional variants:
```bash
# Dry-run (see actions without changing your system)
curl -fsSL https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions/setup-completions.sh | sh -s -- --dry-run
# Override the download source (advanced)
FABRIC_COMPLETIONS_BASE_URL="https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions" \
sh -c "$(curl -fsSL https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions/setup-completions.sh)"
```
#### Zsh Completion
To enable Zsh completion:
@@ -498,6 +575,7 @@ Application Options:
-U, --updatepatterns Update patterns
-c, --copy Copy to clipboard
-m, --model= Choose model
-V, --vendor= Specify vendor for chosen model (e.g., -V "LM Studio" -m openai/gpt-oss-20b)
--modelContextLength= Model context length (only affects ollama)
-o, --output= Output to file
--output-session Output the entire session (also a temporary one) to the output file
@@ -522,6 +600,7 @@ Application Options:
--printsession= Print session
--readability Convert HTML input into a clean, readable view
--input-has-vars Apply variables to user input
--no-variable-replacement Disable pattern variable replacement
--dry-run Show what would be sent to the model without actually sending it
--serve Serve the Fabric Rest API
--serveOllama Serve the Fabric Rest API with ollama endpoints
@@ -536,7 +615,7 @@ Application Options:
--liststrategies List all strategies
--listvendors List all vendors
--shell-complete-list Output raw list without headers/formatting (for shell completion)
--search Enable web search tool for supported models (Anthropic, OpenAI)
--search Enable web search tool for supported models (Anthropic, OpenAI, Gemini)
--search-location= Set location for web search results (e.g., 'America/Los_Angeles')
--image-file= Save generated image to specified file path (e.g., 'output.png')
--image-size= Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)
@@ -555,11 +634,22 @@ Application Options:
--notification-command= Custom command to run for notifications (overrides built-in
notifications)
--yt-dlp-args= Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')
--thinking= Set reasoning/thinking level (e.g., off, low, medium, high, or
numeric tokens for Anthropic or Google Gemini)
--debug= Set debug level (0: off, 1: basic, 2: detailed, 3: trace)
Help Options:
-h, --help Show this help message
```
### Debug Levels
Use the `--debug` flag to control runtime logging:
- `0`: off (default)
- `1`: basic debug info
- `2`: detailed debugging
- `3`: trace level
## Our approach to prompting
Fabric _Patterns_ are different than most prompts you'll see.
@@ -569,7 +659,7 @@ Fabric _Patterns_ are different than most prompts you'll see.
Here's an example of a Fabric Pattern.
```bash
https://github.com/danielmiessler/fabric/blob/main/patterns/extract_wisdom/system.md
https://github.com/danielmiessler/Fabric/blob/main/data/patterns/extract_wisdom/system.md
```
<img width="1461" alt="pattern-example" src="https://github.com/danielmiessler/fabric/assets/50654/b910c551-9263-405f-9735-71ca69bbab6d">

View File

@@ -1,3 +1,3 @@
package main
var version = "v1.4.278"
var version = "v1.4.293"

Binary file not shown.

View File

@@ -1,50 +1,71 @@
#compdef fabric
#compdef fabric fabric-ai
# Zsh completion for fabric CLI
# Place this file in a directory in your $fpath (e.g. /usr/local/share/zsh/site-functions)
_fabric_patterns() {
local -a patterns
patterns=(${(f)"$(fabric --listpatterns --shell-complete-list 2>/dev/null)"})
local cmd=${words[1]}
patterns=(${(f)"$($cmd --listpatterns --shell-complete-list 2>/dev/null)"})
compadd -X "Patterns:" ${patterns}
}
_fabric_models() {
local -a models
models=(${(f)"$(fabric --listmodels --shell-complete-list 2>/dev/null)"})
local cmd=${words[1]}
models=(${(f)"$($cmd --listmodels --shell-complete-list 2>/dev/null)"})
compadd -X "Models:" ${models}
}
_fabric_vendors() {
local -a vendors
local cmd=${words[1]}
vendors=(${(f)"$($cmd --listvendors --shell-complete-list 2>/dev/null)"})
compadd -X "Vendors:" ${vendors}
}
_fabric_contexts() {
local -a contexts
contexts=(${(f)"$(fabric --listcontexts --shell-complete-list 2>/dev/null)"})
local cmd=${words[1]}
contexts=(${(f)"$($cmd --listcontexts --shell-complete-list 2>/dev/null)"})
compadd -X "Contexts:" ${contexts}
}
_fabric_sessions() {
local -a sessions
sessions=(${(f)"$(fabric --listsessions --shell-complete-list 2>/dev/null)"})
local cmd=${words[1]}
sessions=(${(f)"$($cmd --listsessions --shell-complete-list 2>/dev/null)"})
compadd -X "Sessions:" ${sessions}
}
_fabric_strategies() {
local -a strategies
strategies=(${(f)"$(fabric --liststrategies --shell-complete-list 2>/dev/null)"})
local cmd=${words[1]}
strategies=(${(f)"$($cmd --liststrategies --shell-complete-list 2>/dev/null)"})
compadd -X "Strategies:" ${strategies}
}
_fabric_extensions() {
local -a extensions
extensions=(${(f)"$(fabric --listextensions --shell-complete-list 2>/dev/null)"})
local cmd=${words[1]}
extensions=(${(f)"$($cmd --listextensions --shell-complete-list 2>/dev/null)"})
compadd -X "Extensions:" ${extensions}
}
_fabric_gemini_voices() {
local -a voices
voices=(${(f)"$(fabric --list-gemini-voices --shell-complete-list 2>/dev/null)"})
local cmd=${words[1]}
voices=(${(f)"$($cmd --list-gemini-voices --shell-complete-list 2>/dev/null)"})
compadd -X "Gemini TTS Voices:" ${voices}
}
_fabric_transcription_models() {
local -a models
local cmd=${words[1]}
models=(${(f)"$($cmd --list-transcription-models --shell-complete-list 2>/dev/null)"})
compadd -X "Transcription Models:" ${models}
}
_fabric() {
local curcontext="$curcontext" state line
typeset -A opt_args
@@ -69,6 +90,7 @@ _fabric() {
'(-U --updatepatterns)'{-U,--updatepatterns}'[Update patterns]' \
'(-c --copy)'{-c,--copy}'[Copy to clipboard]' \
'(-m --model)'{-m,--model}'[Choose model]:model:_fabric_models' \
'(-V --vendor)'{-V,--vendor}'[Specify vendor for chosen model (e.g., -V "LM Studio" -m openai/gpt-oss-20b)]:vendor:_fabric_vendors' \
'(--modelContextLength)--modelContextLength[Model context length (only affects ollama)]:length:' \
'(-o --output)'{-o,--output}'[Output to file]:file:_files' \
'(--output-session)--output-session[Output the entire session to the output file]' \
@@ -85,12 +107,14 @@ _fabric() {
'(-u --scrape_url)'{-u,--scrape_url}'[Scrape website URL to markdown using Jina AI]:url:' \
'(-q --scrape_question)'{-q,--scrape_question}'[Search question using Jina AI]:question:' \
'(-e --seed)'{-e,--seed}'[Seed to be used for LMM generation]:seed:' \
'(--thinking)--thinking[Set reasoning/thinking level]:level:(off low medium high)' \
'(-w --wipecontext)'{-w,--wipecontext}'[Wipe context]:context:_fabric_contexts' \
'(-W --wipesession)'{-W,--wipesession}'[Wipe session]:session:_fabric_sessions' \
'(--printcontext)--printcontext[Print context]:context:_fabric_contexts' \
'(--printsession)--printsession[Print session]:session:_fabric_sessions' \
'(--readability)--readability[Convert HTML input into a clean, readable view]' \
'(--input-has-vars)--input-has-vars[Apply variables to user input]' \
'(--no-variable-replacement)--no-variable-replacement[Disable pattern variable replacement]' \
'(--dry-run)--dry-run[Show what would be sent to the model without actually sending it]' \
'(--serve)--serve[Serve the Fabric Rest API]' \
'(--serveOllama)--serveOllama[Serve the Fabric Rest API with ollama endpoints]' \
@@ -98,7 +122,7 @@ _fabric() {
'(--api-key)--api-key[API key used to secure server routes]:api-key:' \
'(--config)--config[Path to YAML config file]:config file:_files -g "*.yaml *.yml"' \
'(--version)--version[Print current version]' \
'(--search)--search[Enable web search tool for supported models (Anthropic, OpenAI)]' \
'(--search)--search[Enable web search tool for supported models (Anthropic, OpenAI, Gemini)]' \
'(--search-location)--search-location[Set location for web search results]:location:' \
'(--image-file)--image-file[Save generated image to specified file path]:image file:_files -g "*.png *.webp *.jpeg *.jpg"' \
'(--image-size)--image-size[Image dimensions]:size:(1024x1024 1536x1024 1024x1536 auto)' \
@@ -118,6 +142,10 @@ _fabric() {
'(--think-start-tag)--think-start-tag[Start tag for thinking sections (default: <think>)]:start tag:' \
'(--think-end-tag)--think-end-tag[End tag for thinking sections (default: </think>)]:end tag:' \
'(--disable-responses-api)--disable-responses-api[Disable OpenAI Responses API (default: false)]' \
'(--transcribe-file)--transcribe-file[Audio or video file to transcribe]:audio file:_files -g "*.mp3 *.mp4 *.mpeg *.mpga *.m4a *.wav *.webm"' \
'(--transcribe-model)--transcribe-model[Model to use for transcription (separate from chat model)]:transcribe model:_fabric_transcription_models' \
'(--split-media-file)--split-media-file[Split audio/video files larger than 25MB using ffmpeg]' \
'(--debug)--debug[Set debug level (0=off, 1=basic, 2=detailed, 3=trace)]:debug level:(0 1 2 3)' \
'(--notification)--notification[Send desktop notification when command completes]' \
'(--notification-command)--notification-command[Custom command to run for notifications]:notification command:' \
'(-h --help)'{-h,--help}'[Show this help message]' \

View File

@@ -13,11 +13,11 @@ _fabric() {
_get_comp_words_by_ref -n : cur prev words cword
# Define all possible options/flags
local opts="--pattern -p --variable -v --context -C --session --attachment -a --setup -S --temperature -t --topp -T --stream -s --presencepenalty -P --raw -r --frequencypenalty -F --listpatterns -l --listmodels -L --listcontexts -x --listsessions -X --updatepatterns -U --copy -c --model -m --modelContextLength --output -o --output-session --latest -n --changeDefaultModel -d --youtube -y --playlist --transcript --transcript-with-timestamps --comments --metadata --yt-dlp-args --language -g --scrape_url -u --scrape_question -q --seed -e --wipecontext -w --wipesession -W --printcontext --printsession --readability --input-has-vars --dry-run --serve --serveOllama --address --api-key --config --search --search-location --image-file --image-size --image-quality --image-compression --image-background --suppress-think --think-start-tag --think-end-tag --disable-responses-api --voice --list-gemini-voices --notification --notification-command --version --listextensions --addextension --rmextension --strategy --liststrategies --listvendors --shell-complete-list --help -h"
local opts="--pattern -p --variable -v --context -C --session --attachment -a --setup -S --temperature -t --topp -T --stream -s --presencepenalty -P --raw -r --frequencypenalty -F --listpatterns -l --listmodels -L --listcontexts -x --listsessions -X --updatepatterns -U --copy -c --model -m --vendor -V --modelContextLength --output -o --output-session --latest -n --changeDefaultModel -d --youtube -y --playlist --transcript --transcript-with-timestamps --comments --metadata --yt-dlp-args --language -g --scrape_url -u --scrape_question -q --seed -e --thinking --wipecontext -w --wipesession -W --printcontext --printsession --readability --input-has-vars --no-variable-replacement --dry-run --serve --serveOllama --address --api-key --config --search --search-location --image-file --image-size --image-quality --image-compression --image-background --suppress-think --think-start-tag --think-end-tag --disable-responses-api --transcribe-file --transcribe-model --split-media-file --voice --list-gemini-voices --notification --notification-command --debug --version --listextensions --addextension --rmextension --strategy --liststrategies --listvendors --shell-complete-list --help -h"
# Helper function for dynamic completions
_fabric_get_list() {
fabric "$1" --shell-complete-list 2>/dev/null
"${COMP_WORDS[0]}" "$1" --shell-complete-list 2>/dev/null
}
# Handle completions based on the previous word
@@ -38,6 +38,10 @@ _fabric() {
COMPREPLY=($(compgen -W "$(_fabric_get_list --listmodels)" -- "${cur}"))
return 0
;;
-V | --vendor)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listvendors)" -- "${cur}"))
return 0
;;
-w | --wipecontext)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listcontexts)" -- "${cur}"))
return 0
@@ -54,6 +58,10 @@ _fabric() {
COMPREPLY=($(compgen -W "$(_fabric_get_list --listsessions)" -- "${cur}"))
return 0
;;
--thinking)
COMPREPLY=($(compgen -W "off low medium high" -- "${cur}"))
return 0
;;
--rmextension)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listextensions)" -- "${cur}"))
return 0
@@ -66,8 +74,16 @@ _fabric() {
COMPREPLY=($(compgen -W "$(_fabric_get_list --list-gemini-voices)" -- "${cur}"))
return 0
;;
--transcribe-model)
COMPREPLY=($(compgen -W "$(_fabric_get_list --list-transcription-models)" -- "${cur}"))
return 0
;;
--debug)
COMPREPLY=($(compgen -W "0 1 2 3" -- "${cur}"))
return 0
;;
# Options requiring file/directory paths
-a | --attachment | -o | --output | --config | --addextension | --image-file)
-a | --attachment | -o | --output | --config | --addextension | --image-file | --transcribe-file)
_filedir
return 0
;;
@@ -104,4 +120,4 @@ _fabric() {
}
complete -F _fabric fabric
complete -F _fabric fabric fabric-ai

View File

@@ -8,107 +8,137 @@
# Helper functions for dynamic completions
function __fabric_get_patterns
fabric --listpatterns --shell-complete-list 2>/dev/null
set cmd (commandline -opc)[1]
$cmd --listpatterns --shell-complete-list 2>/dev/null
end
function __fabric_get_models
fabric --listmodels --shell-complete-list 2>/dev/null
set cmd (commandline -opc)[1]
$cmd --listmodels --shell-complete-list 2>/dev/null
end
function __fabric_get_vendors
set cmd (commandline -opc)[1]
$cmd --listvendors --shell-complete-list 2>/dev/null
end
function __fabric_get_contexts
fabric --listcontexts --shell-complete-list 2>/dev/null
set cmd (commandline -opc)[1]
$cmd --listcontexts --shell-complete-list 2>/dev/null
end
function __fabric_get_sessions
fabric --listsessions --shell-complete-list 2>/dev/null
set cmd (commandline -opc)[1]
$cmd --listsessions --shell-complete-list 2>/dev/null
end
function __fabric_get_strategies
fabric --liststrategies --shell-complete-list 2>/dev/null
set cmd (commandline -opc)[1]
$cmd --liststrategies --shell-complete-list 2>/dev/null
end
function __fabric_get_extensions
fabric --listextensions --shell-complete-list 2>/dev/null
set cmd (commandline -opc)[1]
$cmd --listextensions --shell-complete-list 2>/dev/null
end
function __fabric_get_gemini_voices
fabric --list-gemini-voices --shell-complete-list 2>/dev/null
set cmd (commandline -opc)[1]
$cmd --list-gemini-voices --shell-complete-list 2>/dev/null
end
function __fabric_get_transcription_models
set cmd (commandline -opc)[1]
$cmd --list-transcription-models --shell-complete-list 2>/dev/null
end
# Main completion function
complete -c fabric -f
function __fabric_register_completions
set cmd $argv[1]
complete -c $cmd -f
# Flag completions with arguments
complete -c fabric -s p -l pattern -d "Choose a pattern from the available patterns" -a "(__fabric_get_patterns)"
complete -c fabric -s v -l variable -d "Values for pattern variables, e.g. -v=#role:expert -v=#points:30"
complete -c fabric -s C -l context -d "Choose a context from the available contexts" -a "(__fabric_get_contexts)"
complete -c fabric -l session -d "Choose a session from the available sessions" -a "(__fabric_get_sessions)"
complete -c fabric -s a -l attachment -d "Attachment path or URL (e.g. for OpenAI image recognition messages)" -r
complete -c fabric -s t -l temperature -d "Set temperature (default: 0.7)"
complete -c fabric -s T -l topp -d "Set top P (default: 0.9)"
complete -c fabric -s P -l presencepenalty -d "Set presence penalty (default: 0.0)"
complete -c fabric -s F -l frequencypenalty -d "Set frequency penalty (default: 0.0)"
complete -c fabric -s m -l model -d "Choose model" -a "(__fabric_get_models)"
complete -c fabric -l modelContextLength -d "Model context length (only affects ollama)"
complete -c fabric -s o -l output -d "Output to file" -r
complete -c fabric -s n -l latest -d "Number of latest patterns to list (default: 0)"
complete -c fabric -s y -l youtube -d "YouTube video or play list URL to grab transcript, comments from it"
complete -c fabric -s g -l language -d "Specify the Language Code for the chat, e.g. -g=en -g=zh"
complete -c fabric -s u -l scrape_url -d "Scrape website URL to markdown using Jina AI"
complete -c fabric -s q -l scrape_question -d "Search question using Jina AI"
complete -c fabric -s e -l seed -d "Seed to be used for LMM generation"
complete -c fabric -s w -l wipecontext -d "Wipe context" -a "(__fabric_get_contexts)"
complete -c fabric -s W -l wipesession -d "Wipe session" -a "(__fabric_get_sessions)"
complete -c fabric -l printcontext -d "Print context" -a "(__fabric_get_contexts)"
complete -c fabric -l printsession -d "Print session" -a "(__fabric_get_sessions)"
complete -c fabric -l address -d "The address to bind the REST API (default: :8080)"
complete -c fabric -l api-key -d "API key used to secure server routes"
complete -c fabric -l config -d "Path to YAML config file" -r -a "*.yaml *.yml"
complete -c fabric -l search-location -d "Set location for web search results (e.g., 'America/Los_Angeles')"
complete -c fabric -l image-file -d "Save generated image to specified file path (e.g., 'output.png')" -r -a "*.png *.webp *.jpeg *.jpg"
complete -c fabric -l image-size -d "Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)" -a "1024x1024 1536x1024 1024x1536 auto"
complete -c fabric -l image-quality -d "Image quality: low, medium, high, auto (default: auto)" -a "low medium high auto"
complete -c fabric -l image-compression -d "Compression level 0-100 for JPEG/WebP formats (default: not set)" -r
complete -c fabric -l image-background -d "Background type: opaque, transparent (default: opaque, only for PNG/WebP)" -a "opaque transparent"
complete -c fabric -l addextension -d "Register a new extension from config file path" -r -a "*.yaml *.yml"
complete -c fabric -l rmextension -d "Remove a registered extension by name" -a "(__fabric_get_extensions)"
complete -c fabric -l strategy -d "Choose a strategy from the available strategies" -a "(__fabric_get_strategies)"
complete -c fabric -l think-start-tag -d "Start tag for thinking sections (default: <think>)"
complete -c fabric -l think-end-tag -d "End tag for thinking sections (default: </think>)"
complete -c fabric -l voice -d "TTS voice name for supported models (e.g., Kore, Charon, Puck)" -a "(__fabric_get_gemini_voices)"
complete -c fabric -l notification-command -d "Custom command to run for notifications (overrides built-in notifications)"
# Flag completions with arguments
complete -c $cmd -s p -l pattern -d "Choose a pattern from the available patterns" -a "(__fabric_get_patterns)"
complete -c $cmd -s v -l variable -d "Values for pattern variables, e.g. -v=#role:expert -v=#points:30"
complete -c $cmd -s C -l context -d "Choose a context from the available contexts" -a "(__fabric_get_contexts)"
complete -c $cmd -l session -d "Choose a session from the available sessions" -a "(__fabric_get_sessions)"
complete -c $cmd -s a -l attachment -d "Attachment path or URL (e.g. for OpenAI image recognition messages)" -r
complete -c $cmd -s t -l temperature -d "Set temperature (default: 0.7)"
complete -c $cmd -s T -l topp -d "Set top P (default: 0.9)"
complete -c $cmd -s P -l presencepenalty -d "Set presence penalty (default: 0.0)"
complete -c $cmd -s F -l frequencypenalty -d "Set frequency penalty (default: 0.0)"
complete -c $cmd -s m -l model -d "Choose model" -a "(__fabric_get_models)"
complete -c $cmd -s V -l vendor -d "Specify vendor for chosen model (e.g., -V \"LM Studio\" -m openai/gpt-oss-20b)" -a "(__fabric_get_vendors)"
complete -c $cmd -l modelContextLength -d "Model context length (only affects ollama)"
complete -c $cmd -s o -l output -d "Output to file" -r
complete -c $cmd -s n -l latest -d "Number of latest patterns to list (default: 0)"
complete -c $cmd -s y -l youtube -d "YouTube video or play list URL to grab transcript, comments from it"
complete -c $cmd -s g -l language -d "Specify the Language Code for the chat, e.g. -g=en -g=zh"
complete -c $cmd -s u -l scrape_url -d "Scrape website URL to markdown using Jina AI"
complete -c $cmd -s q -l scrape_question -d "Search question using Jina AI"
complete -c $cmd -s e -l seed -d "Seed to be used for LMM generation"
complete -c $cmd -l thinking -d "Set reasoning/thinking level" -a "off low medium high"
complete -c $cmd -s w -l wipecontext -d "Wipe context" -a "(__fabric_get_contexts)"
complete -c $cmd -s W -l wipesession -d "Wipe session" -a "(__fabric_get_sessions)"
complete -c $cmd -l printcontext -d "Print context" -a "(__fabric_get_contexts)"
complete -c $cmd -l printsession -d "Print session" -a "(__fabric_get_sessions)"
complete -c $cmd -l address -d "The address to bind the REST API (default: :8080)"
complete -c $cmd -l api-key -d "API key used to secure server routes"
complete -c $cmd -l config -d "Path to YAML config file" -r -a "*.yaml *.yml"
complete -c $cmd -l search-location -d "Set location for web search results (e.g., 'America/Los_Angeles')"
complete -c $cmd -l image-file -d "Save generated image to specified file path (e.g., 'output.png')" -r -a "*.png *.webp *.jpeg *.jpg"
complete -c $cmd -l image-size -d "Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)" -a "1024x1024 1536x1024 1024x1536 auto"
complete -c $cmd -l image-quality -d "Image quality: low, medium, high, auto (default: auto)" -a "low medium high auto"
complete -c $cmd -l image-compression -d "Compression level 0-100 for JPEG/WebP formats (default: not set)" -r
complete -c $cmd -l image-background -d "Background type: opaque, transparent (default: opaque, only for PNG/WebP)" -a "opaque transparent"
complete -c $cmd -l addextension -d "Register a new extension from config file path" -r -a "*.yaml *.yml"
complete -c $cmd -l rmextension -d "Remove a registered extension by name" -a "(__fabric_get_extensions)"
complete -c $cmd -l strategy -d "Choose a strategy from the available strategies" -a "(__fabric_get_strategies)"
complete -c $cmd -l think-start-tag -d "Start tag for thinking sections (default: <think>)"
complete -c $cmd -l think-end-tag -d "End tag for thinking sections (default: </think>)"
complete -c $cmd -l voice -d "TTS voice name for supported models (e.g., Kore, Charon, Puck)" -a "(__fabric_get_gemini_voices)"
complete -c $cmd -l transcribe-file -d "Audio or video file to transcribe" -r -a "*.mp3 *.mp4 *.mpeg *.mpga *.m4a *.wav *.webm"
complete -c $cmd -l transcribe-model -d "Model to use for transcription (separate from chat model)" -a "(__fabric_get_transcription_models)"
complete -c $cmd -l debug -d "Set debug level (0=off, 1=basic, 2=detailed, 3=trace)" -a "0 1 2 3"
complete -c $cmd -l notification-command -d "Custom command to run for notifications (overrides built-in notifications)"
# Boolean flags (no arguments)
complete -c fabric -s S -l setup -d "Run setup for all reconfigurable parts of fabric"
complete -c fabric -s s -l stream -d "Stream"
complete -c fabric -s r -l raw -d "Use the defaults of the model without sending chat options"
complete -c fabric -s l -l listpatterns -d "List all patterns"
complete -c fabric -s L -l listmodels -d "List all available models"
complete -c fabric -s x -l listcontexts -d "List all contexts"
complete -c fabric -s X -l listsessions -d "List all sessions"
complete -c fabric -s U -l updatepatterns -d "Update patterns"
complete -c fabric -s c -l copy -d "Copy to clipboard"
complete -c fabric -l output-session -d "Output the entire session to the output file"
complete -c fabric -s d -l changeDefaultModel -d "Change default model"
complete -c fabric -l playlist -d "Prefer playlist over video if both ids are present in the URL"
complete -c fabric -l transcript -d "Grab transcript from YouTube video and send to chat"
complete -c fabric -l transcript-with-timestamps -d "Grab transcript from YouTube video with timestamps"
complete -c fabric -l comments -d "Grab comments from YouTube video and send to chat"
complete -c fabric -l metadata -d "Output video metadata"
complete -c fabric -l yt-dlp-args -d "Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')"
complete -c fabric -l readability -d "Convert HTML input into a clean, readable view"
complete -c fabric -l input-has-vars -d "Apply variables to user input"
complete -c fabric -l dry-run -d "Show what would be sent to the model without actually sending it"
complete -c fabric -l search -d "Enable web search tool for supported models (Anthropic, OpenAI)"
complete -c fabric -l serve -d "Serve the Fabric Rest API"
complete -c fabric -l serveOllama -d "Serve the Fabric Rest API with ollama endpoints"
complete -c fabric -l version -d "Print current version"
complete -c fabric -l listextensions -d "List all registered extensions"
complete -c fabric -l liststrategies -d "List all strategies"
complete -c fabric -l listvendors -d "List all vendors"
complete -c fabric -l list-gemini-voices -d "List all available Gemini TTS voices"
complete -c fabric -l shell-complete-list -d "Output raw list without headers/formatting (for shell completion)"
complete -c fabric -l suppress-think -d "Suppress text enclosed in thinking tags"
complete -c fabric -l disable-responses-api -d "Disable OpenAI Responses API (default: false)"
complete -c fabric -l notification -d "Send desktop notification when command completes"
complete -c fabric -s h -l help -d "Show this help message"
# Boolean flags (no arguments)
complete -c $cmd -s S -l setup -d "Run setup for all reconfigurable parts of fabric"
complete -c $cmd -s s -l stream -d "Stream"
complete -c $cmd -s r -l raw -d "Use the defaults of the model without sending chat options"
complete -c $cmd -s l -l listpatterns -d "List all patterns"
complete -c $cmd -s L -l listmodels -d "List all available models"
complete -c $cmd -s x -l listcontexts -d "List all contexts"
complete -c $cmd -s X -l listsessions -d "List all sessions"
complete -c $cmd -s U -l updatepatterns -d "Update patterns"
complete -c $cmd -s c -l copy -d "Copy to clipboard"
complete -c $cmd -l output-session -d "Output the entire session to the output file"
complete -c $cmd -s d -l changeDefaultModel -d "Change default model"
complete -c $cmd -l playlist -d "Prefer playlist over video if both ids are present in the URL"
complete -c $cmd -l transcript -d "Grab transcript from YouTube video and send to chat"
complete -c $cmd -l transcript-with-timestamps -d "Grab transcript from YouTube video with timestamps"
complete -c $cmd -l comments -d "Grab comments from YouTube video and send to chat"
complete -c $cmd -l metadata -d "Output video metadata"
complete -c $cmd -l yt-dlp-args -d "Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')"
complete -c $cmd -l readability -d "Convert HTML input into a clean, readable view"
complete -c $cmd -l input-has-vars -d "Apply variables to user input"
complete -c $cmd -l no-variable-replacement -d "Disable pattern variable replacement"
complete -c $cmd -l dry-run -d "Show what would be sent to the model without actually sending it"
complete -c $cmd -l search -d "Enable web search tool for supported models (Anthropic, OpenAI, Gemini)"
complete -c $cmd -l serve -d "Serve the Fabric Rest API"
complete -c $cmd -l serveOllama -d "Serve the Fabric Rest API with ollama endpoints"
complete -c $cmd -l version -d "Print current version"
complete -c $cmd -l listextensions -d "List all registered extensions"
complete -c $cmd -l liststrategies -d "List all strategies"
complete -c $cmd -l listvendors -d "List all vendors"
complete -c $cmd -l list-gemini-voices -d "List all available Gemini TTS voices"
complete -c $cmd -l shell-complete-list -d "Output raw list without headers/formatting (for shell completion)"
complete -c $cmd -l suppress-think -d "Suppress text enclosed in thinking tags"
complete -c $cmd -l disable-responses-api -d "Disable OpenAI Responses API (default: false)"
complete -c $cmd -l split-media-file -d "Split audio/video files larger than 25MB using ffmpeg"
complete -c $cmd -l notification -d "Send desktop notification when command completes"
complete -c $cmd -s h -l help -d "Show this help message"
end
__fabric_register_completions fabric
__fabric_register_completions fabric-ai

503
completions/setup-completions.sh Executable file
View File

@@ -0,0 +1,503 @@
#!/bin/sh
# Fabric Shell Completions Setup Script
# This script automatically installs shell completions for the fabric CLI
# based on your current shell and the installed fabric command name.
set -e
# Global variables
DRY_RUN=false
# Base URL to fetch completion files when not available locally
# Can be overridden via environment variable FABRIC_COMPLETIONS_BASE_URL
FABRIC_COMPLETIONS_BASE_URL="${FABRIC_COMPLETIONS_BASE_URL:-https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions}"
TEMP_DIR=""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Function to print colored output
print_info() {
printf "${BLUE}[INFO]${NC} %s\n" "$1"
}
print_success() {
printf "${GREEN}[SUCCESS]${NC} %s\n" "$1"
}
print_warning() {
printf "${YELLOW}[WARNING]${NC} %s\n" "$1"
}
print_error() {
printf "${RED}[ERROR]${NC} %s\n" "$1"
}
print_dry_run() {
printf "${CYAN}[DRY-RUN]${NC} %s\n" "$1"
}
# Function to execute commands with dry-run support
execute_command() {
cmd="$1"
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would run: $cmd"
return 0
else
eval "$cmd" 2>/dev/null
fi
}
# Simple downloader that prefers curl, falls back to wget
to_github_raw_url() {
in_url="$1"
case "$in_url" in
https://github.com/*/*/blob/*)
# Convert blob URL to raw
# https://github.com/{owner}/{repo}/blob/{ref}/path -> https://raw.githubusercontent.com/{owner}/{repo}/{ref}/path
echo "$in_url" | sed -E 's#https://github.com/([^/]+)/([^/]+)/blob/([^/]+)/#https://raw.githubusercontent.com/\1/\2/\3/#'
;;
https://github.com/*/*/tree/*)
# Convert tree URL base + file path to raw
# https://github.com/{owner}/{repo}/tree/{ref}/path -> https://raw.githubusercontent.com/{owner}/{repo}/{ref}/path
echo "$in_url" | sed -E 's#https://github.com/([^/]+)/([^/]+)/tree/([^/]+)/#https://raw.githubusercontent.com/\1/\2/\3/#'
;;
*)
echo "$in_url"
;;
esac
}
# Simple downloader that prefers curl, falls back to wget
download_file() {
url="$1"
dest="$2"
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would download: $url -> $dest"
return 0
fi
eff_url="$(to_github_raw_url "$url")"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$eff_url" -o "$dest"
return $?
elif command -v wget >/dev/null 2>&1; then
wget -q "$eff_url" -O "$dest"
return $?
else
print_error "Neither 'curl' nor 'wget' is available to download: $url"
return 1
fi
}
# Attempt to obtain completion files. If local copies are missing,
# download them into a temporary directory and return that directory path.
obtain_completion_files() {
obf_script_dir="$1"
obf_need_download=false
if [ ! -f "$obf_script_dir/_fabric" ] || [ ! -f "$obf_script_dir/fabric.bash" ] || [ ! -f "$obf_script_dir/fabric.fish" ]; then
obf_need_download=true
fi
if [ "$obf_need_download" = false ]; then
echo "$obf_script_dir"
return 0
fi
# Note: write only to stderr in this function except for the final echo which returns the path
printf "%s\n" "[INFO] Local completion files not found; will download from GitHub." 1>&2
printf "%s\n" "[INFO] Source: $FABRIC_COMPLETIONS_BASE_URL" 1>&2
if [ "$DRY_RUN" = true ]; then
printf "%s\n" "[DRY-RUN] Would create temporary directory for downloads" 1>&2
echo "$obf_script_dir" # Keep using original for dry-run copies
return 0
fi
TEMP_DIR="$(mktemp -d 2>/dev/null || mktemp -d -t fabric-completions)"
if [ ! -d "$TEMP_DIR" ]; then
print_error "Failed to create temporary directory for downloads."
return 1
fi
if ! download_file "$FABRIC_COMPLETIONS_BASE_URL/_fabric" "$TEMP_DIR/_fabric"; then
print_error "Failed to download _fabric"
return 1
fi
if [ ! -s "$TEMP_DIR/_fabric" ] || head -n1 "$TEMP_DIR/_fabric" | grep -qi "^<!DOCTYPE\|^<html"; then
print_error "Downloaded _fabric appears invalid (empty or HTML). Check FABRIC_COMPLETIONS_BASE_URL."
return 1
fi
if ! download_file "$FABRIC_COMPLETIONS_BASE_URL/fabric.bash" "$TEMP_DIR/fabric.bash"; then
print_error "Failed to download fabric.bash"
return 1
fi
if [ ! -s "$TEMP_DIR/fabric.bash" ] || head -n1 "$TEMP_DIR/fabric.bash" | grep -qi "^<!DOCTYPE\|^<html"; then
print_error "Downloaded fabric.bash appears invalid (empty or HTML). Check FABRIC_COMPLETIONS_BASE_URL."
return 1
fi
if ! download_file "$FABRIC_COMPLETIONS_BASE_URL/fabric.fish" "$TEMP_DIR/fabric.fish"; then
print_error "Failed to download fabric.fish"
return 1
fi
if [ ! -s "$TEMP_DIR/fabric.fish" ] || head -n1 "$TEMP_DIR/fabric.fish" | grep -qi "^<!DOCTYPE\|^<html"; then
print_error "Downloaded fabric.fish appears invalid (empty or HTML). Check FABRIC_COMPLETIONS_BASE_URL."
return 1
fi
echo "$TEMP_DIR"
}
# Ensure directory exists, try sudo on permission failure
ensure_dir() {
dir="$1"
# Expand ~ if present
case "$dir" in
~/*)
dir="$HOME${dir#~}"
;;
esac
if [ -d "$dir" ]; then
return 0
fi
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would run: mkdir -p \"$dir\""
print_dry_run "If permission denied, would run: sudo mkdir -p \"$dir\""
return 0
fi
if mkdir -p "$dir" 2>/dev/null; then
return 0
fi
if command -v sudo >/dev/null 2>&1 && sudo mkdir -p "$dir" 2>/dev/null; then
return 0
fi
print_error "Failed to create directory: $dir"
return 1
}
# Copy file with sudo fallback on permission failure
install_file() {
src="$1"
dest="$2"
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would run: cp \"$src\" \"$dest\""
print_dry_run "If permission denied, would run: sudo cp \"$src\" \"$dest\""
return 0
fi
if cp "$src" "$dest" 2>/dev/null; then
return 0
fi
if command -v sudo >/dev/null 2>&1 && sudo cp "$src" "$dest" 2>/dev/null; then
return 0
fi
print_error "Failed to install file to: $dest"
return 1
}
# Function to detect fabric command name
detect_fabric_command() {
if command -v fabric >/dev/null 2>&1; then
echo "fabric"
elif command -v fabric-ai >/dev/null 2>&1; then
echo "fabric-ai"
else
print_error "Neither 'fabric' nor 'fabric-ai' command found in PATH"
exit 1
fi
}
# Function to detect shell
detect_shell() {
if [ -n "$SHELL" ]; then
basename "$SHELL"
else
print_warning "SHELL environment variable not set, defaulting to sh"
echo "sh"
fi
}
# Function to get script directory
get_script_dir() {
# Get the directory where this script is located
script_path="$(readlink -f "$0" 2>/dev/null || realpath "$0" 2>/dev/null || echo "$0")"
dirname "$script_path"
}
# Function to setup Zsh completions
setup_zsh_completions() {
fabric_cmd="$1"
script_dir="$2"
completion_file="_${fabric_cmd}"
print_info "Setting up Zsh completions for '$fabric_cmd'..."
# Try to use existing $fpath first, then fall back to default directories
zsh_dirs=""
# Check if user's shell is zsh and try to get fpath from it
if [ "$(basename "$SHELL")" = "zsh" ] && command -v zsh >/dev/null 2>&1; then
# Get fpath from zsh by sourcing user's .zshrc first
fpath_output=$(zsh -c "source \$HOME/.zshrc 2>/dev/null && print -l \$fpath" 2>/dev/null | head -5 | tr '\n' ' ')
if [ -n "$fpath_output" ] && [ "$fpath_output" != "" ]; then
print_info "Using directories from zsh \$fpath"
zsh_dirs="$fpath_output"
fi
fi
# If we couldn't get fpath or it's empty, use default directories
if [ -z "$zsh_dirs" ] || [ "$zsh_dirs" = "" ]; then
print_info "Using default zsh completion directories"
zsh_dirs="/usr/local/share/zsh/site-functions /opt/homebrew/share/zsh/site-functions /usr/share/zsh/site-functions ~/.local/share/zsh/site-functions"
fi
installed=false
for dir in $zsh_dirs; do
# Create directory (with sudo fallback if needed)
if ensure_dir "$dir"; then
if install_file "$script_dir/_fabric" "$dir/$completion_file"; then
if [ "$DRY_RUN" = true ]; then
print_success "Would install Zsh completion to: $dir/$completion_file"
else
print_success "Installed Zsh completion to: $dir/$completion_file"
fi
installed=true
break
fi
fi
done
if [ "$installed" = false ]; then
if [ "$DRY_RUN" = true ]; then
print_warning "Would attempt to install Zsh completions but no writable directory found."
else
print_error "Failed to install Zsh completions. Try running with sudo or check permissions."
return 1
fi
fi
if [ "$DRY_RUN" = true ]; then
print_info "Would suggest: Restart your shell or run 'autoload -U compinit && compinit' to enable completions."
else
print_info "Restart your shell or run 'autoload -U compinit && compinit' to enable completions."
fi
}
# Function to setup Bash completions
setup_bash_completions() {
fabric_cmd="$1"
script_dir="$2"
completion_file="${fabric_cmd}.bash"
print_info "Setting up Bash completions for '$fabric_cmd'..."
# Try different completion directories
bash_dirs="/etc/bash_completion.d /usr/local/etc/bash_completion.d /opt/homebrew/etc/bash_completion.d ~/.local/share/bash-completion/completions"
installed=false
for dir in $bash_dirs; do
if ensure_dir "$dir"; then
if install_file "$script_dir/fabric.bash" "$dir/$completion_file"; then
if [ "$DRY_RUN" = true ]; then
print_success "Would install Bash completion to: $dir/$completion_file"
else
print_success "Installed Bash completion to: $dir/$completion_file"
fi
installed=true
break
fi
fi
done
if [ "$installed" = false ]; then
if [ "$DRY_RUN" = true ]; then
print_warning "Would attempt to install Bash completions but no writable directory found."
else
print_error "Failed to install Bash completions. Try running with sudo or check permissions."
return 1
fi
fi
if [ "$DRY_RUN" = true ]; then
print_info "Would suggest: Restart your shell or run 'source ~/.bashrc' to enable completions."
else
print_info "Restart your shell or run 'source ~/.bashrc' to enable completions."
fi
}
# Function to setup Fish completions
setup_fish_completions() {
fabric_cmd="$1"
script_dir="$2"
completion_file="${fabric_cmd}.fish"
print_info "Setting up Fish completions for '$fabric_cmd'..."
# Fish completion directory
fish_dir="$HOME/.config/fish/completions"
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would run: mkdir -p \"$fish_dir\""
print_dry_run "Would run: cp \"$script_dir/fabric.fish\" \"$fish_dir/$completion_file\""
print_success "Would install Fish completion to: $fish_dir/$completion_file"
print_info "Fish will automatically load the completions (no restart needed)."
elif mkdir -p "$fish_dir" 2>/dev/null; then
if cp "$script_dir/fabric.fish" "$fish_dir/$completion_file"; then
print_success "Installed Fish completion to: $fish_dir/$completion_file"
print_info "Fish will automatically load the completions (no restart needed)."
else
print_error "Failed to copy Fish completion file."
return 1
fi
else
print_error "Failed to create Fish completions directory: $fish_dir"
return 1
fi
}
# Function to setup completions for other shells
setup_other_shell_completions() {
fabric_cmd="$1"
shell_name="$2"
script_dir="$3"
print_warning "Shell '$shell_name' is not directly supported."
print_info "You can manually source the completion files:"
print_info " Bash-compatible: source $script_dir/fabric.bash"
print_info " Zsh-compatible: source $script_dir/_fabric"
}
# Function to show help
show_help() {
cat << EOF
Fabric Shell Completions Setup Script
USAGE:
setup-completions.sh [OPTIONS]
OPTIONS:
--dry-run Show what commands would be run without executing them
--help Show this help message
DESCRIPTION:
This script automatically installs shell completions for the fabric CLI
based on your current shell and the installed fabric command name.
The script will use completion files from the same directory as the script
when available. If they are not present (e.g., when running via curl), it
will download them from GitHub:
$FABRIC_COMPLETIONS_BASE_URL
You can override the download source by setting
FABRIC_COMPLETIONS_BASE_URL to your preferred location.
Supports: zsh, bash, fish
The script will:
1. Detect whether 'fabric' or 'fabric-ai' is installed
2. Detect your current shell from the SHELL environment variable
3. Install the appropriate completion file with the correct name
4. Try multiple standard completion directories
EXAMPLES:
./setup-completions.sh # Install completions
./setup-completions.sh --dry-run # Show what would be done
FABRIC_COMPLETIONS_BASE_URL="https://raw.githubusercontent.com/<owner>/<repo>/main/completions" \\
./setup-completions.sh # Override download source
./setup-completions.sh --help # Show this help
EOF
}
# Main function
main() {
# Parse command line arguments
while [ $# -gt 0 ]; do
case "$1" in
--dry-run)
DRY_RUN=true
shift
;;
--help|-h)
show_help
exit 0
;;
*)
print_error "Unknown option: $1"
print_info "Use --help for usage information."
exit 1
;;
esac
done
print_info "Fabric Shell Completions Setup"
print_info "==============================="
if [ "$DRY_RUN" = true ]; then
print_info "DRY RUN MODE - Commands will be shown but not executed"
print_info ""
fi
# Get script directory and obtain completion files (local or downloaded)
script_dir="$(get_script_dir)"
script_dir="$(obtain_completion_files "$script_dir" || echo "")"
if [ -z "$script_dir" ]; then
print_error "Unable to obtain completion files. Aborting."
exit 1
fi
# If we downloaded into a temp dir, arrange cleanup at process exit
if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then
trap 'if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR"; fi' EXIT INT TERM
fi
# Detect fabric command
fabric_cmd="$(detect_fabric_command)"
print_info "Detected fabric command: $fabric_cmd"
# Detect shell
shell_name="$(detect_shell)"
print_info "Detected shell: $shell_name"
# Setup completions based on shell
case "$shell_name" in
zsh)
setup_zsh_completions "$fabric_cmd" "$script_dir"
;;
bash)
setup_bash_completions "$fabric_cmd" "$script_dir"
;;
fish)
setup_fish_completions "$fabric_cmd" "$script_dir"
;;
*)
setup_other_shell_completions "$fabric_cmd" "$shell_name" "$script_dir"
;;
esac
if [ "$DRY_RUN" = true ]; then
print_success "Dry-run completed! The above commands would set up shell completions."
print_info "Run without --dry-run to actually install the completions."
else
print_success "Shell completion setup completed!"
print_info "You can now use tab completion with the '$fabric_cmd' command."
fi
}
# Run main function
main "$@"

View File

@@ -167,6 +167,8 @@ us the results in
Select the model to use. NOTE: Will not work if you
have set a default model. please use --clear to clear
persistence before using this flag
--vendor VENDOR, -V VENDOR
Specify vendor for the selected model (e.g., -V "LM Studio" -m openai/gpt-oss-20b)
--listmodels List all available models
--remoteOllamaServer REMOTEOLLAMASERVER
The URL of the remote ollamaserver to use. ONLY USE

View File

@@ -1,23 +1,128 @@
# IDENTITY and PURPOSE
You are an AI assistant tasked with creating a new feature for a fabric command-line tool. Your primary responsibility is to develop a pattern that suggests appropriate fabric patterns or commands based on user input. You are knowledgeable about fabric commands and understand the need to expand the tool's functionality. Your role involves analyzing user requests, determining the most suitable fabric commands or patterns, and providing helpful suggestions to users.
You are an expert AI assistant specialized in the Fabric framework - an open-source tool for augmenting human capabilities with AI. Your primary responsibility is to analyze user requests and suggest the most appropriate fabric patterns or commands to accomplish their goals. You have comprehensive knowledge of all available patterns, their categories, capabilities, and use cases.
Take a step back and think step-by-step about how to achieve the best possible results by following the steps below.
# STEPS
- Analyze the user's input to understand their specific needs and context
- Determine the appropriate fabric pattern or command based on the user's request
- Generate a response that suggests the relevant fabric command(s) or pattern(s)
- Provide explanations or multiple options when applicable
- If no specific command is found, suggest using `create_pattern`
## 1. ANALYZE USER INPUT
- Parse the user's request to understand their primary objective
- Identify the type of content they're working with (text, code, data, etc.)
- Determine the desired output format or outcome
- Consider the user's level of expertise with fabric
## 2. CATEGORIZE THE REQUEST
Match the request to one or more of these primary categories:
- **AI** - AI-related patterns for model guidance, art prompts, evaluation
- **ANALYSIS** - Analysis and evaluation of content, data, claims, debates
- **BILL** - Legislative bill analysis and implications
- **BUSINESS** - Business strategy, agreements, sales, presentations
- **CLASSIFICATION** - Content categorization and tagging
- **CONVERSION** - Format conversion between different data types
- **CR THINKING** - Critical thinking, logical analysis, bias detection
- **CREATIVITY** - Creative writing, essay generation, artistic content
- **DEVELOPMENT** - Software development, coding, project design
- **DEVOPS** - Infrastructure, deployment, pipeline management
- **EXTRACT** - Information extraction from various content types
- **GAMING** - RPG, D&D, gaming-related content creation
- **LEARNING** - Educational content, tutorials, explanations
- **OTHER** - Miscellaneous patterns that don't fit other categories
- **RESEARCH** - Academic research, paper analysis, investigation
- **REVIEW** - Evaluation and review of content, code, designs
- **SECURITY** - Cybersecurity analysis, threat modeling, vulnerability assessment
- **SELF** - Personal development, guidance, self-improvement
- **STRATEGY** - Strategic analysis, planning, decision-making
- **SUMMARIZE** - Content summarization at various levels of detail
- **VISUALIZE** - Data visualization, diagrams, charts, graphics
- **WISDOM** - Wisdom extraction, insights, life lessons
- **WRITING** - Writing assistance, improvement, formatting
## 3. SUGGEST APPROPRIATE PATTERNS
- Recommend 1-3 most suitable patterns based on the analysis
- Prioritize patterns that directly address the user's main objective
- Consider alternative patterns for different approaches to the same goal
- Include both primary and secondary pattern suggestions when relevant
## 4. PROVIDE CONTEXT AND USAGE
- Explain WHY each suggested pattern is appropriate
- Include the exact fabric command syntax
- Mention any important considerations or limitations
- Suggest complementary patterns if applicable
# OUTPUT INSTRUCTIONS
- Only output Markdown
- Provide suggestions for fabric commands or patterns based on the user's input
- Include explanations or multiple options when appropriate
- If suggesting `create_pattern`, include instructions for saving and using the new pattern
- Format the output to be clear and easy to understand for users new to fabric
- Ensure the response aligns with the goal of making fabric more accessible and user-friendly
- Ensure you follow ALL these instructions when creating your output
- Structure your response with clear headings and sections
- Provide specific fabric command examples: `fabric --pattern pattern_name`
- Include brief explanations of what each pattern does
- If multiple patterns could work, rank them by relevance
- For complex requests, suggest a workflow using multiple patterns
- If no existing pattern fits perfectly, suggest `create_pattern` with specific guidance
- Format the output to be actionable and easy to follow
- Ensure suggestions align with making fabric more accessible and powerful
# PATTERN MATCHING GUIDELINES
## Common Request Types and Best Patterns
**AI**: ai, create_art_prompt, create_pattern, extract_mcp_servers, extract_wisdom_agents, generate_code_rules, improve_prompt, judge_output, rate_ai_response, rate_ai_result, raw_query, solve_with_cot, suggest_pattern, summarize_prompt
**ANALYSIS**: ai, analyze_answers, analyze_bill, analyze_bill_short, analyze_candidates, analyze_cfp_submission, analyze_claims, analyze_comments, analyze_debate, analyze_email_headers, analyze_incident, analyze_interviewer_techniques, analyze_logs, analyze_malware, analyze_military_strategy, analyze_mistakes, analyze_paper, analyze_paper_simple, analyze_patent, analyze_personality, analyze_presentation, analyze_product_feedback, analyze_proposition, analyze_prose, analyze_prose_json, analyze_prose_pinker, analyze_risk, analyze_sales_call, analyze_spiritual_text, analyze_tech_impact, analyze_terraform_plan, analyze_threat_report, analyze_threat_report_cmds, analyze_threat_report_trends, apply_ul_tags, check_agreement, compare_and_contrast, create_ai_jobs_analysis, create_idea_compass, create_investigation_visualization, create_prediction_block, create_recursive_outline, create_tags, dialog_with_socrates, extract_main_idea, extract_predictions, find_hidden_message, find_logical_fallacies, get_wow_per_minute, identify_dsrp_distinctions, identify_dsrp_perspectives, identify_dsrp_relationships, identify_dsrp_systems, identify_job_stories, label_and_rate, prepare_7s_strategy, provide_guidance, rate_content, rate_value, recommend_artists, recommend_talkpanel_topics, review_design, summarize_board_meeting, t_analyze_challenge_handling, t_check_dunning_kruger, t_check_metrics, t_describe_life_outlook, t_extract_intro_sentences, t_extract_panel_topics, t_find_blindspots, t_find_negative_thinking, t_red_team_thinking, t_threat_model_plans, t_year_in_review, write_hackerone_report
**BILL**: analyze_bill, analyze_bill_short
**BUSINESS**: check_agreement, create_ai_jobs_analysis, create_formal_email, create_hormozi_offer, create_loe_document, create_logo, create_newsletter_entry, create_prd, explain_project, extract_business_ideas, extract_product_features, extract_skills, extract_sponsors, identify_job_stories, prepare_7s_strategy, rate_value, t_check_metrics, t_create_h3_career, t_visualize_mission_goals_projects, t_year_in_review, transcribe_minutes
**CLASSIFICATION**: apply_ul_tags
**CONVERSION**: clean_text, convert_to_markdown, create_graph_from_input, export_data_as_csv, extract_videoid, get_youtube_rss, humanize, md_callout, sanitize_broken_html_to_markdown, to_flashcards, transcribe_minutes, translate, tweet, write_latex
**CR THINKING**: capture_thinkers_work, create_idea_compass, create_markmap_visualization, dialog_with_socrates, extract_alpha, extract_controversial_ideas, extract_extraordinary_claims, extract_predictions, extract_primary_problem, extract_wisdom_nometa, find_hidden_message, find_logical_fallacies, solve_with_cot, summarize_debate, t_analyze_challenge_handling, t_check_dunning_kruger, t_find_blindspots, t_find_negative_thinking, t_find_neglected_goals, t_red_team_thinking
**CREATIVITY**: create_mnemonic_phrases, write_essay
**DEVELOPMENT**: agility_story, analyze_prose_json, answer_interview_question, ask_secure_by_design_questions, ask_uncle_duke, coding_master, create_coding_feature, create_coding_project, create_command, create_design_document, create_git_diff_commit, create_mermaid_visualization, create_mermaid_visualization_for_github, create_pattern, create_sigma_rules, create_user_story, explain_code, explain_docs, export_data_as_csv, extract_algorithm_update_recommendations, extract_mcp_servers, extract_poc, generate_code_rules, get_youtube_rss, improve_prompt, official_pattern_template, recommend_pipeline_upgrades, refine_design_document, review_code, review_design, sanitize_broken_html_to_markdown, show_fabric_options_markmap, suggest_pattern, summarize_git_changes, summarize_git_diff, summarize_pull-requests, write_nuclei_template_rule, write_pull-request, write_semgrep_rule
**DEVOPS**: analyze_terraform_plan
**EXTRACT**: analyze_comments, create_aphorisms, create_tags, create_video_chapters, extract_algorithm_update_recommendations, extract_alpha, extract_article_wisdom, extract_book_ideas, extract_book_recommendations, extract_business_ideas, extract_controversial_ideas, extract_core_message, extract_ctf_writeup, extract_domains, extract_extraordinary_claims, extract_ideas, extract_insights, extract_insights_dm, extract_instructions, extract_jokes, extract_latest_video, extract_main_activities, extract_main_idea, extract_mcp_servers, extract_most_redeeming_thing, extract_patterns, extract_poc, extract_predictions, extract_primary_problem, extract_primary_solution, extract_product_features, extract_questions, extract_recipe, extract_recommendations, extract_references, extract_skills, extract_song_meaning, extract_sponsors, extract_videoid, extract_wisdom, extract_wisdom_agents, extract_wisdom_dm, extract_wisdom_nometa, extract_wisdom_short, generate_code_rules, t_extract_intro_sentences, t_extract_panel_topics
**GAMING**: create_npc, create_rpg_summary, summarize_rpg_session
**LEARNING**: analyze_answers, ask_uncle_duke, coding_master, create_diy, create_flash_cards, create_quiz, create_reading_plan, create_story_explanation, dialog_with_socrates, explain_code, explain_docs, explain_math, explain_project, explain_terms, extract_references, improve_academic_writing, provide_guidance, solve_with_cot, summarize_lecture, summarize_paper, to_flashcards, write_essay_pg
**OTHER**: extract_jokes
**RESEARCH**: analyze_candidates, analyze_claims, analyze_paper, analyze_paper_simple, analyze_patent, analyze_proposition, analyze_spiritual_text, analyze_tech_impact, capture_thinkers_work, create_academic_paper, extract_extraordinary_claims, extract_references, find_hidden_message, find_logical_fallacies, identify_dsrp_distinctions, identify_dsrp_perspectives, identify_dsrp_relationships, identify_dsrp_systems, improve_academic_writing, recommend_artists, summarize_paper, write_essay_pg, write_latex, write_micro_essay
**REVIEW**: analyze_cfp_submission, analyze_presentation, analyze_prose, get_wow_per_minute, judge_output, label_and_rate, rate_ai_response, rate_ai_result, rate_content, rate_value, review_code, review_design
**SECURITY**: analyze_email_headers, analyze_incident, analyze_logs, analyze_malware, analyze_risk, analyze_terraform_plan, analyze_threat_report, analyze_threat_report_cmds, analyze_threat_report_trends, ask_secure_by_design_questions, create_command, create_cyber_summary, create_graph_from_input, create_investigation_visualization, create_network_threat_landscape, create_report_finding, create_security_update, create_sigma_rules, create_stride_threat_model, create_threat_scenarios, create_ttrc_graph, create_ttrc_narrative, extract_ctf_writeup, improve_report_finding, recommend_pipeline_upgrades, review_code, t_red_team_thinking, t_threat_model_plans, write_hackerone_report, write_nuclei_template_rule, write_semgrep_rule
**SELF**: create_better_frame, create_diy, create_reading_plan, dialog_with_socrates, extract_article_wisdom, extract_book_ideas, extract_book_recommendations, extract_insights, extract_insights_dm, extract_most_redeeming_thing, extract_recipe, extract_recommendations, extract_song_meaning, extract_wisdom, extract_wisdom_dm, extract_wisdom_short, find_female_life_partner, provide_guidance, t_check_dunning_kruger, t_create_h3_career, t_describe_life_outlook, t_find_neglected_goals, t_give_encouragement
**STRATEGY**: analyze_military_strategy, create_better_frame, prepare_7s_strategy, t_analyze_challenge_handling, t_find_blindspots, t_find_negative_thinking, t_find_neglected_goals, t_red_team_thinking, t_threat_model_plans, t_visualize_mission_goals_projects
**SUMMARIZE**: capture_thinkers_work, create_5_sentence_summary, create_micro_summary, create_newsletter_entry, create_show_intro, create_summary, extract_core_message, extract_latest_video, extract_main_idea, summarize, summarize_board_meeting, summarize_debate, summarize_git_changes, summarize_git_diff, summarize_lecture, summarize_legislation, summarize_meeting, summarize_micro, summarize_newsletter, summarize_paper, summarize_pull-requests, summarize_rpg_session, youtube_summary
**VISUALIZE**: create_excalidraw_visualization, create_graph_from_input, create_idea_compass, create_investigation_visualization, create_keynote, create_logo, create_markmap_visualization, create_mermaid_visualization, create_mermaid_visualization_for_github, create_video_chapters, create_visualization, enrich_blog_post, show_fabric_options_markmap, t_visualize_mission_goals_projects
**WISDOM**: extract_alpha, extract_article_wisdom, extract_book_ideas, extract_insights, extract_most_redeeming_thing, extract_recommendations, extract_wisdom, extract_wisdom_dm, extract_wisdom_nometa, extract_wisdom_short
**WRITING**: analyze_prose_json, analyze_prose_pinker, apply_ul_tags, clean_text, compare_and_contrast, convert_to_markdown, create_5_sentence_summary, create_academic_paper, create_aphorisms, create_better_frame, create_design_document, create_diy, create_formal_email, create_hormozi_offer, create_keynote, create_micro_summary, create_newsletter_entry, create_prediction_block, create_prd, create_show_intro, create_story_explanation, create_summary, create_tags, create_user_story, enrich_blog_post, explain_docs, explain_terms, humanize, improve_academic_writing, improve_writing, label_and_rate, md_callout, official_pattern_template, recommend_talkpanel_topics, refine_design_document, summarize, summarize_debate, summarize_lecture, summarize_legislation, summarize_meeting, summarize_micro, summarize_newsletter, summarize_paper, summarize_rpg_session, t_create_opening_sentences, t_describe_life_outlook, t_extract_intro_sentences, t_extract_panel_topics, t_give_encouragement, t_year_in_review, transcribe_minutes, tweet, write_essay, write_essay_pg, write_hackerone_report, write_latex, write_micro_essay, write_pull-request
## Workflow Suggestions
- For complex analysis: First use an extract pattern, then an analyze pattern, finally a summarize pattern
- For content creation: Use relevant create_patterns followed by improve_ patterns for refinement
- For research projects: Combine extract_, analyze_, and summarize_ patterns in sequence
# INPUT
INPUT:
INPUT:

File diff suppressed because it is too large Load Diff

View File

@@ -1,919 +0,0 @@
# Suggest Pattern
## OVERVIEW
What It Does: Fabric is an open-source framework designed to augment human capabilities using AI, making it easier to integrate AI into daily tasks.
Why People Use It: Users leverage Fabric to seamlessly apply AI for solving everyday challenges, enhancing productivity, and fostering human creativity through technology.
## HOW TO USE IT
Most Common Syntax: The most common usage involves executing Fabric commands in the terminal, such as `fabric --pattern <PATTERN_NAME>`.
## COMMON USE CASES
For Summarizing Content: `fabric --pattern summarize`
For Analyzing Claims: `fabric --pattern analyze_claims`
For Extracting Wisdom from Videos: `fabric --pattern extract_wisdom`
For creating custom patterns: `fabric --pattern create_pattern`
- One possible place to store them is ~/.config/custom-fabric-patterns.
- Then when you want to use them, simply copy them into ~/.config/fabric/patterns.
`cp -a ~/.config/custom-fabric-patterns/* ~/.config/fabric/patterns/`
- Now you can run them with: `pbpaste | fabric -p your_custom_pattern`
## MOST IMPORTANT AND USED OPTIONS AND FEATURES
- **--pattern PATTERN, -p PATTERN**: Specifies the pattern (prompt) to use. Useful for applying specific AI prompts to your input.
- **--stream, -s**: Streams results in real-time. Ideal for getting immediate feedback from AI operations.
- **--update, -u**: Updates patterns. Ensures you're using the latest AI prompts for your tasks.
- **--model MODEL, -m MODEL**: Selects the AI model to use. Allows customization of the AI backend for different tasks.
- **--setup, -S**: Sets up your Fabric instance. Essential for first-time users to configure Fabric correctly.
- **--list, -l**: Lists available patterns. Helps users discover new AI prompts for various applications.
- **--context, -C**: Uses a Context file to add context to your pattern. Enhances the relevance of AI responses by providing additional background information.
## PATTERNS
**Key pattern to use: `suggest_pattern`** - suggests appropriate fabric patterns or commands based on user input.
### agility_story
Generate a user story and acceptance criteria in JSON format based on the given topic.
### ai
Interpret questions deeply and provide concise, insightful answers in Markdown bullet points.
### analyze_answers
Evaluate quiz answers for correctness based on learning objectives and generated quiz questions.
### analyze_bill
Analyzes legislation to identify overt and covert goals, examining bills for hidden agendas and true intentions.
### analyze_bill_short
Provides a concise analysis of legislation, identifying overt and covert goals in a brief, structured format.
### analyze_candidates
Compare and contrast two political candidates based on key issues and policies.
### analyze_cfp_submission
Review and evaluate conference speaking session submissions based on clarity, relevance, depth, and engagement potential.
### analyze_claims
Analyse and rate truth claims with evidence, counter-arguments, fallacies, and final recommendations.
### analyze_comments
Evaluate internet comments for content, categorize sentiment, and identify reasons for praise, criticism, and neutrality.
### analyze_debate
Rate debates on insight, emotionality, and present an unbiased, thorough analysis of arguments, agreements, and disagreements.
### analyze_email_headers
Provide cybersecurity analysis and actionable insights on SPF, DKIM, DMARC, and ARC email header results.
### analyze_incident
Efficiently extract and organize key details from cybersecurity breach articles, focusing on attack type, vulnerable components, attacker and target info, incident details, and remediation steps.
### analyze_interviewer_techniques
This exercise involves analyzing interviewer techniques, identifying their unique qualities, and succinctly articulating what makes them stand out in a clear, simple format.
### analyze_logs
Analyse server log files to identify patterns, anomalies, and issues, providing data-driven insights and recommendations for improving server reliability and performance.
### analyze_malware
Analyse malware details, extract key indicators, techniques, and potential detection strategies, and summarize findings concisely for a malware analyst's use in identifying and responding to threats.
### analyze_military_strategy
Analyse a historical battle, offering in-depth insights into strategic decisions, strengths, weaknesses, tactical approaches, logistical factors, pivotal moments, and consequences for a comprehensive military evaluation.
### analyze_mistakes
Analyse past mistakes in thinking patterns, map them to current beliefs, and offer recommendations to improve accuracy in predictions.
### analyze_paper
Analyses research papers by summarizing findings, evaluating rigor, and assessing quality to provide insights for documentation and review.
### analyze_paper_simple
Analyzes academic papers with a focus on primary findings, research quality, and study design evaluation.
### analyze_patent
Analyse a patent's field, problem, solution, novelty, inventive step, and advantages in detail while summarizing and extracting keywords.
### analyze_personality
Performs a deep psychological analysis of a person in the input, focusing on their behavior, language, and psychological traits.
### analyze_presentation
Reviews and critiques presentations by analyzing the content, speaker's underlying goals, self-focus, and entertainment value.
### analyze_product_feedback
A prompt for analyzing and organizing user feedback by identifying themes, consolidating similar comments, and prioritizing them based on usefulness.
### analyze_proposition
Analyzes a ballot proposition by identifying its purpose, impact, arguments for and against, and relevant background information.
### analyze_prose
Evaluates writing for novelty, clarity, and prose, providing ratings, improvement recommendations, and an overall score.
### analyze_prose_json
Evaluates writing for novelty, clarity, prose, and provides ratings, explanations, improvement suggestions, and an overall score in a JSON format.
### analyze_prose_pinker
Evaluates prose based on Steven Pinker's The Sense of Style, analyzing writing style, clarity, and bad writing elements.
### analyze_risk
Conducts a risk assessment of a third-party vendor, assigning a risk score and suggesting security controls based on analysis of provided documents and vendor website.
### analyze_sales_call
Rates sales call performance across multiple dimensions, providing scores and actionable feedback based on transcript analysis.
### analyze_spiritual_text
Compares and contrasts spiritual texts by analyzing claims and differences with the King James Bible.
### analyze_tech_impact
Analyzes the societal impact, ethical considerations, and sustainability of technology projects, evaluating their outcomes and benefits.
### analyze_terraform_plan
Analyzes Terraform plan outputs to assess infrastructure changes, security risks, cost implications, and compliance considerations.
### analyze_threat_report
Extracts surprising insights, trends, statistics, quotes, references, and recommendations from cybersecurity threat reports, summarizing key findings and providing actionable information.
### analyze_threat_report_cmds
Extract and synthesize actionable cybersecurity commands from provided materials, incorporating command-line arguments and expert insights for pentesters and non-experts.
### analyze_threat_report_trends
Extract up to 50 surprising, insightful, and interesting trends from a cybersecurity threat report in markdown format.
### answer_interview_question
Generates concise, tailored responses to technical interview questions, incorporating alternative approaches and evidence to demonstrate the candidate's expertise and experience.
### ask_secure_by_design_questions
Generates a set of security-focused questions to ensure a project is built securely by design, covering key components and considerations.
### ask_uncle_duke
Coordinates a team of AI agents to research and produce multiple software development solutions based on provided specifications, and conducts detailed code reviews to ensure adherence to best practices.
### capture_thinkers_work
Analyze philosophers or philosophies and provide detailed summaries about their teachings, background, works, advice, and related concepts in a structured template.
### check_agreement
Analyze contracts and agreements to identify important stipulations, issues, and potential gotchas, then summarize them in Markdown.
### clean_text
Fix broken or malformatted text by correcting line breaks, punctuation, capitalization, and paragraphs without altering content or spelling.
### coding_master
Explain a coding concept to a beginner, providing examples, and formatting code in markdown with specific output sections like ideas, recommendations, facts, and insights.
### compare_and_contrast
Compare and contrast a list of items in a markdown table, with items on the left and topics on top.
### convert_to_markdown
Convert content to clean, complete Markdown format, preserving all original structure, formatting, links, and code blocks without alterations.
### create_5_sentence_summary
Create concise summaries or answers to input at 5 different levels of depth, from 5 words to 1 word.
### create_academic_paper
Generate a high-quality academic paper in LaTeX format with clear concepts, structured content, and a professional layout.
### create_ai_jobs_analysis
Analyze job categories' susceptibility to automation, identify resilient roles, and provide strategies for personal adaptation to AI-driven changes in the workforce.
### create_aphorisms
Find and generate a list of brief, witty statements.
### create_art_prompt
Generates a detailed, compelling visual description of a concept, including stylistic references and direct AI instructions for creating art.
### create_better_frame
Identifies and analyzes different frames of interpreting reality, emphasizing the power of positive, productive lenses in shaping outcomes.
### create_coding_feature
Generates secure and composable code features using modern technology and best practices from project specifications.
### create_coding_project
Generate wireframes and starter code for any coding ideas that you have.
### create_command
Helps determine the correct parameters and switches for penetration testing tools based on a brief description of the objective.
### create_cyber_summary
Summarizes cybersecurity threats, vulnerabilities, incidents, and malware with a 25-word summary and categorized bullet points, after thoroughly analyzing and mapping the provided input.
### create_design_document
Creates a detailed design document for a system using the C4 model, addressing business and security postures, and including a system context diagram.
### create_diy
Creates structured "Do It Yourself" tutorial patterns by analyzing prompts, organizing requirements, and providing step-by-step instructions in Markdown format.
### create_excalidraw_visualization
Creates complex Excalidraw diagrams to visualize relationships between concepts and ideas in structured format.
### create_flash_cards
Creates flashcards for key concepts, definitions, and terms with question-answer format for educational purposes.
### create_formal_email
Crafts professional, clear, and respectful emails by analyzing context, tone, and purpose, ensuring proper structure and formatting.
### create_git_diff_commit
Generates Git commands and commit messages for reflecting changes in a repository, using conventional commits and providing concise shell commands for updates.
### create_graph_from_input
Generates a CSV file with progress-over-time data for a security program, focusing on relevant metrics and KPIs.
### create_hormozi_offer
Creates a customized business offer based on principles from Alex Hormozi's book, "$100M Offers."
### create_idea_compass
Organizes and structures ideas by exploring their definition, evidence, sources, and related themes or consequences.
### create_investigation_visualization
Creates detailed Graphviz visualizations of complex input, highlighting key aspects and providing clear, well-annotated diagrams for investigative analysis and conclusions.
### create_keynote
Creates TED-style keynote presentations with a clear narrative, structured slides, and speaker notes, emphasizing impactful takeaways and cohesive flow.
### create_loe_document
Creates detailed Level of Effort documents for estimating work effort, resources, and costs for tasks or projects.
### create_logo
Creates simple, minimalist company logos without text, generating AI prompts for vector graphic logos based on input.
### create_markmap_visualization
Transforms complex ideas into clear visualizations using MarkMap syntax, simplifying concepts into diagrams with relationships, boxes, arrows, and labels.
### create_mermaid_visualization
Creates detailed, standalone visualizations of concepts using Mermaid (Markdown) syntax, ensuring clarity and coherence in diagrams.
### create_mermaid_visualization_for_github
Creates standalone, detailed visualizations using Mermaid (Markdown) syntax to effectively explain complex concepts, ensuring clarity and precision.
### create_micro_summary
Summarizes content into a concise, 20-word summary with main points and takeaways, formatted in Markdown.
### create_mnemonic_phrases
Creates memorable mnemonic sentences from given words to aid in memory retention and learning.
### create_network_threat_landscape
Analyzes open ports and services from a network scan and generates a comprehensive, insightful, and detailed security threat report in Markdown.
### create_newsletter_entry
Condenses provided article text into a concise, objective, newsletter-style summary with a title in the style of Frontend Weekly.
### create_npc
Generates a detailed D&D 5E NPC, including background, flaws, stats, appearance, personality, goals, and more in Markdown format.
### create_pattern
Extracts, organizes, and formats LLM/AI prompts into structured sections, detailing the AI's role, instructions, output format, and any provided examples for clarity and accuracy.
### create_prd
Creates a precise Product Requirements Document (PRD) in Markdown based on input.
### create_prediction_block
Extracts and formats predictions from input into a structured Markdown block for a blog post.
### create_quiz
Generates review questions based on learning objectives from the input, adapted to the specified student level, and outputs them in a clear markdown format.
### create_reading_plan
Creates a three-phase reading plan based on an author or topic to help the user become significantly knowledgeable, including core, extended, and supplementary readings.
### create_recursive_outline
Breaks down complex tasks or projects into manageable, hierarchical components with recursive outlining for clarity and simplicity.
### create_report_finding
Creates a detailed, structured security finding report in markdown, including sections on Description, Risk, Recommendations, References, One-Sentence-Summary, and Quotes.
### create_rpg_summary
Summarizes an in-person RPG session with key events, combat details, player stats, and role-playing highlights in a structured format.
### create_security_update
Creates concise security updates for newsletters, covering stories, threats, advisories, vulnerabilities, and a summary of key issues.
### create_show_intro
Creates compelling short intros for podcasts, summarizing key topics and themes discussed in the episode.
### create_sigma_rules
Extracts Tactics, Techniques, and Procedures (TTPs) from security news and converts them into Sigma detection rules for host-based detections.
### create_story_explanation
Summarizes complex content in a clear, approachable story format that makes the concepts easy to understand.
### create_stride_threat_model
Create a STRIDE-based threat model for a system design, identifying assets, trust boundaries, data flows, and prioritizing threats with mitigations.
### create_summary
Summarizes content into a 20-word sentence, 10 main points (16 words max), and 5 key takeaways in Markdown format.
### create_tags
Identifies at least 5 tags from text content for mind mapping tools, including authors and existing tags if present.
### create_threat_scenarios
Identifies likely attack methods for any system by providing a narrative-based threat model, balancing risk and opportunity.
### create_ttrc_graph
Creates a CSV file showing the progress of Time to Remediate Critical Vulnerabilities over time using given data.
### create_ttrc_narrative
Creates a persuasive narrative highlighting progress in reducing the Time to Remediate Critical Vulnerabilities metric over time.
### create_upgrade_pack
Extracts world model and task algorithm updates from content, providing beliefs about how the world works and task performance.
### create_user_story
Writes concise and clear technical user stories for new features in complex software programs, formatted for all stakeholders.
### create_video_chapters
Extracts interesting topics and timestamps from a transcript, providing concise summaries of key moments.
### create_visualization
Transforms complex ideas into visualizations using intricate ASCII art, simplifying concepts where necessary.
### dialog_with_socrates
Engages in deep, meaningful dialogues to explore and challenge beliefs using the Socratic method.
### enrich_blog_post
Enhances Markdown blog files by applying instructions to improve structure, visuals, and readability for HTML rendering.
### explain_code
Explains code, security tool output, configuration text, and answers questions based on the provided input.
### explain_docs
Improves and restructures tool documentation into clear, concise instructions, including overviews, usage, use cases, and key features.
### explain_math
Helps you understand mathematical concepts in a clear and engaging way.
### explain_project
Summarizes project documentation into clear, concise sections covering the project, problem, solution, installation, usage, and examples.
### explain_terms
Produces a glossary of advanced terms from content, providing a definition, analogy, and explanation of why each term matters.
### export_data_as_csv
Extracts and outputs all data structures from the input in properly formatted CSV data.
### extract_algorithm_update_recommendations
Extracts concise, practical algorithm update recommendations from the input and outputs them in a bulleted list.
### extract_article_wisdom
Extracts surprising, insightful, and interesting information from content, categorizing it into sections like summary, ideas, quotes, facts, references, and recommendations.
### extract_book_ideas
Extracts and outputs 50 to 100 of the most surprising, insightful, and interesting ideas from a book's content.
### extract_book_recommendations
Extracts and outputs 50 to 100 practical, actionable recommendations from a book's content.
### extract_business_ideas
Extracts top business ideas from content and elaborates on the best 10 with unique differentiators.
### extract_controversial_ideas
Extracts and outputs controversial statements and supporting quotes from the input in a structured Markdown list.
### extract_core_message
Extracts and outputs a clear, concise sentence that articulates the core message of a given text or body of work.
### extract_ctf_writeup
Extracts a short writeup from a warstory-like text about a cyber security engagement.
### extract_domains
Extracts domains and URLs from content to identify sources used for articles, newsletters, and other publications.
### extract_extraordinary_claims
Extracts and outputs a list of extraordinary claims from conversations, focusing on scientifically disputed or false statements.
### extract_ideas
Extracts and outputs all the key ideas from input, presented as 15-word bullet points in Markdown.
### extract_insights
Extracts and outputs the most powerful and insightful ideas from text, formatted as 16-word bullet points in the INSIGHTS section, also IDEAS section.
### extract_insights_dm
Extracts and outputs all valuable insights and a concise summary of the content, including key points and topics discussed.
### extract_instructions
Extracts clear, actionable step-by-step instructions and main objectives from instructional video transcripts, organizing them into a concise list.
### extract_jokes
Extracts jokes from text content, presenting each joke with its punchline in separate bullet points.
### extract_latest_video
Extracts the latest video URL from a YouTube RSS feed and outputs the URL only.
### extract_main_activities
Extracts key events and activities from transcripts or logs, providing a summary of what happened.
### extract_main_idea
Extracts the main idea and key recommendation from the input, summarizing them in 15-word sentences.
### extract_most_redeeming_thing
Extracts the most redeeming aspect from an input, summarizing it in a single 15-word sentence.
### extract_patterns
Extracts and analyzes recurring, surprising, and insightful patterns from input, providing detailed analysis and advice for builders.
### extract_poc
Extracts proof of concept URLs and validation methods from security reports, providing the URL and command to run.
### extract_predictions
Extracts predictions from input, including specific details such as date, confidence level, and verification method.
### extract_primary_problem
Extracts the primary problem with the world as presented in a given text or body of work.
### extract_primary_solution
Extracts the primary solution for the world as presented in a given text or body of work.
### extract_product_features
Extracts and outputs a list of product features from the provided input in a bulleted format.
### extract_questions
Extracts and outputs all questions asked by the interviewer in a conversation or interview.
### extract_recipe
Extracts and outputs a recipe with a short meal description, ingredients with measurements, and preparation steps.
### extract_recommendations
Extracts and outputs concise, practical recommendations from a given piece of content in a bulleted list.
### extract_references
Extracts and outputs a bulleted list of references to art, stories, books, literature, and other sources from content.
### extract_skills
Extracts and classifies skills from a job description into a table, separating each skill and classifying it as either hard or soft.
### extract_song_meaning
Analyzes a song to provide a summary of its meaning, supported by detailed evidence from lyrics, artist commentary, and fan analysis.
### extract_sponsors
Extracts and lists official sponsors and potential sponsors from a provided transcript.
### extract_videoid
Extracts and outputs the video ID from any given URL.
### extract_wisdom
Extracts surprising, insightful, and interesting information from text on topics like human flourishing, AI, learning, and more.
### extract_wisdom_agents
Extracts valuable insights, ideas, quotes, and references from content, emphasizing topics like human flourishing, AI, learning, and technology.
### extract_wisdom_dm
Extracts all valuable, insightful, and thought-provoking information from content, focusing on topics like human flourishing, AI, learning, and technology.
### extract_wisdom_nometa
Extracts insights, ideas, quotes, habits, facts, references, and recommendations from content, focusing on human flourishing, AI, technology, and related topics.
### find_female_life_partner
Analyzes criteria for finding a female life partner and provides clear, direct, and poetic descriptions.
### find_hidden_message
Extracts overt and hidden political messages, justifications, audience actions, and a cynical analysis from content.
### find_logical_fallacies
Identifies and analyzes fallacies in arguments, classifying them as formal or informal with detailed reasoning.
### get_wow_per_minute
Determines the wow-factor of content per minute based on surprise, novelty, insight, value, and wisdom, measuring how rewarding the content is for the viewer.
### get_youtube_rss
Returns the RSS URL for a given YouTube channel based on the channel ID or URL.
### humanize
Rewrites AI-generated text to sound natural, conversational, and easy to understand, maintaining clarity and simplicity.
### identify_dsrp_distinctions
Encourages creative, systems-based thinking by exploring distinctions, boundaries, and their implications, drawing on insights from prominent systems thinkers.
### identify_dsrp_perspectives
Explores the concept of distinctions in systems thinking, focusing on how boundaries define ideas, influence understanding, and reveal or obscure insights.
### identify_dsrp_relationships
Encourages exploration of connections, distinctions, and boundaries between ideas, inspired by systems thinkers to reveal new insights and patterns in complex systems.
### identify_dsrp_systems
Encourages organizing ideas into systems of parts and wholes, inspired by systems thinkers to explore relationships and how changes in organization impact meaning and understanding.
### identify_job_stories
Identifies key job stories or requirements for roles.
### improve_academic_writing
Refines text into clear, concise academic language while improving grammar, coherence, and clarity, with a list of changes.
### improve_prompt
Improves an LLM/AI prompt by applying expert prompt writing strategies for better results and clarity.
### improve_report_finding
Improves a penetration test security finding by providing detailed descriptions, risks, recommendations, references, quotes, and a concise summary in markdown format.
### improve_writing
Refines text by correcting grammar, enhancing style, improving clarity, and maintaining the original meaning.
### judge_output
Evaluates Honeycomb queries by judging their effectiveness, providing critiques and outcomes based on language nuances and analytics relevance.
### label_and_rate
Labels content with up to 20 single-word tags and rates it based on idea count and relevance to human meaning, AI, and other related themes, assigning a tier (S, A, B, C, D) and a quality score.
### md_callout
Classifies content and generates a markdown callout based on the provided text, selecting the most appropriate type.
### official_pattern_template
Template to use if you want to create new fabric patterns.
### prepare_7s_strategy
Prepares a comprehensive briefing document from 7S's strategy capturing organizational profile, strategic elements, and market dynamics with clear, concise, and organized content.
### provide_guidance
Provides psychological and life coaching advice, including analysis, recommendations, and potential diagnoses, with a compassionate and honest tone.
### rate_ai_response
Rates the quality of AI responses by comparing them to top human expert performance, assigning a letter grade, reasoning, and providing a 1-100 score based on the evaluation.
### rate_ai_result
Assesses the quality of AI/ML/LLM work by deeply analyzing content, instructions, and output, then rates performance based on multiple dimensions, including coverage, creativity, and interdisciplinary thinking.
### rate_content
Labels content with up to 20 single-word tags and rates it based on idea count and relevance to human meaning, AI, and other related themes, assigning a tier (S, A, B, C, D) and a quality score.
### rate_value
Produces the best possible output by deeply analyzing and understanding the input and its intended purpose.
### raw_query
Fully digests and contemplates the input to produce the best possible result based on understanding the sender's intent.
### recommend_artists
Recommends a personalized festival schedule with artists aligned to your favorite styles and interests, including rationale.
### recommend_pipeline_upgrades
Optimizes vulnerability-checking pipelines by incorporating new information and improving their efficiency, with detailed explanations of changes.
### recommend_talkpanel_topics
Produces a clean set of proposed talks or panel talking points for a person based on their interests and goals, formatted for submission to a conference organizer.
### refine_design_document
Refines a design document based on a design review by analyzing, mapping concepts, and implementing changes using valid Markdown.
### review_design
Reviews and analyzes architecture design, focusing on clarity, component design, system integrations, security, performance, scalability, and data management.
### sanitize_broken_html_to_markdown
Converts messy HTML into clean, properly formatted Markdown, applying custom styling and ensuring compatibility with Vite.
### show_fabric_options_markmap
Visualizes the functionality of the Fabric framework by representing its components, commands, and features based on the provided input.
### solve_with_cot
Provides detailed, step-by-step responses with chain of thought reasoning, using structured thinking, reflection, and output sections.
### suggest_pattern
Suggests appropriate fabric patterns or commands based on user input, providing clear explanations and options for users.
### summarize
Summarizes content into a 20-word sentence, main points, and takeaways, formatted with numbered lists in Markdown.
### summarize_board_meeting
Creates formal meeting notes from board meeting transcripts for corporate governance documentation.
### summarize_debate
Summarizes debates, identifies primary disagreement, extracts arguments, and provides analysis of evidence and argument strength to predict outcomes.
### summarize_git_changes
Summarizes recent project updates from the last 7 days, focusing on key changes with enthusiasm.
### summarize_git_diff
Summarizes and organizes Git diff changes with clear, succinct commit messages and bullet points.
### summarize_lecture
Extracts relevant topics, definitions, and tools from lecture transcripts, providing structured summaries with timestamps and key takeaways.
### summarize_legislation
Summarizes complex political proposals and legislation by analyzing key points, proposed changes, and providing balanced, positive, and cynical characterizations.
### summarize_meeting
Analyzes meeting transcripts to extract a structured summary, including an overview, key points, tasks, decisions, challenges, timeline, references, and next steps.
### summarize_micro
Summarizes content into a 20-word sentence, 3 main points, and 3 takeaways, formatted in clear, concise Markdown.
### summarize_newsletter
Extracts the most meaningful, interesting, and useful content from a newsletter, summarizing key sections such as content, opinions, tools, companies, and follow-up items in clear, structured Markdown.
### summarize_paper
Summarizes an academic paper by detailing its title, authors, technical approach, distinctive features, experimental setup, results, advantages, limitations, and conclusion in a clear, structured format using human-readable Markdown.
### summarize_prompt
Summarizes AI chat prompts by describing the primary function, unique approach, and expected output in a concise paragraph. The summary is focused on the prompt's purpose without unnecessary details or formatting.
### summarize_pull-requests
Summarizes pull requests for a coding project by providing a summary and listing the top PRs with human-readable descriptions.
### summarize_rpg_session
Summarizes a role-playing game session by extracting key events, combat stats, character changes, quotes, and more.
### t_analyze_challenge_handling
Provides 8-16 word bullet points evaluating how well challenges are being addressed, calling out any lack of effort.
### t_check_metrics
Analyzes deep context from the TELOS file and input instruction, then provides a wisdom-based output while considering metrics and KPIs to assess recent improvements.
### t_create_h3_career
Summarizes context and produces wisdom-based output by deeply analyzing both the TELOS File and the input instruction, considering the relationship between the two.
### t_create_opening_sentences
Describes from TELOS file the person's identity, goals, and actions in 4 concise, 32-word bullet points, humbly.
### t_describe_life_outlook
Describes from TELOS file a person's life outlook in 5 concise, 16-word bullet points.
### t_extract_intro_sentences
Summarizes from TELOS file a person's identity, work, and current projects in 5 concise and grounded bullet points.
### t_extract_panel_topics
Creates 5 panel ideas with titles and descriptions based on deep context from a TELOS file and input.
### t_find_blindspots
Identify potential blindspots in thinking, frames, or models that may expose the individual to error or risk.
### t_find_negative_thinking
Analyze a TELOS file and input to identify negative thinking in documents or journals, followed by tough love encouragement.
### t_find_neglected_goals
Analyze a TELOS file and input instructions to identify goals or projects that have not been worked on recently.
### t_give_encouragement
Analyze a TELOS file and input instructions to evaluate progress, provide encouragement, and offer recommendations for continued effort.
### t_red_team_thinking
Analyze a TELOS file and input instructions to red-team thinking, models, and frames, then provide recommendations for improvement.
### t_threat_model_plans
Analyze a TELOS file and input instructions to create threat models for a life plan and recommend improvements.
### t_visualize_mission_goals_projects
Analyze a TELOS file and input instructions to create an ASCII art diagram illustrating the relationship of missions, goals, and projects.
### t_year_in_review
Analyze a TELOS file to create insights about a person or entity, then summarize accomplishments and visualizations in bullet points.
### to_flashcards
Create Anki flashcards from a given text, focusing on concise, optimized questions and answers without external context.
### transcribe_minutes
Extracts (from meeting transcription) meeting minutes, identifying actionables, insightful ideas, decisions, challenges, and next steps in a structured format.
### translate
Translates sentences or documentation into the specified language code while maintaining the original formatting and tone.
### tweet
Provides a step-by-step guide on crafting engaging tweets with emojis, covering Twitter basics, account creation, features, and audience targeting.
### write_essay
Writes essays in the style of a specified author, embodying their unique voice, vocabulary, and approach. Uses `author_name` variable.
### write_essay_pg
Writes concise, clear essays in the style of Paul Graham, focusing on simplicity, clarity, and illumination of the provided topic.
### write_hackerone_report
Generates concise, clear, and reproducible bug bounty reports, detailing vulnerability impact, steps to reproduce, and exploit details for triagers.
### write_latex
Generates syntactically correct LaTeX code for a new.tex document, ensuring proper formatting and compatibility with pdflatex.
### write_micro_essay
Writes concise, clear, and illuminating essays on the given topic in the style of Paul Graham.
### write_nuclei_template_rule
Generates Nuclei YAML templates for detecting vulnerabilities using HTTP requests, matchers, extractors, and dynamic data extraction.
### write_pull-request
Drafts detailed pull request descriptions, explaining changes, providing reasoning, and identifying potential bugs from the git diff command output.
### write_semgrep_rule
Creates accurate and working Semgrep rules based on input, following syntax guidelines and specific language considerations.
### youtube_summary
Create concise, timestamped Youtube video summaries that highlight key points.

140
docs/Shell-Completions.md Normal file
View File

@@ -0,0 +1,140 @@
# Shell Completions for Fabric
Fabric comes with shell completion support for Zsh, Bash, and Fish shells. These completions provide intelligent tab-completion for commands, flags, patterns, models, contexts, and more.
## Quick Setup (Automated)
You can install completions without cloning the repo:
```bash
# No-clone install (Zsh/Bash/Fish supported)
curl -fsSL https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions/setup-completions.sh | sh
# Optional: dry-run first
curl -fsSL https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions/setup-completions.sh | sh -s -- --dry-run
# Optional: override the download source
FABRIC_COMPLETIONS_BASE_URL="https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions" \
sh -c "$(curl -fsSL https://raw.githubusercontent.com/danielmiessler/Fabric/refs/heads/main/completions/setup-completions.sh)"
```
Or, if you have the repository locally:
```bash
# Run the automated setup script from a cloned repo
./completions/setup-completions.sh
# Or see what it would do first
./completions/setup-completions.sh --dry-run
```
The script will:
- Detect whether you have `fabric` or `fabric-ai` installed
- Detect your current shell (zsh, bash, or fish)
- Use your existing `$fpath` directories (for zsh) or standard completion directories
- Install the completion file with the correct name
- Provide instructions for enabling the completions
If the completion files aren't present locally (e.g., when running via `curl`), the script will automatically download them from GitHub.
For manual installation or troubleshooting, see the detailed instructions below.
## Manual Installation
### Zsh
1. Copy the completion file to a directory in your `$fpath`:
```bash
sudo cp completions/_fabric /usr/local/share/zsh/site-functions/
```
2. **Important**: If you installed fabric as `fabric-ai`, create a symlink so completions work:
```bash
sudo ln -s /usr/local/share/zsh/site-functions/_fabric /usr/local/share/zsh/site-functions/_fabric-ai
```
3. Restart your shell or reload completions:
```bash
autoload -U compinit && compinit
```
### Bash
1. Copy the completion file to a standard completion directory:
```bash
# System-wide installation
sudo cp completions/fabric.bash /etc/bash_completion.d/
# Or user-specific installation
mkdir -p ~/.local/share/bash-completion/completions/
cp completions/fabric.bash ~/.local/share/bash-completion/completions/fabric
```
2. **Important**: If you installed fabric as `fabric-ai`, create a symlink:
```bash
# For system-wide installation
sudo ln -s /etc/bash_completion.d/fabric.bash /etc/bash_completion.d/fabric-ai.bash
# Or for user-specific installation
ln -s ~/.local/share/bash-completion/completions/fabric ~/.local/share/bash-completion/completions/fabric-ai
```
3. Restart your shell or source the completion:
```bash
source ~/.bashrc
```
### Fish
1. Copy the completion file to Fish's completion directory:
```bash
mkdir -p ~/.config/fish/completions
cp completions/fabric.fish ~/.config/fish/completions/
```
2. **Important**: If you installed fabric as `fabric-ai`, create a symlink:
```bash
ln -s ~/.config/fish/completions/fabric.fish ~/.config/fish/completions/fabric-ai.fish
```
3. Fish will automatically load the completions (no restart needed).
## Features
The completions provide intelligent suggestions for:
- **Patterns**: Tab-complete available patterns with `-p` or `--pattern`
- **Models**: Tab-complete available models with `-m` or `--model`
- **Contexts**: Tab-complete contexts for context-related flags
- **Sessions**: Tab-complete sessions for session-related flags
- **Strategies**: Tab-complete available strategies
- **Extensions**: Tab-complete registered extensions
- **Gemini Voices**: Tab-complete TTS voices for `--voice`
- **File paths**: Smart file completion for attachment, output, and config options
- **Flag completion**: All available command-line flags and options
## Alternative Installation Method
You can also source the completion files directly in your shell's configuration file:
- **Zsh**: Add to `~/.zshrc`: `source /path/to/fabric/completions/_fabric`
- **Bash**: Add to `~/.bashrc`: `source /path/to/fabric/completions/fabric.bash`
- **Fish**: The file-based installation method above is preferred for Fish
## Troubleshooting
- If completions don't work, ensure the completion files have proper permissions
- For Zsh, verify that the completion directory is in your `$fpath`
- If you renamed the fabric binary, make sure to create the appropriate symlinks as described above
- Restart your shell after installation to ensure completions are loaded
The completion system dynamically queries the fabric command for current patterns, models, and other resources, so your completions will always be up-to-date with your fabric installation.

View File

@@ -0,0 +1,139 @@
# Using Speech-To-Text (STT) with Fabric
Fabric supports speech-to-text transcription of audio and video files using OpenAI's transcription models. This feature allows you to convert spoken content into text that can then be processed through Fabric's patterns.
## Overview
The STT feature integrates OpenAI's Whisper and GPT-4o transcription models to convert audio/video files into text. The transcribed text is automatically passed as input to your chosen pattern or chat session.
## Requirements
- OpenAI API key configured in Fabric
- For files larger than 25MB: `ffmpeg` installed on your system
- Supported audio/video formats: `.mp3`, `.mp4`, `.mpeg`, `.mpga`, `.m4a`, `.wav`, `.webm`
## Basic Usage
### Simple Transcription
To transcribe an audio file and send the result to a pattern:
```bash
fabric --transcribe-file /path/to/audio.mp3 --transcribe-model whisper-1 --pattern summarize
```
### Transcription Only
To just transcribe a file without applying a pattern:
```bash
fabric --transcribe-file /path/to/audio.mp3 --transcribe-model whisper-1
```
## Command Line Flags
### Required Flags
- `--transcribe-file`: Path to the audio or video file to transcribe
- `--transcribe-model`: Model to use for transcription (required when using transcription)
### Optional Flags
- `--split-media-file`: Automatically split files larger than 25MB into chunks using ffmpeg
## Available Models
You can list all available transcription models with:
```bash
fabric --list-transcription-models
```
Currently supported models:
- `whisper-1`: OpenAI's Whisper model
- `gpt-4o-mini-transcribe`: GPT-4o Mini transcription model
- `gpt-4o-transcribe`: GPT-4o transcription model
## File Size Handling
### Files Under 25MB
Files under the 25MB limit are processed directly without any special handling.
### Files Over 25MB
For files exceeding OpenAI's 25MB limit, you have two options:
1. **Manual handling**: The command will fail with an error message suggesting to use `--split-media-file`
2. **Automatic splitting**: Use the `--split-media-file` flag to automatically split the file into chunks
```bash
fabric --transcribe-file large_recording.mp4 --transcribe-model whisper-1 --split-media-file --pattern summarize
```
When splitting is enabled:
- Fabric uses `ffmpeg` to split the file into 10-minute segments initially
- If segments are still too large, it reduces the segment time by half repeatedly
- All segments are transcribed and the results are concatenated
- Temporary files are automatically cleaned up after processing
## Integration with Patterns
The transcribed text is seamlessly integrated into Fabric's workflow:
1. File is transcribed using the specified model
2. Transcribed text becomes the input message
3. Text is sent to the specified pattern or chat session
### Example Workflows
**Meeting transcription and summarization:**
```bash
fabric --transcribe-file meeting.mp4 --transcribe-model gpt-4o-transcribe --pattern summarize
```
**Interview analysis:**
```bash
fabric --transcribe-file interview.mp3 --transcribe-model whisper-1 --pattern extract_insights
```
**Large video file processing:**
```bash
fabric --transcribe-file presentation.mp4 --transcribe-model gpt-4o-transcribe --split-media-file --pattern create_summary
```
## Error Handling
Common error scenarios:
- **Unsupported format**: Only the listed audio/video formats are supported
- **File too large**: Use `--split-media-file` for files over 25MB
- **Missing ffmpeg**: Install ffmpeg for automatic file splitting
- **Invalid model**: Use `--list-transcription-models` to see available models
- **Missing model**: The `--transcribe-model` flag is required when using `--transcribe-file`
## Technical Details
### Implementation
- Transcription is handled in `internal/cli/transcribe.go:14`
- OpenAI-specific implementation in `internal/plugins/ai/openai/openai_audio.go:41`
- File splitting uses ffmpeg with configurable segment duration
- Supports any vendor that implements the `transcriber` interface
### Processing Pipeline
1. CLI validates file format and size
2. If file > 25MB and splitting enabled, file is split using ffmpeg
3. Each file/segment is sent to OpenAI's transcription API
4. Results are concatenated with spaces between segments
5. Transcribed text is passed as input to the main Fabric pipeline
### Vendor Support
Currently, only OpenAI is supported for transcription, but the interface allows for future expansion to other vendors that provide transcription capabilities.

View File

@@ -62,25 +62,25 @@ Pass additional arguments to yt-dlp for advanced functionality. **User-provided
```bash
# Use browser cookies for age-restricted or private videos
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args "--cookies-from-browser brave"
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args="--cookies-from-browser brave"
# Override language selection (takes precedence over -g flag)
fabric -g en -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args "--sub-langs es,fr"
fabric -g en -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args="--sub-langs es,fr"
# Use specific format
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args "--format best"
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args="--format best"
# Handle rate limiting (slow down requests)
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args "--sleep-requests 1"
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args="--sleep-requests 1"
# Multiple arguments (use quotes)
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args "--cookies-from-browser firefox --write-info-json"
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args="--cookies-from-browser firefox --write-info-json"
# Combine rate limiting with authentication
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args "--cookies-from-browser brave --sleep-requests 1"
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args="--cookies-from-browser brave --sleep-requests 1"
# Override subtitle format (takes precedence over built-in --sub-format vtt)
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args "--sub-format srt"
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --yt-dlp-args="--sub-format srt"
```
#### Argument Precedence
@@ -196,7 +196,7 @@ fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --pattern write_blog_post
### Common Issues
1. **"yt-dlp not found"**: Install yt-dlp using pip or your package manager
2. **Age-restricted videos**: Use `--yt-dlp-args "--cookies-from-browser BROWSER"`
2. **Age-restricted videos**: Use `--yt-dlp-args="--cookies-from-browser BROWSER"`
3. **No subtitles available**: Some videos don't have auto-generated subtitles
4. **API rate limits**: YouTube API has daily quotas for comments/metadata
5. **HTTP 429 errors**: YouTube is rate limiting subtitle requests
@@ -208,8 +208,8 @@ fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" --pattern write_blog_post
- **"No transcript content found"**: Video may not have subtitles available
- **"HTTP Error 429: Too Many Requests"**: YouTube rate limit exceeded. This is increasingly common. Solutions:
- **Wait 10-30 minutes and try again** (most effective)
- Use longer sleep: `--yt-dlp-args "--sleep-requests 5"`
- Try with browser cookies: `--yt-dlp-args "--cookies-from-browser brave --sleep-requests 5"`
- Use longer sleep: `--yt-dlp-args="--sleep-requests 5"`
- Try with browser cookies: `--yt-dlp-args="--cookies-from-browser brave --sleep-requests 5"`
- **Try a different video** - some videos are less restricted
- **Use a VPN** - different IP address may help
- **Try without language specification** - let yt-dlp choose any available language
@@ -270,7 +270,7 @@ fabric -y "https://www.youtube.com/watch?v=dQw4w9WgXcQ" --pattern summarize --st
```bash
fabric -y "https://www.youtube.com/watch?v=VIDEO_ID" \
--yt-dlp-args "--cookies-from-browser chrome" \
--yt-dlp-args="--cookies-from-browser chrome" \
--transcript-with-timestamps \
--comments \
--pattern comprehensive_analysis \
@@ -291,7 +291,7 @@ fabric -y "https://www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLvVUxpDnx4C0823
```bash
# Built-in language selection (-g es) is overridden by user args
fabric -g es -y "https://www.youtube.com/watch?v=VIDEO_ID" \
--yt-dlp-args "--sub-langs fr,de,en" \
--yt-dlp-args="--sub-langs fr,de,en" \
--pattern translate
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

6
go.mod
View File

@@ -5,7 +5,7 @@ go 1.24.0
toolchain go1.24.2
require (
github.com/anthropics/anthropic-sdk-go v1.7.0
github.com/anthropics/anthropic-sdk-go v1.9.1
github.com/atotto/clipboard v0.1.4
github.com/aws/aws-sdk-go-v2 v1.36.4
github.com/aws/aws-sdk-go-v2/config v1.27.27
@@ -19,6 +19,7 @@ require (
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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/mattn/go-sqlite3 v1.14.28
github.com/ollama/ollama v0.9.0
github.com/openai/openai-go v1.8.2
@@ -37,7 +38,6 @@ require (
require (
github.com/google/go-cmp v0.7.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
)
require (
@@ -117,7 +117,7 @@ require (
go.opentelemetry.io/otel/metric v1.36.0 // indirect
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/crypto v0.40.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.16.0 // indirect

14
go.sum
View File

@@ -17,10 +17,8 @@ github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kk
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/anthropics/anthropic-sdk-go v1.4.0 h1:fU1jKxYbQdQDiEXCxeW5XZRIOwKevn/PMg8Ay1nnUx0=
github.com/anthropics/anthropic-sdk-go v1.4.0/go.mod h1:AapDW22irxK2PSumZiQXYUFvsdQgkwIWlpESweWZI/c=
github.com/anthropics/anthropic-sdk-go v1.7.0 h1:5iVf5fG/2gqVsOce8mq02r/WdgqpokM/8DXg2Ue6C9Y=
github.com/anthropics/anthropic-sdk-go v1.7.0/go.mod h1:3qSNQ5NrAmjC8A2ykuruSQttfqfdEYNZY5o8c0XSHB8=
github.com/anthropics/anthropic-sdk-go v1.9.1 h1:raRhZKmayVSVZtLpLDd6IsMXvxLeeSU03/2IBTerWlg=
github.com/anthropics/anthropic-sdk-go v1.9.1/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -270,8 +268,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
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/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
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=
@@ -329,8 +327,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@@ -18,10 +18,23 @@ func handleChatProcessing(currentFlags *Flags, registry *core.PluginRegistry, me
if messageTools != "" {
currentFlags.AppendMessage(messageTools)
}
// Check for pattern-specific model via environment variable
if currentFlags.Pattern != "" && currentFlags.Model == "" {
envVar := "FABRIC_MODEL_" + strings.ToUpper(strings.ReplaceAll(currentFlags.Pattern, "-", "_"))
if modelSpec := os.Getenv(envVar); modelSpec != "" {
parts := strings.SplitN(modelSpec, "|", 2)
if len(parts) == 2 {
currentFlags.Vendor = parts[0]
currentFlags.Model = parts[1]
} else {
currentFlags.Model = modelSpec
}
}
}
var chatter *core.Chatter
if chatter, err = registry.GetChatter(currentFlags.Model, currentFlags.ModelContextLength,
currentFlags.Strategy, currentFlags.Stream, currentFlags.DryRun); err != nil {
currentFlags.Vendor, currentFlags.Strategy, currentFlags.Stream, currentFlags.DryRun); err != nil {
return
}

View File

@@ -74,6 +74,15 @@ func Cli(version string) (err error) {
return
}
// Handle transcription if specified
if currentFlags.TranscribeFile != "" {
var transcriptionMessage string
if transcriptionMessage, err = handleTranscription(currentFlags, registry); err != nil {
return
}
currentFlags.Message = AppendMessage(currentFlags.Message, transcriptionMessage)
}
// Process HTML readability if needed
if currentFlags.HtmlReadability {
if msg, cleanErr := converter.HtmlReadability(currentFlags.Message); cleanErr != nil {

View File

@@ -13,6 +13,7 @@ import (
"github.com/danielmiessler/fabric/internal/chat"
"github.com/danielmiessler/fabric/internal/domain"
debuglog "github.com/danielmiessler/fabric/internal/log"
"github.com/danielmiessler/fabric/internal/util"
"github.com/jessevdk/go-flags"
"golang.org/x/text/language"
@@ -23,89 +24,90 @@ import (
// Chat parameter defaults set in the struct tags must match domain.Default* constants
type Flags struct {
Pattern string `short:"p" long:"pattern" yaml:"pattern" description:"Choose a pattern from the available patterns" default:""`
PatternVariables map[string]string `short:"v" long:"variable" description:"Values for pattern variables, e.g. -v=#role:expert -v=#points:30"`
Context string `short:"C" long:"context" description:"Choose a context from the available contexts" default:""`
Session string `long:"session" description:"Choose a session from the available sessions"`
Attachments []string `short:"a" long:"attachment" description:"Attachment path or URL (e.g. for OpenAI image recognition messages)"`
Setup bool `short:"S" long:"setup" description:"Run setup for all reconfigurable parts of fabric"`
Temperature float64 `short:"t" long:"temperature" yaml:"temperature" description:"Set temperature" default:"0.7"`
TopP float64 `short:"T" long:"topp" yaml:"topp" description:"Set top P" default:"0.9"`
Stream bool `short:"s" long:"stream" yaml:"stream" description:"Stream"`
PresencePenalty float64 `short:"P" long:"presencepenalty" yaml:"presencepenalty" description:"Set presence penalty" default:"0.0"`
Raw bool `short:"r" long:"raw" yaml:"raw" description:"Use the defaults of the model without sending chat options (like temperature etc.) and use the user role instead of the system role for patterns."`
FrequencyPenalty float64 `short:"F" long:"frequencypenalty" yaml:"frequencypenalty" description:"Set frequency penalty" default:"0.0"`
ListPatterns bool `short:"l" long:"listpatterns" description:"List all patterns"`
ListAllModels bool `short:"L" long:"listmodels" description:"List all available models"`
ListAllContexts bool `short:"x" long:"listcontexts" description:"List all contexts"`
ListAllSessions bool `short:"X" long:"listsessions" description:"List all sessions"`
UpdatePatterns bool `short:"U" long:"updatepatterns" description:"Update patterns"`
Message string `hidden:"true" description:"Messages to send to chat"`
Copy bool `short:"c" long:"copy" description:"Copy to clipboard"`
Model string `short:"m" long:"model" yaml:"model" description:"Choose model"`
ModelContextLength int `long:"modelContextLength" yaml:"modelContextLength" description:"Model context length (only affects ollama)"`
Output string `short:"o" long:"output" description:"Output to file" default:""`
OutputSession bool `long:"output-session" description:"Output the entire session (also a temporary one) to the output file"`
LatestPatterns string `short:"n" long:"latest" description:"Number of latest patterns to list" default:"0"`
ChangeDefaultModel bool `short:"d" long:"changeDefaultModel" description:"Change default model"`
YouTube string `short:"y" long:"youtube" description:"YouTube video or play list \"URL\" to grab transcript, comments from it and send to chat or print it put to the console and store it in the output file"`
YouTubePlaylist bool `long:"playlist" description:"Prefer playlist over video if both ids are present in the URL"`
YouTubeTranscript bool `long:"transcript" description:"Grab transcript from YouTube video and send to chat (it is used per default)."`
YouTubeTranscriptWithTimestamps bool `long:"transcript-with-timestamps" description:"Grab transcript from YouTube video with timestamps and send to chat"`
YouTubeComments bool `long:"comments" description:"Grab comments from YouTube video and send to chat"`
YouTubeMetadata bool `long:"metadata" description:"Output video metadata"`
YtDlpArgs string `long:"yt-dlp-args" yaml:"ytDlpArgs" description:"Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')"`
Language string `short:"g" long:"language" description:"Specify the Language Code for the chat, e.g. -g=en -g=zh" default:""`
ScrapeURL string `short:"u" long:"scrape_url" description:"Scrape website URL to markdown using Jina AI"`
ScrapeQuestion string `short:"q" long:"scrape_question" description:"Search question using Jina AI"`
Seed int `short:"e" long:"seed" yaml:"seed" description:"Seed to be used for LMM generation"`
WipeContext string `short:"w" long:"wipecontext" description:"Wipe context"`
WipeSession string `short:"W" long:"wipesession" description:"Wipe session"`
PrintContext string `long:"printcontext" description:"Print context"`
PrintSession string `long:"printsession" description:"Print session"`
HtmlReadability bool `long:"readability" description:"Convert HTML input into a clean, readable view"`
InputHasVars bool `long:"input-has-vars" description:"Apply variables to user input"`
DryRun bool `long:"dry-run" description:"Show what would be sent to the model without actually sending it"`
Serve bool `long:"serve" description:"Serve the Fabric Rest API"`
ServeOllama bool `long:"serveOllama" description:"Serve the Fabric Rest API with ollama endpoints"`
ServeAddress string `long:"address" description:"The address to bind the REST API" default:":8080"`
ServeAPIKey string `long:"api-key" description:"API key used to secure server routes" default:""`
Config string `long:"config" description:"Path to YAML config file"`
Version bool `long:"version" description:"Print current version"`
ListExtensions bool `long:"listextensions" description:"List all registered extensions"`
AddExtension string `long:"addextension" description:"Register a new extension from config file path"`
RemoveExtension string `long:"rmextension" description:"Remove a registered extension by name"`
Strategy string `long:"strategy" description:"Choose a strategy from the available strategies" default:""`
ListStrategies bool `long:"liststrategies" description:"List all strategies"`
ListVendors bool `long:"listvendors" description:"List all vendors"`
ShellCompleteOutput bool `long:"shell-complete-list" description:"Output raw list without headers/formatting (for shell completion)"`
Search bool `long:"search" description:"Enable web search tool for supported models (Anthropic, OpenAI)"`
SearchLocation string `long:"search-location" description:"Set location for web search results (e.g., 'America/Los_Angeles')"`
ImageFile string `long:"image-file" description:"Save generated image to specified file path (e.g., 'output.png')"`
ImageSize string `long:"image-size" description:"Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)"`
ImageQuality string `long:"image-quality" description:"Image quality: low, medium, high, auto (default: auto)"`
ImageCompression int `long:"image-compression" description:"Compression level 0-100 for JPEG/WebP formats (default: not set)"`
ImageBackground string `long:"image-background" description:"Background type: opaque, transparent (default: opaque, only for PNG/WebP)"`
SuppressThink bool `long:"suppress-think" yaml:"suppressThink" description:"Suppress text enclosed in thinking tags"`
ThinkStartTag string `long:"think-start-tag" yaml:"thinkStartTag" description:"Start tag for thinking sections" default:"<think>"`
ThinkEndTag string `long:"think-end-tag" yaml:"thinkEndTag" description:"End tag for thinking sections" default:"</think>"`
DisableResponsesAPI bool `long:"disable-responses-api" yaml:"disableResponsesAPI" description:"Disable OpenAI Responses API (default: false)"`
Voice string `long:"voice" yaml:"voice" description:"TTS voice name for supported models (e.g., Kore, Charon, Puck)" default:"Kore"`
ListGeminiVoices bool `long:"list-gemini-voices" description:"List all available Gemini TTS voices"`
Notification bool `long:"notification" yaml:"notification" description:"Send desktop notification when command completes"`
NotificationCommand string `long:"notification-command" yaml:"notificationCommand" description:"Custom command to run for notifications (overrides built-in notifications)"`
}
var debug = false
func Debugf(format string, a ...interface{}) {
if debug {
fmt.Printf("DEBUG: "+format, a...)
}
Pattern string `short:"p" long:"pattern" yaml:"pattern" description:"Choose a pattern from the available patterns" default:""`
PatternVariables map[string]string `short:"v" long:"variable" description:"Values for pattern variables, e.g. -v=#role:expert -v=#points:30"`
Context string `short:"C" long:"context" description:"Choose a context from the available contexts" default:""`
Session string `long:"session" description:"Choose a session from the available sessions"`
Attachments []string `short:"a" long:"attachment" description:"Attachment path or URL (e.g. for OpenAI image recognition messages)"`
Setup bool `short:"S" long:"setup" description:"Run setup for all reconfigurable parts of fabric"`
Temperature float64 `short:"t" long:"temperature" yaml:"temperature" description:"Set temperature" default:"0.7"`
TopP float64 `short:"T" long:"topp" yaml:"topp" description:"Set top P" default:"0.9"`
Stream bool `short:"s" long:"stream" yaml:"stream" description:"Stream"`
PresencePenalty float64 `short:"P" long:"presencepenalty" yaml:"presencepenalty" description:"Set presence penalty" default:"0.0"`
Raw bool `short:"r" long:"raw" yaml:"raw" description:"Use the defaults of the model without sending chat options (like temperature etc.) and use the user role instead of the system role for patterns."`
FrequencyPenalty float64 `short:"F" long:"frequencypenalty" yaml:"frequencypenalty" description:"Set frequency penalty" default:"0.0"`
ListPatterns bool `short:"l" long:"listpatterns" description:"List all patterns"`
ListAllModels bool `short:"L" long:"listmodels" description:"List all available models"`
ListAllContexts bool `short:"x" long:"listcontexts" description:"List all contexts"`
ListAllSessions bool `short:"X" long:"listsessions" description:"List all sessions"`
UpdatePatterns bool `short:"U" long:"updatepatterns" description:"Update patterns"`
Message string `hidden:"true" description:"Messages to send to chat"`
Copy bool `short:"c" long:"copy" description:"Copy to clipboard"`
Model string `short:"m" long:"model" yaml:"model" description:"Choose model"`
Vendor string `short:"V" long:"vendor" yaml:"vendor" description:"Specify vendor for the selected model (e.g., -V \"LM Studio\" -m openai/gpt-oss-20b)"`
ModelContextLength int `long:"modelContextLength" yaml:"modelContextLength" description:"Model context length (only affects ollama)"`
Output string `short:"o" long:"output" description:"Output to file" default:""`
OutputSession bool `long:"output-session" description:"Output the entire session (also a temporary one) to the output file"`
LatestPatterns string `short:"n" long:"latest" description:"Number of latest patterns to list" default:"0"`
ChangeDefaultModel bool `short:"d" long:"changeDefaultModel" description:"Change default model"`
YouTube string `short:"y" long:"youtube" description:"YouTube video or play list \"URL\" to grab transcript, comments from it and send to chat or print it put to the console and store it in the output file"`
YouTubePlaylist bool `long:"playlist" description:"Prefer playlist over video if both ids are present in the URL"`
YouTubeTranscript bool `long:"transcript" description:"Grab transcript from YouTube video and send to chat (it is used per default)."`
YouTubeTranscriptWithTimestamps bool `long:"transcript-with-timestamps" description:"Grab transcript from YouTube video with timestamps and send to chat"`
YouTubeComments bool `long:"comments" description:"Grab comments from YouTube video and send to chat"`
YouTubeMetadata bool `long:"metadata" description:"Output video metadata"`
YtDlpArgs string `long:"yt-dlp-args" yaml:"ytDlpArgs" description:"Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')"`
Language string `short:"g" long:"language" description:"Specify the Language Code for the chat, e.g. -g=en -g=zh" default:""`
ScrapeURL string `short:"u" long:"scrape_url" description:"Scrape website URL to markdown using Jina AI"`
ScrapeQuestion string `short:"q" long:"scrape_question" description:"Search question using Jina AI"`
Seed int `short:"e" long:"seed" yaml:"seed" description:"Seed to be used for LMM generation"`
WipeContext string `short:"w" long:"wipecontext" description:"Wipe context"`
WipeSession string `short:"W" long:"wipesession" description:"Wipe session"`
PrintContext string `long:"printcontext" description:"Print context"`
PrintSession string `long:"printsession" description:"Print session"`
HtmlReadability bool `long:"readability" description:"Convert HTML input into a clean, readable view"`
InputHasVars bool `long:"input-has-vars" description:"Apply variables to user input"`
NoVariableReplacement bool `long:"no-variable-replacement" description:"Disable pattern variable replacement"`
DryRun bool `long:"dry-run" description:"Show what would be sent to the model without actually sending it"`
Serve bool `long:"serve" description:"Serve the Fabric Rest API"`
ServeOllama bool `long:"serveOllama" description:"Serve the Fabric Rest API with ollama endpoints"`
ServeAddress string `long:"address" description:"The address to bind the REST API" default:":8080"`
ServeAPIKey string `long:"api-key" description:"API key used to secure server routes" default:""`
Config string `long:"config" description:"Path to YAML config file"`
Version bool `long:"version" description:"Print current version"`
ListExtensions bool `long:"listextensions" description:"List all registered extensions"`
AddExtension string `long:"addextension" description:"Register a new extension from config file path"`
RemoveExtension string `long:"rmextension" description:"Remove a registered extension by name"`
Strategy string `long:"strategy" description:"Choose a strategy from the available strategies" default:""`
ListStrategies bool `long:"liststrategies" description:"List all strategies"`
ListVendors bool `long:"listvendors" description:"List all vendors"`
ShellCompleteOutput bool `long:"shell-complete-list" description:"Output raw list without headers/formatting (for shell completion)"`
Search bool `long:"search" description:"Enable web search tool for supported models (Anthropic, OpenAI, Gemini)"`
SearchLocation string `long:"search-location" description:"Set location for web search results (e.g., 'America/Los_Angeles')"`
ImageFile string `long:"image-file" description:"Save generated image to specified file path (e.g., 'output.png')"`
ImageSize string `long:"image-size" description:"Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)"`
ImageQuality string `long:"image-quality" description:"Image quality: low, medium, high, auto (default: auto)"`
ImageCompression int `long:"image-compression" description:"Compression level 0-100 for JPEG/WebP formats (default: not set)"`
ImageBackground string `long:"image-background" description:"Background type: opaque, transparent (default: opaque, only for PNG/WebP)"`
SuppressThink bool `long:"suppress-think" yaml:"suppressThink" description:"Suppress text enclosed in thinking tags"`
ThinkStartTag string `long:"think-start-tag" yaml:"thinkStartTag" description:"Start tag for thinking sections" default:"<think>"`
ThinkEndTag string `long:"think-end-tag" yaml:"thinkEndTag" description:"End tag for thinking sections" default:"</think>"`
DisableResponsesAPI bool `long:"disable-responses-api" yaml:"disableResponsesAPI" description:"Disable OpenAI Responses API (default: false)"`
TranscribeFile string `long:"transcribe-file" yaml:"transcribeFile" description:"Audio or video file to transcribe"`
TranscribeModel string `long:"transcribe-model" yaml:"transcribeModel" description:"Model to use for transcription (separate from chat model)"`
SplitMediaFile bool `long:"split-media-file" yaml:"splitMediaFile" description:"Split audio/video files larger than 25MB using ffmpeg"`
Voice string `long:"voice" yaml:"voice" description:"TTS voice name for supported models (e.g., Kore, Charon, Puck)" default:"Kore"`
ListGeminiVoices bool `long:"list-gemini-voices" description:"List all available Gemini TTS voices"`
ListTranscriptionModels bool `long:"list-transcription-models" description:"List all available transcription models"`
Notification bool `long:"notification" yaml:"notification" description:"Send desktop notification when command completes"`
NotificationCommand string `long:"notification-command" yaml:"notificationCommand" description:"Custom command to run for notifications (overrides built-in notifications)"`
Thinking domain.ThinkingLevel `long:"thinking" yaml:"thinking" description:"Set reasoning/thinking level (e.g., off, low, medium, high, or numeric tokens for Anthropic or Google Gemini)"`
Debug int `long:"debug" description:"Set debug level (0=off, 1=basic, 2=detailed, 3=trace)" default:"0"`
}
// Init Initialize flags. returns a Flags struct and an error
func Init() (ret *Flags, err error) {
debuglog.SetLevel(debuglog.LevelFromInt(parseDebugLevel(os.Args[1:])))
// Track which yaml-configured flags were set on CLI
usedFlags := make(map[string]bool)
yamlArgsScan := os.Args[1:]
@@ -121,11 +123,11 @@ func Init() (ret *Flags, err error) {
shortTag := field.Tag.Get("short")
if longTag != "" {
flagToYamlTag[longTag] = yamlTag
Debugf("Mapped long flag %s to yaml tag %s\n", longTag, yamlTag)
debuglog.Debug(debuglog.Detailed, "Mapped long flag %s to yaml tag %s\n", longTag, yamlTag)
}
if shortTag != "" {
flagToYamlTag[shortTag] = yamlTag
Debugf("Mapped short flag %s to yaml tag %s\n", shortTag, yamlTag)
debuglog.Debug(debuglog.Detailed, "Mapped short flag %s to yaml tag %s\n", shortTag, yamlTag)
}
}
}
@@ -137,7 +139,7 @@ func Init() (ret *Flags, err error) {
if flag != "" {
if yamlTag, exists := flagToYamlTag[flag]; exists {
usedFlags[yamlTag] = true
Debugf("CLI flag used: %s (yaml: %s)\n", flag, yamlTag)
debuglog.Debug(debuglog.Detailed, "CLI flag used: %s (yaml: %s)\n", flag, yamlTag)
}
}
}
@@ -149,6 +151,7 @@ func Init() (ret *Flags, err error) {
if args, err = parser.Parse(); err != nil {
return
}
debuglog.SetLevel(debuglog.LevelFromInt(ret.Debug))
// Check to see if a ~/.config/fabric/config.yaml config file exists (only when user didn't specify a config)
if ret.Config == "" {
@@ -156,7 +159,7 @@ func Init() (ret *Flags, err error) {
if defaultConfigPath, err := util.GetDefaultConfigPath(); err == nil && defaultConfigPath != "" {
ret.Config = defaultConfigPath
} else if err != nil {
Debugf("Could not determine default config path: %v\n", err)
debuglog.Debug(debuglog.Detailed, "Could not determine default config path: %v\n", err)
}
}
@@ -181,13 +184,13 @@ func Init() (ret *Flags, err error) {
if flagField.CanSet() {
if yamlField.Type() != flagField.Type() {
if err := assignWithConversion(flagField, yamlField); err != nil {
Debugf("Type conversion failed for %s: %v\n", yamlTag, err)
debuglog.Debug(debuglog.Detailed, "Type conversion failed for %s: %v\n", yamlTag, err)
continue
}
} else {
flagField.Set(yamlField)
}
Debugf("Applied YAML value for %s: %v\n", yamlTag, yamlField.Interface())
debuglog.Debug(debuglog.Detailed, "Applied YAML value for %s: %v\n", yamlTag, yamlField.Interface())
}
}
}
@@ -213,6 +216,22 @@ func Init() (ret *Flags, err error) {
return
}
func parseDebugLevel(args []string) int {
for i := 0; i < len(args); i++ {
arg := args[i]
if arg == "--debug" && i+1 < len(args) {
if lvl, err := strconv.Atoi(args[i+1]); err == nil {
return lvl
}
} else if strings.HasPrefix(arg, "--debug=") {
if lvl, err := strconv.Atoi(strings.TrimPrefix(arg, "--debug=")); err == nil {
return lvl
}
}
}
return 0
}
func extractFlag(arg string) string {
var flag string
if strings.HasPrefix(arg, "--") {
@@ -282,7 +301,7 @@ func loadYAMLConfig(configPath string) (*Flags, error) {
return nil, fmt.Errorf("error parsing config file: %w", err)
}
Debugf("Config: %v\n", config)
debuglog.Debug(debuglog.Detailed, "Config: %v\n", config)
return config, nil
}
@@ -437,6 +456,7 @@ func (o *Flags) BuildChatOptions() (ret *domain.ChatOptions, err error) {
FrequencyPenalty: o.FrequencyPenalty,
Raw: o.Raw,
Seed: o.Seed,
Thinking: o.Thinking,
ModelContextLength: o.ModelContextLength,
Search: o.Search,
SearchLocation: o.SearchLocation,
@@ -457,13 +477,14 @@ func (o *Flags) BuildChatOptions() (ret *domain.ChatOptions, err error) {
func (o *Flags) BuildChatRequest(Meta string) (ret *domain.ChatRequest, err error) {
ret = &domain.ChatRequest{
ContextName: o.Context,
SessionName: o.Session,
PatternName: o.Pattern,
StrategyName: o.Strategy,
PatternVariables: o.PatternVariables,
InputHasVars: o.InputHasVars,
Meta: Meta,
ContextName: o.Context,
SessionName: o.Session,
PatternName: o.Pattern,
StrategyName: o.Strategy,
PatternVariables: o.PatternVariables,
InputHasVars: o.InputHasVars,
NoVariableReplacement: o.NoVariableReplacement,
Meta: Meta,
}
var message *chat.ChatCompletionMessage

View File

@@ -64,6 +64,7 @@ func TestBuildChatOptions(t *testing.T) {
FrequencyPenalty: 0.2,
Raw: false,
Seed: 1,
Thinking: domain.ThinkingLevel(""),
SuppressThink: false,
ThinkStartTag: "<think>",
ThinkEndTag: "</think>",
@@ -88,6 +89,7 @@ func TestBuildChatOptionsDefaultSeed(t *testing.T) {
FrequencyPenalty: 0.2,
Raw: false,
Seed: 0,
Thinking: domain.ThinkingLevel(""),
SuppressThink: false,
ThinkStartTag: "<think>",
ThinkEndTag: "</think>",

View File

@@ -5,6 +5,8 @@ import (
"os"
"strconv"
openai "github.com/openai/openai-go"
"github.com/danielmiessler/fabric/internal/core"
"github.com/danielmiessler/fabric/internal/plugins/ai"
"github.com/danielmiessler/fabric/internal/plugins/ai/gemini"
@@ -36,7 +38,11 @@ func handleListingCommands(currentFlags *Flags, fabricDb *fsdb.Db, registry *cor
if models, err = registry.VendorManager.GetModels(); err != nil {
return true, err
}
models.Print(currentFlags.ShellCompleteOutput)
if currentFlags.ShellCompleteOutput {
models.Print(true)
} else {
models.PrintWithVendor(false, registry.Defaults.Vendor.Value, registry.Defaults.Model.Value)
}
return true, nil
}
@@ -66,5 +72,30 @@ func handleListingCommands(currentFlags *Flags, fabricDb *fsdb.Db, registry *cor
return true, nil
}
if currentFlags.ListTranscriptionModels {
listTranscriptionModels(currentFlags.ShellCompleteOutput)
return true, nil
}
return false, nil
}
// listTranscriptionModels lists all available transcription models
func listTranscriptionModels(shellComplete bool) {
models := []string{
string(openai.AudioModelWhisper1),
string(openai.AudioModelGPT4oMiniTranscribe),
string(openai.AudioModelGPT4oTranscribe),
}
if shellComplete {
for _, model := range models {
fmt.Println(model)
}
} else {
fmt.Println("Available transcription models:")
for _, model := range models {
fmt.Printf(" %s\n", model)
}
}
}

View File

@@ -0,0 +1,35 @@
package cli
import (
"context"
"fmt"
"github.com/danielmiessler/fabric/internal/core"
)
type transcriber interface {
TranscribeFile(ctx context.Context, filePath, model string, split bool) (string, error)
}
func handleTranscription(flags *Flags, registry *core.PluginRegistry) (message string, err error) {
vendorName := flags.Vendor
if vendorName == "" {
vendorName = "OpenAI"
}
vendor, ok := registry.VendorManager.VendorsByName[vendorName]
if !ok {
return "", fmt.Errorf("vendor %s not configured", vendorName)
}
tr, ok := vendor.(transcriber)
if !ok {
return "", fmt.Errorf("vendor %s does not support audio transcription", vendorName)
}
model := flags.TranscribeModel
if model == "" {
return "", fmt.Errorf("transcription model is required (use --transcribe-model)")
}
if message, err = tr.TranscribeFile(context.Background(), flags.TranscribeFile, model, flags.SplitMediaFile); err != nil {
return
}
return
}

View File

@@ -180,7 +180,7 @@ func (o *Chatter) BuildSession(request *domain.ChatRequest, raw bool) (session *
}
// Now we know request.Message is not nil, process template variables
if request.InputHasVars {
if request.InputHasVars && !request.NoVariableReplacement {
request.Message.Content, err = template.ApplyTemplate(request.Message.Content, request.PatternVariables, "")
if err != nil {
return nil, err
@@ -190,7 +190,12 @@ func (o *Chatter) BuildSession(request *domain.ChatRequest, raw bool) (session *
var patternContent string
inputUsed := false
if request.PatternName != "" {
pattern, err := o.db.Patterns.GetApplyVariables(request.PatternName, request.PatternVariables, request.Message.Content)
var pattern *fsdb.Pattern
if request.NoVariableReplacement {
pattern, err = o.db.Patterns.GetWithoutVariables(request.PatternName, request.Message.Content)
} else {
pattern, err = o.db.Patterns.GetApplyVariables(request.PatternName, request.PatternVariables, request.Message.Content)
}
if err != nil {
return nil, fmt.Errorf("could not get pattern %s: %v", request.PatternName, err)

View File

@@ -288,7 +288,7 @@ func (o *PluginRegistry) Configure() (err error) {
return
}
func (o *PluginRegistry) GetChatter(model string, modelContextLength int, strategy string, stream bool, dryRun bool) (ret *Chatter, err error) {
func (o *PluginRegistry) GetChatter(model string, modelContextLength int, vendorName string, strategy string, stream bool, dryRun bool) (ret *Chatter, err error) {
ret = &Chatter{
db: o.Db,
Stream: stream,
@@ -317,14 +317,32 @@ func (o *PluginRegistry) GetChatter(model string, modelContextLength int, strate
ret.model = defaultModel
}
} else if model == "" {
ret.vendor = vendorManager.FindByName(defaultVendor)
if vendorName != "" {
ret.vendor = vendorManager.FindByName(vendorName)
} else {
ret.vendor = vendorManager.FindByName(defaultVendor)
}
ret.model = defaultModel
} else {
var models *ai.VendorsModels
if models, err = vendorManager.GetModels(); err != nil {
return
}
ret.vendor = vendorManager.FindByName(models.FindGroupsByItemFirst(model))
if vendorName != "" {
// ensure vendor exists and provides model
ret.vendor = vendorManager.FindByName(vendorName)
availableVendors := models.FindGroupsByItem(model)
if ret.vendor == nil || !lo.Contains(availableVendors, vendorName) {
err = fmt.Errorf("model %s not available for vendor %s", model, vendorName)
return
}
} else {
availableVendors := models.FindGroupsByItem(model)
if len(availableVendors) > 1 {
fmt.Fprintf(os.Stderr, "Warning: multiple vendors provide model %s: %s. Using %s. Specify --vendor to select a vendor.\n", model, strings.Join(availableVendors, ", "), availableVendors[0])
}
ret.vendor = vendorManager.FindByName(models.FindGroupsByItemFirst(model))
}
ret.model = model
}

View File

@@ -1,10 +1,19 @@
package core
import (
"bytes"
"context"
"io"
"os"
"strings"
"testing"
"github.com/danielmiessler/fabric/internal/chat"
"github.com/danielmiessler/fabric/internal/domain"
"github.com/danielmiessler/fabric/internal/plugins"
"github.com/danielmiessler/fabric/internal/plugins/ai"
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
"github.com/danielmiessler/fabric/internal/tools"
)
func TestSaveEnvFile(t *testing.T) {
@@ -19,3 +28,65 @@ func TestSaveEnvFile(t *testing.T) {
t.Fatalf("SaveEnvFile() error = %v", err)
}
}
// testVendor implements ai.Vendor for testing purposes
type testVendor struct {
name string
models []string
}
func (m *testVendor) GetName() string { return m.name }
func (m *testVendor) GetSetupDescription() string { return m.name }
func (m *testVendor) IsConfigured() bool { return true }
func (m *testVendor) Configure() error { return nil }
func (m *testVendor) Setup() error { return nil }
func (m *testVendor) SetupFillEnvFileContent(*bytes.Buffer) {}
func (m *testVendor) ListModels() ([]string, error) { return m.models, nil }
func (m *testVendor) SendStream([]*chat.ChatCompletionMessage, *domain.ChatOptions, chan string) error {
return nil
}
func (m *testVendor) Send(context.Context, []*chat.ChatCompletionMessage, *domain.ChatOptions) (string, error) {
return "", nil
}
func (m *testVendor) NeedsRawMode(string) bool { return false }
func TestGetChatter_WarnsOnAmbiguousModel(t *testing.T) {
tempDir := t.TempDir()
db := fsdb.NewDb(tempDir)
vendorA := &testVendor{name: "VendorA", models: []string{"shared-model"}}
vendorB := &testVendor{name: "VendorB", models: []string{"shared-model"}}
vm := ai.NewVendorsManager()
vm.AddVendors(vendorA, vendorB)
defaults := &tools.Defaults{
PluginBase: &plugins.PluginBase{},
Vendor: &plugins.Setting{Value: "VendorA"},
Model: &plugins.SetupQuestion{Setting: &plugins.Setting{Value: "shared-model"}},
ModelContextLength: &plugins.SetupQuestion{Setting: &plugins.Setting{Value: "0"}},
}
registry := &PluginRegistry{Db: db, VendorManager: vm, Defaults: defaults}
r, w, _ := os.Pipe()
oldStderr := os.Stderr
os.Stderr = w
defer func() { os.Stderr = oldStderr }()
chatter, err := registry.GetChatter("shared-model", 0, "", "", false, false)
w.Close()
warning, _ := io.ReadAll(r)
if err != nil {
t.Fatalf("GetChatter() error = %v", err)
}
// Verify that one of the valid vendors was selected (don't care which one due to map iteration randomness)
vendorName := chatter.vendor.GetName()
if vendorName != "VendorA" && vendorName != "VendorB" {
t.Fatalf("expected vendor VendorA or VendorB, got %s", vendorName)
}
if !strings.Contains(string(warning), "multiple vendors provide model shared-model") {
t.Fatalf("expected warning about multiple vendors, got %q", string(warning))
}
}

View File

@@ -13,15 +13,16 @@ const (
)
type ChatRequest struct {
ContextName string
SessionName string
PatternName string
PatternVariables map[string]string
Message *chat.ChatCompletionMessage
Language string
Meta string
InputHasVars bool
StrategyName string
ContextName string
SessionName string
PatternName string
PatternVariables map[string]string
Message *chat.ChatCompletionMessage
Language string
Meta string
InputHasVars bool
NoVariableReplacement bool
StrategyName string
}
type ChatOptions struct {
@@ -32,6 +33,7 @@ type ChatOptions struct {
FrequencyPenalty float64
Raw bool
Seed int
Thinking ThinkingLevel
ModelContextLength int
MaxTokens int
Search bool

View File

@@ -0,0 +1,34 @@
package domain
// ThinkingLevel represents reasoning/thinking levels supported across providers.
type ThinkingLevel string
const (
ThinkingOff ThinkingLevel = "off"
ThinkingLow ThinkingLevel = "low"
ThinkingMedium ThinkingLevel = "medium"
ThinkingHigh ThinkingLevel = "high"
)
// ThinkingBudgets defines standardized token budgets for reasoning-enabled models.
// The map assigns a maximum token count to each ThinkingLevel, representing the
// amount of context or computation that can be used for reasoning at that level.
// These values (e.g., 1024 for low, 2048 for medium, 4096 for high) are used to
// Token budget constants for each ThinkingLevel.
// These values are chosen to align with typical context window sizes for LLMs at different reasoning levels.
// Adjust these if model capabilities change.
const (
// TokenBudgetLow is suitable for basic reasoning or smaller models (e.g., 1k context window).
TokenBudgetLow int64 = 1024
// TokenBudgetMedium is suitable for intermediate reasoning or mid-sized models (e.g., 2k context window).
TokenBudgetMedium int64 = 2048
// TokenBudgetHigh is suitable for advanced reasoning or large models (e.g., 4k context window).
TokenBudgetHigh int64 = 4096
)
// ThinkingBudgets defines standardized token budgets for reasoning-enabled models.
var ThinkingBudgets = map[ThinkingLevel]int64{
ThinkingLow: TokenBudgetLow,
ThinkingMedium: TokenBudgetMedium,
ThinkingHigh: TokenBudgetHigh,
}

69
internal/log/log.go Normal file
View File

@@ -0,0 +1,69 @@
package log
import (
"fmt"
"io"
"os"
"sync"
)
// Level represents the debug verbosity.
type Level int
const (
// Off disables all debug output.
Off Level = iota
// Basic provides minimal debugging information.
Basic
// Detailed provides more verbose debugging.
Detailed
// Trace is the most verbose level.
Trace
)
var (
mu sync.RWMutex
level Level = Off
output io.Writer = os.Stderr
)
// SetLevel sets the global debug level.
func SetLevel(l Level) {
mu.Lock()
level = l
mu.Unlock()
}
// LevelFromInt converts an int to a Level.
func LevelFromInt(i int) Level {
switch {
case i <= 0:
return Off
case i == 1:
return Basic
case i == 2:
return Detailed
case i >= 3:
return Trace
default:
return Off
}
}
// Debug writes a debug message if the global level permits.
func Debug(l Level, format string, a ...interface{}) {
mu.RLock()
current := level
w := output
mu.RUnlock()
if current >= l {
fmt.Fprintf(w, "DEBUG: "+format, a...)
}
}
// SetOutput allows overriding the output destination for debug logs.
func SetOutput(w io.Writer) {
mu.Lock()
output = w
mu.Unlock()
}

View File

@@ -4,6 +4,8 @@ import (
"context"
"fmt"
"net/http"
"os"
"strconv"
"strings"
"github.com/anthropics/anthropic-sdk-go"
@@ -49,6 +51,10 @@ func NewClient() (ret *Client) {
string(anthropic.ModelClaudeOpus4_1_20250805),
}
ret.modelBetas = map[string][]string{
string(anthropic.ModelClaudeSonnet4_20250514): {"context-1m-2025-08-07"},
}
return
}
@@ -93,6 +99,7 @@ type Client struct {
maxTokens int
defaultRequiredUserMessage string
models []string
modelBetas map[string][]string
client anthropic.Client
}
@@ -148,6 +155,26 @@ func (an *Client) ListModels() (ret []string, err error) {
return an.models, nil
}
func parseThinking(level domain.ThinkingLevel) (anthropic.ThinkingConfigParamUnion, bool) {
lower := strings.ToLower(string(level))
switch domain.ThinkingLevel(lower) {
case domain.ThinkingOff:
disabled := anthropic.NewThinkingConfigDisabledParam()
return anthropic.ThinkingConfigParamUnion{OfDisabled: &disabled}, true
case domain.ThinkingLow, domain.ThinkingMedium, domain.ThinkingHigh:
if budget, ok := domain.ThinkingBudgets[domain.ThinkingLevel(lower)]; ok {
return anthropic.ThinkingConfigParamOfEnabled(budget), true
}
default:
if tokens, err := strconv.ParseInt(lower, 10, 64); err == nil {
if tokens >= 1 && tokens <= 10000 {
return anthropic.ThinkingConfigParamOfEnabled(tokens), true
}
}
}
return anthropic.ThinkingConfigParamUnion{}, false
}
func (an *Client) SendStream(
msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan string,
) (err error) {
@@ -160,7 +187,17 @@ func (an *Client) SendStream(
ctx := context.Background()
stream := an.client.Messages.NewStreaming(ctx, an.buildMessageParams(messages, opts))
params := an.buildMessageParams(messages, opts)
betas := an.modelBetas[opts.Model]
var reqOpts []option.RequestOption
if len(betas) > 0 {
reqOpts = append(reqOpts, option.WithHeader("anthropic-beta", strings.Join(betas, ",")))
}
stream := an.client.Messages.NewStreaming(ctx, params, reqOpts...)
if stream.Err() != nil && len(betas) > 0 {
fmt.Fprintf(os.Stderr, "Anthropic beta feature %s failed: %v\n", strings.Join(betas, ","), stream.Err())
stream = an.client.Messages.NewStreaming(ctx, params)
}
for stream.Next() {
event := stream.Current()
@@ -226,6 +263,11 @@ func (an *Client) buildMessageParams(msgs []anthropic.MessageParam, opts *domain
{OfWebSearchTool20250305: &webTool},
}
}
if t, ok := parseThinking(opts.Thinking); ok {
params.Thinking = t
}
return
}
@@ -239,8 +281,21 @@ func (an *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage,
}
var message *anthropic.Message
if message, err = an.client.Messages.New(ctx, an.buildMessageParams(messages, opts)); err != nil {
return
params := an.buildMessageParams(messages, opts)
betas := an.modelBetas[opts.Model]
var reqOpts []option.RequestOption
if len(betas) > 0 {
reqOpts = append(reqOpts, option.WithHeader("anthropic-beta", strings.Join(betas, ",")))
}
if message, err = an.client.Messages.New(ctx, params, reqOpts...); err != nil {
if len(betas) > 0 {
fmt.Fprintf(os.Stderr, "Anthropic beta feature %s failed: %v\n", strings.Join(betas, ","), err)
if message, err = an.client.Messages.New(ctx, params); err != nil {
return
}
} else {
return
}
}
var textParts []string

View File

@@ -168,6 +168,15 @@ func TestBuildMessageParams_WithSearchAndLocation(t *testing.T) {
}
}
func TestModelBetasConfiguration(t *testing.T) {
client := NewClient()
model := string(anthropic.ModelClaudeSonnet4_20250514)
betas, ok := client.modelBetas[model]
if !ok || len(betas) != 1 || betas[0] != "context-1m-2025-08-07" {
t.Errorf("expected beta mapping for %s", model)
}
}
func TestCitationFormatting(t *testing.T) {
// Test the citation formatting logic by creating a mock message with citations
message := &anthropic.Message{

View File

@@ -46,8 +46,13 @@ func (t *OAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// Add OAuth Bearer token
newReq.Header.Set("Authorization", "Bearer "+token)
// Add the anthropic-beta header for OAuth
newReq.Header.Set("anthropic-beta", "oauth-2025-04-20")
// Add the anthropic-beta header for OAuth, preserving existing betas
existing := newReq.Header.Get("anthropic-beta")
beta := "oauth-2025-04-20"
if existing != "" {
beta = existing + "," + beta
}
newReq.Header.Set("anthropic-beta", beta)
// Set User-Agent to match AI SDK exactly
newReq.Header.Set("User-Agent", "ai-sdk/anthropic")

View File

@@ -87,6 +87,9 @@ func (c *Client) formatOptions(opts *domain.ChatOptions) string {
if opts.ImageFile != "" {
builder.WriteString(fmt.Sprintf("ImageFile: %s\n", opts.ImageFile))
}
if opts.Thinking != "" {
builder.WriteString(fmt.Sprintf("Thinking: %s\n", string(opts.Thinking)))
}
if opts.SuppressThink {
builder.WriteString("SuppressThink: enabled\n")
builder.WriteString(fmt.Sprintf("Thinking Start Tag: %s\n", opts.ThinkStartTag))

View File

@@ -5,6 +5,8 @@ import (
"context"
"encoding/binary"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/danielmiessler/fabric/internal/chat"
@@ -26,6 +28,24 @@ const (
AudioDataPrefix = "FABRIC_AUDIO_DATA:"
)
const (
citationHeader = "\n\n## Sources\n\n"
citationSeparator = "\n"
citationFormat = "- [%s](%s)"
errInvalidLocationFormat = "invalid search location format %q: must be timezone (e.g., 'America/Los_Angeles') or language code (e.g., 'en-US')"
locationSeparator = "/"
langCodeSeparator = "_"
langCodeNormalizedSep = "-"
modelPrefix = "models/"
modelTypeTTS = "tts"
modelTypePreviewTTS = "preview-tts"
modelTypeTextToSpeech = "text-to-speech"
)
var langCodeRegex = regexp.MustCompile(`^[a-z]{2}(-[A-Z]{2})?$`)
func NewClient() (ret *Client) {
vendorName := "Gemini"
ret = &Client{}
@@ -93,14 +113,13 @@ func (o *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o
// Convert messages to new SDK format
contents := o.convertMessages(msgs)
// Generate content
temperature := float32(opts.Temperature)
topP := float32(opts.TopP)
response, err := client.Models.GenerateContent(ctx, o.buildModelNameFull(opts.Model), contents, &genai.GenerateContentConfig{
Temperature: &temperature,
TopP: &topP,
MaxOutputTokens: int32(opts.ModelContextLength),
})
cfg, err := o.buildGenerateContentConfig(opts)
if err != nil {
return "", err
}
// Generate content with optional tools
response, err := client.Models.GenerateContent(ctx, o.buildModelNameFull(opts.Model), contents, cfg)
if err != nil {
return "", err
}
@@ -123,14 +142,13 @@ func (o *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.Cha
// Convert messages to new SDK format
contents := o.convertMessages(msgs)
// Generate streaming content
temperature := float32(opts.Temperature)
topP := float32(opts.TopP)
stream := client.Models.GenerateContentStream(ctx, o.buildModelNameFull(opts.Model), contents, &genai.GenerateContentConfig{
Temperature: &temperature,
TopP: &topP,
MaxOutputTokens: int32(opts.ModelContextLength),
})
cfg, err := o.buildGenerateContentConfig(opts)
if err != nil {
return err
}
// Generate streaming content with optional tools
stream := client.Models.GenerateContentStream(ctx, o.buildModelNameFull(opts.Model), contents, cfg)
for response, err := range stream {
if err != nil {
@@ -153,20 +171,109 @@ func (o *Client) NeedsRawMode(modelName string) bool {
return false
}
func parseThinkingConfig(level domain.ThinkingLevel) (*genai.ThinkingConfig, bool) {
lower := strings.ToLower(strings.TrimSpace(string(level)))
switch domain.ThinkingLevel(lower) {
case "", domain.ThinkingOff:
return nil, false
case domain.ThinkingLow, domain.ThinkingMedium, domain.ThinkingHigh:
if budget, ok := domain.ThinkingBudgets[domain.ThinkingLevel(lower)]; ok {
b := int32(budget)
return &genai.ThinkingConfig{IncludeThoughts: true, ThinkingBudget: &b}, true
}
default:
if tokens, err := strconv.ParseInt(lower, 10, 32); err == nil && tokens > 0 {
t := int32(tokens)
return &genai.ThinkingConfig{IncludeThoughts: true, ThinkingBudget: &t}, true
}
}
return nil, false
}
// buildGenerateContentConfig constructs the generation config with optional tools.
// When search is enabled it injects the Google Search tool. The optional search
// location accepts either:
// - A timezone in the format "Continent/City" (e.g., "America/Los_Angeles")
// - An ISO language code "ll" or "ll-CC" (e.g., "en" or "en-US")
//
// Underscores are normalized to hyphens. Returns an error if the location is
// invalid.
func (o *Client) buildGenerateContentConfig(opts *domain.ChatOptions) (*genai.GenerateContentConfig, error) {
temperature := float32(opts.Temperature)
topP := float32(opts.TopP)
cfg := &genai.GenerateContentConfig{
Temperature: &temperature,
TopP: &topP,
MaxOutputTokens: int32(opts.ModelContextLength),
}
if opts.Search {
cfg.Tools = []*genai.Tool{{GoogleSearch: &genai.GoogleSearch{}}}
if loc := opts.SearchLocation; loc != "" {
if isValidLocationFormat(loc) {
loc = normalizeLocation(loc)
cfg.ToolConfig = &genai.ToolConfig{
RetrievalConfig: &genai.RetrievalConfig{LanguageCode: loc},
}
} else {
return nil, fmt.Errorf(errInvalidLocationFormat, loc)
}
}
}
if tc, ok := parseThinkingConfig(opts.Thinking); ok {
cfg.ThinkingConfig = tc
}
return cfg, nil
}
// buildModelNameFull adds the "models/" prefix for API calls
func (o *Client) buildModelNameFull(modelName string) string {
if strings.HasPrefix(modelName, "models/") {
if strings.HasPrefix(modelName, modelPrefix) {
return modelName
}
return "models/" + modelName
return modelPrefix + modelName
}
func isValidLocationFormat(location string) bool {
if strings.Contains(location, locationSeparator) {
parts := strings.Split(location, locationSeparator)
return len(parts) == 2 && parts[0] != "" && parts[1] != ""
}
return isValidLanguageCode(location)
}
func normalizeLocation(location string) string {
if strings.Contains(location, locationSeparator) {
return location
}
return strings.Replace(location, langCodeSeparator, langCodeNormalizedSep, 1)
}
// isValidLanguageCode reports whether the input is an ISO 639-1 language code
// optionally followed by an ISO 3166-1 country code. Underscores are
// normalized to hyphens before validation.
func isValidLanguageCode(code string) bool {
normalized := strings.Replace(code, langCodeSeparator, langCodeNormalizedSep, 1)
parts := strings.Split(normalized, langCodeNormalizedSep)
switch len(parts) {
case 1:
return langCodeRegex.MatchString(strings.ToLower(parts[0]))
case 2:
formatted := strings.ToLower(parts[0]) + langCodeNormalizedSep + strings.ToUpper(parts[1])
return langCodeRegex.MatchString(formatted)
default:
return false
}
}
// isTTSModel checks if the model is a text-to-speech model
func (o *Client) isTTSModel(modelName string) bool {
lowerModel := strings.ToLower(modelName)
return strings.Contains(lowerModel, "tts") ||
strings.Contains(lowerModel, "preview-tts") ||
strings.Contains(lowerModel, "text-to-speech")
return strings.Contains(lowerModel, modelTypeTTS) ||
strings.Contains(lowerModel, modelTypePreviewTTS) ||
strings.Contains(lowerModel, modelTypeTextToSpeech)
}
// extractTextForTTS extracts text content from chat messages for TTS generation
@@ -336,6 +443,19 @@ func (o *Client) convertMessages(msgs []*chat.ChatCompletionMessage) []*genai.Co
for _, msg := range msgs {
content := &genai.Content{Parts: []*genai.Part{}}
switch msg.Role {
case chat.ChatMessageRoleAssistant:
content.Role = "model"
case chat.ChatMessageRoleUser:
content.Role = "user"
case chat.ChatMessageRoleSystem, chat.ChatMessageRoleDeveloper, chat.ChatMessageRoleFunction, chat.ChatMessageRoleTool:
// Gemini's API only accepts "user" and "model" roles.
// Map all other roles to "user" to preserve instruction context.
content.Role = "user"
default:
content.Role = "user"
}
if msg.Content != "" {
content.Parts = append(content.Parts, &genai.Part{Text: msg.Content})
}
@@ -357,19 +477,71 @@ func (o *Client) convertMessages(msgs []*chat.ChatCompletionMessage) []*genai.Co
return contents
}
// extractTextFromResponse extracts text content from the response
// extractTextFromResponse extracts text content from the response and appends
// any web citations in a standardized format.
func (o *Client) extractTextFromResponse(response *genai.GenerateContentResponse) string {
var result strings.Builder
if response == nil {
return ""
}
text := o.extractTextParts(response)
citations := o.extractCitations(response)
if len(citations) > 0 {
return text + citationHeader + strings.Join(citations, citationSeparator)
}
return text
}
func (o *Client) extractTextParts(response *genai.GenerateContentResponse) string {
var builder strings.Builder
for _, candidate := range response.Candidates {
if candidate.Content != nil {
for _, part := range candidate.Content.Parts {
if part.Text != "" {
result.WriteString(part.Text)
}
if candidate == nil || candidate.Content == nil {
continue
}
for _, part := range candidate.Content.Parts {
if part != nil && part.Text != "" {
builder.WriteString(part.Text)
}
}
}
return result.String()
return builder.String()
}
func (o *Client) extractCitations(response *genai.GenerateContentResponse) []string {
if response == nil || len(response.Candidates) == 0 {
return nil
}
citationMap := make(map[string]bool)
var citations []string
for _, candidate := range response.Candidates {
if candidate == nil || candidate.GroundingMetadata == nil {
continue
}
chunks := candidate.GroundingMetadata.GroundingChunks
if len(chunks) == 0 {
continue
}
for _, chunk := range chunks {
if chunk == nil || chunk.Web == nil {
continue
}
uri := chunk.Web.URI
title := chunk.Web.Title
if uri == "" || title == "" {
continue
}
var keyBuilder strings.Builder
keyBuilder.WriteString(uri)
keyBuilder.WriteByte('|')
keyBuilder.WriteString(title)
key := keyBuilder.String()
if !citationMap[key] {
citationMap[key] = true
citationText := fmt.Sprintf(citationFormat, title, uri)
citations = append(citations, citationText)
}
}
}
return citations
}

View File

@@ -1,9 +1,13 @@
package gemini
import (
"strings"
"testing"
"google.golang.org/genai"
"github.com/danielmiessler/fabric/internal/chat"
"github.com/danielmiessler/fabric/internal/domain"
)
// Test buildModelNameFull method
@@ -51,6 +55,162 @@ func TestExtractTextFromResponse(t *testing.T) {
}
}
func TestExtractTextFromResponse_Nil(t *testing.T) {
client := &Client{}
if got := client.extractTextFromResponse(nil); got != "" {
t.Fatalf("expected empty string, got %q", got)
}
}
func TestExtractTextFromResponse_EmptyGroundingChunks(t *testing.T) {
client := &Client{}
response := &genai.GenerateContentResponse{
Candidates: []*genai.Candidate{
{
Content: &genai.Content{Parts: []*genai.Part{{Text: "Hello"}}},
GroundingMetadata: &genai.GroundingMetadata{GroundingChunks: nil},
},
},
}
if got := client.extractTextFromResponse(response); got != "Hello" {
t.Fatalf("expected 'Hello', got %q", got)
}
}
func TestBuildGenerateContentConfig_WithSearch(t *testing.T) {
client := &Client{}
opts := &domain.ChatOptions{Search: true}
cfg, err := client.buildGenerateContentConfig(opts)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.Tools == nil || len(cfg.Tools) != 1 || cfg.Tools[0].GoogleSearch == nil {
t.Errorf("expected google search tool to be included")
}
}
func TestBuildGenerateContentConfig_WithSearchAndLocation(t *testing.T) {
client := &Client{}
opts := &domain.ChatOptions{Search: true, SearchLocation: "America/Los_Angeles"}
cfg, err := client.buildGenerateContentConfig(opts)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.ToolConfig == nil || cfg.ToolConfig.RetrievalConfig == nil {
t.Fatalf("expected retrieval config when search location provided")
}
if cfg.ToolConfig.RetrievalConfig.LanguageCode != opts.SearchLocation {
t.Errorf("expected language code %s, got %s", opts.SearchLocation, cfg.ToolConfig.RetrievalConfig.LanguageCode)
}
}
func TestBuildGenerateContentConfig_InvalidLocation(t *testing.T) {
client := &Client{}
opts := &domain.ChatOptions{Search: true, SearchLocation: "invalid"}
_, err := client.buildGenerateContentConfig(opts)
if err == nil {
t.Fatalf("expected error for invalid location")
}
}
func TestBuildGenerateContentConfig_LanguageCodeNormalization(t *testing.T) {
client := &Client{}
opts := &domain.ChatOptions{Search: true, SearchLocation: "en_US"}
cfg, err := client.buildGenerateContentConfig(opts)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.ToolConfig == nil || cfg.ToolConfig.RetrievalConfig.LanguageCode != "en-US" {
t.Fatalf("expected normalized language code 'en-US', got %+v", cfg.ToolConfig)
}
}
func TestBuildGenerateContentConfig_Thinking(t *testing.T) {
client := &Client{}
opts := &domain.ChatOptions{Thinking: domain.ThinkingLow}
cfg, err := client.buildGenerateContentConfig(opts)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.ThinkingConfig == nil || !cfg.ThinkingConfig.IncludeThoughts {
t.Fatalf("expected thinking config with thoughts included")
}
if cfg.ThinkingConfig.ThinkingBudget == nil || *cfg.ThinkingConfig.ThinkingBudget != int32(domain.TokenBudgetLow) {
t.Errorf("expected thinking budget %d, got %+v", domain.TokenBudgetLow, cfg.ThinkingConfig.ThinkingBudget)
}
}
func TestBuildGenerateContentConfig_ThinkingTokens(t *testing.T) {
client := &Client{}
opts := &domain.ChatOptions{Thinking: domain.ThinkingLevel("123")}
cfg, err := client.buildGenerateContentConfig(opts)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.ThinkingConfig == nil || cfg.ThinkingConfig.ThinkingBudget == nil {
t.Fatalf("expected thinking config with budget")
}
if *cfg.ThinkingConfig.ThinkingBudget != 123 {
t.Errorf("expected thinking budget 123, got %d", *cfg.ThinkingConfig.ThinkingBudget)
}
}
func TestCitationFormatting(t *testing.T) {
client := &Client{}
response := &genai.GenerateContentResponse{
Candidates: []*genai.Candidate{
{
Content: &genai.Content{Parts: []*genai.Part{{Text: "Based on recent research, AI is advancing rapidly."}}},
GroundingMetadata: &genai.GroundingMetadata{
GroundingChunks: []*genai.GroundingChunk{
{Web: &genai.GroundingChunkWeb{URI: "https://example.com/ai", Title: "AI Research"}},
{Web: &genai.GroundingChunkWeb{URI: "https://news.com/tech", Title: "Tech News"}},
{Web: &genai.GroundingChunkWeb{URI: "https://example.com/ai", Title: "AI Research"}}, // duplicate
},
},
},
},
}
result := client.extractTextFromResponse(response)
if !strings.Contains(result, "## Sources") {
t.Fatalf("expected sources section in result: %s", result)
}
if strings.Count(result, "- [") != 2 {
t.Errorf("expected 2 unique citations, got %d", strings.Count(result, "- ["))
}
}
// Test convertMessages handles role mapping correctly
func TestConvertMessagesRoles(t *testing.T) {
client := &Client{}
msgs := []*chat.ChatCompletionMessage{
{Role: chat.ChatMessageRoleUser, Content: "user"},
{Role: chat.ChatMessageRoleAssistant, Content: "assistant"},
{Role: chat.ChatMessageRoleSystem, Content: "system"},
}
contents := client.convertMessages(msgs)
expected := []string{"user", "model", "user"}
if len(contents) != len(expected) {
t.Fatalf("expected %d contents, got %d", len(expected), len(contents))
}
for i, c := range contents {
if c.Role != expected[i] {
t.Errorf("content %d expected role %s, got %s", i, expected[i], c.Role)
}
}
}
// Test isTTSModel method
func TestIsTTSModel(t *testing.T) {
client := &Client{}

View File

@@ -1,6 +1,10 @@
package ai
import (
"fmt"
"sort"
"strings"
"github.com/danielmiessler/fabric/internal/util"
)
@@ -11,3 +15,40 @@ func NewVendorsModels() *VendorsModels {
type VendorsModels struct {
*util.GroupsItemsSelectorString
}
// PrintWithVendor prints models including their vendor on each line.
// When shellCompleteList is true, output is suitable for shell completion.
// Default vendor and model are highlighted with an asterisk.
func (o *VendorsModels) PrintWithVendor(shellCompleteList bool, defaultVendor, defaultModel string) {
if !shellCompleteList {
fmt.Printf("\n%v:\n", o.SelectionLabel)
}
var currentItemIndex int
sortedGroups := make([]*util.GroupItems[string], len(o.GroupsItems))
copy(sortedGroups, o.GroupsItems)
sort.SliceStable(sortedGroups, func(i, j int) bool {
return strings.ToLower(sortedGroups[i].Group) < strings.ToLower(sortedGroups[j].Group)
})
for _, groupItems := range sortedGroups {
items := make([]string, len(groupItems.Items))
copy(items, groupItems.Items)
sort.SliceStable(items, func(i, j int) bool {
return strings.ToLower(items[i]) < strings.ToLower(items[j])
})
for _, item := range items {
currentItemIndex++
if shellCompleteList {
fmt.Printf("%s|%s\n", groupItems.Group, item)
} else {
mark := " "
if strings.EqualFold(groupItems.Group, defaultVendor) && strings.EqualFold(item, defaultModel) {
mark = " *"
}
fmt.Printf("%s\t[%d]\t%s|%s\n", mark, currentItemIndex, groupItems.Group, item)
}
}
}
}

View File

@@ -1,6 +1,9 @@
package ai
import (
"io"
"os"
"strings"
"testing"
)
@@ -31,3 +34,23 @@ func TestFindVendorsByModel(t *testing.T) {
t.Fatalf("FindVendorsByModel() = %v, want %v", foundVendors, []string{"vendor1"})
}
}
func TestPrintWithVendorMarksDefault(t *testing.T) {
vendors := NewVendorsModels()
vendors.AddGroupItems("vendor1", []string{"model1"}...)
vendors.AddGroupItems("vendor2", []string{"model2"}...)
r, w, _ := os.Pipe()
oldStdout := os.Stdout
os.Stdout = w
vendors.PrintWithVendor(false, "vendor2", "model2")
w.Close()
os.Stdout = oldStdout
out, _ := io.ReadAll(r)
if !strings.Contains(string(out), " *\t[2]\tvendor2|model2") {
t.Fatalf("default model not marked: %s", out)
}
}

View File

@@ -85,6 +85,9 @@ func (o *Client) buildChatCompletionParams(
ret.Seed = openai.Int(int64(opts.Seed))
}
}
if eff, ok := parseReasoningEffort(opts.Thinking); ok {
ret.ReasoningEffort = eff
}
return
}

View File

@@ -115,7 +115,11 @@ func (o *Client) sendStreamResponses(
case string(constant.ResponseOutputTextDelta("").Default()):
channel <- event.AsResponseOutputTextDelta().Delta
case string(constant.ResponseOutputTextDone("").Default()):
channel <- event.AsResponseOutputTextDone().Text
// The Responses API sends the full text again in the
// final "done" event. Since we've already streamed all
// delta chunks above, sending it would duplicate the
// output. Ignore it here to prevent doubled results.
continue
}
}
if stream.Err() == nil {
@@ -180,6 +184,19 @@ func (o *Client) NeedsRawMode(modelName string) bool {
return slices.Contains(openAIModelsNeedingRaw, modelName)
}
func parseReasoningEffort(level domain.ThinkingLevel) (shared.ReasoningEffort, bool) {
switch domain.ThinkingLevel(strings.ToLower(string(level))) {
case domain.ThinkingLow:
return shared.ReasoningEffortLow, true
case domain.ThinkingMedium:
return shared.ReasoningEffortMedium, true
case domain.ThinkingHigh:
return shared.ReasoningEffortHigh, true
default:
return "", false
}
}
func (o *Client) buildResponseParams(
inputMsgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions,
) (ret responses.ResponseNewParams) {
@@ -225,6 +242,10 @@ func (o *Client) buildResponseParams(
ret.Tools = tools
}
if eff, ok := parseReasoningEffort(opts.Thinking); ok {
ret.Reasoning = shared.ReasoningParam{Effort: eff}
}
if !opts.Raw {
ret.Temperature = openai.Float(opts.Temperature)
if opts.TopP != 0 {

View File

@@ -0,0 +1,153 @@
package openai
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"slices"
"sort"
"strings"
debuglog "github.com/danielmiessler/fabric/internal/log"
openai "github.com/openai/openai-go"
)
// MaxAudioFileSize defines the maximum allowed size for audio uploads (25MB).
const MaxAudioFileSize int64 = 25 * 1024 * 1024
// AllowedTranscriptionModels lists the models supported for transcription.
var AllowedTranscriptionModels = []string{
string(openai.AudioModelWhisper1),
string(openai.AudioModelGPT4oMiniTranscribe),
string(openai.AudioModelGPT4oTranscribe),
}
// allowedAudioExtensions defines the supported input file extensions.
var allowedAudioExtensions = map[string]struct{}{
".mp3": {},
".mp4": {},
".mpeg": {},
".mpga": {},
".m4a": {},
".wav": {},
".webm": {},
}
// TranscribeFile transcribes the given audio file using the specified model. If the file
// exceeds the size limit, it can optionally be split into chunks using ffmpeg.
func (o *Client) TranscribeFile(ctx context.Context, filePath, model string, split bool) (string, error) {
if ctx == nil {
ctx = context.Background()
}
if !slices.Contains(AllowedTranscriptionModels, model) {
return "", fmt.Errorf("model '%s' is not supported for transcription", model)
}
ext := strings.ToLower(filepath.Ext(filePath))
if _, ok := allowedAudioExtensions[ext]; !ok {
return "", fmt.Errorf("unsupported audio format '%s'", ext)
}
info, err := os.Stat(filePath)
if err != nil {
return "", err
}
var files []string
var cleanup func()
if info.Size() > MaxAudioFileSize {
if !split {
return "", fmt.Errorf("file %s exceeds 25MB limit; use --split-media-file to enable automatic splitting", filePath)
}
debuglog.Debug(debuglog.Basic, "File %s is larger than the size limit... breaking it up into chunks...\n", filePath)
if files, cleanup, err = splitAudioFile(filePath, ext, MaxAudioFileSize); err != nil {
return "", err
}
defer cleanup()
} else {
files = []string{filePath}
}
var builder strings.Builder
for i, f := range files {
debuglog.Debug(debuglog.Basic, "Using model %s to transcribe part %d (file name: %s)...\n", model, i+1, f)
var chunk *os.File
if chunk, err = os.Open(f); err != nil {
return "", err
}
params := openai.AudioTranscriptionNewParams{
File: chunk,
Model: openai.AudioModel(model),
}
var resp *openai.Transcription
resp, err = o.ApiClient.Audio.Transcriptions.New(ctx, params)
chunk.Close()
if err != nil {
return "", err
}
if i > 0 {
builder.WriteString(" ")
}
builder.WriteString(resp.Text)
}
return builder.String(), nil
}
// splitAudioFile splits the source file into chunks smaller than maxSize using ffmpeg.
// It returns the list of chunk file paths and a cleanup function.
func splitAudioFile(src, ext string, maxSize int64) (files []string, cleanup func(), err error) {
if _, err = exec.LookPath("ffmpeg"); err != nil {
return nil, nil, fmt.Errorf("ffmpeg not found: please install it")
}
var dir string
if dir, err = os.MkdirTemp("", "fabric-audio-*"); err != nil {
return nil, nil, err
}
cleanup = func() { os.RemoveAll(dir) }
segmentTime := 600 // start with 10 minutes
for {
pattern := filepath.Join(dir, "chunk-%03d"+ext)
debuglog.Debug(debuglog.Basic, "Running ffmpeg to split audio into %d-second chunks...\n", segmentTime)
cmd := exec.Command("ffmpeg", "-y", "-i", src, "-f", "segment", "-segment_time", fmt.Sprintf("%d", segmentTime), "-c", "copy", pattern)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err = cmd.Run(); err != nil {
return nil, cleanup, fmt.Errorf("ffmpeg failed: %v: %s", err, stderr.String())
}
if files, err = filepath.Glob(filepath.Join(dir, "chunk-*"+ext)); err != nil {
return nil, cleanup, err
}
sort.Strings(files)
tooBig := false
for _, f := range files {
var info os.FileInfo
if info, err = os.Stat(f); err != nil {
return nil, cleanup, err
}
if info.Size() > maxSize {
tooBig = true
break
}
}
if !tooBig {
return files, cleanup, nil
}
for _, f := range files {
_ = os.Remove(f)
}
if segmentTime <= 1 {
return nil, cleanup, fmt.Errorf("unable to split file into acceptable size chunks")
}
segmentTime /= 2
}
}

View File

@@ -148,7 +148,6 @@ func (o *VendorsManager) setupVendorTo(vendor Vendor, configuredVendors map[stri
delete(configuredVendors, vendor.GetName())
fmt.Printf("[%v] skipped\n", vendor.GetName())
}
return
}
type modelResult struct {

View File

@@ -31,6 +31,27 @@ type Pattern struct {
func (o *PatternsEntity) GetApplyVariables(
source string, variables map[string]string, input string) (pattern *Pattern, err error) {
if pattern, err = o.loadPattern(source); err != nil {
return
}
err = o.applyVariables(pattern, variables, input)
return
}
// GetWithoutVariables returns a pattern with only the {{input}} placeholder processed
// and skips template variable replacement
func (o *PatternsEntity) GetWithoutVariables(source, input string) (pattern *Pattern, err error) {
if pattern, err = o.loadPattern(source); err != nil {
return
}
o.applyInput(pattern, input)
return
}
func (o *PatternsEntity) loadPattern(source string) (pattern *Pattern, err error) {
// Determine if this is a file path
isFilePath := strings.HasPrefix(source, "\\") ||
strings.HasPrefix(source, "/") ||
@@ -39,8 +60,8 @@ func (o *PatternsEntity) GetApplyVariables(
if isFilePath {
// Resolve the file path using GetAbsolutePath
absPath, err := util.GetAbsolutePath(source)
if err != nil {
var absPath string
if absPath, err = util.GetAbsolutePath(source); err != nil {
return nil, fmt.Errorf("could not resolve file path: %v", err)
}
@@ -51,26 +72,27 @@ func (o *PatternsEntity) GetApplyVariables(
pattern, err = o.getFromDB(source)
}
if err != nil {
return
}
// Apply variables to the pattern
err = o.applyVariables(pattern, variables, input)
return
}
func (o *PatternsEntity) applyVariables(
pattern *Pattern, variables map[string]string, input string) (err error) {
// Ensure pattern has an {{input}} placeholder
// If not present, append it on a new line
func (o *PatternsEntity) ensureInput(pattern *Pattern) {
if !strings.Contains(pattern.Pattern, "{{input}}") {
if !strings.HasSuffix(pattern.Pattern, "\n") {
pattern.Pattern += "\n"
}
pattern.Pattern += "{{input}}"
}
}
func (o *PatternsEntity) applyInput(pattern *Pattern, input string) {
o.ensureInput(pattern)
pattern.Pattern = strings.ReplaceAll(pattern.Pattern, "{{input}}", input)
}
func (o *PatternsEntity) applyVariables(
pattern *Pattern, variables map[string]string, input string) (err error) {
o.ensureInput(pattern)
// Temporarily replace {{input}} with a sentinel token to protect it
// from recursive variable resolution

View File

@@ -145,6 +145,22 @@ func TestGetApplyVariables(t *testing.T) {
}
}
func TestGetWithoutVariables(t *testing.T) {
entity, cleanup := setupTestPatternsEntity(t)
defer cleanup()
createTestPattern(t, entity, "test-pattern", "Prefix {{input}} {{roam}}")
result, err := entity.GetWithoutVariables("test-pattern", "hello")
require.NoError(t, err)
assert.Equal(t, "Prefix hello {{roam}}", result.Pattern)
createTestPattern(t, entity, "no-input", "Static content")
result, err = entity.GetWithoutVariables("no-input", "hi")
require.NoError(t, err)
assert.Equal(t, "Static content\nhi", result.Pattern)
}
func TestPatternsEntity_Save(t *testing.T) {
entity, cleanup := setupTestPatternsEntity(t)
defer cleanup()

View File

@@ -10,8 +10,9 @@ import (
"strings"
"time"
debuglog "github.com/danielmiessler/fabric/internal/log"
"gopkg.in/yaml.v3"
// Add this import
)
// ExtensionDefinition represents a single extension configuration
@@ -87,9 +88,7 @@ func NewExtensionRegistry(configDir string) *ExtensionRegistry {
r.ensureConfigDir()
if err := r.loadRegistry(); err != nil {
if Debug {
fmt.Printf("Warning: could not load extension registry: %v\n", err)
}
debuglog.Debug(debuglog.Basic, "Warning: could not load extension registry: %v\n", err)
}
return r

View File

@@ -6,6 +6,8 @@ import (
"path/filepath"
"regexp"
"strings"
debuglog "github.com/danielmiessler/fabric/internal/log"
)
var (
@@ -14,7 +16,6 @@ var (
filePlugin = &FilePlugin{}
fetchPlugin = &FetchPlugin{}
sysPlugin = &SysPlugin{}
Debug = false // Debug flag
)
var extensionManager *ExtensionManager
@@ -33,9 +34,7 @@ var pluginPattern = regexp.MustCompile(`\{\{plugin:([^:]+):([^:]+)(?::([^}]+))?\
var extensionPattern = regexp.MustCompile(`\{\{ext:([^:]+):([^:]+)(?::([^}]+))?\}\}`)
func debugf(format string, a ...interface{}) {
if Debug {
fmt.Printf(format, a...)
}
debuglog.Debug(debuglog.Trace, format, a...)
}
func ApplyTemplate(content string, variables map[string]string, input string) (string, error) {

View File

@@ -105,7 +105,7 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
}
}
chatter, err := h.registry.GetChatter(p.Model, 2048, "", false, false)
chatter, err := h.registry.GetChatter(p.Model, 2048, p.Vendor, "", false, false)
if err != nil {
log.Printf("Error creating chatter: %v", err)
streamChan <- fmt.Sprintf("Error: %v", err)
@@ -130,6 +130,7 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
TopP: request.TopP,
FrequencyPenalty: request.FrequencyPenalty,
PresencePenalty: request.PresencePenalty,
Thinking: request.Thinking,
}
session, err := chatter.Send(chatReq, opts)

View File

@@ -181,7 +181,8 @@ func (o *YouTube) tryMethodYtDlpInternal(videoId string, language string, additi
if len(langMatch) > 2 {
langMatch = langMatch[:2]
}
args = append(args, "--sub-langs", langMatch)
langOpts := language + "," + langMatch + ".*," + langMatch
args = append(args, "--sub-langs", langOpts)
}
// Add user-provided arguments last so they take precedence
@@ -210,7 +211,7 @@ func (o *YouTube) tryMethodYtDlpInternal(videoId string, language string, additi
}
if strings.Contains(stderrStr, "Sign in to confirm you're not a bot") || strings.Contains(stderrStr, "Use --cookies-from-browser") {
err = fmt.Errorf("YouTube requires authentication (bot detection). Use --yt-dlp-args '--cookies-from-browser BROWSER' where BROWSER is chrome, firefox, brave, etc. Error: %v", err)
err = fmt.Errorf("YouTube requires authentication (bot detection). Use --yt-dlp-args='--cookies-from-browser BROWSER' where BROWSER is chrome, firefox, brave, etc. Error: %v", err)
return
}

View File

@@ -26,8 +26,8 @@ schema = 3
version = "v1.3.3"
hash = "sha256-jv7ZshpSd7FZzKKN6hqlUgiR8C3y85zNIS/hq7g76Ho="
[mod."github.com/anthropics/anthropic-sdk-go"]
version = "v1.7.0"
hash = "sha256-DvpFXlUE04HeMbqQX4HIC/KMJYPXJ8rEaZkNJb1rWxs="
version = "v1.9.1"
hash = "sha256-1saDnM1DMnDLHT4RoA/EFuOvW7CIFh2tkfOJ1/+itNc="
[mod."github.com/araddon/dateparse"]
version = "v0.0.0-20210429162001-6b43995a97de"
hash = "sha256-UuX84naeRGMsFOgIgRoBHG5sNy1CzBkWPKmd6VbLwFw="
@@ -308,8 +308,8 @@ schema = 3
version = "v0.18.0"
hash = "sha256-tUpUPERjmRi7zldj0oPlnbnBhEkcI9iQGvP1HqlsK10="
[mod."golang.org/x/crypto"]
version = "v0.39.0"
hash = "sha256-FtwjbVoAhZkx7F2hmzi9Y0J87CVVhWcrZzun+zWQLzc="
version = "v0.40.0"
hash = "sha256-I6p2fqvz63P9MwAuoQrljI7IUbfZQvCem0ii4Q2zZng="
[mod."golang.org/x/exp"]
version = "v0.0.0-20250531010427-b6e5de432a8b"
hash = "sha256-QaFfjyB+pogCkUkJskR9xnXwkCOU828XJRrzwwLm6Ms="

View File

@@ -1 +1 @@
"1.4.278"
"1.4.293"

View File

@@ -0,0 +1,99 @@
# README Update Scripts
This directory contains automation scripts for updating the main README.md file with release information from the changelog database.
## `update_readme_features.py`
A Python script that generates the "Recent Major Features" section for the README by extracting and filtering release information from the changelog SQLite database.
### Usage
```bash
# Generate the Recent Major Features section with default limit (20 releases)
python scripts/readme_updates/update_readme_features.py
# Specify a custom limit
python scripts/readme_updates/update_readme_features.py --limit 15
# Use a custom database path
python scripts/readme_updates/update_readme_features.py --db /path/to/changelog.db
```
### How It Works
1. **Database Connection**: Connects to `cmd/generate_changelog/changelog.db` (or custom path)
2. **Data Extraction**: Queries the `versions` table for release information
3. **Feature Filtering**: Uses heuristics to identify feature/improvement releases
4. **Markdown Generation**: Formats output to match README style
### Feature Detection Heuristics
The script uses keyword-based heuristics to filter releases:
#### Include Keywords (Features/Improvements)
- new, feature, feat, add, introduce, enable, support
- improve, enhance, performance, speed
- option, flag, argument, parameter
- integration, provider, search, tts, audio, model
- cli, ui, web, oauth, sync, database
- notifications, desktop, reasoning, thinking
#### Exclude Keywords (Non-Features)
- fix, bug, hotfix
- ci, cd, pipeline, chore
- docs, readme, refactor, style, typo
- test, bump, deps, dependency
- merge, revert, format, lint, build
- release, prepare, coverage, security
### Integration with README
To update the README with new release features:
```bash
# Generate the features and save to a temporary file
python scripts/readme_updates/update_readme_features.py --limit 20 > /tmp/recent_features.md
# Manually replace the "### Recent Major Features" section in README.md
# with the generated content
```
### Database Schema
The script expects the following SQLite table structure:
```sql
CREATE TABLE versions (
name TEXT PRIMARY KEY,
date DATETIME,
commit_sha TEXT,
pr_numbers TEXT,
ai_summary TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
```
### Date Format Support
The script can parse various date formats:
- ISO 8601 with timezone: `2025-08-14 14:11:04+00:00`
- ISO 8601 basic: `2025-08-14T14:11:04`
- Date only: `2025-08-14`
- US format: `08/14/2025`
Output format is standardized to: `Aug 14, 2025`
### Maintenance Notes
- **AI Summary Format Changes**: If the format of AI summaries changes, update the `extract_title_desc()` and `split_summary()` functions
- **Keyword Tuning**: Adjust `INCLUDE_RE` and `EXCLUDE_RE` patterns as needed
- **Title Extraction**: The script attempts to extract concise titles from feature descriptions
- **Description Length**: Descriptions are limited to 200 characters for readability
### Future Enhancements
Potential improvements for automated README updates:
- Add section delimiter markers in README for automated replacement
- Create a GitHub Action to run on new releases
- Add support for categorizing features by type
- Implement confidence scoring for feature detection

View File

@@ -0,0 +1,281 @@
#!/usr/bin/env python3
"""
Generate the '### Recent Major Features' markdown section for README from the changelog SQLite DB.
- Connects to cmd/generate_changelog/changelog.db
- Extracts version, date, and AI summaries from the 'versions' table
- Heuristically filters for feature/improvement items (excludes CI/CD/docs/bug fixes)
- Formats output to match README style:
- [vX.Y.Z](https://github.com/danielmiessler/fabric/releases/tag/vX.Y.Z) (Aug 14, 2025) — **Feature Name**: Short description
Usage:
python scripts/readme_updates/update_readme_features.py --limit 20
"""
import argparse
import sqlite3
from pathlib import Path
from datetime import datetime
import re
import sys
from typing import List, Optional, Tuple
# Heuristics for filtering feature-related lines
EXCLUDE_RE = re.compile(
r"(?i)\b(fix|bug|hotfix|ci|cd|pipeline|chore|docs|doc|readme|refactor|style|typo|"
"test|tests|bump|deps|dependency|merge|revert|format|lint|build|release\b|prepare|"
"codeowners|coverage|security)\b"
)
INCLUDE_RE = re.compile(
r"(?i)\b(new|feature|feat|add|added|introduce|enable|support|improve|enhance|"
"performance|speed|option|flag|argument|parameter|integration|provider|search|tts|"
"audio|model|cli|ui|web|oauth|sync|database|notifications|desktop|reasoning|thinking)\b"
)
def parse_args():
"""Parse command-line arguments."""
p = argparse.ArgumentParser(
description="Generate README 'Recent Major Features' markdown from changelog DB."
)
p.add_argument(
"--limit", type=int, default=20, help="Maximum number of releases to include."
)
p.add_argument(
"--db",
type=str,
default=None,
help="Optional path to changelog.db (defaults to repo cmd/generate_changelog/changelog.db)",
)
return p.parse_args()
def repo_root() -> Path:
"""Get the repository root directory."""
# scripts/readme_updates/update_readme_features.py -> repo root is parent.parent.parent
return Path(__file__).resolve().parent.parent.parent
def db_path(args) -> Path:
"""Determine the database path."""
if args.db:
return Path(args.db).expanduser().resolve()
return repo_root() / "cmd" / "generate_changelog" / "changelog.db"
def connect(dbfile: Path):
"""Connect to the SQLite database."""
if not dbfile.exists():
print(f"ERROR: changelog database not found: {dbfile}", file=sys.stderr)
sys.exit(1)
return sqlite3.connect(str(dbfile))
def normalize_version(name: str) -> str:
"""Ensure version string starts with 'v'."""
n = str(name).strip()
return n if n.startswith("v") else f"v{n}"
def parse_date(value) -> str:
"""Parse various date formats and return formatted string."""
if value is None:
return "(Unknown date)"
# Handle the ISO format with timezone from the database
s = str(value).strip()
# Try to parse the ISO format with timezone
if "+" in s or "T" in s:
# Remove timezone info and microseconds for simpler parsing
s_clean = s.split("+")[0].split(".")[0]
try:
dt = datetime.strptime(s_clean, "%Y-%m-%d %H:%M:%S")
return dt.strftime("%b %d, %Y").replace(" 0", " ")
except ValueError:
pass
# Fallback formats
fmts = [
"%Y-%m-%d",
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%dT%H:%M:%S",
"%Y/%m/%d",
"%m/%d/%Y",
]
for fmt in fmts:
try:
dt = datetime.strptime(s, fmt)
return dt.strftime("%b %d, %Y").replace(" 0", " ")
except ValueError:
continue
# Return original if we can't parse it
return f"({s})"
def split_summary(text: str) -> List[str]:
"""Split AI summary into individual lines/bullets."""
if not text:
return []
lines = []
# Split by newlines first
for line in text.split("\n"):
line = line.strip()
if not line:
continue
# Remove markdown headers
line = re.sub(r"^#+\s+", "", line)
# Remove PR links and author info
line = re.sub(
r"^PR\s+\[#\d+\]\([^)]+\)\s+by\s+\[[^\]]+\]\([^)]+\):\s*", "", line
)
# Remove bullet points
line = re.sub(r"^[-*•]\s+", "", line)
if line:
lines.append(line)
return lines
def is_feature_line(line: str) -> bool:
"""Check if a line describes a feature/improvement (not a bug fix or CI/CD)."""
line_lower = line.lower()
# Strong exclusions first
if any(
word in line_lower
for word in ["chore:", "fix:", "docs:", "test:", "ci:", "build:", "refactor:"]
):
return False
if EXCLUDE_RE.search(line):
return False
return bool(INCLUDE_RE.search(line))
def extract_title_desc(line: str) -> Tuple[str, str]:
"""Extract title and description from a feature line."""
# Remove any markdown formatting
line = re.sub(r"\*\*([^*]+)\*\*", r"\1", line)
# Look for colon separator first
if ":" in line:
parts = line.split(":", 1)
if len(parts) == 2:
title = parts[0].strip()
desc = parts[1].strip()
# Clean up the title
title = (
title.replace("Introduce ", "")
.replace("Enable ", "")
.replace("Add ", "")
)
title = title.replace("Implement ", "").replace("Support ", "")
# Make title more concise
if len(title) > 30:
# Try to extract key words
key_words = []
for word in title.split():
if word[0].isupper() or "-" in word or "_" in word:
key_words.append(word)
if key_words:
title = " ".join(key_words[:3])
return (title, desc)
# Fallback: use first sentence as description
sentences = re.split(r"[.!?]\s+", line)
if sentences:
desc = sentences[0].strip()
# Extract a title from the description
if "thinking" in desc.lower():
return ("AI Reasoning", desc)
elif "token" in desc.lower() and "context" in desc.lower():
return ("Extended Context", desc)
elif "curl" in desc.lower() or "install" in desc.lower():
return ("Easy Setup", desc)
elif "vendor" in desc.lower() or "model" in desc.lower():
return ("Model Management", desc)
elif "notification" in desc.lower():
return ("Desktop Notifications", desc)
elif "tts" in desc.lower() or "speech" in desc.lower():
return ("Text-to-Speech", desc)
elif "oauth" in desc.lower() or "auth" in desc.lower():
return ("OAuth Auto-Auth", desc)
elif "search" in desc.lower() and "web" in desc.lower():
return ("Web Search", desc)
else:
# Generic title from first significant words
words = desc.split()[:2]
title = " ".join(words)
return (title, desc)
return ("Feature", line)
def pick_feature(ai_summary: str) -> Optional[Tuple[str, str]]:
"""Pick the best feature line from the AI summary."""
lines = split_summary(ai_summary)
# Look for the first feature line
for line in lines:
if is_feature_line(line):
title, desc = extract_title_desc(line)
# Clean up description - remove redundant info
desc = desc[:200] if len(desc) > 200 else desc # Limit length
return (title, desc)
return None
def build_item(
version: str, date_str: str, feature_title: str, feature_desc: str
) -> str:
"""Build a markdown list item for a release."""
url = f"https://github.com/danielmiessler/fabric/releases/tag/{version}"
return f"- [{version}]({url}) ({date_str}) — **{feature_title}**: {feature_desc}"
def main():
"""Main function."""
args = parse_args()
dbfile = db_path(args)
conn = connect(dbfile)
cur = conn.cursor()
# Query the database
cur.execute("SELECT name, date, ai_summary FROM versions ORDER BY date DESC")
rows = cur.fetchall()
items = []
for name, date, summary in rows:
version = normalize_version(name)
date_fmt = parse_date(date)
feat = pick_feature(summary or "")
if not feat:
continue
title, desc = feat
items.append(build_item(version, date_fmt, title, desc))
if len(items) >= args.limit:
break
conn.close()
# Output the markdown
print("### Recent Major Features")
print()
for item in items:
print(item)
if __name__ == "__main__":
main()

View File

@@ -11,7 +11,7 @@ This is a web app for Fabric. It was built using [Svelte][svelte], [SkeletonUI][
The goal of this app is to not only provide a user interface for Fabric, but also an out-of-the-box website for those who want to get started with web development, blogging, or to just have a web interface for fabric. You can use this app as a GUI interface for Fabric, a ready to go blog-site, or a website template for your own projects.
![Preview](./static/preview.png)
![Preview](../docs/images/svelte-preview.png)
## Installing

View File

@@ -1,6 +1,6 @@
---
title: README
description: fabric is an open-source framework for augmenting humans using AI. It provides a modular framework for solving specific problems using a crowdsourced set of AI prompts that can be used anywhere.
description: fabric is an open-source framework for augmenting humans using AI. It provides a modular framework for solving specific problems using a crowd-sourced set of AI prompts that can be used anywhere.
aliases: Fabric/Docs
date: 2024-1-12
updated: 2024-11-22
@@ -12,7 +12,6 @@ updated: 2024-11-22
# `fabric`
<div class="justify-left flex gap-2">
<img src="https://img.shields.io/github/languages/top/danielmiessler/fabric" alt="Github top language">
<img src="https://img.shields.io/github/last-commit/danielmiessler/fabric" alt="GitHub last commit">
@@ -23,10 +22,10 @@ updated: 2024-11-22
<h4><code>fabric</code> is an open-source framework for augmenting humans using AI.</h4>
[Updates](#updates) •
[What and Why](#whatandwhy) •
[What and Why](#what-and-why) •
[Philosophy](#philosophy) •
[Installation](#Installation) •
[Usage](#Usage) •
[Installation](#installation) •
[Usage](#usage) •
[Examples](#examples) •
[Just Use the Patterns](#just-use-the-patterns) •
[Custom Patterns](#custom-patterns) •
@@ -42,8 +41,8 @@ updated: 2024-11-22
- [`fabric`](#fabric)
- [Navigation](#navigation)
- [Updates](#updates)
- [Intro videos](#intro-videos)
- [What and why](#what-and-why)
- [Intro videos](#intro-videos)
- [Philosophy](#philosophy)
- [Breaking problems into components](#breaking-problems-into-components)
- [Too many prompts](#too-many-prompts)
@@ -65,7 +64,9 @@ updated: 2024-11-22
- [`to_pdf`](#to_pdf)
- [`to_pdf` Installation](#to_pdf-installation)
- [pbpaste](#pbpaste)
- [Web Interface](#Web_Interface)
- [Web Interface](#web-interface)
- [Installing](#installing)
- [Streamlit UI](#streamlit-ui)
- [Meta](#meta)
- [Primary contributors](#primary-contributors)
@@ -76,7 +77,7 @@ updated: 2024-11-22
> [!NOTE]
> November 8, 2024
>
> - **Multimodal Support**: You can now use `-a` (attachment) for Multimodal submissions to OpenAI models that support it. Example: `fabric -a https://path/to/image "Give me a description of this image."`
> - **Multi-modal Support**: You can now use `-a` (attachment) for Multi-modal submissions to OpenAI models that support it. Example: `fabric -a https://path/to/image "Give me a description of this image."`
## What and why
@@ -90,7 +91,7 @@ Fabric was created to address this by enabling everyone to granularly apply AI t
## Intro videos
Keep in mind that many of these were recorded when Fabric was Python-based, so remember to use the current [install instructions](#Installation) below.
Keep in mind that many of these were recorded when Fabric was Python-based, so remember to use the current [install instructions](#installation) below.
- [Network Chuck](https://www.youtube.com/watch?v=UbDyjIIGaxQ)
- [David Bombal](https://www.youtube.com/watch?v=vF-MQmVxnCs)
@@ -223,7 +224,7 @@ This also creates a `yt` alias that allows you to use `yt https://www.youtube.co
#### Save your files in markdown using aliases
If in addition to the above aliases you would like to have the option to save the output to your favourite markdown note vault like Obsidian then instead of the above add the following to your `.zshrc` or `.bashrc` file:
If in addition to the above aliases you would like to have the option to save the output to your favorite markdown note vault like Obsidian then instead of the above add the following to your `.zshrc` or `.bashrc` file:
```bash
# Define the base directory for Obsidian notes
@@ -281,7 +282,7 @@ go install github.com/danielmiessler/fabric@latest
fabric --setup
```
Then [set your environmental variables](#environmental-variables) as shown above.
Then [set your environmental variables](#environment-variables) as shown above.
### Upgrading
@@ -324,6 +325,7 @@ Application Options:
-U, --updatepatterns Update patterns
-c, --copy Copy to clipboard
-m, --model= Choose model
-V, --vendor= Specify vendor for chosen model (e.g., -V "LM Studio" -m openai/gpt-oss-20b)
-o, --output= Output to file
--output-session Output the entire session (also a temporary one) to the output file
-n, --latest= Number of latest patterns to list (default: 0)
@@ -375,21 +377,21 @@ Now let's look at some things you can do with Fabric.
1. Run the `summarize` Pattern based on input from `stdin`. In this case, the body of an article.
```bash
pbpaste | fabric --pattern summarize
```
```bash
pbpaste | fabric --pattern summarize
```
2. Run the `analyze_claims` Pattern with the `--stream` option to get immediate and streaming results.
```bash
pbpaste | fabric --stream --pattern analyze_claims
```
```bash
pbpaste | fabric --stream --pattern analyze_claims
```
3. Run the `extract_wisdom` Pattern with the `--stream` option to get immediate and streaming results from any Youtube video (much like in the original introduction video).
```bash
fabric -y "https://youtube.com/watch?v=uXs-zPc63kM" --stream --pattern extract_wisdom
```
```bash
fabric -y "https://youtube.com/watch?v=uXs-zPc63kM" --stream --pattern extract_wisdom
```
4. Create patterns- you must create a .md file with the pattern and save it to ~/.config/fabric/patterns/[yourpatternname].
@@ -414,11 +416,7 @@ You may want to use Fabric to create your own custom Patterns—but not share th
Just make a directory in `~/.config/custompatterns/` (or wherever) and put your `.md` files in there.
When you're ready to use them, copy them into:
```
~/.config/fabric/patterns/
```
When you're ready to use them, copy them into: `~/.config/fabric/patterns/`
You can then use them like any other Patterns, but they won't be public unless you explicitly submit them as Pull Requests to the Fabric project. So don't worry—they're private to you.
@@ -462,7 +460,7 @@ The [examples](#examples) use the macOS program `pbpaste` to paste content from
On Windows, you can use the PowerShell command `Get-Clipboard` from a PowerShell command prompt. If you like, you can also alias it to `pbpaste`. If you are using classic PowerShell, edit the file `~\Documents\WindowsPowerShell\.profile.ps1`, or if you are using PowerShell Core, edit `~\Documents\PowerShell\.profile.ps1` and add the alias,
```
```powershell
Set-Alias pbpaste Get-Clipboard
```
@@ -481,17 +479,19 @@ alias pbpaste='xclip -selection clipboard -o'
## Web Interface
Fabric now includes a built-in web interface that provides a GUI alternative to the command-line interface and an out-of-the-box website for those who want to get started with web development or blogging.
You can use this app as a GUI interface for Fabric, a ready to go blog-site, or a website template for your own projects.
Fabric now includes a built-in web interface that provides a GUI alternative to the command-line interface and an out-of-the-box website for those who want to get started with web development or blogging.
You can use this app as a GUI interface for Fabric, a ready to go blog-site, or a website template for your own projects.
The `web/src/lib/content` directory includes starter `.obsidian/` and `templates/` directories, allowing you to open up the `web/src/lib/content/` directory as an [Obsidian.md](https://obsidian.md) vault. You can place your posts in the posts directory when you're ready to publish.
The `web/src/lib/content` directory includes starter `.obsidian/` and `templates/` directories, allowing you to open up the `web/src/lib/content/` directory as an [Obsidian.md](https://obsidian.md) vault. You can place your posts in the posts directory when you're ready to publish.
### Installing
The GUI can be installed by navigating to the `web` directory and using `npm install`, `pnpm install`, or your favorite package manager. Then simply run the development server to start the app. 
The GUI can be installed by navigating to the `web` directory and using `npm install`, `pnpm install`, or your favorite package manager. Then simply run the development server to start the app.
_You will need to run fabric in a separate terminal with the `fabric --serve` command._
_You will need to run fabric in a separate terminal with the `fabric --serve` command._
**From the fabric project `web/` directory:**
```shell
npm run dev
@@ -499,7 +499,7 @@ npm run dev
pnpm run dev
## or your equivalent
## or your equivalent
```
### Streamlit UI
@@ -515,10 +515,12 @@ streamlit run streamlit.py
```
The Streamlit UI provides a user-friendly interface for:
- Running and chaining patterns
- Managing pattern outputs
- Creating and editing patterns
- Analyzing pattern results
## Meta
> [!NOTE]