@cryptiklemur/lattice 1.1.0 → 1.2.0
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/client/src/components/project-settings/ProjectMemory.tsx +471 -0
- package/client/src/components/project-settings/ProjectSettingsView.tsx +6 -0
- package/client/src/components/sidebar/AddProjectModal.tsx +432 -0
- package/client/src/components/sidebar/ProjectRail.tsx +8 -4
- package/client/src/components/sidebar/SettingsSidebar.tsx +2 -1
- package/client/src/hooks/useSidebar.ts +16 -0
- package/client/src/router.tsx +62 -0
- package/client/src/stores/sidebar.ts +28 -0
- package/package.json +1 -1
- package/server/src/daemon.ts +1 -0
- package/server/src/handlers/fs.ts +159 -0
- package/server/src/handlers/memory.ts +179 -0
- package/server/src/handlers/settings.ts +6 -1
- package/shared/src/messages.ts +97 -2
- package/shared/src/project-settings.ts +1 -1
|
@@ -3,6 +3,10 @@ import { registerHandler } from "../ws/router";
|
|
|
3
3
|
import { sendTo, broadcast } from "../ws/broadcast";
|
|
4
4
|
import { getProjectBySlug } from "../project/registry";
|
|
5
5
|
import { listDirectory, readFile, writeFile } from "../project/file-browser";
|
|
6
|
+
import { readdirSync, existsSync, readFileSync, statSync } from "node:fs";
|
|
7
|
+
import { join, basename } from "node:path";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { loadConfig } from "../config";
|
|
6
10
|
|
|
7
11
|
var activeProjectByClient = new Map<string, string>();
|
|
8
12
|
|
|
@@ -82,3 +86,158 @@ registerHandler("fs", function (clientId: string, message: ClientMessage) {
|
|
|
82
86
|
return;
|
|
83
87
|
}
|
|
84
88
|
});
|
|
89
|
+
|
|
90
|
+
function resolvePath(path: string): string {
|
|
91
|
+
if (!path || path === "~") return homedir();
|
|
92
|
+
if (path.startsWith("~/")) return join(homedir(), path.slice(2));
|
|
93
|
+
return path;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function detectProjectName(dirPath: string): string | null {
|
|
97
|
+
try {
|
|
98
|
+
var pkgPath = join(dirPath, "package.json");
|
|
99
|
+
if (existsSync(pkgPath)) {
|
|
100
|
+
var pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
101
|
+
if (pkg.name) return pkg.name;
|
|
102
|
+
}
|
|
103
|
+
} catch {}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
var cargoPath = join(dirPath, "Cargo.toml");
|
|
107
|
+
if (existsSync(cargoPath)) {
|
|
108
|
+
var cargo = readFileSync(cargoPath, "utf-8");
|
|
109
|
+
var cargoMatch = cargo.match(/\[package\][\s\S]*?name\s*=\s*"([^"]+)"/);
|
|
110
|
+
if (cargoMatch) return cargoMatch[1];
|
|
111
|
+
}
|
|
112
|
+
} catch {}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
var composerPath = join(dirPath, "composer.json");
|
|
116
|
+
if (existsSync(composerPath)) {
|
|
117
|
+
var composer = JSON.parse(readFileSync(composerPath, "utf-8"));
|
|
118
|
+
if (composer.name) return composer.name;
|
|
119
|
+
}
|
|
120
|
+
} catch {}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
var pyprojectPath = join(dirPath, "pyproject.toml");
|
|
124
|
+
if (existsSync(pyprojectPath)) {
|
|
125
|
+
var pyproject = readFileSync(pyprojectPath, "utf-8");
|
|
126
|
+
var pyMatch = pyproject.match(/\[project\][\s\S]*?name\s*=\s*"([^"]+)"/);
|
|
127
|
+
if (pyMatch) return pyMatch[1];
|
|
128
|
+
}
|
|
129
|
+
} catch {}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
var goModPath = join(dirPath, "go.mod");
|
|
133
|
+
if (existsSync(goModPath)) {
|
|
134
|
+
var goMod = readFileSync(goModPath, "utf-8");
|
|
135
|
+
var goMatch = goMod.match(/^module\s+(\S+)/m);
|
|
136
|
+
if (goMatch) {
|
|
137
|
+
var parts = goMatch[1].split("/");
|
|
138
|
+
return parts[parts.length - 1];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} catch {}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
var entries = readdirSync(dirPath);
|
|
145
|
+
for (var i = 0; i < entries.length; i++) {
|
|
146
|
+
if (entries[i].endsWith(".sln") || entries[i].endsWith(".csproj")) {
|
|
147
|
+
return entries[i].replace(/\.[^.]+$/, "");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} catch {}
|
|
151
|
+
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
registerHandler("browse", function (clientId: string, message: ClientMessage) {
|
|
156
|
+
if (message.type === "browse:list") {
|
|
157
|
+
var browseMsg = message as { type: "browse:list"; path: string };
|
|
158
|
+
var resolvedPath = resolvePath(browseMsg.path);
|
|
159
|
+
var home = homedir();
|
|
160
|
+
|
|
161
|
+
if (!existsSync(resolvedPath)) {
|
|
162
|
+
sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: [] });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
var stat = statSync(resolvedPath);
|
|
168
|
+
if (!stat.isDirectory()) {
|
|
169
|
+
sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: [] });
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
} catch {
|
|
173
|
+
sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: [] });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
var dirEntries = readdirSync(resolvedPath, { withFileTypes: true });
|
|
179
|
+
var results: Array<{ name: string; path: string; hasClaudeMd: boolean; projectName: string | null }> = [];
|
|
180
|
+
|
|
181
|
+
for (var i = 0; i < dirEntries.length; i++) {
|
|
182
|
+
var entry = dirEntries[i];
|
|
183
|
+
if (!entry.isDirectory()) continue;
|
|
184
|
+
|
|
185
|
+
var entryPath = join(resolvedPath, entry.name);
|
|
186
|
+
var hasClaudeMd = existsSync(join(entryPath, "CLAUDE.md"));
|
|
187
|
+
var projectName = detectProjectName(entryPath);
|
|
188
|
+
|
|
189
|
+
results.push({
|
|
190
|
+
name: entry.name,
|
|
191
|
+
path: entryPath,
|
|
192
|
+
hasClaudeMd: hasClaudeMd,
|
|
193
|
+
projectName: projectName,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
results.sort(function (a, b) { return a.name.localeCompare(b.name); });
|
|
198
|
+
|
|
199
|
+
sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: results });
|
|
200
|
+
} catch {
|
|
201
|
+
sendTo(clientId, { type: "browse:list_result", path: resolvedPath, homedir: home, entries: [] });
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (message.type === "browse:suggestions") {
|
|
207
|
+
var claudeProjectsDir = join(homedir(), ".claude", "projects");
|
|
208
|
+
var config = loadConfig();
|
|
209
|
+
var existingPaths = new Set(config.projects.map(function (p) { return p.path; }));
|
|
210
|
+
var suggestions: Array<{ path: string; name: string; hasClaudeMd: boolean }> = [];
|
|
211
|
+
|
|
212
|
+
if (existsSync(claudeProjectsDir)) {
|
|
213
|
+
try {
|
|
214
|
+
var hashDirs = readdirSync(claudeProjectsDir);
|
|
215
|
+
for (var i = 0; i < hashDirs.length; i++) {
|
|
216
|
+
var hashDir = hashDirs[i];
|
|
217
|
+
var candidatePath = "/" + hashDir.slice(1).replace(/-/g, "/");
|
|
218
|
+
|
|
219
|
+
if (!existsSync(candidatePath)) continue;
|
|
220
|
+
if (existingPaths.has(candidatePath)) continue;
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
var stat = statSync(candidatePath);
|
|
224
|
+
if (!stat.isDirectory()) continue;
|
|
225
|
+
} catch { continue; }
|
|
226
|
+
|
|
227
|
+
var hasClaudeMd = existsSync(join(candidatePath, "CLAUDE.md"));
|
|
228
|
+
var name = candidatePath.split("/").pop() || hashDir;
|
|
229
|
+
|
|
230
|
+
suggestions.push({
|
|
231
|
+
path: candidatePath,
|
|
232
|
+
name: name,
|
|
233
|
+
hasClaudeMd: hasClaudeMd,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
} catch {}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
suggestions.sort(function (a, b) { return a.name.localeCompare(b.name); });
|
|
240
|
+
sendTo(clientId, { type: "browse:suggestions_result", suggestions: suggestions });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync, unlinkSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import type { ClientMessage } from "@lattice/shared";
|
|
5
|
+
import { registerHandler } from "../ws/router";
|
|
6
|
+
import { sendTo } from "../ws/broadcast";
|
|
7
|
+
import { loadConfig } from "../config";
|
|
8
|
+
|
|
9
|
+
function getMemoryDir(projectSlug: string): string | null {
|
|
10
|
+
var config = loadConfig();
|
|
11
|
+
var project = config.projects.find(function (p) { return p.slug === projectSlug; });
|
|
12
|
+
if (!project) return null;
|
|
13
|
+
var hash = "-" + project.path.replace(/\//g, "-").replace(/^-/, "");
|
|
14
|
+
return join(homedir(), ".claude", "projects", hash, "memory");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseFrontmatter(content: string): { name: string; description: string; type: string } {
|
|
18
|
+
var match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
19
|
+
if (!match) return { name: "", description: "", type: "" };
|
|
20
|
+
var yaml = match[1];
|
|
21
|
+
var name = "";
|
|
22
|
+
var description = "";
|
|
23
|
+
var type = "";
|
|
24
|
+
var lines = yaml.split(/\r?\n/);
|
|
25
|
+
for (var i = 0; i < lines.length; i++) {
|
|
26
|
+
var line = lines[i];
|
|
27
|
+
var nameMatch = line.match(/^name:\s*(.+)/);
|
|
28
|
+
if (nameMatch) name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
29
|
+
var descMatch = line.match(/^description:\s*(.+)/);
|
|
30
|
+
if (descMatch) description = descMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
31
|
+
var typeMatch = line.match(/^type:\s*(.+)/);
|
|
32
|
+
if (typeMatch) type = typeMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
33
|
+
}
|
|
34
|
+
return { name, description, type };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function regenerateIndex(memoryDir: string): void {
|
|
38
|
+
if (!existsSync(memoryDir)) return;
|
|
39
|
+
var files = readdirSync(memoryDir).filter(function (f) {
|
|
40
|
+
return f.endsWith(".md") && f !== "MEMORY.md";
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
var grouped: Record<string, Array<{ filename: string; name: string; description: string }>> = {};
|
|
44
|
+
|
|
45
|
+
for (var i = 0; i < files.length; i++) {
|
|
46
|
+
try {
|
|
47
|
+
var content = readFileSync(join(memoryDir, files[i]), "utf-8");
|
|
48
|
+
var meta = parseFrontmatter(content);
|
|
49
|
+
var type = meta.type || "other";
|
|
50
|
+
if (!grouped[type]) grouped[type] = [];
|
|
51
|
+
grouped[type].push({ filename: files[i], name: meta.name || files[i], description: meta.description || "" });
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
var lines: string[] = ["# Memory Index", ""];
|
|
56
|
+
var types = Object.keys(grouped).sort();
|
|
57
|
+
for (var t = 0; t < types.length; t++) {
|
|
58
|
+
lines.push("## " + types[t].charAt(0).toUpperCase() + types[t].slice(1));
|
|
59
|
+
var entries = grouped[types[t]];
|
|
60
|
+
for (var e = 0; e < entries.length; e++) {
|
|
61
|
+
var desc = entries[e].description ? " — " + entries[e].description : "";
|
|
62
|
+
lines.push("- [" + entries[e].filename + "](" + entries[e].filename + ")" + desc);
|
|
63
|
+
}
|
|
64
|
+
lines.push("");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
writeFileSync(join(memoryDir, "MEMORY.md"), lines.join("\n"), "utf-8");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
registerHandler("memory", function (clientId: string, message: ClientMessage) {
|
|
71
|
+
if (message.type === "memory:list") {
|
|
72
|
+
var listMsg = message as { type: "memory:list"; projectSlug: string };
|
|
73
|
+
var memDir = getMemoryDir(listMsg.projectSlug);
|
|
74
|
+
if (!memDir || !existsSync(memDir)) {
|
|
75
|
+
sendTo(clientId, { type: "memory:list_result", projectSlug: listMsg.projectSlug, memories: [] });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
var files = readdirSync(memDir).filter(function (f) {
|
|
80
|
+
return f.endsWith(".md") && f !== "MEMORY.md";
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
var memories: Array<{ filename: string; name: string; description: string; type: string }> = [];
|
|
84
|
+
for (var i = 0; i < files.length; i++) {
|
|
85
|
+
try {
|
|
86
|
+
var content = readFileSync(join(memDir, files[i]), "utf-8");
|
|
87
|
+
var meta = parseFrontmatter(content);
|
|
88
|
+
memories.push({
|
|
89
|
+
filename: files[i],
|
|
90
|
+
name: meta.name || files[i].replace(/\.md$/, ""),
|
|
91
|
+
description: meta.description,
|
|
92
|
+
type: meta.type || "other",
|
|
93
|
+
});
|
|
94
|
+
} catch {}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
memories.sort(function (a, b) { return a.name.localeCompare(b.name); });
|
|
98
|
+
sendTo(clientId, { type: "memory:list_result", projectSlug: listMsg.projectSlug, memories: memories });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (message.type === "memory:view") {
|
|
103
|
+
var viewMsg = message as { type: "memory:view"; projectSlug: string; filename: string };
|
|
104
|
+
var viewDir = getMemoryDir(viewMsg.projectSlug);
|
|
105
|
+
if (!viewDir) {
|
|
106
|
+
sendTo(clientId, { type: "memory:view_result", filename: viewMsg.filename, content: "Project not found." });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
var viewContent = readFileSync(join(viewDir, viewMsg.filename), "utf-8");
|
|
111
|
+
sendTo(clientId, { type: "memory:view_result", filename: viewMsg.filename, content: viewContent });
|
|
112
|
+
} catch {
|
|
113
|
+
sendTo(clientId, { type: "memory:view_result", filename: viewMsg.filename, content: "File not found." });
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (message.type === "memory:save") {
|
|
119
|
+
var saveMsg = message as { type: "memory:save"; projectSlug: string; filename: string; content: string };
|
|
120
|
+
var saveDir = getMemoryDir(saveMsg.projectSlug);
|
|
121
|
+
if (!saveDir) {
|
|
122
|
+
sendTo(clientId, { type: "memory:save_result", success: false, message: "Project not found." });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
mkdirSync(saveDir, { recursive: true });
|
|
127
|
+
writeFileSync(join(saveDir, saveMsg.filename), saveMsg.content, "utf-8");
|
|
128
|
+
regenerateIndex(saveDir);
|
|
129
|
+
sendTo(clientId, { type: "memory:save_result", success: true });
|
|
130
|
+
var updatedFiles = readdirSync(saveDir).filter(function (f) { return f.endsWith(".md") && f !== "MEMORY.md"; });
|
|
131
|
+
var updatedMemories: Array<{ filename: string; name: string; description: string; type: string }> = [];
|
|
132
|
+
for (var j = 0; j < updatedFiles.length; j++) {
|
|
133
|
+
try {
|
|
134
|
+
var c = readFileSync(join(saveDir, updatedFiles[j]), "utf-8");
|
|
135
|
+
var m = parseFrontmatter(c);
|
|
136
|
+
updatedMemories.push({ filename: updatedFiles[j], name: m.name || updatedFiles[j].replace(/\.md$/, ""), description: m.description, type: m.type || "other" });
|
|
137
|
+
} catch {}
|
|
138
|
+
}
|
|
139
|
+
updatedMemories.sort(function (a, b) { return a.name.localeCompare(b.name); });
|
|
140
|
+
sendTo(clientId, { type: "memory:list_result", projectSlug: saveMsg.projectSlug, memories: updatedMemories });
|
|
141
|
+
} catch (err) {
|
|
142
|
+
sendTo(clientId, { type: "memory:save_result", success: false, message: "Failed to save: " + String(err) });
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (message.type === "memory:delete") {
|
|
148
|
+
var delMsg = message as { type: "memory:delete"; projectSlug: string; filename: string };
|
|
149
|
+
var delDir = getMemoryDir(delMsg.projectSlug);
|
|
150
|
+
if (!delDir) {
|
|
151
|
+
sendTo(clientId, { type: "memory:delete_result", success: false, message: "Project not found." });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
var filePath = join(delDir, delMsg.filename);
|
|
156
|
+
if (!existsSync(filePath)) {
|
|
157
|
+
sendTo(clientId, { type: "memory:delete_result", success: false, message: "Memory not found." });
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
unlinkSync(filePath);
|
|
161
|
+
regenerateIndex(delDir);
|
|
162
|
+
sendTo(clientId, { type: "memory:delete_result", success: true });
|
|
163
|
+
var remainingFiles = readdirSync(delDir).filter(function (f) { return f.endsWith(".md") && f !== "MEMORY.md"; });
|
|
164
|
+
var remainingMemories: Array<{ filename: string; name: string; description: string; type: string }> = [];
|
|
165
|
+
for (var k = 0; k < remainingFiles.length; k++) {
|
|
166
|
+
try {
|
|
167
|
+
var rc = readFileSync(join(delDir, remainingFiles[k]), "utf-8");
|
|
168
|
+
var rm = parseFrontmatter(rc);
|
|
169
|
+
remainingMemories.push({ filename: remainingFiles[k], name: rm.name || remainingFiles[k].replace(/\.md$/, ""), description: rm.description, type: rm.type || "other" });
|
|
170
|
+
} catch {}
|
|
171
|
+
}
|
|
172
|
+
remainingMemories.sort(function (a, b) { return a.name.localeCompare(b.name); });
|
|
173
|
+
sendTo(clientId, { type: "memory:list_result", projectSlug: delMsg.projectSlug, memories: remainingMemories });
|
|
174
|
+
} catch (err) {
|
|
175
|
+
sendTo(clientId, { type: "memory:delete_result", success: false, message: "Failed to delete: " + String(err) });
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
@@ -2,7 +2,7 @@ import type { ClientMessage, SettingsGetMessage, SettingsUpdateMessage } from "@
|
|
|
2
2
|
import { registerHandler } from "../ws/router";
|
|
3
3
|
import { sendTo, broadcast } from "../ws/broadcast";
|
|
4
4
|
import { loadConfig, saveConfig } from "../config";
|
|
5
|
-
import { addProject } from "../project/registry";
|
|
5
|
+
import { addProject, removeProject } from "../project/registry";
|
|
6
6
|
import type { LatticeConfig } from "@lattice/shared";
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
@@ -63,6 +63,11 @@ registerHandler("settings", function (clientId: string, message: ClientMessage)
|
|
|
63
63
|
delete incoming.mcpServers;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
if (typeof incoming.removeProject === "string") {
|
|
67
|
+
removeProject(incoming.removeProject as string);
|
|
68
|
+
delete incoming.removeProject;
|
|
69
|
+
}
|
|
70
|
+
|
|
66
71
|
var incomingProjects = incoming.projects as Array<{ path: string; slug?: string; title: string; env?: Record<string, string> }> | undefined;
|
|
67
72
|
if (incomingProjects && incomingProjects.length > 0) {
|
|
68
73
|
for (var i = 0; i < incomingProjects.length; i++) {
|
package/shared/src/messages.ts
CHANGED
|
@@ -223,6 +223,39 @@ export interface SkillsUpdateMessage {
|
|
|
223
223
|
source: string;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
export interface BrowseListMessage {
|
|
227
|
+
type: "browse:list";
|
|
228
|
+
path: string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface MemoryListMessage {
|
|
232
|
+
type: "memory:list";
|
|
233
|
+
projectSlug: string;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export interface MemoryViewMessage {
|
|
237
|
+
type: "memory:view";
|
|
238
|
+
projectSlug: string;
|
|
239
|
+
filename: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface MemorySaveMessage {
|
|
243
|
+
type: "memory:save";
|
|
244
|
+
projectSlug: string;
|
|
245
|
+
filename: string;
|
|
246
|
+
content: string;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface MemoryDeleteMessage {
|
|
250
|
+
type: "memory:delete";
|
|
251
|
+
projectSlug: string;
|
|
252
|
+
filename: string;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface BrowseSuggestionsMessage {
|
|
256
|
+
type: "browse:suggestions";
|
|
257
|
+
}
|
|
258
|
+
|
|
226
259
|
export interface ProjectSettingsGetMessage {
|
|
227
260
|
type: "project-settings:get";
|
|
228
261
|
projectSlug: string;
|
|
@@ -289,7 +322,13 @@ export type ClientMessage =
|
|
|
289
322
|
| SkillsInstallMessage
|
|
290
323
|
| SkillsViewMessage
|
|
291
324
|
| SkillsDeleteMessage
|
|
292
|
-
| SkillsUpdateMessage
|
|
325
|
+
| SkillsUpdateMessage
|
|
326
|
+
| BrowseListMessage
|
|
327
|
+
| MemoryListMessage
|
|
328
|
+
| MemoryViewMessage
|
|
329
|
+
| MemorySaveMessage
|
|
330
|
+
| MemoryDeleteMessage
|
|
331
|
+
| BrowseSuggestionsMessage;
|
|
293
332
|
|
|
294
333
|
export interface SessionListMessage {
|
|
295
334
|
type: "session:list";
|
|
@@ -548,6 +587,56 @@ export interface SkillsDeleteResultMessage {
|
|
|
548
587
|
message?: string;
|
|
549
588
|
}
|
|
550
589
|
|
|
590
|
+
export interface BrowseListResultMessage {
|
|
591
|
+
type: "browse:list_result";
|
|
592
|
+
path: string;
|
|
593
|
+
homedir: string;
|
|
594
|
+
entries: Array<{
|
|
595
|
+
name: string;
|
|
596
|
+
path: string;
|
|
597
|
+
hasClaudeMd: boolean;
|
|
598
|
+
projectName: string | null;
|
|
599
|
+
}>;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
export interface MemoryListResultMessage {
|
|
603
|
+
type: "memory:list_result";
|
|
604
|
+
projectSlug: string;
|
|
605
|
+
memories: Array<{
|
|
606
|
+
filename: string;
|
|
607
|
+
name: string;
|
|
608
|
+
description: string;
|
|
609
|
+
type: string;
|
|
610
|
+
}>;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
export interface MemoryViewResultMessage {
|
|
614
|
+
type: "memory:view_result";
|
|
615
|
+
filename: string;
|
|
616
|
+
content: string;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export interface MemorySaveResultMessage {
|
|
620
|
+
type: "memory:save_result";
|
|
621
|
+
success: boolean;
|
|
622
|
+
message?: string;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export interface MemoryDeleteResultMessage {
|
|
626
|
+
type: "memory:delete_result";
|
|
627
|
+
success: boolean;
|
|
628
|
+
message?: string;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export interface BrowseSuggestionsResultMessage {
|
|
632
|
+
type: "browse:suggestions_result";
|
|
633
|
+
suggestions: Array<{
|
|
634
|
+
path: string;
|
|
635
|
+
name: string;
|
|
636
|
+
hasClaudeMd: boolean;
|
|
637
|
+
}>;
|
|
638
|
+
}
|
|
639
|
+
|
|
551
640
|
export type ServerMessage =
|
|
552
641
|
| SessionListMessage
|
|
553
642
|
| SessionCreatedMessage
|
|
@@ -592,7 +681,13 @@ export type ServerMessage =
|
|
|
592
681
|
| SkillsSearchResultsMessage
|
|
593
682
|
| SkillsInstallResultMessage
|
|
594
683
|
| SkillsViewResultMessage
|
|
595
|
-
| SkillsDeleteResultMessage
|
|
684
|
+
| SkillsDeleteResultMessage
|
|
685
|
+
| BrowseListResultMessage
|
|
686
|
+
| MemoryListResultMessage
|
|
687
|
+
| MemoryViewResultMessage
|
|
688
|
+
| MemorySaveResultMessage
|
|
689
|
+
| MemoryDeleteResultMessage
|
|
690
|
+
| BrowseSuggestionsResultMessage;
|
|
596
691
|
|
|
597
692
|
export interface MeshHelloMessage {
|
|
598
693
|
type: "mesh:hello";
|
|
@@ -15,7 +15,7 @@ export type McpServerConfig =
|
|
|
15
15
|
| { type: "sse"; url: string; headers?: Record<string, string> };
|
|
16
16
|
|
|
17
17
|
export type ProjectSettingsSection =
|
|
18
|
-
| "general" | "claude" | "environment" | "mcp" | "skills" | "rules" | "permissions";
|
|
18
|
+
| "general" | "claude" | "environment" | "mcp" | "skills" | "rules" | "permissions" | "memory";
|
|
19
19
|
|
|
20
20
|
export interface ProjectSettings {
|
|
21
21
|
title: string;
|