opencode-lore 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agents-file.ts +65 -10
package/package.json
CHANGED
package/src/agents-file.ts
CHANGED
|
@@ -21,6 +21,17 @@ export const LORE_SECTION_START =
|
|
|
21
21
|
"<!-- This section is maintained by the coding agent via lore (https://github.com/BYK/opencode-lore) -->";
|
|
22
22
|
export const LORE_SECTION_END = "<!-- End lore-managed section -->";
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* All known start-marker variants, ordered newest-first.
|
|
26
|
+
* When we renamed the marker in the past, old files kept the old text.
|
|
27
|
+
* splitFile() matches any of these so it can strip all lore sections
|
|
28
|
+
* regardless of which marker version was used to write them.
|
|
29
|
+
*/
|
|
30
|
+
const ALL_START_MARKERS = [
|
|
31
|
+
LORE_SECTION_START,
|
|
32
|
+
"<!-- This section is auto-maintained by lore (https://github.com/BYK/opencode-lore) -->",
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
24
35
|
/** Regex matching a valid UUID (v4 or v7) — 8-4-4-4-12 hex groups. */
|
|
25
36
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
26
37
|
|
|
@@ -45,26 +56,70 @@ export type ParsedFileEntry = {
|
|
|
45
56
|
|
|
46
57
|
/**
|
|
47
58
|
* Split file content into three parts: before, lore section body, after.
|
|
48
|
-
* Returns null for section body when markers are
|
|
59
|
+
* Returns null for section body when no lore markers are found.
|
|
60
|
+
*
|
|
61
|
+
* Handles multiple lore sections (from duplication bugs) and all known
|
|
62
|
+
* start-marker variants (old + new text) by:
|
|
63
|
+
* - Collecting every lore section span in the file
|
|
64
|
+
* - Returning `before` = content before the first section
|
|
65
|
+
* - Returning `after` = content after the last section (all intermediate
|
|
66
|
+
* sections are discarded)
|
|
67
|
+
* - Returning `section` = body of the first section found (for import
|
|
68
|
+
* and shouldImport to read the canonical content)
|
|
69
|
+
*
|
|
70
|
+
* This is self-healing: a file with N duplicate sections will be collapsed
|
|
71
|
+
* to exactly one on the next exportToFile() call.
|
|
49
72
|
*/
|
|
50
73
|
function splitFile(fileContent: string): {
|
|
51
74
|
before: string;
|
|
52
75
|
section: string | null;
|
|
53
76
|
after: string;
|
|
54
77
|
} {
|
|
55
|
-
|
|
56
|
-
|
|
78
|
+
// Collect every lore section span in the file, matching all known
|
|
79
|
+
// start-marker variants (current + historical renamed markers).
|
|
80
|
+
// Each span records: where the section body begins/ends and where the
|
|
81
|
+
// full span (including end-marker) ends.
|
|
82
|
+
type Span = { markerStart: number; bodyStart: number; bodyEnd: number; spanEnd: number };
|
|
83
|
+
const spans: Span[] = [];
|
|
84
|
+
|
|
85
|
+
let searchFrom = 0;
|
|
86
|
+
while (searchFrom < fileContent.length) {
|
|
87
|
+
// Find the earliest occurrence of any known start marker
|
|
88
|
+
let markerStart = -1;
|
|
89
|
+
let markerLen = 0;
|
|
90
|
+
for (const marker of ALL_START_MARKERS) {
|
|
91
|
+
const idx = fileContent.indexOf(marker, searchFrom);
|
|
92
|
+
if (idx !== -1 && (markerStart === -1 || idx < markerStart)) {
|
|
93
|
+
markerStart = idx;
|
|
94
|
+
markerLen = marker.length;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (markerStart === -1) break; // no more start markers
|
|
98
|
+
|
|
99
|
+
const bodyStart = markerStart + markerLen;
|
|
100
|
+
const endIdx = fileContent.indexOf(LORE_SECTION_END, bodyStart);
|
|
101
|
+
if (endIdx === -1) {
|
|
102
|
+
// Unclosed section — consume to EOF
|
|
103
|
+
spans.push({ markerStart, bodyStart, bodyEnd: fileContent.length, spanEnd: fileContent.length });
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
57
106
|
|
|
58
|
-
|
|
107
|
+
spans.push({ markerStart, bodyStart, bodyEnd: endIdx, spanEnd: endIdx + LORE_SECTION_END.length });
|
|
108
|
+
searchFrom = endIdx + LORE_SECTION_END.length;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (spans.length === 0) {
|
|
59
112
|
return { before: fileContent, section: null, after: "" };
|
|
60
113
|
}
|
|
61
114
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
);
|
|
67
|
-
const
|
|
115
|
+
// before = everything before the first lore section (start marker not included)
|
|
116
|
+
// section = body of the first section (used by shouldImport and importFromFile)
|
|
117
|
+
// after = everything after the LAST lore section's end marker
|
|
118
|
+
// Any intermediate duplicate sections are discarded.
|
|
119
|
+
const before = fileContent.slice(0, spans[0].markerStart);
|
|
120
|
+
const section = fileContent.slice(spans[0].bodyStart, spans[0].bodyEnd);
|
|
121
|
+
const after = fileContent.slice(spans[spans.length - 1].spanEnd);
|
|
122
|
+
|
|
68
123
|
return { before, section, after };
|
|
69
124
|
}
|
|
70
125
|
|