Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,20 @@ func (cc *codingContext) ruleFileWalker(ctx context.Context) func(path string, i
return fmt.Errorf("failed to run bootstrap script (path: %s): %w", path, err)
}

// Expand parameters in rule content
expanded := os.Expand(content, func(key string) string {
if val, ok := cc.params[key]; ok {
return val
}
// this might not exist, in that case, return the original text
return fmt.Sprintf("${%s}", key)
})

// Estimate tokens for this file
tokens := estimateTokens(content)
tokens := estimateTokens(expanded)
cc.totalTokens += tokens
cc.logger.Info("Including rule file", "path", path, "tokens", tokens)
fmt.Fprintln(cc.output, content)
fmt.Fprintln(cc.output, expanded)

return nil
}
Expand Down
36 changes: 36 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ func TestFindExecuteRuleFiles(t *testing.T) {
name string
resume bool
includes selectors
params Params // Parameters for template expansion
setupFiles func(t *testing.T, tmpDir string)
downloadedDirs []string // Directories to add to downloadedDirs
wantTokens int
Expand Down Expand Up @@ -500,6 +501,37 @@ func TestFindExecuteRuleFiles(t *testing.T) {
expectBootstrapRun: false,
bootstrapPath: "CLAUDE-bootstrap",
},
{
name: "rule with parameter substitution",
resume: false,
params: Params{
"issue_key": "PROJ-123",
"project_name": "MyProject",
},
setupFiles: func(t *testing.T, tmpDir string) {
createMarkdownFile(t, filepath.Join(tmpDir, "CLAUDE.md"),
"",
"# Rule with params\nIssue: ${issue_key}\nProject: ${project_name}")
},
wantMinTokens: true,
expectInOutput: "Issue: PROJ-123\nProject: MyProject",
expectNotInOutput: "${issue_key}",
},
{
name: "rule with missing parameter preserved",
resume: false,
params: Params{
"issue_key": "PROJ-456",
},
setupFiles: func(t *testing.T, tmpDir string) {
createMarkdownFile(t, filepath.Join(tmpDir, "CLAUDE.md"),
"",
"# Rule with partial params\nIssue: ${issue_key}\nProject: ${missing_param}")
},
wantMinTokens: true,
expectInOutput: "Issue: PROJ-456\nProject: ${missing_param}",
expectNotInOutput: "${issue_key}",
},
}

for _, tt := range tests {
Expand All @@ -522,6 +554,7 @@ func TestFindExecuteRuleFiles(t *testing.T) {
cc := &codingContext{
resume: tt.resume,
includes: tt.includes,
params: tt.params,
output: &output,
logger: slog.New(slog.NewTextHandler(&logOut, nil)),
cmdRunner: func(cmd *exec.Cmd) error {
Expand All @@ -535,6 +568,9 @@ func TestFindExecuteRuleFiles(t *testing.T) {
if cc.includes == nil {
cc.includes = make(selectors)
}
if cc.params == nil {
cc.params = make(Params)
}

// Set downloadedDirs if specified in test case
if len(tt.downloadedDirs) > 0 {
Expand Down
Loading