MCPcopy Create free account
hub / github.com/diillson/chatcli / extractAndSave

Method extractAndSave

cli/memory_worker.go:273–377  ·  view source on GitHub ↗
(ctx context.Context, messages []models.Message)

Source from the content-addressed store, hash-verified

271carries what was learned. A bare comma-separated list of names is still accepted.`
272
273func (mw *memoryWorker) extractAndSave(ctx context.Context, messages []models.Message) error {
274 if mw.cli.memoryStore == nil {
275 return fmt.Errorf("memory store not available")
276 }
277
278 mgr := mw.cli.memoryStore.Manager()
279
280 // Self-evolution piggybacks on this same extraction pass (no extra LLM
281 // call): when enabled, the prompt asks for SKILL_CANDIDATES alongside the
282 // memory sections, and we act on them after parsing the response.
283 evolveMode := resolveSelfEvolveMode()
284 // Topic threading rides as an appended directive so the base extraction
285 // prompt constant stays byte-stable (an exported const value change reads as
286 // an incompatible API change); the parser accepts both formats.
287 instructions := memory.EnhancedExtractionPromptV3 + "\n" + topicSummaryDirective
288 if evolveMode != selfEvolveOff {
289 instructions += "\n" + selfEvolveSkillDirective
290 // Inject only the compact skill index (names + descriptions), so the
291 // model can target an existing skill for evolution without any bodies
292 // bloating the per-turn prompt. The body is pulled on demand at merge.
293 if idx := mw.cli.buildSkillIndex(); idx != "" {
294 instructions += "\n\n" + idx
295 }
296 }
297
298 // Build conversation snippet for extraction
299 var sb strings.Builder
300 for _, msg := range messages {
301 content := msg.Content
302 // Truncate very long messages to keep the extraction prompt small
303 if len(content) > 1500 {
304 content = content[:1200] + "\n... [truncated] ...\n" + content[len(content)-200:]
305 }
306 sb.WriteString(fmt.Sprintf("[%s]: %s\n\n", msg.Role, content))
307 }
308
309 // Build enhanced prompt with existing context
310 var fullPrompt strings.Builder
311 fullPrompt.WriteString(instructions)
312 fullPrompt.WriteString("\n\n---\n\n")
313
314 // Include current workspace so the extraction LLM can distinguish session context
315 if wsDir := mgr.WorkspaceDir(); wsDir != "" {
316 fullPrompt.WriteString(fmt.Sprintf("CURRENT SESSION WORKSPACE: %s\n", wsDir))
317 fullPrompt.WriteString("(All paths and facts from this conversation belong to this workspace.)\n\n---\n\n")
318 }
319
320 existingContext := mgr.FormatExistingContext()
321 if existingContext != "" {
322 fullPrompt.WriteString(existingContext)
323 fullPrompt.WriteString("\n\n---\n\n")
324 }
325
326 fullPrompt.WriteString("CONVERSATION SEGMENT TO ANALYZE:\n\n")
327 fullPrompt.WriteString(sb.String())
328
329 prompt := fullPrompt.String()
330

Callers 2

drainPendingMethod · 0.95
maybeExtractMethod · 0.95

Calls 15

callExtractionMethod · 0.95
resolveSelfEvolveModeFunction · 0.85
formatMemoryNoticeFunction · 0.85
formatSelfEvolveNoticeFunction · 0.85
ErrorfMethod · 0.80
ManagerMethod · 0.80
buildSkillIndexMethod · 0.80
FormatExistingContextMethod · 0.80
pushMemoryNoticeMethod · 0.80
applySkillCandidatesMethod · 0.80
isEmptyMethod · 0.80
isNothingNewFunction · 0.70

Tested by

no test coverage detected