coder-config 0.40.1

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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +553 -0
  3. package/cli.js +431 -0
  4. package/config-loader.js +294 -0
  5. package/hooks/activity-track.sh +56 -0
  6. package/hooks/codex-workstream.sh +44 -0
  7. package/hooks/gemini-workstream.sh +44 -0
  8. package/hooks/workstream-inject.sh +20 -0
  9. package/lib/activity.js +283 -0
  10. package/lib/apply.js +344 -0
  11. package/lib/cli.js +267 -0
  12. package/lib/config.js +171 -0
  13. package/lib/constants.js +55 -0
  14. package/lib/env.js +114 -0
  15. package/lib/index.js +47 -0
  16. package/lib/init.js +122 -0
  17. package/lib/mcps.js +139 -0
  18. package/lib/memory.js +201 -0
  19. package/lib/projects.js +138 -0
  20. package/lib/registry.js +83 -0
  21. package/lib/utils.js +129 -0
  22. package/lib/workstreams.js +652 -0
  23. package/package.json +80 -0
  24. package/scripts/capture-screenshots.js +142 -0
  25. package/scripts/postinstall.js +122 -0
  26. package/scripts/release.sh +71 -0
  27. package/scripts/sync-version.js +77 -0
  28. package/scripts/tauri-prepare.js +328 -0
  29. package/shared/mcp-registry.json +76 -0
  30. package/ui/dist/assets/index-DbZ3_HBD.js +3204 -0
  31. package/ui/dist/assets/index-DjLdm3Mr.css +32 -0
  32. package/ui/dist/icons/icon-192.svg +16 -0
  33. package/ui/dist/icons/icon-512.svg +16 -0
  34. package/ui/dist/index.html +39 -0
  35. package/ui/dist/manifest.json +25 -0
  36. package/ui/dist/sw.js +24 -0
  37. package/ui/dist/tutorial/claude-settings.png +0 -0
  38. package/ui/dist/tutorial/header.png +0 -0
  39. package/ui/dist/tutorial/mcp-registry.png +0 -0
  40. package/ui/dist/tutorial/memory-view.png +0 -0
  41. package/ui/dist/tutorial/permissions.png +0 -0
  42. package/ui/dist/tutorial/plugins-view.png +0 -0
  43. package/ui/dist/tutorial/project-explorer.png +0 -0
  44. package/ui/dist/tutorial/projects-view.png +0 -0
  45. package/ui/dist/tutorial/sidebar.png +0 -0
  46. package/ui/dist/tutorial/tutorial-view.png +0 -0
  47. package/ui/dist/tutorial/workstreams-view.png +0 -0
  48. package/ui/routes/activity.js +58 -0
  49. package/ui/routes/commands.js +74 -0
  50. package/ui/routes/configs.js +329 -0
  51. package/ui/routes/env.js +40 -0
  52. package/ui/routes/file-explorer.js +668 -0
  53. package/ui/routes/index.js +41 -0
  54. package/ui/routes/mcp-discovery.js +235 -0
  55. package/ui/routes/memory.js +385 -0
  56. package/ui/routes/package.json +3 -0
  57. package/ui/routes/plugins.js +466 -0
  58. package/ui/routes/projects.js +198 -0
  59. package/ui/routes/registry.js +30 -0
  60. package/ui/routes/rules.js +74 -0
  61. package/ui/routes/search.js +125 -0
  62. package/ui/routes/settings.js +381 -0
  63. package/ui/routes/subprojects.js +208 -0
  64. package/ui/routes/tool-sync.js +127 -0
  65. package/ui/routes/updates.js +339 -0
  66. package/ui/routes/workstreams.js +224 -0
  67. package/ui/server.cjs +773 -0
  68. package/ui/terminal-server.cjs +160 -0
