Compare commits

...

128 Commits

Author SHA1 Message Date
github-actions[bot]
36eb321059 Update version to v1.4.192 and commit 2025-05-23 05:44:31 +00:00
Eugen Eisler
47bf9600d6 Merge pull request #1480 from ksylvan/0522-auto-raw-mode-for-some-models
Automatic setting of "raw mode" for some models
2025-05-23 07:43:04 +02:00
Kayvan Sylvan
be674841e7 feat: add automatic raw mode detection for specific AI models
## CHANGES

- Add model-specific raw mode detection logic
- Check Ollama llama2/llama3 models for raw mode
- Check OpenAI o1/o3/o4 models for raw mode
- Use model from options or default chatter
- Auto-enable raw mode when vendor requires it
- Import strings package for prefix matching
2025-05-22 17:04:11 -07:00
Kayvan Sylvan
39a8b67438 feat: add NeedsRawMode method to AI vendor interface
## CHANGES

- Add NeedsRawMode to Vendor interface
- Implement NeedsRawMode in all AI clients
- Return false for all implementations
- Support model-specific raw mode detection
- Enable future raw mode requirements
2025-05-22 16:41:12 -07:00
github-actions[bot]
0a4950dd08 Update version to v1.4.191 and commit 2025-05-22 19:03:47 +00:00
Eugen Eisler
593c1558c0 Merge pull request #1478 from ksylvan/0522-upgrade-to-claude-4
Claude 4 Integration and README Updates
2025-05-22 21:02:11 +02:00
Kayvan Sylvan
c8f9a39a40 feat: add support for Anthropic Claude 4 models and update SDK to v1.2.0
CHANGES
- Upgrade `anthropic-sdk-go` dependency to version `v1.2.0`.
- Integrate new Anthropic Claude 4 Opus and Sonnet models.
- Remove deprecated Claude 2.0 and 2.1 models from list.
- Adjust model type casting for `anthropic-sdk-go v1.2.0` compatibility.
- Refresh README: announce Claude 4, update date, fix links.
2025-05-22 11:26:04 -07:00
github-actions[bot]
50ec02546f Update version to v1.4.190 and commit 2025-05-20 10:12:21 +00:00
Eugen Eisler
881085d0fe Merge pull request #1475 from ksylvan/0519-fix-dupe-input-attempt-2
refactor: improve raw mode handling in BuildSession
2025-05-20 12:10:47 +02:00
Kayvan Sylvan
2d75052e57 refactor: improve raw mode handling in BuildSession
## CHANGES

- Fix system message handling with patterns in raw mode
- Prevent duplicate inputs when using patterns
- Add conditional logic for pattern vs non-pattern scenarios
- Simplify message construction with clearer variable names
- Improve code comments for better readability
2025-05-19 22:18:12 -07:00
github-actions[bot]
fee604682b Update version to v1.4.189 and commit 2025-05-19 21:39:14 +00:00
Eugen Eisler
941ccabd92 Merge pull request #1473 from roumy/add_authent_ollama
add authentification for ollama instance
2025-05-19 23:37:45 +02:00
github-actions[bot]
57cd563963 Update version to v1.4.188 and commit 2025-05-19 21:36:30 +00:00
Eugen Eisler
274b6eada6 Merge pull request #1474 from ksylvan/0519-fix-doubled-user-input
feat: update `BuildSession` to handle message appending logic
2025-05-19 23:35:03 +02:00
Kayvan Sylvan
bc27f9d685 refactor: improve message handling for raw mode and Anthropic client
## CHANGES

- Clarify raw mode message handling in BuildSession
- Fix pattern-based message handling in non-raw mode
- Refactor Anthropic client message normalization
- Add proper handling for empty message arrays
- Implement user/assistant message alternation for Anthropic
- Preserve system messages in Anthropic conversations
- Add safeguards for message sequence validation
2025-05-19 12:50:41 -07:00
pr
1291b35b63 add authentification for ollama instance 2025-05-19 11:01:47 +02:00
Eugen Eisler
9862564c45 Merge pull request #1467 from joshuafuller/main
Typos, spelling, grammar and other minor updates
2025-05-19 07:52:12 +02:00
Eugen Eisler
bbc183f276 Merge pull request #1468 from NavNab/main
Refactor content structure in create_hormozi_offer system.md for clarity and readability
2025-05-18 20:26:28 +02:00
NavNab
9c4445d7bd Refactor content structure in system.md for clarity and readability
- Improved formatting of the introduction and content summary sections for better flow.
- Consolidated repetitive sentences and enhanced the overall coherence of the text.
- Adjusted bullet points and numbering for consistency and easier comprehension.
- Ensured that key concepts are clearly articulated and visually distinct to aid understanding.
2025-05-18 17:03:24 +02:00
Joshua Fuller
920620d771 Merge pull request #1 from joshuafuller/branch/fix-spelling-in-pattern-management-guide 2025-05-16 23:48:52 -05:00
Joshua Fuller
d734e25e0d Merge pull request #2 from joshuafuller/branch/fix-spelling-in-pr-1284-update-notes 2025-05-16 23:48:42 -05:00
Joshua Fuller
a31b2d5e41 Merge pull request #3 from joshuafuller/branch/fix-typos-in-web-readme 2025-05-16 23:48:31 -05:00
Joshua Fuller
8e7e4aa169 Merge pull request #4 from joshuafuller/branch/fix-spelling-of-anthropic-in-notes-md 2025-05-16 23:48:23 -05:00
Joshua Fuller
ea57a64afa Merge pull request #5 from joshuafuller/branch/fix-grammar-in-nuclei-template-instructions 2025-05-16 23:48:13 -05:00
Joshua Fuller
da1a9dab56 docs: fix grammar in nuclei template instructions 2025-05-16 23:45:33 -05:00
Joshua Fuller
068f111986 docs: correct Anthropic spelling in notes 2025-05-16 23:44:31 -05:00
Joshua Fuller
dd0be51726 docs: fix typos in web README 2025-05-16 23:44:19 -05:00
Joshua Fuller
43a1e66cc8 docs: fix spelling in PR 1284 update notes 2025-05-16 23:44:06 -05:00
Joshua Fuller
430a272e1d docs: fix spelling in pattern management guide 2025-05-16 23:43:38 -05:00
github-actions[bot]
0e892f38e4 Update version to v1.4.187 and commit 2025-05-10 07:42:11 +00:00
Eugen Eisler
aa0fe90258 Merge pull request #1463 from CodeCorrupt/nixpkgs_completion
Add completion to the build output for Nix
2025-05-10 09:40:45 +02:00
CodeCorrupt
c59c7553b3 Add completion files to the build output for Nix 2025-05-07 17:06:00 -04:00
github-actions[bot]
703756d0b0 Update version to v1.4.186 and commit 2025-05-06 22:06:46 +00:00
Eugen Eisler
50d22f8e77 Merge pull request #1459 from ksylvan/0505-cleanup-some-old-detritus
chore: Repository cleanup and .gitignore Update
2025-05-07 00:05:19 +02:00
Kayvan Sylvan
fde2efd4ce chore: update .gitignore and remove obsolete files
- Add `coverage.out` to `.gitignore` for ignoring coverage output.
- Remove `Alma.md` documentation file from the repository.
- Delete `rate_ai_result.txt` stitch script from `stitches` folder.
- Remove `readme.md` for `rate_ai_result` stitch documentation.
2025-05-05 17:16:38 -07:00
github-actions[bot]
0150c3a37d Update version to v1.4.185 and commit 2025-04-28 19:27:01 +00:00
Eugen Eisler
2a0216b9aa Merge pull request #1453 from ksylvan/0428-default-model-setting-fix
Fix for default model setting
2025-04-28 21:25:35 +02:00
Kayvan Sylvan
a6d14d86b8 refactor: introduce getSortedGroupsItems for consistent sorting logic
### CHANGES
- Add `getSortedGroupsItems` to centralize sorting logic.
- Sort groups and items alphabetically, case-insensitive.
- Replace inline sorting in `Print` with new method.
- Update `GetGroupAndItemByItemNumber` to use sorted data.
- Ensure original `GroupsItems` remains unmodified.
2025-04-28 11:41:32 -07:00
github-actions[bot]
a9374c128b Update version to v1.4.184 and commit 2025-04-25 08:27:55 +00:00
Eugen Eisler
f32b9f81da Merge pull request #1447 from ksylvan/0424-more-shell-completions
More shell completion scripts: Zsh, Bash, and Fish
2025-04-25 10:26:20 +02:00
Kayvan Sylvan
bf3af8e98e feat: add shell completion scripts for Zsh, Bash, and Fish
CHANGES:
- Add shell completion support for three major shells
- Create standardized completion scripts in completions/ directory
- Add --shell-complete-list flag for machine-readable output
- Update Print() methods to support plain output format
- Document installation steps for each shell in README
- Replace old fish completion script with improved version
2025-04-24 17:47:39 -07:00
github-actions[bot]
095c295ee5 Update version to v1.4.183 and commit 2025-04-23 20:03:10 +00:00
Eugen Eisler
93ecc9cfea Merge pull request #1431 from KenMacD/fish-completion
Add a completion script for fish
2025-04-23 22:01:41 +02:00
github-actions[bot]
e7aaa23fc2 Update version to v1.4.182 and commit 2025-04-23 20:00:59 +00:00
Eugen Eisler
197d3454f8 Merge pull request #1441 from ksylvan/0423-nix-go-build-toolchain-update
Update go toolchain and go module packages to latest versions
2025-04-23 21:59:25 +02:00
Kayvan Sylvan
50a4f8b491 chore: fix "nix flake check" errors 2025-04-23 11:07:01 -07:00
Kayvan Sylvan
894b4967dd refactor: centralize Go version definition in flake.nix
CHANGES
*   Define `getGoVersion` function in `flake.nix`.
*   Use `getGoVersion` to set Go version consistently.
*   Pass `goVersion` explicitly into `nix/shell.nix`.
*   Remove redundant Go version definition from `shell.nix`.
2025-04-23 09:34:39 -07:00
Kayvan Sylvan
9837bd6664 chore: update Go to 1.24.2 and refresh dependencies
Update Go version across Dockerfile, Nix configurations, and Go modules.
Refresh dependencies and Nix flake inputs.

CHANGES:
*   Update Go version to 1.24.2 in Dockerfile.
*   Set Go version to 1.24.0 and toolchain to 1.24.2.
*   Refresh Go module dependencies and sums (go.mod, go.sum).
*   Update Nix flake lock file inputs.
*   Configure Nix environment and packages for Go 1.24.
*   Update gomod2nix lock file with dependency hashes.
*   Use Go 1.24 in Nix development shell environment.
2025-04-23 09:18:01 -07:00
github-actions[bot]
6ca1b5dac4 Update version to v1.4.181 and commit 2025-04-22 16:03:07 +00:00
Eugen Eisler
c85135c04e Merge pull request #1433 from ksylvan/0421-anthropic-api-update
chore: update Anthropic SDK to v0.2.0-beta.3 and migrate to V2 API
2025-04-22 18:01:53 +02:00
github-actions[bot]
31e4e42a94 Update version to v1.4.180 and commit 2025-04-22 11:40:37 +00:00
Eugen Eisler
196db04fc2 Merge pull request #1435 from ksylvan/0421-fix-raw-input-with-stratetgies
chore: Fix user input handling when using raw mode and `--strategy` flag
2025-04-22 13:39:22 +02:00
Kayvan Sylvan
b3b1b5a471 chore: unify raw mode message handling and preserve env vars in extension executor
## CHANGES

- refactor BuildSession raw mode to prepend system to user content
- ensure raw mode messages always have User role
- keep existing user message when no systemMessage provided
- append systemMessage separately in non-raw mode sessions
- store original cmd.Env before context-based exec command creation
- recreate exec command with context then restore originalEnv
- add comments clarifying raw vs non-raw handling behavior
2025-04-21 17:04:11 -07:00
Kayvan Sylvan
892439a177 chore: update Anthropic SDK to v0.2.0-beta.3 and migrate to V2 API
## CHANGES

- Upgrade Anthropic SDK from alpha.11 to beta.3
- Update API endpoint from v1 to v2
- Replace anthropic.F() with direct assignment
- Replace anthropic.F() with anthropic.Opt() for optional params
- Simplify event delta handling in streaming
- Change client type from pointer to value type
- Update comment with SDK changelog reference
2025-04-21 13:17:03 -07:00
github-actions[bot]
ba2e178e03 Update version to v1.4.179 and commit 2025-04-21 18:08:47 +00:00
Eugen Eisler
ed298bcedd Merge pull request #1432 from ksylvan/0421-fix-tools-selection-in-setup
chore: fix fabric setup mess-up introduced by sorting lists (tools and models)
2025-04-21 20:07:33 +02:00
Kayvan Sylvan
6b04e6e674 chore: sort AI models alphabetically for consistent listing
CHANGES
*   Import `sort` and `strings` packages for sorting functionality.
*   Sort retrieved AI model names alphabetically, ignoring case.
*   Ensure consistent ordering of AI models in lists.
2025-04-21 10:41:41 -07:00
Kayvan Sylvan
04c0f6a0a5 chore: alphabetize the order of plugin tools 2025-04-21 10:26:04 -07:00
Kenny MacDermid
486ff42b59 Add a completion script for fish 2025-04-21 12:58:05 -03:00
github-actions[bot]
f7ab484510 Update version to v1.4.178 and commit 2025-04-21 13:21:52 +00:00
Eugen Eisler
f50a14305a Merge pull request #1427 from ksylvan/0420-refactor-openai-compatible-providers
Refactor OpenAI-compatible AI providers and add `--listvendors` flag
2025-04-21 15:20:33 +02:00
github-actions[bot]
d5f0cd7616 Update version to v1.4.177 and commit 2025-04-21 07:10:40 +00:00
Eugen Eisler
67c658f5b4 Merge pull request #1428 from ksylvan/0420-sorted-group-lists
feat: Alphabetical case-insensitive sorting for groups and items
2025-04-21 09:09:21 +02:00
github-actions[bot]
ef3bc03343 Update version to v1.4.176 and commit 2025-04-21 07:09:11 +00:00
Eugen Eisler
e31cb2b46a Merge pull request #1429 from ksylvan/0420-fix-strategies-api
feat: enhance StrategyMeta with Prompt field and dynamic naming
2025-04-21 09:07:57 +02:00
Kayvan Sylvan
6ca7142ea4 feat: enhance StrategyMeta with Prompt field and dynamic naming
### CHANGES

