summaryrefslogtreecommitdiff
path: root/internal/executor/summary.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/executor/summary.go')
-rw-r--r--internal/executor/summary.go57
1 files changed, 57 insertions, 0 deletions
diff --git a/internal/executor/summary.go b/internal/executor/summary.go
new file mode 100644
index 0000000..a942de0
--- /dev/null
+++ b/internal/executor/summary.go
@@ -0,0 +1,57 @@
+package executor
+
+import (
+ "bufio"
+ "encoding/json"
+ "os"
+ "strings"
+)
+
+// extractSummary reads a stream-json stdout log and returns the text following
+// the last "## Summary" heading found in any assistant text block.
+// Returns empty string if the file cannot be read or no summary is found.
+func extractSummary(stdoutPath string) string {
+ f, err := os.Open(stdoutPath)
+ if err != nil {
+ return ""
+ }
+ defer f.Close()
+
+ var last string
+ scanner := bufio.NewScanner(f)
+ scanner.Buffer(make([]byte, 1024*1024), 1024*1024)
+ for scanner.Scan() {
+ if text := summaryFromLine(scanner.Bytes()); text != "" {
+ last = text
+ }
+ }
+ return last
+}
+
+// summaryFromLine parses a single stream-json line and returns the text after
+// "## Summary" if the line is an assistant text block containing that heading.
+func summaryFromLine(line []byte) string {
+ var event struct {
+ Type string `json:"type"`
+ Message struct {
+ Content []struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ } `json:"content"`
+ } `json:"message"`
+ }
+ if err := json.Unmarshal(line, &event); err != nil || event.Type != "assistant" {
+ return ""
+ }
+ for _, block := range event.Message.Content {
+ if block.Type != "text" {
+ continue
+ }
+ idx := strings.Index(block.Text, "## Summary")
+ if idx == -1 {
+ continue
+ }
+ return strings.TrimSpace(block.Text[idx+len("## Summary"):])
+ }
+ return ""
+}