parseTopics parses the TOPICS section into a name→summary map. It accepts the new "name: summary" line format and the legacy bare/comma-separated names (summary empty), so old prompts and partial model output still parse.
(content string)
| 723 | // new "name: summary" line format and the legacy bare/comma-separated names |
| 724 | // (summary empty), so old prompts and partial model output still parse. |
| 725 | func parseTopics(content string) map[string]string { |
| 726 | out := make(map[string]string) |
| 727 | for _, line := range strings.Split(content, "\n") { |
| 728 | line = strings.TrimSpace(line) |
| 729 | line = strings.TrimLeft(line, "-•*# ") |
| 730 | line = strings.TrimSpace(line) |
| 731 | if line == "" || isNothingNew(line) { |
| 732 | continue |
| 733 | } |
| 734 | if i := strings.Index(line, ":"); i >= 0 { |
| 735 | name := strings.TrimSpace(line[:i]) |
| 736 | summary := strings.TrimSpace(line[i+1:]) |
| 737 | if name != "" { |
| 738 | out[name] = summary |
| 739 | } |
| 740 | continue |
| 741 | } |
| 742 | // No colon → a bare (possibly comma-separated) list of names. |
| 743 | for _, part := range strings.Split(line, ",") { |
| 744 | if n := strings.TrimSpace(part); n != "" { |
| 745 | if _, exists := out[n]; !exists { |
| 746 | out[n] = "" |
| 747 | } |
| 748 | } |
| 749 | } |
| 750 | } |
| 751 | return out |
| 752 | } |
| 753 | |
| 754 | // knownCategories is the fixed set of fact buckets shared by the migration |
| 755 | // heuristic, the extraction parser, and the manual commands. |