perf: reduce ReadLongTerm calls from 5+ to 1 per GetMemoryContext
Refactor GetMemoryContext, GetPlanContext, FormatPlanDisplay to read MEMORY.md once and pass content to internal *From() helpers. Inline regex checks for HasActivePlan/GetPlanStatus/GetCurrentPhase/ GetTotalPhases within the hot path. Public methods preserved for backward compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4507ace9e3
commit
a08d1031b9
1 changed files with 59 additions and 20 deletions
|
|
@ -268,12 +268,15 @@ type PlanStep struct {
|
||||||
|
|
||||||
// GetPlanPhases parses MEMORY.md and returns all phases with their steps.
|
// GetPlanPhases parses MEMORY.md and returns all phases with their steps.
|
||||||
func (ms *MemoryStore) GetPlanPhases() []PlanPhase {
|
func (ms *MemoryStore) GetPlanPhases() []PlanPhase {
|
||||||
content := ms.ReadLongTerm()
|
return ms.getPlanPhasesFrom(ms.ReadLongTerm())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MemoryStore) getPlanPhasesFrom(content string) []PlanPhase {
|
||||||
if !reActivePlan.MatchString(content) {
|
if !reActivePlan.MatchString(content) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
totalPhases := ms.GetTotalPhases()
|
totalPhases := maxPhaseNumber(content)
|
||||||
phases := make([]PlanPhase, 0, totalPhases)
|
phases := make([]PlanPhase, 0, totalPhases)
|
||||||
|
|
||||||
for p := 1; p <= totalPhases; p++ {
|
for p := 1; p <= totalPhases; p++ {
|
||||||
|
|
@ -495,8 +498,10 @@ func BuildInterviewSeed(task, workDir string) string {
|
||||||
// GetInterviewContext returns context for injection during the interviewing phase.
|
// GetInterviewContext returns context for injection during the interviewing phase.
|
||||||
// Includes the full seed + interview guide + target format template.
|
// Includes the full seed + interview guide + target format template.
|
||||||
func (ms *MemoryStore) GetInterviewContext() string {
|
func (ms *MemoryStore) GetInterviewContext() string {
|
||||||
content := ms.ReadLongTerm()
|
return ms.getInterviewContextFrom(ms.ReadLongTerm())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MemoryStore) getInterviewContextFrom(content string) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString("## Active Plan (interviewing)\n\n")
|
sb.WriteString("## Active Plan (interviewing)\n\n")
|
||||||
sb.WriteString(content)
|
sb.WriteString(content)
|
||||||
|
|
@ -543,8 +548,10 @@ func (ms *MemoryStore) GetInterviewContext() string {
|
||||||
// GetReviewContext returns context for injection during the review phase.
|
// GetReviewContext returns context for injection during the review phase.
|
||||||
// Shows the full plan and instructs the AI to wait for user approval.
|
// Shows the full plan and instructs the AI to wait for user approval.
|
||||||
func (ms *MemoryStore) GetReviewContext() string {
|
func (ms *MemoryStore) GetReviewContext() string {
|
||||||
content := ms.ReadLongTerm()
|
return ms.getReviewContextFrom(ms.ReadLongTerm())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MemoryStore) getReviewContextFrom(content string) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString("## Active Plan (awaiting approval)\n\n")
|
sb.WriteString("## Active Plan (awaiting approval)\n\n")
|
||||||
sb.WriteString(content)
|
sb.WriteString(content)
|
||||||
|
|
@ -558,9 +565,15 @@ func (ms *MemoryStore) GetReviewContext() string {
|
||||||
// Only the current phase is shown in detail; completed phases are compressed
|
// Only the current phase is shown in detail; completed phases are compressed
|
||||||
// to one-line summaries; future phases are omitted.
|
// to one-line summaries; future phases are omitted.
|
||||||
func (ms *MemoryStore) GetPlanContext() string {
|
func (ms *MemoryStore) GetPlanContext() string {
|
||||||
content := ms.ReadLongTerm()
|
return ms.getPlanContextFrom(ms.ReadLongTerm())
|
||||||
currentPhase := ms.GetCurrentPhase()
|
}
|
||||||
totalPhases := ms.GetTotalPhases()
|
|
||||||
|
func (ms *MemoryStore) getPlanContextFrom(content string) string {
|
||||||
|
var currentPhase int
|
||||||
|
if m := rePhase.FindStringSubmatch(content); len(m) >= 2 {
|
||||||
|
currentPhase, _ = strconv.Atoi(m[1])
|
||||||
|
}
|
||||||
|
totalPhases := maxPhaseNumber(content)
|
||||||
|
|
||||||
taskLine := ""
|
taskLine := ""
|
||||||
if m := reTaskLine.FindStringSubmatch(content); len(m) >= 2 {
|
if m := reTaskLine.FindStringSubmatch(content); len(m) >= 2 {
|
||||||
|
|
@ -569,18 +582,18 @@ func (ms *MemoryStore) GetPlanContext() string {
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString("## Active Plan\n")
|
sb.WriteString("## Active Plan\n")
|
||||||
sb.WriteString(fmt.Sprintf("Task: %s | Phase %d/%d\n", taskLine, currentPhase, totalPhases))
|
fmt.Fprintf(&sb, "Task: %s | Phase %d/%d\n", taskLine, currentPhase, totalPhases)
|
||||||
|
|
||||||
// Completed phases: one-line summaries
|
// Completed phases: one-line summaries
|
||||||
for p := 1; p < currentPhase; p++ {
|
for p := 1; p < currentPhase; p++ {
|
||||||
title := ms.getPhaseTitle(content, p)
|
title := ms.getPhaseTitle(content, p)
|
||||||
sb.WriteString(fmt.Sprintf("Done: Phase %d (%s)\n", p, title))
|
fmt.Fprintf(&sb, "Done: Phase %d (%s)\n", p, title)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current phase: full detail
|
// Current phase: full detail
|
||||||
if currentPhase > 0 {
|
if currentPhase > 0 {
|
||||||
title := ms.getPhaseTitle(content, currentPhase)
|
title := ms.getPhaseTitle(content, currentPhase)
|
||||||
sb.WriteString(fmt.Sprintf("### Current: Phase %d — %s\n", currentPhase, title))
|
fmt.Fprintf(&sb, "### Current: Phase %d — %s\n", currentPhase, title)
|
||||||
phaseContent := ms.extractPhaseContent(content, currentPhase)
|
phaseContent := ms.extractPhaseContent(content, currentPhase)
|
||||||
sb.WriteString(strings.TrimSpace(phaseContent))
|
sb.WriteString(strings.TrimSpace(phaseContent))
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
|
|
@ -605,6 +618,21 @@ func (ms *MemoryStore) GetPlanContext() string {
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maxPhaseNumber returns the highest phase number found in content.
|
||||||
|
func maxPhaseNumber(content string) int {
|
||||||
|
matches := rePhaseHeader.FindAllStringSubmatch(content, -1)
|
||||||
|
max := 0
|
||||||
|
for _, m := range matches {
|
||||||
|
if len(m) >= 2 {
|
||||||
|
n, _ := strconv.Atoi(m[1])
|
||||||
|
if n > max {
|
||||||
|
max = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
// getPhaseTitle extracts the title of a phase from "## Phase N: Title".
|
// getPhaseTitle extracts the title of a phase from "## Phase N: Title".
|
||||||
func (ms *MemoryStore) getPhaseTitle(content string, phase int) string {
|
func (ms *MemoryStore) getPhaseTitle(content string, phase int) string {
|
||||||
matches := rePhaseHeader.FindAllStringSubmatch(content, -1)
|
matches := rePhaseHeader.FindAllStringSubmatch(content, -1)
|
||||||
|
|
@ -654,7 +682,7 @@ func (ms *MemoryStore) extractCommandsSection(content string) string {
|
||||||
// FormatPlanDisplay returns a user-facing display of the full plan with emoji indicators.
|
// FormatPlanDisplay returns a user-facing display of the full plan with emoji indicators.
|
||||||
func (ms *MemoryStore) FormatPlanDisplay() string {
|
func (ms *MemoryStore) FormatPlanDisplay() string {
|
||||||
content := ms.ReadLongTerm()
|
content := ms.ReadLongTerm()
|
||||||
if !ms.HasActivePlan() {
|
if !reActivePlan.MatchString(content) {
|
||||||
return "No active plan."
|
return "No active plan."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -662,9 +690,15 @@ func (ms *MemoryStore) FormatPlanDisplay() string {
|
||||||
if m := reTaskLine.FindStringSubmatch(content); len(m) >= 2 {
|
if m := reTaskLine.FindStringSubmatch(content); len(m) >= 2 {
|
||||||
taskLine = strings.TrimSpace(m[1])
|
taskLine = strings.TrimSpace(m[1])
|
||||||
}
|
}
|
||||||
status := ms.GetPlanStatus()
|
var status string
|
||||||
currentPhase := ms.GetCurrentPhase()
|
if m := reStatus.FindStringSubmatch(content); len(m) >= 2 {
|
||||||
phases := ms.GetPlanPhases()
|
status = strings.TrimSpace(m[1])
|
||||||
|
}
|
||||||
|
var currentPhase int
|
||||||
|
if m := rePhase.FindStringSubmatch(content); len(m) >= 2 {
|
||||||
|
currentPhase, _ = strconv.Atoi(m[1])
|
||||||
|
}
|
||||||
|
phases := ms.getPlanPhasesFrom(content)
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString(fmt.Sprintf("Plan: %s\n", taskLine))
|
sb.WriteString(fmt.Sprintf("Plan: %s\n", taskLine))
|
||||||
|
|
@ -725,16 +759,21 @@ func (ms *MemoryStore) GetMemoryContext() string {
|
||||||
var parts []string
|
var parts []string
|
||||||
|
|
||||||
longTerm := ms.ReadLongTerm()
|
longTerm := ms.ReadLongTerm()
|
||||||
|
hasActivePlan := longTerm != "" && reActivePlan.MatchString(longTerm)
|
||||||
|
|
||||||
if longTerm != "" {
|
if longTerm != "" {
|
||||||
if ms.HasActivePlan() {
|
if hasActivePlan {
|
||||||
status := ms.GetPlanStatus()
|
var status string
|
||||||
|
if m := reStatus.FindStringSubmatch(longTerm); len(m) >= 2 {
|
||||||
|
status = strings.TrimSpace(m[1])
|
||||||
|
}
|
||||||
switch status {
|
switch status {
|
||||||
case "interviewing":
|
case "interviewing":
|
||||||
parts = append(parts, ms.GetInterviewContext())
|
parts = append(parts, ms.getInterviewContextFrom(longTerm))
|
||||||
case "review":
|
case "review":
|
||||||
parts = append(parts, ms.GetReviewContext())
|
parts = append(parts, ms.getReviewContextFrom(longTerm))
|
||||||
default:
|
default:
|
||||||
parts = append(parts, ms.GetPlanContext())
|
parts = append(parts, ms.getPlanContextFrom(longTerm))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parts = append(parts, "## Long-term Memory\n\n"+longTerm)
|
parts = append(parts, "## Long-term Memory\n\n"+longTerm)
|
||||||
|
|
@ -742,7 +781,7 @@ func (ms *MemoryStore) GetMemoryContext() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suppress daily notes when a plan is active to save context
|
// Suppress daily notes when a plan is active to save context
|
||||||
if !ms.HasActivePlan() {
|
if !hasActivePlan {
|
||||||
recentNotes := ms.GetRecentDailyNotes(3)
|
recentNotes := ms.GetRecentDailyNotes(3)
|
||||||
if recentNotes != "" {
|
if recentNotes != "" {
|
||||||
parts = append(parts, "## Recent Daily Notes\n\n"+recentNotes)
|
parts = append(parts, "## Recent Daily Notes\n\n"+recentNotes)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue