mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-20 19:58:03 -05:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5e36d93b6 | ||
|
|
2241b2a283 | ||
|
|
ef60f8ca89 | ||
|
|
a23c698947 | ||
|
|
1e693cd5e8 | ||
|
|
4fd1584518 | ||
|
|
794a71a82b | ||
|
|
1e4ed78bcf | ||
|
|
360682eb6f | ||
|
|
095dcd8434 | ||
|
|
fb407ccfed | ||
|
|
c9d4c19ef8 | ||
|
|
f4e7489d42 | ||
|
|
7012acd12a | ||
|
|
387610bcf8 | ||
|
|
9e1ee4d48e |
36
CHANGELOG.md
36
CHANGELOG.md
@@ -1,5 +1,41 @@
|
||||
# Changelog
|
||||
|
||||
## v1.4.385 (2026-01-20)
|
||||
|
||||
### PR [#1947](https://github.com/danielmiessler/Fabric/pull/1947) by [cleong14](https://github.com/cleong14): feat(patterns): add extract_bd_ideas pattern
|
||||
|
||||
- Added extract_bd_ideas pattern that extracts actionable ideas from content and transforms them into well-structured bd issue tracker commands
|
||||
- Implemented identification system for tasks, problems, ideas, improvements, bugs, and features
|
||||
- Added actionability evaluation and appropriate scoping functionality
|
||||
- Integrated priority assignment system (P0-P4) with relevant labels
|
||||
- Created ready-to-execute bd create commands output format
|
||||
|
||||
### PR [#1948](https://github.com/danielmiessler/Fabric/pull/1948) by [cleong14](https://github.com/cleong14): feat(patterns): add create_bd_issue pattern
|
||||
|
||||
- Added create_bd_issue pattern that transforms natural language issue descriptions into optimal bd (Beads) issue tracker commands
|
||||
- Implemented comprehensive bd create flag reference for better command generation
|
||||
- Added intelligent type detection system that automatically categorizes issues as bug, feature, task, epic, or chore
|
||||
- Included priority assessment capability that assigns P0-P4 priority levels based on urgency signals in descriptions
|
||||
- Integrated smart label selection feature that automatically chooses 1-4 relevant labels for each issue
|
||||
|
||||
### PR [#1949](https://github.com/danielmiessler/Fabric/pull/1949) by [ksylvan](https://github.com/ksylvan): Fix #1931 - Image Generation Feature should warn if the model is not capable of Image Generation
|
||||
|
||||
- Add image generation compatibility warnings for unsupported models
|
||||
- Add warning to stderr when using incompatible models with image generation
|
||||
- Add GPT-5, GPT-5-nano, and GPT-5.2 to supported image generation models
|
||||
- Create `checkImageGenerationCompatibility` function in OpenAI plugin
|
||||
- Add comprehensive tests for image generation compatibility warnings
|
||||
|
||||
## v1.4.384 (2026-01-19)
|
||||
|
||||
### PR [#1944](https://github.com/danielmiessler/Fabric/pull/1944) by [ksylvan](https://github.com/ksylvan): Add Infermatic AI Provider Support
|
||||
|
||||
- Add Infermatic provider to ProviderMap as part of Phase 1 implementation for issue #1033
|
||||
- Add test coverage for the Infermatic AI provider in TestCreateClient to verify provider exists and creates valid client
|
||||
- Replace go-git status API with native `git status --porcelain` command to fix worktree compatibility issues
|
||||
- Simplify `IsWorkingDirectoryClean` and `GetStatusDetails` functions to use CLI output parsing instead of go-git library
|
||||
- Use native `git rev-parse HEAD` to get commit hash after commit and remove unused imports from walker.go
|
||||
|
||||
## v1.4.383 (2026-01-18)
|
||||
|
||||
### PR [#1943](https://github.com/danielmiessler/Fabric/pull/1943) by [ksylvan](https://github.com/ksylvan): fix: Ollama server now respects the default context window
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v1.4.383"
|
||||
var version = "v1.4.385"
|
||||
|
||||
Binary file not shown.
@@ -2,9 +2,7 @@ package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -425,64 +423,49 @@ func (w *Walker) Repository() *git.Repository {
|
||||
}
|
||||
|
||||
// IsWorkingDirectoryClean checks if the working directory has any uncommitted changes
|
||||
// Uses native git CLI instead of go-git to properly handle worktree scenarios
|
||||
func (w *Walker) IsWorkingDirectoryClean() (bool, error) {
|
||||
worktree, err := w.repo.Worktree()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get worktree: %w", err)
|
||||
}
|
||||
|
||||
status, err := worktree.Status()
|
||||
worktreePath := worktree.Filesystem.Root()
|
||||
|
||||
// Use native git status --porcelain to avoid go-git worktree issues
|
||||
// go-git's status API has known bugs with linked worktrees
|
||||
cmd := exec.Command("git", "status", "--porcelain")
|
||||
cmd.Dir = worktreePath
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get git status: %w", err)
|
||||
}
|
||||
|
||||
worktreePath := worktree.Filesystem.Root()
|
||||
|
||||
// In worktrees, files staged in the main repo may appear in status but not exist in the worktree
|
||||
// We need to check both the working directory status AND filesystem existence
|
||||
for file, fileStatus := range status {
|
||||
// Check if there are any changes in the working directory
|
||||
if fileStatus.Worktree != git.Unmodified && fileStatus.Worktree != git.Untracked {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// For staged files (Added, Modified in index), verify they exist in this worktree's filesystem
|
||||
// This handles the worktree case where the main repo has staged files that don't exist here
|
||||
if fileStatus.Staging != git.Unmodified && fileStatus.Staging != git.Untracked {
|
||||
filePath := filepath.Join(worktreePath, file)
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
// File is staged but doesn't exist in this worktree - ignore it
|
||||
continue
|
||||
}
|
||||
// File is staged AND exists in this worktree - not clean
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
// If output is empty, working directory is clean
|
||||
return len(strings.TrimSpace(string(output))) == 0, nil
|
||||
}
|
||||
|
||||
// GetStatusDetails returns a detailed status of the working directory
|
||||
// Uses native git CLI instead of go-git to properly handle worktree scenarios
|
||||
func (w *Walker) GetStatusDetails() (string, error) {
|
||||
worktree, err := w.repo.Worktree()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get worktree: %w", err)
|
||||
}
|
||||
|
||||
status, err := worktree.Status()
|
||||
worktreePath := worktree.Filesystem.Root()
|
||||
|
||||
// Use native git status --porcelain to avoid go-git worktree issues
|
||||
cmd := exec.Command("git", "status", "--porcelain")
|
||||
cmd.Dir = worktreePath
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get git status: %w", err)
|
||||
}
|
||||
|
||||
var details strings.Builder
|
||||
for file, fileStatus := range status {
|
||||
// Only include files with actual working directory changes
|
||||
if fileStatus.Worktree != git.Unmodified && fileStatus.Worktree != git.Untracked {
|
||||
details.WriteString(fmt.Sprintf(" %c%c %s\n", fileStatus.Staging, fileStatus.Worktree, file))
|
||||
}
|
||||
}
|
||||
|
||||
return details.String(), nil
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// AddFile adds a file to the git index
|
||||
@@ -526,13 +509,17 @@ func (w *Walker) CommitChanges(message string) (plumbing.Hash, error) {
|
||||
return plumbing.ZeroHash, fmt.Errorf("failed to commit: %w (output: %s)", err, string(output))
|
||||
}
|
||||
|
||||
// Get the commit hash from HEAD
|
||||
ref, err := w.repo.Head()
|
||||
// Get the commit hash from HEAD using native git to avoid go-git worktree issues
|
||||
hashCmd := exec.Command("git", "rev-parse", "HEAD")
|
||||
hashCmd.Dir = worktreePath
|
||||
|
||||
hashOutput, err := hashCmd.Output()
|
||||
if err != nil {
|
||||
return plumbing.ZeroHash, fmt.Errorf("failed to get HEAD after commit: %w", err)
|
||||
}
|
||||
|
||||
return ref.Hash(), nil
|
||||
hashStr := strings.TrimSpace(string(hashOutput))
|
||||
return plumbing.NewHash(hashStr), nil
|
||||
}
|
||||
|
||||
// PushToRemote pushes the current branch to the remote repository
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -164,3 +165,182 @@ func TestSendNotification_MessageTruncation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageGenerationCompatibilityWarning(t *testing.T) {
|
||||
// Save original stderr to restore later
|
||||
originalStderr := os.Stderr
|
||||
defer func() {
|
||||
os.Stderr = originalStderr
|
||||
}()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
model string
|
||||
imageFile string
|
||||
expectWarning bool
|
||||
warningSubstr string
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "Compatible model with image",
|
||||
model: "gpt-4o",
|
||||
imageFile: "test.png",
|
||||
expectWarning: false,
|
||||
description: "Should not warn for compatible model",
|
||||
},
|
||||
{
|
||||
name: "Incompatible model with image",
|
||||
model: "o1-mini",
|
||||
imageFile: "test.png",
|
||||
expectWarning: true,
|
||||
warningSubstr: "Warning: Model 'o1-mini' does not support image generation",
|
||||
description: "Should warn for incompatible model",
|
||||
},
|
||||
{
|
||||
name: "Incompatible model without image",
|
||||
model: "o1-mini",
|
||||
imageFile: "",
|
||||
expectWarning: false,
|
||||
description: "Should not warn when no image file specified",
|
||||
},
|
||||
{
|
||||
name: "Compatible model without image",
|
||||
model: "gpt-4o-mini",
|
||||
imageFile: "",
|
||||
expectWarning: false,
|
||||
description: "Should not warn when no image file specified even for compatible model",
|
||||
},
|
||||
{
|
||||
name: "Another incompatible model with image",
|
||||
model: "gpt-3.5-turbo",
|
||||
imageFile: "output.jpg",
|
||||
expectWarning: true,
|
||||
warningSubstr: "Warning: Model 'gpt-3.5-turbo' does not support image generation",
|
||||
description: "Should warn for different incompatible model",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Note: In a real integration test, we would capture stderr like this:
|
||||
// stderrCapture := &bytes.Buffer{}
|
||||
// os.Stderr = stderrCapture
|
||||
// But since we can't test the actual openai plugin from here due to import cycles,
|
||||
// we'll simulate the integration behavior
|
||||
|
||||
// Create test options (for structure validation)
|
||||
_ = &domain.ChatOptions{
|
||||
Model: tt.model,
|
||||
ImageFile: tt.imageFile,
|
||||
}
|
||||
|
||||
// We'll test the warning function that was added to openai.go
|
||||
// but we need to simulate the same behavior in our test
|
||||
// Since we can't directly access the openai package here due to import cycles,
|
||||
// we'll create a minimal test that verifies the integration would work
|
||||
|
||||
// For integration testing purposes, we'll verify that the warning conditions
|
||||
// are correctly identified and the process continues as expected
|
||||
hasImage := tt.imageFile != ""
|
||||
shouldWarn := hasImage && tt.expectWarning
|
||||
|
||||
// Check if the expected warning condition matches our test case
|
||||
if shouldWarn && tt.expectWarning {
|
||||
// Verify warning substr is provided for warning cases
|
||||
if tt.warningSubstr == "" {
|
||||
t.Errorf("Expected warning substring for warning case")
|
||||
}
|
||||
}
|
||||
|
||||
// The actual warning would be printed by the openai plugin
|
||||
// Here we verify the integration logic is sound
|
||||
// In a real integration test, we would check stderr output
|
||||
|
||||
if tt.expectWarning {
|
||||
// This is expected since we're not calling the actual openai plugin
|
||||
// In a real integration test, the warning would appear in stderr
|
||||
t.Logf("Note: Warning would be printed by openai plugin for model '%s'", tt.model)
|
||||
}
|
||||
|
||||
// In a real test with stderr capture, we would check for unexpected warnings
|
||||
// Since we're not calling the actual plugin, we just validate the logic structure
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageGenerationIntegrationScenarios(t *testing.T) {
|
||||
// Test various real-world scenarios that users might encounter
|
||||
scenarios := []struct {
|
||||
name string
|
||||
cliArgs []string
|
||||
expectWarning bool
|
||||
warningModel string
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "User tries o1-mini with image",
|
||||
cliArgs: []string{
|
||||
"-m", "o1-mini",
|
||||
"--image-file", "output.png",
|
||||
"Describe this image",
|
||||
},
|
||||
expectWarning: true,
|
||||
warningModel: "o1-mini",
|
||||
description: "Common user error - using incompatible model",
|
||||
},
|
||||
{
|
||||
name: "User uses compatible model",
|
||||
cliArgs: []string{
|
||||
"-m", "gpt-4o",
|
||||
"--image-file", "output.png",
|
||||
"Describe this image",
|
||||
},
|
||||
expectWarning: false,
|
||||
description: "Correct usage - should work without warnings",
|
||||
},
|
||||
{
|
||||
name: "User specifies model via pattern env var",
|
||||
cliArgs: []string{
|
||||
"--pattern", "summarize",
|
||||
"--image-file", "output.png",
|
||||
"Summarize this image",
|
||||
},
|
||||
expectWarning: false, // Depends on env var, not tested here
|
||||
description: "Pattern-based model selection",
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
// This test validates the CLI argument parsing would work correctly
|
||||
// The actual warning functionality is tested in the openai package
|
||||
|
||||
// Verify CLI arguments are properly structured
|
||||
hasImage := false
|
||||
model := ""
|
||||
|
||||
for i, arg := range scenario.cliArgs {
|
||||
if arg == "-m" && i+1 < len(scenario.cliArgs) {
|
||||
model = scenario.cliArgs[i+1]
|
||||
}
|
||||
if arg == "--image-file" && i+1 < len(scenario.cliArgs) {
|
||||
hasImage = true
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the scenario setup
|
||||
if scenario.expectWarning && scenario.warningModel == "" {
|
||||
t.Errorf("Expected warning scenario must specify warning model")
|
||||
}
|
||||
|
||||
// Log the scenario for debugging
|
||||
t.Logf("Scenario: %s", scenario.description)
|
||||
t.Logf("Model: %s, Has Image: %v, Expect Warning: %v", model, hasImage, scenario.expectWarning)
|
||||
|
||||
// In actual integration, the warning would appear when:
|
||||
// 1. hasImage is true
|
||||
// 2. model is in the incompatible list
|
||||
// The openai package tests cover the actual warning functionality
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -71,6 +72,14 @@ func (o *Client) SetResponsesAPIEnabled(enabled bool) {
|
||||
o.ImplementsResponses = enabled
|
||||
}
|
||||
|
||||
// checkImageGenerationCompatibility warns if the model doesn't support image generation
|
||||
func checkImageGenerationCompatibility(model string) {
|
||||
if !supportsImageGeneration(model) {
|
||||
fmt.Fprintf(os.Stderr, "Warning: Model '%s' does not support image generation. Supported models: %s. Consider using -m gpt-4o for image generation.\n",
|
||||
model, strings.Join(ImageGenerationSupportedModels, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Client) configure() (ret error) {
|
||||
opts := []option.RequestOption{option.WithAPIKey(o.ApiKey.Value)}
|
||||
if o.ApiBaseURL.Value != "" {
|
||||
@@ -154,6 +163,11 @@ func (o *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o
|
||||
}
|
||||
|
||||
func (o *Client) sendResponses(ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions) (ret string, err error) {
|
||||
// Warn if model doesn't support image generation when image file is specified
|
||||
if opts.ImageFile != "" {
|
||||
checkImageGenerationCompatibility(opts.Model)
|
||||
}
|
||||
|
||||
// Validate model supports image generation if image file is specified
|
||||
if opts.ImageFile != "" && !supportsImageGeneration(opts.Model) {
|
||||
return "", fmt.Errorf("model '%s' does not support image generation. Supported models: %s", opts.Model, strings.Join(ImageGenerationSupportedModels, ", "))
|
||||
|
||||
@@ -28,6 +28,9 @@ var ImageGenerationSupportedModels = []string{
|
||||
"gpt-4.1-mini",
|
||||
"gpt-4.1-nano",
|
||||
"o3",
|
||||
"gpt-5",
|
||||
"gpt-5-nano",
|
||||
"gpt-5.2",
|
||||
}
|
||||
|
||||
// supportsImageGeneration checks if the given model supports the image_generation tool
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package openai
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -257,6 +259,21 @@ func TestSupportsImageGeneration(t *testing.T) {
|
||||
model: "o3",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "gpt-5 supports image generation",
|
||||
model: "gpt-5",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "gpt-5-nano supports image generation",
|
||||
model: "gpt-5-nano",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "gpt-5.2 supports image generation",
|
||||
model: "gpt-5.2",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "o1 does not support image generation",
|
||||
model: "o1",
|
||||
@@ -442,3 +459,165 @@ func TestAddImageGenerationToolWithUserParameters(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckImageGenerationCompatibility(t *testing.T) {
|
||||
// Capture stderr output
|
||||
oldStderr := os.Stderr
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stderr = w
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
model string
|
||||
expectWarning bool
|
||||
expectedText string
|
||||
}{
|
||||
{
|
||||
name: "Supported model - no warning",
|
||||
model: "gpt-4o",
|
||||
expectWarning: false,
|
||||
},
|
||||
{
|
||||
name: "Unsupported model - warning expected",
|
||||
model: "o1-mini",
|
||||
expectWarning: true,
|
||||
expectedText: "Warning: Model 'o1-mini' does not support image generation",
|
||||
},
|
||||
{
|
||||
name: "Another unsupported model - warning expected",
|
||||
model: "gpt-3.5-turbo",
|
||||
expectWarning: true,
|
||||
expectedText: "Warning: Model 'gpt-3.5-turbo' does not support image generation",
|
||||
},
|
||||
{
|
||||
name: "Supported o3 model - no warning",
|
||||
model: "o3",
|
||||
expectWarning: false,
|
||||
},
|
||||
{
|
||||
name: "Empty model - warning expected",
|
||||
model: "",
|
||||
expectWarning: true,
|
||||
expectedText: "Warning: Model '' does not support image generation",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Reset pipe for each test
|
||||
r, w, _ = os.Pipe()
|
||||
os.Stderr = w
|
||||
|
||||
checkImageGenerationCompatibility(tt.model)
|
||||
|
||||
// Close writer and read output
|
||||
w.Close()
|
||||
var buf bytes.Buffer
|
||||
buf.ReadFrom(r)
|
||||
output := buf.String()
|
||||
|
||||
if tt.expectWarning {
|
||||
assert.NotEmpty(t, output, "Expected warning output for unsupported model")
|
||||
assert.Contains(t, output, tt.expectedText, "Warning message should contain model name")
|
||||
assert.Contains(t, output, "Supported models:", "Warning should mention supported models")
|
||||
assert.Contains(t, output, "gpt-4o", "Warning should suggest gpt-4o")
|
||||
} else {
|
||||
assert.Empty(t, output, "No warning expected for supported model")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Restore stderr
|
||||
os.Stderr = oldStderr
|
||||
}
|
||||
|
||||
func TestSendResponses_WithWarningIntegration(t *testing.T) {
|
||||
client := NewClient()
|
||||
client.ApiKey.Value = "test-api-key"
|
||||
client.ApiBaseURL.Value = "https://api.openai.com/v1"
|
||||
client.ImplementsResponses = true
|
||||
client.Configure() // Initialize client
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
model string
|
||||
imageFile string
|
||||
expectWarning bool
|
||||
expectError bool
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Unsupported model with image - warning then error",
|
||||
model: "o1-mini",
|
||||
imageFile: "test.png",
|
||||
expectWarning: true,
|
||||
expectError: true,
|
||||
expectedError: "model 'o1-mini' does not support image generation",
|
||||
},
|
||||
{
|
||||
name: "Supported model with image - no warning, no error",
|
||||
model: "gpt-4o",
|
||||
imageFile: "test.png",
|
||||
expectWarning: false,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Unsupported model without image - no warning, no error",
|
||||
model: "o1-mini",
|
||||
imageFile: "",
|
||||
expectWarning: false,
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Capture stderr for warning detection
|
||||
oldStderr := os.Stderr
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stderr = w
|
||||
|
||||
opts := &domain.ChatOptions{
|
||||
Model: tt.model,
|
||||
ImageFile: tt.imageFile,
|
||||
}
|
||||
|
||||
msgs := []*chat.ChatCompletionMessage{
|
||||
{Role: "user", Content: "Generate an image"},
|
||||
}
|
||||
|
||||
// Call sendResponses - this will trigger the warning and potentially error
|
||||
_, err := client.sendResponses(nil, msgs, opts)
|
||||
|
||||
// Close writer and read warning output
|
||||
w.Close()
|
||||
var buf bytes.Buffer
|
||||
buf.ReadFrom(r)
|
||||
warningOutput := buf.String()
|
||||
|
||||
// Restore stderr
|
||||
os.Stderr = oldStderr
|
||||
|
||||
// Check warning expectations
|
||||
if tt.expectWarning {
|
||||
assert.NotEmpty(t, warningOutput, "Expected warning output")
|
||||
assert.Contains(t, warningOutput, "Warning: Model '"+tt.model+"' does not support image generation")
|
||||
} else {
|
||||
assert.Empty(t, warningOutput, "No warning expected")
|
||||
}
|
||||
|
||||
// Check error expectations
|
||||
if tt.expectError {
|
||||
assert.Error(t, err, "Expected error for unsupported model with image")
|
||||
assert.Contains(t, err.Error(), tt.expectedError)
|
||||
} else {
|
||||
// We expect an error here because we don't have a real API key/config
|
||||
// But it shouldn't be the image generation validation error
|
||||
if err != nil {
|
||||
assert.NotContains(t, err.Error(), "does not support image generation",
|
||||
"Should not get image generation error for supported cases")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,11 @@ var ProviderMap = map[string]ProviderConfig{
|
||||
ModelsURL: "https://models.github.ai/catalog", // FetchModelsDirectly will append /models
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
"Infermatic": {
|
||||
Name: "Infermatic",
|
||||
BaseURL: "https://api.totalgpt.ai/v1",
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
"GrokAI": {
|
||||
Name: "GrokAI",
|
||||
BaseURL: "https://api.x.ai/v1",
|
||||
|
||||
@@ -30,6 +30,11 @@ func TestCreateClient(t *testing.T) {
|
||||
provider: "Abacus",
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: "Existing provider - Infermatic",
|
||||
provider: "Infermatic",
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: "Existing provider - MiniMax",
|
||||
provider: "MiniMax",
|
||||
|
||||
@@ -1 +1 @@
|
||||
"1.4.383"
|
||||
"1.4.385"
|
||||
|
||||
104
patterns/create_bd_issue/system.md
Normal file
104
patterns/create_bd_issue/system.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You are an expert at transforming natural language issue descriptions into optimal `bd create` commands. You understand the bd (Beads) issue tracker deeply and always select the most appropriate flags based on the user's intent.
|
||||
|
||||
Your goal is to produce a single, well-crafted `bd create` command that captures all the relevant details from the input while following bd best practices.
|
||||
|
||||
# BD CREATE REFERENCE
|
||||
|
||||
Available flags:
|
||||
- `--title "string"` or positional first arg: Issue title (imperative mood: "Add...", "Fix...", "Update...")
|
||||
- `-d, --description "string"`: Issue description (context, acceptance criteria, notes)
|
||||
- `-t, --type TYPE`: bug|feature|task|epic|chore|merge-request|molecule|gate|agent|role|rig|convoy|event (default: task)
|
||||
- `-p, --priority P0-P4`: P0=critical, P1=high, P2=normal (default), P3=low, P4=wishlist
|
||||
- `-l, --labels strings`: Comma-separated labels (e.g., ux,backend,docs)
|
||||
- `-a, --assignee string`: Who should work on this
|
||||
- `-e, --estimate int`: Time estimate in minutes
|
||||
- `--due string`: Due date (+6h, +1d, +2w, tomorrow, next monday, 2025-01-15)
|
||||
- `--defer string`: Hide until date (same formats as --due)
|
||||
- `--deps strings`: Dependencies (e.g., 'bd-20' or 'blocks:bd-15')
|
||||
- `--parent string`: Parent issue ID for hierarchical child
|
||||
- `--acceptance string`: Acceptance criteria
|
||||
- `--design string`: Design notes
|
||||
- `--notes string`: Additional notes
|
||||
- `--external-ref string`: External reference (e.g., 'gh-9', 'jira-ABC')
|
||||
- `--ephemeral`: Mark as ephemeral (not exported)
|
||||
- `--prefix string` or `--rig string`: Create in specific rig
|
||||
- `--repo string`: Target repository for issue
|
||||
|
||||
Type-specific flags:
|
||||
- Molecules: `--mol-type swarm|patrol|work`
|
||||
- Agents: `--agent-rig string`, `--role-type polecat|crew|witness|refinery|mayor|deacon`
|
||||
- Events: `--event-actor string`, `--event-category string`, `--event-payload string`, `--event-target string`
|
||||
- Gates: `--waits-for string`, `--waits-for-gate all-children|any-children`
|
||||
|
||||
# STEPS
|
||||
|
||||
1. Parse the input to understand:
|
||||
- What is being requested (the core action/fix/feature)
|
||||
- Any context or background
|
||||
- Urgency or priority signals
|
||||
- Technical domain (for labels)
|
||||
- Time-related constraints
|
||||
- Dependencies or blockers
|
||||
- Acceptance criteria
|
||||
|
||||
2. Determine the issue type:
|
||||
- bug: Something is broken
|
||||
- feature: New capability
|
||||
- task: Work that needs doing
|
||||
- epic: Large multi-part effort
|
||||
- chore: Maintenance/cleanup
|
||||
|
||||
3. Assess priority:
|
||||
- P0: Production down, security breach, data loss
|
||||
- P1: Major functionality broken, blocking release
|
||||
- P2: Standard work (default)
|
||||
- P3: Nice to have, can wait
|
||||
- P4: Someday/maybe, ideas
|
||||
|
||||
4. Select appropriate labels (limit to 1-4):
|
||||
- Domain: frontend, backend, api, db, infra, mobile
|
||||
- Category: ux, perf, security, docs, tech-debt
|
||||
- Size: quick-win, spike, refactor
|
||||
|
||||
5. Construct the optimal command:
|
||||
- Title: 3-8 words, imperative mood
|
||||
- Description: 1-3 sentences if complex
|
||||
- Only include flags that add value (skip defaults)
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- Output ONLY the bd create command, nothing else
|
||||
- No markdown code blocks, no explanations, no warnings
|
||||
- Use double quotes for all string values
|
||||
- Escape any internal quotes with backslash
|
||||
- If description is short, use -d; if long, suggest --body-file
|
||||
- Prefer explicit type when not "task"
|
||||
- Only include priority when not P2 (default)
|
||||
- Only include labels when they add categorization value
|
||||
- Order flags: type, priority, labels, then others
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
Input: "We need to add dark mode to the settings page"
|
||||
Output: bd create "Add dark mode toggle to settings page" -t feature -l ux,frontend
|
||||
|
||||
Input: "URGENT: login is broken on production"
|
||||
Output: bd create "Fix broken login on production" -t bug -p P0 -d "Login functionality is completely broken in production environment"
|
||||
|
||||
Input: "maybe someday we could add keyboard shortcuts"
|
||||
Output: bd create "Add keyboard shortcuts" -t feature -p P4 -l ux
|
||||
|
||||
Input: "need to update the deps before next week"
|
||||
Output: bd create "Update dependencies" -t chore --due "next week"
|
||||
|
||||
Input: "the api docs are missing the new v2 endpoints, john should handle it"
|
||||
Output: bd create "Document v2 API endpoints" -t task -l docs,api -a john
|
||||
|
||||
Input: "track time spent on customer dashboard - estimate about 2 hours"
|
||||
Output: bd create "Track time spent on customer dashboard" -e 120 -l analytics
|
||||
|
||||
# INPUT
|
||||
|
||||
INPUT:
|
||||
96
patterns/extract_bd_ideas/system.md
Normal file
96
patterns/extract_bd_ideas/system.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You are an expert at extracting actionable ideas from content and transforming them into well-structured issue tracker commands. You analyze input text—meeting notes, brainstorms, articles, conversations, or any content—and identify concrete, actionable items that should be tracked as issues.
|
||||
|
||||
You understand that good issues are:
|
||||
- Specific and actionable (not vague wishes)
|
||||
- Appropriately scoped (not too big, not too small)
|
||||
- Self-contained (understandable without reading the source)
|
||||
- Prioritized by impact and urgency
|
||||
|
||||
Take a step back and think step-by-step about how to achieve the best possible results.
|
||||
|
||||
# STEPS
|
||||
|
||||
1. Read the input content thoroughly, looking for:
|
||||
- Explicit tasks or action items mentioned
|
||||
- Problems that need solving
|
||||
- Ideas that could be implemented
|
||||
- Improvements or enhancements suggested
|
||||
- Bugs or issues reported
|
||||
- Features requested
|
||||
|
||||
2. For each potential issue, evaluate:
|
||||
- Is this actionable? (Skip vague wishes or opinions)
|
||||
- Is this appropriately scoped? (Break down large items)
|
||||
- What priority does this deserve? (P0=critical, P1=high, P2=normal, P3=low, P4=wishlist)
|
||||
- What type is it? (feature, bug, task, idea, improvement)
|
||||
- What labels apply? (e.g., ux, backend, docs, perf)
|
||||
|
||||
3. Transform each item into a bd create command with:
|
||||
- Clear, concise title (imperative mood: "Add...", "Fix...", "Update...")
|
||||
- Description providing context from the source
|
||||
- Appropriate priority
|
||||
- Relevant labels
|
||||
|
||||
4. Order results by priority (highest first)
|
||||
|
||||
# OUTPUT SECTIONS
|
||||
|
||||
## SUMMARY
|
||||
|
||||
A 2-3 sentence summary of what was analyzed and how many actionable items were found.
|
||||
|
||||
## ISSUES
|
||||
|
||||
Output each issue as a `bd create` command, one per line, formatted for easy copy-paste execution.
|
||||
|
||||
## SKIPPED
|
||||
|
||||
List any items from the input that were considered but not included, with brief reason (e.g., "too vague", "not actionable", "duplicate of above").
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- Output in Markdown format
|
||||
- Each bd command should be on its own line in a code block
|
||||
- Use this exact format for commands:
|
||||
```bash
|
||||
bd create "Title in imperative mood" -d "Description with context" -p P2 -l label1,label2
|
||||
```
|
||||
- Priorities: P0 (critical/blocking), P1 (high/important), P2 (normal), P3 (low), P4 (wishlist)
|
||||
- Common labels: bug, feature, task, idea, docs, ux, backend, frontend, perf, security, tech-debt
|
||||
- Titles should be 3-8 words, imperative mood ("Add X", "Fix Y", "Update Z")
|
||||
- Descriptions should be 1-3 sentences providing context
|
||||
- Do not include dependencies (--deps) unless explicitly stated in the source
|
||||
- Do not include estimates (--estimate) unless explicitly stated
|
||||
- Do not give warnings or notes outside the defined sections
|
||||
- Extract at least 3 ideas if possible, maximum 20
|
||||
- Ensure each issue is distinct—no duplicates
|
||||
|
||||
# EXAMPLE OUTPUT
|
||||
|
||||
## SUMMARY
|
||||
|
||||
Analyzed meeting notes from product planning session. Found 7 actionable items ranging from critical bugs to wishlist features.
|
||||
|
||||
## ISSUES
|
||||
|
||||
```bash
|
||||
bd create "Fix login timeout on mobile Safari" -d "Users report being logged out after 5 minutes on iOS Safari. Affects conversion flow." -p P1 -l bug,mobile,auth
|
||||
|
||||
bd create "Add dark mode toggle to settings" -d "Multiple user requests for dark mode. Should respect system preference by default." -p P2 -l feature,ux,settings
|
||||
|
||||
bd create "Update API docs for v2 endpoints" -d "New endpoints from last sprint are undocumented. Blocking external integrations." -p P1 -l docs,api,tech-debt
|
||||
|
||||
bd create "Explore caching for dashboard queries" -d "Dashboard load times averaging 3s. Consider Redis or query optimization." -p P3 -l perf,backend,idea
|
||||
```
|
||||
|
||||
## SKIPPED
|
||||
|
||||
- "Make everything faster" - too vague, not actionable
|
||||
- "The design looks nice" - not an action item
|
||||
- "Fix the bug" - insufficient detail to create issue
|
||||
|
||||
# INPUT
|
||||
|
||||
INPUT:
|
||||
Reference in New Issue
Block a user