@@ -0,0 +1,235 @@
1
+ /**
2
+ * MCP Server Discovery Routes
3
+ *
4
+ * Connects to MCP servers and discovers their available tools.
5
+ */
6
+
7
+ const { spawn } = require('child_process');
8
+ const path = require('path');
9
+ const os = require('os');
10
+
11
+ // Cache for discovered tools (serverName -> { tools, timestamp })
12
+ const toolsCache = new Map();
13
+ const CACHE_TTL = 60000; // 1 minute cache
14
+
15
+ /**
16
+ * Send a JSON-RPC message to an MCP server via stdio
17
+ */
18
+ function sendMessage(proc, method, params = {}, id = 1) {
19
+ const message = {
20
+ jsonrpc: '2.0',
21
+ id,
22
+ method,
23
+ params
24
+ };
25
+ proc.stdin.write(JSON.stringify(message) + '\n');
26
+ }
27
+
28
+ /**
29
+ * Parse JSON-RPC responses from stdout
30
+ */
31
+ function parseResponses(data) {
32
+ const responses = [];
33
+ const lines = data.toString().split('\n').filter(l => l.trim());
34
+
35
+ for (const line of lines) {
36
+ try {
37
+ const parsed = JSON.parse(line);
38
+ if (parsed.jsonrpc === '2.0') {
39
+ responses.push(parsed);
40
+ }
41
+ } catch (e) {
42
+ // Skip non-JSON lines (could be server logs)
43
+ }
44
+ }
45
+
46
+ return responses;
47
+ }
48
+
49
+ /**
50
+ * Expand environment variables in a string
51
+ */
52
+ function expandEnv(str, env = {}) {
53
+ if (typeof str !== 'string') return str;
54
+
55
+ // Combine process env with custom env
56
+ const fullEnv = { ...process.env, ...env };
57
+
58
+ return str.replace(/\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, p1, p2) => {
59
+ const varName = p1 || p2;
60
+ return fullEnv[varName] || match;
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Discover tools from a stdio-based MCP server
66
+ */
67
+ async function discoverStdioTools(serverName, config) {
68
+ return new Promise((resolve, reject) => {
69
+ const timeout = setTimeout(() => {
70
+ proc.kill();
71
+ reject(new Error('Timeout waiting for server response'));
72
+ }, 10000); // 10 second timeout
73
+
74
+ // Expand environment variables in command and args
75
+ const command = expandEnv(config.command, config.env);
76
+ const args = (config.args || []).map(arg => expandEnv(arg, config.env));
77
+
78
+ // Prepare environment
79
+ const env = { ...process.env };
80
+ if (config.env) {
81
+ for (const [key, value] of Object.entries(config.env)) {
82
+ env[key] = expandEnv(value, config.env);
83
+ }
84
+ }
85
+
86
+ const proc = spawn(command, args, {
87
+ stdio: ['pipe', 'pipe', 'pipe'],
88
+ env,
89
+ cwd: config.cwd || os.homedir(),
90
+ shell: process.platform === 'win32'
91
+ });
92
+
93
+ let stdout = '';
94
+ let stderr = '';
95
+ let initialized = false;
96
+ let toolsReceived = false;
97
+
98
+ proc.stdout.on('data', (data) => {
99
+ stdout += data.toString();
100
+ const responses = parseResponses(data);
101
+
102
+ for (const response of responses) {
103
+ if (response.id === 1 && !initialized) {
104
+ // Initialize response received, now request tools
105
+ initialized = true;
106
+ sendMessage(proc, 'tools/list', {}, 2);
107
+ } else if (response.id === 2 && !toolsReceived) {
108
+ // Tools list response
109
+ toolsReceived = true;
110
+ clearTimeout(timeout);
111
+ proc.kill();
112
+
113
+ const tools = response.result?.tools || [];
114
+ resolve(tools.map(t => ({
115
+ name: t.name,
116
+ description: t.description || '',
117
+ inputSchema: t.inputSchema
118
+ })));
119
+ }
120
+ }
121
+ });
122
+
123
+ proc.stderr.on('data', (data) => {
124
+ stderr += data.toString();
125
+ });
126
+
127
+ proc.on('error', (err) => {
128
+ clearTimeout(timeout);
129
+ reject(new Error(`Failed to spawn server: ${err.message}`));
130
+ });
131
+
132
+ proc.on('close', (code) => {
133
+ if (!toolsReceived) {
134
+ clearTimeout(timeout);
135
+ reject(new Error(`Server exited with code ${code}. stderr: ${stderr.slice(0, 200)}`));
136
+ }
137
+ });
138
+
139
+ // Send initialize request
140
+ sendMessage(proc, 'initialize', {
141
+ protocolVersion: '2024-11-05',
142
+ capabilities: {},
143
+ clientInfo: {
144
+ name: 'claude-config-ui',
145
+ version: '1.0.0'
146
+ }
147
+ }, 1);
148
+ });
149
+ }
150
+
151
+ /**
152
+ * Discover tools from an SSE-based MCP server
153
+ */
154
+ async function discoverSseTools(serverName, config) {
155
+ // SSE servers are more complex - they require establishing an event stream
156
+ // For now, return empty and note that SSE discovery is not yet supported
157
+ return [];
158
+ }
159
+
160
+ /**
161
+ * Get tools for a specific MCP server
162
+ */
163
+ async function getServerTools(manager, serverName) {
164
+ // Check cache first
165
+ const cached = toolsCache.get(serverName);
166
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
167
+ return { serverName, tools: cached.tools, cached: true };
168
+ }
169
+
170
+ // Get registry
171
+ const registry = manager.loadJson(manager.registryPath) || { mcpServers: {} };
172
+ const config = registry.mcpServers?.[serverName];
173
+
174
+ if (!config) {
175
+ return { serverName, error: 'Server not found in registry' };
176
+ }
177
+
178
+ try {
179
+ let tools;
180
+
181
+ if (config.command) {
182
+ // stdio server
183
+ tools = await discoverStdioTools(serverName, config);
184
+ } else if (config.url) {
185
+ // SSE server
186
+ tools = await discoverSseTools(serverName, config);
187
+ } else {
188
+ return { serverName, error: 'Unknown server type' };
189
+ }
190
+
191
+ // Cache the result
192
+ toolsCache.set(serverName, { tools, timestamp: Date.now() });
193
+
194
+ return { serverName, tools, cached: false };
195
+ } catch (err) {
196
+ return { serverName, error: err.message };
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Get tools for all MCP servers
202
+ */
203
+ async function getAllServerTools(manager) {
204
+ const registry = manager.loadJson(manager.registryPath) || { mcpServers: {} };
205
+ const serverNames = Object.keys(registry.mcpServers || {});
206
+
207
+ const results = await Promise.all(
208
+ serverNames.map(name => getServerTools(manager, name))
209
+ );
210
+
211
+ return results.reduce((acc, result) => {
212
+ acc[result.serverName] = result.error
213
+ ? { error: result.error }
214
+ : { tools: result.tools, cached: result.cached };
215
+ return acc;
216
+ }, {});
217
+ }
218
+
219
+ /**
220
+ * Clear the tools cache
221
+ */
222
+ function clearCache(serverName = null) {
223
+ if (serverName) {
224
+ toolsCache.delete(serverName);
225
+ } else {
226
+ toolsCache.clear();
227
+ }
228
+ return { success: true };
229
+ }
230
+
231
+ module.exports = {
232
+ getServerTools,
233
+ getAllServerTools,
234
+ clearCache
235
+ };
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Memory Routes
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+
9
+ /**
10
+ * Get all memory files (global + project + sync)
11
+ */
12
+ function getMemory(projectDir) {
13
+ const home = os.homedir();
14
+ const globalMemoryDir = path.join(home, '.claude', 'memory');
15
+ const projectMemoryDir = path.join(projectDir, '.claude', 'memory');
16
+ const syncDir = path.join(home, '.claude', 'sync');
17
+ const templatesDir = path.join(home, '.claude', 'templates', 'project-memory');
18
+
19
+ const result = {
20
+ global: {
21
+ dir: globalMemoryDir,
22
+ files: []
23
+ },
24
+ project: {
25
+ dir: projectMemoryDir,
26
+ files: [],
27
+ initialized: false
28
+ },
29
+ sync: {
30
+ dir: syncDir,
31
+ state: null,
32
+ history: []
33
+ },
34
+ templates: {
35
+ dir: templatesDir,
36
+ available: fs.existsSync(templatesDir)
37
+ }
38
+ };
39
+
40
+ // Global memory files
41
+ const globalFiles = ['index.md', 'preferences.md', 'corrections.md', 'facts.md'];
42
+ for (const file of globalFiles) {
43
+ const filePath = path.join(globalMemoryDir, file);
44
+ result.global.files.push({
45
+ name: file,
46
+ path: filePath,
47
+ exists: fs.existsSync(filePath),
48
+ type: file.replace('.md', '')
49
+ });
50
+ }
51
+
52
+ // Project memory files
53
+ const projectFiles = ['context.md', 'patterns.md', 'decisions.md', 'issues.md', 'history.md'];
54
+ result.project.initialized = fs.existsSync(projectMemoryDir);
55
+ for (const file of projectFiles) {
56
+ const filePath = path.join(projectMemoryDir, file);
57
+ result.project.files.push({
58
+ name: file,
59
+ path: filePath,
60
+ exists: fs.existsSync(filePath),
61
+ type: file.replace('.md', '')
62
+ });
63
+ }
64
+
65
+ // Sync state
66
+ const stateJsonPath = path.join(syncDir, 'state.json');
67
+ const stateMdPath = path.join(syncDir, 'state.md');
68
+ if (fs.existsSync(stateJsonPath)) {
69
+ try {
70
+ result.sync.state = JSON.parse(fs.readFileSync(stateJsonPath, 'utf8'));
71
+ result.sync.stateMd = fs.existsSync(stateMdPath) ? fs.readFileSync(stateMdPath, 'utf8') : null;
72
+ } catch (e) {
73
+ result.sync.state = null;
74
+ }
75
+ }
76
+
77
+ // Sync history
78
+ const historyDir = path.join(syncDir, 'history');
79
+ if (fs.existsSync(historyDir)) {
80
+ try {
81
+ const files = fs.readdirSync(historyDir)
82
+ .filter(f => f.endsWith('.json'))
83
+ .sort()
84
+ .reverse()
85
+ .slice(0, 10);
86
+ result.sync.history = files.map(f => ({
87
+ name: f,
88
+ path: path.join(historyDir, f)
89
+ }));
90
+ } catch (e) {}
91
+ }
92
+
93
+ return result;
94
+ }
95
+
96
+ /**
97
+ * Get a specific memory file content
98
+ */
99
+ function getMemoryFile(filePath, projectDir) {
100
+ if (!filePath) {
101
+ return { error: 'Path required' };
102
+ }
103
+
104
+ const home = os.homedir();
105
+ const normalizedPath = path.resolve(filePath);
106
+ const isGlobalMemory = normalizedPath.startsWith(path.join(home, '.claude'));
107
+ const isProjectMemory = normalizedPath.startsWith(path.join(projectDir, '.claude'));
108
+
109
+ if (!isGlobalMemory && !isProjectMemory) {
110
+ return { error: 'Access denied: path must be within .claude directory' };
111
+ }
112
+
113
+ if (!fs.existsSync(normalizedPath)) {
114
+ return { content: '', exists: false };
115
+ }
116
+
117
+ try {
118
+ const content = fs.readFileSync(normalizedPath, 'utf8');
119
+ return { content, exists: true, path: normalizedPath };
120
+ } catch (e) {
121
+ return { error: e.message };
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Save a memory file
127
+ */
128
+ function saveMemoryFile(body, projectDir) {
129
+ const { path: filePath, content } = body;
130
+
131
+ if (!filePath) {
132
+ return { error: 'Path required' };
133
+ }
134
+
135
+ const home = os.homedir();
136
+ const normalizedPath = path.resolve(filePath);
137
+ const isGlobalMemory = normalizedPath.startsWith(path.join(home, '.claude'));
138
+ const isProjectMemory = normalizedPath.startsWith(path.join(projectDir, '.claude'));
139
+
140
+ if (!isGlobalMemory && !isProjectMemory) {
141
+ return { error: 'Access denied: path must be within .claude directory' };
142
+ }
143
+
144
+ try {
145
+ const dir = path.dirname(normalizedPath);
146
+ if (!fs.existsSync(dir)) {
147
+ fs.mkdirSync(dir, { recursive: true });
148
+ }
149
+
150
+ fs.writeFileSync(normalizedPath, content, 'utf8');
151
+ return { success: true, path: normalizedPath };
152
+ } catch (e) {
153
+ return { error: e.message };
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Add a memory entry to the appropriate file
159
+ */
160
+ function addMemoryEntry(body, projectDir) {
161
+ const { type, content, scope = 'global' } = body;
162
+
163
+ if (!type || !content) {
164
+ return { error: 'Type and content required' };
165
+ }
166
+
167
+ const typeToFile = {
168
+ preference: { file: 'preferences.md', dir: 'global' },
169
+ correction: { file: 'corrections.md', dir: 'global' },
170
+ fact: { file: 'facts.md', dir: 'global' },
171
+ pattern: { file: 'patterns.md', dir: 'project' },
172
+ decision: { file: 'decisions.md', dir: 'project' },
173
+ issue: { file: 'issues.md', dir: 'project' },
174
+ history: { file: 'history.md', dir: 'project' },
175
+ context: { file: 'context.md', dir: 'project' }
176
+ };
177
+
178
+ const mapping = typeToFile[type];
179
+ if (!mapping) {
180
+ return { error: `Unknown type: ${type}. Valid types: ${Object.keys(typeToFile).join(', ')}` };
181
+ }
182
+
183
+ const home = os.homedir();
184
+ let targetDir;
185
+ if (mapping.dir === 'global' || scope === 'global') {
186
+ targetDir = path.join(home, '.claude', 'memory');
187
+ } else {
188
+ targetDir = path.join(projectDir, '.claude', 'memory');
189
+ }
190
+
191
+ const targetPath = path.join(targetDir, mapping.file);
192
+
193
+ try {
194
+ if (!fs.existsSync(targetDir)) {
195
+ fs.mkdirSync(targetDir, { recursive: true });
196
+ }
197
+
198
+ let existing = '';
199
+ if (fs.existsSync(targetPath)) {
200
+ existing = fs.readFileSync(targetPath, 'utf8');
201
+ } else {
202
+ existing = `# ${type.charAt(0).toUpperCase() + type.slice(1)}s\n\n`;
203
+ }
204
+
205
+ const timestamp = new Date().toISOString().split('T')[0];
206
+ const entry = `\n## [${timestamp}]\n${content}\n`;
207
+
208
+ fs.writeFileSync(targetPath, existing + entry, 'utf8');
209
+
210
+ return { success: true, path: targetPath, type };
211
+ } catch (e) {
212
+ return { error: e.message };
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Initialize project memory from templates
218
+ */
219
+ function initProjectMemory(dir, projectDir) {
220
+ const targetDir = dir || projectDir;
221
+ const home = os.homedir();
222
+ const templatesDir = path.join(home, '.claude', 'templates', 'project-memory');
223
+ const memoryDir = path.join(targetDir, '.claude', 'memory');
224
+
225
+ if (!fs.existsSync(templatesDir)) {
226
+ const defaultTemplates = {
227
+ 'context.md': `# Project Context\n\n## Overview\n[Describe what this project does]\n\n## Tech Stack\n- \n\n## Key Conventions\n- \n`,
228
+ 'patterns.md': `# Code Patterns\n\n## Common Patterns\n[Document recurring patterns in this codebase]\n`,
229
+ 'decisions.md': `# Architecture Decisions\n\n## ADRs\n[Record important decisions and their rationale]\n`,
230
+ 'issues.md': `# Known Issues\n\n## Current Issues\n[Track bugs, limitations, and workarounds]\n`,
231
+ 'history.md': `# Session History\n\n[Chronological log of significant work]\n`
232
+ };
233
+
234
+ try {
235
+ fs.mkdirSync(templatesDir, { recursive: true });
236
+ for (const [file, content] of Object.entries(defaultTemplates)) {
237
+ fs.writeFileSync(path.join(templatesDir, file), content, 'utf8');
238
+ }
239
+ } catch (e) {
240
+ return { error: `Failed to create templates: ${e.message}` };
241
+ }
242
+ }
243
+
244
+ if (fs.existsSync(memoryDir)) {
245
+ return { error: 'Project memory already exists', dir: memoryDir };
246
+ }
247
+
248
+ try {
249
+ fs.mkdirSync(memoryDir, { recursive: true });
250
+ const templateFiles = fs.readdirSync(templatesDir);
251
+
252
+ for (const file of templateFiles) {
253
+ const src = path.join(templatesDir, file);
254
+ const dest = path.join(memoryDir, file);
255
+ if (fs.statSync(src).isFile()) {
256
+ fs.copyFileSync(src, dest);
257
+ }
258
+ }
259
+
260
+ return { success: true, dir: memoryDir, files: templateFiles };
261
+ } catch (e) {
262
+ return { error: e.message };
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Search memory files
268
+ */
269
+ function searchMemory(query, projectDir) {
270
+ if (!query) {
271
+ return { results: [] };
272
+ }
273
+
274
+ const home = os.homedir();
275
+ const searchDirs = [
276
+ path.join(home, '.claude', 'memory'),
277
+ path.join(projectDir, '.claude', 'memory')
278
+ ];
279
+
280
+ const results = [];
281
+ const queryLower = query.toLowerCase();
282
+
283
+ for (const dir of searchDirs) {
284
+ if (!fs.existsSync(dir)) continue;
285
+
286
+ try {
287
+ const files = fs.readdirSync(dir).filter(f => f.endsWith('.md'));
288
+
289
+ for (const file of files) {
290
+ const filePath = path.join(dir, file);
291
+ const content = fs.readFileSync(filePath, 'utf8');
292
+
293
+ if (content.toLowerCase().includes(queryLower)) {
294
+ const lines = content.split('\n');
295
+ const matches = [];
296
+
297
+ for (let i = 0; i < lines.length; i++) {
298
+ if (lines[i].toLowerCase().includes(queryLower)) {
299
+ matches.push({
300
+ line: i + 1,
301
+ text: lines[i].trim().substring(0, 200)
302
+ });
303
+ }
304
+ }
305
+
306
+ if (matches.length > 0) {
307
+ results.push({
308
+ file,
309
+ path: filePath,
310
+ scope: dir.includes(projectDir) ? 'project' : 'global',
311
+ matches: matches.slice(0, 5)
312
+ });
313
+ }
314
+ }
315
+ }
316
+ } catch (e) {}
317
+ }
318
+
319
+ return { query, results };
320
+ }
321
+
322
+ /**
323
+ * Get sync state
324
+ */
325
+ function getSyncState() {
326
+ const home = os.homedir();
327
+ const syncDir = path.join(home, '.claude', 'sync');
328
+
329
+ const result = {
330
+ dir: syncDir,
331
+ state: null,
332
+ stateMd: null,
333
+ history: []
334
+ };
335
+
336
+ const stateJsonPath = path.join(syncDir, 'state.json');
337
+ if (fs.existsSync(stateJsonPath)) {
338
+ try {
339
+ result.state = JSON.parse(fs.readFileSync(stateJsonPath, 'utf8'));
340
+ } catch (e) {}
341
+ }
342
+
343
+ const stateMdPath = path.join(syncDir, 'state.md');
344
+ if (fs.existsSync(stateMdPath)) {
345
+ try {
346
+ result.stateMd = fs.readFileSync(stateMdPath, 'utf8');
347
+ } catch (e) {}
348
+ }
349
+
350
+ const historyDir = path.join(syncDir, 'history');
351
+ if (fs.existsSync(historyDir)) {
352
+ try {
353
+ const files = fs.readdirSync(historyDir)
354
+ .filter(f => f.endsWith('.json'))
355
+ .sort()
356
+ .reverse()
357
+ .slice(0, 10);
358
+
359
+ result.history = files.map(f => {
360
+ const filePath = path.join(historyDir, f);
361
+ try {
362
+ return {
363
+ name: f,
364
+ path: filePath,
365
+ data: JSON.parse(fs.readFileSync(filePath, 'utf8'))
366
+ };
367
+ } catch (e) {
368
+ return { name: f, path: filePath, error: e.message };
369
+ }
370
+ });
371
+ } catch (e) {}
372
+ }
373
+
374
+ return result;
375
+ }
376
+
377
+ module.exports = {
378
+ getMemory,
379
+ getMemoryFile,
380
+ saveMemoryFile,
381
+ addMemoryEntry,
382
+ initProjectMemory,
383
+ searchMemory,
384
+ getSyncState,
385
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }