summaryrefslogtreecommitdiff
path: root/internal/api
diff options
context:
space:
mode:
Diffstat (limited to 'internal/api')
-rw-r--r--internal/api/elaborate.go26
-rw-r--r--internal/api/elaborate_test.go50
2 files changed, 76 insertions, 0 deletions
diff --git a/internal/api/elaborate.go b/internal/api/elaborate.go
index 5954e29..eb686bf 100644
--- a/internal/api/elaborate.go
+++ b/internal/api/elaborate.go
@@ -105,6 +105,29 @@ func readProjectContext(workDir string) string {
return sb.String()
}
+func (s *Server) appendRawNarrative(workDir, prompt string) {
+ if workDir == "" {
+ return
+ }
+ docsDir := filepath.Join(workDir, "docs")
+ if err := os.MkdirAll(docsDir, 0755); err != nil {
+ s.logger.Error("elaborate: failed to create docs directory", "error", err, "path", docsDir)
+ return
+ }
+ path := filepath.Join(docsDir, "RAW_NARRATIVE.md")
+ f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+ if err != nil {
+ s.logger.Error("elaborate: failed to open RAW_NARRATIVE.md", "error", err, "path", path)
+ return
+ }
+ defer f.Close()
+
+ entry := fmt.Sprintf("\n--- %s ---\n%s\n", time.Now().Format(time.RFC3339), prompt)
+ if _, err := f.WriteString(entry); err != nil {
+ s.logger.Error("elaborate: failed to write to RAW_NARRATIVE.md", "error", err, "path", path)
+ }
+}
+
func (s *Server) handleElaborateTask(w http.ResponseWriter, r *http.Request) {
if s.elaborateLimiter != nil && !s.elaborateLimiter.allow(realIP(r)) {
writeJSON(w, http.StatusTooManyRequests, map[string]string{"error": "rate limit exceeded"})
@@ -129,6 +152,9 @@ func (s *Server) handleElaborateTask(w http.ResponseWriter, r *http.Request) {
workDir = input.ProjectDir
}
+ // Append verbatim user input to RAW_NARRATIVE.md in the background (best effort).
+ go s.appendRawNarrative(workDir, input.Prompt)
+
projectContext := readProjectContext(workDir)
fullPrompt := input.Prompt
if projectContext != "" {
diff --git a/internal/api/elaborate_test.go b/internal/api/elaborate_test.go
index 114e75e..330c111 100644
--- a/internal/api/elaborate_test.go
+++ b/internal/api/elaborate_test.go
@@ -10,6 +10,7 @@ import (
"path/filepath"
"strings"
"testing"
+ "time"
)
// createFakeClaude writes a shell script to a temp dir that prints output and exits with the
@@ -260,3 +261,52 @@ func TestElaborateTask_WithProjectContext(t *testing.T) {
t.Errorf("expected arguments to contain SESSION_STATE.md content, got %s", argsStr)
}
}
+
+func TestElaborateTask_AppendsRawNarrative(t *testing.T) {
+ srv, _ := testServer(t)
+
+ workDir := t.TempDir()
+ prompt := "this is my raw request"
+
+ task := elaboratedTask{
+ Name: "Task",
+ Agent: elaboratedAgent{
+ Instructions: "Instructions",
+ },
+ }
+ taskJSON, _ := json.Marshal(task)
+ wrapper := map[string]string{"result": string(taskJSON)}
+ wrapperJSON, _ := json.Marshal(wrapper)
+
+ srv.elaborateCmdPath = createFakeClaude(t, string(wrapperJSON), 0)
+
+ body := fmt.Sprintf(`{"prompt":"%s", "project_dir":"%s"}`, prompt, workDir)
+ req := httptest.NewRequest("POST", "/api/tasks/elaborate", bytes.NewBufferString(body))
+ req.Header.Set("Content-Type", "application/json")
+ w := httptest.NewRecorder()
+
+ srv.Handler().ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Fatalf("status: want 200, got %d; body: %s", w.Code, w.Body.String())
+ }
+
+ // It runs in a goroutine, so wait a bit
+ path := filepath.Join(workDir, "docs", "RAW_NARRATIVE.md")
+ var data []byte
+ var err error
+ for i := 0; i < 10; i++ {
+ data, err = os.ReadFile(path)
+ if err == nil {
+ break
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+
+ if err != nil {
+ t.Fatalf("failed to read RAW_NARRATIVE.md: %v", err)
+ }
+ if !strings.Contains(string(data), prompt) {
+ t.Errorf("expected RAW_NARRATIVE.md to contain prompt, got %s", string(data))
+ }
+}