Compare commits

...

14 Commits

Author SHA1 Message Date
github-actions[bot]
90712506f1 chore(release): Update version to v1.4.374 2026-01-05 17:23:23 +00:00
Kayvan Sylvan
edc02120bb Merge pull request #1924 from ksylvan/rename-code_helper-to-code2context
Rename `code_helper` to `code2context` across documentation and CLI
2026-01-05 09:20:30 -08:00
Kayvan Sylvan
8f05883581 chore: incoming 1924 changelog entry 2026-01-05 09:17:13 -08:00
Kayvan Sylvan
996933e687 docs: rename code_helper to code2context across documentation and CLI
- Rename `code_helper` command to `code2context` throughout codebase
- Update README.md table of contents and references
- Update installation instructions with new binary name
- Update all usage examples in main.go help text
- Update create_coding_feature pattern documentation
- Rename cmd directory from code_helper to code2context
2026-01-05 08:35:25 -08:00
github-actions[bot]
8806f4c2f4 chore(release): Update version to v1.4.373 2026-01-04 21:08:00 +00:00
Kayvan Sylvan
b381bae24a Merge pull request #1915 from majiayu000/fix-1842-feature-request-parallelize-au-0101-2335
feat: parallelize audio chunk transcription for improved performance
2026-01-04 13:04:56 -08:00
Kayvan Sylvan
a6c753499b chore: incoming 1915 changelog entry 2026-01-04 13:01:42 -08:00
Kayvan Sylvan
90b2975fba Merge pull request #1914 from majiayu000/fix-1869-feature-request-make-codehelpe-0101-2323
feat(code_helper): add stdin support for piping file lists
2026-01-04 12:55:06 -08:00
Kayvan Sylvan
145499ee4c chore: incoming 1914 changelog entry 2026-01-04 12:51:06 -08:00
Kayvan Sylvan
f9359c99dc Merge branch 'main' into fix-1869-feature-request-make-codehelpe-0101-2323 2026-01-04 12:48:30 -08:00
Kayvan Sylvan
cb2759a5a1 Merge branch 'main' into fix-1842-feature-request-parallelize-au-0101-2335 2026-01-03 17:05:14 -08:00
Kayvan Sylvan
c32a650eaa Merge branch 'main' into fix-1869-feature-request-make-codehelpe-0101-2323 2026-01-03 17:03:59 -08:00
majiayu000
8a28ca7b1e feat: parallelize audio chunk transcription using goroutines
Signed-off-by: majiayu000 <1835304752@qq.com>
2026-01-01 23:38:32 +08:00
majiayu000
435d61ae0e feat(code_helper): add stdin support for piping file lists
Add ability to pipe file lists to code_helper via stdin, enabling
use cases like:
  find . -name '*.go' | code_helper "instructions"
  git ls-files '*.py' | code_helper "Add type hints"

The tool now detects if stdin is a pipe and accepts a single argument
(instructions) in that mode, reading file paths from stdin line by line.

Backward compatible with existing directory scanning mode.

Signed-off-by: majiayu000 <1835304752@qq.com>
2026-01-01 23:28:11 +08:00
11 changed files with 366 additions and 94 deletions

View File

@@ -1,5 +1,27 @@
# Changelog # Changelog
## v1.4.374 (2026-01-05)
### PR [#1924](https://github.com/danielmiessler/Fabric/pull/1924) by [ksylvan](https://github.com/ksylvan): Rename `code_helper` to `code2context` across documentation and CLI
- Rename `code_helper` command to `code2context` throughout codebase
- Update README.md table of contents and references
- Update installation instructions with new binary name
- Update all usage examples in main.go help text
- Update create_coding_feature pattern documentation
## v1.4.373 (2026-01-04)
### PR [#1914](https://github.com/danielmiessler/Fabric/pull/1914) by [majiayu000](https://github.com/majiayu000): feat(code_helper): add stdin support for piping file lists
- Added stdin support for piping file lists to code_helper, enabling commands like `find . -name '*.go' | code_helper "instructions"` and `git ls-files '*.py' | code_helper "Add type hints"`
- Implemented automatic detection of stdin pipe mode with single argument (instructions) support
- Enhanced tool to read file paths from stdin line by line while maintaining backward compatibility with existing directory scanning functionality
### PR [#1915](https://github.com/danielmiessler/Fabric/pull/1915) by [majiayu000](https://github.com/majiayu000): feat: parallelize audio chunk transcription for improved performance
- Parallelize audio chunk transcription using goroutines for improved performance
## v1.4.372 (2026-01-04) ## v1.4.372 (2026-01-04)
### PR [#1913](https://github.com/danielmiessler/Fabric/pull/1913) by [majiayu000](https://github.com/majiayu000): fix: REST API /chat endpoint doesn't pass 'search' parameter to ChatOptions ### PR [#1913](https://github.com/danielmiessler/Fabric/pull/1913) by [majiayu000](https://github.com/majiayu000): fix: REST API /chat endpoint doesn't pass 'search' parameter to ChatOptions

View File

@@ -185,7 +185,7 @@ Keep in mind that many of these were recorded when Fabric was Python-based, so r
- [Helper Apps](#helper-apps) - [Helper Apps](#helper-apps)
- [`to_pdf`](#to_pdf) - [`to_pdf`](#to_pdf)
- [`to_pdf` Installation](#to_pdf-installation) - [`to_pdf` Installation](#to_pdf-installation)
- [`code_helper`](#code_helper) - [`code2context`](#code2context)
- [pbpaste](#pbpaste) - [pbpaste](#pbpaste)
- [Web Interface (Fabric Web App)](#web-interface-fabric-web-app) - [Web Interface (Fabric Web App)](#web-interface-fabric-web-app)
- [Meta](#meta) - [Meta](#meta)
@@ -904,9 +904,9 @@ go install github.com/danielmiessler/fabric/cmd/to_pdf@latest
Make sure you have a LaTeX distribution (like TeX Live or MiKTeX) installed on your system, as `to_pdf` requires `pdflatex` to be available in your system's PATH. Make sure you have a LaTeX distribution (like TeX Live or MiKTeX) installed on your system, as `to_pdf` requires `pdflatex` to be available in your system's PATH.
### `code_helper` ### `code2context`
`code_helper` is used in conjunction with the `create_coding_feature` pattern. `code2context` is used in conjunction with the `create_coding_feature` pattern.
It generates a `json` representation of a directory of code that can be fed into an AI model It generates a `json` representation of a directory of code that can be fed into an AI model
with instructions to create a new feature or edit the code in a specified way. with instructions to create a new feature or edit the code in a specified way.
@@ -915,7 +915,7 @@ See [the Create Coding Feature Pattern README](./data/patterns/create_coding_fea
Install it first using: Install it first using:
```bash ```bash
go install github.com/danielmiessler/fabric/cmd/code_helper@latest go install github.com/danielmiessler/fabric/cmd/code2context@latest
``` ```
## pbpaste ## pbpaste

View File

@@ -131,6 +131,75 @@ func ScanDirectory(rootDir string, maxDepth int, instructions string, ignoreList
return json.MarshalIndent(data, "", " ") return json.MarshalIndent(data, "", " ")
} }
// ScanFiles scans specific files and returns a JSON representation
func ScanFiles(files []string, instructions string) ([]byte, error) {
fileCount := 0
dirSet := make(map[string]bool)
// Create root directory item
rootItem := FileItem{
Type: "directory",
Name: ".",
Contents: []FileItem{},
}
for _, filePath := range files {
// Skip directories
info, err := os.Stat(filePath)
if err != nil {
return nil, fmt.Errorf("error accessing file %s: %v", filePath, err)
}
if info.IsDir() {
continue
}
// Track unique directories
dir := filepath.Dir(filePath)
if dir != "." {
dirSet[dir] = true
}
fileCount++
// Read file content
content, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("error reading file %s: %v", filePath, err)
}
// Clean path for consistent handling
cleanPath := filepath.Clean(filePath)
if strings.HasPrefix(cleanPath, "./") {
cleanPath = cleanPath[2:]
}
// Add file to the structure
addFileToDirectory(&rootItem, cleanPath, string(content), ".")
}
// Create final data structure
var data []any
data = append(data, rootItem)
// Add report
reportItem := map[string]any{
"type": "report",
"directories": len(dirSet) + 1,
"files": fileCount,
}
data = append(data, reportItem)
// Add instructions
instructionsItem := map[string]any{
"type": "instructions",
"name": "code_change_instructions",
"details": instructions,
}
data = append(data, instructionsItem)
return json.MarshalIndent(data, "", " ")
}
// addFileToDirectory adds a file to the correct directory in the structure // addFileToDirectory adds a file to the correct directory in the structure
func addFileToDirectory(root *FileItem, path, content, rootDir string) { func addFileToDirectory(root *FileItem, path, content, rootDir string) {
parts := strings.Split(path, string(filepath.Separator)) parts := strings.Split(path, string(filepath.Separator))

View File

@@ -0,0 +1,100 @@
package main
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestScanFiles(t *testing.T) {
// Create temp directory with test files
tmpDir := t.TempDir()
// Create test files
file1 := filepath.Join(tmpDir, "test1.go")
file2 := filepath.Join(tmpDir, "test2.go")
subDir := filepath.Join(tmpDir, "subdir")
file3 := filepath.Join(subDir, "test3.go")
require.NoError(t, os.WriteFile(file1, []byte("package main\n"), 0644))
require.NoError(t, os.WriteFile(file2, []byte("package main\n\nfunc main() {}\n"), 0644))
require.NoError(t, os.MkdirAll(subDir, 0755))
require.NoError(t, os.WriteFile(file3, []byte("package subdir\n"), 0644))
// Test scanning specific files
files := []string{file1, file3}
instructions := "Test instructions"
jsonData, err := ScanFiles(files, instructions)
require.NoError(t, err)
// Parse the JSON output
var result []any
err = json.Unmarshal(jsonData, &result)
require.NoError(t, err)
assert.Len(t, result, 3) // directory, report, instructions
// Check report
report := result[1].(map[string]any)
assert.Equal(t, "report", report["type"])
assert.Equal(t, float64(2), report["files"])
// Check instructions
instr := result[2].(map[string]any)
assert.Equal(t, "instructions", instr["type"])
assert.Equal(t, "Test instructions", instr["details"])
}
func TestScanFilesSkipsDirectories(t *testing.T) {
tmpDir := t.TempDir()
file1 := filepath.Join(tmpDir, "test.go")
subDir := filepath.Join(tmpDir, "subdir")
require.NoError(t, os.WriteFile(file1, []byte("package main\n"), 0644))
require.NoError(t, os.MkdirAll(subDir, 0755))
// Include a directory in the file list - should be skipped
files := []string{file1, subDir}
jsonData, err := ScanFiles(files, "test")
require.NoError(t, err)
var result []any
err = json.Unmarshal(jsonData, &result)
require.NoError(t, err)
// Check that only 1 file was counted (directory was skipped)
report := result[1].(map[string]any)
assert.Equal(t, float64(1), report["files"])
}
func TestScanFilesNonExistentFile(t *testing.T) {
files := []string{"/nonexistent/file.go"}
_, err := ScanFiles(files, "test")
assert.Error(t, err)
assert.Contains(t, err.Error(), "error accessing file")
}
func TestScanDirectory(t *testing.T) {
tmpDir := t.TempDir()
file1 := filepath.Join(tmpDir, "main.go")
require.NoError(t, os.WriteFile(file1, []byte("package main\n"), 0644))
jsonData, err := ScanDirectory(tmpDir, 3, "Test instructions", []string{})
require.NoError(t, err)
var result []any
err = json.Unmarshal(jsonData, &result)
require.NoError(t, err)
assert.Len(t, result, 3)
// Check instructions
instr := result[2].(map[string]any)
assert.Equal(t, "Test instructions", instr["details"])
}

109
cmd/code2context/main.go Normal file
View File

@@ -0,0 +1,109 @@
package main
import (
"bufio"
"flag"
"fmt"
"os"
"strings"
)
func main() {
// Command line flags
maxDepth := flag.Int("depth", 3, "Maximum directory depth to scan")
ignorePatterns := flag.String("ignore", ".git,node_modules,vendor", "Comma-separated patterns to ignore")
outputFile := flag.String("out", "", "Output file (default: stdout)")
flag.Usage = printUsage
flag.Parse()
// Check if stdin has data (is a pipe)
stdinInfo, _ := os.Stdin.Stat()
hasStdin := (stdinInfo.Mode() & os.ModeCharDevice) == 0
var jsonData []byte
var err error
if hasStdin {
// Stdin mode: read file list from stdin, instructions from argument
if flag.NArg() != 1 {
fmt.Fprintf(os.Stderr, "Error: When piping file list via stdin, provide exactly 1 argument: <instructions>\n")
fmt.Fprintf(os.Stderr, "Usage: find . -name '*.go' | code2context \"instructions\"\n")
os.Exit(1)
}
instructions := flag.Arg(0)
// Read file paths from stdin
var files []string
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line != "" {
files = append(files, line)
}
}
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "Error reading stdin: %v\n", err)
os.Exit(1)
}
if len(files) == 0 {
fmt.Fprintf(os.Stderr, "Error: No files provided via stdin\n")
os.Exit(1)
}
jsonData, err = ScanFiles(files, instructions)
} else {
// Directory mode: require directory and instructions arguments
if flag.NArg() != 2 {
printUsage()
os.Exit(1)
}
directory := flag.Arg(0)
instructions := flag.Arg(1)
// Validate directory
if info, err := os.Stat(directory); err != nil || !info.IsDir() {
fmt.Fprintf(os.Stderr, "Error: Directory '%s' does not exist or is not a directory\n", directory)
os.Exit(1)
}
// Parse ignore patterns and scan directory
jsonData, err = ScanDirectory(directory, *maxDepth, instructions, strings.Split(*ignorePatterns, ","))
}
if err != nil {
fmt.Fprintf(os.Stderr, "Error scanning: %v\n", err)
os.Exit(1)
}
// Output result
if *outputFile != "" {
if err := os.WriteFile(*outputFile, jsonData, 0644); err != nil {
fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err)
os.Exit(1)
}
} else {
fmt.Print(string(jsonData))
}
}
func printUsage() {
fmt.Fprintf(os.Stderr, `code2context - Code project scanner for use with Fabric AI
Usage:
code2context [options] <directory> <instructions>
<file_list> | code2context [options] <instructions>
Examples:
code2context . "Add input validation to all user inputs"
code2context -depth 4 ./my-project "Implement error handling"
code2context -out project.json ./src "Fix security issues"
find . -name '*.go' | code2context "Refactor error handling"
git ls-files '*.py' | code2context "Add type hints"
Options:
`)
flag.PrintDefaults()
}

View File

@@ -1,65 +0,0 @@
package main
import (
"flag"
"fmt"
"os"
"strings"
)
func main() {
// Command line flags
maxDepth := flag.Int("depth", 3, "Maximum directory depth to scan")
ignorePatterns := flag.String("ignore", ".git,node_modules,vendor", "Comma-separated patterns to ignore")
outputFile := flag.String("out", "", "Output file (default: stdout)")
flag.Usage = printUsage
flag.Parse()
// Require exactly two positional arguments: directory and instructions
if flag.NArg() != 2 {
printUsage()
os.Exit(1)
}
directory := flag.Arg(0)
instructions := flag.Arg(1)
// Validate directory
if info, err := os.Stat(directory); err != nil || !info.IsDir() {
fmt.Fprintf(os.Stderr, "Error: Directory '%s' does not exist or is not a directory\n", directory)
os.Exit(1)
}
// Parse ignore patterns and scan directory
jsonData, err := ScanDirectory(directory, *maxDepth, instructions, strings.Split(*ignorePatterns, ","))
if err != nil {
fmt.Fprintf(os.Stderr, "Error scanning directory: %v\n", err)
os.Exit(1)
}
// Output result
if *outputFile != "" {
if err := os.WriteFile(*outputFile, jsonData, 0644); err != nil {
fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err)
os.Exit(1)
}
} else {
fmt.Print(string(jsonData))
}
}
func printUsage() {
fmt.Fprintf(os.Stderr, `code_helper - Code project scanner for use with Fabric AI
Usage:
code_helper [options] <directory> <instructions>
Examples:
code_helper . "Add input validation to all user inputs"
code_helper -depth 4 ./my-project "Implement error handling"
code_helper -out project.json ./src "Fix security issues"
Options:
`)
flag.PrintDefaults()
}

View File

@@ -1,3 +1,3 @@
package main package main
var version = "v1.4.372" var version = "v1.4.374"

Binary file not shown.

View File

@@ -4,10 +4,10 @@ Generate code changes to an existing coding project using AI.
## Installation ## Installation
After installing the `code_helper` binary: After installing the `code2context` binary:
```bash ```bash
go install github.com/danielmiessler/fabric/cmd/code_helper@latest go install github.com/danielmiessler/fabric/cmd/code2context@latest
``` ```
## Usage ## Usage
@@ -15,18 +15,18 @@ go install github.com/danielmiessler/fabric/cmd/code_helper@latest
The create_coding_feature allows you to apply AI-suggested code changes directly to your project files. Use it like this: The create_coding_feature allows you to apply AI-suggested code changes directly to your project files. Use it like this:
```bash ```bash
code_helper [project_directory] "[instructions for code changes]" | fabric --pattern create_coding_feature code2context [project_directory] "[instructions for code changes]" | fabric --pattern create_coding_feature
``` ```
For example: For example:
```bash ```bash
code_helper . "Create a simple Hello World C program in file main.c" | fabric --pattern create_coding_feature code2context . "Create a simple Hello World C program in file main.c" | fabric --pattern create_coding_feature
``` ```
## How It Works ## How It Works
1. `code_helper` scans your project directory and creates a JSON representation 1. `code2context` scans your project directory and creates a JSON representation
2. The AI model analyzes your project structure and instructions 2. The AI model analyzes your project structure and instructions
3. AI generates file changes in a standard format 3. AI generates file changes in a standard format
4. Fabric parses these changes and prompts you to confirm 4. Fabric parses these changes and prompts you to confirm
@@ -36,7 +36,7 @@ code_helper . "Create a simple Hello World C program in file main.c" | fabric --
```bash ```bash
# Request AI to create a Hello World program # Request AI to create a Hello World program
code_helper . "Create a simple Hello World C program in file main.c" | fabric --pattern create_coding_feature code2context . "Create a simple Hello World C program in file main.c" | fabric --pattern create_coding_feature
# Review the changes made to your project # Review the changes made to your project
git diff git diff
@@ -52,7 +52,7 @@ git commit -s -m "Add Hello World program"
### Security Enhancement Example ### Security Enhancement Example
```bash ```bash
code_helper . "Ensure that all user input is validated and sanitized before being used in the program." | fabric --pattern create_coding_feature code2context . "Ensure that all user input is validated and sanitized before being used in the program." | fabric --pattern create_coding_feature
git diff git diff
make check make check
git add <changed files> git add <changed files>

View File

@@ -10,12 +10,20 @@ import (
"slices" "slices"
"sort" "sort"
"strings" "strings"
"sync"
debuglog "github.com/danielmiessler/fabric/internal/log" debuglog "github.com/danielmiessler/fabric/internal/log"
openai "github.com/openai/openai-go" openai "github.com/openai/openai-go"
) )
// transcriptionResult holds the result of a single chunk transcription.
type transcriptionResult struct {
index int
text string
err error
}
// MaxAudioFileSize defines the maximum allowed size for audio uploads (25MB). // MaxAudioFileSize defines the maximum allowed size for audio uploads (25MB).
const MaxAudioFileSize int64 = 25 * 1024 * 1024 const MaxAudioFileSize int64 = 25 * 1024 * 1024
@@ -73,27 +81,56 @@ func (o *Client) TranscribeFile(ctx context.Context, filePath, model string, spl
files = []string{filePath} files = []string{filePath}
} }
var builder strings.Builder resultsChan := make(chan transcriptionResult, len(files))
var wg sync.WaitGroup
for i, f := range files { for i, f := range files {
debuglog.Log("Using model %s to transcribe part %d (file name: %s)...\n", model, i+1, f) wg.Add(1)
var chunk *os.File go func(index int, filePath string) {
if chunk, err = os.Open(f); err != nil { defer wg.Done()
return "", err debuglog.Log("Using model %s to transcribe part %d (file name: %s)...\n", model, index+1, filePath)
}
params := openai.AudioTranscriptionNewParams{ chunk, openErr := os.Open(filePath)
File: chunk, if openErr != nil {
Model: openai.AudioModel(model), resultsChan <- transcriptionResult{index: index, err: openErr}
} return
var resp *openai.Transcription }
resp, err = o.ApiClient.Audio.Transcriptions.New(ctx, params) defer chunk.Close()
chunk.Close()
if err != nil { params := openai.AudioTranscriptionNewParams{
return "", err File: chunk,
Model: openai.AudioModel(model),
}
resp, transcribeErr := o.ApiClient.Audio.Transcriptions.New(ctx, params)
if transcribeErr != nil {
resultsChan <- transcriptionResult{index: index, err: transcribeErr}
return
}
resultsChan <- transcriptionResult{index: index, text: resp.Text}
}(i, f)
}
wg.Wait()
close(resultsChan)
results := make([]transcriptionResult, 0, len(files))
for result := range resultsChan {
if result.err != nil {
return "", result.err
} }
results = append(results, result)
}
sort.Slice(results, func(i, j int) bool {
return results[i].index < results[j].index
})
var builder strings.Builder
for i, result := range results {
if i > 0 { if i > 0 {
builder.WriteString(" ") builder.WriteString(" ")
} }
builder.WriteString(resp.Text) builder.WriteString(result.text)
} }
return builder.String(), nil return builder.String(), nil

View File

@@ -1 +1 @@
"1.4.372" "1.4.374"