- Add `Prompt` field to `StrategyMeta` struct.
- Include `strings` package for filename processing.
- Derive strategy name from filename using `strings.TrimSuffix`.
- Store `Prompt` value from JSON data in `StrategyMeta`
2025-04-20 17:20:55 -07:00
Kayvan Sylvan
8b2174897a feat: add alphabetical sorting to groups and items in Print method**
### CHANGES
- Import `sort` and `strings` packages for sorting functionality.
- Create a copy of groups for stable sorting.
- Sort groups alphabetically in a case-insensitive manner.
- Create a copy of items within each group for sorting.
- Sort items alphabetically in a case-insensitive manner.
- Iterate over sorted groups and items for display.
2025-04-20 10:56:52 -07:00
Kayvan Sylvan
ac5eab0563 feat: add --listvendors command to list AI vendors
### CHANGES
- Introduce `--listvendors` flag to display all AI vendors.
- Refactor OpenAI-compatible providers into a unified configuration.
- Remove individual vendor packages for streamlined management.
- Add sorting for consistent vendor listing output.
- Update documentation to include new `--listvendors` option.
2025-04-20 08:53:20 -07:00
github-actions[bot]
65414dcc1c Update version to v1.4.175 and commit 2025-04-19 20:57:20 +00:00
Eugen Eisler
5db352f5be Merge pull request #1418 from danielmiessler/dependabot/go_modules/go_modules-bbb8b02913
chore(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 in the go_modules group across 1 directory
2025-04-19 22:56:00 +02:00
github-actions[bot]
9e7830ff77 Update version to v1.4.174 and commit 2025-04-19 06:13:44 +00:00
Eugen Eisler
5945c0e16b Merge pull request #1425 from ksylvan/0418-cerebras-ai
feat: add Cerebras AI plugin to plugin registry
2025-04-19 08:12:32 +02:00
Kayvan Sylvan
29ee141822 feat: add Cerebras AI plugin to plugin registry
### CHANGES
- Introduce Cerebras AI plugin import in plugin registry.
- Register Cerebras client in the NewPluginRegistry function.
2025-04-18 15:14:08 -07:00
github-actions[bot]
8a69621e87 Update version to v1.4.173 and commit 2025-04-18 16:23:09 +00:00
Eugen Eisler
1645b0c4ea Merge pull request #1420 from sherif-fanous/main
Fix error in deleting patterns due to non empty directory
2025-04-18 18:21:50 +02:00
Eugen Eisler
45205574d5 Merge pull request #1421 from ksylvan/0417-atom-of-thought
feat: add Atom-of-Thought (AoT) strategy and prompt definition
2025-04-18 18:20:42 +02:00
Kayvan Sylvan
71a5e0394a chore: add final newline to aot json file 2025-04-17 13:43:06 -07:00
Kayvan Sylvan
f286936c23 feat: add Atom-of-Thought (AoT) strategy and prompt definition
## CHANGES

- add new aot.json for Atom-of-Thought (AoT) prompting
- define AoT strategy description and detailed prompt instructions
- update strategies.json to include AoT in available strategies list
- ensure AoT strategy appears alongside CoD, CoT, and LTM options
2025-04-17 13:31:18 -07:00
Sherif Fanous
9000f92a55 Fix error in deleting patterns due to non empty directory 2025-04-17 12:05:52 -04:00
dependabot[bot]
8c84d4b3c8 chore(deps): bump golang.org/x/net
Bumps the go_modules group with 1 update in the / directory: [golang.org/x/net](https://github.com/golang/net).


Updates `golang.org/x/net` from 0.36.0 to 0.38.0
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 23:25:39 +00:00
github-actions[bot]
1d77afcc44 Update version to v1.4.172 and commit 2025-04-16 18:17:12 +00:00
Eugen Eisler
835bc6044b Merge pull request #1415 from ksylvan/0416-grok-ai
feat: add Grok AI provider support
2025-04-16 20:15:45 +02:00
Kayvan Sylvan
ef895a1ab9 chore: Update README with a note about Grok 2025-04-16 09:23:20 -07:00
Kayvan Sylvan
82039cedaf feat: add Grok AI provider support`
Integrate the Grok AI provider into the Fabric system for AI model interactions.

### CHANGES

*   Add Grok AI client to the plugin registry.
*   Include Grok AI API key in REST API configuration endpoints.
2025-04-16 09:15:32 -07:00
Eugen Eisler
973df61dfd Merge pull request #1411 from ksylvan/0415-readme-add-contributors
docs: add contributors section to README with contrib.rocks image
2025-04-16 11:29:58 +02:00
Kayvan Sylvan
661c85d7a6 # docs: add contributors section to README with contrib.rocks image
## CHANGES

- Add contributors section with visual representation
- Include link to project contributors page
- Add attribution to contrib.rocks tool
2025-04-15 08:29:45 -07:00
github-actions[bot]
4638f67fb7 Update version to v1.4.171 and commit 2025-04-15 08:56:37 +00:00
Eugen Eisler
ab71dbcd4f Merge pull request #1407 from sherif-fanous/main
Update Dockerfile so that Go image version matches go.mod version
2025-04-15 10:55:18 +02:00
Daniel Miessler
2abdabc100 Update README.md 2025-04-14 09:45:02 -07:00
Daniel Miessler
9f78a2c8e1 Update README.md 2025-04-14 09:44:16 -07:00
Daniel Miessler
76f78601f2 Update README.md 2025-04-14 09:43:36 -07:00
Daniel Miessler
4eaba2dc56 Update README.md 2025-04-14 09:43:06 -07:00
Daniel Miessler
2dcd9cb5f7 Update README.md 2025-04-14 09:42:19 -07:00
Daniel Miessler
2943872bde Update README.md 2025-04-14 09:41:05 -07:00
Daniel Miessler
b901542a48 Update README.md 2025-04-14 09:40:17 -07:00
Daniel Miessler
c122ff8960 Update README.md 2025-04-14 09:39:52 -07:00
Daniel Miessler
e128d818c4 Update README.md 2025-04-14 09:39:15 -07:00
Daniel Miessler
5e9d6d0a91 Update README.md 2025-04-14 09:38:51 -07:00
Daniel Miessler
70edf9cbe3 Update README.md 2025-04-14 09:36:53 -07:00
Daniel Miessler
e61a0a9391 Update README.md 2025-04-14 09:35:50 -07:00
github-actions[bot]
f8ddf98404 Update version to v1.4.170 and commit 2025-04-13 07:11:30 +00:00
Eugen Eisler
55219467f3 Merge pull request #1406 from jmd1010/chatinput-fix-clean2
Fix chat history LLM response sequence in ChatInput.svelte
2025-04-13 09:10:11 +02:00
Sherif Fanous
74d4be1ac6 Bump golang version to match go.mod 2025-04-12 21:03:53 -04:00
JM
9e57f8c6f1 Update pattern_descriptions.json 2025-04-12 19:26:32 -04:00
jmd1010
3d2903cb47 Finalize WEB UI V2 loose endsfixes 2025-04-12 17:15:14 -04:00
jmd1010
13e9d22ec6 Fix chat history LLM response sequence in ChatInput.svelte 2025-04-11 21:40:33 -04:00
github-actions[bot]
01d12c47cf Update version to v1.4.169 and commit 2025-04-11 19:13:26 +00:00
Eugen Eisler
c3258a2c3f Merge pull request #1403 from jmd1010/strategy-flag-web
Strategy flag enhancement - Web UI implementation
2025-04-11 21:12:12 +02:00
JM
746885e263 Update strategies.json 2025-04-11 12:40:27 -04:00
jmd1010
b25895c1d2 Integrate in web ui the strategy flag enhancement first developed in fabric cli 2025-04-10 18:25:09 -04:00
Daniel Miessler
e40b1c1f66 updated ed 2025-04-06 15:23:25 -07:00
Daniel Miessler
ef2ec8bffe Added excalidraw pattern. 2025-04-06 15:18:17 -07:00
Daniel Miessler
589991e6a6 Shorter version of analyze bill. 2025-04-06 13:42:03 -07:00
Daniel Miessler
965392ebbd Merge branch 'main' of github.com:danielmiessler/fabric 2025-04-06 13:33:31 -07:00
Daniel Miessler
6f615baf53 Added bill analyzer. 2025-04-06 13:33:21 -07:00
github-actions[bot]
b60bad7799 Update version to v1.4.168 and commit 2025-04-02 13:33:53 +00:00
Eugen Eisler
234d1303ad Merge pull request #1399 from HaroldFinchIFT/add-optional-simple-apikey-for-server
feat: add simple optional api key management for protect routes in --serve mode
2025-04-02 15:32:31 +02:00
Harold
cd74a96be2 refactor: refactor API key middleware based on code review feedback 2025-04-01 22:47:39 +02:00
Harold
ceaa90a7c7 fix: bad format 2025-04-01 01:26:53 +02:00
Harold
15a2eeadc9 feat: add simple optional api key management for protect routes in --serve mode 2025-04-01 01:26:53 +02:00
github-actions[bot]
8d02f5b21d Update version to v1.4.167 and commit 2025-03-31 14:42:50 +00:00
Eugen Eisler
0f8f0b6b39 Merge pull request #1397 from HaroldFinchIFT/add-italian-language-gui
feat: add it lang to the chat drop down menu lang in web gui
2025-03-31 16:41:34 +02:00
Harold
fd58b6d410 feat: add it lang to the chat drop down menu lang in web gui 2025-03-30 12:05:22 +02:00
github-actions[bot]
2579d37c16 Update version to v1.4.166 and commit 2025-03-29 20:12:49 +00:00
Eugen Eisler
4f28d85e96 Merge pull request #1392 from ksylvan/0327-fix-code-helper-arg-handling
chore: enhance argument validation in `code_helper` tool
2025-03-29 21:11:34 +01:00
Kayvan Sylvan
f529b8bb80 refactor: streamline code_helper CLI interface and require explicit instructions
## CHANGES

- Require exactly two arguments: directory and instructions
- Remove dedicated help flag, use flag.Usage instead
- Improve directory validation to check if it's a directory
- Inline pattern parsing, removing separate function
- Simplify error messages for better clarity
- Update usage text to reflect required instructions parameter
- Print usage to stderr instead of stdout
2025-03-27 19:17:54 -07:00
Eugen Eisler
71437605e1 Merge pull request #1390 from PatrickCLee/03-26-README-fix
docs: improve README link
2025-03-26 08:31:15 +01:00
PatrickCLee
01770cc6e3 docs: improve README link
- Fix broken what-and-why link reference
2025-03-26 11:34:11 +08:00
78 changed files with 1987 additions and 1274 deletions

1
.gitignore vendored
View File

@@ -58,6 +58,7 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
coverage.out
# Translations
*.mo

318
Alma.md
View File

@@ -1,318 +0,0 @@
# SPQA Policy and State for Alma Security
## Document Purpose
This document captures the SPQA policy and State for Alma Security, a security startup out of Redwood City, Ca.
This is part of the SPQA context that will be used to answer questions and create artifacts for the company, e.g., company strategy, security strategy, quarterly security reports (QSRs), project plans, recommendations on which projects to undertake, which investments to take and avoid, and other such decisions.
A major aspect of the SPQA system is the definition of the company's mission, goals, KPIs, and challenges. These shape everything within the company and thus should be used to shape the recommendations made when asked.
In addition to the clearly stated goals and other defining characteristics listed above, there will also be a streaming list of updates coming into this system using the Activity document.
Those will be changes, updates, or modifications to the direction of the company. For example, if Goal number 4 is to build a new datacenter in Boise, Idaho, but we see an update in the Activity section that says we've lost the ability to build in Boise, we should consider goal #4 out of the picture for prioritization and other decision purposes. In other words, the streaming activity log into this document should be considered updates to the core content.
## Company History
Alma Security was started by Chris Meyers, who was previously at Sigma Systems as CTO and HPE as a senior security engineer.
He started the company because, "I saw a gap in the authentication market, where companies were only looking at one or two aspects of one's identity to do authentication. They we're looking at the whole picture and turning that into a continuous authentication story."
## Company Mission
The mission of Alma Security is to ensure businesses can continuously authenticate their users using their whole selves.
## Company Goals (G1 means goal 1, G2 is goal 2, etc. Treat each item (goal/kpi/etc) as half as important as the one before it.)
NOTE: Some goals are things like project rollout which serve the higher goals. In that case they shouldn't always be considered so much lower priority because one is serving the other.
## Company Goals
- G1: Achieve 20% market share by January 2025
- G2: Hit 10000 active customers by January 2025
- G3: Hit a customer trust score of 90+% by January 2025
- G4: Get churn below 5% by August 2024
- G5: Launch in Europe by August 2024
- G6: Launch in India by November 2024
- G7: Launch Mood-monitor integration by February 2024
- G8: Launch partnership with Apple Passkeys by June 2024
## Company KPIs
- K1: Current market share percentage
- K2: Number of active customers
- K3: Current churn percentage
- K4: Launched_in_Europe (yes/no)
- K4: Launched_in_India (yes/no)
-----------------------------------------------------------------------------------------------------------------------
## Security Team Mission
- SM1: Protect Alma Security's customers and intellectual property from security and privacy incidents.
## Security Team Goals
- SG1: Secure all customer data -- especially biometric -- from security and privacy incidents.
- SG2: Protect Alma Security's intellectual property from being captured by unauthorized parties.
- SG3: Reach a time to detect malicious behavior of less than 4 minutes by January 2025
- SG4: Ensure the public trusts our product, because it's an authentication product we can't survive if people don't trust us.
- SG5: Reach a time to remediate critical vulnerabilities on crown jewel systems of less than 16 hours by August 2025
- SG6: Reach a time to remediate critical vulnerabilities on all systems of less than 3 days by August 2025
- SG5: Reach a time to remediate critical vulnerabilities on crown jewel systems of less than 16 hours by August 2025
- SG6: Reach a time to remediate critical vulnerabilities on all systems of less than 3 days by August 2025
- SG7: Complete audit of Apple Passkey integration by February 2025
- SG8: Complete remediation of Apple Passkey vulnerabilities by February 2025
## Security Team KPIs (How we measure the team)
- SK1: TTD: Time to detect malicious behavior (Minutes)
- SK1: TTI: Time to begin investigation of malicious behavior (Minutes)
- SK3: TTR-CJC: Time to remediate critical vulnerabilities on crown jewel systems (Hours)
- SK3: TTR-C: Time to remediate critical vulnerabilities on all systems (Hours)
- SK4: PT: Public trust score (Complete, Significant, Moderate, Minimal, Distrust, N/A)
## Risk Register (The things we're most worried about)
- R1: Our infrastructure security team is understaffed by 50% after 5 key people left
- R2: We are not currently monitoring our external perimeter for attack surface related vulnerabilities like open ports, listening applications, unknown hosts, unknown subdomains pointing to these things, etc. We only do scans once every couple of months and we don't really have anyone to look at the results
- R3: It takes us multiple days to investigate potential malicious behavior on our systems.
- R4: We lack a full list of our assets, including externally facing hosts, S3 buckets, etc., which make up our attack surface
- R5: We have a low public trust score due to the events of 2022.
## Security Team Narrative
### Background
Alma hired a new security team starting in January of 2023 and we have been building out the program since then. The philosophy and approach for the security team is to explicitly articulate what we believe the highest risks are to Alma, to deploy targeted strategies to address those risks, and to use clear, transparent KPIs to show progress towards our goals over time.
### Current Risks
So our risk register looks like this:
1. We are understaffed by 50% after 5 key people left in 2022
2. Our perimeter is not being monitored for attack surface related vulnerabilities
3. It takes us too long to detect and start investigating malicious behavior on our systems
4. We do not have a full list of our assets, which makes it difficult to know what we need to protect
5. We have a low public trust score due to the events of 2022
### Strategies
As such, our strategies are as follows:
1. Hire 5 more A-tier security professionals
2. Purchase and implement an attack surface management solution
3. Invest in our detection and response capabilities
4. Purchase an asset inventory system that integrates with our attack surface management tool
5. Leverage PR to share as much of our progress as possible with the public to rebuild trust
### How We're Doing
We believe being transparent about our progress is key to everything, and for that reason we maintain a limited number of KPIs that we update every quarter. These metrics will not change often. They will remain consistent so that it's easy to track how we're spending our resources and the progress we're making.
Those KPIs are:
1. Time to detect malicious behavior
2. Time to start investigating malicious behavior
3. Time to remediate critical vulnerabilities on crown jewel systems
4. Time to remediate critical vulnerabilities on all systems
5. Our public trust score
As of $DATE$, our KPIs for these are currently:
$GIVE CURRENT KPIs from the Activity section below$
$INSERT GRAPHS OF KPI PROGRESS OVER TIME HERE$
## Security Team Strategies
- STS1: Hire 5 more A-tier security professionals
- STS2: Purchase an attack surface management solution
- STS3: Invest in our detection and response capabilities
- STS4: Purchase an asset inventory system that integrates with our attack surface management tool
## Infrastructure Notes (a basic description of our tech stack and various context around it)
- We currently have no WAF protecting our main web app but we're considering adding one
- We have had some issues with S3 buckets becoming public, or being set up as public, which has lead to some close calls with customer data almost being exposed.
- alma.amazon-domain.com is our primary S3 bucket that contains everything, but it's not public readable or listable
- We have a root account for our AWS account that doesn't yet have 2FA on it, but we're working on fixing that within a few weeks (but it's been open for a few months)
- We also use Postgres for all our databases.
- Developers have root access to the all kubernetes nodes via SSH on port 45,001 using a shared developer key issued during laptop provisioning.
- We're a kubernetes shop and do everything through AWS
- We're logging most stuff to Cloudtrail and we kind of use Amazon Guarduty, but we don't have a 24/7 team to monitor alerts and logs. We should add that to our list of challenges next time we update our overall policy
- We also have a Windows infrastructure because some key personnel came from Microsoft. The DC is hosted in our head office which is in Redwood City, and anyone who works in that office (most of the 300 employees) uses that to log in when they start work. The domain is ALMA.
- There's a domain-joined fileserver running Windows 2012 that most people use to upload new ideas and plans for new products. It uses Windows authentication from the domain.
- We use a Palo Alto Networks firewall with 2fa using windows authenticator tied to SSO.
- The name of the AI system doing all this context creation using SPQA is Alma, which is also the name of the company.
- We use Workday for HR stuff. Slack for realtime communications. Outlook 365 as a service. Sentinel One on the workstations and laptops. Servers in AWS are mostly Amazon Linux 2 with a few Ubuntu boxes that are a few years old.
- We also primarily use Postgres for all of our systems.
## Team
TEAM MEMBER | TEAM ASSIGNED | SKILLS | PAY LEVEL | LOCATION | PROJECTS
Nadia Khan | Detection and Response | D&R (Expert), AWS (Strong), Python (Expert), Kubernetes (Basic), Postgres (Basic) | $249K | Redwood City
Chris Magann | Vulnerability Management | VM (Expert), AWS (Strong), Python (Basic), Postgres (Basic) | $212K | Redwood City
Tigan Wang | Vulnerability Management | VM (Expert), AWS (Strong), Python (Basic), Postgres (Basic) | $217K | Redwood City
## Projects
PROJECT NAME | PROJECT DESCRIPTION | PROJECT PRIORITY | PROJECT MEMBERS | START DATE | END DATE | STATUS | PROJECT COST
WAF Install | Install a WAF in front of our main web app | Critical | Nadia Khan | 2024-01-01 - Ongoing | In Progress | $112K one-time, $9K/month
Multi-Factor Authentication (MFA) Rollout | Implement MFA across all internal and external systems | Critical | Chris Magann | 2024-01-15 | 2024-05-01 | Planned | $80K one-time, $5K/month
Procure and Implement ASM | Implement continuous monitoring for attack surface vulnerabilities | High | Tigan Wang | 2024-02-15 | 2024-06-15 | Not Started | $75K one-time, $6K/month
Data Encryption Upgrade | Upgrade encryption protocols for all sensitive data | Medium | Nadia Khan | 2024-04-01 | 2024-08-01 | Planned | $95K one-time
Incident Response Enhancement | Develop and implement a 24/7 incident response team | High | Nadia Khan | 2024-03-01 | 2024-07-01 | In Progress | $150K one-time, $10K/month
Cloud Security Optimization | Optimize AWS cloud security configurations and practices | Medium | Tigan Wang | 2024-02-01 | 2024-06-01 | In Progress | $100K one-time, $8K/month
S3 Bucket Security | Review and secure all S3 buckets to prevent data breaches | High | Chris Magann | 2024-01-10 | 2024-04-10 | In Progress | $70K one-time, $5K/month
SQL Injection Mitigation | Implement measures to eliminate SQL injection vulnerabilities | High | Tigan Wang | 2024-01-20 | 2024-05-20 | Not Started | $60K one-time
## SECURITY POSTURE (To be referenced for compliance questions and security questionnaires)
July 2019
Admin accounts still not required to use 2FA.
Company laptops distributed to employees, no MDM yet for device management.
AWS IAM roles created for engineers, but root access still frequently used.
Started basic vulnerability scanning using open-source tools.
December 2019
MFA enforced for all Google Workspace accounts after a phishing attempt.
Introduced ClamAV for basic endpoint protection on corporate laptops.
AWS GuardDuty enabled for threat detection, but no formal incident response team.
First incident response plan table-top exercise conducted, but findings not fully documented.
April 2020
Migrated from Google Workspace to Office 365, with MFA enabled for all users.
Rolled out SentinelOne for endpoint protection on 50% of company laptops.
Implemented least-privilege access control for AWS IAM roles.
First formal vendor risk management review completed for major SaaS providers.
August 2020
Completed full deployment of SentinelOne across all endpoints.
Implemented AWS CloudWatch for real-time alerts; however, logs still not monitored 24/7.
Began encrypting all AWS S3 buckets at rest using server-side encryption.
First internal review of data retention policies, started drafting data disposal policy.
January 2021
Rolled out Jamf MDM for centralized management of macOS devices, enforcing encryption (FileVault) on all laptops.
Strengthened Office 365 security by implementing phishing-resistant MFA using authenticator apps.
AWS KMS introduced for managing encryption keys; manual key rotation policy documented.
Introduced formal onboarding and offboarding processes for employee account management.
July 2021
Conditional access policies introduced for Office 365, restricting access based on geography (US-only).
Conducted company-wide security awareness training for the first time, focusing on phishing threats.
Completed first backup and disaster recovery (DR) drill with AWS, documenting recovery times.
AWS Config deployed to monitor and enforce encryption and access control policies across accounts.
December 2021
Full migration to AWS for all production systems completed.
Incident response playbook finalized and shared with the security team; still no 24/7 monitoring.
Documented data classification policies for handling sensitive customer data in preparation for SOC 2 audit.
First third-party penetration test conducted, critical vulnerabilities identified and remediated within 30 days.
March 2022
Rolled out company-wide 2FA for all critical systems, including Office 365, AWS, GitHub, and Slack.
Introduced AWS Secrets Manager for managing sensitive credentials, eliminating hardcoded API keys.
Updated all documentation for identity and access management in preparation for SOC 2 Type 1 audit.
First external vulnerability scan completed using Qualys, with remediation SLAs established.
April 2022
Updated and consolidated all security policies (incident response, access control, data retention) in preparation for SOC 2 audit.
Conducted tabletop exercise for ransomware response, documenting gaps in the incident response process.
Implemented Just-In-Time (JIT) access for administrative privileges in AWS, reducing unnecessary persistent access.
October 2022
Passed SOC 2 Type 1 audit, with recommendations to improve monitoring and asset management.
Launched quarterly phishing simulations to raise employee awareness and track training effectiveness.
Fully enforced encryption for all customer data in transit and at rest using AWS KMS.
Extended GuardDuty to cover all AWS regions; started monitoring alerts daily.
January 2023
Hired a dedicated CISO and expanded security team by 30%.
Integrated continuous vulnerability scanning across all externally facing assets using Qualys.
Conducted first third-party vendor risk assessment to ensure alignment with SOC 2 and internal security standards.
Implemented automated patch management for all AWS EC2 instances, reducing time to deploy critical patches.
July 2023
Rolled out continuous attack surface monitoring (ASM) to identify and remediate external vulnerabilities.
Performed annual data retention review, ensuring compliance with SOC 2 and GDPR requirements.
Conducted a disaster recovery drill for AWS workloads, achieving a recovery time objective (RTO) of under 4 hours.
Completed SOC 2 Type 2 readiness assessment, with focus on improving incident response times.
November 2023
Updated incident response documentation and assigned 24/7 monitoring to a third-party SOC provider.
Rolled out zero-trust network architecture across the organization, removing reliance on VPN for remote access.
Passed SOC 2 Type 2 audit with no major findings; recommendations included improved asset inventory tracking.
Conducted full audit of access control policies and JIT access implementation in preparation for ISO 27001 certification.
April 2024
Implemented AI-driven threat detection to reduce time to detect security incidents from 10 hours to under 2 hours.
Completed full encryption audit across all databases, ensuring compliance with GDPR, HIPAA, and other privacy regulations.
Updated employee training programs to include privacy regulations (GDPR, CCPA) and data handling best practices.
Completed internal review and audit of vendor access to critical systems as part of SOC 2 compliance effort.
Completed move of all AWS services to us-west-2 and us-east-1 regions for 100% us-based cloud services.
October 2024
Conducted organization-wide review of data retention and disposal policies, implementing automated data deletion for expired data.
Implemented continuous compliance monitoring for SOC 2, with automated alerts for deviations in access controls and encryption settings.
Finalized implementation of AI-based monitoring and response systems, significantly reducing time to remediate critical vulnerabilities.
Passed SOC 2 Type 2 and ISO 27001 audits with zero non-conformities, achieving full compliance across all control areas.March 2018
Personal Gmail accounts used for internal and external communication.
No 2FA enabled on any accounts.
AWS accounts shared with engineers, no IAM roles or formal access control policies.
No centralized endpoint protection; employees use personal laptops with no security controls.
No documented security policies or incident response plan.
September 2018
Initiated migration from personal Gmail to Google Workspace (G Suite) for business email.
Password complexity requirements introduced (minimum 8 characters).
AWS root credentials still shared among team members, no MFA enabled.
No formal logging or monitoring in place for AWS activity.
February 2019
Completed migration to Google Workspace; no email encryption yet.
Introduced a basic password manager (LastPass) but no enforcement policy.
AWS CloudTrail enabled for logging, but no one is reviewing logs.
First draft of the incident response plan created, but not tested.
June 2019
Enforced MFA for Google Workspace admin accounts; standard user
## CURRENT STATE (KPIs, Metrics, Project Activity Updates, etc.)
- October 2022: Current time to detect malicious behavior is 81 hours
- October 2022: Current time to start investigating malicious behavior is 82 hours
- October 2022: Current time to remediate critical vulnerabilities on crown jewel systems is 21 days
- October 2022: Current time to remediate critical vulnerabilities on all systems is 51 days
- January 2023: Current time to detect malicious behavior is 62 hours
- January 2023: Current time to start investigating malicious behavior is 72 hours
- January 2023: Current time to remediate critical vulnerabilities on crown jewel systems is 17 days
- January 2023: Current time to remediate critical vulnerabilities on all systems is 43 days
- July 2023: Current time to detect malicious behavior is 29 hours
- July 2023: Current time to start investigating malicious behavior is 41 hours
- July 2023: Current time to remediate critical vulnerabilities on crown jewel systems is 12 days
- July 2023: Current time to remediate critical vulnerabilities on all systems is 29 days
- November 2023: Current time to start detect malicious behavior is 12 hours
- November 2023: Current time to start investigating malicious behavior is 16 hours
- November 2023: Current time to remediate critical vulnerabilities on crown jewel systems is 9 days
- November 2023: Current time to remediate critical vulnerabilities on all systems is 17 days
- February 2024: Started attack surface management vendor selection process
- January 2024: Current time to start detect malicious behavior is 9 hours
- January 2024: Current time to start investigating malicious behavior is 14 hours
- January 2024: Current time to remediate critical vulnerabilities on crown jewel systems is 8 days
- January 2024: Current time to remediate critical vulnerabilities on all systems is 12 days
- March 2024: We're now remediating critical vulnerabilities on crown jewels in less than 6 days
- April 2024: We're now remediating all critical vulnerabilities within 11 days
- July 2024: critical vulnerabilities are now being fixed in 9 days
- On August 5 we got remediation of critical vulnerabilities down to 7 days

View File

@@ -1,5 +1,5 @@
# Use official golang image as builder
FROM golang:1.23.3-alpine AS builder
FROM golang:1.24.2-alpine AS builder
# Set working directory
WORKDIR /app

View File

@@ -2,7 +2,7 @@
- The goal is to bring more encapsulation of the models management and simplified configuration management to bring increased flexibility, transparency on the overall flow, and simplicity in adding new model.
- We need to differentiate:
- Vendors: the producer of models (like OpenAI, Azure, Anthropric, Ollama, ..etc) and their associated APIs
- Vendors: the producer of models (like OpenAI, Azure, Anthropic, Ollama, ..etc) and their associated APIs
- Models: the LLM models these vendors are making public
- Each vendor and operations allowed by the vendor needs to be encapsulated. This includes:
- The questions needed to setup the model (like the API key, or the URL)

View File

@@ -53,7 +53,7 @@ Pattern descriptions and tags are managed in pattern_descriptions.json:
3. How to update Pattern short descriptions (one sentence).
You can update your descriptions in pattern_descriptions.json manually or using LLM assistance (prefered approach).
You can update your descriptions in pattern_descriptions.json manually or using LLM assistance (preferred approach).
Tell AI to look for "Description pending" entries in this file and write a short description based on the extract info in the pattern_extracts.json file. You can also ask your LLM to add tags for those newly added patterns, using other patterns tag assignments as example.

View File

@@ -1693,8 +1693,87 @@
},
{
"patternName": "extract_wisdom_short",
"description": "Extract condensed insightful ideas and recommendations focusing on life wisdom.",
"tags": [
"EXTRACT",
"WISDOM",
"SELF"
]
},
{
"patternName": "analyze_bill",
"description": "Analyze a legislative bill and implications.",
"tags": [
"ANALYSIS",
"BILL"
]
},
{
"patternName": "analyze_bill_short",
"description": "Consended - Analyze a legislative bill and implications.",
"tags": [
"ANALYSIS",
"BILL"
]
},
{
"patternName": "create_coding_feature",
"description": "[Description pending]",
"tags": []
"tags": [
"DEVELOPMENT"
]
},
{
"patternName": "create_excalidraw_visualization",
"description": "Create visualizations using Excalidraw.",
"tags": [
"VISUALIZATION"
]
},
{
"patternName": "create_flash_cards",
"description": "Generate flashcards for key concepts and definitions.",
"tags": [
"LEARNING"
]
},
{
"patternName": "create_loe_document",
"description": "Create detailed Level of Effort (LOE) estimation documents.",
"tags": [
"DEVELOPMENT",
"BUSINESS"
]
},
{
"patternName": "extract_domains",
"description": "Extract key content and source.",
"tags": [
"EXTRACT",
"ANALYSIS"
]
},
{
"patternName": "extract_main_activities",
"description": "Extract and list main events from transcripts.",
"tags": [
"EXTRACT",
"ANALYSIS"
]
},
{
"patternName": "find_female_life_partner",
"description": "Clarify and summarize partner criteria in direct language.",
"tags": [
"SELF"
]
},
{
"patternName": "youtube_summary",
"description": "Summarize YouTube videos with key points and timestamps.",
"tags": [
"SUMMARIZE"
]
}
]
}
}

View File

@@ -823,6 +823,46 @@
{
"patternName": "extract_wisdom_short",
"pattern_extract": "# IDENTITY and PURPOSE You extract surprising, insightful, and interesting information from text content. You are interested in insights related to the purpose and meaning of life, human flourishing, the role of technology in the future of humanity, artificial intelligence and its affect on humans, memes, learning, reading, books, continuous improvement, and similar topics. Take a step back and think step-by-step about how to achieve the best possible results by following the steps below. # STEPS - Extract a summary of the content in 50 words, including who is presenting and the content being discussed into a section called SUMMARY. - Extract 10 to 20 of the most surprising, insightful, and/or interesting ideas from the input in a section called IDEAS:. If there are less than 50 then collect all of them. Make sure you extract at least 20. - Extract 5 to 10 of the best insights from the input and from a combination of the raw input and the IDEAS above into a section called INSIGHTS. These INSIGHTS should be fewer, more refined, more insightful, and more abstracted versions of the best ideas in the content. - Extract 10 TO 15 of the most surprising, insightful, and/or interesting quotes from the input into a section called QUOTES:. Use the exact quote text from the input. - Extract 5 to 10 of the most practical and useful personal habits of the speakers, or mentioned by the speakers, in the content into a section called HABITS. Examples include but aren't limited to: sleep schedule, reading habits, things they always do, things they always avoid, productivity tips, diet, exercise, etc. - Extract 5 to 10 of the most surprising, insightful, and/or interesting valid facts about the greater world that were mentioned in the content into a section called FACTS:. - Extract all mentions of writing, art, tools, projects and other sources of inspiration mentioned by the speakers into a section called REFERENCES. This should include any and all references to something that the speaker mentioned. - Extract the most potent takeaway and recommendation into a section called ONE-SENTENCE TAKEAWAY. This should be a 15-word sentence that captures the most important essence of the content. - Extract the 5 to 10 of the most surprising, insightful, and/or interesting recommendations that can be collected from the content into a section called RECOMMENDATIONS. # OUTPUT INSTRUCTIONS - Only output Markdown. - Write the IDEAS bullets as exactly 16 words. - Write the RECOMMENDATIONS bullets as exactly 16 words. - Write the HABITS bullets as exactly 16 words. - Write the FACTS bullets as exactly 16 words. - Write the INSIGHTS bullets as exactly 16 words. - Extract at least 25 IDEAS from the content. - Extract at least 5 INSIGHTS from the content. - Extract at least 10 items for the other output sections. - Do not give warnings or notes; only output the requested sections. - You use bulleted lists for output, not numbered lists. - Do not repeat ideas, quotes, facts, or"
},
{
"patternName": "analyze_bill",
"pattern_extract": "# IDENTITY You are an AI with a 3,129 IQ that specializes in discerning the true nature and goals of a piece of legislation. It captures all the overt things, but also the covert ones as well, and points out gotchas as part of it's summary of the bill. # STEPS 1. Read the entire bill 37 times using different perspectives. 2. Map out all the stuff it's trying to do on a 10 KM by 10K mental whiteboard. 3. Notice all the overt things it's trying to do, that it doesn't mind being seen. 4. Pay special attention to things its trying to hide in subtext or deep in the document. # OUTPUT 1. Give the metadata for the bill, such as who proposed it, when, etc. 2. Create a 24-word summary of the bill and what it's trying to accomplish. 3. Create a section called OVERT GOALS, and list 5-10 16-word bullets for those. 4. Create a section called COVERT GOALS, and list 5-10 16-word bullets for those. 5. Create a conclusion sentence that gives opinionated judgement on whether the bill is mostly overt or mostly dirty with ulterior motives."
},
{
"patternName": "analyze_bill_short",
"pattern_extract": "# IDENTITY You are an AI with a 3,129 IQ that specializes in discerning the true nature and goals of a piece of legislation. It captures all the overt things, but also the covert ones as well, and points out gotchas as part of it's summary of the bill. # STEPS 1. Read the entire bill 37 times using different perspectives. 2. Map out all the stuff it's trying to do on a 10 KM by 10K mental whiteboard. 3. Notice all the overt things it's trying to do, that it doesn't mind being seen. 4. Pay special attention to things its trying to hide in subtext or deep in the document. # OUTPUT 1. Give the metadata for the bill, such as who proposed it, when, etc. 2. Create a 16-word summary of the bill and what it's trying to accomplish. 3. Create a section called OVERT GOALS, and list the main overt goal in 8 words and 2 supporting goals in 8-word sentences. 3. Create a section called COVERT GOALS, and list the main covert goal in 8 words and 2 supporting goals in 8-word sentences. 5. Create an 16-word conclusion sentence that gives opinionated judgement on whether the bill is mostly overt or mostly dirty with ulterior motives."
},
{
"patternName": "create_coding_feature",
"pattern_extract": "# IDENTITY and PURPOSE You are an elite programmer. You take project ideas in and output secure and composable code using the format below. You always use the latest technology and best practices. Take a deep breath and think step by step about how to best accomplish this goal using the following steps. Input is a JSON file with the following format: Example input: ```json [ { \"type\": \"directory\", \"name\": \".\", \"contents\": [ { \"type\": \"file\", \"name\": \"README.md\", \"content\": \"This is the README.md file content\" }, { \"type\": \"file\", \"name\": \"system.md\", \"content\": \"This is the system.md file contents\" } ] }, { \"type\": \"report\", \"directories\": 1, \"files\": 5 }, { \"type\": \"instructions\", \"name\": \"code_change_instructions\", \"details\": \"Update README and refactor main.py\" } ] ``` The object with `\"type\": \"instructions\"`, and field `\"details\"` contains the for the instructions for the suggested code changes. The `\"name\"` field is always `\"code_change_instructions\"` The `\"details\"` field above, with type `\"instructions\"` contains the instructions for the suggested code changes. ## File Management Interface Instructions You have access to a powerful file management system with the following capabilities: ### File Creation and Modification - Use the **EXACT** JSON format below to define files that you want to be changed - If the file listed does not exist, it will be created - If a directory listed does not exist, it will be created - If the file already exists, it will be overwritten - It is **not possible** to delete files ```plaintext __CREATE_CODING_FEATURE_FILE_CHANGES__ [ { \"operation\": \"create\", \"path\": \"README.md\", \"content\": \"This is the new README.md file content\" }, { \"operation\": \"update\", \"path\": \"src/main.c\", \"content\": \"int main(){return 0;}\" } ] ``` ### Important Guidelines - Always use relative paths from the project root - Provide complete, functional code when creating or modifying files - Be precise and concise in your file operations - Never create files outside of the project root ### Constraints - Do not attempt to read or modify files outside the project root directory. - Ensure code follows best practices and is production-ready. - Handle potential errors gracefully in your code suggestions. - Do not trust external input to applications, assume users are malicious. ### Workflow 1. Analyze the user's request 2. Determine necessary file operations 3. Provide clear, executable file creation/modification instructions 4. Explain the purpose and functionality of proposed changes ## Output Sections - Output a summary of the file changes - Output directory and file changes according to File Management Interface Instructions, in a json array marked by `__CREATE_CODING_FEATURE_FILE_CHANGES__` - Be exact in the `__CREATE_CODING_FEATURE_FILE_CHANGES__` section, and do not deviate from the proposed JSON format. - **never** omit the `__CREATE_CODING_FEATURE_FILE_CHANGES__` section. - If the proposed changes change how the project is built and installed, document these changes in the projects README.md - Implement build configurations changes if needed, prefer ninja if nothing already exists in the project, or is otherwise specified. - Document new dependencies according to best practices for the language used in the project. - Do not output sections that were"
},
{
"patternName": "create_excalidraw_visualization",
"pattern_extract": "# IDENTITY You are an expert AI with a 1,222 IQ that deeply understands the relationships between complex ideas and concepts. You are also an expert in the Excalidraw tool and schema. You specialize in mapping input concepts into Excalidraw diagram syntax so that humans can visualize the relationships between them. # STEPS 1. Deeply study the input. 2. Think for 47 minutes about each of the sections in the input. 3. Spend 19 minutes thinking about each and every item in the various sections, and specifically how each one relates to all the others. E.g., how a project relates to a strategy, and which strategies are addressing which challenges, and which challenges are obstructing which goals, etc. 4. Build out this full mapping in on a 9KM x 9KM whiteboard in your mind. 5. Analyze and improve this mapping for 13 minutes. # KNOWLEDGE Here is the official schema documentation for creating Excalidraw diagrams. Skip to main content Excalidraw Logo Excalidraw Docs Blog GitHub Introduction Codebase JSON Schema Frames @excalidraw/excalidraw Installation Integration Customizing Styles API FAQ Development @excalidraw/mermaid-to-excalidraw CodebaseJSON Schema JSON Schema The Excalidraw data format uses plaintext JSON. Excalidraw files When saving an Excalidraw scene locally to a file, the JSON file (.excalidraw) is using the below format. Attributes Attribute Description Value type The type of the Excalidraw schema \"excalidraw\" version The version of the Excalidraw schema number source The source URL of the Excalidraw application \"https://excalidraw.com\" elements An array of objects representing excalidraw elements on canvas Array containing excalidraw element objects appState Additional application state/configuration Object containing application state properties files Data for excalidraw image elements Object containing image data JSON Schema example { // schema information \"type\": \"excalidraw\", \"version\": 2, \"source\": \"https://excalidraw.com\", // elements on canvas \"elements\": [ // example element { \"id\": \"pologsyG-tAraPgiN9xP9b\", \"type\": \"rectangle\", \"x\": 928, \"y\": 319, \"width\": 134, \"height\": 90 /* ...other element properties */ } /* other elements */ ], // editor state (canvas config, preferences, ...) \"appState\": { \"gridSize\": 20, \"viewBackgroundColor\": \"#ffffff\" }, // files data for \"image\" elements, using format `{ [fileId]: fileData }` \"files\": { // example of an image data object \"3cebd7720911620a3938ce77243696149da03861\": { \"mimeType\": \"image/png\", \"id\": \"3cebd7720911620a3938c.77243626149da03861\", \"dataURL\": \"data:image/png;base64,iVBORWOKGgoAAAANSUhEUgA=\", \"created\": 1690295874454, \"lastRetrieved\": 1690295874454 } /* ...other image data objects */ } } Excalidraw clipboard format When copying selected excalidraw elements to clipboard, the JSON schema is similar to .excalidraw format, except it differs in attributes. Attributes Attribute Description Example Value type The type of the Excalidraw document. \"excalidraw/clipboard\" elements An array of objects representing excalidraw elements on canvas. Array containing excalidraw element objects (see example below) files Data for excalidraw image elements. Object containing image data Edit this page Previous Contributing Next Frames Excalidraw files Attributes JSON Schema example Excalidraw clipboard format Attributes Docs Get Started Community Discord Twitter Linkedin More Blog GitHub Copyright © 2023 Excalidraw community. Built with Docusaurus ❤️ # OUTPUT 1. Output the perfect excalidraw schema file that can be directly importted in to Excalidraw. This should have no preamble or follow-on text"
},
{
"patternName": "create_flash_cards",
"pattern_extract": "# IDENTITY You are an expert educator AI with a 4,221 IQ. You specialize in understanding the key concepts in a piece of input and creating flashcards for those key concepts. # STEPS - Fully read and comprehend the input and map out all the concepts on a 4KM x 4KM virtual whiteboard. - Make a list of the key concepts, definitions, terms, etc. that are associated with the input. - Create flashcards for each key concept, definition, term, etc. that you have identified. - The flashcard should be a question of 8-16 words and an answer of up to 32 words. # OUTPUT - Output the flashcards in Markdown format using no special characters like italics or bold (asterisks)."
},
{
"patternName": "create_loe_document",
"pattern_extract": "# Identity and Purpose You are an expert in software, cloud, and cybersecurity architecture. You specialize in creating clear, well-structured Level of Effort (LOE) documents for estimating work effort, resources, and costs associated with a given task or project. # Goal Given a description of a task or system, provide a detailed Level of Effort (LOE) document covering scope, business impact, resource requirements, estimated effort, risks, dependencies, and assumptions. # Steps 1. Analyze the input task thoroughly to ensure full comprehension. 2. Map out all key components of the task, considering requirements, dependencies, risks, and effort estimation factors. 3. Consider business priorities and risk appetite based on the nature of the organization. 4. Break the LOE document into structured sections for clarity and completeness. --- # Level of Effort (LOE) Document Structure ## Section 1: Task Overview - Provide a high-level summary of the task, project, or initiative being estimated. - Define objectives and expected outcomes. - Identify key stakeholders and beneficiaries. ## Section 2: Business Impact - Define the business problem this task is addressing. - List the expected benefits and value to the organization. - Highlight any business risks or regulatory considerations. ## Section 3: Scope & Deliverables - Outline in-scope and out-of-scope work. - Break down major deliverables and milestones. - Specify acceptance criteria for successful completion. ## Section 4: Resource Requirements - Identify required skill sets and roles (e.g., software engineers, security analysts, cloud architects, scrum master , project manager). - Estimate the number of personnel needed , in tabular format. - List tooling, infrastructure, or licenses required. ## Section 5: Estimated Effort - Break down tasks into granular units (e.g., design, development, testing, deployment). - Provide time estimates per task in hours, days, or sprints, in tabular format. - Aggregate total effort for the entire task or project. - Include buffer time for unforeseen issues or delays. - Use T-shirt sizing (S/M/L/XL) or effort points to classify work complexity. ## Section 6: Dependencies - List external dependencies (e.g., APIs, third-party vendors, internal teams). - Specify hardware/software requirements that may impact effort. ## Section 7: Risks & Mitigations - Identify technical, security, or operational risks that could affect effort. - Propose mitigation strategies to address risks. - Indicate if risks could lead to effort overruns. ## Section 8: Assumptions & Constraints - List key assumptions that influence effort estimates. - Identify any constraints such as budget, team availability, or deadlines. ## Section 9: Questions & Open Items - List outstanding questions or clarifications required to refine the LOE. - Highlight areas needing further input from stakeholders. --- # Output Instructions - Output the LOE document in valid Markdown format. - Do not use bold or italic formatting. - Do not provide commentary or disclaimers, just execute the request. # Input Input: [Provide the specific task or project for estimation here]"
},
{
"patternName": "extract_domains",
"pattern_extract": "# IDENTITY and PURPOSE You extract domains and URLs from input like articles and newsletters for the purpose of understanding the sources that were used for their content. # STEPS - For every story that was mentioned in the article, story, blog, newsletter, output the source it came from. - The source should be the central source, not the exact URL necessarily, since the purpose is to find new sources to follow. - As such, if it's a person, link their profile that was in the input. If it's a Github project, link the person or company's Github, If it's a company blog, output link the base blog URL. If it's a paper, link the publication site. Etc. - Only output each source once. - Only output the source, nothing else, one per line # INPUT INPUT:"
},
{
"patternName": "extract_main_activities",
"pattern_extract": "# IDENTITY You are an expert activity extracting AI with a 24,221 IQ. You specialize in taking any transcript and extracting the key events that happened. # STEPS - Fully understand the input transcript or log. - Extract the key events and map them on a 24KM x 24KM virtual whiteboard. - See if there is any shared context between the events and try to link them together if possible. # OUTPUT - Write a 16 word summary sentence of the activity. - Create a list of the main events that happened, such as watching media, conversations, playing games, watching a TV show, etc. # OUTPUT INSTRUCTIONS - Output only in Markdown with no italics or bolding."
},
{
"patternName": "find_female_life_partner",
"pattern_extract": "# IDENTITY AND PURPOSE You are a relationship and marriage and life happiness expert AI with a 4,227 IQ. You take criteria given to you about what a man is looking for in a woman life partner, and you turn that into a perfect sentence. # PROBLEM People aren't clear about what they're actually looking for, so they're too indirect and abstract and unfocused in how they describe it. They actually don't know what they want, so this analysis will tell them what they're not seeing for themselves that they need to acknowledge. # STEPS - Analyze all the content given to you about what they think they're looking for. - Figure out what they're skirting around and not saying directly. - Figure out the best way to say that in a clear, direct, sentence that answers the question: \"What would I tell people I'm looking for if I knew what I wanted and wasn't afraid.\" - Write the perfect 24-word sentence in these versions: 1. DIRECT: The no bullshit, revealing version that shows the person what they're actually looking for. Only 8 words in extremely straightforward language. 2. CLEAR: A revealing version that shows the person what they're really looking for. 3. POETIC: An equally accurate version that says the same thing in a slightly more poetic and storytelling way. # OUTPUT INSTRUCTIONS - Only output those two sentences, nothing else."
},
{
"patternName": "youtube_summary",
"pattern_extract": "# IDENTITY and PURPOSE You are an AI assistant specialized in creating concise, informative summaries of YouTube video content based on transcripts. Your role is to analyze video transcripts, identify key points, main themes, and significant moments, then organize this information into a well-structured summary that includes relevant timestamps. You excel at distilling lengthy content into digestible summaries while preserving the most valuable information and maintaining the original flow of the video. Take a step back and think step-by-step about how to achieve the best possible results by following the steps below. ## STEPS - Carefully read through the entire transcript to understand the overall content and structure of the video - Identify the main topic and purpose of the video - Note key points, important concepts, and significant moments throughout the transcript - Pay attention to natural transitions or segment changes in the video - Extract relevant timestamps for important moments or topic changes - Organize information into a logical structure that follows the video's progression - Create a concise summary that captures the essence of the video - Include timestamps alongside key points to allow easy navigation - Ensure the summary is comprehensive yet concise ## OUTPUT INSTRUCTIONS - Only output Markdown - Begin with a brief overview of the video's main topic and purpose - Structure the summary with clear headings and subheadings that reflect the video's organization - Include timestamps in [HH:MM:SS] format before each key point or section - Keep the summary concise but comprehensive, focusing on the most valuable information - Use bullet points for lists of related points when appropriate - Bold or italicize particularly important concepts or takeaways - End with a brief conclusion summarizing the video's main message or call to action - Ensure you follow ALL these instructions when creating your output. ## INPUT INPUT:"
}
]
}

View File

@@ -1,4 +1,7 @@
<div align="center">
Fabric is graciously supported by…
[![Github Repo Tagline](https://github.com/user-attachments/assets/96ab3d81-9b13-4df4-ba09-75dee7a5c3d2)](https://warp.dev/fabric)
<img src="./images/fabric-logo-gif.gif" alt="fabriclogo" width="400" height="400"/>
@@ -15,10 +18,10 @@
</p>
[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) •
@@ -56,6 +59,10 @@
- [Save your files in markdown using aliases](#save-your-files-in-markdown-using-aliases)
- [Migration](#migration)
- [Upgrading](#upgrading)
- [Shell Completions](#shell-completions)
- [Zsh Completion](#zsh-completion)
- [Bash Completion](#bash-completion)
- [Fish Completion](#fish-completion)
- [Usage](#usage)
- [Our approach to prompting](#our-approach-to-prompting)
- [Examples](#examples)
@@ -73,15 +80,16 @@
- [Clipboard Support](#clipboard-support)
- [Meta](#meta)
- [Primary contributors](#primary-contributors)
- [Contributors](#contributors)
<br />
## Updates
> [!NOTE]
> February 24, 2025
> May 22, 2025
>
> - Fabric now supports Sonnet 3.7! Update and use `-S` to select it as your default if you want, or just use the shortcut `-m claude-3-7-sonnet-latest`. Enjoy!
> - Fabric now supports Anthropic's Claude 4. Read the [blog post from Anthropic](https://www.anthropic.com/news/claude-4).
## What and why
@@ -407,6 +415,48 @@ The great thing about Go is that it's super easy to upgrade. Just run the same c
go install github.com/danielmiessler/fabric@latest
```
### Shell Completions
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.
#### Zsh Completion
To enable Zsh completion:
```bash
# Copy the completion file to a directory in your $fpath
mkdir -p ~/.zsh/completions
cp completions/_fabric ~/.zsh/completions/
# Add the directory to fpath in your .zshrc before compinit
echo 'fpath=(~/.zsh/completions $fpath)' >> ~/.zshrc
echo 'autoload -Uz compinit && compinit' >> ~/.zshrc
```
#### Bash Completion
To enable Bash completion:
```bash
# Source the completion script in your .bashrc
echo 'source /path/to/fabric/completions/fabric.bash' >> ~/.bashrc
# Or copy to the system-wide bash completion directory
sudo cp completions/fabric.bash /etc/bash_completion.d/
```
#### Fish Completion
To enable Fish completion:
```bash
# Copy the completion file to the fish completions directory
mkdir -p ~/.config/fish/completions
cp completions/fabric.fish ~/.config/fish/completions/
```
## Usage
Once you have it all set up, here's how to use it.
@@ -415,7 +465,7 @@ Once you have it all set up, here's how to use it.
fabric -h
```
```bash
```plaintext
Usage:
fabric [OPTIONS]
@@ -465,6 +515,7 @@ Application Options:
--serve Serve the Fabric Rest API
--serveOllama Serve the Fabric Rest API with ollama endpoints
--address= The address to bind the REST API (default: :8080)
--api-key= API key used to secure server routes
--config= Path to YAML config file
--version Print current version
--listextensions List all registered extensions
@@ -472,6 +523,8 @@ Application Options:
--rmextension= Remove a registered extension by name
--strategy= Choose a strategy from the available strategies
--liststrategies List all strategies
--listvendors List all vendors
--shell-complete-list Output raw list without headers/formatting (for shell completion)
Help Options:
-h, --help Show this help message
@@ -713,6 +766,14 @@ The Streamlit UI supports clipboard operations across different platforms:
<a href="https://github.com/sbehrens"><img src="https://avatars.githubusercontent.com/u/688589?v=4" title="Scott Behrens" width="50" height="50"></a>
<a href="https://github.com/agu3rra"><img src="https://avatars.githubusercontent.com/u/10410523?v=4" title="Andre Guerra" width="50" height="50"></a>
### Contributors
<a href="https://github.com/danielmiessler/fabric/graphs/contributors">
<img src="https://contrib.rocks/image?repo=danielmiessler/fabric" />
</a>
Made with [contrib.rocks](https://contrib.rocks).
`fabric` was created by <a href="https://danielmiessler.com/subscribe" target="_blank">Daniel Miessler</a> in January of 2024.
<br /><br />
<a href="https://twitter.com/intent/user?screen_name=danielmiessler">![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/danielmiessler)</a>

View File

@@ -1,4 +1,4 @@
This Cummulative PR adds several Web UI and functionality improvements to make pattern selection more intuitive with the addition of pattern descriptions, ability to save favorite patterns, a Pattern TAG system, powerful multilingual capabilities, PDF-to-markdown functionnalities, a help reference section, more robust Youtube processing and a variety of other ui improvements.
This Cumulative PR adds several Web UI and functionality improvements to make pattern selection more intuitive with the addition of pattern descriptions, ability to save favorite patterns, a Pattern TAG system, powerful multilingual capabilities, PDF-to-markdown functionalities, a help reference section, more robust Youtube processing and a variety of other ui improvements.
## 🎥 Demo Video
https://youtu.be/XMzjgqvdltM

View File

@@ -1,20 +1,25 @@
# YAML Configuration Support
## Overview
Fabric now supports YAML configuration files for commonly used options. This allows users to persist settings and share configurations across multiple runs.
## Usage
Use the `--config` flag to specify a YAML configuration file:
```bash
fabric --config ~/.config/fabric/config.yaml "Tell me about APIs"
```
## Configuration Precedence
1. CLI flags (highest priority)
2. YAML config values
3. Default values (lowest priority)
## Supported Configuration Options
```yaml
# Model selection
model: gpt-4
@@ -36,6 +41,7 @@ raw: false
```
## Rules and Behavior
- Only long flag names are supported in YAML (e.g., `temperature` not `-t`)
- CLI flags always override YAML values
- Unknown YAML declarations are ignored
@@ -43,12 +49,15 @@ raw: false
- The order of YAML declarations doesn't matter
## Type Conversions
The following string-to-type conversions are supported:
- String to number: `"42"``42`
- String to float: `"42.5"``42.5`
- String to boolean: `"true"``true`
## Example Config
```yaml
# ~/.config/fabric/config.yaml
model: gpt-4
@@ -61,8 +70,8 @@ frequencypenalty: 0.2
```
## CLI Override Example
```bash
# Override temperature from config
fabric --config ~/.config/fabric/config.yaml --temperature 0.9 "Query"
```

View File

@@ -57,7 +57,7 @@ func Cli(version string) (err error) {
if currentFlags.Serve {
registry.ConfigureVendors()
err = restapi.Serve(registry, currentFlags.ServeAddress)
err = restapi.Serve(registry, currentFlags.ServeAddress, currentFlags.ServeAPIKey)
return
}
@@ -93,7 +93,7 @@ func Cli(version string) (err error) {
}
if currentFlags.ListPatterns {
err = fabricDb.Patterns.ListNames()
err = fabricDb.Patterns.ListNames(currentFlags.ShellCompleteOutput)
return
}
@@ -102,17 +102,17 @@ func Cli(version string) (err error) {
if models, err = registry.VendorManager.GetModels(); err != nil {
return
}
models.Print()
models.Print(currentFlags.ShellCompleteOutput)
return
}
if currentFlags.ListAllContexts {
err = fabricDb.Contexts.ListNames()
err = fabricDb.Contexts.ListNames(currentFlags.ShellCompleteOutput)
return
}
if currentFlags.ListAllSessions {
err = fabricDb.Sessions.ListNames()
err = fabricDb.Sessions.ListNames(currentFlags.ShellCompleteOutput)
return
}
@@ -160,7 +160,12 @@ func Cli(version string) (err error) {
}
if currentFlags.ListStrategies {
err = registry.Strategies.ListStrategies()
err = registry.Strategies.ListStrategies(currentFlags.ShellCompleteOutput)
return
}
if currentFlags.ListVendors {
err = registry.ListVendors(os.Stdout)
return
}
@@ -211,7 +216,9 @@ func Cli(version string) (err error) {
return
}
messageTools, err = processYoutubeVideo(currentFlags, registry, videoId)
if messageTools, err = processYoutubeVideo(currentFlags, registry, videoId); err != nil {
return
}
if !currentFlags.IsChatRequest() {
err = currentFlags.WriteOutput(messageTools)
return

View File

@@ -64,6 +64,7 @@ type Flags struct {
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"`
@@ -71,6 +72,8 @@ type Flags struct {
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)"`
}
var debug = false
@@ -333,7 +336,6 @@ func (o *Flags) BuildChatRequest(Meta string) (ret *common.ChatRequest, err erro
func (o *Flags) AppendMessage(message string) {
o.Message = AppendMessage(o.Message, message)
return
}
func (o *Flags) IsChatRequest() (ret bool) {

View File

@@ -2,6 +2,8 @@ package common
import (
"fmt"
"sort"
"strings"
"github.com/samber/lo"
)
@@ -40,13 +42,43 @@ func (o *GroupsItemsSelector[I]) AddGroupItems(group string, items ...I) {
o.GroupsItems = append(o.GroupsItems, &GroupItems[I]{group, items})
}
// getSortedGroupsItems returns a new slice of GroupItems with both groups and their items
// sorted alphabetically in a case-insensitive manner. The original GroupsItems are not modified.
func (o *GroupsItemsSelector[I]) getSortedGroupsItems() []*GroupItems[I] {
// Copy and sort groups (caseinsensitive)
sortedGroupsItems := make([]*GroupItems[I], len(o.GroupsItems))
copy(sortedGroupsItems, o.GroupsItems)
sort.SliceStable(sortedGroupsItems, func(i, j int) bool {
return strings.ToLower(sortedGroupsItems[i].Group) < strings.ToLower(sortedGroupsItems[j].Group)
})
// For each group, sort its items
for i, groupItems := range sortedGroupsItems {
sortedItems := make([]I, len(groupItems.Items))
copy(sortedItems, groupItems.Items)
sort.SliceStable(sortedItems, func(i, j int) bool {
return strings.ToLower(o.GetItemKey(sortedItems[i])) < strings.ToLower(o.GetItemKey(sortedItems[j]))
})
// Create a new GroupItems with the sorted items
sortedGroupsItems[i] = &GroupItems[I]{
Group: groupItems.Group,
Items: sortedItems,
}
}
return sortedGroupsItems
}
func (o *GroupsItemsSelector[I]) GetGroupAndItemByItemNumber(number int) (group string, item I, err error) {
var currentItemNumber int
found := false
for _, groupItems := range o.GroupsItems {
if currentItemNumber+groupItems.Count() < number {
currentItemNumber += groupItems.Count()
sortedGroupsItems := o.getSortedGroupsItems()
for _, groupItems := range sortedGroupsItems {
if currentItemNumber+len(groupItems.Items) < number {
currentItemNumber += len(groupItems.Items)
continue
}
@@ -59,6 +91,10 @@ func (o *GroupsItemsSelector[I]) GetGroupAndItemByItemNumber(number int) (group
break
}
}
if found {
break
}
}
if !found {
@@ -67,19 +103,30 @@ func (o *GroupsItemsSelector[I]) GetGroupAndItemByItemNumber(number int) (group
return
}
func (o *GroupsItemsSelector[I]) Print() {
fmt.Printf("\n%v:\n", o.SelectionLabel)
func (o *GroupsItemsSelector[I]) Print(shellCompleteList bool) {
// Only print the section header if not in plain output mode
if !shellCompleteList {
fmt.Printf("\n%v:\n", o.SelectionLabel)
}
var currentItemIndex int
for _, groupItems := range o.GroupsItems {
fmt.Println()
fmt.Printf("%s\n", groupItems.Group)
fmt.Println()
sortedGroupsItems := o.getSortedGroupsItems()
for _, groupItems := range sortedGroupsItems {
if !shellCompleteList {
fmt.Println()
fmt.Printf("%s\n\n", groupItems.Group)
}
for _, item := range groupItems.Items {
currentItemIndex++
fmt.Printf("\t[%d]\t%s\n", currentItemIndex, o.GetItemKey(item))
if shellCompleteList {
// plain mode: "index key"
fmt.Printf("%s\n", o.GetItemKey(item))
} else {
// formatted mode: "[index] key"
fmt.Printf("\t[%d]\t%s\n", currentItemIndex, o.GetItemKey(item))
}
}
}
}

111
completions/_fabric Normal file
View File

@@ -0,0 +1,111 @@
#compdef fabric
# 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)"})
compadd -X "Patterns:" ${patterns}
}
_fabric_models() {
local -a models
models=(${(f)"$(fabric --listmodels --shell-complete-list 2>/dev/null)"})
compadd -X "Models:" ${models}
}
_fabric_contexts() {
local -a contexts
contexts=(${(f)"$(fabric --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)"})
compadd -X "Sessions:" ${sessions}
}
_fabric_strategies() {
local -a strategies
strategies=(${(f)"$(fabric --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)"})
compadd -X "Extensions:" ${extensions}
'(-L --listmodels)'{-L,--listmodels}'[List all available models]:list models:_fabric_models' \
'(-x --listcontexts)'{-x,--listcontexts}'[List all contexts]:list contexts:_fabric_contexts' \
'(-X --listsessions)'{-X,--listsessions}'[List all sessions]:list sessions:_fabric_sessions' \
'(--listextensions)--listextensions[List all registered extensions]' \
'(--liststrategies)--liststrategies[List all strategies]:list strategies:_fabric_strategies' \
'(--listvendors)--listvendors[List all vendors]' \
vendors=(${(f)"$(fabric --listvendors 2>/dev/null)"})
compadd -X "Vendors:" ${vendors}
}
_fabric() {
local curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C \
'(-p --pattern)'{-p,--pattern}'[Choose a pattern from the available patterns]:pattern:_fabric_patterns' \
'(-v --variable)'{-v,--variable}'[Values for pattern variables, e.g. -v=#role:expert -v=#points:30]:variable:' \
'(-C --context)'{-C,--context}'[Choose a context from the available contexts]:context:_fabric_contexts' \
'(--session)--session[Choose a session from the available sessions]:session:_fabric_sessions' \
'(-a --attachment)'{-a,--attachment}'[Attachment path or URL (e.g. for OpenAI image recognition messages)]:file:_files' \
'(-S --setup)'{-S,--setup}'[Run setup for all reconfigurable parts of fabric]' \
'(-t --temperature)'{-t,--temperature}'[Set temperature (default: 0.7)]:temperature:' \
'(-T --topp)'{-T,--topp}'[Set top P (default: 0.9)]:topp:' \
'(-s --stream)'{-s,--stream}'[Stream]' \
'(-P --presencepenalty)'{-P,--presencepenalty}'[Set presence penalty (default: 0.0)]:presence penalty:' \
'(-r --raw)'{-r,--raw}'[Use the defaults of the model without sending chat options]' \
'(-F --frequencypenalty)'{-F,--frequencypenalty}'[Set frequency penalty (default: 0.0)]:frequency penalty:' \
'(-l --listpatterns)'{-l,--listpatterns}'[List all patterns]' \
'(-L --listmodels)'{-L,--listmodels}'[List all available models]' \
'(-x --listcontexts)'{-x,--listcontexts}'[List all contexts]' \
'(-X --listsessions)'{-X,--listsessions}'[List all sessions]' \
'(-U --updatepatterns)'{-U,--updatepatterns}'[Update patterns]' \
'(-c --copy)'{-c,--copy}'[Copy to clipboard]' \
'(-m --model)'{-m,--model}'[Choose model]:model:_fabric_models' \
'(--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]' \
'(-n --latest)'{-n,--latest}'[Number of latest patterns to list (default: 0)]:number:' \
'(-d --changeDefaultModel)'{-d,--changeDefaultModel}'[Change default model]' \
'(-y --youtube)'{-y,--youtube}'[YouTube video or play list URL]:youtube url:' \
'(--playlist)--playlist[Prefer playlist over video if both ids are present in the URL]' \
'(--transcript)--transcript[Grab transcript from YouTube video and send to chat]' \
'(--transcript-with-timestamps)--transcript-with-timestamps[Grab transcript from YouTube video with timestamps]' \
'(--comments)--comments[Grab comments from YouTube video and send to chat]' \
'(--metadata)--metadata[Output video metadata]' \
'(-g --language)'{-g,--language}'[Specify the Language Code for the chat, e.g. -g=en -g=zh]:language:' \
'(-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:' \
'(-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]' \
'(--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]' \
'(--address)--address[The address to bind the REST API (default: :8080)]:address:' \
'(--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]' \
'(--listextensions)--listextensions[List all registered extensions]' \
'(--addextension)--addextension[Register a new extension from config file path]:config file:_files -g "*.yaml *.yml"' \
'(--rmextension)--rmextension[Remove a registered extension by name]:extension:_fabric_extensions' \
'(--strategy)--strategy[Choose a strategy from the available strategies]:strategy:_fabric_strategies' \
'(--liststrategies)--liststrategies[List all strategies]' \
'(--listvendors)--listvendors[List all vendors]' \
'(--shell-complete-list)--shell-complete-list[Output raw list without headers/formatting (for shell completion)]' \
'(-h --help)'{-h,--help}'[Show this help message]' \
'*:arguments:'
}
_fabric "$@"

90
completions/fabric.bash Normal file
View File

@@ -0,0 +1,90 @@
# Bash completion for fabric CLI
#
# Installation:
# 1. Place this file in a standard completion directory, e.g.,
# - /etc/bash_completion.d/
# - /usr/local/etc/bash_completion.d/
# - ~/.local/share/bash-completion/completions/
# 2. Or, source it directly in your ~/.bashrc or ~/.bash_profile:
# source /path/to/fabric.bash
_fabric() {
local cur prev words cword
_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 --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 --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
}
# Handle completions based on the previous word
case "${prev}" in
-p | --pattern)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listpatterns)" -- "${cur}"))
return 0
;;
-C | --context)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listcontexts)" -- "${cur}"))
return 0
;;
--session)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listsessions)" -- "${cur}"))
return 0
;;
-m | --model)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listmodels)" -- "${cur}"))
return 0
;;
-w | --wipecontext)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listcontexts)" -- "${cur}"))
return 0
;;
-W | --wipesession)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listsessions)" -- "${cur}"))
return 0
;;
--printcontext)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listcontexts)" -- "${cur}"))
return 0
;;
--printsession)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listsessions)" -- "${cur}"))
return 0
;;
--rmextension)
COMPREPLY=($(compgen -W "$(_fabric_get_list --listextensions)" -- "${cur}"))
return 0
;;
--strategy)
COMPREPLY=($(compgen -W "$(_fabric_get_list --liststrategies)" -- "${cur}"))
return 0
;;
# Options requiring file/directory paths
-a | --attachment | -o | --output | --config | --addextension)
_filedir
return 0
;;
# Options requiring simple arguments (no specific completion logic here)
-v | --variable | -t | --temperature | -T | --topp | -P | --presencepenalty | -F | --frequencypenalty | --modelContextLength | -n | --latest | -y | --youtube | -g | --language | -u | --scrape_url | -q | --scrape_question | -e | --seed | --address | --api-key)
# No specific completion suggestions, user types the value
return 0
;;
esac
# If the current word starts with '-', suggest options
if [[ "${cur}" == -* ]]; then
COMPREPLY=($(compgen -W "${opts}" -- "${cur}"))
return 0
fi
# Default: complete files/directories if no other rule matches
# _filedir
# Or provide no completions if it's not an option or argument following a known flag
COMPREPLY=()
}
complete -F _fabric fabric

94
completions/fabric.fish Executable file
View File

@@ -0,0 +1,94 @@
# Fish shell completion for fabric CLI
#
# Installation:
# Copy this file to ~/.config/fish/completions/fabric.fish
# or run:
# mkdir -p ~/.config/fish/completions
# cp completions/fabric.fish ~/.config/fish/completions/
# Helper functions for dynamic completions
function __fabric_get_patterns
fabric --listpatterns --shell-complete-list 2>/dev/null
end
function __fabric_get_models
fabric --listmodels --shell-complete-list 2>/dev/null
end
function __fabric_get_contexts
fabric --listcontexts --shell-complete-list 2>/dev/null
end
function __fabric_get_sessions
fabric --listsessions --shell-complete-list 2>/dev/null
end
function __fabric_get_strategies
fabric --liststrategies --shell-complete-list 2>/dev/null
end
function __fabric_get_extensions
fabric --listextensions --shell-complete-list 2>/dev/null
end
# Main completion function
complete -c fabric -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 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)"
# 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 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 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 shell-complete-list -d "Output raw list without headers/formatting (for shell completion)"
complete -c fabric -s h -l help -d "Show this help message"

View File

@@ -32,6 +32,13 @@ type Chatter struct {
// Send processes a chat request and applies any file changes if using the create_coding_feature pattern
func (o *Chatter) Send(request *common.ChatRequest, opts *common.ChatOptions) (session *fsdb.Session, err error) {
modelToUse := opts.Model
if modelToUse == "" {
modelToUse = o.model // Default to the model set in the Chatter struct
}
if o.vendor.NeedsRawMode(modelToUse) {
opts.Raw = true
}
if session, err = o.BuildSession(request, opts.Raw); err != nil {
return
}
@@ -185,29 +192,42 @@ func (o *Chatter) BuildSession(request *common.ChatRequest, raw bool) (session *
}
}
if request.Language != "" {
systemMessage = fmt.Sprintf("%s. Please use the language '%s' for the output.", systemMessage, request.Language)
// Apply refined language instruction if specified
if request.Language != "" && request.Language != "en" {
// Refined instruction: Execute pattern using user input, then translate the entire response.
systemMessage = fmt.Sprintf("%s\n\nIMPORTANT: First, execute the instructions provided in this prompt using the user's input. Second, ensure your entire final response, including any section headers or titles generated as part of executing the instructions, is written ONLY in the %s language.", systemMessage, request.Language)
}
if raw {
if request.Message != nil {
if systemMessage != "" {
request.Message.Content = systemMessage
// system contains pattern which contains user input
// In raw mode, we want to avoid duplicating the input that's already in the pattern
var finalContent string
if systemMessage != "" {
// If we have a pattern, it already includes the user input
if request.PatternName != "" {
finalContent = systemMessage
} else {
// No pattern, combine system message with user input
finalContent = fmt.Sprintf("%s\n\n%s", systemMessage, request.Message.Content)
}
} else {
if systemMessage != "" {
request.Message = &goopenai.ChatCompletionMessage{Role: goopenai.ChatMessageRoleSystem, Content: systemMessage}
request.Message = &goopenai.ChatCompletionMessage{
Role: goopenai.ChatMessageRoleUser,
Content: finalContent,
}
}
} else {
// After this, if request.Message is not nil, append it
if request.Message != nil {
session.Append(request.Message)
}
} else { // Not raw mode
if systemMessage != "" {
session.Append(&goopenai.ChatCompletionMessage{Role: goopenai.ChatMessageRoleSystem, Content: systemMessage})
}
}
if request.Message != nil {
session.Append(request.Message)
// If a pattern was used (request.PatternName != ""), its output (systemMessage)
// already incorporates the user input (request.Message.Content via GetApplyVariables).
// So, we only append the direct user message if NO pattern was used.
if request.PatternName == "" && request.Message != nil {
session.Append(request.Message)
}
}
if session.IsEmpty() {

View File

@@ -3,9 +3,12 @@ package core
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/danielmiessler/fabric/plugins/ai/exolab"
"github.com/danielmiessler/fabric/plugins/strategy"
@@ -17,17 +20,12 @@ import (
"github.com/danielmiessler/fabric/plugins/ai"
"github.com/danielmiessler/fabric/plugins/ai/anthropic"
"github.com/danielmiessler/fabric/plugins/ai/azure"
"github.com/danielmiessler/fabric/plugins/ai/deepseek"
"github.com/danielmiessler/fabric/plugins/ai/dryrun"
"github.com/danielmiessler/fabric/plugins/ai/gemini"
"github.com/danielmiessler/fabric/plugins/ai/groq"
"github.com/danielmiessler/fabric/plugins/ai/litellm"
"github.com/danielmiessler/fabric/plugins/ai/lmstudio"
"github.com/danielmiessler/fabric/plugins/ai/mistral"
"github.com/danielmiessler/fabric/plugins/ai/ollama"
"github.com/danielmiessler/fabric/plugins/ai/openai"
"github.com/danielmiessler/fabric/plugins/ai/openrouter"
"github.com/danielmiessler/fabric/plugins/ai/siliconcloud"
"github.com/danielmiessler/fabric/plugins/ai/openai_compatible"
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/danielmiessler/fabric/plugins/template"
"github.com/danielmiessler/fabric/plugins/tools"
@@ -56,27 +54,49 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
ret.Defaults = tools.NeeDefaults(ret.GetModels)
ret.VendorsAll.AddVendors(
// Create a vendors slice to hold all vendors (order doesn't matter initially)
vendors := []ai.Vendor{}
// Add non-OpenAI compatible clients
vendors = append(vendors,
openai.NewClient(),
ollama.NewClient(),
azure.NewClient(),
groq.NewClient(),
gemini.NewClient(),
//gemini_openai.NewClient(),
anthropic.NewClient(),
siliconcloud.NewClient(),
openrouter.NewClient(),
lmstudio.NewClient(),
mistral.NewClient(),
deepseek.NewClient(),
exolab.NewClient(),
litellm.NewClient(),
)
// Add all OpenAI-compatible providers
for providerName := range openai_compatible.ProviderMap {
provider, _ := openai_compatible.GetProviderByName(providerName)
vendors = append(vendors, openai_compatible.NewClient(provider))
}
// Sort vendors by name for consistent ordering (case-insensitive)
sort.Slice(vendors, func(i, j int) bool {
return strings.ToLower(vendors[i].GetName()) < strings.ToLower(vendors[j].GetName())
})
// Add all sorted vendors to VendorsAll
ret.VendorsAll.AddVendors(vendors...)
_ = ret.Configure()
return
}
func (o *PluginRegistry) ListVendors(out io.Writer) error {
vendors := lo.Map(o.VendorsAll.Vendors, func(vendor ai.Vendor, _ int) string {
return vendor.GetName()
})
fmt.Fprint(out, "Available Vendors:\n\n")
for _, vendor := range vendors {
fmt.Fprintf(out, "%s\n", vendor)
}
return nil
}
type PluginRegistry struct {
Db *fsdb.Db
@@ -129,10 +149,10 @@ func (o *PluginRegistry) Setup() (err error) {
return vendor
})...)
groupsPlugins.AddGroupItems("Tools", o.Defaults, o.PatternsLoader, o.YouTube, o.Language, o.Jina, o.Strategies)
groupsPlugins.AddGroupItems("Tools", o.Defaults, o.Jina, o.Language, o.PatternsLoader, o.Strategies, o.YouTube)
for {
groupsPlugins.Print()
groupsPlugins.Print(false)
if answerErr := setupQuestion.Ask("Plugin Number"); answerErr != nil {
break

18
flake.lock generated
View File

@@ -26,11 +26,11 @@
]
},
"locked": {
"lastModified": 1733668782,
"narHash": "sha256-tPsqU00FhgdFr0JiQUiBMgPVbl1jbPCY5gbFiJycL3I=",
"lastModified": 1742209644,
"narHash": "sha256-jMy1XqXqD0/tJprEbUmKilTkvbDY/C0ZGSsJJH4TNCE=",
"owner": "nix-community",
"repo": "gomod2nix",
"rev": "514283ec89c39ad0079ff2f3b1437404e4cba608",
"rev": "8f3534eb8f6c5c3fce799376dc3b91bae6b11884",
"type": "github"
},
"original": {
@@ -41,11 +41,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1736344531,
"narHash": "sha256-8YVQ9ZbSfuUk2bUf2KRj60NRraLPKPS0Q4QFTbc+c2c=",
"lastModified": 1745234285,
"narHash": "sha256-GfpyMzxwkfgRVN0cTGQSkTC0OHhEkv3Jf6Tcjm//qZ0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "bffc22eb12172e6db3c5dde9e3e5628f8e3e7912",
"rev": "c11863f1e964833214b767f4a369c6e6a7aba141",
"type": "github"
},
"original": {
@@ -100,11 +100,11 @@
]
},
"locked": {
"lastModified": 1736154270,
"narHash": "sha256-p2r8xhQZ3TYIEKBoiEhllKWQqWNJNoT9v64Vmg4q8Zw=",
"lastModified": 1744961264,
"narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "13c913f5deb3a5c08bb810efd89dc8cb24dd968b",
"rev": "8d404a69efe76146368885110f29a2ca3700bee6",
"type": "github"
},
"original": {

View File

@@ -28,6 +28,8 @@
let
forAllSystems = nixpkgs.lib.genAttrs (import systems);
getGoVersion = system: nixpkgs.legacyPackages.${system}.go_1_24;
treefmtEval = forAllSystems (
system:
let
@@ -47,10 +49,14 @@
system:
let
pkgs = nixpkgs.legacyPackages.${system};
goEnv = gomod2nix.legacyPackages.${system}.mkGoEnv { pwd = ./.; };
goVersion = getGoVersion system;
goEnv = gomod2nix.legacyPackages.${system}.mkGoEnv {
pwd = ./.;
go = goVersion;
};
in
import ./nix/shell.nix {
inherit pkgs goEnv;
inherit pkgs goEnv goVersion;
inherit (gomod2nix.legacyPackages.${system}) gomod2nix;
}
);
@@ -59,10 +65,12 @@
system:
let
pkgs = nixpkgs.legacyPackages.${system};
goVersion = getGoVersion system;
in
{
default = self.packages.${system}.fabric;
fabric = pkgs.callPackage ./nix/pkgs/fabric {
go = goVersion;
inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
};
inherit (gomod2nix.legacyPackages.${system}) gomod2nix;

76
go.mod
View File

@@ -1,65 +1,67 @@
module github.com/danielmiessler/fabric
go 1.23.4
go 1.24.0
toolchain go1.24.2
require (
github.com/anaskhan96/soup v1.2.5
github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.11
github.com/anthropics/anthropic-sdk-go v1.2.0
github.com/atotto/clipboard v0.1.4
github.com/gabriel-vasile/mimetype v1.4.8
github.com/gabriel-vasile/mimetype v1.4.9
github.com/gin-gonic/gin v1.10.0
github.com/go-git/go-git/v5 v5.13.2
github.com/go-git/go-git/v5 v5.16.0
github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612
github.com/google/generative-ai-go v0.19.0
github.com/jessevdk/go-flags v1.6.1
github.com/joho/godotenv v1.5.1
github.com/ollama/ollama v0.5.12
github.com/ollama/ollama v0.6.6
github.com/otiai10/copy v1.14.1
github.com/pkg/errors v0.9.1
github.com/samber/lo v1.49.1
github.com/sashabaranov/go-openai v1.38.0
github.com/sashabaranov/go-openai v1.38.2
github.com/stretchr/testify v1.10.0
golang.org/x/text v0.22.0
google.golang.org/api v0.223.0
golang.org/x/text v0.24.0
google.golang.org/api v0.230.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
)
require (
cloud.google.com/go v0.118.3 // indirect
cloud.google.com/go/ai v0.10.0 // indirect
cloud.google.com/go/auth v0.15.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
cloud.google.com/go v0.120.1 // indirect
cloud.google.com/go/ai v0.10.2 // indirect
cloud.google.com/go/auth v0.16.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/longrunning v0.6.4 // indirect
cloud.google.com/go/longrunning v0.6.7 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/ProtonMail/go-crypto v1.2.0 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
github.com/bytedance/sonic v1.12.9 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/cloudflare/circl v1.6.0 // indirect
github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.25.0 // indirect
github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@@ -70,7 +72,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
@@ -83,21 +85,21 @@ require (
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/arch v0.14.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/time v0.10.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250224174004-546df14abb99 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99 // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/arch v0.16.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
google.golang.org/grpc v1.72.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

170
go.sum
View File

@@ -1,43 +1,43 @@
cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
cloud.google.com/go/ai v0.10.0 h1:hwj6CI6sMKubXodoJJGTy/c2T1RbbLGM6TL3QoAvzU8=
cloud.google.com/go/ai v0.10.0/go.mod h1:kvnt2KeHqX8+41PVeMRBETDyQAp/RFvBWGdx/aGjNMo=
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
cloud.google.com/go v0.120.1 h1:Z+5V7yd383+9617XDCyszmK5E4wJRJL+tquMfDj9hLM=
cloud.google.com/go v0.120.1/go.mod h1:56Vs7sf/i2jYM6ZL9NYlC82r04PThNcPS5YgFmb0rp8=
cloud.google.com/go/ai v0.10.2 h1:5NHzmZlRs+3kvlsVdjT0cTnLrjQdROJ/8VOljVfs+8o=
cloud.google.com/go/ai v0.10.2/go.mod h1:xZuZuE9d3RgsR132meCnPadiU9XV0qXjpLr+P4J46eE=
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg=
cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs=
github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/anaskhan96/soup v1.2.5 h1:V/FHiusdTrPrdF4iA1YkVxsOpdNcgvqT1hG+YtcZ5hM=
github.com/anaskhan96/soup v1.2.5/go.mod h1:6YnEp9A2yywlYdM4EgDz9NEHclocMepEtku7wg6Cq3s=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
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 v0.2.0-alpha.11 h1:O3/AMObKntZyu1KH6Xks6E0gbE8w6HVaKHE+/vXARzM=
github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.11/go.mod h1:GJxtdOs9K4neo8Gg65CjJ7jNautmldGli5/OFNabOoo=
github.com/anthropics/anthropic-sdk-go v1.2.0 h1:RQzJUqaROewrPTl7Rl4hId/TqmjFvfnkmhHJ6pP1yJ8=
github.com/anthropics/anthropic-sdk-go v1.2.0/go.mod h1:AapDW22irxK2PSumZiQXYUFvsdQgkwIWlpESweWZI/c=
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=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/bytedance/sonic v1.12.9 h1:Od1BvK55NnewtGaJsTDeAOSnLVO2BTSLOe0+ooKokmQ=
github.com/bytedance/sonic v1.12.9/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
@@ -46,16 +46,16 @@ github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGL
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
@@ -66,8 +66,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -79,8 +79,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c h1:wpkoddUomPfHiOziHZixGO5ZBS73cKqVzZipfrLmO1w=
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c/go.mod h1:oVDCh3qjJMLVUSILBRwrm+Bc6RNXGZYtoh9xdvf1ffM=
github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612 h1:BYLNYdZaepitbZreRIa9xeCQZocWmy/wj4cGIH0qyw0=
@@ -103,8 +103,8 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@@ -138,16 +138,16 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/ollama/ollama v0.5.12 h1:qM+k/ozyHLJzEQoAEPrUQ0qXqsgDEEdpIVwuwScrd2U=
github.com/ollama/ollama v0.5.12/go.mod h1:ibdmDvb/TjKY1OArBWIazL3pd1DHTk8eG2MMjEkWhiI=
github.com/ollama/ollama v0.6.6 h1:rnCQTSTiRD3Dsvd35dh2j2YB9DlQMFQR/y3XOhWZOmI=
github.com/ollama/ollama v0.6.6/go.mod h1:pGgtoNyc9DdM6oZI6yMfI6jTk2Eh4c36c2GpfQCH7PY=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -155,12 +155,12 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
github.com/sashabaranov/go-openai v1.38.0 h1:hNN5uolKwdbpiqOn7l+Z2alch/0n0rSFyg4n+GZxR5k=
github.com/sashabaranov/go-openai v1.38.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/sashabaranov/go-openai v1.38.2 h1:akrssjj+6DY3lWuDwHv6cBvJ8Z+FZDM9XEaaYFt0Auo=
github.com/sashabaranov/go-openai v1.38.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
@@ -170,7 +170,6 @@ github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQ
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -179,7 +178,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -201,22 +199,22 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@@ -224,10 +222,10 @@ 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.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -244,10 +242,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -255,8 +253,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -273,8 +271,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -284,8 +282,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.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
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=
@@ -296,10 +294,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -307,16 +305,16 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.223.0 h1:JUTaWEriXmEy5AhvdMgksGGPEFsYfUKaPEYXd4c3Wvc=
google.golang.org/api v0.223.0/go.mod h1:C+RS7Z+dDwds2b+zoAk5hN/eSfsiCn0UDrYof/M4d2M=
google.golang.org/genproto/googleapis/api v0.0.0-20250224174004-546df14abb99 h1:ilJhrCga0AptpJZXmUYG4MCrx/zf3l1okuYz7YK9PPw=
google.golang.org/genproto/googleapis/api v0.0.0-20250224174004-546df14abb99/go.mod h1:Xsh8gBVxGCcbV8ZeTB9wI5XPyZ5RvC6V3CTeeplHbiA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99 h1:ZSlhAUqC4r8TPzqLXQ0m3upBNZeF+Y8jQ3c4CR3Ujms=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0=
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View File

@@ -1,6 +1,8 @@
{
lib,
buildGoApplication,
go,
installShellFiles,
}:
buildGoApplication {
@@ -17,6 +19,15 @@ buildGoApplication {
"-w"
];
inherit go;
nativeBuildInputs = [ installShellFiles ];
postInstall = ''
installShellCompletion --zsh ./completions/_fabric
installShellCompletion --bash ./completions/fabric.bash
installShellCompletion --fish ./completions/fabric.fish
'';
meta = with lib; {
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";
homepage = "https://github.com/danielmiessler/fabric";

View File

@@ -2,23 +2,23 @@ schema = 3
[mod]
[mod."cloud.google.com/go"]
version = "v0.118.3"
hash = "sha256-y3YHioDLx9/asf2AWuincnq4BVO2S/GQFxpa1dEpxKs="
version = "v0.120.1"
hash = "sha256-yWaLc06rGXk16K53rix8O4uPSX+AOZDgIpIXf+wlh10="
[mod."cloud.google.com/go/ai"]
version = "v0.10.0"
hash = "sha256-huE2q1HBA6d9FQ152HFQhOe9fX0QlLFVuFO3XAfln8U="
version = "v0.10.2"
hash = "sha256-bsqvdylG8kk+AHtyvMRMv1TOjUmvONAgJ+14mKcwuzs="
[mod."cloud.google.com/go/auth"]
version = "v0.15.0"
hash = "sha256-N9xjLPDLhG5cqUx94tNccv74Q/fIlukWU6NbWpuNi+I="
version = "v0.16.1"
hash = "sha256-rMPMNQh/YM/67b9Grfu0BFccWpS1SRhBepubQqXRAyg="
[mod."cloud.google.com/go/auth/oauth2adapt"]
version = "v0.2.7"
hash = "sha256-U+pXaY0kPnSeBzHWxELZ75bZnb74nygwIVZDdXYcP5g="
version = "v0.2.8"
hash = "sha256-GoXFqAbp1WO1tDj07PF5EyxDYvCBP0l0qwxY2oV2hfc="
[mod."cloud.google.com/go/compute/metadata"]
version = "v0.6.0"
hash = "sha256-E8/cwio4xR8buCryR4HwR7+agb4M3zqgXSm7rBglmIY="
[mod."cloud.google.com/go/longrunning"]
version = "v0.6.4"
hash = "sha256-Q0JtsyxSgVwi91ZhvefpAq8fKbblRrtQ2bQhQYiTY48="
version = "v0.6.7"
hash = "sha256-9I0Nc2KWAEVoxDngNkqFUdASmZIAySfMEELlPh3Q3xA="
[mod."dario.cat/mergo"]
version = "v1.0.1"
hash = "sha256-wcG6+x0k6KzOSlaPA+1RFxa06/RIAePJTAjjuhLbImw="
@@ -26,8 +26,8 @@ schema = 3
version = "v0.6.2"
hash = "sha256-tVNWDUMILZbJvarcl/E7tpSnkn7urqgSHa2Eaka5vSU="
[mod."github.com/ProtonMail/go-crypto"]
version = "v1.1.5"
hash = "sha256-N5Zn0f/NF3ezyGou2kRw9BwM25feJqnp7TPkRt6oK6I="
version = "v1.2.0"
hash = "sha256-5fKgWUz6BoyFNNZ1OD9QjhBrhNEBCuVfO2WqH+X59oo="
[mod."github.com/anaskhan96/soup"]
version = "v1.2.5"
hash = "sha256-t8yCyK2y7x2qaI/3Yw16q3zVFqu+3acLcPgTr1MIKWg="
@@ -35,8 +35,8 @@ schema = 3
version = "v1.3.3"
hash = "sha256-jv7ZshpSd7FZzKKN6hqlUgiR8C3y85zNIS/hq7g76Ho="
[mod."github.com/anthropics/anthropic-sdk-go"]
version = "v0.2.0-alpha.11"
hash = "sha256-0wl62e6AVhDY3KkoYrfAHFtBrwNC4nzqrR55iyCJlwk="
version = "v1.2.0"
hash = "sha256-IzSmJBfMB2OAyFOCqwSzwdJMPoTQqJ1rBtKXGrFo2Bc="
[mod."github.com/araddon/dateparse"]
version = "v0.0.0-20210429162001-6b43995a97de"
hash = "sha256-UuX84naeRGMsFOgIgRoBHG5sNy1CzBkWPKmd6VbLwFw="
@@ -44,14 +44,14 @@ schema = 3
version = "v0.1.4"
hash = "sha256-ZZ7U5X0gWOu8zcjZcWbcpzGOGdycwq0TjTFh/eZHjXk="
[mod."github.com/bytedance/sonic"]
version = "v1.12.9"
hash = "sha256-smlXGC4n6fkOiVR+A3VGd71xp+cYo42MSHuWq7H3jew="
version = "v1.13.2"
hash = "sha256-IF2qmt4IxTwivMWHUJC8sg6d85/ORb2SWvJ54fvoAMI="
[mod."github.com/bytedance/sonic/loader"]
version = "v0.2.3"
hash = "sha256-c0m1nl1jv76LVaUgFFNjZU9jss/hoSWXyCRimhRWYjM="
version = "v0.2.4"
hash = "sha256-rv9LnePpm4OspSVbfSoVbohXzhu+dxE1BH1gm3mTmTc="
[mod."github.com/cloudflare/circl"]
version = "v1.6.0"
hash = "sha256-a+SVfnHYC8Fb+NQLboNg5P9sry+WutzuNetVHFVAAo0="
version = "v1.6.1"
hash = "sha256-Dc69V12eIFnJoUNmwg6VKXHfAMijbAeEVSDe8AiOaLo="
[mod."github.com/cloudwego/base64x"]
version = "v0.1.5"
hash = "sha256-MyUYTveN48DhnL8mwAgCRuMExLct98uzSPsmYlfaa4I="
@@ -68,11 +68,11 @@ schema = 3
version = "v1.0.4"
hash = "sha256-c1JKoRSndwwOyOxq9ddCe+8qn7mG9uRq2o/822x5O/c="
[mod."github.com/gabriel-vasile/mimetype"]
version = "v1.4.8"
hash = "sha256-ElqfQtnoGHyVqtN0mJjeWakQ6N5x+nVaX3+uOV7Q5Xk="
version = "v1.4.9"
hash = "sha256-75uELLqb01djHTe7KdXvUidBK7SuejarYouEUuxaj8Q="
[mod."github.com/gin-contrib/sse"]
version = "v1.0.0"
hash = "sha256-xnaabOxDN+ojnHQC7mHd/876Z9nWFScW+JrMm1HWREw="
version = "v1.1.0"
hash = "sha256-2VP6zHEsPi0u2ZYpOTcLulwj1Gsmb6oA19qcP2/AzVM="
[mod."github.com/gin-gonic/gin"]
version = "v1.10.0"
hash = "sha256-esJasHrJtuTBwGPGAoc/XSb428J8va+tPGcZ0gTfsgc="
@@ -83,8 +83,8 @@ schema = 3
version = "v5.6.2"
hash = "sha256-VgbxcLkHjiSyRIfKS7E9Sn8OynCrMGUDkwFz6K2TVL4="
[mod."github.com/go-git/go-git/v5"]
version = "v5.13.2"
hash = "sha256-voZQHN2OSYcoQF2bIjsdRrHT5NohZ/8q9RrmY7j2Lbc="
version = "v5.16.0"
hash = "sha256-01obPHvt1PG3r8XH8TgnNfcOhaYwWEkJ0TR5QGdZqmE="
[mod."github.com/go-logr/logr"]
version = "v1.4.2"
hash = "sha256-/W6qGilFlZNTb9Uq48xGZ4IbsVeSwJiAMLw4wiNYHLI="
@@ -98,8 +98,8 @@ schema = 3
version = "v0.18.1"
hash = "sha256-2/B2qP51zfiY+k8G0w0D03KXUc7XpWj6wKY7NjNP/9E="
[mod."github.com/go-playground/validator/v10"]
version = "v10.25.0"
hash = "sha256-198CQ0f+WC7UNxCCPg6rpogez6c5ivpignJNhx+z0W4="
version = "v10.26.0"
hash = "sha256-/jMKICp8LTcJVt+b4YRTnJM84r7HK6aT0oqO7Q8SRs8="
[mod."github.com/go-shiori/dom"]
version = "v0.0.0-20230515143342-73569d674e1c"
hash = "sha256-4lm9KZfR2XnfZU9KTG+4jqLYZqbfL74AMO4y3dKpIbg="
@@ -125,8 +125,8 @@ schema = 3
version = "v1.6.0"
hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw="
[mod."github.com/googleapis/enterprise-certificate-proxy"]
version = "v0.3.4"
hash = "sha256-RVHWa0I68CTegjlXnM/GlishoZhmmwG4z+9KBucAJ1A="
version = "v0.3.6"
hash = "sha256-hPMF0s+X4/ul98GvVuw/ZNOupEXhIDB1yvWymZWYEbU="
[mod."github.com/googleapis/gax-go/v2"]
version = "v2.14.1"
hash = "sha256-iRS/KsAVTePrvTlwA7vKcQnwY6Jz329WdgzFw0hF8wk="
@@ -161,8 +161,8 @@ schema = 3
version = "v1.0.2"
hash = "sha256-+W9EIW7okXIXjWEgOaMh58eLvBZ7OshW2EhaIpNLSBU="
[mod."github.com/ollama/ollama"]
version = "v0.5.12"
hash = "sha256-Uf4GZdD77RZ5KJtz3iYVRDVCHqEh0UEihzquO4/nrss="
version = "v0.6.6"
hash = "sha256-a2Be14e+pcJo15fM/+0ksE9HVl8I4hW6ujqbpNh9bpA="
[mod."github.com/otiai10/copy"]
version = "v1.14.1"
hash = "sha256-8RR7u17SbYg9AeBXVHIv5ZMU+kHmOcx0rLUKyz6YtU0="
@@ -170,8 +170,8 @@ schema = 3
version = "v1.6.3"
hash = "sha256-/FT3dYP2+UiW/qe1pxQ7HiS8et4+KHGPIMhc+8mHvzw="
[mod."github.com/pelletier/go-toml/v2"]
version = "v2.2.3"
hash = "sha256-fE++SVgnCGdnFZoROHWuYjIR7ENl7k9KKxQrRTquv/o="
version = "v2.2.4"
hash = "sha256-8qQIPldbsS5RO8v/FW/se3ZsAyvLzexiivzJCbGRg2Q="
[mod."github.com/pjbgf/sha1cd"]
version = "v0.3.2"
hash = "sha256-jdbiRhU8xc1C5c8m7BSCj71PUXHY3f7TWFfxDKKpUMk="
@@ -185,8 +185,8 @@ schema = 3
version = "v1.49.1"
hash = "sha256-xMQS9Sx2Bpvwo/9JvSVkJ4RXYOSHm642WRqWA6y0AnU="
[mod."github.com/sashabaranov/go-openai"]
version = "v1.38.0"
hash = "sha256-p6C/7oTWgnRjZLNrLLdIzaXvm+1WCrUd1fjZkjuiz1s="
version = "v1.38.2"
hash = "sha256-AnBycaxufzWlLS1YBq7MiHDED+Jqtu9oAySKcoL4HOA="
[mod."github.com/sergi/go-diff"]
version = "v1.3.2-0.20230802210424-5b0b94c5c0d3"
hash = "sha256-UcLU83CPMbSoKI8RLvLJ7nvGaE2xRSL1RjoHCVkMzUM="
@@ -221,59 +221,59 @@ schema = 3
version = "v1.1.0"
hash = "sha256-cA9qCCu8P1NSJRxgmpfkfa5rKyn9X+Y/9FSmSd5xjyo="
[mod."go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"]
version = "v0.59.0"
hash = "sha256-jItb6nG5/urw6Pv3zb8i5ywianqTQfrheyAIsPIQcnY="
version = "v0.60.0"
hash = "sha256-DkIpL4xUy+UIQBUK6VgbsI79TbZUltaIhXl4UJWym6E="
[mod."go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"]
version = "v0.59.0"
hash = "sha256-zeC30S2MV7W2xxS5rVfPGhZO4jcdPpxxfy3QvBkt/pQ="
version = "v0.60.0"
hash = "sha256-twGSnNbXzcw5qvRiFc/zz5rS+nhmbgSVPcd5jrZjlDg="
[mod."go.opentelemetry.io/otel"]
version = "v1.34.0"
hash = "sha256-hnuuTSxaf9yMO/23xWdcTGNzvnnJiqUiL4nzYwUV5bc="
version = "v1.35.0"
hash = "sha256-LHrBtBnyDtvJGtrXHMPIFe7U53B4bZzpePB4u8Xo4Bg="
[mod."go.opentelemetry.io/otel/metric"]
version = "v1.34.0"
hash = "sha256-JklGKJiMf1fpsE9pmnuLUq26g6wVp173v4GWJ7Xp5s4="
version = "v1.35.0"
hash = "sha256-K9I0LRZqSLrC09Cuk7tp0VEk3cUVDs8S5MGnu9jw92Q="
[mod."go.opentelemetry.io/otel/trace"]
version = "v1.34.0"
hash = "sha256-u11KJ4WTDtcb0tVv7d/HOdhq8Ea+c1QPBO8MbsCQu9Q="
version = "v1.35.0"
hash = "sha256-HC2+OGDe2rg0+E8WymQbUNoc249NXM1gIBJzK4UhcQE="
[mod."golang.org/x/arch"]
version = "v0.14.0"
hash = "sha256-9akWthLBB+Au/JIg3WKcSx1YAfHEHOCnQF62sJoMJG4="
version = "v0.16.0"
hash = "sha256-+DMOuIw9GVyhM4VHdYCZepTU/EEHqDfrxJ2F83TOs5k="
[mod."golang.org/x/crypto"]
version = "v0.35.0"
hash = "sha256-XT1VU0+m1nZbhrMYXN2+eaKBlScfiT4bCBgXu4mfa1Q="
version = "v0.37.0"
hash = "sha256-9NwDEcii1e2JYM/+3y1yNzWnt/ChMm27e9OtfuF39OM="
[mod."golang.org/x/net"]
version = "v0.36.0"
hash = "sha256-2c9AvnizBMCaqKPzwF2IMlRfvjRNDFmxIYhrgZjhOm0="
version = "v0.39.0"
hash = "sha256-IP29+yGphWKUT7wHTyzqA2rnRT4AJ7oWcT6NKLzkWcM="
[mod."golang.org/x/oauth2"]
version = "v0.27.0"
hash = "sha256-TBKV2c/m0SgPqrJSE0ltJXfImrYPafNuziLN25jgsYY="
version = "v0.29.0"
hash = "sha256-IzAypzW8cN5ZbQiIdMTcTiVuUNpMSkwuxeFrJZxcDl8="
[mod."golang.org/x/sync"]
version = "v0.11.0"
hash = "sha256-5ZBfDJvNaUBM4Vhk0fgYblCGL3eBxiJL85nIE8LiKl0="
version = "v0.13.0"
hash = "sha256-CElRNe74Or/ysUkb/m3Wcz/juO/tB5fhQbAaxA5AizY="
[mod."golang.org/x/sys"]
version = "v0.30.0"
hash = "sha256-BuhWtwDkciVioc03rxty6G2vcZVnPX85lI7tgQOFVP8="
version = "v0.32.0"
hash = "sha256-c9RRnyKQy9Kl8hpbtcgkm1O5H7gOdk9Rv925F8fZS6E="
[mod."golang.org/x/text"]
version = "v0.22.0"
hash = "sha256-kUwLNFk9K/YuWmO5/u2IshrmhT2CCuk+mAShSlTTeZo="
version = "v0.24.0"
hash = "sha256-qFbmteGOvJfvbLXiOSI8Fsz5Ixt2ZhSYx0/sIqApC7Y="
[mod."golang.org/x/time"]
version = "v0.10.0"
hash = "sha256-vnlAME3gDR6R4cbCmSYAlR1Rjc0yUpkufTOPNvCdf6Q="
version = "v0.11.0"
hash = "sha256-ImTej/e5iUHbWPZMA4M2GYbsbiiZQxIrgcnYsc7uD68="
[mod."google.golang.org/api"]
version = "v0.223.0"
hash = "sha256-sNLRocS4vcjPj0vsInI/ioZ29rSVdGD0bGz8ZzBSbus="
version = "v0.230.0"
hash = "sha256-ihEdZnRbQdwpbgj9AZEZLNY14FqHmacFGFocOqExSVY="
[mod."google.golang.org/genproto/googleapis/api"]
version = "v0.0.0-20250224174004-546df14abb99"
hash = "sha256-8er5KyVDLmuuOZEDd8cHHTkpb/JifejdHwcHfqAD83o="
version = "v0.0.0-20250422160041-2d3770c4ea7f"
hash = "sha256-Y4wbEHh9Un0QKplTl2S5lhWDUha9QThx5DhWJbDG9fo="
[mod."google.golang.org/genproto/googleapis/rpc"]
version = "v0.0.0-20250224174004-546df14abb99"
hash = "sha256-l/2ByVhr10DBqSp5y1d8mtEY3++RUZKg89FCEptT0nQ="
version = "v0.0.0-20250422160041-2d3770c4ea7f"
hash = "sha256-WK7iDtAhH19NPe3TywTQlGjDawNaDKWnxhFL9PgVUwM="
[mod."google.golang.org/grpc"]
version = "v1.70.0"
hash = "sha256-7SCJx6Y35O/0P3cFtELDXrOSOb+HshxaTQYdzv2gVmg="
version = "v1.72.0"
hash = "sha256-tqu+ACMfKjhqdCGN3jLEmtaHB5ywgHGaS/eDeDRnf+M="
[mod."google.golang.org/protobuf"]
version = "v1.36.5"
hash = "sha256-isupBiQUrKPEFzK94k5cgzM3Ab5fMXp352/zcsXV1JU="
version = "v1.36.6"
hash = "sha256-lT5qnefI5FDJnowz9PEkAGylH3+fE+A3DJDkAyy9RMc="
[mod."gopkg.in/warnings.v0"]
version = "v0.1.2"
hash = "sha256-ATVL9yEmgYbkJ1DkltDGRn/auGAjqGOfjQyBYyUo8s8="

View File

@@ -1 +1 @@
"1.4.165"
"1.4.192"

View File

@@ -2,12 +2,13 @@
pkgs,
gomod2nix,
goEnv,
goVersion,
}:
{
default = pkgs.mkShell {
nativeBuildInputs = [
pkgs.go
goVersion
pkgs.gopls
pkgs.gotools
pkgs.go-tools

View File

@@ -0,0 +1,20 @@
# IDENTITY
You are an AI with a 3,129 IQ that specializes in discerning the true nature and goals of a piece of legislation.
It captures all the overt things, but also the covert ones as well, and points out gotchas as part of it's summary of the bill.
# STEPS
1. Read the entire bill 37 times using different perspectives.
2. Map out all the stuff it's trying to do on a 10 KM by 10K mental whiteboard.
3. Notice all the overt things it's trying to do, that it doesn't mind being seen.
4. Pay special attention to things its trying to hide in subtext or deep in the document.
# OUTPUT
1. Give the metadata for the bill, such as who proposed it, when, etc.
2. Create a 24-word summary of the bill and what it's trying to accomplish.
3. Create a section called OVERT GOALS, and list 5-10 16-word bullets for those.
4. Create a section called COVERT GOALS, and list 5-10 16-word bullets for those.
5. Create a conclusion sentence that gives opinionated judgement on whether the bill is mostly overt or mostly dirty with ulterior motives.

View File

@@ -0,0 +1,20 @@
# IDENTITY
You are an AI with a 3,129 IQ that specializes in discerning the true nature and goals of a piece of legislation.
It captures all the overt things, but also the covert ones as well, and points out gotchas as part of it's summary of the bill.
# STEPS
1. Read the entire bill 37 times using different perspectives.
2. Map out all the stuff it's trying to do on a 10 KM by 10K mental whiteboard.
3. Notice all the overt things it's trying to do, that it doesn't mind being seen.
4. Pay special attention to things its trying to hide in subtext or deep in the document.
# OUTPUT
1. Give the metadata for the bill, such as who proposed it, when, etc.
2. Create a 16-word summary of the bill and what it's trying to accomplish.
3. Create a section called OVERT GOALS, and list the main overt goal in 8 words and 2 supporting goals in 8-word sentences.
3. Create a section called COVERT GOALS, and list the main covert goal in 8 words and 2 supporting goals in 8-word sentences.
5. Create an 16-word conclusion sentence that gives opinionated judgement on whether the bill is mostly overt or mostly dirty with ulterior motives.

View File

@@ -0,0 +1,131 @@
# IDENTITY
You are an expert AI with a 1,222 IQ that deeply understands the relationships between complex ideas and concepts. You are also an expert in the Excalidraw tool and schema.
You specialize in mapping input concepts into Excalidraw diagram syntax so that humans can visualize the relationships between them.
# STEPS
1. Deeply study the input.
2. Think for 47 minutes about each of the sections in the input.
3. Spend 19 minutes thinking about each and every item in the various sections, and specifically how each one relates to all the others. E.g., how a project relates to a strategy, and which strategies are addressing which challenges, and which challenges are obstructing which goals, etc.
4. Build out this full mapping in on a 9KM x 9KM whiteboard in your mind.
5. Analyze and improve this mapping for 13 minutes.
# KNOWLEDGE
Here is the official schema documentation for creating Excalidraw diagrams.
Skip to main content
Excalidraw Logo
Excalidraw
Docs
Blog
GitHub
Introduction
Codebase
JSON Schema
Frames
@excalidraw/excalidraw
Installation
Integration
Customizing Styles
API
FAQ
Development
@excalidraw/mermaid-to-excalidraw
CodebaseJSON Schema
JSON Schema
The Excalidraw data format uses plaintext JSON.
Excalidraw files
When saving an Excalidraw scene locally to a file, the JSON file (.excalidraw) is using the below format.
Attributes
Attribute Description Value
type The type of the Excalidraw schema "excalidraw"
version The version of the Excalidraw schema number
source The source URL of the Excalidraw application "https://excalidraw.com"
elements An array of objects representing excalidraw elements on canvas Array containing excalidraw element objects
appState Additional application state/configuration Object containing application state properties
files Data for excalidraw image elements Object containing image data
JSON Schema example
{
// schema information
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
// elements on canvas
"elements": [
// example element
{
"id": "pologsyG-tAraPgiN9xP9b",
"type": "rectangle",
"x": 928,
"y": 319,
"width": 134,
"height": 90
/* ...other element properties */
}
/* other elements */
],
// editor state (canvas config, preferences, ...)
"appState": {
"gridSize": 20,
"viewBackgroundColor": "#ffffff"
},
// files data for "image" elements, using format `{ [fileId]: fileData }`
"files": {
// example of an image data object
"3cebd7720911620a3938ce77243696149da03861": {
"mimeType": "image/png",
"id": "3cebd7720911620a3938c.77243626149da03861",
"dataURL": "data:image/png;base64,iVBORWOKGgoAAAANSUhEUgA=",
"created": 1690295874454,
"lastRetrieved": 1690295874454
}
/* ...other image data objects */
}
}
Excalidraw clipboard format
When copying selected excalidraw elements to clipboard, the JSON schema is similar to .excalidraw format, except it differs in attributes.
Attributes
Attribute Description Example Value
type The type of the Excalidraw document. "excalidraw/clipboard"
elements An array of objects representing excalidraw elements on canvas. Array containing excalidraw element objects (see example below)
files Data for excalidraw image elements. Object containing image data
Edit this page
Previous
Contributing
Next
Frames
Excalidraw files
Attributes
JSON Schema example
Excalidraw clipboard format
Attributes
Docs
Get Started
Community
Discord
Twitter
Linkedin
More
Blog
GitHub
Copyright © 2023 Excalidraw community. Built with Docusaurus ❤️
# OUTPUT
1. Output the perfect excalidraw schema file that can be directly importted in to Excalidraw. This should have no preamble or follow-on text that breaks the format. It should be pure Excalidraw schema JSON.
2. Ensure all components are high contrast on a white background, and that you include all the arrows and appropriate relationship components that preserve the meaning of the original input.
3. Do not output the first and last lines of the schema, , e.g., json and backticks and then ending backticks. as this is automatically added by Excalidraw when importing.

View File

@@ -16,349 +16,279 @@ The goal of this exercise are to:
CONTENT SUMMARY
$100M Offers by Alex Hormozi
$100M Offers, Alex Hormozi shows you “how to make offers so good people will
Introduction
In his book, feel stupid saying no.
” The offer is “the starting point of any conversation to initiate a
transaction with a customer.”
Alex Hormozi shows you how to make profitable offers by “reliably turning advertising dollars
into (enormous) profits using a combination of pricing, value, guarantees, and naming
strategies.” Combining these factors in the right amounts will result in a Grand Slam Offer. “The
good news is that in business, you only need to hit one Grand Slam Offer to retire forever.”
Introduction: $100M Offers
In his book, Alex Hormozi shows you “how to make offers so good people feel stupid saying no."
The offer is “the starting point of any conversation to initiate a transaction with a customer.”
Alex Hormozi shows you how to make profitable offers by “reliably turning advertising dollars into (enormous) profits using a combination of pricing, value, guarantees, and naming strategies.” Combining these factors in the right amounts will result in a Grand Slam Offer. “The good news is that in business, you only need to hit one Grand Slam Offer to retire forever.”
Section I: How We Got Here
In Section I of $100M Offers, Alex Hormozi introduces his personal story from debt to success
along with the concept of the “Grand Slam Offer.”
In Section I of $100M Offers, Alex Hormozi introduces his personal story from debt to success along with the concept of the “Grand Slam Offer.”
Chapter 1. How We Got Here
Alex Hormozi begins with his story from Christmas Eve in 2016. He was on the verge of going
broke. But a few days later, he hit a grand slam in early January of 2017. In $100M Offers, Alex
Hormozi shares this vital skill of making offers, as it was life-changing for him, and he wants to
deliver for you.
Alex Hormozi begins with his story from Christmas Eve in 2016. He was on the verge of going broke. But a few days later, he hit a grand slam in early January of 2017. In $100M Offers, Alex Hormozi shares this vital skill of making offers, as it was life-changing for him, and he wants to deliver for you.
Chapter 2. Grand Slam Offers
In Chapter 2 of $100M Offers, Alex Hormozi introduces the concept of the “Grand Slam Offer.”
Travis Jones states that the secret to sales is to “Make people an offer so good they would feel
stupid saying no.” Further, to have a business, we need to make our prospects an offer:
Offer “the goods and services you agree to provide, how you accept payment, and the terms
of the agreement”
Offers start the process of customer acquisition and earning money, and they can range from
nothing to a grand slam:
• No offer? No business. No life.
• Bad offer? Negative profit. No business. Miserable life.
• Decent offer? No profit. Stagnating business. Stagnating life.
• Good offer? Some profit. Okay business. Okay life.
• Grand Slam Offer? Fantastic profit. Insane business. Freedom.
In Chapter 2 of $100M Offers, Alex Hormozi introduces the concept of the “Grand Slam Offer.” Travis Jones states that the secret to sales is to “Make people an offer so good they would feel stupid saying no.” Further, to have a business, we need to make our prospects an offer:
Offer “the goods and services you agree to provide, how you accept payment, and the terms of the agreement”
Offers start the process of customer acquisition and earning money, and they can range from nothing to a grand slam:
- No offer? No business. No life.
- Bad offer? Negative profit. No business. Miserable life.
- Decent offer? No profit. Stagnating business. Stagnating life.
- Good offer? Some profit. Okay business. Okay life.
- Grand Slam Offer? Fantastic profit. Insane business. Freedom.
There are two significant issues that most entrepreneurs face:
1. Not Enough Clients
2. Not Enough Cash or excess profit at the end of the month
$100M Offers by Alex Hormozi |
Section II: Pricing
In Section II of $100M Offers, Alex Hormozi shows you “How to charge lots of money for stuff.”
Chapter 3. The Commodity Problem
In Chapter 3 of $100M Offers, Alex Hormozi illustrates the fundamental problem with
commoditization and how Grand Slam Offers solves that. You are either growing or dying, as
maintenance is a myth. Therefore, you need to be growing with three simple things:
In Chapter 3 of $100M Offers, Alex Hormozi illustrates the fundamental problem with commoditization and how Grand Slam Offers solves that. You are either growing or dying, as maintenance is a myth. Therefore, you need to be growing with three simple things:
1. Get More Customers
2. 3. Increase their Average Purchase Value
Get Them to Buy More Times
2. Increase their average purchase value
3. Get Them to Buy More Times
The book introduces the following key business terms:
Gross Profit “the revenue minus the direct cost of servicing an ADDITIONAL customer”
Lifetime Value “the gross profit accrued over the entire lifetime of a customer”
Many businesses provide readily available commodities and compete on price, which is a race
to the bottom. However, you should sell your products based on value with a grand slam offer:
Grand Slam Offer “an offer you present to the marketplace that cannot be compared to any
other product or service available, combining an attractive promotion, an unmatchable value
proposition, a premium price, and an unbeatable guarantee with a money model (payment
terms) that allows you to get paid to get new customers . . . forever removing the cash
constraint on business growth”
This offer gets you out of the pricing war and into a category of one, which results in more
customers, at higher ticket prices, for less money. In terms of marketing, you will have:
- Gross Profit “the revenue minus the direct cost of servicing an ADDITIONAL customer”
- Lifetime Value “the gross profit accrued over the entire lifetime of a customer”
Many businesses provide readily available commodities and compete on price, which is a race to the bottom. However, you should sell your products based on value with a grand slam offer:
Grand Slam Offer “an offer you present to the marketplace that cannot be compared to any other product or service available, combining an attractive promotion, an unmatchable value proposition, a premium price, and an unbeatable guarantee with a money model (payment terms) that allows you to get paid to get new customers . . . forever removing the cash constraint on business growth”.
This offer gets you out of the pricing war and into a category of one, which results in more customers, at higher ticket prices, for less money. In terms of marketing, you will have:
1. Increased Response Rates
2. Increased Conversion
3. Premium Prices
Chapter 4. Finding The Right Market -- A Starving Crowd
In Chapter 4 of $100M Offers, Alex Hormozi focuses on finding the correct market to apply our
pricing strategies. You should avoid choosing a bad market. Instead, you can pick a great market
with demand by looking at four indicators:
1. 2. 3. 4. Massive Pain: Your prospects must have a desperate need, not want, for your offer.
Purchasing Power: Your prospects must afford or access the money needed to buy.
Easy to Target: Your audience should be in easy-to-target markets.
Growing: The market should be growing to make things move faster.
$100M Offers by Alex Hormozi |
First, start with the three primary markets resembling the core human pains: Health, Wealth,
and Relationships. Then, find a subgroup in one of these larger markets that is growing, has the
buying power, and is easy to target. Ultimately, picking a great market matters much more than
your offer strength and persuasion skill:
In Chapter 4 of $100M Offers, Alex Hormozi focuses on finding the correct market to apply our pricing strategies. You should avoid choosing a bad market. Instead, you can pick a great market with demand by looking at four indicators:
1. Massive Pain: Your prospects must have a desperate need, not want, for your offer.
2. Purchasing Power: Your prospects must afford or access the money needed to buy.
3. Easy to Target: Your audience should be in easy-to-target markets.
4. Growing: The market should be growing to make things move faster.
First, start with the three primary markets resembling the core human pains: Health, Wealth, and Relationships. Then, find a subgroup in one of these larger markets that is growing, has the buying power, and is easy to target. Ultimately, picking a great market matters much more than your offer strength and persuasion skill:
Starving Crowd (market) > Offer Strength > Persuasion Skills
Next, you need to commit to a niche until you have found a great offer. The niches will make
you more money as you can charge more for a similar product. In the process of committing,
you will try out many offers and failures. Therefore, you must be resilient, as you will eventually
succeed.
If you find a crazy niche market, take advantage of it. And if you can pair the niche with a Grand
Slam Offer, you will probably never need to work again.
Next, you need to commit to a niche until you have found a great offer. The niches will make you more money as you can charge more for a similar product. In the process of committing, you will try out many offers and failures. Therefore, you must be resilient, as you will eventually succeed.
If you find a crazy niche market, take advantage of it. And if you can pair the niche with a Grand Slam Offer, you will probably never need to work again.
Chapter 5. Pricing: Charge What Its Worth
In Chapter 5 of $100M Offers, Alex Hormozi advocates that you charge a premium as it allows
you to do things no one else can to make your clients successful.
Warren Buffet has said, “Price is what you pay. Value is what you get.” Thus, people buy to get
a deal for what they are getting (value) is worth more than what they are giving in exchange for
it (price).” When someone perceives the value dipping lower than the price, they stop buying.
Avoid lowering prices to improve the price-value gap because you will fall into a vicious cycle,
and your business will lose money and impact. Instead, you want to improve the gap by raising
your price after sufficiently increasing the value to the customer. As a result, the virtuous cycle
works for you and your business profits significantly.
$100M Offers by Alex Hormozi |
Further, you must have clients fully committed by offering a service where they must pay high
enough and take action required to achieve results or solve issues. Higher levels of investment
correlate to a higher likelihood of accomplishing the positive outcome.
$100M Offers by Alex Hormozi |
In Chapter 5 of $100M Offers, Alex Hormozi advocates that you charge a premium as it allows you to do things no one else can to make your clients successful.
Warren Buffet has said, “Price is what you pay. Value is what you get.” Thus, people buy to get a deal for what they are getting (value) is worth more than what they are giving in exchange for it (price).”
When someone perceives the value dipping lower than the price, they stop buying.
Avoid lowering prices to improve the price-value gap because you will fall into a vicious cycle, and your business will lose money and impact. Instead, you want to improve the gap by raising your price after sufficiently increasing the value to the customer. As a result, the virtuous cycle works for you and your business profits significantly.
Further, you must have clients fully committed by offering a service where they must pay high enough and take action required to achieve results or solve issues. Higher levels of investment correlate to a higher likelihood of accomplishing the positive outcome.
Section III: Value - Create Your Offer
In Section III of $100M Offers, Alex Hormozi shows you “How to make something so good
people line up to buy.”
In Section III of $100M Offers, Alex Hormozi shows you “How to make something so good people line up to buy.”
Chapter 6. The Value Equation
In Chapter 6 of $100M Offers, Alex Hormozi introduces the value equation. Most entrepreneurs
think that charging a lot is wrong, but you should “charge as much money for your products or
services as humanly possible.” However, never charge more than what they are worth.
You must understand the value to charge the most for your goods and services. Further, you
should price them much more than the cost of fulfillment. The Value Equation quantifies the
four variables that create the value for any offer:
Value is based on the perception of reality. Thus, your prospect must perceive the first two
factors increasing and the second two factors decreasing to perceive value in their mind:
1. 2. 3. 4. The Dream Outcome (Goal: Increase)
“the expression of the feelings and
experiences the prospect has envisioned in their mind; the gap between their
current reality and their dreams”
Perceived Likelihood of Achievement (Goal: Increase) the probability that the
purchase will work and achieve the result that the prospect is looking for
Perceived Time Delay Between Start and Achievement (Goal: Decrease)
“the time
between a client buying and receiving the promised benefit;” this driver consists of
long-term outcome and short-term experience
Perceived Effort & Sacrifice (Goal: Decrease) “the ancillary costs or other costs
accrued” of effort and sacrifice; supports why “done for you services” are almost
always more expensive than “do-it-yourself”
In Chapter 6 of $100M Offers, Alex Hormozi introduces the value equation. Most entrepreneurs think that charging a lot is wrong, but you should “charge as much money for your products or services as humanly possible.” However, never charge more than what they are worth.
You must understand the value to charge the most for your goods and services. Further, you should price them much more than the cost of fulfillment. The Value Equation quantifies the four variables that create the value for any offer:
Value is based on the perception of reality. Thus, your prospect must perceive the first two factors increasing and the second two factors decreasing to perceive value in their mind:
1. The Dream Outcome (Goal: Increase) “the expression of the feelings and experiences the prospect has envisioned in their mind; the gap between their current reality and their dreams”
2. Perceived Likelihood of Achievement (Goal: Increase) the probability that the purchase will work and achieve the result that the prospect is looking for
3. Perceived Time Delay Between Start and Achievement (Goal: Decrease) “the time between a client buying and receiving the promised benefit;” this driver consists of long-term outcome and short-term experience
4. Perceived Effort & Sacrifice (Goal: Decrease) “the ancillary costs or other costs accrued” of effort and sacrifice; supports why “done for you services” are almost always more expensive than “do-it-yourself”
Chapter 7. Free Goodwill
In Chapter 7, Alex Hormozi asks you to leave a review of $100M Offers if you have gotten value
so far to help reach more people.
$100M Offers by Alex Hormozi |
“People who help others (with zero expectation) experience higher levels of fulfillment, live
longer, and make more money.” And so, “if you introduce something valuable to someone,
they associate that value with you.”
In Chapter 7, Alex Hormozi asks you to leave a review of $100M Offers if you have gotten value so far to help reach more people.
“People who help others (with zero expectation) experience higher levels of fulfillment, live longer, and make more money.” And so, “if you introduce something valuable to someone, they associate that value with you.”
Chapter 8. The Thought Process
In Chapter 8 of $100M Offers, Alex Hormozi shows you the difference between convergent and
divergent problem solving:
Convergent problem solving where there are many known variables with unchanging
conditions to converge on a singular answer
• Divergent problem solving in which there are many solutions to a singular problem
with known variables, unknown variables, and dynamic conditions
Exercise: Set a timer for 2 minutes and “write down as many different uses of a brick as you can
possibly think of.”
This exercise illustrates that “every offer has building blocks, the pieces that when combined
make an offer irresistible.” You need to use divergent thinking to determine how to combine
the elements to provide value.
In Chapter 8 of $100M Offers, Alex Hormozi shows you the difference between convergent and divergent problem solving:
- Convergent problem solving where there are many known variables with unchanging conditions to converge on a singular answer
- Divergent problem solving in which there are many solutions to a singular problem with known variables, unknown variables, and dynamic conditions
Exercise: Set a timer for 2 minutes and “write down as many different uses of a brick as you can possibly think of.”
This exercise illustrates that “every offer has building blocks, the pieces that when combined make an offer irresistible.” You need to use divergent thinking to determine how to combine the elements to provide value.
Chapter 9. Creating Your Grand Slam Offer Part I: Problems & Solutions
In Chapter 9 of $100M Offers, Alex Hormozi helps you craft the problems and solutions of your
Grand Slam Offer:
Step #1: Identify Dream Outcome: When thinking about the dream outcome, you need to
determine what your customer experiences when they arrive at the destination.
Step #2: List the Obstacles Encountered: Think of all the problems that prevent them from
achieving their outcome or continually reaching it. Each problem has four negative elements
that align with the four value drivers.
Step #3: List the Obstacles as Solutions: Transform our problems into solutions by determining
what is needed to solve each problem. Then, name each of the solutions.
In Chapter 9 of $100M Offers, Alex Hormozi helps you craft the problems and solutions of your Grand Slam Offer:
Step #1: Identify Dream Outcome: When thinking about the dream outcome, you need to determine what your customer experiences when they arrive at the destination.
Step #2: List the Obstacles Encountered: Think of all the problems that prevent them from achieving their outcome or continually reaching it. Each problem has four negative elements that align with the four value drivers.
Step #3: List the Obstacles as Solutions: Transform our problems into solutions by determining what is needed to solve each problem. Then, name each of the solutions.
Chapter 10. Creating Your Grand Slam Offer Part II: Trim & Stack
In Chapter 10 of $100M Offers, Alex Hormozi helps you tactically determine what you do or
provide for your client in your Grand Slam Offer. Specifically, you need to understand trimming
and stacking by reframing with the concept of the sales to fulfillment continuum:
Sales to Fulfillment Continuum
“a continuum between ease of fulfillment and ease of sales”
to find the sweet spot of selling something well that is easy to fulfill:
$100M Offers by Alex Hormozi |
In Chapter 10 of $100M Offers, Alex Hormozi helps you tactically determine what you do or provide for your client in your Grand Slam Offer. Specifically, you need to understand trimming and stacking by reframing with the concept of the sales to fulfillment continuum:
Sales to Fulfillment Continuum “a continuum between ease of fulfillment and ease of sales” to find the sweet spot of selling something well that is easy to fulfill:
The goal is “to find a sweet spot where you sell something very well thats also easy to fulfill.”
Alex Hormozi lives by the mantra, “Create flow. Monetize flow. Then add friction:”
Create Flow: Generate demand first to validate that what you have is good.
Monetize Flow: Get the prospect to say yes to your offer.
Add Friction: Create friction in the marketing or reduce the offer for the same price.
“If this is your first Grand Slam Offer, its important to over-deliver like crazy,” which generates
cash flow. Then, invest the cash flow to create systems and optimize processes to improve
efficiency. As a result, your offer may not change, but rather the newly implemented systems
will provide the same value to clients for significantly fewer resources.
- Create Flow: Generate demand first to validate that what you have is good.
- Monetize Flow: Get the prospect to say yes to your offer.
- Add Friction: Create friction in the marketing or reduce the offer for the same price.
“If this is your first Grand Slam Offer, its important to over-deliver like crazy,” which generates cash flow. Then, invest the cash flow to create systems and optimize processes to improve efficiency. As a result, your offer may not change, but rather the newly implemented systems will provide the same value to clients for significantly fewer resources.
Finally, here are the last steps of creating the Grand Slam offer:
Step #4: Create Your Solutions Delivery Vehicles (“The How”): Think through every possibility
to solve each identified issue in exchange for money. There are several product delivery “cheat
codes” for product variation or enhancement:
1. 2. 3. 4. Attention: What level of personal attention do I want to provide?
a. One-on-one private and personalized
b. Small group intimate, small audience but not private
c. One to many large audience and not private
Effort: What level of effort is expected from them?
a. Do it Yourself (DIY) the business helps the customer figure it out on their own
b. Done with You (DWY) the business coaches the customer on how to do it
c. Done for You (DFY) the company does it for the customer
Support: If doing something live, what setting or medium do I want to deliver it in?
a. In-person or support via phone, email, text, Zoom, chat, etc.
Consumption: If doing a recording, how do I want them to consume it?
a. Audio, Video, or Written materials.
$100M Offers by Alex Hormozi |
5. 6. 7. Speed & Convenience: How quickly do we want to reply? On what days and hours?
a. All-day (24/7), Workday (9-5), Time frame (within 5 minutes, 1 hour, or 1 day)
10x Test: What would I provide if my customers paid me 10x my price (or $100,000)?
1/10th Test: How can I ensure a successful outcome if they paid me 1/10th of the price?
Step #5a: Trim Down the Possibilities: From your huge list of possibilities, determine those that
provide the highest value to the customer while having the lowest cost to the business. Remove
the high cost and low value items, followed by the low cost and low value items. The remaining
items should be (1) low cost, high value, and (2) high cost, high value.
Step #5b: Stack to Configure the Most Value: Combine the high value items together to create
the ultimate high value deliverable. This Grand Slam Offer is unique, “differentiated, and unable
to be compared to anything else in the marketplace.”
$100M Offers by Alex Hormozi |
Step #4: Create Your Solutions Delivery Vehicles (“The How”): Think through every possibility to solve each identified issue in exchange for money. There are several product delivery “cheat codes” for product variation or enhancement:
1. Attention: What level of personal attention do I want to provide?
a. One-on-one private and personalized
b. Small group intimate, small audience but not private
c. One to many large audience and not private
2. Effort: What level of effort is expected from them?
a. Do it Yourself (DIY) the business helps the customer figure it out on their own
b. Done with You (DWY) the business coaches the customer on how to do it
c. Done for You (DFY) the company does it for the customer
3. Support: If doing something live, what setting or medium do I want to deliver it in?
a. In-person or support via phone, email, text, Zoom, chat, etc.
4. Consumption: If doing a recording, how do I want them to consume it?
a. Audio, Video, or Written materials.
5. Speed & Convenience: How quickly do we want to reply? On what days and hours?
a. All-day (24/7), Workday (9-5), Time frame (within 5 minutes, 1 hour, or 1 day)
b. 10x Test: What would I provide if my customers paid me 10x my price (or $100,000)?
c. 1/10th Test: How can I ensure a successful outcome if they paid me 1/10th of the price?
Step #5a: Trim Down the Possibilities: From your huge list of possibilities, determine those that provide the highest value to the customer while having the lowest cost to the business. Remove the high cost and low value items, followed by the low cost and low value items. The remaining items should be (1) low cost, high value, and (2) high cost, high value.
Step #5b: Stack to Configure the Most Value: Combine the high value items together to create the ultimate high value deliverable. This Grand Slam Offer is unique, “differentiated, and unable to be compared to anything else in the marketplace.”
Section IV: Enhancing Your Offer
In Section IV of $100M Offers, Alex Hormozi shows you “How to make your offer so good they
feel stupid saying no.”
In Section IV of $100M Offers, Alex Hormozi shows you “How to make your offer so good they feel stupid saying no.”
Chapter 11. Scarcity, Urgency, Bonuses, Guarantees, and Naming
In Chapter 11 of $100M Offers, Alex Hormozi discusses how to enhance the offer by
understanding human psychology. Naval Ravikant has said that “Desire is a contract you make
with yourself to be unhappy until you get what you want,” as it follows that:
“People want what they cant have. People want what other people want. People want things
only a select few have access to.”
In Chapter 11 of $100M Offers, Alex Hormozi discusses how to enhance the offer by understanding human psychology. Naval Ravikant has said that “Desire is a contract you make with yourself to be unhappy until you get what you want,” as it follows that:
“People want what they cant have. People want what other people want. People want things only a select few have access to.”
Essentially, all marketing exists to influence the supply and demand curve:
Therefore, you can enhance your core offer by doing the following:
Increase demand or desire with persuasive communication
Decrease or delay satisfying the desires by selling fewer units
If you provide zero supply or desire, you will not make money and repel people. But,
conversely, if you satisfy all the demands, you will kill your golden goose and eventually not
make money.
The result is engaging in a “Delicate Dance of Desire” between supply and demand to “sell the
same products for more money than you otherwise could, and in higher volumes, than you
otherwise would (over a longer time horizon).”
$100M Offers by Alex Hormozi |
Until now, the book has focused on the internal aspects of the offer. For more on marketing,
check out the book, The 1-Page Marketing Plan (book summary) by Allan Dib. The following
chapters discuss the outside factors that position the product in your prospects mind, including
scarcity, urgency, bonuses, guarantees, and naming.
- Increase demand or desire with persuasive communication
- Decrease or delay satisfying the desires by selling fewer units
If you provide zero supply or desire, you will not make money and repel people. But, conversely, if you satisfy all the demands, you will kill your golden goose and eventually not make money.
The result is engaging in a “Delicate Dance of Desire” between supply and demand to “sell the same products for more money than you otherwise could, and in higher volumes, than you otherwise would (over a longer time horizon).”
Until now, the book has focused on the internal aspects of the offer. For more on marketing, check out the book, The 1-Page Marketing Plan (book summary) by Allan Dib. The following chapters discuss the outside factors that position the product in your prospects mind, including scarcity, urgency, bonuses, guarantees, and naming.
Chapter 12. Scarcity
In a transaction, “the person who needs the exchange less always has the upper hand.” In
Chapter 12 of $100M Offers, Alex Hormozi shows you how to “use scarcity to decrease supply
to raise prices (and indirectly increase demand through perceived exclusiveness):”
Scarcity the “fear of missing out” or the psychological lever of limiting the “supply or quantity
of products or services that are available for purchase”
Scarcity works as the “fear of loss is stronger than the desire for gain.” Therefore, so you can
influence prospects to take action and purchase your offer with the following types of scarcity:
In a transaction, “the person who needs the exchange less always has the upper hand.”
In Chapter 12 of $100M Offers, Alex Hormozi shows you how to “use scarcity to decrease supply to raise prices (and indirectly increase demand through perceived exclusiveness):”
Scarcity the “fear of missing out” or the psychological lever of limiting the “supply or quantity of products or services that are available for purchase”
Scarcity works as the “fear of loss is stronger than the desire for gain.” Therefore, so you can influence prospects to take action and purchase your offer with the following types of scarcity:
1. Limited Supply of Seats/Slots
2. Limited Supply of Bonuses
3. Never Available Again
Physical Goods: Produce limited releases of flavors, colors, designs, sizes, etc. You must sell out
consistently with each release to effectively create scarcity. Also, let everyone know that you
sold out as social proof to get everyone to value it.
Physical Goods: Produce limited releases of flavors, colors, designs, sizes, etc. You must sell out consistently with each release to effectively create scarcity. Also, let everyone know that you sold out as social proof to get everyone to value it.
Services: Limit the number of clients to cap capacity or create cadence:
1. 2. 3. Total Business Cap “only accepting X clients at this level of service (on-going)”
Growth Rate Cap “only accepting X clients per time period (on-going)”
Cohort Cap “only accepting X clients per class or cohort”
Honesty: The most ethical and easiest scarcity strategy is honesty. Simply let people know how
close you are to the cap or selling out, which creates social proof.
1. Total Business Cap “only accepting X clients at this level of service (on-going)”
2. Growth Rate Cap “only accepting X clients per time period (on-going)”
3. Cohort Cap “only accepting X clients per class or cohort”
4. Honesty: The most ethical and easiest scarcity strategy is honesty. Simply let people know how close you are to the cap or selling out, which creates social proof.
Chapter 13. Urgency
In Chapter 13 of $100M Offers, Alex Hormozi shows you how to “use urgency to increase
demand by decreasing the action threshold of a prospect.” Scarcity and urgency are frequently
used together, but “scarcity is a function of quantity, while urgency is a function of time:”
Urgency the psychological lever of limiting timing and establishing deadlines for the products
or services that are available for purchase; implement the following four methods:
1. 2. Rolling Cohorts accepting clients in a limited buying window per time period
Rolling Seasonal Urgency accepting clients during a season with a deadline to buy
$100M Offers by Alex Hormozi |
3. 4. Promotional or Pricing Urgency “using your actual offer or promotion or pricing
structure as the thing they could miss out on”
Exploding Opportunity “occasionally exposing the prospect to an arbitrage
opportunity with a ticking time clock”
In Chapter 13 of $100M Offers, Alex Hormozi shows you how to “use urgency to increase demand by decreasing the action threshold of a prospect.” Scarcity and urgency are frequently used together, but “scarcity is a function of quantity, while urgency is a function of time:”
Urgency the psychological lever of limiting timing and establishing deadlines for the products or services that are available for purchase; implement the following four methods:
1. Rolling Cohorts accepting clients in a limited buying window per time period
2. Rolling Seasonal Urgency accepting clients during a season with a deadline to buy
3. Promotional or Pricing Urgency “using your actual offer or promotion or pricing structure as the thing they could miss out on”
4. Exploding Opportunity “occasionally exposing the prospect to an arbitrage opportunity with a ticking time clock”
Chapter 14. Bonuses
In Chapter 14 of $100M Offers, Alex Hormozi shows you how to “use bonuses to increase
demand (and increase perceived exclusivity).” The main takeaway is that “a single offer is less
valuable than the same offer broken into its component parts and stacked as bonuses:”
Bonus an addition to the core offer that “increases the prospects price-to-value discrepancy
by increasing the value delivering instead of cutting the price”
The price is anchored to the core offer, and when selling 1-on-1, you should ask for the sale
first. Then, offer the bonuses to grow the discrepancy such that it becomes irresistible and
compels the prospect to buy. Additionally, there are a few keys when offering bonuses:
1. 2. 3. Always offer them a bonus.
Give each bonus a unique name with the benefit contained in the title.
Tell them (a) how it relates to their issue; (b) what it is; (c) how you discovered it or
created it; and (d) how it explicitly improves their lives or provides value.
4. 5. 6. 7. 8. 9. Prove that each bonus provides value using stats, case studies, or personal anecdotes.
Paint a vivid mental picture of their future life and the benefits of using the bonus.
Assign a price to each bonus and justify it.
Provide tools and checklists rather than additional training as they are more valuable.
Each bonus should address a specific concern or obstacle in the prospects mind.
Bonuses can solve a next or future problem before the prospect even encounters it.
In Chapter 14 of $100M Offers, Alex Hormozi shows you how to “use bonuses to increase demand (and increase perceived exclusivity).” The main takeaway is that “a single offer is less valuable than the same offer broken into its component parts and stacked as bonuses:”
Bonus an addition to the core offer that “increases the prospects price-to-value discrepancy by increasing the value delivering instead of cutting the price”
The price is anchored to the core offer, and when selling 1-on-1, you should ask for the sale first. Then, offer the bonuses to grow the discrepancy such that it becomes irresistible and compels the prospect to buy. Additionally, there are a few keys when offering bonuses:
1. Always offer them a bonus.
2. Give each bonus a unique name with the benefit contained in the title.
3. Tell them (a) how it relates to their issue; (b) what it is; (c) how you discovered it or created it; and (d) how it explicitly improves their lives or provides value.
4. Prove that each bonus provides value using stats, case studies, or personal anecdotes.
5. Paint a vivid mental picture of their future life and the benefits of using the bonus.
6. Assign a price to each bonus and justify it.
7. Provide tools and checklists rather than additional training as they are more valuable.
8. Each bonus should address a specific concern or obstacle in the prospects mind.
9. Bonuses can solve a next or future problem before the prospect even encounters it.
10. Ensure that each bonus expands the price to value discrepancy of the entire offer.
11. Enhance bonus value by adding scarcity and urgency to the bonus themselves.
Further, you can partner with other businesses to provide you with their high-value goods and
services as a part of your bonuses.” In exchange, they will get exposure to your clients for free
or provide you with additional revenue from affiliate marketing.
Further, you can partner with other businesses to provide you with their high-value goods and services as a part of your bonuses.” In exchange, they will get exposure to your clients for free or provide you with additional revenue from affiliate marketing.
Chapter 15. Guarantees
The most significant objection to any sale of a good or service is the risk that it will not work for
a prospect. In Chapter 15 of $100M Offers, Alex Hormozi shows you how to “use guarantees to
increase demand by reversing risk:
Guarantee “a formal assurance or promise, especially that certain conditions shall be fulfilled
relating to a product, service, or transaction”
$100M Offers by Alex Hormozi |
Your guarantee gets power by telling the prospect what you will do if they do not get the
promised result in this conditional statement: If you do not get X result in Y time period, we will
Z.” There are four types of guarantees:
1. 2. 3. 4. Unconditional the strongest guarantee that allows customers to pay to try the
product or service to see if they like it and get a refund if they dont like it
a. “No Questions Asked” Refund simple but risky as it holds you accountable
b. Satisfaction-Based Refund triggers when a prospect is unsatisfied with service
Conditional a guarantee with “terms and conditions;” can incorporate the key actions
someone needs to take to get the successful outcome
a. Outsized Refund additional money back attached to doing the work to qualify
b. Service provide work that is free of charge until X result is achieved
c. Modified Service grant another period Y of service or access free of charge
d. Credit-Based provide a refund in the form of a credit toward your other offers
e. Personal Service work with client one-on-one for free until X result is achieved
f. Hotel + Airfare Perks reimburse your product with hotel and airfare if no value
g. Wage-Payment pay their hourly rate if they dont get value from your session
h. Release of Service cancel the contract free of charge if they stop getting value
i. Delayed Second Payment stop 2nd payment until the first outcome is reached
j. First Outcome pay ancillary costs until they reach their first outcome
Anti-Guarantee a non-guarantee that explicitly states “all sales are final” with a
creative reason for why
Implied Guarantees a performance-based offer based on trust and transparency
a. Performance pay $X per sale, show, or milestone
b. Revenue-Share pay X% of top-line revenue or X% of revenue growth
c. Profit-Share pay X% of profit or X% of Gross Profit
d. Ratchets pay X% if over Y revenue or profit
e. Bonuses/Triggers pay X when Y event occurs
The most significant objection to any sale of a good or service is the risk that it will not work for a prospect. In Chapter 15 of $100M Offers, Alex Hormozi shows you how to “use guarantees to increase demand by reversing risk:”
Guarantee “a formal assurance or promise, especially that certain conditions shall be fulfilled relating to a product, service, or transaction
Your guarantee gets power by telling the prospect what you will do if they do not get the promised result in this conditional statement: If you do not get X result in Y time period, we will Z.” There are four types of guarantees:
1. Unconditional the strongest guarantee that allows customers to pay to try the product or service to see if they like it and get a refund if they dont like it
a. “No Questions Asked” Refund simple but risky as it holds you accountable
b. Satisfaction-Based Refund triggers when a prospect is unsatisfied with service
2. Conditional a guarantee with “terms and conditions;” can incorporate the key actions someone needs to take to get the successful outcome
3. Outsized Refund additional money back attached to doing the work to qualify
4. Service provide work that is free of charge until X result is achieved
5. Modified Service grant another period Y of service or access free of charge
6. Credit-Based provide a refund in the form of a credit toward your other offers
7. Personal Service work with client one-on-one for free until X result is achieved
8. Hotel + Airfare Perks reimburse your product with hotel and airfare if no value
9. Wage-Payment pay their hourly rate if they dont get value from your session
10. Release of Service cancel the contract free of charge if they stop getting value
11. Delayed Second Payment stop 2nd payment until the first outcome is reached
12. First Outcome pay ancillary costs until they reach their first outcome
13. Anti-Guarantee a non-guarantee that explicitly states “all sales are final” with a creative reason for why
14. Implied Guarantees a performance-based offer based on trust and transparency
15. Performance pay $X per sale, show, or milestone
16. Revenue-Share pay X% of top-line revenue or X% of revenue growth
17. Profit-Share pay X% of profit or X% of Gross Profit
18. Ratchets pay X% if over Y revenue or profit
19. Bonuses/Triggers pay X when Y event occurs
Hormozi prefers “selling service-based guarantees or setting up performance partnerships.”
Also, you can create your own one from your prospects biggest fears, pain, and obstacles.
Further, stack guarantees to show your seriousness about their outcome. Lastly, despite
guarantees being effective, people who specially buy based on them tend to be worse clients.
Further, stack guarantees to show your seriousness about their outcome. Lastly, despite guarantees being effective, people who specially buy based on them tend to be worse clients.
Chapter 16. Naming
“Over time, offers fatigue; and in local markets, they fatigue even faster.” In Chapter 16 of
$100M Offers, Alex Hormozi shows you how to “use names to re-stimulate demand and expand
awareness of your offer to your target audience.”
“We must appropriately name our offer to attract the right avatar to our business.” You can
rename your offer to get leads repeatedly using the five parts of the MAGIC formula:
• Make a Magnetic Reason Why: Start with a word or phrase that provides a strong
reason for running the promotion or presentation.
$100M Offers by Alex Hormozi |
• Announce Your Avatar: Broadcast specifically “who you are looking for and who you are
not looking for as a client.”
• Give Them a Goal: Elaborate upon the dream outcome for your prospect to achieve.
• Indicate a Time Interval: Specify the expected period for the client to achieve their
dream results.
• Complete with a Container Word: Wrap up the offer as “a bundle of lots of things put
together” with a container word.
“Over time, offers fatigue; and in local markets, they fatigue even faster.”
In Chapter 16 of $100M Offers, Alex Hormozi shows you how to “use names to re-stimulate demand and expand awareness of your offer to your target audience.”
“We must appropriately name our offer to attract the right avatar to our business.” You can rename your offer to get leads repeatedly using the five parts of the MAGIC formula:
- Make a Magnetic Reason Why: Start with a word or phrase that provides a strong reason for running the promotion or presentation.
- Announce Your Avatar: Broadcast specifically “who you are looking for and who you are not looking for as a client.”
- Give Them a Goal: Elaborate upon the dream outcome for your prospect to achieve.
- Indicate a Time Interval: Specify the expected period for the client to achieve their dream results.
- Complete with a Container Word: Wrap up the offer as “a bundle of lots of things put together” with a container word.
Note that you only need to use three to five components in naming your product or service.
This amount will allow you to distinguish yourself from the competition. Further, you can create
variations when the market offers fatigues:
1. 2. 3. 4. 5. 6. Change the creative elements or images in your adds
Change the body copy in your ads
Change the headline or the “wrapper” of your offer
Change the duration of your offer
Change the enhancer or free/discounted component of your offer
Change the monetization structure, the series of offers, and the associated price points
Section V:Execution
In Section V of $100M Offers, Alex Hormozi discusses “How to make this happen in the real
world.” Finally, after many years of ups and downs, Alex Hormozi made his first $100K in March
of 2017. “It was the beginning of the next chapter in his life as a business person and
entrepreneur,” so do not give up and keep moving forward.
This amount will allow you to distinguish yourself from the competition. Further, you can create variations when the market offers fatigues:
1. Change the creative elements or images in your adds
2. Change the body copy in your ads
3. Change the headline or the “wrapper” of your offer
4. Change the duration of your offer
5. Change the enhancer or free/discounted component of your offer
6. Change the monetization structure, the series of offers, and the associated price points
Section V: Execution
In Section V of $100M Offers, Alex Hormozi discusses “How to make this happen in the real world.”
Finally, after many years of ups and downs, Alex Hormozi made his first $100K in March of 2017. “It was the beginning of the next chapter in his life as a business person and entrepreneur,” so do not give up and keep moving forward.
END CONTENT SUMMARY

View File

@@ -19,10 +19,10 @@ Take a deep breath and work on this problem step-by-step.
You must output only a working YAML file.
"""
As Nuclei AI, your primary function is to assist users in creating Nuclei templates.Your responses should focus on generating Nuclei templates based on user requirements, incorporating elements like HTTP requests, matchers, extractors, and conditions. You are now required to always use extractors when needed to extract a value from a request and use it in a subsequent request. This includes handling cases involving dynamic data extraction and response pattern matching. Provide templates for common security vulnerabilities like SSTI, XSS, Open Redirect, SSRF, and others, utilizing complex matchers and extractors. Additionally, handle cases involving raw HTTP requests, HTTP fuzzing, unsafe HTTP, and HTTP payloads, and use correct regexes in RE2 syntax. Avoid including hostnames directly in the template paths, instead, use placeholders like {{BaseURL}}. Your expertise includes understanding and implementing matchers and extractors in Nuclei templates, especially for dynamic data extraction and response pattern matching. Your responses are focused solely on Nuclei template generation and related guidance, tailored to cybersecurity applications.
As Nuclei AI, your primary function is to assist users in creating Nuclei templates. Your responses should focus on generating Nuclei templates based on user requirements, incorporating elements like HTTP requests, matchers, extractors, and conditions. You are now required to always use extractors when needed to extract a value from a request and use it in a subsequent request. This includes handling cases involving dynamic data extraction and response pattern matching. Provide templates for common security vulnerabilities like SSTI, XSS, Open Redirect, SSRF, and others, utilizing complex matchers and extractors. Additionally, handle cases involving raw HTTP requests, HTTP fuzzing, unsafe HTTP, and HTTP payloads, and use correct regexes in RE2 syntax. Avoid including hostnames directly in the template paths, instead, use placeholders like {{BaseURL}}. Your expertise includes understanding and implementing matchers and extractors in Nuclei templates, especially for dynamic data extraction and response pattern matching. Your responses are focused solely on Nuclei template generation and related guidance, tailored to cybersecurity applications.
Notes:
When using a json extractor, use jq like syntax to extract json keys, E.g to extract the json key \"token\" you will need to use \'.token\'
When using a json extractor, use jq like syntax to extract json keys, E.g., to extract the json key \"token\" you will need to use \'.token\'
While creating headless templates remember to not mix it up with http protocol
Always read the helper functions from the documentation first before answering a query.
@@ -30,7 +30,7 @@ Remember, the most important thing is to:
Only respond with a nuclei template, nothing else, just the generated yaml nuclei template
When creating a multi step template and extracting something from a request's response, use internal: true in that extractor unless asked otherwise.
When using dsl you dont need to re-use {{}} if you are already inside a {{
When using dsl you dont need to re-use {{}} if you are already inside a {{
### What are Nuclei Templates?
Nuclei templates are the cornerstone of the Nuclei scanning engine. Nuclei templates enable precise and rapid scanning across various protocols like TCP, DNS, HTTP, and more. They are designed to send targeted requests based on specific vulnerability checks, ensuring low-to-zero false positives and efficient scanning over large networks.

View File

@@ -31,13 +31,12 @@ func NewClient() (ret *Client) {
ret.maxTokens = 4096
ret.defaultRequiredUserMessage = "Hi"
ret.models = []string{
anthropic.ModelClaude3_7SonnetLatest, anthropic.ModelClaude3_7Sonnet20250219,
anthropic.ModelClaude3_5HaikuLatest, anthropic.ModelClaude3_5Haiku20241022,
anthropic.ModelClaude3_5SonnetLatest, anthropic.ModelClaude3_5Sonnet20241022,
anthropic.ModelClaude_3_5_Sonnet_20240620, anthropic.ModelClaude3OpusLatest,
anthropic.ModelClaude_3_Opus_20240229, anthropic.ModelClaude_3_Sonnet_20240229,
anthropic.ModelClaude_3_Haiku_20240307, anthropic.ModelClaude_2_1,
anthropic.ModelClaude_2_0,
string(anthropic.ModelClaude3_7SonnetLatest), string(anthropic.ModelClaude3_7Sonnet20250219),
string(anthropic.ModelClaude3_5HaikuLatest), string(anthropic.ModelClaude3_5Haiku20241022),
string(anthropic.ModelClaude3_5SonnetLatest), string(anthropic.ModelClaude3_5Sonnet20241022),
string(anthropic.ModelClaude_3_5_Sonnet_20240620), string(anthropic.ModelClaude3OpusLatest),
string(anthropic.ModelClaude_3_Opus_20240229), string(anthropic.ModelClaude_3_Haiku_20240307),
string(anthropic.ModelClaudeOpus4_20250514), string(anthropic.ModelClaudeSonnet4_20250514),
}
return
@@ -52,18 +51,18 @@ type Client struct {
defaultRequiredUserMessage string
models []string
client *anthropic.Client
client anthropic.Client
}
func (an *Client) configure() (err error) {
if an.ApiBaseURL.Value != "" {
baseURL := an.ApiBaseURL.Value
if strings.Contains(baseURL, "-") && !strings.HasSuffix(baseURL, "/v1") {
if strings.HasSuffix(baseURL, "/") {
baseURL = strings.TrimSuffix(baseURL, "/")
}
baseURL = baseURL + "/v1"
// As of 2.0beta1, using v2 API endpoint.
// https://github.com/anthropics/anthropic-sdk-go/blob/main/CHANGELOG.md#020-beta1-2025-03-25
if strings.Contains(baseURL, "-") && !strings.HasSuffix(baseURL, "/v2") {
baseURL = strings.TrimSuffix(baseURL, "/")
baseURL = baseURL + "/v2"
}
an.client = anthropic.NewClient(
@@ -84,24 +83,27 @@ func (an *Client) SendStream(
msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string,
) (err error) {
messages := an.toMessages(msgs)
if len(messages) == 0 {
close(channel)
// No messages to send after normalization, consider this a non-error condition for streaming.
return nil
}
ctx := context.Background()
stream := an.client.Messages.NewStreaming(ctx, anthropic.MessageNewParams{
Model: anthropic.F(opts.Model),
MaxTokens: anthropic.F(int64(an.maxTokens)),
TopP: anthropic.F(opts.TopP),
Temperature: anthropic.F(opts.Temperature),
Messages: anthropic.F(messages),
Model: anthropic.Model(opts.Model),
MaxTokens: int64(an.maxTokens),
TopP: anthropic.Opt(opts.TopP),
Temperature: anthropic.Opt(opts.Temperature),
Messages: messages,
})
for stream.Next() {
event := stream.Current()
switch delta := event.Delta.(type) {
case anthropic.ContentBlockDeltaEventDelta:
if delta.Text != "" {
channel <- delta.Text
}
// directly send any non-empty delta text
if event.Delta.Text != "" {
channel <- event.Delta.Text
}
}
@@ -114,33 +116,96 @@ func (an *Client) SendStream(
func (an *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) {
messages := an.toMessages(msgs)
if len(messages) == 0 {
// No messages to send after normalization, return empty string and no error.
return "", nil
}
var message *anthropic.Message
if message, err = an.client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.F(opts.Model),
MaxTokens: anthropic.F(int64(an.maxTokens)),
TopP: anthropic.F(opts.TopP),
Temperature: anthropic.F(opts.Temperature),
Messages: anthropic.F(messages),
Model: anthropic.Model(opts.Model),
MaxTokens: int64(an.maxTokens),
TopP: anthropic.Opt(opts.TopP),
Temperature: anthropic.Opt(opts.Temperature),
Messages: messages,
}); err != nil {
return
}
if len(message.Content) == 0 {
// Model returned no content blocks.
return "", nil
}
ret = message.Content[0].Text
return
}
func (an *Client) toMessages(msgs []*goopenai.ChatCompletionMessage) (ret []anthropic.MessageParam) {
normalizedMessages := common.NormalizeMessages(msgs, an.defaultRequiredUserMessage)
// Custom normalization for Anthropic:
// - System messages become the first part of the first user message.
// - Messages must alternate user/assistant.
// - Skip empty messages.
for _, msg := range normalizedMessages {
var message anthropic.MessageParam
switch msg.Role {
case goopenai.ChatMessageRoleUser:
message = anthropic.NewUserMessage(anthropic.NewTextBlock(msg.Content))
default:
message = anthropic.NewAssistantMessage(anthropic.NewTextBlock(msg.Content))
var anthropicMessages []anthropic.MessageParam
var systemContent string
isFirstUserMessage := true
lastRoleWasUser := false
for _, msg := range msgs {
if msg.Content == "" {
continue // Skip empty messages
}
switch msg.Role {
case goopenai.ChatMessageRoleSystem:
// Accumulate system content. It will be prepended to the first user message.
if systemContent != "" {
systemContent += "\\n" + msg.Content
} else {
systemContent = msg.Content
}
case goopenai.ChatMessageRoleUser:
userContent := msg.Content
if isFirstUserMessage && systemContent != "" {
userContent = systemContent + "\\n\\n" + userContent
isFirstUserMessage = false // System content now consumed
}
if lastRoleWasUser {
// Enforce alternation: add a minimal assistant message if two user messages are consecutive.
// This shouldn't happen with current chatter.go logic but is a safeguard.
anthropicMessages = append(anthropicMessages, anthropic.NewAssistantMessage(anthropic.NewTextBlock("Okay.")))
}
anthropicMessages = append(anthropicMessages, anthropic.NewUserMessage(anthropic.NewTextBlock(userContent)))
lastRoleWasUser = true
case goopenai.ChatMessageRoleAssistant:
// If the first message is an assistant message, and we have system content,
// prepend a user message with the system content.
if isFirstUserMessage && systemContent != "" {
anthropicMessages = append(anthropicMessages, anthropic.NewUserMessage(anthropic.NewTextBlock(systemContent)))
lastRoleWasUser = true
isFirstUserMessage = false // System content now consumed
} else if !lastRoleWasUser && len(anthropicMessages) > 0 {
// Enforce alternation: add a minimal user message if two assistant messages are consecutive
// or if an assistant message is first without prior system prompt handling.
anthropicMessages = append(anthropicMessages, anthropic.NewUserMessage(anthropic.NewTextBlock(an.defaultRequiredUserMessage)))
lastRoleWasUser = true
}
anthropicMessages = append(anthropicMessages, anthropic.NewAssistantMessage(anthropic.NewTextBlock(msg.Content)))
lastRoleWasUser = false
default:
// Other roles (like 'meta') are ignored for Anthropic's message structure.
continue
}
ret = append(ret, message)
}
return
// If only system content was provided, create a user message with it.
if len(anthropicMessages) == 0 && systemContent != "" {
anthropicMessages = append(anthropicMessages, anthropic.NewUserMessage(anthropic.NewTextBlock(systemContent)))
}
return anthropicMessages
}
func (an *Client) NeedsRawMode(modelName string) bool {
return false
}

View File

@@ -41,3 +41,7 @@ func (oi *Client) ListModels() (ret []string, err error) {
ret = oi.apiDeployments
return
}
func (oi *Client) NeedsRawMode(modelName string) bool {
return false
}

View File

@@ -1,15 +0,0 @@
package deepseek
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("DeepSeek", "https://api.deepseek.com", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -1,13 +0,0 @@
package deepseek
// Test generated using Keploy
import (
"testing"
)
func TestNewClient_EmbeddedClientNotNil(t *testing.T) {
client := NewClient()
if client.Client == nil {
t.Fatalf("Expected embedded openai.Client to be non-nil, got nil")
}
}

View File

@@ -90,3 +90,7 @@ func (c *Client) Setup() error {
func (c *Client) SetupFillEnvFileContent(_ *bytes.Buffer) {
// No environment variables needed for dry run
}
func (c *Client) NeedsRawMode(modelName string) bool {
return false
}

View File

@@ -43,3 +43,7 @@ func (oi *Client) ListModels() (ret []string, err error) {
ret = oi.apiModels
return
}
func (oi *Client) NeedsRawMode(modelName string) bool {
return false
}

View File

@@ -143,6 +143,10 @@ func (o *Client) extractText(response *genai.GenerateContentResponse) (ret strin
return
}
func (o *Client) NeedsRawMode(modelName string) bool {
return false
}
func toMessages(msgs []*goopenai.ChatCompletionMessage) (systemInstruction *genai.Content, messages []genai.Part) {
if len(msgs) >= 2 {
systemInstruction = &genai.Content{

View File

@@ -1,17 +0,0 @@
package groq
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
// NewClient initializes and returns a new Groq Client.
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("Groq", "https://api.groq.com/openai/v1", nil)
return
}
// Client wraps the openai.Client to provide additional functionality specific to Groq.
type Client struct {
*openai.Client
}

View File

@@ -1,13 +0,0 @@
package groq
// Test generated using Keploy
import (
"testing"
)
func TestNewClientEmbeddedClientNotNil(t *testing.T) {
client := NewClient()
if client.Client == nil {
t.Fatalf("Expected embedded openai.Client to be non-nil, got nil")
}
}

View File

@@ -1,15 +0,0 @@
package litellm
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("LiteLLM", "http://localhost:4000", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -345,3 +345,7 @@ func (c *Client) GetEmbeddings(ctx context.Context, input string, opts *common.C
embeddings = result.Data[0].Embedding
return
}
func (c *Client) NeedsRawMode(modelName string) bool {
return false
}

View File

@@ -1,15 +0,0 @@
package mistral
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("Mistral", "https://api.mistral.ai/v1", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"net/url"
"strings"
"time"
ollamaapi "github.com/ollama/ollama/api"
@@ -15,6 +16,8 @@ import (
"github.com/danielmiessler/fabric/plugins"
)
const defaultBaseUrl = "http://localhost:11434"
func NewClient() (ret *Client) {
vendorName := "Ollama"
ret = &Client{}
@@ -26,7 +29,10 @@ func NewClient() (ret *Client) {
}
ret.ApiUrl = ret.AddSetupQuestionCustom("API URL", true,
"Enter your Ollama URL (as a reminder, it is usually http://localhost:1234/v1')")
"Enter your Ollama URL (as a reminder, it is usually http://localhost:11434')")
ret.ApiUrl.Value = defaultBaseUrl
ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", false)
ret.ApiKey.Value = ""
return
}
@@ -34,18 +40,30 @@ func NewClient() (ret *Client) {
type Client struct {
*plugins.PluginBase
ApiUrl *plugins.SetupQuestion
ApiKey *plugins.SetupQuestion
apiUrl *url.URL
client *ollamaapi.Client
}
type transport_sec struct {
underlyingTransport http.RoundTripper
ApiKey *plugins.SetupQuestion
}
func (t *transport_sec) RoundTrip(req *http.Request) (*http.Response, error) {
if t.ApiKey.Value != "" {
req.Header.Add("Authorization", "Bearer "+t.ApiKey.Value)
}
return t.underlyingTransport.RoundTrip(req)
}
func (o *Client) configure() (err error) {
if o.apiUrl, err = url.Parse(o.ApiUrl.Value); err != nil {
fmt.Printf("cannot parse URL: %s: %v\n", o.ApiUrl.Value, err)
return
}
o.client = ollamaapi.NewClient(o.apiUrl, &http.Client{Timeout: 1200000 * time.Millisecond})
o.client = ollamaapi.NewClient(o.apiUrl, &http.Client{Timeout: 1200000 * time.Millisecond, Transport: &transport_sec{underlyingTransport: http.DefaultTransport, ApiKey: o.ApiKey}})
return
}
@@ -121,3 +139,16 @@ func (o *Client) createChatRequest(msgs []*goopenai.ChatCompletionMessage, opts
}
return
}
func (o *Client) NeedsRawMode(modelName string) bool {
ollamaPrefixes := []string{
"llama3",
"llama2",
}
for _, prefix := range ollamaPrefixes {
if strings.HasPrefix(modelName, prefix) {
return true
}
}
return false
}

View File

@@ -6,12 +6,11 @@ import (
"fmt"
"io"
"log/slog"
"github.com/danielmiessler/fabric/plugins"
"strings"
"github.com/danielmiessler/fabric/common"
"github.com/samber/lo"
"github.com/sashabaranov/go-openai"
"github.com/danielmiessler/fabric/plugins"
goopenai "github.com/sashabaranov/go-openai"
)
func NewClient() (ret *Client) {
@@ -48,20 +47,20 @@ type Client struct {
*plugins.PluginBase
ApiKey *plugins.SetupQuestion
ApiBaseURL *plugins.SetupQuestion
ApiClient *openai.Client
ApiClient *goopenai.Client
}
func (o *Client) configure() (ret error) {
config := openai.DefaultConfig(o.ApiKey.Value)
config := goopenai.DefaultConfig(o.ApiKey.Value)
if o.ApiBaseURL.Value != "" {
config.BaseURL = o.ApiBaseURL.Value
}
o.ApiClient = openai.NewClientWithConfig(config)
o.ApiClient = goopenai.NewClientWithConfig(config)
return
}
func (o *Client) ListModels() (ret []string, err error) {
var models openai.ModelsList
var models goopenai.ModelsList
if models, err = o.ApiClient.ListModels(context.Background()); err != nil {
return
}
@@ -74,12 +73,12 @@ func (o *Client) ListModels() (ret []string, err error) {
}
func (o *Client) SendStream(
msgs []*openai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string,
msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string,
) (err error) {
req := o.buildChatCompletionRequest(msgs, opts)
req.Stream = true
var stream *openai.ChatCompletionStream
var stream *goopenai.ChatCompletionStream
if stream, err = o.ApiClient.CreateChatCompletionStream(context.Background(), req); err != nil {
fmt.Printf("ChatCompletionStream error: %v\n", err)
return
@@ -88,7 +87,7 @@ func (o *Client) SendStream(
defer stream.Close()
for {
var response openai.ChatCompletionStreamResponse
var response goopenai.ChatCompletionStreamResponse
if response, err = stream.Recv(); err == nil {
if len(response.Choices) > 0 {
channel <- response.Choices[0].Delta.Content
@@ -110,10 +109,10 @@ func (o *Client) SendStream(
return
}
func (o *Client) Send(ctx context.Context, msgs []*openai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) {
func (o *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) {
req := o.buildChatCompletionRequest(msgs, opts)
var resp openai.ChatCompletionResponse
var resp goopenai.ChatCompletionResponse
if resp, err = o.ApiClient.CreateChatCompletion(ctx, req); err != nil {
return
}
@@ -124,36 +123,68 @@ func (o *Client) Send(ctx context.Context, msgs []*openai.ChatCompletionMessage,
return
}
func (o *Client) NeedsRawMode(modelName string) bool {
openaiModelsPrefixes := []string{
"o1",
"o3",
"o4",
}
for _, prefix := range openaiModelsPrefixes {
if strings.HasPrefix(modelName, prefix) {
return true
}
}
return false
}
func (o *Client) buildChatCompletionRequest(
msgs []*openai.ChatCompletionMessage, opts *common.ChatOptions,
) (ret openai.ChatCompletionRequest) {
messages := lo.Map(msgs, func(message *openai.ChatCompletionMessage, _ int) openai.ChatCompletionMessage {
return *message
})
inputMsgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions,
) (ret goopenai.ChatCompletionRequest) {
// Create a new slice for messages to be sent, converting from []*Msg to []Msg.
// This also serves as a mutable copy for provider-specific modifications.
messagesForRequest := make([]goopenai.ChatCompletionMessage, len(inputMsgs))
for i, msgPtr := range inputMsgs {
messagesForRequest[i] = *msgPtr // Dereference and copy
}
// Provider-specific modification for DeepSeek:
// DeepSeek requires the last message to be a user message.
// If fabric constructs a single system message (common when a pattern includes user input),
// we change its role to user for DeepSeek.
if strings.Contains(opts.Model, "deepseek") { // Heuristic to identify DeepSeek models
if len(messagesForRequest) == 1 && messagesForRequest[0].Role == goopenai.ChatMessageRoleSystem {
messagesForRequest[0].Role = goopenai.ChatMessageRoleUser
}
// Note: This handles the most common case arising from pattern usage.
// More complex scenarios where a multi-message sequence ends in 'system'
// are not currently expected from chatter.go's BuildSession logic for OpenAI providers
// but might require further rules if they arise.
}
if opts.Raw {
ret = openai.ChatCompletionRequest{
ret = goopenai.ChatCompletionRequest{
Model: opts.Model,
Messages: messages,
Messages: messagesForRequest,
}
} else {
if opts.Seed == 0 {
ret = openai.ChatCompletionRequest{
ret = goopenai.ChatCompletionRequest{
Model: opts.Model,
Temperature: float32(opts.Temperature),
TopP: float32(opts.TopP),
PresencePenalty: float32(opts.PresencePenalty),
FrequencyPenalty: float32(opts.FrequencyPenalty),
Messages: messages,
Messages: messagesForRequest,
}
} else {
ret = openai.ChatCompletionRequest{
ret = goopenai.ChatCompletionRequest{
Model: opts.Model,
Temperature: float32(opts.Temperature),
TopP: float32(opts.TopP),
PresencePenalty: float32(opts.PresencePenalty),
FrequencyPenalty: float32(opts.FrequencyPenalty),
Messages: messages,
Messages: messagesForRequest,
Seed: &opts.Seed,
}
}

View File

@@ -0,0 +1,74 @@
package openai_compatible
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
// ProviderConfig defines the configuration for an OpenAI-compatible API provider
type ProviderConfig struct {
Name string
BaseURL string
}
// Client is the common structure for all OpenAI-compatible providers
type Client struct {
*openai.Client
}
// NewClient creates a new OpenAI-compatible client for the specified provider
func NewClient(providerConfig ProviderConfig) *Client {
client := &Client{}
client.Client = openai.NewClientCompatible(providerConfig.Name, providerConfig.BaseURL, nil)
return client
}
// ProviderMap is a map of provider name to ProviderConfig for O(1) lookup
var ProviderMap = map[string]ProviderConfig{
"Mistral": {
Name: "Mistral",
BaseURL: "https://api.mistral.ai/v1",
},
"LiteLLM": {
Name: "LiteLLM",
BaseURL: "http://localhost:4000",
},
"Groq": {
Name: "Groq",
BaseURL: "https://api.groq.com/openai/v1",
},
"GrokAI": {
Name: "GrokAI",
BaseURL: "https://api.x.ai/v1",
},
"DeepSeek": {
Name: "DeepSeek",
BaseURL: "https://api.deepseek.com",
},
"Cerebras": {
Name: "Cerebras",
BaseURL: "https://api.cerebras.ai/v1",
},
"OpenRouter": {
Name: "OpenRouter",
BaseURL: "https://openrouter.ai/api/v1",
},
"SiliconCloud": {
Name: "SiliconCloud",
BaseURL: "https://api.siliconflow.cn/v1",
},
}
// GetProviderByName returns the provider configuration for a given name with O(1) lookup
func GetProviderByName(name string) (ProviderConfig, bool) {
provider, found := ProviderMap[name]
return provider, found
}
// CreateClient creates a new client for a provider by name
func CreateClient(providerName string) (*Client, bool) {
providerConfig, found := GetProviderByName(providerName)
if !found {
return nil, false
}
return NewClient(providerConfig), true
}

View File

@@ -0,0 +1,42 @@
package openai_compatible
import (
"testing"
)
func TestCreateClient(t *testing.T) {
testCases := []struct {
name string
provider string
exists bool
}{
{
name: "Existing provider - Mistral",
provider: "Mistral",
exists: true,
},
{
name: "Existing provider - Groq",
provider: "Groq",
exists: true,
},
{
name: "Non-existent provider",
provider: "NonExistent",
exists: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
client, exists := CreateClient(tc.provider)
if exists != tc.exists {
t.Errorf("Expected exists=%v for provider %s, got %v",
tc.exists, tc.provider, exists)
}
if exists && client == nil {
t.Errorf("Expected non-nil client for provider %s", tc.provider)
}
})
}
}

View File

@@ -1,16 +0,0 @@
package openrouter
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("OpenRouter", "https://openrouter.ai/api/v1", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -1 +0,0 @@
package openrouter

View File

@@ -1,15 +0,0 @@
package siliconcloud
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("SiliconCloud", "https://api.siliconflow.cn/v1", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -14,4 +14,5 @@ type Vendor interface {
ListModels() ([]string, error)
SendStream([]*goopenai.ChatCompletionMessage, *common.ChatOptions, chan string) error
Send(context.Context, []*goopenai.ChatCompletionMessage, *common.ChatOptions) (string, error)
NeedsRawMode(modelName string) bool
}

View File

@@ -4,6 +4,8 @@ import (
"bytes"
"context"
"fmt"
"sort"
"strings"
"sync"
"github.com/danielmiessler/fabric/plugins"
@@ -95,6 +97,9 @@ func (o *VendorsManager) readModels() (err error) {
if result.err != nil {
fmt.Println(result.vendorName, result.err)
} else {
sort.Slice(result.models, func(i, j int) bool {
return strings.ToLower(result.models[i]) < strings.ToLower(result.models[j])
})
o.Models.AddGroupItems(result.vendorName, result.models...)
}
}

View File

@@ -9,5 +9,5 @@ type Storage[T any] interface {
Rename(oldName, newName string) (err error)
Save(name string, content []byte) (err error)
Load(name string) (ret []byte, err error)
ListNames() (err error)
ListNames(shellCompleteList bool) (err error)
}

View File

@@ -67,7 +67,7 @@ func (o *StorageEntity) GetNames() (ret []string, err error) {
}
func (o *StorageEntity) Delete(name string) (err error) {
if err = os.Remove(o.BuildFilePathByName(name)); err != nil {
if err = os.RemoveAll(o.BuildFilePathByName(name)); err != nil {
err = fmt.Errorf("could not delete %s: %v", name, err)
}
return
@@ -100,14 +100,16 @@ func (o *StorageEntity) Load(name string) (ret []byte, err error) {
return
}
func (o *StorageEntity) ListNames() (err error) {
func (o *StorageEntity) ListNames(shellCompleteList bool) (err error) {
var names []string
if names, err = o.GetNames(); err != nil {
return
}
if len(names) == 0 {
fmt.Printf("\nNo %v\n", o.Label)
if !shellCompleteList {
fmt.Printf("\nNo %v\n", o.Label)
}
return
}

View File

@@ -197,12 +197,13 @@ func LoadStrategy(filename string) (*Strategy, error) {
}
// ListStrategies prints available strategies
func (sm *StrategiesManager) ListStrategies() error {
func (sm *StrategiesManager) ListStrategies(shellCompleteList bool) error {
if len(sm.Strategies) == 0 {
return fmt.Errorf("no strategies found. Please run 'fabric --setup' to download strategies")
}
fmt.Print("Available Strategies:\n\n")
if !shellCompleteList {
fmt.Print("Available Strategies:\n\n")
}
// Get all strategy names for sorting
names := []string{}
for name := range sm.Strategies {
@@ -224,7 +225,11 @@ func (sm *StrategiesManager) ListStrategies() error {
formatString := "%-" + fmt.Sprintf("%d", maxNameLength+2) + "s %s\n"
for _, name := range names {
strategy := sm.Strategies[name]
fmt.Printf(formatString, strategy.Name, strategy.Description)
if shellCompleteList {
fmt.Printf("%s\n", strategy.Name)
} else {
fmt.Printf(formatString, strategy.Name, strategy.Description)
}
}
return nil

View File

@@ -117,8 +117,12 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// Store the original environment
originalEnv := cmd.Env
// Create a new command with context. This might reset Env, depending on the Go version.
cmd = exec.CommandContext(ctx, cmd.Path, cmd.Args[1:]...)
cmd.Env = cmd.Env
// Restore the environment variables explicitly
cmd.Env = originalEnv
fileConfig := ext.GetFileConfig()
if fileConfig == nil {

View File

@@ -8,49 +8,39 @@ import (
)
func main() {
// Define command line flags
// Command line flags
maxDepth := flag.Int("depth", 3, "Maximum directory depth to scan")
ignorePatterns := flag.String("ignore", ".git,node_modules,vendor", "Comma-separated patterns to ignore")
outputFile := flag.String("out", "", "Output file (default: stdout)")
showHelp := flag.Bool("help", false, "Show help message")
// Parse command line flags
flag.Usage = printUsage
flag.Parse()
// Show help if requested or no arguments provided
if *showHelp || flag.NArg() < 1 {
// Require exactly two positional arguments: directory and instructions
if flag.NArg() != 2 {
printUsage()
os.Exit(0)
os.Exit(1)
}
// Get directory and instructions from positional arguments
directory := flag.Arg(0)
instructions := ""
if flag.NArg() > 1 {
instructions = flag.Arg(1)
}
instructions := flag.Arg(1)
// Validate directory
if _, err := os.Stat(directory); os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "Error: Directory '%s' does not exist\n", directory)
if info, err := os.Stat(directory); err != nil || !info.IsDir() {
fmt.Fprintf(os.Stderr, "Error: Directory '%s' does not exist or is not a directory\n", directory)
os.Exit(1)
}
// Parse ignore patterns
ignoreList := ParseIgnorePatterns(*ignorePatterns)
// Scan directory and generate JSON
var jsonData []byte
var err error
jsonData, err = ScanDirectory(directory, *maxDepth, instructions, ignoreList)
// Parse ignore patterns and scan directory
jsonData, err := ScanDirectory(directory, *maxDepth, instructions, strings.Split(*ignorePatterns, ","))
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
fmt.Fprintf(os.Stderr, "Error scanning directory: %v\n", err)
os.Exit(1)
}
// Write output
// Output result
if *outputFile != "" {
if err := os.WriteFile(*outputFile, jsonData, 0644); err != nil {
fmt.Fprintf(os.Stderr, "Error writing to file: %v\n", err)
fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err)
os.Exit(1)
}
} else {
@@ -58,30 +48,18 @@ func main() {
}
}
// ParseIgnorePatterns converts a comma-separated string of patterns to a slice
func ParseIgnorePatterns(patterns string) []string {
if patterns == "" {
return nil
}
return strings.Split(patterns, ",")
}
func printUsage() {
fmt.Println(`code_helper - Code project scanner for use with Fabric AI
fmt.Fprintf(os.Stderr, `code_helper - Code project scanner for use with Fabric AI
Usage:
code_helper [options] <directory> [instructions]
code_helper [options] <directory> <instructions>
Examples:
# Scan current directory with instructions
code_helper . "Add input validation to all user inputs"
# Scan specific directory with depth limit
code_helper -depth 4 ./my-project "Implement error handling"
# Output to file instead of stdout
code_helper -out project.json ./src "Fix security issues"
Options:`)
Options:
`)
flag.PrintDefaults()
}

View File

@@ -47,7 +47,7 @@ func (o *Defaults) Setup() (err error) {
return
}
vendorsModels.Print()
vendorsModels.Print(false)
if err = o.Ask(o.Name); err != nil {
return

27
restapi/auth.go Normal file
View File

@@ -0,0 +1,27 @@
package restapi
import (
"net/http"
"github.com/gin-gonic/gin"
)
const APIKeyHeader = "X-API-Key"
func APIKeyMiddleware(apiKey string) gin.HandlerFunc {
return func(c *gin.Context) {
headerApiKey := c.GetHeader(APIKeyHeader)
if headerApiKey == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing API Key"})
return
}
if headerApiKey != apiKey {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Wrong API Key"})
return
}
c.Next()
}
}

View File

@@ -3,8 +3,11 @@ package restapi
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"strings"
goopenai "github.com/sashabaranov/go-openai"
@@ -21,15 +24,17 @@ type ChatHandler struct {
}
type PromptRequest struct {
UserInput string `json:"userInput"`
Vendor string `json:"vendor"`
Model string `json:"model"`
ContextName string `json:"contextName"`
PatternName string `json:"patternName"`
UserInput string `json:"userInput"`
Vendor string `json:"vendor"`
Model string `json:"model"`
ContextName string `json:"contextName"`
PatternName string `json:"patternName"`
StrategyName string `json:"strategyName"` // Optional strategy name
}
type ChatRequest struct {
Prompts []PromptRequest `json:"prompts"`
Language string `json:"language"` // Add Language field to bind from request
common.ChatOptions // Embed the ChatOptions from common package
}
@@ -60,7 +65,8 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
return
}
log.Printf("Received chat request with %d prompts", len(request.Prompts))
// Add log to check received language field
log.Printf("Received chat request - Language: '%s', Prompts: %d", request.Language, len(request.Prompts))
// Set headers for SSE
c.Writer.Header().Set("Content-Type", "text/readystream")
@@ -80,13 +86,25 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
log.Printf("Processing prompt %d: Model=%s Pattern=%s Context=%s",
i+1, prompt.Model, prompt.PatternName, prompt.ContextName)
// Create chat channel for streaming
streamChan := make(chan string)
// Start chat processing in goroutine
go func(p PromptRequest) {
defer close(streamChan)
// Load and prepend strategy prompt if strategyName is set
if p.StrategyName != "" {
strategyFile := filepath.Join(os.Getenv("HOME"), ".config", "fabric", "strategies", p.StrategyName+".json")
data, err := ioutil.ReadFile(strategyFile)
if err == nil {
var s struct {
Prompt string `json:"prompt"`
}
if err := json.Unmarshal(data, &s); err == nil && s.Prompt != "" {
p.UserInput = s.Prompt + "\n" + p.UserInput
}
}
}
chatter, err := h.registry.GetChatter(p.Model, 2048, "", false, false)
if err != nil {
log.Printf("Error creating chatter: %v", err)
@@ -94,6 +112,7 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
return
}
// Pass the language received in the initial request to the common.ChatRequest
chatReq := &common.ChatRequest{
Message: &goopenai.ChatCompletionMessage{
Role: "user",
@@ -101,6 +120,7 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
},
PatternName: p.PatternName,
ContextName: p.ContextName,
Language: request.Language, // Pass the language field
}
opts := &common.ChatOptions{
@@ -124,7 +144,6 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
return
}
// Get the last message from the session
lastMsg := session.GetLastMessage()
if lastMsg != nil {
streamChan <- lastMsg.Content
@@ -134,37 +153,32 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
}
}(prompt)
// Read from streamChan and write to client
for content := range streamChan {
select {
case <-clientGone:
return
default:
var response StreamResponse
if strings.HasPrefix(content, "Error:") {
response := StreamResponse{
response = StreamResponse{
Type: "error",
Format: "plain",
Content: content,
}
if err := writeSSEResponse(c.Writer, response); err != nil {
log.Printf("Error writing error response: %v", err)
return
}
} else {
response := StreamResponse{
response = StreamResponse{
Type: "content",
Format: detectFormat(content),
Content: content,
}
if err := writeSSEResponse(c.Writer, response); err != nil {
log.Printf("Error writing content response: %v", err)
return
}
}
if err := writeSSEResponse(c.Writer, response); err != nil {
log.Printf("Error writing response: %v", err)
return
}
}
}
// Signal completion of this prompt
completeResponse := StreamResponse{
Type: "complete",
Format: "plain",
@@ -192,26 +206,6 @@ func writeSSEResponse(w gin.ResponseWriter, response StreamResponse) error {
return nil
}
/*
func detectFormat(content string) string {
if strings.HasPrefix(content, "graph TD") ||
strings.HasPrefix(content, "gantt") ||
strings.HasPrefix(content, "flowchart") ||
strings.HasPrefix(content, "sequenceDiagram") ||
strings.HasPrefix(content, "classDiagram") ||
strings.HasPrefix(content, "stateDiagram") {
return "mermaid"
}
if strings.Contains(content, "```") ||
strings.Contains(content, "#") ||
strings.Contains(content, "*") ||
strings.Contains(content, "_") ||
strings.Contains(content, "-") {
return "markdown"
}
return "plain"
}
*/
func detectFormat(content string) string {
if strings.HasPrefix(content, "graph TD") ||
strings.HasPrefix(content, "gantt") ||

View File

@@ -45,6 +45,7 @@ func (h *ConfigHandler) GetConfig(c *gin.Context) {
"openrouter": "",
"silicon": "",
"deepseek": "",
"grokai": "",
})
return
}
@@ -65,6 +66,7 @@ func (h *ConfigHandler) GetConfig(c *gin.Context) {
"openrouter": os.Getenv("OPENROUTER_API_KEY"),
"silicon": os.Getenv("SILICON_API_KEY"),
"deepseek": os.Getenv("DEEPSEEK_API_KEY"),
"grokai": os.Getenv("GROKAI_API_KEY"),
"lmstudio": os.Getenv("LM_STUDIO_API_BASE_URL"),
}
@@ -87,6 +89,7 @@ func (h *ConfigHandler) UpdateConfig(c *gin.Context) {
OpenRouterApiKey string `json:"openrouter_api_key"`
SiliconApiKey string `json:"silicon_api_key"`
DeepSeekApiKey string `json:"deepseek_api_key"`
GrokaiApiKey string `json:"grokai_api_key"`
LMStudioURL string `json:"lm_studio_base_url"`
}
@@ -105,6 +108,7 @@ func (h *ConfigHandler) UpdateConfig(c *gin.Context) {
"OPENROUTER_API_KEY": config.OpenRouterApiKey,
"SILICON_API_KEY": config.SiliconApiKey,
"DEEPSEEK_API_KEY": config.DeepSeekApiKey,
"GROKAI_API_KEY": config.GrokaiApiKey,
"LM_STUDIO_API_BASE_URL": config.LMStudioURL,
}

View File

@@ -1,17 +1,25 @@
package restapi
import (
"log/slog"
"github.com/danielmiessler/fabric/core"
"github.com/gin-gonic/gin"
)
func Serve(registry *core.PluginRegistry, address string) (err error) {
func Serve(registry *core.PluginRegistry, address string, apiKey string) (err error) {
r := gin.New()
// Middleware
r.Use(gin.Logger())
r.Use(gin.Recovery())
if apiKey != "" {
r.Use(APIKeyMiddleware(apiKey))
} else {
slog.Warn("Starting REST API server without API key authentication. This may pose security risks.")
}
// Register routes
fabricDb := registry.Db
NewPatternsHandler(r, fabricDb.Patterns)
@@ -20,6 +28,7 @@ func Serve(registry *core.PluginRegistry, address string) (err error) {
NewChatHandler(r, registry, fabricDb)
NewConfigHandler(r, fabricDb)
NewModelsHandler(r, registry.VendorManager)
NewStrategiesHandler(r)
// Start server
err = r.Run(address)

62
restapi/strategies.go Normal file
View File

@@ -0,0 +1,62 @@
package restapi
import (
"encoding/json"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
)
// StrategyMeta represents the minimal info about a strategy
type StrategyMeta struct {
Name string `json:"name"`
Description string `json:"description"`
Prompt string `json:"prompt"`
}
// NewStrategiesHandler registers the /strategies GET endpoint
func NewStrategiesHandler(r *gin.Engine) {
r.GET("/strategies", func(c *gin.Context) {
strategiesDir := filepath.Join(os.Getenv("HOME"), ".config", "fabric", "strategies")
files, err := ioutil.ReadDir(strategiesDir)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read strategies directory"})
return
}
var strategies []StrategyMeta
for _, file := range files {
if file.IsDir() || filepath.Ext(file.Name()) != ".json" {
continue
}
fullPath := filepath.Join(strategiesDir, file.Name())
data, err := ioutil.ReadFile(fullPath)
if err != nil {
continue
}
var s struct {
Description string `json:"description"`
Prompt string `json:"prompt"`
}
if err := json.Unmarshal(data, &s); err != nil {
continue
}
strategies = append(strategies, StrategyMeta{
Name: strings.TrimSuffix(file.Name(), ".json"),
Description: s.Description,
Prompt: s.Prompt,
})
}
c.JSON(http.StatusOK, strategies)
})
}

View File

@@ -1 +0,0 @@
(echo "beginning of content input" ; f -u https://danielmiessler.com/p/framing-is-everything ; echo "end of content input"; echo "beginning of AI instructions (prompt)"; cat ~/.config/fabric/patterns/extract_insights/system.md; echo "endof AI instructions (prompt)" ; echo "beginning of AI output" ; f -u https://danielmiessler.com/p/framing-is-everything | f -p extract_insights -m gpt-3.5-turbo; echo "end of AI output. Now you should have all three." ) | f -rp rate_ai_result -m o1-preview-2024-09-12

View File

@@ -1,60 +0,0 @@
# Rate AI Result
This is an example of a Fabric Stitch, which is a chained Fabric command that pipes Fabric results into each other to achieve a result. So it's multiple Patterns…*stitched* together.
## Problem
The problem we're trying to solve with this Stitch is not being able to tell how smart given AI models are. I want to be able to rate their output vs. the output from a different model with the same instructions.
## Solution
What `rate_ai_result` does is run a result using AI 1, and then rate it with AI 2.
## Functionality
`rate_ai_result` accomplishes that like so:
1. Get the input that will be operated on by an AI.
2. Get the instruction/pattern/prompt that will be used by the AI.
3. Get the result of the instructions running against the AI.
4. Combine all three of those together as the input to another Fabric call.
4. Send that combined input to the most advanced model you have available to assess the quality of the AI result.
```
(echo "beginning of content input" ; f -u https://danielmiessler.com/p/framing-is-everything ; echo "end ofcontent input"; echo "beginning of AI instructions (prompt)"; cat ~/.config/fabric/patterns/extract_insights/system.md; echo "end of AI instructions (prompt)" ; echo "beginning of AI output" ; f -u https://danielmiessler.com/p/framing-is-everything | f -p extract_insights -m gpt-3.5-turbo ; echo "end of AI output. Now you should have all three." ) | f -rp rate_ai_result -m o1-preview-2024-09-12
```
In this case we're taking:
* A blog post as the input
* Getting the content of the extract_insights pattern
* Capturing the output of extract_insights on the blog post using `gpt-3.5-turbo`
* Sending all of that to `o1-preview` using the `rate_ai_result` prompt
NOTE: `rate_ai_result` is both a Pattern name and the name of this Stitch.
## Output
The `rate_ai_result` Pattern is designed to judge the output of another AI on a human sophistication scale that roughly maps to educational and world-state achievement, with the assumption that higher stages require higher cognitive ability as well. These are:
- Superhuman
- Best humans in the world
- Ph.D
- Masters
- Bachelors
- High School
- Partially Educated
- Uneducated
## How to run it
To run it, just execute the code in the `rate_ai_result` file in this repository. And adjust the components as desired to change the input, the AI you're testing, and the AI you're using to judge.
### Blog Post
Here's a full blog post describing in even more detail.
[Using the Smartest AI to Rate Other AI](https://danielmiessler.com/p/using-the-smartest-ai-to-rate-other-ai)
#### Credit
Created by Daniel Miessler on November 7th, 2024.

4
strategies/aot.json Normal file
View File

@@ -0,0 +1,4 @@
{
"description": "Atom-of-Thought (AoT) Prompting",
"prompt": "To solve this problem, break it down into the smallest independent 'atomic' sub-problems. For each atomic sub-problem: 1. Label it as 'Atom X: [brief description]' 2. Solve that specific subproblem completely 3. Make sure each atom can be solved independently. After solving all atomic sub-problems, provide a synthesis that combines them into a final answer. Return the final answer in the required format."
}

View File

@@ -1,3 +1,3 @@
package main
var version = "v1.4.165"
var version = "v1.4.192"

View File

@@ -3,7 +3,7 @@
This is a web app for Fabric. It was built using [Svelte](https://svelte.dev/), [SkeletonUI](https://skeleton.dev/), and [Mdsvex](https://mdsvex.pngwn.io/).
The goal of this app is to not only provide a user interface for Fabric, but also a 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.
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](/fabric-png.png)
@@ -17,7 +17,7 @@ When creating new posts make sure to include a date, description, tags, and alia
You can include images, tags to other articles, code blocks, and more all within your markdown files.
### If you choose to use Obsidian along side ths app
### If you choose to use Obsidian alongside this app
You can design and order your vault however you like, though a `posts` folder should be kept in your vault to house any articles you'd like to post.

View File

@@ -1,4 +1,4 @@
This Cummulative PR adds several Web UI and functionality improvements to make pattern selection more intuitive with the addition of pattern descriptions, ability to save favorite patterns, a Pattern TAG system, powerful multilingual capabilities, PDF-to-markdown functionnalities, a help reference section, more robust Youtube processing and a variety of other ui improvements.
This Cumulative PR adds several Web UI and functionality improvements to make pattern selection more intuitive with the addition of pattern descriptions, ability to save favorite patterns, a Pattern TAG system, powerful multilingual capabilities, PDF-to-markdown functionalities, a help reference section, more robust Youtube processing and a variety of other ui improvements.
## 🎥 Demo Video
https://youtu.be/bhwtWXoMASA
@@ -35,6 +35,11 @@ https://youtu.be/fcVitd4Kb98
The tag filtering system has been deeply integrated into the Pattern Selection interface through several UI enhancements:
### 5. Strategy flags
- strategies are fetch from .config/fabric/strategies for server processing
- for gui, they are fetched from static/strategies
1. **Dual-Position Tag Panel**
- Sliding panel positioned to the right of pattern modal
- Dynamic toggle button that adapts position and text based on panel state

View File

@@ -305,10 +305,11 @@ async function readFileContent(file: File): Promise<string> {
}
}
// Centralized language instruction logic in ChatService.ts; YouTube flow now passes plain transcript and system prompt
async function processYouTubeURL(input: string) {
console.log('\n=== YouTube Flow Start ===');
const originalLanguage = get(languageStore);
try {
// Add processing message first
messageStore.update(messages => [...messages, {
@@ -316,16 +317,11 @@ async function readFileContent(file: File): Promise<string> {
content: 'Processing YouTube video...',
format: 'loading'
}]);
// Get transcript but don't display it
const { transcript } = await getTranscript(input);
// Log system prompt BEFORE createChatRequest
console.log('System prompt BEFORE createChatRequest in YouTube flow:', $systemPrompt);
// Log system prompt BEFORE streamChat
console.log(`System prompt BEFORE streamChat in YouTube flow: ${$systemPrompt}`);
// Pass plain transcript and system prompt; ChatService will handle language instruction
const stream = await chatService.streamChat(transcript, $systemPrompt);
await chatService.processStream(
stream,
@@ -447,21 +443,16 @@ async function readFileContent(file: File): Promise<string> {
newMessages.splice(loadingIndex, 1);
}
// Add or update the assistant message
const assistantIndex = newMessages.findIndex(m => m.role === 'assistant');
if (assistantIndex !== -1) {
newMessages[assistantIndex].content = content;
newMessages[assistantIndex].format = response?.format;
} else {
newMessages.push({
role: 'assistant',
content,
format: response?.format
});
}
// Always append a new assistant message
newMessages.push({
role: 'assistant',
content,
format: response?.format
});
return newMessages;
});
},
(error) => {
// Make sure to remove loading message on error
messageStore.update(messages =>

View File

@@ -4,6 +4,8 @@
import ModelConfig from "./ModelConfig.svelte";
import { Select } from "$lib/components/ui/select";
import { languageStore } from '$lib/store/language-store';
import { strategies, selectedStrategy, fetchStrategies } from '$lib/store/strategy-store';
import { onMount } from 'svelte';
const languages = [
{ code: '', name: 'Default Language' },
@@ -12,8 +14,13 @@
{ code: 'es', name: 'Spanish' },
{ code: 'de', name: 'German' },
{ code: 'zh', name: 'Chinese' },
{ code: 'ja', name: 'Japanese' }
{ code: 'ja', name: 'Japanese' },
{ code: 'it', name: 'Italian' }
];
onMount(() => {
fetchStrategies();
});
</script>
<div class="flex gap-4">
@@ -35,6 +42,17 @@
{/each}
</Select>
</div>
<div>
<Select
bind:value={$selectedStrategy}
class="bg-primary-800/30 border-none hover:bg-primary-800/40 transition-colors"
>
<option value="">None</option>
{#each $strategies as strategy}
<option value={strategy.name}>{strategy.name} - {strategy.description}</option>
{/each}
</Select>
</div>
</div>
<!-- Right side - Model Config -->

View File

@@ -15,23 +15,31 @@
});
// Watch selectedPreset changes
$: if (selectedPreset) {
console.log('Pattern selected from dropdown:', selectedPreset);
// Always call selectPattern when the dropdown value changes.
// The patternAPI.selectPattern function handles empty strings correctly.
$: {
// Log the change regardless of the value
console.log('Dropdown selection changed to:', selectedPreset);
try {
// Call the function to select the pattern (or reset if selectedPreset is empty)
patternAPI.selectPattern(selectedPreset);
// Verify the selection
// Optional: Keep verification logs if helpful for debugging
const currentSystemPrompt = get(systemPrompt);
const currentPattern = get(selectedPatternName);
console.log('After dropdown selection - Pattern:', currentPattern);
console.log('After dropdown selection - System Prompt length:', currentSystemPrompt?.length);
if (!currentPattern || !currentSystemPrompt) {
console.error('Pattern selection verification failed:');
console.error('- Selected Pattern:', currentPattern);
console.error('- System Prompt:', currentSystemPrompt);
}
// Optional: Refine verification logic if needed
// For example, only log error if a pattern was expected but not set
// if (selectedPreset && (!currentPattern || !currentSystemPrompt)) {
// console.error('Pattern selection verification failed:');
// console.error('- Selected Pattern:', currentPattern);
// console.error('- System Prompt:', currentSystemPrompt);
// }
} catch (error) {
console.error('Error in pattern selection:', error);
// Log any errors during the pattern selection process
console.error('Error processing pattern selection:', error);
}
}

View File

@@ -6,7 +6,8 @@ export interface ChatPrompt {
userInput: string;
systemPrompt: string;
model: string;
patternName: string;
patternName?: string;
strategyName?: string; // Optional strategy name to prepend strategy prompt
}
export interface ChatConfig {
@@ -23,6 +24,7 @@ export interface ChatRequest {
top_p: number;
frequency_penalty: number;
presence_penalty: number;
language?: string;
}
export interface Message {

View File

@@ -10,6 +10,7 @@ import { systemPrompt, selectedPatternName } from '$lib/store/pattern-store';
import { chatConfig } from '$lib/store/chat-config';
import { messageStore } from '$lib/store/chat-store';
import { languageStore } from '$lib/store/language-store';
import { selectedStrategy } from '$lib/store/strategy-store';
class LanguageValidator {
constructor(private targetLanguage: string) {}
@@ -47,6 +48,8 @@ export class ChatService {
promptCount: request.prompts?.length,
messageCount: request.messages?.length
});
// NEW: Log the full payload before sending to backend
console.log('Final ChatRequest payload:', JSON.stringify(request, null, 2));
const response = await fetch('/api/chat', {
method: 'POST',
@@ -179,7 +182,8 @@ export class ChatService {
userInput: finalUserInput,
systemPrompt: finalSystemPrompt,
model: config.model,
patternName: get(selectedPatternName)
patternName: get(selectedPatternName),
strategyName: get(selectedStrategy) // Add selected strategy to prompt
};
}
@@ -191,10 +195,12 @@ export class ChatService {
public async createChatRequest(userInput: string, systemPromptText?: string, isPattern: boolean = false): Promise<ChatRequest> {
const prompt = this.createChatPrompt(userInput, systemPromptText);
const config = get(chatConfig);
const language = get(languageStore);
return {
prompts: [prompt],
messages: [],
language: language, // Add language at the top level for backend compatibility
...config
};
}

View File

@@ -0,0 +1,32 @@
import { writable } from 'svelte/store';
/**
* List of available strategies fetched from backend.
* Each strategy has a name and description.
*/
export const strategies = writable<Array<{ name: string; description: string }>>([]);
/**
* Currently selected strategy name.
* Default is empty string meaning "None".
*/
export const selectedStrategy = writable<string>("");
/**
* Fetches available strategies from the backend `/strategies` endpoint.
* Populates the `strategies` store.
*/
export async function fetchStrategies() {
try {
const response = await fetch('/strategies/strategies.json');
if (!response.ok) {
console.error('Failed to fetch strategies:', response.statusText);
return;
}
const data = await response.json();
// Expecting an array of { name, description }
strategies.set(data);
} catch (error) {
console.error('Error fetching strategies:', error);
}
}

View File

@@ -61,16 +61,16 @@ export const POST: RequestHandler = async ({ request }) => {
language: body.language
});
// Ensure language instruction is present
if (body.prompts?.[0] && body.language && body.language !== 'en') {
const languageInstruction = `. Please use the language '${body.language}' for the output.`;
if (!body.prompts[0].userInput?.includes(languageInstruction)) {
body.prompts[0].userInput = (body.prompts[0].userInput || '') + languageInstruction;
}
}
// Removed redundant language instruction logic; Go backend handles this
// if (body.prompts?.[0] && body.language && body.language !== 'en') {
// const languageInstruction = `. Please use the language '${body.language}' for the output.`;
// if (!body.prompts[0].userInput?.includes(languageInstruction)) {
// body.prompts[0].userInput = (body.prompts[0].userInput || '') + languageInstruction;
// }
// }
console.log('2. Language analysis:', {
input: body.prompts?.[0]?.userInput?.substring(0, 100),
input: body.prompts?.[0]?.userInput?.substring(0, 100), // Note: This input no longer has the instruction appended here
hasLanguageInstruction: body.prompts?.[0]?.userInput?.includes('language'),
containsFr: body.prompts?.[0]?.userInput?.includes('fr'),
containsEn: body.prompts?.[0]?.userInput?.includes('en'),

View File

@@ -1693,8 +1693,87 @@
},
{
"patternName": "extract_wisdom_short",
"description": "Extract condensed insightful ideas and recommendations focusing on life wisdom.",
"tags": [
"EXTRACT",
"WISDOM",
"SELF"
]
},
{
"patternName": "analyze_bill",
"description": "Analyze a legislative bill and implications.",
"tags": [
"ANALYSIS",
"BILL"
]
},
{
"patternName": "analyze_bill_short",
"description": "Consended - Analyze a legislative bill and implications.",
"tags": [
"ANALYSIS",
"BILL"
]
},
{
"patternName": "create_coding_feature",
"description": "[Description pending]",
"tags": []
"tags": [
"DEVELOPMENT"
]
},
{
"patternName": "create_excalidraw_visualization",
"description": "Create visualizations using Excalidraw.",
"tags": [
"VISUALIZATION"
]
},
{
"patternName": "create_flash_cards",
"description": "Generate flashcards for key concepts and definitions.",
"tags": [
"LEARNING"
]
},
{
"patternName": "create_loe_document",
"description": "Create detailed Level of Effort (LOE) estimation documents.",
"tags": [
"DEVELOPMENT",
"BUSINESS"
]
},
{
"patternName": "extract_domains",
"description": "Extract key content and source.",
"tags": [
"EXTRACT",
"ANALYSIS"
]
},
{
"patternName": "extract_main_activities",
"description": "Extract and list main events from transcripts.",
"tags": [
"EXTRACT",
"ANALYSIS"
]
},
{
"patternName": "find_female_life_partner",
"description": "Clarify and summarize partner criteria in direct language.",
"tags": [
"SELF"
]
},
{
"patternName": "youtube_summary",
"description": "Summarize YouTube videos with key points and timestamps.",
"tags": [
"SUMMARIZE"
]
}
]
}

View File

@@ -0,0 +1,11 @@
[
{ "name": "aot", "description": "Atom-of-Thought (AoT)" },
{ "name": "cod", "description": "Chain-of-Draft (CoD)" },
{ "name": "cot", "description": "Chain-of-Thought (CoT) Prompting" },
{ "name": "ltm", "description": "Least-to-Most Prompting" },
{ "name": "reflexion", "description": "Reflexion Prompting" },
{ "name": "self-consistent", "description": "Self-Consistency Prompting" },
{ "name": "self-refine", "description": "Self-Refinement" },
{ "name": "standard", "description": "Standard Prompting" },
{ "name": "tot", "description": "Tree-of-Thoughts (ToT)" }
]