{"owner":"danielmiessler","repo":"Fabric","hasSkills":true,"totalSkillsCount":12,"totalTokensCount":6252,"categories":["anthropic-skill","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":["internal/plugins/template/file.md","internal/plugins/template/datetime.md","internal/plugins/template/fetch.md","internal/plugins/template/sys.md","internal/plugins/template/README.md","internal/plugins/template/Examples/security-report.yaml","internal/plugins/template/Examples/openai.yaml","internal/plugins/template/Examples/README.md","internal/plugins/template/Examples/remote-security-report.yaml","internal/plugins/template/Examples/sqlite3_demo.yaml","internal/plugins/template/Examples/test_pattern.md","internal/plugins/template/Examples/word-generator.yaml"],"skills":{"internal/plugins/template/file.md":"# File Plugin Tests\n\nSimple test file for validating file plugin functionality.\n\nEach path is relative to the working directory of the fabric process.\n\n## Basic File Operations\n\n```\nRead File:\n{{plugin:file:read:docs/file.txt}}\n\nLast 5 Lines:\n{{plugin:file:tail:logs/app.log|5}}\n\nCheck Existence:\n{{plugin:file:exists:docs/file.txt}}\n\nGet Size:\n{{plugin:file:size:docs/file.txt}}\n\nLast Modified:\n{{plugin:file:modified:docs/file.txt}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation:\n{{plugin:file:invalid:docs/file.txt}}\n\nNon-existent File:\n{{plugin:file:read:docs/nonexistent.txt}}\n\nPath Traversal Attempt:\n{{plugin:file:read:../../../etc/passwd}}\n\nAbsolute Path:\n{{plugin:file:read:/path/to/file.txt}}\n\nHome Directory Path:\n{{plugin:file:read:~/file.txt}}\n\nInvalid Tail Format:\n{{plugin:file:tail:docs/file.txt}}\n\nLarge File:\n{{plugin:file:read:data/huge.iso}}\n```\n\n## Security Considerations\n\n- The plugin rejects an absolute path, a path that starts with `~`, and a path that contains `..`\n- A symbolic link must be relative and must stay in the working directory\n- The plugin can read all files in the working directory and its subdirectories. Run `fabric` in a directory that has only the files that patterns can read\n- The REST server (`fabric --serve`) does not run the file plugin\n- Be aware of file size limits (1MB max)\n- All paths are cleaned and normalized\n","internal/plugins/template/datetime.md":"# DateTime Plugin Tests\n\nSimple test file for validating datetime plugin functionality.\n\n## Basic Time Operations\n\n```\nCurrent Time: {{plugin:datetime:now}}\nTime Only: {{plugin:datetime:time}}\nUnix Timestamp: {{plugin:datetime:unix}}\nHour Start: {{plugin:datetime:startofhour}}\nHour End: {{plugin:datetime:endofhour}}\n```\n\n## Date Operations\n\n```\nToday: {{plugin:datetime:today}}\nFull Date: {{plugin:datetime:full}}\nCurrent Month: {{plugin:datetime:month}}\nCurrent Year: {{plugin:datetime:year}}\n```\n\n## Period Operations\n\n```\nWeek Start: {{plugin:datetime:startofweek}}\nWeek End: {{plugin:datetime:endofweek}}\nMonth Start: {{plugin:datetime:startofmonth}}\nMonth End: {{plugin:datetime:endofmonth}}\n```\n\n## Relative Time/Date\n\n```\n2 Hours Ahead: {{plugin:datetime:rel:2h}}\n1 Day Ago: {{plugin:datetime:rel:-1d}}\nNext Week: {{plugin:datetime:rel:1w}}\nLast Month: {{plugin:datetime:rel:-1m}}\nNext Year: {{plugin:datetime:rel:1y}}\n```","internal/plugins/template/fetch.md":"# Fetch Plugin Tests\n\nSimple test file for validating fetch plugin functionality.\n\n## Basic Fetch Operations\n\n```\nRaw Content:\n{{plugin:fetch:get:https://raw.githubusercontent.com/user/repo/main/README.md}}\n\nJSON API:\n{{plugin:fetch:get:https://api.example.com/data.json}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation:\n{{plugin:fetch:invalid:https://example.com}}\n\nInvalid URL:\n{{plugin:fetch:get:not-a-url}}\n\nNon-text Content:\n{{plugin:fetch:get:https://example.com/image.jpg}}\n\nServer Error:\n{{plugin:fetch:get:https://httpstat.us/500}}\n\nNon-public Address:\n{{plugin:fetch:get:http://127.0.0.1:8080/status}}\n```\n\n## Security Considerations\n\n- Only use trusted URLs\n- Be aware of rate limits\n- Content is limited to 1MB\n- Only text content types are allowed\n- The plugin connects only to public IP addresses. It refuses loopback, private, shared (`100.64.0.0/10`), link-local, multicast and unspecified addresses. It checks the address after DNS resolution, and again for each redirect\n- A fetch stops after 30 seconds or after 5 redirects\n- The plugin does not use `HTTP_PROXY` or `HTTPS_PROXY`\n- Consider URL allow listing in production\n- Validate and sanitize fetched content before use","internal/plugins/template/sys.md":"# System Plugin Tests\n\nSimple test file for validating system plugin functionality.\n\n## Basic System Information\n\n```\nHostname: {{plugin:sys:hostname}}\nUsername: {{plugin:sys:user}}\nOperating System: {{plugin:sys:os}}\nArchitecture: {{plugin:sys:arch}}\n```\n\n## Paths and Directories\n\n```\nCurrent Directory: {{plugin:sys:pwd}}\nHome Directory: {{plugin:sys:home}}\n```\n\n## Environment Variables\n\n```\nPath: {{plugin:sys:env:PATH}}\nHome: {{plugin:sys:env:HOME}}\nShell: {{plugin:sys:env:SHELL}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation: {{plugin:sys:invalid}}\nMissing Env Var: {{plugin:sys:env:}}\nNon-existent Env Var: {{plugin:sys:env:NONEXISTENT_VAR_123456}}\n```\n\n## Security Note\n\nBe careful when exposing system information in templates, especially:\n- Environment variables that might contain sensitive data\n- Full paths that reveal system structure\n- Username/hostname information in public templates","internal/plugins/template/README.md":"# Fabric Template System\n\n## Quick Start\necho \"Hello {{name}}!\" | fabric -v=name:World\n\n## Overview\n\nThe Fabric Template System provides a powerful and extensible way to handle variable substitution and dynamic content generation through a plugin architecture. It uses a double-brace syntax (`{{}}`) for variables and plugin operations, making it both readable and flexible.\n\n\n\n## Basic Usage\n\n### Variable Substitution\n\nThe template system supports basic variable substitution using double braces:\n\n```markdown\nHello {{name}}!\nCurrent role: {{role}}\n```\n\nVariables can be provided via:\n- Command line arguments: `-v=name:John -v=role:admin`\n- YAML front matter in input files\n- Environment variables (when configured)\n\n### Special Variables\n\n- `{{input}}`: Represents the main input content\n  ```markdown\n  Here is the analysis:\n  {{input}}\n  End of analysis.\n  ```\n\n## Nested Tokens and Resolution\n\n### Basic Nesting\n\nThe template system supports nested tokens, where inner tokens are resolved before outer ones. This enables complex, dynamic template generation.\n\n#### Simple Variable Nesting\n```markdown\n{{user_{{inner}}}}\n\nExample:\nVariables: {\n  \"inner\": \"name\",\n  \"user_name\": \"John Doe\"\n}\n{{user_{{inner}}}} -> {{user_name}} -> John Doe\n```\n\nThe outer token must have template text in addition to the inner token. In `{{{{inner}}}}`, the body is only the value of `inner`, so the result is the literal text `{{name}}`.\n\n#### Nested Plugin Calls\n```markdown\n{{plugin:text:upper:{{plugin:sys:env:USER}}}}\nFirst resolves: {{plugin:sys:env:USER}} -> \"john\"\nThen resolves: {{plugin:text:upper:john}} -> \"JOHN\"\n```\n\n### How Nested Resolution Works\n\n1. **Single Pass**\n   - The engine reads the template one time, from left to right\n   - Each `{{...}}` token in the template resolves one time\n   - Inner tokens resolve before the token that contains them\n   - The engine does not scan text from a variable, a plugin or an extension, so a `{{...}}` in that text stays as literal text\n   - In `{{{name}}}` (three braces), the outer braces are literal text: `{John Doe}`\n   - A token with a single brace, for example `{{a}{b}}`, stays as literal text\n\n2. **Resolution Order**\n   ```markdown\n   Original: {{plugin:text:upper:{{user}}}}\n   Step 1: Found {{user}} -> \"john\"\n   Step 2: Now have {{plugin:text:upper:john}}\n   Step 3: Final result -> \"JOHN\"\n   ```\n\n3. **Complex Nesting Example**\n   ```markdown\n   {{plugin:text:{{case}}:{{plugin:sys:env:{{varname}}}}}}\n   \n   With variables:\n   {\n     \"case\": \"upper\",\n     \"varname\": \"USER\"\n   }\n   \n   Resolution steps:\n   1. {{varname}} -> \"USER\"\n   2. {{plugin:sys:env:USER}} -> \"john\"\n   3. {{case}} -> \"upper\"\n   4. {{plugin:text:upper:john}} -> \"JOHN\"\n   ```\n\n### Important Considerations\n\n1. **Depth Limitations**\n   - While nesting is supported, avoid excessive nesting for clarity\n   - Complex nested structures can be hard to debug\n   - Consider breaking very complex templates into smaller parts\n\n2. **Variable Resolution**\n   - Inner variables must resolve to valid values for outer operations\n   - Error messages will point to the innermost failed resolution\n   - Debug logs show the step-by-step resolution process\n\n3. **Plugin Nesting**\n   ```markdown\n   # Valid:\n   {{plugin:text:upper:{{plugin:sys:env:USER}}}}\n   \n   # Also Valid:\n   {{plugin:text:{{operation}}:{{value}}}}\n   \n   # Invalid (plugin namespace cannot be dynamic):\n   {{plugin:{{namespace}}:operation:value}}\n   ```\n\n4. **Debugging Nested Templates**\n   ```go\n   Debug = true  // Enable debug logging\n   \n   Template: {{plugin:text:upper:{{user}}}}\n   Debug output:\n   > Processing variable: user\n   > Replacing {{user}} with john\n   > Plugin call:\n   >   Namespace: text\n   >   Operation: upper\n   >   Value: john\n   > Plugin result: JOHN\n   ```\n\n5. **Variables in Plugin Tokens**\n   - A token in the template can use a variable value as a plugin operation or value. For example, `{{plugin:sys:env:{{env_var}}}}` reads the environment variable that `env_var` names\n   - A REST client can set the variables of a request. Thus a pattern for REST use must not take a plugin namespace, operation or value from a variable\n\n6. **Variables in User Input**\n   - With `--input-has-vars`, Fabric replaces the variables in the user input\n   - A plugin or extension token in the user input stays as literal text, and the plugin or extension does not run. The input can contain text from other sources, for example a web page or a transcript\n\n### Examples\n\n1. **Dynamic Operation Selection**\n   ```markdown\n   {{plugin:text:{{operation}}:hello}}\n   \n   With variables:\n   {\n     \"operation\": \"upper\"\n   }\n   \n   Result: HELLO\n   ```\n\n2. **Dynamic Environment Variable Lookup**\n   ```markdown\n   {{plugin:sys:env:{{env_var}}}}\n   \n   With variables:\n   {\n     \"env_var\": \"HOME\"\n   }\n   \n   Result: /home/user\n   ```\n\n3. **Nested Date Formatting**\n   ```markdown\n   {{plugin:datetime:{{format}}:{{plugin:datetime:now}}}}\n   \n   With variables:\n   {\n     \"format\": \"full\"\n   }\n   \n   Result: Wednesday, November 20, 2024\n   ```\n\n\n\n\n\n## Plugin System\n\n### Plugin Syntax\n\nPlugins use the following syntax:\n```\n{{plugin:namespace:operation:value}}\n```\n\n- `namespace`: The plugin category (e.g., text, datetime, sys)\n- `operation`: The specific operation to perform\n- `value`: Optional value for the operation\n\n### Built-in Plugins\n\n#### Text Plugin\nText manipulation operations:\n```markdown\n{{plugin:text:upper:hello}}  -> HELLO\n{{plugin:text:lower:HELLO}}  -> hello\n{{plugin:text:title:hello world}} -> Hello World\n```\n\n#### DateTime Plugin\nTime and date operations:\n```markdown\n{{plugin:datetime:now}}       -> 2024-11-20T15:04:05Z\n{{plugin:datetime:today}}     -> 2024-11-20\n{{plugin:datetime:rel:-1d}}   -> 2024-11-19\n{{plugin:datetime:month}}     -> November\n```\n\n#### System Plugin\nSystem information:\n```markdown\n{{plugin:sys:hostname}}   -> server1\n{{plugin:sys:user}}       -> currentuser\n{{plugin:sys:os}}         -> linux\n{{plugin:sys:env:HOME}}   -> /home/user\n```\n\n## Developing Plugins\n\n### Plugin Interface\n\nTo create a new plugin, implement the following interface:\n\n```go\ntype Plugin interface {\n    Apply(operation string, value string) (string, error)\n}\n```\n\n### Example Plugin Implementation\n\nHere's a simple plugin that performs basic math operations:\n\n```go\npackage template\n\ntype MathPlugin struct{}\n\nfunc (p *MathPlugin) Apply(operation string, value string) (string, error) {\n    switch operation {\n    case \"add\":\n        // Parse value as \"a,b\" and return a+b\n        nums := strings.Split(value, \",\")\n        if len(nums) != 2 {\n            return \"\", fmt.Errorf(\"add requires two numbers\")\n        }\n        a, err := strconv.Atoi(nums[0])\n        if err != nil {\n            return \"\", err\n        }\n        b, err := strconv.Atoi(nums[1])\n        if err != nil {\n            return \"\", err\n        }\n        return fmt.Sprintf(\"%d\", a+b), nil\n    \n    default:\n        return \"\", fmt.Errorf(\"unknown math operation: %s\", operation)\n    }\n}\n```\n\n### Registering a New Plugin\n\n1. Add your plugin struct to the template package\n2. Register it in template.go:\n\n```go\nvar (\n    // Existing plugins\n    textPlugin = &TextPlugin{}\n    datetimePlugin = &DateTimePlugin{}\n    \n    // Add your new plugin\n    mathPlugin = &MathPlugin{}\n)\n\n// Update the plugin handler in ApplyTemplate\nswitch namespace {\n    case \"text\":\n        result, err = textPlugin.Apply(operation, value)\n    case \"datetime\":\n        result, err = datetimePlugin.Apply(operation, value)\n    // Add your namespace\n    case \"math\":\n        result, err = mathPlugin.Apply(operation, value)\n    default:\n        return \"\", fmt.Errorf(\"unknown plugin namespace: %s\", namespace)\n}\n```\n\n### Plugin Development Guidelines\n\n1. **Error Handling**\n   - Return clear error messages\n   - Validate all inputs\n   - Handle edge cases gracefully\n\n2. **Debugging**\n   - Use the `debugf` function for logging\n   - Log entry and exit points\n   - Log intermediate calculations\n\n```go\nfunc (p *MyPlugin) Apply(operation string, value string) (string, error) {\n    debugf(\"MyPlugin operation: %s value: %s\\n\", operation, value)\n    // ... plugin logic ...\n    debugf(\"MyPlugin result: %s\\n\", result)\n    return result, nil\n}\n```\n\n3. **Security Considerations**\n   - Validate and sanitize inputs\n   - Avoid shell execution\n   - Be careful with file operations\n   - Limit resource usage\n\n4. **Performance**\n   - Cache expensive computations\n   - Minimize allocations\n   - Consider concurrent access\n\n### Testing Plugins\n\nCreate tests for your plugin in `plugin_test.go`:\n\n```go\nfunc TestMathPlugin(t *testing.T) {\n    plugin := &MathPlugin{}\n    \n    tests := []struct {\n        operation string\n        value     string\n        expected  string\n        wantErr   bool\n    }{\n        {\"add\", \"5,3\", \"8\", false},\n        {\"add\", \"bad,input\", \"\", true},\n        {\"unknown\", \"value\", \"\", true},\n    }\n    \n    for _, tt := range tests {\n        result, err := plugin.Apply(tt.operation, tt.value)\n        if (err != nil) != tt.wantErr {\n            t.Errorf(\"MathPlugin.Apply(%s, %s) error = %v, wantErr %v\",\n                tt.operation, tt.value, err, tt.wantErr)\n            continue\n        }\n        if result != tt.expected {\n            t.Errorf(\"MathPlugin.Apply(%s, %s) = %v, want %v\",\n                tt.operation, tt.value, result, tt.expected)\n        }\n    }\n}\n```\n\n## Best Practices\n\n1. **Namespace Selection**\n   - Choose clear, descriptive names\n   - Avoid conflicts with existing plugins\n   - Group related operations together\n\n2. **Operation Names**\n   - Use lowercase names\n   - Keep names concise but clear\n   - Be consistent with similar operations\n\n3. **Value Format**\n   - Document expected formats\n   - Use common separators consistently\n   - Provide examples in comments\n\n4. **Error Messages**\n   - Be specific about what went wrong\n   - Include valid operation examples\n   - Help users fix the problem\n\n## Common Issues and Solutions\n\n1. **Missing Variables**\n   ```\n   Error: missing required variables: [name]\n   Solution: Provide all required variables using -v=name:value\n   ```\n\n2. **Invalid Plugin Operations**\n   ```\n   Error: unknown operation 'invalid' for plugin 'text'\n   Solution: Check plugin documentation for supported operations\n   ```\n\n3. **Plugin Value Format**\n   ```\n   Error: invalid format for datetime:rel, expected -1d, -2w, etc.\n   Solution: Follow the required format for plugin values\n   ```\n\n\n\n\n## Contributing\n\n1. Fork the repository\n2. Create your plugin branch\n3. Implement your plugin following the guidelines\n4. Add comprehensive tests\n5. Submit a pull request\n\n## Support\n\nFor issues and questions:\n1. Check the debugging output (enable with Debug=true)\n2. Review the plugin documentation\n3. Open an issue with:\n   - Template content\n   - Variables used\n   - Expected vs actual output\n   - Debug logs","internal/plugins/template/Examples/security-report.yaml":"name: \"security-report\"\nexecutable: \"/usr/local/bin/security-report.sh\"\ntype: \"executable\"\ntimeout: \"30s\"\ndescription: \"Generate system security report\"\nversion: \"1.0.0\"\n\noperations:\n  generate:\n    cmd_template: \"{{executable}} /tmp/security-report-{{1}}.txt\"\n\nconfig:\n  output:\n    method: \"file\"\n    file_config:\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n","internal/plugins/template/Examples/openai.yaml":"name: openai\nexecutable: \"/path/to/your/openai-chat.sh\"\ntype: executable\ntimeout: \"30s\"\ndescription: \"Call OpenAI Chat Completions API\"\nversion: \"1.0.0\"\n\noperations:\n  chat:\n    cmd_template: \"{{executable}} {{value}}\"\n\nconfig:\n  output:\n    method: stdout\n","internal/plugins/template/Examples/README.md":"\n# Fabric Extensions: Complete Guide\n\n## Important: Extensions Only Work in Patterns\n\n**Extensions are ONLY processed when used within pattern files, not via direct piping to fabric.**\n\n```bash\n# ❌ This DOES NOT WORK - extensions are not processed in stdin\necho \"{{ext:word-generator:generate:3}}\" | fabric\n\n# ✅ This WORKS - extensions are processed within patterns\nfabric -p my-pattern-with-extensions.md\n```\n\nWhen you pipe directly to fabric without a pattern, the input goes straight to the LLM without template processing. Extensions are only evaluated during pattern template processing via `ApplyTemplate()`.\n\n## Understanding Extension Architecture\n\n### Registry Structure\n\nThe extension registry is stored at `~/.config/fabric/extensions/extensions.yaml` and tracks registered extensions:\n\n```yaml\nextensions:\n    extension-name:\n        config_path: /path/to/config.yaml\n        config_hash: <sha256>\n        executable_hash: <sha256>\n```\n\nThe registry maintains security through hash verification of both configs and executables.\n\n### Extension Configuration\n\nEach extension requires a YAML configuration file with the following structure:\n\n```yaml\nname: \"extension-name\"          # Unique identifier\nexecutable: \"/path/to/binary\"   # Full path to executable\ntype: \"executable\"             # Type of extension\ntimeout: \"30s\"                 # Execution timeout\ndescription: \"Description\"     # What the extension does\nversion: \"1.0.0\"              # Version number\nenv: []                       # Optional environment variables\n\noperations:                   # Defined operations\n  operation-name:\n    cmd_template: \"{{executable}} {{operation}} {{value}}\"\n\nconfig:                      # Output configuration\n  output:\n    method: \"stdout\"         # or \"file\"\n    file_config:            # Optional, for file output\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n```\n\n### Directory Structure\n\nRecommended organization:\n\n```text\n~/.config/fabric/extensions/\n├── bin/           # Extension executables\n├── configs/       # Extension YAML configs\n└── extensions.yaml # Registry file\n```\n\n## Example 1: Python Wrapper (Word Generator)\n\nA simple example wrapping a Python script.\n\n### 1. Position Files\n\n```bash\n# Create directories\nmkdir -p ~/.config/fabric/extensions/{bin,configs}\n\n# Install script\ncp word-generator.py ~/.config/fabric/extensions/bin/\nchmod +x ~/.config/fabric/extensions/bin/word-generator.py\n```\n\n### 2. Configure\n\nCreate `~/.config/fabric/extensions/configs/word-generator.yaml`:\n\n```yaml\nname: word-generator\nexecutable: \"~/.config/fabric/extensions/bin/word-generator.py\"\ntype: executable\ntimeout: \"5s\"\ndescription: \"Generates random words based on count parameter\"\nversion: \"1.0.0\"\n\noperations:\n  generate:\n    cmd_template: \"{{executable}} {{value}}\"\n\nconfig:\n  output:\n    method: stdout\n```\n\n### 3. Register & Run\n\n```bash\n# Register\nfabric --addextension ~/.config/fabric/extensions/configs/word-generator.yaml\n\n# Extensions must be used within patterns (see \"Extensions in patterns\" section below)\n# Direct piping to fabric will NOT process extension syntax\n```\n\n## Example 2: Direct Executable (SQLite3)\n\nUsing a system executable directly.\n\ncopy the memories to your home directory\n ~/memories.db\n\n### 1. Configure\n\nCreate `~/.config/fabric/extensions/configs/memory-query.yaml`:\n\n```yaml\nname: memory-query\nexecutable: \"/usr/bin/sqlite3\"\ntype: executable\ntimeout: \"5s\"\ndescription: \"Query memories database\"\nversion: \"1.0.0\"\n\noperations:\n  goal:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where type= 'goal'\\\"\"\n  value:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where type= 'value'\\\"\"\n  byid:\n    # Do not put {{value}} directly in double quotes. There, the shell escape\n    # quotes are literal, and $(cmd) in the value runs. printf %d makes the\n    # value an integer in its own command.\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where uid= $(printf %d {{value}})\\\"\"\n  all:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories\\\"\"\n\nconfig:\n  output:\n    method: stdout\n```\n\n### 2. Register & Run\n\n```bash\n# Register\nfabric --addextension ~/.config/fabric/extensions/configs/memory-query.yaml\n\n# Extensions must be used within patterns (see \"Extensions in patterns\" section below)\n# Direct piping to fabric will NOT process extension syntax\n```\n\n## Extension Management Commands\n\n### Add Extension\n\n```bash\nfabric --addextension ~/.config/fabric/extensions/configs/memory-query.yaml\n```\n\nNote : if the executable or config file changes, you must re-add the extension.\nThis will recompute the hash for the extension.\n\n### List Extensions\n\n```bash\nfabric --listextensions\n```\n\nShows all registered extensions with their status and configuration details.\n\n### Remove Extension\n\n```bash\nfabric --rmextension <extension-name>\n```\n\nRemoves an extension from the registry.\n\n## Extensions in patterns\n\n**IMPORTANT**: Extensions are ONLY processed when used within pattern files, not via direct piping to fabric.\n\nCreate a pattern file (e.g., `test_pattern.md`):\n\n```markdown\nThese are my favorite\n{{ext:word-generator:generate:3}}\n\nThese are my least favorite\n{{ext:word-generator:generate:2}}\n\nwhat does this say about me?\n```\n\nRun the pattern:\n\n```bash\nfabric -p ./internal/plugins/template/Examples/test_pattern.md\n```\n\n## Passing {{input}} to extensions inside patterns\n\n```text\nCreate a pattern called ai_summarize that uses extensions (see openai.yaml and copy for claude)\n\nSummarize the responses from both AI models:\n\nOpenAI Response:\n{{ext:openai:chat:{{input}}}}\n\nClaude Response:\n{{ext:claude:chat:{{input}}}}\n\n```\n\n```bash\necho \"What is Artificial Intelligence\" | ../fabric-fix -p ai_summarize\n\n```\n\n## Security Considerations\n\n1. **Hash Verification**\n   - Both configs and executables are verified via SHA-256 hashes\n   - Changes to either require re-registration\n   - Prevents tampering with registered extensions\n\n2. **Execution Safety**\n   - Extensions run with user permissions\n   - Timeout constraints prevent runaway processes\n   - Environment variables can be controlled via config\n\n3. **Best Practices**\n   - Review extension code before installation\n   - Keep executables in protected directories\n   - Use absolute paths in configurations\n   - Implement proper error handling in scripts\n   - Regular security audits of registered extensions\n\n## Troubleshooting\n\n### Common Issues\n\n1. **Registration Failures**\n   - Verify file permissions\n   - Check executable paths\n   - Validate YAML syntax\n\n2. **Execution Errors**\n   - Check operation exists in config\n   - Verify timeout settings\n   - Monitor system resources\n   - Check extension logs\n\n3. **Output Issues**\n   - Verify output method configuration\n   - Check file permissions for file output\n   - Monitor disk space for file operations\n\n### Debug Tips\n\n1. Enable verbose logging when available\n2. Check system logs for execution errors\n3. Verify extension dependencies\n4. Test extensions with minimal configurations first\n\nWould you like me to expand on any particular section or add more examples?\n","internal/plugins/template/Examples/remote-security-report.yaml":"name: \"remote-security\"\nexecutable: \"/usr/local/bin/remote-security-report.sh\"\ntype: \"executable\"\ntimeout: \"60s\"\ndescription: \"Generate security report from remote system\"\n\noperations:\n  report:\n    cmd_template: \"{{executable}} {{1}} {{2}} {{3}}\"\n\nconfig:\n  output:\n    method: \"file\"\n    file_config:\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n","internal/plugins/template/Examples/sqlite3_demo.yaml":"name: memory-query\nexecutable: /usr/bin/sqlite3\ntype: executable\ntimeout: \"5s\"\ndescription: \"Query memories database\"\nversion: \"1.0.0\"\nenv: []\n\noperations:\n  goal:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'goal'\\\"\"\n  value:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'value'\\\"\"\n  project:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'project'\\\"\"\n  byid:\n    # Do not put {{value}} directly in double quotes. printf %d makes the\n    # value an integer in its own command.\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where uid= $(printf %d {{value}})\\\"\"\n  all:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories\\\"\"\n\nconfig:\n  output:\n    method: stdout\n","internal/plugins/template/Examples/test_pattern.md":"These are my favorite\n{{ext:word-generator:generate:3}}\n\nThese are my least favorite\n{{ext:word-generator:generate:2}}\n\nwhat does this say about me?\n\n","internal/plugins/template/Examples/word-generator.yaml":"# Examples Skill\n\nPath: `internal/plugins/template/Examples/word-generator.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/word-generator.yaml)"},"files":{"internal/plugins/template/file.md":"# File Plugin Tests\n\nSimple test file for validating file plugin functionality.\n\nEach path is relative to the working directory of the fabric process.\n\n## Basic File Operations\n\n```\nRead File:\n{{plugin:file:read:docs/file.txt}}\n\nLast 5 Lines:\n{{plugin:file:tail:logs/app.log|5}}\n\nCheck Existence:\n{{plugin:file:exists:docs/file.txt}}\n\nGet Size:\n{{plugin:file:size:docs/file.txt}}\n\nLast Modified:\n{{plugin:file:modified:docs/file.txt}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation:\n{{plugin:file:invalid:docs/file.txt}}\n\nNon-existent File:\n{{plugin:file:read:docs/nonexistent.txt}}\n\nPath Traversal Attempt:\n{{plugin:file:read:../../../etc/passwd}}\n\nAbsolute Path:\n{{plugin:file:read:/path/to/file.txt}}\n\nHome Directory Path:\n{{plugin:file:read:~/file.txt}}\n\nInvalid Tail Format:\n{{plugin:file:tail:docs/file.txt}}\n\nLarge File:\n{{plugin:file:read:data/huge.iso}}\n```\n\n## Security Considerations\n\n- The plugin rejects an absolute path, a path that starts with `~`, and a path that contains `..`\n- A symbolic link must be relative and must stay in the working directory\n- The plugin can read all files in the working directory and its subdirectories. Run `fabric` in a directory that has only the files that patterns can read\n- The REST server (`fabric --serve`) does not run the file plugin\n- Be aware of file size limits (1MB max)\n- All paths are cleaned and normalized\n","internal/plugins/template/datetime.md":"# DateTime Plugin Tests\n\nSimple test file for validating datetime plugin functionality.\n\n## Basic Time Operations\n\n```\nCurrent Time: {{plugin:datetime:now}}\nTime Only: {{plugin:datetime:time}}\nUnix Timestamp: {{plugin:datetime:unix}}\nHour Start: {{plugin:datetime:startofhour}}\nHour End: {{plugin:datetime:endofhour}}\n```\n\n## Date Operations\n\n```\nToday: {{plugin:datetime:today}}\nFull Date: {{plugin:datetime:full}}\nCurrent Month: {{plugin:datetime:month}}\nCurrent Year: {{plugin:datetime:year}}\n```\n\n## Period Operations\n\n```\nWeek Start: {{plugin:datetime:startofweek}}\nWeek End: {{plugin:datetime:endofweek}}\nMonth Start: {{plugin:datetime:startofmonth}}\nMonth End: {{plugin:datetime:endofmonth}}\n```\n\n## Relative Time/Date\n\n```\n2 Hours Ahead: {{plugin:datetime:rel:2h}}\n1 Day Ago: {{plugin:datetime:rel:-1d}}\nNext Week: {{plugin:datetime:rel:1w}}\nLast Month: {{plugin:datetime:rel:-1m}}\nNext Year: {{plugin:datetime:rel:1y}}\n```","internal/plugins/template/fetch.md":"# Fetch Plugin Tests\n\nSimple test file for validating fetch plugin functionality.\n\n## Basic Fetch Operations\n\n```\nRaw Content:\n{{plugin:fetch:get:https://raw.githubusercontent.com/user/repo/main/README.md}}\n\nJSON API:\n{{plugin:fetch:get:https://api.example.com/data.json}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation:\n{{plugin:fetch:invalid:https://example.com}}\n\nInvalid URL:\n{{plugin:fetch:get:not-a-url}}\n\nNon-text Content:\n{{plugin:fetch:get:https://example.com/image.jpg}}\n\nServer Error:\n{{plugin:fetch:get:https://httpstat.us/500}}\n\nNon-public Address:\n{{plugin:fetch:get:http://127.0.0.1:8080/status}}\n```\n\n## Security Considerations\n\n- Only use trusted URLs\n- Be aware of rate limits\n- Content is limited to 1MB\n- Only text content types are allowed\n- The plugin connects only to public IP addresses. It refuses loopback, private, shared (`100.64.0.0/10`), link-local, multicast and unspecified addresses. It checks the address after DNS resolution, and again for each redirect\n- A fetch stops after 30 seconds or after 5 redirects\n- The plugin does not use `HTTP_PROXY` or `HTTPS_PROXY`\n- Consider URL allow listing in production\n- Validate and sanitize fetched content before use","internal/plugins/template/sys.md":"# System Plugin Tests\n\nSimple test file for validating system plugin functionality.\n\n## Basic System Information\n\n```\nHostname: {{plugin:sys:hostname}}\nUsername: {{plugin:sys:user}}\nOperating System: {{plugin:sys:os}}\nArchitecture: {{plugin:sys:arch}}\n```\n\n## Paths and Directories\n\n```\nCurrent Directory: {{plugin:sys:pwd}}\nHome Directory: {{plugin:sys:home}}\n```\n\n## Environment Variables\n\n```\nPath: {{plugin:sys:env:PATH}}\nHome: {{plugin:sys:env:HOME}}\nShell: {{plugin:sys:env:SHELL}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation: {{plugin:sys:invalid}}\nMissing Env Var: {{plugin:sys:env:}}\nNon-existent Env Var: {{plugin:sys:env:NONEXISTENT_VAR_123456}}\n```\n\n## Security Note\n\nBe careful when exposing system information in templates, especially:\n- Environment variables that might contain sensitive data\n- Full paths that reveal system structure\n- Username/hostname information in public templates","internal/plugins/template/README.md":"# Fabric Template System\n\n## Quick Start\necho \"Hello {{name}}!\" | fabric -v=name:World\n\n## Overview\n\nThe Fabric Template System provides a powerful and extensible way to handle variable substitution and dynamic content generation through a plugin architecture. It uses a double-brace syntax (`{{}}`) for variables and plugin operations, making it both readable and flexible.\n\n\n\n## Basic Usage\n\n### Variable Substitution\n\nThe template system supports basic variable substitution using double braces:\n\n```markdown\nHello {{name}}!\nCurrent role: {{role}}\n```\n\nVariables can be provided via:\n- Command line arguments: `-v=name:John -v=role:admin`\n- YAML front matter in input files\n- Environment variables (when configured)\n\n### Special Variables\n\n- `{{input}}`: Represents the main input content\n  ```markdown\n  Here is the analysis:\n  {{input}}\n  End of analysis.\n  ```\n\n## Nested Tokens and Resolution\n\n### Basic Nesting\n\nThe template system supports nested tokens, where inner tokens are resolved before outer ones. This enables complex, dynamic template generation.\n\n#### Simple Variable Nesting\n```markdown\n{{user_{{inner}}}}\n\nExample:\nVariables: {\n  \"inner\": \"name\",\n  \"user_name\": \"John Doe\"\n}\n{{user_{{inner}}}} -> {{user_name}} -> John Doe\n```\n\nThe outer token must have template text in addition to the inner token. In `{{{{inner}}}}`, the body is only the value of `inner`, so the result is the literal text `{{name}}`.\n\n#### Nested Plugin Calls\n```markdown\n{{plugin:text:upper:{{plugin:sys:env:USER}}}}\nFirst resolves: {{plugin:sys:env:USER}} -> \"john\"\nThen resolves: {{plugin:text:upper:john}} -> \"JOHN\"\n```\n\n### How Nested Resolution Works\n\n1. **Single Pass**\n   - The engine reads the template one time, from left to right\n   - Each `{{...}}` token in the template resolves one time\n   - Inner tokens resolve before the token that contains them\n   - The engine does not scan text from a variable, a plugin or an extension, so a `{{...}}` in that text stays as literal text\n   - In `{{{name}}}` (three braces), the outer braces are literal text: `{John Doe}`\n   - A token with a single brace, for example `{{a}{b}}`, stays as literal text\n\n2. **Resolution Order**\n   ```markdown\n   Original: {{plugin:text:upper:{{user}}}}\n   Step 1: Found {{user}} -> \"john\"\n   Step 2: Now have {{plugin:text:upper:john}}\n   Step 3: Final result -> \"JOHN\"\n   ```\n\n3. **Complex Nesting Example**\n   ```markdown\n   {{plugin:text:{{case}}:{{plugin:sys:env:{{varname}}}}}}\n   \n   With variables:\n   {\n     \"case\": \"upper\",\n     \"varname\": \"USER\"\n   }\n   \n   Resolution steps:\n   1. {{varname}} -> \"USER\"\n   2. {{plugin:sys:env:USER}} -> \"john\"\n   3. {{case}} -> \"upper\"\n   4. {{plugin:text:upper:john}} -> \"JOHN\"\n   ```\n\n### Important Considerations\n\n1. **Depth Limitations**\n   - While nesting is supported, avoid excessive nesting for clarity\n   - Complex nested structures can be hard to debug\n   - Consider breaking very complex templates into smaller parts\n\n2. **Variable Resolution**\n   - Inner variables must resolve to valid values for outer operations\n   - Error messages will point to the innermost failed resolution\n   - Debug logs show the step-by-step resolution process\n\n3. **Plugin Nesting**\n   ```markdown\n   # Valid:\n   {{plugin:text:upper:{{plugin:sys:env:USER}}}}\n   \n   # Also Valid:\n   {{plugin:text:{{operation}}:{{value}}}}\n   \n   # Invalid (plugin namespace cannot be dynamic):\n   {{plugin:{{namespace}}:operation:value}}\n   ```\n\n4. **Debugging Nested Templates**\n   ```go\n   Debug = true  // Enable debug logging\n   \n   Template: {{plugin:text:upper:{{user}}}}\n   Debug output:\n   > Processing variable: user\n   > Replacing {{user}} with john\n   > Plugin call:\n   >   Namespace: text\n   >   Operation: upper\n   >   Value: john\n   > Plugin result: JOHN\n   ```\n\n5. **Variables in Plugin Tokens**\n   - A token in the template can use a variable value as a plugin operation or value. For example, `{{plugin:sys:env:{{env_var}}}}` reads the environment variable that `env_var` names\n   - A REST client can set the variables of a request. Thus a pattern for REST use must not take a plugin namespace, operation or value from a variable\n\n6. **Variables in User Input**\n   - With `--input-has-vars`, Fabric replaces the variables in the user input\n   - A plugin or extension token in the user input stays as literal text, and the plugin or extension does not run. The input can contain text from other sources, for example a web page or a transcript\n\n### Examples\n\n1. **Dynamic Operation Selection**\n   ```markdown\n   {{plugin:text:{{operation}}:hello}}\n   \n   With variables:\n   {\n     \"operation\": \"upper\"\n   }\n   \n   Result: HELLO\n   ```\n\n2. **Dynamic Environment Variable Lookup**\n   ```markdown\n   {{plugin:sys:env:{{env_var}}}}\n   \n   With variables:\n   {\n     \"env_var\": \"HOME\"\n   }\n   \n   Result: /home/user\n   ```\n\n3. **Nested Date Formatting**\n   ```markdown\n   {{plugin:datetime:{{format}}:{{plugin:datetime:now}}}}\n   \n   With variables:\n   {\n     \"format\": \"full\"\n   }\n   \n   Result: Wednesday, November 20, 2024\n   ```\n\n\n\n\n\n## Plugin System\n\n### Plugin Syntax\n\nPlugins use the following syntax:\n```\n{{plugin:namespace:operation:value}}\n```\n\n- `namespace`: The plugin category (e.g., text, datetime, sys)\n- `operation`: The specific operation to perform\n- `value`: Optional value for the operation\n\n### Built-in Plugins\n\n#### Text Plugin\nText manipulation operations:\n```markdown\n{{plugin:text:upper:hello}}  -> HELLO\n{{plugin:text:lower:HELLO}}  -> hello\n{{plugin:text:title:hello world}} -> Hello World\n```\n\n#### DateTime Plugin\nTime and date operations:\n```markdown\n{{plugin:datetime:now}}       -> 2024-11-20T15:04:05Z\n{{plugin:datetime:today}}     -> 2024-11-20\n{{plugin:datetime:rel:-1d}}   -> 2024-11-19\n{{plugin:datetime:month}}     -> November\n```\n\n#### System Plugin\nSystem information:\n```markdown\n{{plugin:sys:hostname}}   -> server1\n{{plugin:sys:user}}       -> currentuser\n{{plugin:sys:os}}         -> linux\n{{plugin:sys:env:HOME}}   -> /home/user\n```\n\n## Developing Plugins\n\n### Plugin Interface\n\nTo create a new plugin, implement the following interface:\n\n```go\ntype Plugin interface {\n    Apply(operation string, value string) (string, error)\n}\n```\n\n### Example Plugin Implementation\n\nHere's a simple plugin that performs basic math operations:\n\n```go\npackage template\n\ntype MathPlugin struct{}\n\nfunc (p *MathPlugin) Apply(operation string, value string) (string, error) {\n    switch operation {\n    case \"add\":\n        // Parse value as \"a,b\" and return a+b\n        nums := strings.Split(value, \",\")\n        if len(nums) != 2 {\n            return \"\", fmt.Errorf(\"add requires two numbers\")\n        }\n        a, err := strconv.Atoi(nums[0])\n        if err != nil {\n            return \"\", err\n        }\n        b, err := strconv.Atoi(nums[1])\n        if err != nil {\n            return \"\", err\n        }\n        return fmt.Sprintf(\"%d\", a+b), nil\n    \n    default:\n        return \"\", fmt.Errorf(\"unknown math operation: %s\", operation)\n    }\n}\n```\n\n### Registering a New Plugin\n\n1. Add your plugin struct to the template package\n2. Register it in template.go:\n\n```go\nvar (\n    // Existing plugins\n    textPlugin = &TextPlugin{}\n    datetimePlugin = &DateTimePlugin{}\n    \n    // Add your new plugin\n    mathPlugin = &MathPlugin{}\n)\n\n// Update the plugin handler in ApplyTemplate\nswitch namespace {\n    case \"text\":\n        result, err = textPlugin.Apply(operation, value)\n    case \"datetime\":\n        result, err = datetimePlugin.Apply(operation, value)\n    // Add your namespace\n    case \"math\":\n        result, err = mathPlugin.Apply(operation, value)\n    default:\n        return \"\", fmt.Errorf(\"unknown plugin namespace: %s\", namespace)\n}\n```\n\n### Plugin Development Guidelines\n\n1. **Error Handling**\n   - Return clear error messages\n   - Validate all inputs\n   - Handle edge cases gracefully\n\n2. **Debugging**\n   - Use the `debugf` function for logging\n   - Log entry and exit points\n   - Log intermediate calculations\n\n```go\nfunc (p *MyPlugin) Apply(operation string, value string) (string, error) {\n    debugf(\"MyPlugin operation: %s value: %s\\n\", operation, value)\n    // ... plugin logic ...\n    debugf(\"MyPlugin result: %s\\n\", result)\n    return result, nil\n}\n```\n\n3. **Security Considerations**\n   - Validate and sanitize inputs\n   - Avoid shell execution\n   - Be careful with file operations\n   - Limit resource usage\n\n4. **Performance**\n   - Cache expensive computations\n   - Minimize allocations\n   - Consider concurrent access\n\n### Testing Plugins\n\nCreate tests for your plugin in `plugin_test.go`:\n\n```go\nfunc TestMathPlugin(t *testing.T) {\n    plugin := &MathPlugin{}\n    \n    tests := []struct {\n        operation string\n        value     string\n        expected  string\n        wantErr   bool\n    }{\n        {\"add\", \"5,3\", \"8\", false},\n        {\"add\", \"bad,input\", \"\", true},\n        {\"unknown\", \"value\", \"\", true},\n    }\n    \n    for _, tt := range tests {\n        result, err := plugin.Apply(tt.operation, tt.value)\n        if (err != nil) != tt.wantErr {\n            t.Errorf(\"MathPlugin.Apply(%s, %s) error = %v, wantErr %v\",\n                tt.operation, tt.value, err, tt.wantErr)\n            continue\n        }\n        if result != tt.expected {\n            t.Errorf(\"MathPlugin.Apply(%s, %s) = %v, want %v\",\n                tt.operation, tt.value, result, tt.expected)\n        }\n    }\n}\n```\n\n## Best Practices\n\n1. **Namespace Selection**\n   - Choose clear, descriptive names\n   - Avoid conflicts with existing plugins\n   - Group related operations together\n\n2. **Operation Names**\n   - Use lowercase names\n   - Keep names concise but clear\n   - Be consistent with similar operations\n\n3. **Value Format**\n   - Document expected formats\n   - Use common separators consistently\n   - Provide examples in comments\n\n4. **Error Messages**\n   - Be specific about what went wrong\n   - Include valid operation examples\n   - Help users fix the problem\n\n## Common Issues and Solutions\n\n1. **Missing Variables**\n   ```\n   Error: missing required variables: [name]\n   Solution: Provide all required variables using -v=name:value\n   ```\n\n2. **Invalid Plugin Operations**\n   ```\n   Error: unknown operation 'invalid' for plugin 'text'\n   Solution: Check plugin documentation for supported operations\n   ```\n\n3. **Plugin Value Format**\n   ```\n   Error: invalid format for datetime:rel, expected -1d, -2w, etc.\n   Solution: Follow the required format for plugin values\n   ```\n\n\n\n\n## Contributing\n\n1. Fork the repository\n2. Create your plugin branch\n3. Implement your plugin following the guidelines\n4. Add comprehensive tests\n5. Submit a pull request\n\n## Support\n\nFor issues and questions:\n1. Check the debugging output (enable with Debug=true)\n2. Review the plugin documentation\n3. Open an issue with:\n   - Template content\n   - Variables used\n   - Expected vs actual output\n   - Debug logs","internal/plugins/template/Examples/security-report.yaml":"name: \"security-report\"\nexecutable: \"/usr/local/bin/security-report.sh\"\ntype: \"executable\"\ntimeout: \"30s\"\ndescription: \"Generate system security report\"\nversion: \"1.0.0\"\n\noperations:\n  generate:\n    cmd_template: \"{{executable}} /tmp/security-report-{{1}}.txt\"\n\nconfig:\n  output:\n    method: \"file\"\n    file_config:\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n","internal/plugins/template/Examples/openai.yaml":"name: openai\nexecutable: \"/path/to/your/openai-chat.sh\"\ntype: executable\ntimeout: \"30s\"\ndescription: \"Call OpenAI Chat Completions API\"\nversion: \"1.0.0\"\n\noperations:\n  chat:\n    cmd_template: \"{{executable}} {{value}}\"\n\nconfig:\n  output:\n    method: stdout\n","internal/plugins/template/Examples/README.md":"\n# Fabric Extensions: Complete Guide\n\n## Important: Extensions Only Work in Patterns\n\n**Extensions are ONLY processed when used within pattern files, not via direct piping to fabric.**\n\n```bash\n# ❌ This DOES NOT WORK - extensions are not processed in stdin\necho \"{{ext:word-generator:generate:3}}\" | fabric\n\n# ✅ This WORKS - extensions are processed within patterns\nfabric -p my-pattern-with-extensions.md\n```\n\nWhen you pipe directly to fabric without a pattern, the input goes straight to the LLM without template processing. Extensions are only evaluated during pattern template processing via `ApplyTemplate()`.\n\n## Understanding Extension Architecture\n\n### Registry Structure\n\nThe extension registry is stored at `~/.config/fabric/extensions/extensions.yaml` and tracks registered extensions:\n\n```yaml\nextensions:\n    extension-name:\n        config_path: /path/to/config.yaml\n        config_hash: <sha256>\n        executable_hash: <sha256>\n```\n\nThe registry maintains security through hash verification of both configs and executables.\n\n### Extension Configuration\n\nEach extension requires a YAML configuration file with the following structure:\n\n```yaml\nname: \"extension-name\"          # Unique identifier\nexecutable: \"/path/to/binary\"   # Full path to executable\ntype: \"executable\"             # Type of extension\ntimeout: \"30s\"                 # Execution timeout\ndescription: \"Description\"     # What the extension does\nversion: \"1.0.0\"              # Version number\nenv: []                       # Optional environment variables\n\noperations:                   # Defined operations\n  operation-name:\n    cmd_template: \"{{executable}} {{operation}} {{value}}\"\n\nconfig:                      # Output configuration\n  output:\n    method: \"stdout\"         # or \"file\"\n    file_config:            # Optional, for file output\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n```\n\n### Directory Structure\n\nRecommended organization:\n\n```text\n~/.config/fabric/extensions/\n├── bin/           # Extension executables\n├── configs/       # Extension YAML configs\n└── extensions.yaml # Registry file\n```\n\n## Example 1: Python Wrapper (Word Generator)\n\nA simple example wrapping a Python script.\n\n### 1. Position Files\n\n```bash\n# Create directories\nmkdir -p ~/.config/fabric/extensions/{bin,configs}\n\n# Install script\ncp word-generator.py ~/.config/fabric/extensions/bin/\nchmod +x ~/.config/fabric/extensions/bin/word-generator.py\n```\n\n### 2. Configure\n\nCreate `~/.config/fabric/extensions/configs/word-generator.yaml`:\n\n```yaml\nname: word-generator\nexecutable: \"~/.config/fabric/extensions/bin/word-generator.py\"\ntype: executable\ntimeout: \"5s\"\ndescription: \"Generates random words based on count parameter\"\nversion: \"1.0.0\"\n\noperations:\n  generate:\n    cmd_template: \"{{executable}} {{value}}\"\n\nconfig:\n  output:\n    method: stdout\n```\n\n### 3. Register & Run\n\n```bash\n# Register\nfabric --addextension ~/.config/fabric/extensions/configs/word-generator.yaml\n\n# Extensions must be used within patterns (see \"Extensions in patterns\" section below)\n# Direct piping to fabric will NOT process extension syntax\n```\n\n## Example 2: Direct Executable (SQLite3)\n\nUsing a system executable directly.\n\ncopy the memories to your home directory\n ~/memories.db\n\n### 1. Configure\n\nCreate `~/.config/fabric/extensions/configs/memory-query.yaml`:\n\n```yaml\nname: memory-query\nexecutable: \"/usr/bin/sqlite3\"\ntype: executable\ntimeout: \"5s\"\ndescription: \"Query memories database\"\nversion: \"1.0.0\"\n\noperations:\n  goal:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where type= 'goal'\\\"\"\n  value:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where type= 'value'\\\"\"\n  byid:\n    # Do not put {{value}} directly in double quotes. There, the shell escape\n    # quotes are literal, and $(cmd) in the value runs. printf %d makes the\n    # value an integer in its own command.\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where uid= $(printf %d {{value}})\\\"\"\n  all:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories\\\"\"\n\nconfig:\n  output:\n    method: stdout\n```\n\n### 2. Register & Run\n\n```bash\n# Register\nfabric --addextension ~/.config/fabric/extensions/configs/memory-query.yaml\n\n# Extensions must be used within patterns (see \"Extensions in patterns\" section below)\n# Direct piping to fabric will NOT process extension syntax\n```\n\n## Extension Management Commands\n\n### Add Extension\n\n```bash\nfabric --addextension ~/.config/fabric/extensions/configs/memory-query.yaml\n```\n\nNote : if the executable or config file changes, you must re-add the extension.\nThis will recompute the hash for the extension.\n\n### List Extensions\n\n```bash\nfabric --listextensions\n```\n\nShows all registered extensions with their status and configuration details.\n\n### Remove Extension\n\n```bash\nfabric --rmextension <extension-name>\n```\n\nRemoves an extension from the registry.\n\n## Extensions in patterns\n\n**IMPORTANT**: Extensions are ONLY processed when used within pattern files, not via direct piping to fabric.\n\nCreate a pattern file (e.g., `test_pattern.md`):\n\n```markdown\nThese are my favorite\n{{ext:word-generator:generate:3}}\n\nThese are my least favorite\n{{ext:word-generator:generate:2}}\n\nwhat does this say about me?\n```\n\nRun the pattern:\n\n```bash\nfabric -p ./internal/plugins/template/Examples/test_pattern.md\n```\n\n## Passing {{input}} to extensions inside patterns\n\n```text\nCreate a pattern called ai_summarize that uses extensions (see openai.yaml and copy for claude)\n\nSummarize the responses from both AI models:\n\nOpenAI Response:\n{{ext:openai:chat:{{input}}}}\n\nClaude Response:\n{{ext:claude:chat:{{input}}}}\n\n```\n\n```bash\necho \"What is Artificial Intelligence\" | ../fabric-fix -p ai_summarize\n\n```\n\n## Security Considerations\n\n1. **Hash Verification**\n   - Both configs and executables are verified via SHA-256 hashes\n   - Changes to either require re-registration\n   - Prevents tampering with registered extensions\n\n2. **Execution Safety**\n   - Extensions run with user permissions\n   - Timeout constraints prevent runaway processes\n   - Environment variables can be controlled via config\n\n3. **Best Practices**\n   - Review extension code before installation\n   - Keep executables in protected directories\n   - Use absolute paths in configurations\n   - Implement proper error handling in scripts\n   - Regular security audits of registered extensions\n\n## Troubleshooting\n\n### Common Issues\n\n1. **Registration Failures**\n   - Verify file permissions\n   - Check executable paths\n   - Validate YAML syntax\n\n2. **Execution Errors**\n   - Check operation exists in config\n   - Verify timeout settings\n   - Monitor system resources\n   - Check extension logs\n\n3. **Output Issues**\n   - Verify output method configuration\n   - Check file permissions for file output\n   - Monitor disk space for file operations\n\n### Debug Tips\n\n1. Enable verbose logging when available\n2. Check system logs for execution errors\n3. Verify extension dependencies\n4. Test extensions with minimal configurations first\n\nWould you like me to expand on any particular section or add more examples?\n","internal/plugins/template/Examples/remote-security-report.yaml":"name: \"remote-security\"\nexecutable: \"/usr/local/bin/remote-security-report.sh\"\ntype: \"executable\"\ntimeout: \"60s\"\ndescription: \"Generate security report from remote system\"\n\noperations:\n  report:\n    cmd_template: \"{{executable}} {{1}} {{2}} {{3}}\"\n\nconfig:\n  output:\n    method: \"file\"\n    file_config:\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n","internal/plugins/template/Examples/sqlite3_demo.yaml":"name: memory-query\nexecutable: /usr/bin/sqlite3\ntype: executable\ntimeout: \"5s\"\ndescription: \"Query memories database\"\nversion: \"1.0.0\"\nenv: []\n\noperations:\n  goal:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'goal'\\\"\"\n  value:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'value'\\\"\"\n  project:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'project'\\\"\"\n  byid:\n    # Do not put {{value}} directly in double quotes. printf %d makes the\n    # value an integer in its own command.\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where uid= $(printf %d {{value}})\\\"\"\n  all:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories\\\"\"\n\nconfig:\n  output:\n    method: stdout\n","internal/plugins/template/Examples/test_pattern.md":"These are my favorite\n{{ext:word-generator:generate:3}}\n\nThese are my least favorite\n{{ext:word-generator:generate:2}}\n\nwhat does this say about me?\n\n","internal/plugins/template/Examples/word-generator.yaml":"# Examples Skill\n\nPath: `internal/plugins/template/Examples/word-generator.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/word-generator.yaml)"},"items":[{"name":"datetime.md","path":"internal/plugins/template/datetime.md","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/datetime.md","title":"Template Skill","category":"anthropic-skill","format":"markdown","content":"# DateTime Plugin Tests\n\nSimple test file for validating datetime plugin functionality.\n\n## Basic Time Operations\n\n```\nCurrent Time: {{plugin:datetime:now}}\nTime Only: {{plugin:datetime:time}}\nUnix Timestamp: {{plugin:datetime:unix}}\nHour Start: {{plugin:datetime:startofhour}}\nHour End: {{plugin:datetime:endofhour}}\n```\n\n## Date Operations\n\n```\nToday: {{plugin:datetime:today}}\nFull Date: {{plugin:datetime:full}}\nCurrent Month: {{plugin:datetime:month}}\nCurrent Year: {{plugin:datetime:year}}\n```\n\n## Period Operations\n\n```\nWeek Start: {{plugin:datetime:startofweek}}\nWeek End: {{plugin:datetime:endofweek}}\nMonth Start: {{plugin:datetime:startofmonth}}\nMonth End: {{plugin:datetime:endofmonth}}\n```\n\n## Relative Time/Date\n\n```\n2 Hours Ahead: {{plugin:datetime:rel:2h}}\n1 Day Ago: {{plugin:datetime:rel:-1d}}\nNext Week: {{plugin:datetime:rel:1w}}\nLast Month: {{plugin:datetime:rel:-1m}}\nNext Year: {{plugin:datetime:rel:1y}}\n```","isInternal":false,"tokens":250,"sizeBytes":931},{"name":"openai.yaml","path":"internal/plugins/template/Examples/openai.yaml","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/openai.yaml","title":"Examples Skill","category":"anthropic-skill","format":"yaml","content":"name: openai\nexecutable: \"/path/to/your/openai-chat.sh\"\ntype: executable\ntimeout: \"30s\"\ndescription: \"Call OpenAI Chat Completions API\"\nversion: \"1.0.0\"\n\noperations:\n  chat:\n    cmd_template: \"{{executable}} {{value}}\"\n\nconfig:\n  output:\n    method: stdout\n","isInternal":false,"tokens":74,"sizeBytes":257},{"name":"remote-security-report.yaml","path":"internal/plugins/template/Examples/remote-security-report.yaml","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/remote-security-report.yaml","title":"Examples Skill","category":"anthropic-skill","format":"yaml","content":"name: \"remote-security\"\nexecutable: \"/usr/local/bin/remote-security-report.sh\"\ntype: \"executable\"\ntimeout: \"60s\"\ndescription: \"Generate security report from remote system\"\n\noperations:\n  report:\n    cmd_template: \"{{executable}} {{1}} {{2}} {{3}}\"\n\nconfig:\n  output:\n    method: \"file\"\n    file_config:\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n","isInternal":false,"tokens":95,"sizeBytes":375},{"name":"security-report.yaml","path":"internal/plugins/template/Examples/security-report.yaml","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/security-report.yaml","title":"Examples Skill","category":"anthropic-skill","format":"yaml","content":"name: \"security-report\"\nexecutable: \"/usr/local/bin/security-report.sh\"\ntype: \"executable\"\ntimeout: \"30s\"\ndescription: \"Generate system security report\"\nversion: \"1.0.0\"\n\noperations:\n  generate:\n    cmd_template: \"{{executable}} /tmp/security-report-{{1}}.txt\"\n\nconfig:\n  output:\n    method: \"file\"\n    file_config:\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n","isInternal":false,"tokens":101,"sizeBytes":388},{"name":"sqlite3_demo.yaml","path":"internal/plugins/template/Examples/sqlite3_demo.yaml","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/sqlite3_demo.yaml","title":"Examples Skill","category":"anthropic-skill","format":"yaml","content":"name: memory-query\nexecutable: /usr/bin/sqlite3\ntype: executable\ntimeout: \"5s\"\ndescription: \"Query memories database\"\nversion: \"1.0.0\"\nenv: []\n\noperations:\n  goal:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'goal'\\\"\"\n  value:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'value'\\\"\"\n  project:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'project'\\\"\"\n  byid:\n    # Do not put {{value}} directly in double quotes. printf %d makes the\n    # value an integer in its own command.\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where uid= $(printf %d {{value}})\\\"\"\n  all:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories\\\"\"\n\nconfig:\n  output:\n    method: stdout\n","isInternal":false,"tokens":245,"sizeBytes":895},{"name":"test_pattern.md","path":"internal/plugins/template/Examples/test_pattern.md","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/test_pattern.md","title":"Examples Skill","category":"anthropic-skill","format":"markdown","content":"These are my favorite\n{{ext:word-generator:generate:3}}\n\nThese are my least favorite\n{{ext:word-generator:generate:2}}\n\nwhat does this say about me?\n\n","isInternal":false,"tokens":38,"sizeBytes":150},{"name":"word-generator.yaml","path":"internal/plugins/template/Examples/word-generator.yaml","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/word-generator.yaml","title":"Examples Skill","category":"anthropic-skill","format":"yaml","content":"# Examples Skill\n\nPath: `internal/plugins/template/Examples/word-generator.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/word-generator.yaml)","tokens":300,"sizeBytes":227},{"name":"fetch.md","path":"internal/plugins/template/fetch.md","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/fetch.md","title":"Template Skill","category":"anthropic-skill","format":"markdown","content":"# Fetch Plugin Tests\n\nSimple test file for validating fetch plugin functionality.\n\n## Basic Fetch Operations\n\n```\nRaw Content:\n{{plugin:fetch:get:https://raw.githubusercontent.com/user/repo/main/README.md}}\n\nJSON API:\n{{plugin:fetch:get:https://api.example.com/data.json}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation:\n{{plugin:fetch:invalid:https://example.com}}\n\nInvalid URL:\n{{plugin:fetch:get:not-a-url}}\n\nNon-text Content:\n{{plugin:fetch:get:https://example.com/image.jpg}}\n\nServer Error:\n{{plugin:fetch:get:https://httpstat.us/500}}\n\nNon-public Address:\n{{plugin:fetch:get:http://127.0.0.1:8080/status}}\n```\n\n## Security Considerations\n\n- Only use trusted URLs\n- Be aware of rate limits\n- Content is limited to 1MB\n- Only text content types are allowed\n- The plugin connects only to public IP addresses. It refuses loopback, private, shared (`100.64.0.0/10`), link-local, multicast and unspecified addresses. It checks the address after DNS resolution, and again for each redirect\n- A fetch stops after 30 seconds or after 5 redirects\n- The plugin does not use `HTTP_PROXY` or `HTTPS_PROXY`\n- Consider URL allow listing in production\n- Validate and sanitize fetched content before use","isInternal":false,"tokens":293,"sizeBytes":1237},{"name":"file.md","path":"internal/plugins/template/file.md","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/file.md","title":"Template Skill","category":"anthropic-skill","format":"markdown","content":"# File Plugin Tests\n\nSimple test file for validating file plugin functionality.\n\nEach path is relative to the working directory of the fabric process.\n\n## Basic File Operations\n\n```\nRead File:\n{{plugin:file:read:docs/file.txt}}\n\nLast 5 Lines:\n{{plugin:file:tail:logs/app.log|5}}\n\nCheck Existence:\n{{plugin:file:exists:docs/file.txt}}\n\nGet Size:\n{{plugin:file:size:docs/file.txt}}\n\nLast Modified:\n{{plugin:file:modified:docs/file.txt}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation:\n{{plugin:file:invalid:docs/file.txt}}\n\nNon-existent File:\n{{plugin:file:read:docs/nonexistent.txt}}\n\nPath Traversal Attempt:\n{{plugin:file:read:../../../etc/passwd}}\n\nAbsolute Path:\n{{plugin:file:read:/path/to/file.txt}}\n\nHome Directory Path:\n{{plugin:file:read:~/file.txt}}\n\nInvalid Tail Format:\n{{plugin:file:tail:docs/file.txt}}\n\nLarge File:\n{{plugin:file:read:data/huge.iso}}\n```\n\n## Security Considerations\n\n- The plugin rejects an absolute path, a path that starts with `~`, and a path that contains `..`\n- A symbolic link must be relative and must stay in the working directory\n- The plugin can read all files in the working directory and its subdirectories. Run `fabric` in a directory that has only the files that patterns can read\n- The REST server (`fabric --serve`) does not run the file plugin\n- Be aware of file size limits (1MB max)\n- All paths are cleaned and normalized\n","isInternal":false,"tokens":344,"sizeBytes":1415},{"name":"sys.md","path":"internal/plugins/template/sys.md","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/sys.md","title":"Template Skill","category":"anthropic-skill","format":"markdown","content":"# System Plugin Tests\n\nSimple test file for validating system plugin functionality.\n\n## Basic System Information\n\n```\nHostname: {{plugin:sys:hostname}}\nUsername: {{plugin:sys:user}}\nOperating System: {{plugin:sys:os}}\nArchitecture: {{plugin:sys:arch}}\n```\n\n## Paths and Directories\n\n```\nCurrent Directory: {{plugin:sys:pwd}}\nHome Directory: {{plugin:sys:home}}\n```\n\n## Environment Variables\n\n```\nPath: {{plugin:sys:env:PATH}}\nHome: {{plugin:sys:env:HOME}}\nShell: {{plugin:sys:env:SHELL}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation: {{plugin:sys:invalid}}\nMissing Env Var: {{plugin:sys:env:}}\nNon-existent Env Var: {{plugin:sys:env:NONEXISTENT_VAR_123456}}\n```\n\n## Security Note\n\nBe careful when exposing system information in templates, especially:\n- Environment variables that might contain sensitive data\n- Full paths that reveal system structure\n- Username/hostname information in public templates","isInternal":false,"tokens":227,"sizeBytes":949},{"name":"README.md","path":"internal/plugins/template/Examples/README.md","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/README.md","title":"Examples Documentation","category":"plugin-manifest","format":"markdown","content":"\n# Fabric Extensions: Complete Guide\n\n## Important: Extensions Only Work in Patterns\n\n**Extensions are ONLY processed when used within pattern files, not via direct piping to fabric.**\n\n```bash\n# ❌ This DOES NOT WORK - extensions are not processed in stdin\necho \"{{ext:word-generator:generate:3}}\" | fabric\n\n# ✅ This WORKS - extensions are processed within patterns\nfabric -p my-pattern-with-extensions.md\n```\n\nWhen you pipe directly to fabric without a pattern, the input goes straight to the LLM without template processing. Extensions are only evaluated during pattern template processing via `ApplyTemplate()`.\n\n## Understanding Extension Architecture\n\n### Registry Structure\n\nThe extension registry is stored at `~/.config/fabric/extensions/extensions.yaml` and tracks registered extensions:\n\n```yaml\nextensions:\n    extension-name:\n        config_path: /path/to/config.yaml\n        config_hash: <sha256>\n        executable_hash: <sha256>\n```\n\nThe registry maintains security through hash verification of both configs and executables.\n\n### Extension Configuration\n\nEach extension requires a YAML configuration file with the following structure:\n\n```yaml\nname: \"extension-name\"          # Unique identifier\nexecutable: \"/path/to/binary\"   # Full path to executable\ntype: \"executable\"             # Type of extension\ntimeout: \"30s\"                 # Execution timeout\ndescription: \"Description\"     # What the extension does\nversion: \"1.0.0\"              # Version number\nenv: []                       # Optional environment variables\n\noperations:                   # Defined operations\n  operation-name:\n    cmd_template: \"{{executable}} {{operation}} {{value}}\"\n\nconfig:                      # Output configuration\n  output:\n    method: \"stdout\"         # or \"file\"\n    file_config:            # Optional, for file output\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n```\n\n### Directory Structure\n\nRecommended organization:\n\n```text\n~/.config/fabric/extensions/\n├── bin/           # Extension executables\n├── configs/       # Extension YAML configs\n└── extensions.yaml # Registry file\n```\n\n## Example 1: Python Wrapper (Word Generator)\n\nA simple example wrapping a Python script.\n\n### 1. Position Files\n\n```bash\n# Create directories\nmkdir -p ~/.config/fabric/extensions/{bin,configs}\n\n# Install script\ncp word-generator.py ~/.config/fabric/extensions/bin/\nchmod +x ~/.config/fabric/extensions/bin/word-generator.py\n```\n\n### 2. Configure\n\nCreate `~/.config/fabric/extensions/configs/word-generator.yaml`:\n\n```yaml\nname: word-generator\nexecutable: \"~/.config/fabric/extensions/bin/word-generator.py\"\ntype: executable\ntimeout: \"5s\"\ndescription: \"Generates random words based on count parameter\"\nversion: \"1.0.0\"\n\noperations:\n  generate:\n    cmd_template: \"{{executable}} {{value}}\"\n\nconfig:\n  output:\n    method: stdout\n```\n\n### 3. Register & Run\n\n```bash\n# Register\nfabric --addextension ~/.config/fabric/extensions/configs/word-generator.yaml\n\n# Extensions must be used within patterns (see \"Extensions in patterns\" section below)\n# Direct piping to fabric will NOT process extension syntax\n```\n\n## Example 2: Direct Executable (SQLite3)\n\nUsing a system executable directly.\n\ncopy the memories to your home directory\n ~/memories.db\n\n### 1. Configure\n\nCreate `~/.config/fabric/extensions/configs/memory-query.yaml`:\n\n```yaml\nname: memory-query\nexecutable: \"/usr/bin/sqlite3\"\ntype: executable\ntimeout: \"5s\"\ndescription: \"Query memories database\"\nversion: \"1.0.0\"\n\noperations:\n  goal:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where type= 'goal'\\\"\"\n  value:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where type= 'value'\\\"\"\n  byid:\n    # Do not put {{value}} directly in double quotes. There, the shell escape\n    # quotes are literal, and $(cmd) in the value runs. printf %d makes the\n    # value an integer in its own command.\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where uid= $(printf %d {{value}})\\\"\"\n  all:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories\\\"\"\n\nconfig:\n  output:\n    method: stdout\n```\n\n### 2. Register & Run\n\n```bash\n# Register\nfabric --addextension ~/.config/fabric/extensions/configs/memory-query.yaml\n\n# Extensions must be used within patterns (see \"Extensions in patterns\" section below)\n# Direct piping to fabric will NOT process extension syntax\n```\n\n## Extension Management Commands\n\n### Add Extension\n\n```bash\nfabric --addextension ~/.config/fabric/extensions/configs/memory-query.yaml\n```\n\nNote : if the executable or config file changes, you must re-add the extension.\nThis will recompute the hash for the extension.\n\n### List Extensions\n\n```bash\nfabric --listextensions\n```\n\nShows all registered extensions with their status and configuration details.\n\n### Remove Extension\n\n```bash\nfabric --rmextension <extension-name>\n```\n\nRemoves an extension from the registry.\n\n## Extensions in patterns\n\n**IMPORTANT**: Extensions are ONLY processed when used within pattern files, not via direct piping to fabric.\n\nCreate a pattern file (e.g., `test_pattern.md`):\n\n```markdown\nThese are my favorite\n{{ext:word-generator:generate:3}}\n\nThese are my least favorite\n{{ext:word-generator:generate:2}}\n\nwhat does this say about me?\n```\n\nRun the pattern:\n\n```bash\nfabric -p ./internal/plugins/template/Examples/test_pattern.md\n```\n\n## Passing {{input}} to extensions inside patterns\n\n```text\nCreate a pattern called ai_summarize that uses extensions (see openai.yaml and copy for claude)\n\nSummarize the responses from both AI models:\n\nOpenAI Response:\n{{ext:openai:chat:{{input}}}}\n\nClaude Response:\n{{ext:claude:chat:{{input}}}}\n\n```\n\n```bash\necho \"What is Artificial Intelligence\" | ../fabric-fix -p ai_summarize\n\n```\n\n## Security Considerations\n\n1. **Hash Verification**\n   - Both configs and executables are verified via SHA-256 hashes\n   - Changes to either require re-registration\n   - Prevents tampering with registered extensions\n\n2. **Execution Safety**\n   - Extensions run with user permissions\n   - Timeout constraints prevent runaway processes\n   - Environment variables can be controlled via config\n\n3. **Best Practices**\n   - Review extension code before installation\n   - Keep executables in protected directories\n   - Use absolute paths in configurations\n   - Implement proper error handling in scripts\n   - Regular security audits of registered extensions\n\n## Troubleshooting\n\n### Common Issues\n\n1. **Registration Failures**\n   - Verify file permissions\n   - Check executable paths\n   - Validate YAML syntax\n\n2. **Execution Errors**\n   - Check operation exists in config\n   - Verify timeout settings\n   - Monitor system resources\n   - Check extension logs\n\n3. **Output Issues**\n   - Verify output method configuration\n   - Check file permissions for file output\n   - Monitor disk space for file operations\n\n### Debug Tips\n\n1. Enable verbose logging when available\n2. Check system logs for execution errors\n3. Verify extension dependencies\n4. Test extensions with minimal configurations first\n\nWould you like me to expand on any particular section or add more examples?\n","isInternal":false,"tokens":1602,"sizeBytes":7193},{"name":"README.md","path":"internal/plugins/template/README.md","rawUrl":"https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/README.md","title":"template Documentation","category":"plugin-manifest","format":"markdown","content":"# Fabric Template System\n\n## Quick Start\necho \"Hello {{name}}!\" | fabric -v=name:World\n\n## Overview\n\nThe Fabric Template System provides a powerful and extensible way to handle variable substitution and dynamic content generation through a plugin architecture. It uses a double-brace syntax (`{{}}`) for variables and plugin operations, making it both readable and flexible.\n\n\n\n## Basic Usage\n\n### Variable Substitution\n\nThe template system supports basic variable substitution using double braces:\n\n```markdown\nHello {{name}}!\nCurrent role: {{role}}\n```\n\nVariables can be provided via:\n- Command line arguments: `-v=name:John -v=role:admin`\n- YAML front matter in input files\n- Environment variables (when configured)\n\n### Special Variables\n\n- `{{input}}`: Represents the main input content\n  ```markdown\n  Here is the analysis:\n  {{input}}\n  End of analysis.\n  ```\n\n## Nested Tokens and Resolution\n\n### Basic Nesting\n\nThe template system supports nested tokens, where inner tokens are resolved before outer ones. This enables complex, dynamic template generation.\n\n#### Simple Variable Nesting\n```markdown\n{{user_{{inner}}}}\n\nExample:\nVariables: {\n  \"inner\": \"name\",\n  \"user_name\": \"John Doe\"\n}\n{{user_{{inner}}}} -> {{user_name}} -> John Doe\n```\n\nThe outer token must have template text in addition to the inner token. In `{{{{inner}}}}`, the body is only the value of `inner`, so the result is the literal text `{{name}}`.\n\n#### Nested Plugin Calls\n```markdown\n{{plugin:text:upper:{{plugin:sys:env:USER}}}}\nFirst resolves: {{plugin:sys:env:USER}} -> \"john\"\nThen resolves: {{plugin:text:upper:john}} -> \"JOHN\"\n```\n\n### How Nested Resolution Works\n\n1. **Single Pass**\n   - The engine reads the template one time, from left to right\n   - Each `{{...}}` token in the template resolves one time\n   - Inner tokens resolve before the token that contains them\n   - The engine does not scan text from a variable, a plugin or an extension, so a `{{...}}` in that text stays as literal text\n   - In `{{{name}}}` (three braces), the outer braces are literal text: `{John Doe}`\n   - A token with a single brace, for example `{{a}{b}}`, stays as literal text\n\n2. **Resolution Order**\n   ```markdown\n   Original: {{plugin:text:upper:{{user}}}}\n   Step 1: Found {{user}} -> \"john\"\n   Step 2: Now have {{plugin:text:upper:john}}\n   Step 3: Final result -> \"JOHN\"\n   ```\n\n3. **Complex Nesting Example**\n   ```markdown\n   {{plugin:text:{{case}}:{{plugin:sys:env:{{varname}}}}}}\n   \n   With variables:\n   {\n     \"case\": \"upper\",\n     \"varname\": \"USER\"\n   }\n   \n   Resolution steps:\n   1. {{varname}} -> \"USER\"\n   2. {{plugin:sys:env:USER}} -> \"john\"\n   3. {{case}} -> \"upper\"\n   4. {{plugin:text:upper:john}} -> \"JOHN\"\n   ```\n\n### Important Considerations\n\n1. **Depth Limitations**\n   - While nesting is supported, avoid excessive nesting for clarity\n   - Complex nested structures can be hard to debug\n   - Consider breaking very complex templates into smaller parts\n\n2. **Variable Resolution**\n   - Inner variables must resolve to valid values for outer operations\n   - Error messages will point to the innermost failed resolution\n   - Debug logs show the step-by-step resolution process\n\n3. **Plugin Nesting**\n   ```markdown\n   # Valid:\n   {{plugin:text:upper:{{plugin:sys:env:USER}}}}\n   \n   # Also Valid:\n   {{plugin:text:{{operation}}:{{value}}}}\n   \n   # Invalid (plugin namespace cannot be dynamic):\n   {{plugin:{{namespace}}:operation:value}}\n   ```\n\n4. **Debugging Nested Templates**\n   ```go\n   Debug = true  // Enable debug logging\n   \n   Template: {{plugin:text:upper:{{user}}}}\n   Debug output:\n   > Processing variable: user\n   > Replacing {{user}} with john\n   > Plugin call:\n   >   Namespace: text\n   >   Operation: upper\n   >   Value: john\n   > Plugin result: JOHN\n   ```\n\n5. **Variables in Plugin Tokens**\n   - A token in the template can use a variable value as a plugin operation or value. For example, `{{plugin:sys:env:{{env_var}}}}` reads the environment variable that `env_var` names\n   - A REST client can set the variables of a request. Thus a pattern for REST use must not take a plugin namespace, operation or value from a variable\n\n6. **Variables in User Input**\n   - With `--input-has-vars`, Fabric replaces the variables in the user input\n   - A plugin or extension token in the user input stays as literal text, and the plugin or extension does not run. The input can contain text from other sources, for example a web page or a transcript\n\n### Examples\n\n1. **Dynamic Operation Selection**\n   ```markdown\n   {{plugin:text:{{operation}}:hello}}\n   \n   With variables:\n   {\n     \"operation\": \"upper\"\n   }\n   \n   Result: HELLO\n   ```\n\n2. **Dynamic Environment Variable Lookup**\n   ```markdown\n   {{plugin:sys:env:{{env_var}}}}\n   \n   With variables:\n   {\n     \"env_var\": \"HOME\"\n   }\n   \n   Result: /home/user\n   ```\n\n3. **Nested Date Formatting**\n   ```markdown\n   {{plugin:datetime:{{format}}:{{plugin:datetime:now}}}}\n   \n   With variables:\n   {\n     \"format\": \"full\"\n   }\n   \n   Result: Wednesday, November 20, 2024\n   ```\n\n\n\n\n\n## Plugin System\n\n### Plugin Syntax\n\nPlugins use the following syntax:\n```\n{{plugin:namespace:operation:value}}\n```\n\n- `namespace`: The plugin category (e.g., text, datetime, sys)\n- `operation`: The specific operation to perform\n- `value`: Optional value for the operation\n\n### Built-in Plugins\n\n#### Text Plugin\nText manipulation operations:\n```markdown\n{{plugin:text:upper:hello}}  -> HELLO\n{{plugin:text:lower:HELLO}}  -> hello\n{{plugin:text:title:hello world}} -> Hello World\n```\n\n#### DateTime Plugin\nTime and date operations:\n```markdown\n{{plugin:datetime:now}}       -> 2024-11-20T15:04:05Z\n{{plugin:datetime:today}}     -> 2024-11-20\n{{plugin:datetime:rel:-1d}}   -> 2024-11-19\n{{plugin:datetime:month}}     -> November\n```\n\n#### System Plugin\nSystem information:\n```markdown\n{{plugin:sys:hostname}}   -> server1\n{{plugin:sys:user}}       -> currentuser\n{{plugin:sys:os}}         -> linux\n{{plugin:sys:env:HOME}}   -> /home/user\n```\n\n## Developing Plugins\n\n### Plugin Interface\n\nTo create a new plugin, implement the following interface:\n\n```go\ntype Plugin interface {\n    Apply(operation string, value string) (string, error)\n}\n```\n\n### Example Plugin Implementation\n\nHere's a simple plugin that performs basic math operations:\n\n```go\npackage template\n\ntype MathPlugin struct{}\n\nfunc (p *MathPlugin) Apply(operation string, value string) (string, error) {\n    switch operation {\n    case \"add\":\n        // Parse value as \"a,b\" and return a+b\n        nums := strings.Split(value, \",\")\n        if len(nums) != 2 {\n            return \"\", fmt.Errorf(\"add requires two numbers\")\n        }\n        a, err := strconv.Atoi(nums[0])\n        if err != nil {\n            return \"\", err\n        }\n        b, err := strconv.Atoi(nums[1])\n        if err != nil {\n            return \"\", err\n        }\n        return fmt.Sprintf(\"%d\", a+b), nil\n    \n    default:\n        return \"\", fmt.Errorf(\"unknown math operation: %s\", operation)\n    }\n}\n```\n\n### Registering a New Plugin\n\n1. Add your plugin struct to the template package\n2. Register it in template.go:\n\n```go\nvar (\n    // Existing plugins\n    textPlugin = &TextPlugin{}\n    datetimePlugin = &DateTimePlugin{}\n    \n    // Add your new plugin\n    mathPlugin = &MathPlugin{}\n)\n\n// Update the plugin handler in ApplyTemplate\nswitch namespace {\n    case \"text\":\n        result, err = textPlugin.Apply(operation, value)\n    case \"datetime\":\n        result, err = datetimePlugin.Apply(operation, value)\n    // Add your namespace\n    case \"math\":\n        result, err = mathPlugin.Apply(operation, value)\n    default:\n        return \"\", fmt.Errorf(\"unknown plugin namespace: %s\", namespace)\n}\n```\n\n### Plugin Development Guidelines\n\n1. **Error Handling**\n   - Return clear error messages\n   - Validate all inputs\n   - Handle edge cases gracefully\n\n2. **Debugging**\n   - Use the `debugf` function for logging\n   - Log entry and exit points\n   - Log intermediate calculations\n\n```go\nfunc (p *MyPlugin) Apply(operation string, value string) (string, error) {\n    debugf(\"MyPlugin operation: %s value: %s\\n\", operation, value)\n    // ... plugin logic ...\n    debugf(\"MyPlugin result: %s\\n\", result)\n    return result, nil\n}\n```\n\n3. **Security Considerations**\n   - Validate and sanitize inputs\n   - Avoid shell execution\n   - Be careful with file operations\n   - Limit resource usage\n\n4. **Performance**\n   - Cache expensive computations\n   - Minimize allocations\n   - Consider concurrent access\n\n### Testing Plugins\n\nCreate tests for your plugin in `plugin_test.go`:\n\n```go\nfunc TestMathPlugin(t *testing.T) {\n    plugin := &MathPlugin{}\n    \n    tests := []struct {\n        operation string\n        value     string\n        expected  string\n        wantErr   bool\n    }{\n        {\"add\", \"5,3\", \"8\", false},\n        {\"add\", \"bad,input\", \"\", true},\n        {\"unknown\", \"value\", \"\", true},\n    }\n    \n    for _, tt := range tests {\n        result, err := plugin.Apply(tt.operation, tt.value)\n        if (err != nil) != tt.wantErr {\n            t.Errorf(\"MathPlugin.Apply(%s, %s) error = %v, wantErr %v\",\n                tt.operation, tt.value, err, tt.wantErr)\n            continue\n        }\n        if result != tt.expected {\n            t.Errorf(\"MathPlugin.Apply(%s, %s) = %v, want %v\",\n                tt.operation, tt.value, result, tt.expected)\n        }\n    }\n}\n```\n\n## Best Practices\n\n1. **Namespace Selection**\n   - Choose clear, descriptive names\n   - Avoid conflicts with existing plugins\n   - Group related operations together\n\n2. **Operation Names**\n   - Use lowercase names\n   - Keep names concise but clear\n   - Be consistent with similar operations\n\n3. **Value Format**\n   - Document expected formats\n   - Use common separators consistently\n   - Provide examples in comments\n\n4. **Error Messages**\n   - Be specific about what went wrong\n   - Include valid operation examples\n   - Help users fix the problem\n\n## Common Issues and Solutions\n\n1. **Missing Variables**\n   ```\n   Error: missing required variables: [name]\n   Solution: Provide all required variables using -v=name:value\n   ```\n\n2. **Invalid Plugin Operations**\n   ```\n   Error: unknown operation 'invalid' for plugin 'text'\n   Solution: Check plugin documentation for supported operations\n   ```\n\n3. **Plugin Value Format**\n   ```\n   Error: invalid format for datetime:rel, expected -1d, -2w, etc.\n   Solution: Follow the required format for plugin values\n   ```\n\n\n\n\n## Contributing\n\n1. Fork the repository\n2. Create your plugin branch\n3. Implement your plugin following the guidelines\n4. Add comprehensive tests\n5. Submit a pull request\n\n## Support\n\nFor issues and questions:\n1. Check the debugging output (enable with Debug=true)\n2. Review the plugin documentation\n3. Open an issue with:\n   - Template content\n   - Variables used\n   - Expected vs actual output\n   - Debug logs","isInternal":false,"tokens":2683,"sizeBytes":10835}],"systemPromptSnippet":"<yakaai_skills repo=\"danielmiessler/Fabric\">\n<!-- File: internal/plugins/template/datetime.md (Tokens: ~250 | Category: anthropic-skill) -->\n# DateTime Plugin Tests\n\nSimple test file for validating datetime plugin functionality.\n\n## Basic Time Operations\n\n```\nCurrent Time: {{plugin:datetime:now}}\nTime Only: {{plugin:datetime:time}}\nUnix Timestamp: {{plugin:datetime:unix}}\nHour Start: {{plugin:datetime:startofhour}}\nHour End: {{plugin:datetime:endofhour}}\n```\n\n## Date Operations\n\n```\nToday: {{plugin:datetime:today}}\nFull Date: {{plugin:datetime:full}}\nCurrent Month: {{plugin:datetime:month}}\nCurrent Year: {{plugin:datetime:year}}\n```\n\n## Period Operations\n\n```\nWeek Start: {{plugin:datetime:startofweek}}\nWeek End: {{plugin:datetime:endofweek}}\nMonth Start: {{plugin:datetime:startofmonth}}\nMonth End: {{plugin:datetime:endofmonth}}\n```\n\n## Relative Time/Date\n\n```\n2 Hours Ahead: {{plugin:datetime:rel:2h}}\n1 Day Ago: {{plugin:datetime:rel:-1d}}\nNext Week: {{plugin:datetime:rel:1w}}\nLast Month: {{plugin:datetime:rel:-1m}}\nNext Year: {{plugin:datetime:rel:1y}}\n```\n\n<!-- File: internal/plugins/template/Examples/openai.yaml (Tokens: ~74 | Category: anthropic-skill) -->\nname: openai\nexecutable: \"/path/to/your/openai-chat.sh\"\ntype: executable\ntimeout: \"30s\"\ndescription: \"Call OpenAI Chat Completions API\"\nversion: \"1.0.0\"\n\noperations:\n  chat:\n    cmd_template: \"{{executable}} {{value}}\"\n\nconfig:\n  output:\n    method: stdout\n\n<!-- File: internal/plugins/template/Examples/remote-security-report.yaml (Tokens: ~95 | Category: anthropic-skill) -->\nname: \"remote-security\"\nexecutable: \"/usr/local/bin/remote-security-report.sh\"\ntype: \"executable\"\ntimeout: \"60s\"\ndescription: \"Generate security report from remote system\"\n\noperations:\n  report:\n    cmd_template: \"{{executable}} {{1}} {{2}} {{3}}\"\n\nconfig:\n  output:\n    method: \"file\"\n    file_config:\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n\n<!-- File: internal/plugins/template/Examples/security-report.yaml (Tokens: ~101 | Category: anthropic-skill) -->\nname: \"security-report\"\nexecutable: \"/usr/local/bin/security-report.sh\"\ntype: \"executable\"\ntimeout: \"30s\"\ndescription: \"Generate system security report\"\nversion: \"1.0.0\"\n\noperations:\n  generate:\n    cmd_template: \"{{executable}} /tmp/security-report-{{1}}.txt\"\n\nconfig:\n  output:\n    method: \"file\"\n    file_config:\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n\n<!-- File: internal/plugins/template/Examples/sqlite3_demo.yaml (Tokens: ~245 | Category: anthropic-skill) -->\nname: memory-query\nexecutable: /usr/bin/sqlite3\ntype: executable\ntimeout: \"5s\"\ndescription: \"Query memories database\"\nversion: \"1.0.0\"\nenv: []\n\noperations:\n  goal:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'goal'\\\"\"\n  value:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'value'\\\"\"\n  project:\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where type= 'project'\\\"\"\n  byid:\n    # Do not put {{value}} directly in double quotes. printf %d makes the\n    # value an integer in its own command.\n    cmd_template: \"{{executable}} -json /home/matt/memories.db \\\"select * from memories where uid= $(printf %d {{value}})\\\"\"\n  all:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories\\\"\"\n\nconfig:\n  output:\n    method: stdout\n\n<!-- File: internal/plugins/template/Examples/test_pattern.md (Tokens: ~38 | Category: anthropic-skill) -->\nThese are my favorite\n{{ext:word-generator:generate:3}}\n\nThese are my least favorite\n{{ext:word-generator:generate:2}}\n\nwhat does this say about me?\n\n<!-- File: internal/plugins/template/Examples/word-generator.yaml (Tokens: ~300 | Category: anthropic-skill) -->\n# Examples Skill\n\nPath: `internal/plugins/template/Examples/word-generator.yaml`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/danielmiessler/Fabric/HEAD/internal/plugins/template/Examples/word-generator.yaml)\n\n<!-- File: internal/plugins/template/fetch.md (Tokens: ~293 | Category: anthropic-skill) -->\n# Fetch Plugin Tests\n\nSimple test file for validating fetch plugin functionality.\n\n## Basic Fetch Operations\n\n```\nRaw Content:\n{{plugin:fetch:get:https://raw.githubusercontent.com/user/repo/main/README.md}}\n\nJSON API:\n{{plugin:fetch:get:https://api.example.com/data.json}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation:\n{{plugin:fetch:invalid:https://example.com}}\n\nInvalid URL:\n{{plugin:fetch:get:not-a-url}}\n\nNon-text Content:\n{{plugin:fetch:get:https://example.com/image.jpg}}\n\nServer Error:\n{{plugin:fetch:get:https://httpstat.us/500}}\n\nNon-public Address:\n{{plugin:fetch:get:http://127.0.0.1:8080/status}}\n```\n\n## Security Considerations\n\n- Only use trusted URLs\n- Be aware of rate limits\n- Content is limited to 1MB\n- Only text content types are allowed\n- The plugin connects only to public IP addresses. It refuses loopback, private, shared (`100.64.0.0/10`), link-local, multicast and unspecified addresses. It checks the address after DNS resolution, and again for each redirect\n- A fetch stops after 30 seconds or after 5 redirects\n- The plugin does not use `HTTP_PROXY` or `HTTPS_PROXY`\n- Consider URL allow listing in production\n- Validate and sanitize fetched content before use\n\n<!-- File: internal/plugins/template/file.md (Tokens: ~344 | Category: anthropic-skill) -->\n# File Plugin Tests\n\nSimple test file for validating file plugin functionality.\n\nEach path is relative to the working directory of the fabric process.\n\n## Basic File Operations\n\n```\nRead File:\n{{plugin:file:read:docs/file.txt}}\n\nLast 5 Lines:\n{{plugin:file:tail:logs/app.log|5}}\n\nCheck Existence:\n{{plugin:file:exists:docs/file.txt}}\n\nGet Size:\n{{plugin:file:size:docs/file.txt}}\n\nLast Modified:\n{{plugin:file:modified:docs/file.txt}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation:\n{{plugin:file:invalid:docs/file.txt}}\n\nNon-existent File:\n{{plugin:file:read:docs/nonexistent.txt}}\n\nPath Traversal Attempt:\n{{plugin:file:read:../../../etc/passwd}}\n\nAbsolute Path:\n{{plugin:file:read:/path/to/file.txt}}\n\nHome Directory Path:\n{{plugin:file:read:~/file.txt}}\n\nInvalid Tail Format:\n{{plugin:file:tail:docs/file.txt}}\n\nLarge File:\n{{plugin:file:read:data/huge.iso}}\n```\n\n## Security Considerations\n\n- The plugin rejects an absolute path, a path that starts with `~`, and a path that contains `..`\n- A symbolic link must be relative and must stay in the working directory\n- The plugin can read all files in the working directory and its subdirectories. Run `fabric` in a directory that has only the files that patterns can read\n- The REST server (`fabric --serve`) does not run the file plugin\n- Be aware of file size limits (1MB max)\n- All paths are cleaned and normalized\n\n<!-- File: internal/plugins/template/sys.md (Tokens: ~227 | Category: anthropic-skill) -->\n# System Plugin Tests\n\nSimple test file for validating system plugin functionality.\n\n## Basic System Information\n\n```\nHostname: {{plugin:sys:hostname}}\nUsername: {{plugin:sys:user}}\nOperating System: {{plugin:sys:os}}\nArchitecture: {{plugin:sys:arch}}\n```\n\n## Paths and Directories\n\n```\nCurrent Directory: {{plugin:sys:pwd}}\nHome Directory: {{plugin:sys:home}}\n```\n\n## Environment Variables\n\n```\nPath: {{plugin:sys:env:PATH}}\nHome: {{plugin:sys:env:HOME}}\nShell: {{plugin:sys:env:SHELL}}\n```\n\n## Error Cases\nThese should produce appropriate error messages:\n\n```\nInvalid Operation: {{plugin:sys:invalid}}\nMissing Env Var: {{plugin:sys:env:}}\nNon-existent Env Var: {{plugin:sys:env:NONEXISTENT_VAR_123456}}\n```\n\n## Security Note\n\nBe careful when exposing system information in templates, especially:\n- Environment variables that might contain sensitive data\n- Full paths that reveal system structure\n- Username/hostname information in public templates\n\n<!-- File: internal/plugins/template/Examples/README.md (Tokens: ~1602 | Category: plugin-manifest) -->\n# Fabric Extensions: Complete Guide\n\n## Important: Extensions Only Work in Patterns\n\n**Extensions are ONLY processed when used within pattern files, not via direct piping to fabric.**\n\n```bash\n# ❌ This DOES NOT WORK - extensions are not processed in stdin\necho \"{{ext:word-generator:generate:3}}\" | fabric\n\n# ✅ This WORKS - extensions are processed within patterns\nfabric -p my-pattern-with-extensions.md\n```\n\nWhen you pipe directly to fabric without a pattern, the input goes straight to the LLM without template processing. Extensions are only evaluated during pattern template processing via `ApplyTemplate()`.\n\n## Understanding Extension Architecture\n\n### Registry Structure\n\nThe extension registry is stored at `~/.config/fabric/extensions/extensions.yaml` and tracks registered extensions:\n\n```yaml\nextensions:\n    extension-name:\n        config_path: /path/to/config.yaml\n        config_hash: <sha256>\n        executable_hash: <sha256>\n```\n\nThe registry maintains security through hash verification of both configs and executables.\n\n### Extension Configuration\n\nEach extension requires a YAML configuration file with the following structure:\n\n```yaml\nname: \"extension-name\"          # Unique identifier\nexecutable: \"/path/to/binary\"   # Full path to executable\ntype: \"executable\"             # Type of extension\ntimeout: \"30s\"                 # Execution timeout\ndescription: \"Description\"     # What the extension does\nversion: \"1.0.0\"              # Version number\nenv: []                       # Optional environment variables\n\noperations:                   # Defined operations\n  operation-name:\n    cmd_template: \"{{executable}} {{operation}} {{value}}\"\n\nconfig:                      # Output configuration\n  output:\n    method: \"stdout\"         # or \"file\"\n    file_config:            # Optional, for file output\n      cleanup: true\n      path_from_stdout: true\n      work_dir: \"/tmp\"\n```\n\n### Directory Structure\n\nRecommended organization:\n\n```text\n~/.config/fabric/extensions/\n├── bin/           # Extension executables\n├── configs/       # Extension YAML configs\n└── extensions.yaml # Registry file\n```\n\n## Example 1: Python Wrapper (Word Generator)\n\nA simple example wrapping a Python script.\n\n### 1. Position Files\n\n```bash\n# Create directories\nmkdir -p ~/.config/fabric/extensions/{bin,configs}\n\n# Install script\ncp word-generator.py ~/.config/fabric/extensions/bin/\nchmod +x ~/.config/fabric/extensions/bin/word-generator.py\n```\n\n### 2. Configure\n\nCreate `~/.config/fabric/extensions/configs/word-generator.yaml`:\n\n```yaml\nname: word-generator\nexecutable: \"~/.config/fabric/extensions/bin/word-generator.py\"\ntype: executable\ntimeout: \"5s\"\ndescription: \"Generates random words based on count parameter\"\nversion: \"1.0.0\"\n\noperations:\n  generate:\n    cmd_template: \"{{executable}} {{value}}\"\n\nconfig:\n  output:\n    method: stdout\n```\n\n### 3. Register & Run\n\n```bash\n# Register\nfabric --addextension ~/.config/fabric/extensions/configs/word-generator.yaml\n\n# Extensions must be used within patterns (see \"Extensions in patterns\" section below)\n# Direct piping to fabric will NOT process extension syntax\n```\n\n## Example 2: Direct Executable (SQLite3)\n\nUsing a system executable directly.\n\ncopy the memories to your home directory\n ~/memories.db\n\n### 1. Configure\n\nCreate `~/.config/fabric/extensions/configs/memory-query.yaml`:\n\n```yaml\nname: memory-query\nexecutable: \"/usr/bin/sqlite3\"\ntype: executable\ntimeout: \"5s\"\ndescription: \"Query memories database\"\nversion: \"1.0.0\"\n\noperations:\n  goal:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where type= 'goal'\\\"\"\n  value:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where type= 'value'\\\"\"\n  byid:\n    # Do not put {{value}} directly in double quotes. There, the shell escape\n    # quotes are literal, and $(cmd) in the value runs. printf %d makes the\n    # value an integer in its own command.\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories where uid= $(printf %d {{value}})\\\"\"\n  all:\n    cmd_template: \"{{executable}} -json ~/memories.db \\\"select * from memories\\\"\"\n\nconfig:\n  output:\n    method: stdout\n```\n\n### 2. Register & Run\n\n```bash\n# Register\nfabric --addextension ~/.config/fabric/extensions/configs/memory-query.yaml\n\n# Extensions must be used within patterns (see \"Extensions in patterns\" section below)\n# Direct piping to fabric will NOT process extension syntax\n```\n\n## Extension Management Commands\n\n### Add Extension\n\n```bash\nfabric --addextension ~/.config/fabric/extensions/configs/memory-query.yaml\n```\n\nNote : if the executable or config file changes, you must re-add the extension.\nThis will recompute the hash for the extension.\n\n### List Extensions\n\n```bash\nfabric --listextensions\n```\n\nShows all registered extensions with their status and configuration details.\n\n### Remove Extension\n\n```bash\nfabric --rmextension <extension-name>\n```\n\nRemoves an extension from the registry.\n\n## Extensions in patterns\n\n**IMPORTANT**: Extensions are ONLY processed when used within pattern files, not via direct piping to fabric.\n\nCreate a pattern file (e.g., `test_pattern.md`):\n\n```markdown\nThese are my favorite\n{{ext:word-generator:generate:3}}\n\nThese are my least favorite\n{{ext:word-generator:generate:2}}\n\nwhat does this say about me?\n```\n\nRun the pattern:\n\n```bash\nfabric -p ./internal/plugins/template/Examples/test_pattern.md\n```\n\n## Passing {{input}} to extensions inside patterns\n\n```text\nCreate a pattern called ai_summarize that uses extensions (see openai.yaml and copy for claude)\n\nSummarize the responses from both AI models:\n\nOpenAI Response:\n{{ext:openai:chat:{{input}}}}\n\nClaude Response:\n{{ext:claude:chat:{{input}}}}\n\n```\n\n```bash\necho \"What is Artificial Intelligence\" | ../fabric-fix -p ai_summarize\n\n```\n\n## Security Considerations\n\n1. **Hash Verification**\n   - Both configs and executables are verified via SHA-256 hashes\n   - Changes to either require re-registration\n   - Prevents tampering with registered extensions\n\n2. **Execution Safety**\n   - Extensions run with user permissions\n   - Timeout constraints prevent runaway processes\n   - Environment variables can be controlled via config\n\n3. **Best Practices**\n   - Review extension code before installation\n   - Keep executables in protected directories\n   - Use absolute paths in configurations\n   - Implement proper error handling in scripts\n   - Regular security audits of registered extensions\n\n## Troubleshooting\n\n### Common Issues\n\n1. **Registration Failures**\n   - Verify file permissions\n   - Check executable paths\n   - Validate YAML syntax\n\n2. **Execution Errors**\n   - Check operation exists in config\n   - Verify timeout settings\n   - Monitor system resources\n   - Check extension logs\n\n3. **Output Issues**\n   - Verify output method configuration\n   - Check file permissions for file output\n   - Monitor disk space for file operations\n\n### Debug Tips\n\n1. Enable verbose logging when available\n2. Check system logs for execution errors\n3. Verify extension dependencies\n4. Test extensions with minimal configurations first\n\nWould you like me to expand on any particular section or add more examples?\n\n<!-- File: internal/plugins/template/README.md (Tokens: ~2683 | Category: plugin-manifest) -->\n# Fabric Template System\n\n## Quick Start\necho \"Hello {{name}}!\" | fabric -v=name:World\n\n## Overview\n\nThe Fabric Template System provides a powerful and extensible way to handle variable substitution and dynamic content generation through a plugin architecture. It uses a double-brace syntax (`{{}}`) for variables and plugin operations, making it both readable and flexible.\n\n\n\n## Basic Usage\n\n### Variable Substitution\n\nThe template system supports basic variable substitution using double braces:\n\n```markdown\nHello {{name}}!\nCurrent role: {{role}}\n```\n\nVariables can be provided via:\n- Command line arguments: `-v=name:John -v=role:admin`\n- YAML front matter in input files\n- Environment variables (when configured)\n\n### Special Variables\n\n- `{{input}}`: Represents the main input content\n  ```markdown\n  Here is the analysis:\n  {{input}}\n  End of analysis.\n  ```\n\n## Nested Tokens and Resolution\n\n### Basic Nesting\n\nThe template system supports nested tokens, where inner tokens are resolved before outer ones. This enables complex, dynamic template generation.\n\n#### Simple Variable Nesting\n```markdown\n{{user_{{inner}}}}\n\nExample:\nVariables: {\n  \"inner\": \"name\",\n  \"user_name\": \"John Doe\"\n}\n{{user_{{inner}}}} -> {{user_name}} -> John Doe\n```\n\nThe outer token must have template text in addition to the inner token. In `{{{{inner}}}}`, the body is only the value of `inner`, so the result is the literal text `{{name}}`.\n\n#### Nested Plugin Calls\n```markdown\n{{plugin:text:upper:{{plugin:sys:env:USER}}}}\nFirst resolves: {{plugin:sys:env:USER}} -> \"john\"\nThen resolves: {{plugin:text:upper:john}} -> \"JOHN\"\n```\n\n### How Nested Resolution Works\n\n1. **Single Pass**\n   - The engine reads the template one time, from left to right\n   - Each `{{...}}` token in the template resolves one time\n   - Inner tokens resolve before the token that contains them\n   - The engine does not scan text from a variable, a plugin or an extension, so a `{{...}}` in that text stays as literal text\n   - In `{{{name}}}` (three braces), the outer braces are literal text: `{John Doe}`\n   - A token with a single brace, for example `{{a}{b}}`, stays as literal text\n\n2. **Resolution Order**\n   ```markdown\n   Original: {{plugin:text:upper:{{user}}}}\n   Step 1: Found {{user}} -> \"john\"\n   Step 2: Now have {{plugin:text:upper:john}}\n   Step 3: Final result -> \"JOHN\"\n   ```\n\n3. **Complex Nesting Example**\n   ```markdown\n   {{plugin:text:{{case}}:{{plugin:sys:env:{{varname}}}}}}\n   \n   With variables:\n   {\n     \"case\": \"upper\",\n     \"varname\": \"USER\"\n   }\n   \n   Resolution steps:\n   1. {{varname}} -> \"USER\"\n   2. {{plugin:sys:env:USER}} -> \"john\"\n   3. {{case}} -> \"upper\"\n   4. {{plugin:text:upper:john}} -> \"JOHN\"\n   ```\n\n### Important Considerations\n\n1. **Depth Limitations**\n   - While nesting is supported, avoid excessive nesting for clarity\n   - Complex nested structures can be hard to debug\n   - Consider breaking very complex templates into smaller parts\n\n2. **Variable Resolution**\n   - Inner variables must resolve to valid values for outer operations\n   - Error messages will point to the innermost failed resolution\n   - Debug logs show the step-by-step resolution process\n\n3. **Plugin Nesting**\n   ```markdown\n   # Valid:\n   {{plugin:text:upper:{{plugin:sys:env:USER}}}}\n   \n   # Also Valid:\n   {{plugin:text:{{operation}}:{{value}}}}\n   \n   # Invalid (plugin namespace cannot be dynamic):\n   {{plugin:{{namespace}}:operation:value}}\n   ```\n\n4. **Debugging Nested Templates**\n   ```go\n   Debug = true  // Enable debug logging\n   \n   Template: {{plugin:text:upper:{{user}}}}\n   Debug output:\n   > Processing variable: user\n   > Replacing {{user}} with john\n   > Plugin call:\n   >   Namespace: text\n   >   Operation: upper\n   >   Value: john\n   > Plugin result: JOHN\n   ```\n\n5. **Variables in Plugin Tokens**\n   - A token in the template can use a variable value as a plugin operation or value. For example, `{{plugin:sys:env:{{env_var}}}}` reads the environment variable that `env_var` names\n   - A REST client can set the variables of a request. Thus a pattern for REST use must not take a plugin namespace, operation or value from a variable\n\n6. **Variables in User Input**\n   - With `--input-has-vars`, Fabric replaces the variables in the user input\n   - A plugin or extension token in the user input stays as literal text, and the plugin or extension does not run. The input can contain text from other sources, for example a web page or a transcript\n\n### Examples\n\n1. **Dynamic Operation Selection**\n   ```markdown\n   {{plugin:text:{{operation}}:hello}}\n   \n   With variables:\n   {\n     \"operation\": \"upper\"\n   }\n   \n   Result: HELLO\n   ```\n\n2. **Dynamic Environment Variable Lookup**\n   ```markdown\n   {{plugin:sys:env:{{env_var}}}}\n   \n   With variables:\n   {\n     \"env_var\": \"HOME\"\n   }\n   \n   Result: /home/user\n   ```\n\n3. **Nested Date Formatting**\n   ```markdown\n   {{plugin:datetime:{{format}}:{{plugin:datetime:now}}}}\n   \n   With variables:\n   {\n     \"format\": \"full\"\n   }\n   \n   Result: Wednesday, November 20, 2024\n   ```\n\n\n\n\n\n## Plugin System\n\n### Plugin Syntax\n\nPlugins use the following syntax:\n```\n{{plugin:namespace:operation:value}}\n```\n\n- `namespace`: The plugin category (e.g., text, datetime, sys)\n- `operation`: The specific operation to perform\n- `value`: Optional value for the operation\n\n### Built-in Plugins\n\n#### Text Plugin\nText manipulation operations:\n```markdown\n{{plugin:text:upper:hello}}  -> HELLO\n{{plugin:text:lower:HELLO}}  -> hello\n{{plugin:text:title:hello world}} -> Hello World\n```\n\n#### DateTime Plugin\nTime and date operations:\n```markdown\n{{plugin:datetime:now}}       -> 2024-11-20T15:04:05Z\n{{plugin:datetime:today}}     -> 2024-11-20\n{{plugin:datetime:rel:-1d}}   -> 2024-11-19\n{{plugin:datetime:month}}     -> November\n```\n\n#### System Plugin\nSystem information:\n```markdown\n{{plugin:sys:hostname}}   -> server1\n{{plugin:sys:user}}       -> currentuser\n{{plugin:sys:os}}         -> linux\n{{plugin:sys:env:HOME}}   -> /home/user\n```\n\n## Developing Plugins\n\n### Plugin Interface\n\nTo create a new plugin, implement the following interface:\n\n```go\ntype Plugin interface {\n    Apply(operation string, value string) (string, error)\n}\n```\n\n### Example Plugin Implementation\n\nHere's a simple plugin that performs basic math operations:\n\n```go\npackage template\n\ntype MathPlugin struct{}\n\nfunc (p *MathPlugin) Apply(operation string, value string) (string, error) {\n    switch operation {\n    case \"add\":\n        // Parse value as \"a,b\" and return a+b\n        nums := strings.Split(value, \",\")\n        if len(nums) != 2 {\n            return \"\", fmt.Errorf(\"add requires two numbers\")\n        }\n        a, err := strconv.Atoi(nums[0])\n        if err != nil {\n            return \"\", err\n        }\n        b, err := strconv.Atoi(nums[1])\n        if err != nil {\n            return \"\", err\n        }\n        return fmt.Sprintf(\"%d\", a+b), nil\n    \n    default:\n        return \"\", fmt.Errorf(\"unknown math operation: %s\", operation)\n    }\n}\n```\n\n### Registering a New Plugin\n\n1. Add your plugin struct to the template package\n2. Register it in template.go:\n\n```go\nvar (\n    // Existing plugins\n    textPlugin = &TextPlugin{}\n    datetimePlugin = &DateTimePlugin{}\n    \n    // Add your new plugin\n    mathPlugin = &MathPlugin{}\n)\n\n// Update the plugin handler in ApplyTemplate\nswitch namespace {\n    case \"text\":\n        result, err = textPlugin.Apply(operation, value)\n    case \"datetime\":\n        result, err = datetimePlugin.Apply(operation, value)\n    // Add your namespace\n    case \"math\":\n        result, err = mathPlugin.Apply(operation, value)\n    default:\n        return \"\", fmt.Errorf(\"unknown plugin namespace: %s\", namespace)\n}\n```\n\n### Plugin Development Guidelines\n\n1. **Error Handling**\n   - Return clear error messages\n   - Validate all inputs\n   - Handle edge cases gracefully\n\n2. **Debugging**\n   - Use the `debugf` function for logging\n   - Log entry and exit points\n   - Log intermediate calculations\n\n```go\nfunc (p *MyPlugin) Apply(operation string, value string) (string, error) {\n    debugf(\"MyPlugin operation: %s value: %s\\n\", operation, value)\n    // ... plugin logic ...\n    debugf(\"MyPlugin result: %s\\n\", result)\n    return result, nil\n}\n```\n\n3. **Security Considerations**\n   - Validate and sanitize inputs\n   - Avoid shell execution\n   - Be careful with file operations\n   - Limit resource usage\n\n4. **Performance**\n   - Cache expensive computations\n   - Minimize allocations\n   - Consider concurrent access\n\n### Testing Plugins\n\nCreate tests for your plugin in `plugin_test.go`:\n\n```go\nfunc TestMathPlugin(t *testing.T) {\n    plugin := &MathPlugin{}\n    \n    tests := []struct {\n        operation string\n        value     string\n        expected  string\n        wantErr   bool\n    }{\n        {\"add\", \"5,3\", \"8\", false},\n        {\"add\", \"bad,input\", \"\", true},\n        {\"unknown\", \"value\", \"\", true},\n    }\n    \n    for _, tt := range tests {\n        result, err := plugin.Apply(tt.operation, tt.value)\n        if (err != nil) != tt.wantErr {\n            t.Errorf(\"MathPlugin.Apply(%s, %s) error = %v, wantErr %v\",\n                tt.operation, tt.value, err, tt.wantErr)\n            continue\n        }\n        if result != tt.expected {\n            t.Errorf(\"MathPlugin.Apply(%s, %s) = %v, want %v\",\n                tt.operation, tt.value, result, tt.expected)\n        }\n    }\n}\n```\n\n## Best Practices\n\n1. **Namespace Selection**\n   - Choose clear, descriptive names\n   - Avoid conflicts with existing plugins\n   - Group related operations together\n\n2. **Operation Names**\n   - Use lowercase names\n   - Keep names concise but clear\n   - Be consistent with similar operations\n\n3. **Value Format**\n   - Document expected formats\n   - Use common separators consistently\n   - Provide examples in comments\n\n4. **Error Messages**\n   - Be specific about what went wrong\n   - Include valid operation examples\n   - Help users fix the problem\n\n## Common Issues and Solutions\n\n1. **Missing Variables**\n   ```\n   Error: missing required variables: [name]\n   Solution: Provide all required variables using -v=name:value\n   ```\n\n2. **Invalid Plugin Operations**\n   ```\n   Error: unknown operation 'invalid' for plugin 'text'\n   Solution: Check plugin documentation for supported operations\n   ```\n\n3. **Plugin Value Format**\n   ```\n   Error: invalid format for datetime:rel, expected -1d, -2w, etc.\n   Solution: Follow the required format for plugin values\n   ```\n\n\n\n\n## Contributing\n\n1. Fork the repository\n2. Create your plugin branch\n3. Implement your plugin following the guidelines\n4. Add comprehensive tests\n5. Submit a pull request\n\n## Support\n\nFor issues and questions:\n1. Check the debugging output (enable with Debug=true)\n2. Review the plugin documentation\n3. Open an issue with:\n   - Template content\n   - Variables used\n   - Expected vs actual output\n   - Debug logs\n</yakaai_skills>"}