@contextstream/mcp-server 0.2.5 → 0.2.7
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 +42 -50
- package/package.json +1 -1
- package/.env.example +0 -5
- package/src/client.ts +0 -764
- package/src/config.ts +0 -36
- package/src/files.ts +0 -261
- package/src/http.ts +0 -154
- package/src/index.ts +0 -38
- package/src/prompts.ts +0 -308
- package/src/resources.ts +0 -49
- package/src/tools.ts +0 -1040
- package/src/workspace-config.ts +0 -150
- package/tsconfig.json +0 -14
package/src/client.ts
DELETED
|
@@ -1,764 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import type { Config } from './config.js';
|
|
3
|
-
import { request } from './http.js';
|
|
4
|
-
import { readFilesFromDirectory, readAllFilesInBatches } from './files.js';
|
|
5
|
-
import {
|
|
6
|
-
resolveWorkspace,
|
|
7
|
-
readLocalConfig,
|
|
8
|
-
writeLocalConfig,
|
|
9
|
-
addGlobalMapping,
|
|
10
|
-
type WorkspaceConfig
|
|
11
|
-
} from './workspace-config.js';
|
|
12
|
-
|
|
13
|
-
const uuidSchema = z.string().uuid();
|
|
14
|
-
|
|
15
|
-
export class ContextStreamClient {
|
|
16
|
-
constructor(private config: Config) {}
|
|
17
|
-
|
|
18
|
-
private withDefaults<T extends { workspace_id?: string; project_id?: string }>(
|
|
19
|
-
input: T
|
|
20
|
-
): T {
|
|
21
|
-
const { defaultWorkspaceId, defaultProjectId } = this.config;
|
|
22
|
-
return {
|
|
23
|
-
...input,
|
|
24
|
-
workspace_id: input.workspace_id || defaultWorkspaceId,
|
|
25
|
-
project_id: input.project_id || defaultProjectId,
|
|
26
|
-
} as T;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Auth
|
|
30
|
-
me() {
|
|
31
|
-
return request(this.config, '/auth/me');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Workspaces & Projects
|
|
35
|
-
listWorkspaces(params?: { page?: number; page_size?: number }) {
|
|
36
|
-
const query = new URLSearchParams();
|
|
37
|
-
if (params?.page) query.set('page', String(params.page));
|
|
38
|
-
if (params?.page_size) query.set('page_size', String(params.page_size));
|
|
39
|
-
const suffix = query.toString() ? `?${query.toString()}` : '';
|
|
40
|
-
return request(this.config, `/workspaces${suffix}`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
createWorkspace(input: { name: string; description?: string; visibility?: string }) {
|
|
44
|
-
return request(this.config, '/workspaces', { body: input });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
listProjects(params?: { workspace_id?: string; page?: number; page_size?: number }) {
|
|
48
|
-
const withDefaults = this.withDefaults(params || {});
|
|
49
|
-
const query = new URLSearchParams();
|
|
50
|
-
if (withDefaults.workspace_id) query.set('workspace_id', withDefaults.workspace_id);
|
|
51
|
-
if (params?.page) query.set('page', String(params.page));
|
|
52
|
-
if (params?.page_size) query.set('page_size', String(params.page_size));
|
|
53
|
-
const suffix = query.toString() ? `?${query.toString()}` : '';
|
|
54
|
-
return request(this.config, `/projects${suffix}`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
createProject(input: { name: string; description?: string; workspace_id?: string }) {
|
|
58
|
-
const payload = this.withDefaults(input);
|
|
59
|
-
return request(this.config, '/projects', { body: payload });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
indexProject(projectId: string) {
|
|
63
|
-
uuidSchema.parse(projectId);
|
|
64
|
-
return request(this.config, `/projects/${projectId}/index`, { body: {} });
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Search
|
|
68
|
-
searchSemantic(body: { query: string; workspace_id?: string; project_id?: string; limit?: number }) {
|
|
69
|
-
return request(this.config, '/search/semantic', { body: this.withDefaults(body) });
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
searchHybrid(body: { query: string; workspace_id?: string; project_id?: string; limit?: number }) {
|
|
73
|
-
return request(this.config, '/search/hybrid', { body: this.withDefaults(body) });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
searchKeyword(body: { query: string; workspace_id?: string; project_id?: string; limit?: number }) {
|
|
77
|
-
return request(this.config, '/search/keyword', { body: this.withDefaults(body) });
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
searchPattern(body: { query: string; workspace_id?: string; project_id?: string; limit?: number }) {
|
|
81
|
-
return request(this.config, '/search/pattern', { body: this.withDefaults(body) });
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Memory / Knowledge
|
|
85
|
-
createMemoryEvent(body: {
|
|
86
|
-
workspace_id?: string;
|
|
87
|
-
project_id?: string;
|
|
88
|
-
event_type: string;
|
|
89
|
-
title: string;
|
|
90
|
-
content: string;
|
|
91
|
-
metadata?: Record<string, any>;
|
|
92
|
-
}) {
|
|
93
|
-
return request(this.config, '/memory/events', { body: this.withDefaults(body) });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
bulkIngestEvents(body: { workspace_id?: string; project_id?: string; events: any[] }) {
|
|
97
|
-
return request(this.config, '/memory/events/ingest', { body: this.withDefaults(body) });
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
listMemoryEvents(params?: { workspace_id?: string; project_id?: string; limit?: number }) {
|
|
101
|
-
const withDefaults = this.withDefaults(params || {});
|
|
102
|
-
if (!withDefaults.workspace_id) {
|
|
103
|
-
throw new Error('workspace_id is required for listing memory events');
|
|
104
|
-
}
|
|
105
|
-
const query = new URLSearchParams();
|
|
106
|
-
if (params?.limit) query.set('limit', String(params.limit));
|
|
107
|
-
if (withDefaults.project_id) query.set('project_id', withDefaults.project_id);
|
|
108
|
-
const suffix = query.toString() ? `?${query.toString()}` : '';
|
|
109
|
-
return request(this.config, `/memory/events/workspace/${withDefaults.workspace_id}${suffix}`, { method: 'GET' });
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
createKnowledgeNode(body: {
|
|
113
|
-
workspace_id?: string;
|
|
114
|
-
project_id?: string;
|
|
115
|
-
node_type: string;
|
|
116
|
-
title: string;
|
|
117
|
-
content: string;
|
|
118
|
-
relations?: Array<{ type: string; target_id: string }>;
|
|
119
|
-
}) {
|
|
120
|
-
return request(this.config, '/memory/nodes', { body: this.withDefaults(body) });
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
listKnowledgeNodes(params?: { workspace_id?: string; project_id?: string; limit?: number }) {
|
|
124
|
-
const withDefaults = this.withDefaults(params || {});
|
|
125
|
-
if (!withDefaults.workspace_id) {
|
|
126
|
-
throw new Error('workspace_id is required for listing knowledge nodes');
|
|
127
|
-
}
|
|
128
|
-
const query = new URLSearchParams();
|
|
129
|
-
if (params?.limit) query.set('limit', String(params.limit));
|
|
130
|
-
if (withDefaults.project_id) query.set('project_id', withDefaults.project_id);
|
|
131
|
-
const suffix = query.toString() ? `?${query.toString()}` : '';
|
|
132
|
-
return request(this.config, `/memory/nodes/workspace/${withDefaults.workspace_id}${suffix}`, { method: 'GET' });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
memorySearch(body: { query: string; workspace_id?: string; project_id?: string; limit?: number }) {
|
|
136
|
-
return request(this.config, '/memory/search', { body: this.withDefaults(body) });
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
memoryDecisions(params?: { workspace_id?: string; project_id?: string; limit?: number }) {
|
|
140
|
-
const query = new URLSearchParams();
|
|
141
|
-
const withDefaults = this.withDefaults(params || {});
|
|
142
|
-
if (withDefaults.workspace_id) query.set('workspace_id', withDefaults.workspace_id);
|
|
143
|
-
if (withDefaults.project_id) query.set('project_id', withDefaults.project_id);
|
|
144
|
-
if (params?.limit) query.set('limit', String(params.limit));
|
|
145
|
-
const suffix = query.toString() ? `?${query.toString()}` : '';
|
|
146
|
-
return request(this.config, `/memory/search/decisions${suffix}`, { method: 'GET' });
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Graph
|
|
150
|
-
graphRelated(body: { workspace_id?: string; project_id?: string; node_id: string; limit?: number }) {
|
|
151
|
-
return request(this.config, '/graph/knowledge/related', { body: this.withDefaults(body) });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
graphPath(body: { workspace_id?: string; project_id?: string; source_id: string; target_id: string }) {
|
|
155
|
-
return request(this.config, '/graph/knowledge/path', { body: this.withDefaults(body) });
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
graphDecisions(body?: { workspace_id?: string; project_id?: string; limit?: number }) {
|
|
159
|
-
return request(this.config, '/graph/knowledge/decisions', { body: this.withDefaults(body || {}) });
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
graphDependencies(body: { target: { type: string; id: string }; max_depth?: number; include_transitive?: boolean }) {
|
|
163
|
-
return request(this.config, '/graph/dependencies', { body });
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
graphCallPath(body: { source: { type: string; id: string }; target: { type: string; id: string }; max_depth?: number }) {
|
|
167
|
-
return request(this.config, '/graph/call-paths', { body });
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
graphImpact(body: { target: { type: string; id: string }; max_depth?: number }) {
|
|
171
|
-
return request(this.config, '/graph/impact-analysis', { body });
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// AI
|
|
175
|
-
aiContext(body: {
|
|
176
|
-
query: string;
|
|
177
|
-
workspace_id?: string;
|
|
178
|
-
project_id?: string;
|
|
179
|
-
include_code?: boolean;
|
|
180
|
-
include_docs?: boolean;
|
|
181
|
-
include_memory?: boolean;
|
|
182
|
-
limit?: number;
|
|
183
|
-
}) {
|
|
184
|
-
return request(this.config, '/ai/context', { body: this.withDefaults(body) });
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
aiEmbeddings(body: { text: string }) {
|
|
188
|
-
return request(this.config, '/ai/embeddings', { body });
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
aiPlan(body: { description: string; project_id?: string; complexity?: string }) {
|
|
192
|
-
return request(this.config, '/ai/plan/generate', { body: this.withDefaults(body) });
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
aiTasks(body: { plan_id?: string; description?: string; project_id?: string; granularity?: string }) {
|
|
196
|
-
return request(this.config, '/ai/tasks/generate', { body: this.withDefaults(body) });
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
aiEnhancedContext(body: {
|
|
200
|
-
query: string;
|
|
201
|
-
workspace_id?: string;
|
|
202
|
-
project_id?: string;
|
|
203
|
-
include_code?: boolean;
|
|
204
|
-
include_docs?: boolean;
|
|
205
|
-
include_memory?: boolean;
|
|
206
|
-
limit?: number;
|
|
207
|
-
}) {
|
|
208
|
-
return request(this.config, '/ai/context/enhanced', { body: this.withDefaults(body) });
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Project extended operations
|
|
212
|
-
getProject(projectId: string) {
|
|
213
|
-
uuidSchema.parse(projectId);
|
|
214
|
-
return request(this.config, `/projects/${projectId}`, { method: 'GET' });
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
projectOverview(projectId: string) {
|
|
218
|
-
uuidSchema.parse(projectId);
|
|
219
|
-
return request(this.config, `/projects/${projectId}/overview`, { method: 'GET' });
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
projectStatistics(projectId: string) {
|
|
223
|
-
uuidSchema.parse(projectId);
|
|
224
|
-
return request(this.config, `/projects/${projectId}/statistics`, { method: 'GET' });
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
projectFiles(projectId: string) {
|
|
228
|
-
uuidSchema.parse(projectId);
|
|
229
|
-
return request(this.config, `/projects/${projectId}/files`, { method: 'GET' });
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
projectIndexStatus(projectId: string) {
|
|
233
|
-
uuidSchema.parse(projectId);
|
|
234
|
-
return request(this.config, `/projects/${projectId}/index/status`, { method: 'GET' });
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Ingest files for indexing
|
|
239
|
-
* This uploads files to the API for indexing
|
|
240
|
-
*/
|
|
241
|
-
ingestFiles(projectId: string, files: Array<{ path: string; content: string; language?: string }>) {
|
|
242
|
-
uuidSchema.parse(projectId);
|
|
243
|
-
return request(this.config, `/projects/${projectId}/files/ingest`, {
|
|
244
|
-
body: { files },
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Workspace extended operations
|
|
249
|
-
getWorkspace(workspaceId: string) {
|
|
250
|
-
uuidSchema.parse(workspaceId);
|
|
251
|
-
return request(this.config, `/workspaces/${workspaceId}`, { method: 'GET' });
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
workspaceOverview(workspaceId: string) {
|
|
255
|
-
uuidSchema.parse(workspaceId);
|
|
256
|
-
return request(this.config, `/workspaces/${workspaceId}/overview`, { method: 'GET' });
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
workspaceAnalytics(workspaceId: string) {
|
|
260
|
-
uuidSchema.parse(workspaceId);
|
|
261
|
-
return request(this.config, `/workspaces/${workspaceId}/analytics`, { method: 'GET' });
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
workspaceContent(workspaceId: string) {
|
|
265
|
-
uuidSchema.parse(workspaceId);
|
|
266
|
-
return request(this.config, `/workspaces/${workspaceId}/content`, { method: 'GET' });
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Memory extended operations
|
|
270
|
-
getMemoryEvent(eventId: string) {
|
|
271
|
-
uuidSchema.parse(eventId);
|
|
272
|
-
return request(this.config, `/memory/events/${eventId}`, { method: 'GET' });
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
updateMemoryEvent(eventId: string, body: { title?: string; content?: string; metadata?: Record<string, any> }) {
|
|
276
|
-
uuidSchema.parse(eventId);
|
|
277
|
-
return request(this.config, `/memory/events/${eventId}`, { method: 'PUT', body });
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
deleteMemoryEvent(eventId: string) {
|
|
281
|
-
uuidSchema.parse(eventId);
|
|
282
|
-
return request(this.config, `/memory/events/${eventId}`, { method: 'DELETE' });
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
distillMemoryEvent(eventId: string) {
|
|
286
|
-
uuidSchema.parse(eventId);
|
|
287
|
-
return request(this.config, `/memory/events/${eventId}/distill`, { body: {} });
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
getKnowledgeNode(nodeId: string) {
|
|
291
|
-
uuidSchema.parse(nodeId);
|
|
292
|
-
return request(this.config, `/memory/nodes/${nodeId}`, { method: 'GET' });
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
updateKnowledgeNode(nodeId: string, body: { title?: string; content?: string; relations?: Array<{ type: string; target_id: string }> }) {
|
|
296
|
-
uuidSchema.parse(nodeId);
|
|
297
|
-
return request(this.config, `/memory/nodes/${nodeId}`, { method: 'PUT', body });
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
deleteKnowledgeNode(nodeId: string) {
|
|
301
|
-
uuidSchema.parse(nodeId);
|
|
302
|
-
return request(this.config, `/memory/nodes/${nodeId}`, { method: 'DELETE' });
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
supersedeKnowledgeNode(nodeId: string, body: { new_content: string; reason?: string }) {
|
|
306
|
-
uuidSchema.parse(nodeId);
|
|
307
|
-
return request(this.config, `/memory/nodes/${nodeId}/supersede`, { body });
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
memoryTimeline(workspaceId: string) {
|
|
311
|
-
uuidSchema.parse(workspaceId);
|
|
312
|
-
return request(this.config, `/memory/search/timeline/${workspaceId}`, { method: 'GET' });
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
memorySummary(workspaceId: string) {
|
|
316
|
-
uuidSchema.parse(workspaceId);
|
|
317
|
-
return request(this.config, `/memory/search/summary/${workspaceId}`, { method: 'GET' });
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Graph extended operations
|
|
321
|
-
findCircularDependencies(projectId: string) {
|
|
322
|
-
uuidSchema.parse(projectId);
|
|
323
|
-
return request(this.config, `/graph/circular-dependencies/${projectId}`, { method: 'GET' });
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
findUnusedCode(projectId: string) {
|
|
327
|
-
uuidSchema.parse(projectId);
|
|
328
|
-
return request(this.config, `/graph/unused-code/${projectId}`, { method: 'GET' });
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
findContradictions(nodeId: string) {
|
|
332
|
-
uuidSchema.parse(nodeId);
|
|
333
|
-
return request(this.config, `/graph/knowledge/contradictions/${nodeId}`, { method: 'GET' });
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Search suggestions
|
|
337
|
-
searchSuggestions(body: { query: string; workspace_id?: string; project_id?: string }) {
|
|
338
|
-
return request(this.config, '/search/suggest', { body: this.withDefaults(body) });
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// ============================================
|
|
342
|
-
// Session & Auto-Context Initialization
|
|
343
|
-
// ============================================
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Initialize a conversation session and retrieve relevant context automatically.
|
|
347
|
-
* This is the key tool for AI assistants to get context at the start of a conversation.
|
|
348
|
-
*
|
|
349
|
-
* Discovery chain:
|
|
350
|
-
* 1. Check local .contextstream/config.json in repo root
|
|
351
|
-
* 2. Check parent folder heuristic mappings (~/.contextstream-mappings.json)
|
|
352
|
-
* 3. If ambiguous, return workspace candidates for user/agent selection
|
|
353
|
-
*
|
|
354
|
-
* Once workspace is resolved, loads WORKSPACE-LEVEL context (not just project),
|
|
355
|
-
* ensuring cross-project decisions and memory are available.
|
|
356
|
-
*/
|
|
357
|
-
async initSession(params: {
|
|
358
|
-
workspace_id?: string;
|
|
359
|
-
project_id?: string;
|
|
360
|
-
session_id?: string;
|
|
361
|
-
context_hint?: string;
|
|
362
|
-
include_recent_memory?: boolean;
|
|
363
|
-
include_decisions?: boolean;
|
|
364
|
-
include_user_preferences?: boolean;
|
|
365
|
-
auto_index?: boolean;
|
|
366
|
-
}, ideRoots: string[] = []) {
|
|
367
|
-
let workspaceId = params.workspace_id || this.config.defaultWorkspaceId;
|
|
368
|
-
let projectId = params.project_id || this.config.defaultProjectId;
|
|
369
|
-
let workspaceName: string | undefined;
|
|
370
|
-
|
|
371
|
-
// Build comprehensive initial context
|
|
372
|
-
const context: Record<string, unknown> = {
|
|
373
|
-
session_id: params.session_id || crypto.randomUUID(),
|
|
374
|
-
initialized_at: new Date().toISOString(),
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
const rootPath = ideRoots.length > 0 ? ideRoots[0] : undefined;
|
|
378
|
-
|
|
379
|
-
// ========================================
|
|
380
|
-
// STEP 1: Workspace Discovery Chain
|
|
381
|
-
// ========================================
|
|
382
|
-
if (!workspaceId && rootPath) {
|
|
383
|
-
// Try local config and parent mappings first
|
|
384
|
-
const resolved = resolveWorkspace(rootPath);
|
|
385
|
-
|
|
386
|
-
if (resolved.config) {
|
|
387
|
-
workspaceId = resolved.config.workspace_id;
|
|
388
|
-
workspaceName = resolved.config.workspace_name;
|
|
389
|
-
projectId = resolved.config.project_id || projectId;
|
|
390
|
-
context.workspace_source = resolved.source;
|
|
391
|
-
context.workspace_resolved_from = resolved.source === 'local_config'
|
|
392
|
-
? `${rootPath}/.contextstream/config.json`
|
|
393
|
-
: 'parent_folder_mapping';
|
|
394
|
-
} else {
|
|
395
|
-
// Ambiguous - need user selection
|
|
396
|
-
// Fetch all workspaces as candidates
|
|
397
|
-
try {
|
|
398
|
-
const workspaces = await this.listWorkspaces({ page_size: 50 }) as {
|
|
399
|
-
items?: Array<{ id: string; name: string; description?: string }>
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
if (workspaces.items && workspaces.items.length > 0) {
|
|
403
|
-
// Return ambiguous status with candidates
|
|
404
|
-
context.status = 'requires_workspace_selection';
|
|
405
|
-
context.workspace_candidates = workspaces.items.map(w => ({
|
|
406
|
-
id: w.id,
|
|
407
|
-
name: w.name,
|
|
408
|
-
description: w.description,
|
|
409
|
-
}));
|
|
410
|
-
context.message = `New folder detected: "${rootPath?.split('/').pop()}". Please select which workspace this belongs to, or create a new one.`;
|
|
411
|
-
context.ide_roots = ideRoots;
|
|
412
|
-
context.folder_name = rootPath?.split('/').pop();
|
|
413
|
-
|
|
414
|
-
// Still return early - agent needs to ask user
|
|
415
|
-
return context;
|
|
416
|
-
} else {
|
|
417
|
-
// No workspaces exist - create default
|
|
418
|
-
const newWorkspace = await this.createWorkspace({
|
|
419
|
-
name: 'My Workspace',
|
|
420
|
-
description: 'Default workspace created by ContextStream',
|
|
421
|
-
visibility: 'private',
|
|
422
|
-
}) as { id?: string; name?: string };
|
|
423
|
-
if (newWorkspace.id) {
|
|
424
|
-
workspaceId = newWorkspace.id;
|
|
425
|
-
workspaceName = newWorkspace.name;
|
|
426
|
-
context.workspace_source = 'auto_created';
|
|
427
|
-
context.workspace_created = true;
|
|
428
|
-
|
|
429
|
-
// Save to local config for next time
|
|
430
|
-
writeLocalConfig(rootPath, {
|
|
431
|
-
workspace_id: newWorkspace.id,
|
|
432
|
-
workspace_name: newWorkspace.name,
|
|
433
|
-
associated_at: new Date().toISOString(),
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
} catch (e) {
|
|
438
|
-
context.workspace_error = String(e);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// Fallback: if still no workspace and no IDE roots, pick first available
|
|
444
|
-
if (!workspaceId && !rootPath) {
|
|
445
|
-
try {
|
|
446
|
-
const workspaces = await this.listWorkspaces({ page_size: 1 }) as { items?: Array<{ id: string; name: string }> };
|
|
447
|
-
if (workspaces.items && workspaces.items.length > 0) {
|
|
448
|
-
workspaceId = workspaces.items[0].id;
|
|
449
|
-
workspaceName = workspaces.items[0].name;
|
|
450
|
-
context.workspace_source = 'fallback_first';
|
|
451
|
-
}
|
|
452
|
-
} catch (e) {
|
|
453
|
-
context.workspace_error = String(e);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// ========================================
|
|
458
|
-
// STEP 2: Project Discovery
|
|
459
|
-
// ========================================
|
|
460
|
-
if (!projectId && workspaceId && rootPath && params.auto_index !== false) {
|
|
461
|
-
const projectName = rootPath.split('/').pop() || 'My Project';
|
|
462
|
-
|
|
463
|
-
try {
|
|
464
|
-
// Check if a project with this name already exists in this workspace
|
|
465
|
-
const projects = await this.listProjects({ workspace_id: workspaceId }) as { items?: Array<{ id: string; name: string }> };
|
|
466
|
-
const existingProject = projects.items?.find(p => p.name === projectName);
|
|
467
|
-
|
|
468
|
-
if (existingProject) {
|
|
469
|
-
projectId = existingProject.id;
|
|
470
|
-
context.project_source = 'existing';
|
|
471
|
-
} else {
|
|
472
|
-
// Create project from IDE root
|
|
473
|
-
const newProject = await this.createProject({
|
|
474
|
-
name: projectName,
|
|
475
|
-
description: `Auto-created from ${rootPath}`,
|
|
476
|
-
workspace_id: workspaceId,
|
|
477
|
-
}) as { id?: string };
|
|
478
|
-
|
|
479
|
-
if (newProject.id) {
|
|
480
|
-
projectId = newProject.id;
|
|
481
|
-
context.project_source = 'auto_created';
|
|
482
|
-
context.project_created = true;
|
|
483
|
-
context.project_path = rootPath;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Update local config with project info
|
|
488
|
-
if (projectId) {
|
|
489
|
-
const existingConfig = readLocalConfig(rootPath);
|
|
490
|
-
if (existingConfig || workspaceId) {
|
|
491
|
-
writeLocalConfig(rootPath, {
|
|
492
|
-
workspace_id: workspaceId!,
|
|
493
|
-
workspace_name: workspaceName,
|
|
494
|
-
project_id: projectId,
|
|
495
|
-
project_name: projectName,
|
|
496
|
-
associated_at: existingConfig?.associated_at || new Date().toISOString(),
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// Ingest files if auto_index is enabled (default: true)
|
|
502
|
-
// Runs in BACKGROUND - does not block session_init
|
|
503
|
-
if (projectId && (params.auto_index === undefined || params.auto_index === true)) {
|
|
504
|
-
context.indexing_status = 'started';
|
|
505
|
-
|
|
506
|
-
// Fire-and-forget: start indexing in background
|
|
507
|
-
const projectIdCopy = projectId;
|
|
508
|
-
const rootPathCopy = rootPath;
|
|
509
|
-
(async () => {
|
|
510
|
-
try {
|
|
511
|
-
for await (const batch of readAllFilesInBatches(rootPathCopy, { batchSize: 50 })) {
|
|
512
|
-
await this.ingestFiles(projectIdCopy, batch);
|
|
513
|
-
}
|
|
514
|
-
console.error(`[ContextStream] Background indexing completed for ${rootPathCopy}`);
|
|
515
|
-
} catch (e) {
|
|
516
|
-
console.error(`[ContextStream] Background indexing failed:`, e);
|
|
517
|
-
}
|
|
518
|
-
})();
|
|
519
|
-
}
|
|
520
|
-
} catch (e) {
|
|
521
|
-
context.project_error = String(e);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
context.status = 'connected';
|
|
526
|
-
context.workspace_id = workspaceId;
|
|
527
|
-
context.workspace_name = workspaceName;
|
|
528
|
-
context.project_id = projectId;
|
|
529
|
-
context.ide_roots = ideRoots;
|
|
530
|
-
|
|
531
|
-
// ========================================
|
|
532
|
-
// STEP 3: Load WORKSPACE-LEVEL Context
|
|
533
|
-
// (Cross-project memories and decisions)
|
|
534
|
-
// ========================================
|
|
535
|
-
if (workspaceId) {
|
|
536
|
-
try {
|
|
537
|
-
context.workspace = await this.workspaceOverview(workspaceId);
|
|
538
|
-
} catch { /* optional */ }
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (projectId) {
|
|
542
|
-
try {
|
|
543
|
-
context.project = await this.projectOverview(projectId);
|
|
544
|
-
} catch { /* optional */ }
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Fetch WORKSPACE-level memory (not filtered by project)
|
|
548
|
-
// This ensures cross-project context is available
|
|
549
|
-
if (params.include_recent_memory !== false && workspaceId) {
|
|
550
|
-
try {
|
|
551
|
-
context.recent_memory = await this.listMemoryEvents({
|
|
552
|
-
workspace_id: workspaceId,
|
|
553
|
-
// NOTE: Intentionally NOT filtering by project_id
|
|
554
|
-
// to get workspace-wide context
|
|
555
|
-
limit: 10,
|
|
556
|
-
});
|
|
557
|
-
} catch { /* optional */ }
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// Fetch WORKSPACE-level decisions
|
|
561
|
-
if (params.include_decisions !== false && workspaceId) {
|
|
562
|
-
try {
|
|
563
|
-
context.recent_decisions = await this.memoryDecisions({
|
|
564
|
-
workspace_id: workspaceId,
|
|
565
|
-
// NOTE: Intentionally NOT filtering by project_id
|
|
566
|
-
limit: 5,
|
|
567
|
-
});
|
|
568
|
-
} catch { /* optional */ }
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// Search for context hint if provided
|
|
572
|
-
if (params.context_hint && workspaceId) {
|
|
573
|
-
try {
|
|
574
|
-
context.relevant_context = await this.memorySearch({
|
|
575
|
-
query: params.context_hint,
|
|
576
|
-
workspace_id: workspaceId,
|
|
577
|
-
// Search workspace-wide first
|
|
578
|
-
limit: 5,
|
|
579
|
-
});
|
|
580
|
-
} catch { /* optional */ }
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
return context;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* Associate a folder with a workspace (called after user selects from candidates).
|
|
588
|
-
* Persists the selection to .contextstream/config.json for future sessions.
|
|
589
|
-
*/
|
|
590
|
-
async associateWorkspace(params: {
|
|
591
|
-
folder_path: string;
|
|
592
|
-
workspace_id: string;
|
|
593
|
-
workspace_name?: string;
|
|
594
|
-
create_parent_mapping?: boolean; // Also create a parent folder mapping
|
|
595
|
-
}) {
|
|
596
|
-
const { folder_path, workspace_id, workspace_name, create_parent_mapping } = params;
|
|
597
|
-
|
|
598
|
-
// Save local config
|
|
599
|
-
const saved = writeLocalConfig(folder_path, {
|
|
600
|
-
workspace_id,
|
|
601
|
-
workspace_name,
|
|
602
|
-
associated_at: new Date().toISOString(),
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
// Optionally create parent folder mapping (e.g., /home/user/dev/company/* -> workspace)
|
|
606
|
-
if (create_parent_mapping) {
|
|
607
|
-
const parentDir = folder_path.split('/').slice(0, -1).join('/');
|
|
608
|
-
addGlobalMapping({
|
|
609
|
-
pattern: `${parentDir}/*`,
|
|
610
|
-
workspace_id,
|
|
611
|
-
workspace_name: workspace_name || 'Unknown',
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
return {
|
|
616
|
-
success: saved,
|
|
617
|
-
config_path: `${folder_path}/.contextstream/config.json`,
|
|
618
|
-
workspace_id,
|
|
619
|
-
workspace_name,
|
|
620
|
-
parent_mapping_created: create_parent_mapping || false,
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
/**
|
|
625
|
-
* Get user preferences and persona from memory.
|
|
626
|
-
* Useful for AI to understand user's coding style, preferences, etc.
|
|
627
|
-
*/
|
|
628
|
-
async getUserContext(params: { workspace_id?: string }) {
|
|
629
|
-
const withDefaults = this.withDefaults(params);
|
|
630
|
-
|
|
631
|
-
if (!withDefaults.workspace_id) {
|
|
632
|
-
throw new Error('workspace_id is required for getUserContext');
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const context: Record<string, unknown> = {};
|
|
636
|
-
|
|
637
|
-
// Search for user preferences
|
|
638
|
-
try {
|
|
639
|
-
context.preferences = await this.memorySearch({
|
|
640
|
-
query: 'user preferences coding style settings',
|
|
641
|
-
workspace_id: withDefaults.workspace_id,
|
|
642
|
-
limit: 10,
|
|
643
|
-
});
|
|
644
|
-
} catch { /* optional */ }
|
|
645
|
-
|
|
646
|
-
// Get memory summary for overall context
|
|
647
|
-
try {
|
|
648
|
-
context.summary = await this.memorySummary(withDefaults.workspace_id);
|
|
649
|
-
} catch { /* optional */ }
|
|
650
|
-
|
|
651
|
-
return context;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Capture and store conversation context automatically.
|
|
656
|
-
* Call this to persist important context from the current conversation.
|
|
657
|
-
*/
|
|
658
|
-
async captureContext(params: {
|
|
659
|
-
workspace_id?: string;
|
|
660
|
-
project_id?: string;
|
|
661
|
-
session_id?: string;
|
|
662
|
-
event_type: 'conversation' | 'decision' | 'insight' | 'preference' | 'task' | 'bug' | 'feature';
|
|
663
|
-
title: string;
|
|
664
|
-
content: string;
|
|
665
|
-
tags?: string[];
|
|
666
|
-
importance?: 'low' | 'medium' | 'high' | 'critical';
|
|
667
|
-
}) {
|
|
668
|
-
const withDefaults = this.withDefaults(params);
|
|
669
|
-
|
|
670
|
-
// Map high-level types to API EventType
|
|
671
|
-
let apiEventType = 'manual_note';
|
|
672
|
-
const tags = params.tags || [];
|
|
673
|
-
|
|
674
|
-
switch (params.event_type) {
|
|
675
|
-
case 'conversation':
|
|
676
|
-
apiEventType = 'chat';
|
|
677
|
-
break;
|
|
678
|
-
case 'task':
|
|
679
|
-
apiEventType = 'task_created';
|
|
680
|
-
break;
|
|
681
|
-
case 'bug':
|
|
682
|
-
case 'feature':
|
|
683
|
-
apiEventType = 'ticket';
|
|
684
|
-
tags.push(params.event_type);
|
|
685
|
-
break;
|
|
686
|
-
case 'decision':
|
|
687
|
-
case 'insight':
|
|
688
|
-
case 'preference':
|
|
689
|
-
apiEventType = 'manual_note';
|
|
690
|
-
tags.push(params.event_type);
|
|
691
|
-
break;
|
|
692
|
-
default:
|
|
693
|
-
apiEventType = 'manual_note';
|
|
694
|
-
tags.push(params.event_type);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
return this.createMemoryEvent({
|
|
698
|
-
workspace_id: withDefaults.workspace_id,
|
|
699
|
-
project_id: withDefaults.project_id,
|
|
700
|
-
event_type: apiEventType,
|
|
701
|
-
title: params.title,
|
|
702
|
-
content: params.content,
|
|
703
|
-
metadata: {
|
|
704
|
-
original_type: params.event_type,
|
|
705
|
-
session_id: params.session_id,
|
|
706
|
-
tags: tags,
|
|
707
|
-
importance: params.importance || 'medium',
|
|
708
|
-
captured_at: new Date().toISOString(),
|
|
709
|
-
source: 'mcp_auto_capture',
|
|
710
|
-
},
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
/**
|
|
715
|
-
* Search memory with automatic context enrichment.
|
|
716
|
-
* Returns both direct matches and related context.
|
|
717
|
-
*/
|
|
718
|
-
async smartSearch(params: {
|
|
719
|
-
query: string;
|
|
720
|
-
workspace_id?: string;
|
|
721
|
-
project_id?: string;
|
|
722
|
-
include_related?: boolean;
|
|
723
|
-
include_decisions?: boolean;
|
|
724
|
-
}) {
|
|
725
|
-
const withDefaults = this.withDefaults(params);
|
|
726
|
-
|
|
727
|
-
const results: Record<string, unknown> = {};
|
|
728
|
-
|
|
729
|
-
// Primary memory search
|
|
730
|
-
try {
|
|
731
|
-
results.memory_results = await this.memorySearch({
|
|
732
|
-
query: params.query,
|
|
733
|
-
workspace_id: withDefaults.workspace_id,
|
|
734
|
-
project_id: withDefaults.project_id,
|
|
735
|
-
limit: 10,
|
|
736
|
-
});
|
|
737
|
-
} catch { /* optional */ }
|
|
738
|
-
|
|
739
|
-
// Semantic code search if project specified
|
|
740
|
-
if (withDefaults.project_id) {
|
|
741
|
-
try {
|
|
742
|
-
results.code_results = await this.searchSemantic({
|
|
743
|
-
query: params.query,
|
|
744
|
-
workspace_id: withDefaults.workspace_id,
|
|
745
|
-
project_id: withDefaults.project_id,
|
|
746
|
-
limit: 5,
|
|
747
|
-
});
|
|
748
|
-
} catch { /* optional */ }
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
// Include related decisions
|
|
752
|
-
if (params.include_decisions !== false && withDefaults.workspace_id) {
|
|
753
|
-
try {
|
|
754
|
-
results.related_decisions = await this.memoryDecisions({
|
|
755
|
-
workspace_id: withDefaults.workspace_id,
|
|
756
|
-
project_id: withDefaults.project_id,
|
|
757
|
-
limit: 3,
|
|
758
|
-
});
|
|
759
|
-
} catch { /* optional */ }
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
return results;
|
|
763
|
-
}
|
|
764
|
-
}
|