Skip to content

Commit a97defe

Browse files
refactor(prompt): inline story ID and title into commit message template
Replace [Story ID] and [Story Title] placeholders with {{STORY_ID}} and {{STORY_TITLE}} that get substituted at prompt build time, so the agent gets the exact commit message instead of having to derive it from the story context JSON.
1 parent 97bd382 commit a97defe

4 files changed

Lines changed: 24 additions & 9 deletions

File tree

embed/embed.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ var detectSetupPromptTemplate string
2626
// current story context substituted. The storyContext is the JSON of the
2727
// current story to work on, inlined directly into the prompt so that the
2828
// agent does not need to read the entire prd.json file.
29-
func GetPrompt(prdPath, progressPath, storyContext string) string {
29+
func GetPrompt(prdPath, progressPath, storyContext, storyID, storyTitle string) string {
3030
result := strings.ReplaceAll(promptTemplate, "{{PRD_PATH}}", prdPath)
3131
result = strings.ReplaceAll(result, "{{PROGRESS_PATH}}", progressPath)
32-
return strings.ReplaceAll(result, "{{STORY_CONTEXT}}", storyContext)
32+
result = strings.ReplaceAll(result, "{{STORY_CONTEXT}}", storyContext)
33+
result = strings.ReplaceAll(result, "{{STORY_ID}}", storyID)
34+
return strings.ReplaceAll(result, "{{STORY_TITLE}}", storyTitle)
3335
}
3436

3537
// GetInitPrompt returns the PRD generator prompt with the PRD directory and optional context substituted.

embed/embed_test.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ func TestGetPrompt(t *testing.T) {
99
prdPath := "/path/to/prd.json"
1010
progressPath := "/path/to/progress.md"
1111
storyContext := `{"id":"US-001","title":"Test Story"}`
12-
prompt := GetPrompt(prdPath, progressPath, storyContext)
12+
prompt := GetPrompt(prdPath, progressPath, storyContext, "US-001", "Test Story")
1313

1414
// Verify all placeholders were substituted
1515
if strings.Contains(prompt, "{{PRD_PATH}}") {
@@ -21,6 +21,17 @@ func TestGetPrompt(t *testing.T) {
2121
if strings.Contains(prompt, "{{STORY_CONTEXT}}") {
2222
t.Error("Expected {{STORY_CONTEXT}} to be substituted")
2323
}
24+
if strings.Contains(prompt, "{{STORY_ID}}") {
25+
t.Error("Expected {{STORY_ID}} to be substituted")
26+
}
27+
if strings.Contains(prompt, "{{STORY_TITLE}}") {
28+
t.Error("Expected {{STORY_TITLE}} to be substituted")
29+
}
30+
31+
// Verify the commit message contains the exact story ID and title
32+
if !strings.Contains(prompt, "feat: US-001 - Test Story") {
33+
t.Error("Expected prompt to contain exact commit message 'feat: US-001 - Test Story'")
34+
}
2435

2536
// Verify the PRD path appears in the prompt
2637
if !strings.Contains(prompt, prdPath) {
@@ -52,7 +63,7 @@ func TestGetPrompt(t *testing.T) {
5263
}
5364

5465
func TestGetPrompt_NoFileReadInstruction(t *testing.T) {
55-
prompt := GetPrompt("/path/prd.json", "/path/progress.md", `{"id":"US-001"}`)
66+
prompt := GetPrompt("/path/prd.json", "/path/progress.md", `{"id":"US-001"}`, "US-001", "Test Story")
5667

5768
// The prompt should NOT instruct Claude to read the PRD file
5869
if strings.Contains(prompt, "Read the PRD") {
@@ -67,7 +78,7 @@ func TestPromptTemplateNotEmpty(t *testing.T) {
6778
}
6879

6980
func TestGetPrompt_ChiefExclusion(t *testing.T) {
70-
prompt := GetPrompt("/path/prd.json", "/path/progress.md", `{"id":"US-001"}`)
81+
prompt := GetPrompt("/path/prd.json", "/path/progress.md", `{"id":"US-001"}`, "US-001", "Test Story")
7182

7283
// The prompt must instruct Claude to never stage or commit .chief/ files
7384
if !strings.Contains(prompt, ".chief/") {

embed/prompt.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Your current story:
1313
2. After determining which story to work on, output exact story id, e.g.: <ralph-status>US-056</ralph-status>
1414
3. Implement the user story above
1515
4. Run quality checks (e.g., typecheck, lint, test - use whatever your project requires)
16-
5. If checks pass, commit changes with message: `feat: [Story ID] - [Story Title]`
16+
5. If checks pass, commit changes with message: `feat: {{STORY_ID}} - {{STORY_TITLE}}`
1717
- **NEVER stage or commit `.chief/` files** — these are local working files and must stay out of version control
1818
- Stage only the files you changed for the story (do NOT use `git add -A` or `git add .`)
1919
6. Update the PRD at `{{PRD_PATH}}` to set `passes: true` for the completed story

internal/loop/loop.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,14 @@ func promptBuilderForPRD(prdPath string) func() (string, error) {
102102
return "", fmt.Errorf("failed to load PRD for prompt: %w", err)
103103
}
104104

105-
storyCtx := p.NextStoryContext()
106-
if storyCtx == nil {
105+
story := p.NextStory()
106+
if story == nil {
107107
return "", fmt.Errorf("all stories are complete")
108108
}
109109

110-
return embed.GetPrompt(prdPath, prd.ProgressPath(prdPath), *storyCtx), nil
110+
storyCtx := p.NextStoryContext()
111+
112+
return embed.GetPrompt(prdPath, prd.ProgressPath(prdPath), *storyCtx, story.ID, story.Title), nil
111113
}
112114
}
113115

0 commit comments

Comments
 (0)