byterover-cli 1.0.4 → 1.0.5
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/README.md +13 -2
- package/dist/commands/curate.js +1 -1
- package/dist/commands/main.d.ts +13 -0
- package/dist/commands/main.js +53 -2
- package/dist/commands/query.js +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/domain/cipher/llm/registry.js +53 -2
- package/dist/core/domain/cipher/llm/types.d.ts +2 -0
- package/dist/core/domain/cipher/process/types.d.ts +7 -0
- package/dist/core/domain/cipher/session/session-metadata.d.ts +178 -0
- package/dist/core/domain/cipher/session/session-metadata.js +147 -0
- package/dist/core/domain/knowledge/markdown-writer.d.ts +15 -18
- package/dist/core/domain/knowledge/markdown-writer.js +232 -34
- package/dist/core/domain/knowledge/relation-parser.d.ts +25 -39
- package/dist/core/domain/knowledge/relation-parser.js +39 -61
- package/dist/core/domain/transport/schemas.d.ts +37 -2
- package/dist/core/domain/transport/schemas.js +23 -2
- package/dist/core/interfaces/cipher/i-session-persistence.d.ts +133 -0
- package/dist/core/interfaces/cipher/i-session-persistence.js +7 -0
- package/dist/core/interfaces/cipher/message-types.d.ts +6 -0
- package/dist/core/interfaces/executor/i-curate-executor.d.ts +2 -2
- package/dist/core/interfaces/i-context-file-reader.d.ts +3 -0
- package/dist/core/interfaces/usecase/{i-clear-use-case.d.ts → i-reset-use-case.d.ts} +1 -1
- package/dist/infra/cipher/agent/agent-schemas.d.ts +6 -6
- package/dist/infra/cipher/agent/service-initializer.js +4 -4
- package/dist/infra/cipher/file-system/context-tree-file-system-factory.js +3 -2
- package/dist/infra/cipher/file-system/file-system-service.js +1 -0
- package/dist/infra/cipher/http/internal-llm-http-service.js +3 -5
- package/dist/infra/cipher/interactive-loop.js +3 -1
- package/dist/infra/cipher/llm/context/context-manager.js +40 -16
- package/dist/infra/cipher/llm/formatters/gemini-formatter.d.ts +13 -0
- package/dist/infra/cipher/llm/formatters/gemini-formatter.js +98 -6
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +6 -2
- package/dist/infra/cipher/llm/thought-parser.d.ts +21 -0
- package/dist/infra/cipher/llm/thought-parser.js +27 -0
- package/dist/infra/cipher/llm/tool-output-processor.d.ts +10 -0
- package/dist/infra/cipher/llm/tool-output-processor.js +80 -7
- package/dist/infra/cipher/process/process-service.js +11 -3
- package/dist/infra/cipher/session/chat-session.d.ts +7 -2
- package/dist/infra/cipher/session/chat-session.js +90 -52
- package/dist/infra/cipher/session/session-metadata-store.d.ts +52 -0
- package/dist/infra/cipher/session/session-metadata-store.js +406 -0
- package/dist/infra/cipher/tools/implementations/curate-tool.js +113 -35
- package/dist/infra/cipher/tools/implementations/task-tool.js +1 -0
- package/dist/infra/context-tree/file-context-file-reader.js +4 -0
- package/dist/infra/core/task-processor.d.ts +2 -2
- package/dist/infra/process/process-manager.d.ts +10 -1
- package/dist/infra/process/process-manager.js +16 -6
- package/dist/infra/process/transport-handlers.js +31 -0
- package/dist/infra/repl/commands/index.js +5 -2
- package/dist/infra/repl/commands/new-command.d.ts +14 -0
- package/dist/infra/repl/commands/new-command.js +61 -0
- package/dist/infra/repl/commands/{clear-command.d.ts → reset-command.d.ts} +2 -2
- package/dist/infra/repl/commands/{clear-command.js → reset-command.js} +10 -10
- package/dist/infra/usecase/generate-rules-use-case.js +2 -2
- package/dist/infra/usecase/init-use-case.js +4 -4
- package/dist/infra/usecase/logout-use-case.js +1 -1
- package/dist/infra/usecase/push-use-case.js +1 -1
- package/dist/infra/usecase/{clear-use-case.d.ts → reset-use-case.d.ts} +5 -5
- package/dist/infra/usecase/{clear-use-case.js → reset-use-case.js} +5 -5
- package/dist/resources/prompts/curate.yml +68 -13
- package/dist/resources/tools/curate.txt +60 -15
- package/dist/tui/components/inline-prompts/inline-confirm.js +2 -2
- package/dist/tui/components/onboarding/onboarding-flow.js +1 -0
- package/dist/tui/views/command-view.js +15 -0
- package/dist/utils/file-validator.js +9 -7
- package/oclif.manifest.json +3 -3
- package/package.json +1 -1
- package/dist/config/context-tree-domains.d.ts +0 -29
- package/dist/config/context-tree-domains.js +0 -29
- /package/dist/core/interfaces/usecase/{i-clear-use-case.js → i-reset-use-case.js} +0 -0
|
@@ -1,16 +1,130 @@
|
|
|
1
1
|
import { generateRelationsSection, parseRelations } from './relation-parser.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
function generateRawConceptSection(rawConcept) {
|
|
3
|
+
if (!rawConcept) {
|
|
4
|
+
return '';
|
|
5
|
+
}
|
|
6
|
+
const parts = [];
|
|
7
|
+
if (rawConcept.task) {
|
|
8
|
+
parts.push(`**Task:**\n${rawConcept.task}`);
|
|
9
|
+
}
|
|
10
|
+
if (rawConcept.changes && rawConcept.changes.length > 0) {
|
|
11
|
+
parts.push(`**Changes:**\n${rawConcept.changes.map(c => `- ${c}`).join('\n')}`);
|
|
12
|
+
}
|
|
13
|
+
if (rawConcept.files && rawConcept.files.length > 0) {
|
|
14
|
+
parts.push(`**Files:**\n${rawConcept.files.map(f => `- ${f}`).join('\n')}`);
|
|
15
|
+
}
|
|
16
|
+
if (rawConcept.flow) {
|
|
17
|
+
parts.push(`**Flow:**\n${rawConcept.flow}`);
|
|
18
|
+
}
|
|
19
|
+
if (rawConcept.timestamp) {
|
|
20
|
+
parts.push(`**Timestamp:** ${rawConcept.timestamp}`);
|
|
21
|
+
}
|
|
22
|
+
if (parts.length === 0) {
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
return `\n## Raw Concept\n${parts.join('\n\n')}\n`;
|
|
26
|
+
}
|
|
27
|
+
function generateNarrativeSection(narrative) {
|
|
28
|
+
if (!narrative) {
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
const parts = [];
|
|
32
|
+
if (narrative.structure) {
|
|
33
|
+
parts.push(`### Structure\n${narrative.structure}`);
|
|
34
|
+
}
|
|
35
|
+
if (narrative.dependencies) {
|
|
36
|
+
parts.push(`### Dependencies\n${narrative.dependencies}`);
|
|
37
|
+
}
|
|
38
|
+
if (narrative.features) {
|
|
39
|
+
parts.push(`### Features\n${narrative.features}`);
|
|
40
|
+
}
|
|
41
|
+
if (parts.length === 0) {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
return `\n## Narrative\n${parts.join('\n\n')}\n`;
|
|
45
|
+
}
|
|
46
|
+
function parseRawConceptSection(content) {
|
|
47
|
+
// Forgiving regex: allows optional whitespace after "## Raw Concept"
|
|
48
|
+
const rawConceptMatch = content.match(/##\s*Raw Concept\s*\n([\s\S]*?)(?=\n##\s|\n---\n|$)/i);
|
|
49
|
+
if (!rawConceptMatch) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const sectionContent = rawConceptMatch[1];
|
|
53
|
+
const rawConcept = {};
|
|
54
|
+
// Forgiving: allows whitespace around "Task:" and after the newline
|
|
55
|
+
const taskMatch = sectionContent.match(/\*\*\s*Task\s*:\s*\*\*\s*\n([\s\S]*?)(?=\n\*\*|\n##|$)/i);
|
|
56
|
+
if (taskMatch) {
|
|
57
|
+
rawConcept.task = taskMatch[1].trim();
|
|
58
|
+
}
|
|
59
|
+
const changesMatch = sectionContent.match(/\*\*\s*Changes\s*:\s*\*\*\s*\n([\s\S]*?)(?=\n\*\*|\n##|$)/i);
|
|
60
|
+
if (changesMatch) {
|
|
61
|
+
rawConcept.changes = changesMatch[1]
|
|
62
|
+
.split('\n')
|
|
63
|
+
.filter(line => line.trim().startsWith('- '))
|
|
64
|
+
.map(line => line.trim().slice(2));
|
|
65
|
+
}
|
|
66
|
+
const filesMatch = sectionContent.match(/\*\*\s*Files\s*:\s*\*\*\s*\n([\s\S]*?)(?=\n\*\*|\n##|$)/i);
|
|
67
|
+
if (filesMatch) {
|
|
68
|
+
rawConcept.files = filesMatch[1]
|
|
69
|
+
.split('\n')
|
|
70
|
+
.filter(line => line.trim().startsWith('- '))
|
|
71
|
+
.map(line => line.trim().slice(2));
|
|
72
|
+
}
|
|
73
|
+
const flowMatch = sectionContent.match(/\*\*\s*Flow\s*:\s*\*\*\s*\n([\s\S]*?)(?=\n\*\*|\n##|$)/i);
|
|
74
|
+
if (flowMatch) {
|
|
75
|
+
rawConcept.flow = flowMatch[1].trim();
|
|
76
|
+
}
|
|
77
|
+
// Timestamp can be inline, so more flexible pattern
|
|
78
|
+
const timestampMatch = sectionContent.match(/\*\*\s*Timestamp\s*:\s*\*\*\s*(.+)/i);
|
|
79
|
+
if (timestampMatch) {
|
|
80
|
+
rawConcept.timestamp = timestampMatch[1].trim();
|
|
81
|
+
}
|
|
82
|
+
if (Object.keys(rawConcept).length === 0) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return rawConcept;
|
|
86
|
+
}
|
|
87
|
+
function parseNarrativeSection(content) {
|
|
88
|
+
// Forgiving regex: allows optional whitespace after "## Narrative"
|
|
89
|
+
const narrativeMatch = content.match(/##\s*Narrative\s*\n([\s\S]*?)(?=\n##\s[^#]|\n---\n|$)/i);
|
|
90
|
+
if (!narrativeMatch) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const sectionContent = narrativeMatch[1];
|
|
94
|
+
const narrative = {};
|
|
95
|
+
// Forgiving: allows whitespace after "### Structure"
|
|
96
|
+
const structureMatch = sectionContent.match(/###\s*Structure\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i);
|
|
97
|
+
if (structureMatch) {
|
|
98
|
+
narrative.structure = structureMatch[1].trim();
|
|
99
|
+
}
|
|
100
|
+
const dependenciesMatch = sectionContent.match(/###\s*Dependencies\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i);
|
|
101
|
+
if (dependenciesMatch) {
|
|
102
|
+
narrative.dependencies = dependenciesMatch[1].trim();
|
|
103
|
+
}
|
|
104
|
+
const featuresMatch = sectionContent.match(/###\s*Features\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i);
|
|
105
|
+
if (featuresMatch) {
|
|
106
|
+
narrative.features = featuresMatch[1].trim();
|
|
107
|
+
}
|
|
108
|
+
if (Object.keys(narrative).length === 0) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
return narrative;
|
|
112
|
+
}
|
|
6
113
|
function extractSnippetsFromContent(content) {
|
|
7
|
-
// Remove relations section if present
|
|
8
114
|
let snippetContent = content;
|
|
9
|
-
|
|
115
|
+
// Forgiving regex patterns for section removal
|
|
116
|
+
const relationsMatch = content.match(/##\s*Relations[\s\S]*?(?=\n[^@\n]|$)/i);
|
|
10
117
|
if (relationsMatch) {
|
|
11
|
-
snippetContent =
|
|
118
|
+
snippetContent = snippetContent.replace(relationsMatch[0], '').trim();
|
|
119
|
+
}
|
|
120
|
+
const rawConceptMatch = snippetContent.match(/##\s*Raw Concept[\s\S]*?(?=\n##\s|\n---\n|$)/i);
|
|
121
|
+
if (rawConceptMatch) {
|
|
122
|
+
snippetContent = snippetContent.replace(rawConceptMatch[0], '').trim();
|
|
123
|
+
}
|
|
124
|
+
const narrativeMatch = snippetContent.match(/##\s*Narrative[\s\S]*?(?=\n##\s|\n---\n|$)/i);
|
|
125
|
+
if (narrativeMatch) {
|
|
126
|
+
snippetContent = snippetContent.replace(narrativeMatch[0], '').trim();
|
|
12
127
|
}
|
|
13
|
-
// Split by separator and filter empty
|
|
14
128
|
const snippets = snippetContent
|
|
15
129
|
.split(/\n---\n/)
|
|
16
130
|
.map(s => s.trim())
|
|
@@ -18,39 +132,112 @@ function extractSnippetsFromContent(content) {
|
|
|
18
132
|
return snippets;
|
|
19
133
|
}
|
|
20
134
|
/**
|
|
21
|
-
*
|
|
135
|
+
* Merges two RawConcept objects with the following strategy:
|
|
136
|
+
*
|
|
137
|
+
* **Scalars (task, flow, timestamp)**: Source wins (source.X || target.X)
|
|
138
|
+
* - Rationale: The source represents "new" or "incoming" data that should
|
|
139
|
+
* take precedence over existing target data for singular values.
|
|
140
|
+
*
|
|
141
|
+
* **Arrays (changes, files)**: Concatenated and deduplicated (target first, then source)
|
|
142
|
+
* - Rationale: For lists, we want to accumulate all entries rather than
|
|
143
|
+
* replacing them. Target entries are placed first to preserve order.
|
|
144
|
+
*
|
|
145
|
+
* @param source - The incoming/new RawConcept to merge (takes precedence for scalars)
|
|
146
|
+
* @param target - The existing/base RawConcept to merge into
|
|
147
|
+
* @returns Merged RawConcept or undefined if both inputs are empty
|
|
22
148
|
*/
|
|
149
|
+
function mergeRawConcepts(source, target) {
|
|
150
|
+
if (!source && !target) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
if (!source)
|
|
154
|
+
return target;
|
|
155
|
+
if (!target)
|
|
156
|
+
return source;
|
|
157
|
+
const merged = {};
|
|
158
|
+
// Scalars: source wins (newer data takes precedence)
|
|
159
|
+
merged.task = source.task || target.task;
|
|
160
|
+
merged.flow = source.flow || target.flow;
|
|
161
|
+
merged.timestamp = source.timestamp || target.timestamp;
|
|
162
|
+
// Arrays: concatenate and deduplicate (target first, then source)
|
|
163
|
+
const allChanges = [...(target.changes || []), ...(source.changes || [])];
|
|
164
|
+
if (allChanges.length > 0) {
|
|
165
|
+
merged.changes = [...new Set(allChanges)];
|
|
166
|
+
}
|
|
167
|
+
const allFiles = [...(target.files || []), ...(source.files || [])];
|
|
168
|
+
if (allFiles.length > 0) {
|
|
169
|
+
merged.files = [...new Set(allFiles)];
|
|
170
|
+
}
|
|
171
|
+
if (Object.keys(merged).length === 0) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
return merged;
|
|
175
|
+
}
|
|
176
|
+
function mergeNarratives(source, target) {
|
|
177
|
+
if (!source && !target) {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
if (!source)
|
|
181
|
+
return target;
|
|
182
|
+
if (!target)
|
|
183
|
+
return source;
|
|
184
|
+
const merged = {};
|
|
185
|
+
if (source.structure || target.structure) {
|
|
186
|
+
const parts = [target.structure, source.structure].filter(Boolean);
|
|
187
|
+
merged.structure = parts.join('\n\n');
|
|
188
|
+
}
|
|
189
|
+
if (source.dependencies || target.dependencies) {
|
|
190
|
+
const parts = [target.dependencies, source.dependencies].filter(Boolean);
|
|
191
|
+
merged.dependencies = parts.join('\n\n');
|
|
192
|
+
}
|
|
193
|
+
if (source.features || target.features) {
|
|
194
|
+
const parts = [target.features, source.features].filter(Boolean);
|
|
195
|
+
merged.features = parts.join('\n\n');
|
|
196
|
+
}
|
|
197
|
+
if (Object.keys(merged).length === 0) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
return merged;
|
|
201
|
+
}
|
|
23
202
|
export const MarkdownWriter = {
|
|
24
|
-
/**
|
|
25
|
-
* Generate context.md content with snippets and optional relations.
|
|
26
|
-
* Used for both topics and subtopics in the knowledge hierarchy.
|
|
27
|
-
*/
|
|
28
203
|
generateContext(data) {
|
|
29
|
-
const snippets = data.snippets || [];
|
|
204
|
+
const snippets = (data.snippets || []).filter(s => s && s.trim());
|
|
30
205
|
const relations = data.relations || [];
|
|
31
206
|
const relationsSection = generateRelationsSection(relations);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
207
|
+
const rawConceptSection = generateRawConceptSection(data.rawConcept);
|
|
208
|
+
const narrativeSection = generateNarrativeSection(data.narrative);
|
|
209
|
+
const hasSnippets = snippets.length > 0;
|
|
210
|
+
// Build the content parts
|
|
211
|
+
const parts = [];
|
|
212
|
+
// Add sections (relations, rawConcept, narrative)
|
|
213
|
+
const sectionsContent = `${relationsSection}${rawConceptSection}${narrativeSection}`.trim();
|
|
214
|
+
if (sectionsContent) {
|
|
215
|
+
parts.push(sectionsContent);
|
|
216
|
+
}
|
|
217
|
+
// Add snippets if present
|
|
218
|
+
if (hasSnippets) {
|
|
219
|
+
const snippetsContent = snippets.join('\n\n---\n\n');
|
|
220
|
+
parts.push(snippetsContent);
|
|
221
|
+
}
|
|
222
|
+
// If nothing at all, return empty (should not happen in practice)
|
|
223
|
+
if (parts.length === 0) {
|
|
224
|
+
return '';
|
|
225
|
+
}
|
|
226
|
+
// Join parts with separator only if we have both sections and snippets
|
|
227
|
+
return parts.join('\n\n---\n\n') + '\n';
|
|
35
228
|
},
|
|
36
|
-
/**
|
|
37
|
-
* Merge two context.md contents into one.
|
|
38
|
-
* Combines snippets and relations, deduplicating where possible.
|
|
39
|
-
*
|
|
40
|
-
* @param sourceContent - Raw content from source context.md
|
|
41
|
-
* @param targetContent - Raw content from target context.md
|
|
42
|
-
* @returns Merged context.md content
|
|
43
|
-
*/
|
|
44
229
|
mergeContexts(sourceContent, targetContent) {
|
|
45
|
-
// Extract relations from both contents
|
|
46
230
|
const sourceRelations = parseRelations(sourceContent);
|
|
47
231
|
const targetRelations = parseRelations(targetContent);
|
|
48
|
-
// Merge and deduplicate relations
|
|
49
232
|
const mergedRelations = [...new Set([...sourceRelations, ...targetRelations])];
|
|
233
|
+
const sourceRawConcept = parseRawConceptSection(sourceContent);
|
|
234
|
+
const targetRawConcept = parseRawConceptSection(targetContent);
|
|
235
|
+
const mergedRawConcept = mergeRawConcepts(sourceRawConcept, targetRawConcept);
|
|
236
|
+
const sourceNarrative = parseNarrativeSection(sourceContent);
|
|
237
|
+
const targetNarrative = parseNarrativeSection(targetContent);
|
|
238
|
+
const mergedNarrative = mergeNarratives(sourceNarrative, targetNarrative);
|
|
50
239
|
const sourceSnippets = extractSnippetsFromContent(sourceContent);
|
|
51
240
|
const targetSnippets = extractSnippetsFromContent(targetContent);
|
|
52
|
-
// Merge snippets (target first, then source)
|
|
53
|
-
// Deduplicate by exact match
|
|
54
241
|
const seenSnippets = new Set();
|
|
55
242
|
const mergedSnippets = [];
|
|
56
243
|
for (const snippet of [...targetSnippets, ...sourceSnippets]) {
|
|
@@ -59,10 +246,21 @@ ${snippets.length > 0 ? snippets.map(s => `${s}`).join('\n\n---\n\n') : 'No cont
|
|
|
59
246
|
mergedSnippets.push(snippet);
|
|
60
247
|
}
|
|
61
248
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
249
|
+
return MarkdownWriter.generateContext({
|
|
250
|
+
name: '',
|
|
251
|
+
narrative: mergedNarrative,
|
|
252
|
+
rawConcept: mergedRawConcept,
|
|
253
|
+
relations: mergedRelations,
|
|
254
|
+
snippets: mergedSnippets,
|
|
255
|
+
});
|
|
256
|
+
},
|
|
257
|
+
parseContent(content, name = '') {
|
|
258
|
+
return {
|
|
259
|
+
name,
|
|
260
|
+
narrative: parseNarrativeSection(content),
|
|
261
|
+
rawConcept: parseRawConceptSection(content),
|
|
262
|
+
relations: parseRelations(content),
|
|
263
|
+
snippets: extractSnippetsFromContent(content),
|
|
264
|
+
};
|
|
67
265
|
},
|
|
68
266
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utilities for parsing and managing context relations.
|
|
3
|
-
* Relations are expressed using @ notation: @domain/topic or @domain/topic/subtopic
|
|
3
|
+
* Relations are expressed using @ notation: @domain/topic/title.md or @domain/topic/subtopic/title.md
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Parse relations from
|
|
7
|
-
* Extracts all @domain/topic or @domain/topic/subtopic references.
|
|
6
|
+
* Parse relations from title.md content.
|
|
7
|
+
* Extracts all @domain/topic/title.md or @domain/topic/subtopic/title.md references.
|
|
8
8
|
*
|
|
9
9
|
* @param content - Markdown content to parse
|
|
10
10
|
* @returns Array of unique relation paths (without @ prefix)
|
|
@@ -13,43 +13,27 @@
|
|
|
13
13
|
* ```ts
|
|
14
14
|
* const content = `
|
|
15
15
|
* ## Relations
|
|
16
|
-
* @code_style/error-handling
|
|
17
|
-
* @structure/api
|
|
16
|
+
* @code_style/error-handling/overview.md
|
|
17
|
+
* @structure/api/endpoints/rest.md
|
|
18
18
|
* `
|
|
19
|
-
* parseRelations(content) // ['code_style/error-handling', 'structure/api
|
|
19
|
+
* parseRelations(content) // ['code_style/error-handling/overview.md', 'structure/api/endpoints/rest.md']
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
export declare function parseRelations(content: string): string[];
|
|
23
|
-
/**
|
|
24
|
-
* Validate a relation path format.
|
|
25
|
-
* Valid formats: domain/topic or domain/topic/subtopic
|
|
26
|
-
*
|
|
27
|
-
* @param path - Relation path to validate (without @ prefix)
|
|
28
|
-
* @returns True if path format is valid
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```ts
|
|
32
|
-
* validateRelationPath('code_style/error-handling') // true
|
|
33
|
-
* validateRelationPath('code_style/error-handling/try-catch') // true
|
|
34
|
-
* validateRelationPath('invalid') // false
|
|
35
|
-
* validateRelationPath('too/many/parts/here') // false
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export declare function validateRelationPath(path: string): boolean;
|
|
39
23
|
/**
|
|
40
24
|
* Resolve a relation path to an absolute file system path.
|
|
41
25
|
*
|
|
42
26
|
* @param basePath - Base path to context tree (e.g., '.brv/context-tree')
|
|
43
|
-
* @param relation - Relation path (e.g., 'domain/topic' or 'domain/topic/subtopic')
|
|
44
|
-
* @returns Absolute path to the
|
|
27
|
+
* @param relation - Relation path (e.g., 'domain/topic/title.md' or 'domain/topic/subtopic/title.md')
|
|
28
|
+
* @returns Absolute path to the title.md file
|
|
45
29
|
*
|
|
46
30
|
* @example
|
|
47
31
|
* ```ts
|
|
48
|
-
* resolveRelationPath('.brv/context-tree', 'code_style/error-handling')
|
|
49
|
-
* // => '.brv/context-tree/code_style/error-handling/
|
|
32
|
+
* resolveRelationPath('.brv/context-tree', 'code_style/error-handling/overview.md')
|
|
33
|
+
* // => '.brv/context-tree/code_style/error-handling/overview.md'
|
|
50
34
|
*
|
|
51
|
-
* resolveRelationPath('.brv/context-tree', 'structure/api/endpoints')
|
|
52
|
-
* // => '.brv/context-tree/structure/api/endpoints/
|
|
35
|
+
* resolveRelationPath('.brv/context-tree', 'structure/api/endpoints/rest.md')
|
|
36
|
+
* // => '.brv/context-tree/structure/api/endpoints/rest.md'
|
|
53
37
|
* ```
|
|
54
38
|
*/
|
|
55
39
|
export declare function resolveRelationPath(basePath: string, relation: string): string;
|
|
@@ -58,31 +42,33 @@ export declare function resolveRelationPath(basePath: string, relation: string):
|
|
|
58
42
|
*
|
|
59
43
|
* @param domain - Domain name
|
|
60
44
|
* @param topic - Topic name
|
|
45
|
+
* @param title - Title (with .md extension)
|
|
61
46
|
* @param subtopic - Optional subtopic name
|
|
62
47
|
* @returns Formatted relation string with @ prefix
|
|
63
48
|
*
|
|
64
49
|
* @example
|
|
65
50
|
* ```ts
|
|
66
|
-
* formatRelation('code_style', 'error-handling')
|
|
67
|
-
* // => '@code_style/error-handling'
|
|
51
|
+
* formatRelation('code_style', 'error-handling', 'overview.md')
|
|
52
|
+
* // => '@code_style/error-handling/overview.md'
|
|
68
53
|
*
|
|
69
|
-
* formatRelation('structure', 'api', 'endpoints')
|
|
70
|
-
* // => '@structure/api/endpoints'
|
|
54
|
+
* formatRelation('structure', 'api', 'endpoints', 'rest.md')
|
|
55
|
+
* // => '@structure/api/endpoints/rest.md'
|
|
71
56
|
* ```
|
|
72
57
|
*/
|
|
73
|
-
export declare function formatRelation(domain: string, topic: string, subtopic?: string): string;
|
|
58
|
+
export declare function formatRelation(domain: string, topic: string, title: string, subtopic?: string): string;
|
|
74
59
|
/**
|
|
75
60
|
* Normalize a relation path by removing the @ prefix.
|
|
61
|
+
* Preserves file extensions (e.g., .md).
|
|
76
62
|
*
|
|
77
63
|
* @param relation - Relation path to normalize
|
|
78
|
-
* @returns Normalized relation path
|
|
64
|
+
* @returns Normalized relation path without @ prefix (file extension preserved)
|
|
79
65
|
*
|
|
80
66
|
* @example
|
|
81
67
|
* ```ts
|
|
82
|
-
* normalizeRelation('code_style/error-handling') // 'code_style/error-handling'
|
|
83
|
-
* normalizeRelation('@code_style/error-handling') // 'code_style/error-handling'
|
|
68
|
+
* normalizeRelation('code_style/error-handling.md') // 'code_style/error-handling.md'
|
|
69
|
+
* normalizeRelation('@code_style/error-handling.md') // 'code_style/error-handling.md'
|
|
84
70
|
* normalizeRelation('code_style/error-handling/title.md') // 'code_style/error-handling/title.md'
|
|
85
|
-
* normalizeRelation('
|
|
71
|
+
* normalizeRelation('code_style/error-handling/file.md') // 'code_style/error-handling/file.md'
|
|
86
72
|
* ```
|
|
87
73
|
*/
|
|
88
74
|
export declare function normalizeRelation(relation: string): string;
|
|
@@ -95,8 +81,8 @@ export declare function normalizeRelation(relation: string): string;
|
|
|
95
81
|
*
|
|
96
82
|
* @example
|
|
97
83
|
* ```ts
|
|
98
|
-
* generateRelationsSection(['code_style/error-handling', 'structure/api'])
|
|
99
|
-
* // => '\n## Relations\n@code_style/error-handling\n@structure/api\n'
|
|
84
|
+
* generateRelationsSection(['code_style/error-handling/overview.md', 'structure/api/rest.md'])
|
|
85
|
+
* // => '\n## Relations\n@code_style/error-handling/overview.md\n@structure/api/rest.md\n'
|
|
100
86
|
*
|
|
101
87
|
* generateRelationsSection([])
|
|
102
88
|
* // => ''
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utilities for parsing and managing context relations.
|
|
3
|
-
* Relations are expressed using @ notation: @domain/topic or @domain/topic/subtopic
|
|
3
|
+
* Relations are expressed using @ notation: @domain/topic/title.md or @domain/topic/subtopic/title.md
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
6
|
* Regular expression to match relation paths in markdown content.
|
|
7
|
-
* Matches: @domain/topic or @domain/topic/subtopic
|
|
7
|
+
* Matches: @domain/topic/title.md or @domain/topic/subtopic/title.md
|
|
8
8
|
*/
|
|
9
|
-
const RELATION_PATTERN = /@([\w-]+
|
|
9
|
+
const RELATION_PATTERN = /@([\w-]+\/[\w-]+(?:\/[\w-]+)?\/[\w-]+(?:\.[\w]+)?)(?![\w/-])/g;
|
|
10
10
|
/**
|
|
11
|
-
* Parse relations from
|
|
12
|
-
* Extracts all @domain/topic or @domain/topic/subtopic references.
|
|
11
|
+
* Parse relations from title.md content.
|
|
12
|
+
* Extracts all @domain/topic/title.md or @domain/topic/subtopic/title.md references.
|
|
13
13
|
*
|
|
14
14
|
* @param content - Markdown content to parse
|
|
15
15
|
* @returns Array of unique relation paths (without @ prefix)
|
|
@@ -18,104 +18,77 @@ const RELATION_PATTERN = /@([\w-]+)\/([\w-]+)(?:\/([\w-]+))?/g;
|
|
|
18
18
|
* ```ts
|
|
19
19
|
* const content = `
|
|
20
20
|
* ## Relations
|
|
21
|
-
* @code_style/error-handling
|
|
22
|
-
* @structure/api
|
|
21
|
+
* @code_style/error-handling/overview.md
|
|
22
|
+
* @structure/api/endpoints/rest.md
|
|
23
23
|
* `
|
|
24
|
-
* parseRelations(content) // ['code_style/error-handling', 'structure/api
|
|
24
|
+
* parseRelations(content) // ['code_style/error-handling/overview.md', 'structure/api/endpoints/rest.md']
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
27
|
export function parseRelations(content) {
|
|
28
28
|
const relations = new Set();
|
|
29
|
-
// Extract all @domain/topic or @domain/topic/subtopic patterns
|
|
29
|
+
// Extract all @domain/topic/title.md or @domain/topic/subtopic/title.md patterns
|
|
30
30
|
const matches = content.matchAll(RELATION_PATTERN);
|
|
31
31
|
for (const match of matches) {
|
|
32
|
-
const [,
|
|
33
|
-
|
|
34
|
-
? `${domain}/${topic}/${subtopic}`
|
|
35
|
-
: `${domain}/${topic}`;
|
|
36
|
-
relations.add(relation);
|
|
32
|
+
const [, fullPath] = match;
|
|
33
|
+
relations.add(fullPath.trim());
|
|
37
34
|
}
|
|
38
35
|
return [...relations];
|
|
39
36
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Validate a relation path format.
|
|
42
|
-
* Valid formats: domain/topic or domain/topic/subtopic
|
|
43
|
-
*
|
|
44
|
-
* @param path - Relation path to validate (without @ prefix)
|
|
45
|
-
* @returns True if path format is valid
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```ts
|
|
49
|
-
* validateRelationPath('code_style/error-handling') // true
|
|
50
|
-
* validateRelationPath('code_style/error-handling/try-catch') // true
|
|
51
|
-
* validateRelationPath('invalid') // false
|
|
52
|
-
* validateRelationPath('too/many/parts/here') // false
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export function validateRelationPath(path) {
|
|
56
|
-
const parts = path.split('/');
|
|
57
|
-
// Must have 2 or 3 parts: domain/topic or domain/topic/subtopic
|
|
58
|
-
if (parts.length < 2 || parts.length > 3) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
// Each part must be non-empty and contain only valid characters
|
|
62
|
-
const validPartPattern = /^[\w-]+$/;
|
|
63
|
-
return parts.every(part => validPartPattern.test(part));
|
|
64
|
-
}
|
|
65
37
|
/**
|
|
66
38
|
* Resolve a relation path to an absolute file system path.
|
|
67
39
|
*
|
|
68
40
|
* @param basePath - Base path to context tree (e.g., '.brv/context-tree')
|
|
69
|
-
* @param relation - Relation path (e.g., 'domain/topic' or 'domain/topic/subtopic')
|
|
70
|
-
* @returns Absolute path to the
|
|
41
|
+
* @param relation - Relation path (e.g., 'domain/topic/title.md' or 'domain/topic/subtopic/title.md')
|
|
42
|
+
* @returns Absolute path to the title.md file
|
|
71
43
|
*
|
|
72
44
|
* @example
|
|
73
45
|
* ```ts
|
|
74
|
-
* resolveRelationPath('.brv/context-tree', 'code_style/error-handling')
|
|
75
|
-
* // => '.brv/context-tree/code_style/error-handling/
|
|
46
|
+
* resolveRelationPath('.brv/context-tree', 'code_style/error-handling/overview.md')
|
|
47
|
+
* // => '.brv/context-tree/code_style/error-handling/overview.md'
|
|
76
48
|
*
|
|
77
|
-
* resolveRelationPath('.brv/context-tree', 'structure/api/endpoints')
|
|
78
|
-
* // => '.brv/context-tree/structure/api/endpoints/
|
|
49
|
+
* resolveRelationPath('.brv/context-tree', 'structure/api/endpoints/rest.md')
|
|
50
|
+
* // => '.brv/context-tree/structure/api/endpoints/rest.md'
|
|
79
51
|
* ```
|
|
80
52
|
*/
|
|
81
53
|
export function resolveRelationPath(basePath, relation) {
|
|
82
|
-
|
|
83
|
-
return `${basePath}/${parts.join('/')}/context.md`;
|
|
54
|
+
return `${basePath}/${relation}`;
|
|
84
55
|
}
|
|
85
56
|
/**
|
|
86
57
|
* Format a relation path using @ notation.
|
|
87
58
|
*
|
|
88
59
|
* @param domain - Domain name
|
|
89
60
|
* @param topic - Topic name
|
|
61
|
+
* @param title - Title (with .md extension)
|
|
90
62
|
* @param subtopic - Optional subtopic name
|
|
91
63
|
* @returns Formatted relation string with @ prefix
|
|
92
64
|
*
|
|
93
65
|
* @example
|
|
94
66
|
* ```ts
|
|
95
|
-
* formatRelation('code_style', 'error-handling')
|
|
96
|
-
* // => '@code_style/error-handling'
|
|
67
|
+
* formatRelation('code_style', 'error-handling', 'overview.md')
|
|
68
|
+
* // => '@code_style/error-handling/overview.md'
|
|
97
69
|
*
|
|
98
|
-
* formatRelation('structure', 'api', 'endpoints')
|
|
99
|
-
* // => '@structure/api/endpoints'
|
|
70
|
+
* formatRelation('structure', 'api', 'endpoints', 'rest.md')
|
|
71
|
+
* // => '@structure/api/endpoints/rest.md'
|
|
100
72
|
* ```
|
|
101
73
|
*/
|
|
102
|
-
export function formatRelation(domain, topic, subtopic) {
|
|
74
|
+
export function formatRelation(domain, topic, title, subtopic) {
|
|
103
75
|
return subtopic
|
|
104
|
-
? `@${domain}/${topic}/${subtopic}`
|
|
105
|
-
: `@${domain}/${topic}`;
|
|
76
|
+
? `@${domain}/${topic}/${subtopic}/${title}`
|
|
77
|
+
: `@${domain}/${topic}/${title}`;
|
|
106
78
|
}
|
|
107
79
|
/**
|
|
108
80
|
* Normalize a relation path by removing the @ prefix.
|
|
81
|
+
* Preserves file extensions (e.g., .md).
|
|
109
82
|
*
|
|
110
83
|
* @param relation - Relation path to normalize
|
|
111
|
-
* @returns Normalized relation path
|
|
84
|
+
* @returns Normalized relation path without @ prefix (file extension preserved)
|
|
112
85
|
*
|
|
113
86
|
* @example
|
|
114
87
|
* ```ts
|
|
115
|
-
* normalizeRelation('code_style/error-handling') // 'code_style/error-handling'
|
|
116
|
-
* normalizeRelation('@code_style/error-handling') // 'code_style/error-handling'
|
|
88
|
+
* normalizeRelation('code_style/error-handling.md') // 'code_style/error-handling.md'
|
|
89
|
+
* normalizeRelation('@code_style/error-handling.md') // 'code_style/error-handling.md'
|
|
117
90
|
* normalizeRelation('code_style/error-handling/title.md') // 'code_style/error-handling/title.md'
|
|
118
|
-
* normalizeRelation('
|
|
91
|
+
* normalizeRelation('code_style/error-handling/file.md') // 'code_style/error-handling/file.md'
|
|
119
92
|
* ```
|
|
120
93
|
*/
|
|
121
94
|
export function normalizeRelation(relation) {
|
|
@@ -130,8 +103,8 @@ export function normalizeRelation(relation) {
|
|
|
130
103
|
*
|
|
131
104
|
* @example
|
|
132
105
|
* ```ts
|
|
133
|
-
* generateRelationsSection(['code_style/error-handling', 'structure/api'])
|
|
134
|
-
* // => '\n## Relations\n@code_style/error-handling\n@structure/api\n'
|
|
106
|
+
* generateRelationsSection(['code_style/error-handling/overview.md', 'structure/api/rest.md'])
|
|
107
|
+
* // => '\n## Relations\n@code_style/error-handling/overview.md\n@structure/api/rest.md\n'
|
|
135
108
|
*
|
|
136
109
|
* generateRelationsSection([])
|
|
137
110
|
* // => ''
|
|
@@ -142,7 +115,12 @@ export function generateRelationsSection(relations) {
|
|
|
142
115
|
return '';
|
|
143
116
|
}
|
|
144
117
|
const formattedRelations = relations
|
|
145
|
-
.map(rel =>
|
|
118
|
+
.map(rel => {
|
|
119
|
+
const normalized = normalizeRelation(rel);
|
|
120
|
+
// Ensure .md extension is present
|
|
121
|
+
const withExtension = normalized.endsWith('.md') ? normalized : `${normalized}.md`;
|
|
122
|
+
return `@${withExtension}`;
|
|
123
|
+
})
|
|
146
124
|
.join('\n');
|
|
147
125
|
return `\n## Relations\n${formattedRelations}\n`;
|
|
148
126
|
}
|
|
@@ -429,6 +429,8 @@ export declare const TransportLlmEventList: readonly ["llmservice:thinking", "ll
|
|
|
429
429
|
export declare const TransportAgentEventNames: {
|
|
430
430
|
readonly CONNECTED: "agent:connected";
|
|
431
431
|
readonly DISCONNECTED: "agent:disconnected";
|
|
432
|
+
readonly NEW_SESSION: "agent:newSession";
|
|
433
|
+
readonly NEW_SESSION_CREATED: "agent:newSessionCreated";
|
|
432
434
|
readonly REGISTER: "agent:register";
|
|
433
435
|
readonly RESTART: "agent:restart";
|
|
434
436
|
readonly RESTARTED: "agent:restarted";
|
|
@@ -449,10 +451,10 @@ export declare const TransportSessionEventNames: {
|
|
|
449
451
|
* Internal message, not exposed to external clients
|
|
450
452
|
*/
|
|
451
453
|
export declare const TaskExecuteSchema: z.ZodObject<{
|
|
452
|
-
/** Client ID that created the task (for response routing) */
|
|
453
|
-
clientId: z.ZodString;
|
|
454
454
|
/** Client's working directory for file validation */
|
|
455
455
|
clientCwd: z.ZodOptional<z.ZodString>;
|
|
456
|
+
/** Client ID that created the task (for response routing) */
|
|
457
|
+
clientId: z.ZodString;
|
|
456
458
|
/** Task content/prompt */
|
|
457
459
|
content: z.ZodString;
|
|
458
460
|
/** Optional file paths for curate --files */
|
|
@@ -1137,6 +1139,37 @@ export declare const AgentRestartResponseSchema: z.ZodObject<{
|
|
|
1137
1139
|
success: boolean;
|
|
1138
1140
|
error?: string | undefined;
|
|
1139
1141
|
}>;
|
|
1142
|
+
/**
|
|
1143
|
+
* Request to create a new session (end current, start fresh).
|
|
1144
|
+
* Used by /new command to start a fresh conversation.
|
|
1145
|
+
*/
|
|
1146
|
+
export declare const AgentNewSessionRequestSchema: z.ZodObject<{
|
|
1147
|
+
/** Optional reason for new session (for logging) */
|
|
1148
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
1149
|
+
}, "strip", z.ZodTypeAny, {
|
|
1150
|
+
reason?: string | undefined;
|
|
1151
|
+
}, {
|
|
1152
|
+
reason?: string | undefined;
|
|
1153
|
+
}>;
|
|
1154
|
+
/**
|
|
1155
|
+
* Response after new session is created.
|
|
1156
|
+
*/
|
|
1157
|
+
export declare const AgentNewSessionResponseSchema: z.ZodObject<{
|
|
1158
|
+
/** Error message if session creation failed */
|
|
1159
|
+
error: z.ZodOptional<z.ZodString>;
|
|
1160
|
+
/** The new session ID */
|
|
1161
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
1162
|
+
/** Whether the new session was created successfully */
|
|
1163
|
+
success: z.ZodBoolean;
|
|
1164
|
+
}, "strip", z.ZodTypeAny, {
|
|
1165
|
+
success: boolean;
|
|
1166
|
+
error?: string | undefined;
|
|
1167
|
+
sessionId?: string | undefined;
|
|
1168
|
+
}, {
|
|
1169
|
+
success: boolean;
|
|
1170
|
+
error?: string | undefined;
|
|
1171
|
+
sessionId?: string | undefined;
|
|
1172
|
+
}>;
|
|
1140
1173
|
export type TodoItem = z.infer<typeof TodoItemSchema>;
|
|
1141
1174
|
export type ChunkPayload = z.infer<typeof ChunkPayloadSchema>;
|
|
1142
1175
|
export type ResponsePayload = z.infer<typeof ResponsePayloadSchema>;
|
|
@@ -1163,3 +1196,5 @@ export type SessionSwitchResponse = z.infer<typeof SessionSwitchResponseSchema>;
|
|
|
1163
1196
|
export type SessionSwitchedBroadcast = z.infer<typeof SessionSwitchedBroadcastSchema>;
|
|
1164
1197
|
export type AgentRestartRequest = z.infer<typeof AgentRestartRequestSchema>;
|
|
1165
1198
|
export type AgentRestartResponse = z.infer<typeof AgentRestartResponseSchema>;
|
|
1199
|
+
export type AgentNewSessionRequest = z.infer<typeof AgentNewSessionRequestSchema>;
|
|
1200
|
+
export type AgentNewSessionResponse = z.infer<typeof AgentNewSessionResponseSchema>;
|