cckb 0.1.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/README.md +247 -0
- package/dist/bin/cckb.d.ts +1 -0
- package/dist/bin/cckb.js +89 -0
- package/dist/bin/cckb.js.map +1 -0
- package/dist/chunk-GUB5D6EN.js +197 -0
- package/dist/chunk-GUB5D6EN.js.map +1 -0
- package/dist/chunk-K4W3KOBL.js +239 -0
- package/dist/chunk-K4W3KOBL.js.map +1 -0
- package/dist/chunk-TFFLX3YY.js +131 -0
- package/dist/chunk-TFFLX3YY.js.map +1 -0
- package/dist/chunk-XAY6TTXB.js +149 -0
- package/dist/chunk-XAY6TTXB.js.map +1 -0
- package/dist/chunk-Z3CJQKTH.js +547 -0
- package/dist/chunk-Z3CJQKTH.js.map +1 -0
- package/dist/hooks/notification.d.ts +3 -0
- package/dist/hooks/notification.js +132 -0
- package/dist/hooks/notification.js.map +1 -0
- package/dist/hooks/post-tool-use.d.ts +3 -0
- package/dist/hooks/post-tool-use.js +96 -0
- package/dist/hooks/post-tool-use.js.map +1 -0
- package/dist/hooks/session-start.d.ts +3 -0
- package/dist/hooks/session-start.js +57 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/hooks/stop.d.ts +3 -0
- package/dist/hooks/stop.js +80 -0
- package/dist/hooks/stop.js.map +1 -0
- package/dist/hooks/user-prompt.d.ts +3 -0
- package/dist/hooks/user-prompt.js +48 -0
- package/dist/hooks/user-prompt.js.map +1 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
- package/templates/CLAUDE.md.tmpl +29 -0
- package/templates/settings.json.tmpl +44 -0
- package/templates/vault/INDEX.md +19 -0
- package/templates/vault/architecture.md +15 -0
- package/templates/vault/entities/INDEX.md +10 -0
- package/templates/vault/general-knowledge.md +15 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appendToFile,
|
|
3
|
+
ensureDir,
|
|
4
|
+
fileExists,
|
|
5
|
+
generateSessionId,
|
|
6
|
+
getConversationsPath,
|
|
7
|
+
getCurrentTimestamp,
|
|
8
|
+
getFileSize,
|
|
9
|
+
getStatePath,
|
|
10
|
+
listDir,
|
|
11
|
+
loadConfig,
|
|
12
|
+
readJSON,
|
|
13
|
+
readTextFile,
|
|
14
|
+
writeJSON,
|
|
15
|
+
writeTextFile
|
|
16
|
+
} from "./chunk-TFFLX3YY.js";
|
|
17
|
+
|
|
18
|
+
// src/core/conversation-manager.ts
|
|
19
|
+
import * as path from "path";
|
|
20
|
+
var ConversationManager = class {
|
|
21
|
+
projectPath;
|
|
22
|
+
constructor(projectPath) {
|
|
23
|
+
this.projectPath = projectPath;
|
|
24
|
+
}
|
|
25
|
+
async createConversation(sessionId) {
|
|
26
|
+
const conversationsPath = getConversationsPath(this.projectPath);
|
|
27
|
+
const conversationPath = path.join(conversationsPath, sessionId);
|
|
28
|
+
await ensureDir(conversationPath);
|
|
29
|
+
const initialFile = path.join(conversationPath, "0.txt");
|
|
30
|
+
await writeTextFile(
|
|
31
|
+
initialFile,
|
|
32
|
+
`# Conversation: ${sessionId}
|
|
33
|
+
# Started: ${getCurrentTimestamp()}
|
|
34
|
+
|
|
35
|
+
`
|
|
36
|
+
);
|
|
37
|
+
await this.updateSessionState({
|
|
38
|
+
sessionId,
|
|
39
|
+
conversationPath,
|
|
40
|
+
currentFileIndex: 0,
|
|
41
|
+
startedAt: getCurrentTimestamp()
|
|
42
|
+
});
|
|
43
|
+
return conversationPath;
|
|
44
|
+
}
|
|
45
|
+
async appendUserInput(sessionId, prompt) {
|
|
46
|
+
const state = await this.getSessionState(sessionId);
|
|
47
|
+
if (!state) {
|
|
48
|
+
throw new Error(`No session found for: ${sessionId}`);
|
|
49
|
+
}
|
|
50
|
+
const entry = this.formatEntry({
|
|
51
|
+
type: "USER",
|
|
52
|
+
timestamp: getCurrentTimestamp(),
|
|
53
|
+
content: prompt
|
|
54
|
+
});
|
|
55
|
+
const filePath = path.join(
|
|
56
|
+
state.conversationPath,
|
|
57
|
+
`${state.currentFileIndex}.txt`
|
|
58
|
+
);
|
|
59
|
+
await appendToFile(filePath, entry);
|
|
60
|
+
await this.checkRotation(sessionId);
|
|
61
|
+
}
|
|
62
|
+
async appendClaudeOutput(sessionId, toolName, action, target) {
|
|
63
|
+
const state = await this.getSessionState(sessionId);
|
|
64
|
+
if (!state) {
|
|
65
|
+
throw new Error(`No session found for: ${sessionId}`);
|
|
66
|
+
}
|
|
67
|
+
const entry = this.formatEntry({
|
|
68
|
+
type: "CLAUDE",
|
|
69
|
+
timestamp: getCurrentTimestamp(),
|
|
70
|
+
content: action,
|
|
71
|
+
tool: {
|
|
72
|
+
name: toolName,
|
|
73
|
+
action,
|
|
74
|
+
target
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
const filePath = path.join(
|
|
78
|
+
state.conversationPath,
|
|
79
|
+
`${state.currentFileIndex}.txt`
|
|
80
|
+
);
|
|
81
|
+
await appendToFile(filePath, entry);
|
|
82
|
+
}
|
|
83
|
+
async getConversationFiles(sessionId) {
|
|
84
|
+
const state = await this.getSessionState(sessionId);
|
|
85
|
+
if (!state) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
const files = await listDir(state.conversationPath);
|
|
89
|
+
return files.filter((f) => f.endsWith(".txt")).sort((a, b) => {
|
|
90
|
+
const numA = parseInt(a.replace(".txt", ""), 10);
|
|
91
|
+
const numB = parseInt(b.replace(".txt", ""), 10);
|
|
92
|
+
return numA - numB;
|
|
93
|
+
}).map((f) => path.join(state.conversationPath, f));
|
|
94
|
+
}
|
|
95
|
+
async getFullConversation(sessionId) {
|
|
96
|
+
const files = await this.getConversationFiles(sessionId);
|
|
97
|
+
const contents = [];
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
const content = await readTextFile(file);
|
|
100
|
+
if (content) {
|
|
101
|
+
contents.push(content);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return contents.join("\n\n---\n\n");
|
|
105
|
+
}
|
|
106
|
+
async rotateConversation(sessionId) {
|
|
107
|
+
const state = await this.getSessionState(sessionId);
|
|
108
|
+
if (!state) {
|
|
109
|
+
throw new Error(`No session found for: ${sessionId}`);
|
|
110
|
+
}
|
|
111
|
+
const newIndex = state.currentFileIndex + 1;
|
|
112
|
+
const newFile = path.join(state.conversationPath, `${newIndex}.txt`);
|
|
113
|
+
await writeTextFile(
|
|
114
|
+
newFile,
|
|
115
|
+
`# Conversation: ${sessionId} (continued)
|
|
116
|
+
# Compaction: ${newIndex}
|
|
117
|
+
# Time: ${getCurrentTimestamp()}
|
|
118
|
+
|
|
119
|
+
`
|
|
120
|
+
);
|
|
121
|
+
await this.updateSessionState({
|
|
122
|
+
...state,
|
|
123
|
+
currentFileIndex: newIndex
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async getOrCreateSession(transcriptPath) {
|
|
127
|
+
if (transcriptPath) {
|
|
128
|
+
const sessionMap = await this.loadSessionMap();
|
|
129
|
+
const existing = sessionMap[transcriptPath];
|
|
130
|
+
if (existing) {
|
|
131
|
+
return existing.sessionId;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const sessionId = generateSessionId();
|
|
135
|
+
await this.createConversation(sessionId);
|
|
136
|
+
if (transcriptPath) {
|
|
137
|
+
const sessionMap = await this.loadSessionMap();
|
|
138
|
+
const state = await this.getSessionState(sessionId);
|
|
139
|
+
if (state) {
|
|
140
|
+
sessionMap[transcriptPath] = state;
|
|
141
|
+
await this.saveSessionMap(sessionMap);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return sessionId;
|
|
145
|
+
}
|
|
146
|
+
async getActiveSessionId() {
|
|
147
|
+
const statePath = getStatePath(this.projectPath);
|
|
148
|
+
const activeSessionFile = path.join(statePath, "active-session.json");
|
|
149
|
+
const data = await readJSON(activeSessionFile);
|
|
150
|
+
return data?.sessionId || null;
|
|
151
|
+
}
|
|
152
|
+
async setActiveSession(sessionId) {
|
|
153
|
+
const statePath = getStatePath(this.projectPath);
|
|
154
|
+
const activeSessionFile = path.join(statePath, "active-session.json");
|
|
155
|
+
await ensureDir(statePath);
|
|
156
|
+
await writeJSON(activeSessionFile, { sessionId });
|
|
157
|
+
}
|
|
158
|
+
async checkRotation(sessionId) {
|
|
159
|
+
const config = await loadConfig(this.projectPath);
|
|
160
|
+
const state = await this.getSessionState(sessionId);
|
|
161
|
+
if (!state) return;
|
|
162
|
+
const currentFile = path.join(
|
|
163
|
+
state.conversationPath,
|
|
164
|
+
`${state.currentFileIndex}.txt`
|
|
165
|
+
);
|
|
166
|
+
const size = await getFileSize(currentFile);
|
|
167
|
+
const thresholdBytes = config.compaction.sizeThresholdKB * 1024;
|
|
168
|
+
if (size >= thresholdBytes) {
|
|
169
|
+
await this.rotateConversation(sessionId);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
formatEntry(entry) {
|
|
173
|
+
let content = `[${entry.type}][${entry.timestamp}]`;
|
|
174
|
+
if (entry.tool) {
|
|
175
|
+
content += `[TOOL:${entry.tool.name}]`;
|
|
176
|
+
}
|
|
177
|
+
content += `
|
|
178
|
+
${entry.content}
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
`;
|
|
182
|
+
return content;
|
|
183
|
+
}
|
|
184
|
+
async getSessionState(sessionId) {
|
|
185
|
+
const sessionMap = await this.loadSessionMap();
|
|
186
|
+
for (const state of Object.values(sessionMap)) {
|
|
187
|
+
if (state.sessionId === sessionId) {
|
|
188
|
+
return state;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const conversationsPath = getConversationsPath(this.projectPath);
|
|
192
|
+
const conversationPath = path.join(conversationsPath, sessionId);
|
|
193
|
+
if (await fileExists(conversationPath)) {
|
|
194
|
+
const files = await listDir(conversationPath);
|
|
195
|
+
const txtFiles = files.filter((f) => f.endsWith(".txt"));
|
|
196
|
+
const maxIndex = Math.max(
|
|
197
|
+
...txtFiles.map((f) => parseInt(f.replace(".txt", ""), 10))
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
sessionId,
|
|
201
|
+
conversationPath,
|
|
202
|
+
currentFileIndex: maxIndex >= 0 ? maxIndex : 0,
|
|
203
|
+
startedAt: getCurrentTimestamp()
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
async updateSessionState(state) {
|
|
209
|
+
const sessionMap = await this.loadSessionMap();
|
|
210
|
+
let found = false;
|
|
211
|
+
for (const [key, existing] of Object.entries(sessionMap)) {
|
|
212
|
+
if (existing.sessionId === state.sessionId) {
|
|
213
|
+
sessionMap[key] = state;
|
|
214
|
+
found = true;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (!found) {
|
|
219
|
+
sessionMap[state.conversationPath] = state;
|
|
220
|
+
}
|
|
221
|
+
await this.saveSessionMap(sessionMap);
|
|
222
|
+
}
|
|
223
|
+
async loadSessionMap() {
|
|
224
|
+
const statePath = getStatePath(this.projectPath);
|
|
225
|
+
const mapPath = path.join(statePath, "session-map.json");
|
|
226
|
+
return await readJSON(mapPath) || {};
|
|
227
|
+
}
|
|
228
|
+
async saveSessionMap(map) {
|
|
229
|
+
const statePath = getStatePath(this.projectPath);
|
|
230
|
+
const mapPath = path.join(statePath, "session-map.json");
|
|
231
|
+
await ensureDir(statePath);
|
|
232
|
+
await writeJSON(mapPath, map);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export {
|
|
237
|
+
ConversationManager
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=chunk-K4W3KOBL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/conversation-manager.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport {\n ensureDir,\n fileExists,\n appendToFile,\n readTextFile,\n writeTextFile,\n readJSON,\n writeJSON,\n listDir,\n getFileSize,\n generateSessionId,\n getCurrentTimestamp,\n} from \"../utils/file-utils.js\";\nimport {\n getConversationsPath,\n getStatePath,\n loadConfig,\n} from \"../utils/config.js\";\n\nexport interface ConversationEntry {\n type: \"USER\" | \"CLAUDE\";\n timestamp: string;\n content: string;\n tool?: {\n name: string;\n action: string;\n target?: string;\n };\n}\n\ninterface SessionState {\n sessionId: string;\n conversationPath: string;\n currentFileIndex: number;\n startedAt: string;\n}\n\ninterface SessionMap {\n [transcriptPath: string]: SessionState;\n}\n\nexport class ConversationManager {\n private projectPath: string;\n\n constructor(projectPath: string) {\n this.projectPath = projectPath;\n }\n\n async createConversation(sessionId: string): Promise<string> {\n const conversationsPath = getConversationsPath(this.projectPath);\n const conversationPath = path.join(conversationsPath, sessionId);\n\n await ensureDir(conversationPath);\n\n // Create initial file\n const initialFile = path.join(conversationPath, \"0.txt\");\n await writeTextFile(\n initialFile,\n `# Conversation: ${sessionId}\\n# Started: ${getCurrentTimestamp()}\\n\\n`\n );\n\n // Update session state\n await this.updateSessionState({\n sessionId,\n conversationPath,\n currentFileIndex: 0,\n startedAt: getCurrentTimestamp(),\n });\n\n return conversationPath;\n }\n\n async appendUserInput(sessionId: string, prompt: string): Promise<void> {\n const state = await this.getSessionState(sessionId);\n if (!state) {\n throw new Error(`No session found for: ${sessionId}`);\n }\n\n const entry = this.formatEntry({\n type: \"USER\",\n timestamp: getCurrentTimestamp(),\n content: prompt,\n });\n\n const filePath = path.join(\n state.conversationPath,\n `${state.currentFileIndex}.txt`\n );\n await appendToFile(filePath, entry);\n\n // Check if we need to rotate\n await this.checkRotation(sessionId);\n }\n\n async appendClaudeOutput(\n sessionId: string,\n toolName: string,\n action: string,\n target?: string\n ): Promise<void> {\n const state = await this.getSessionState(sessionId);\n if (!state) {\n throw new Error(`No session found for: ${sessionId}`);\n }\n\n const entry = this.formatEntry({\n type: \"CLAUDE\",\n timestamp: getCurrentTimestamp(),\n content: action,\n tool: {\n name: toolName,\n action,\n target,\n },\n });\n\n const filePath = path.join(\n state.conversationPath,\n `${state.currentFileIndex}.txt`\n );\n await appendToFile(filePath, entry);\n }\n\n async getConversationFiles(sessionId: string): Promise<string[]> {\n const state = await this.getSessionState(sessionId);\n if (!state) {\n return [];\n }\n\n const files = await listDir(state.conversationPath);\n return files\n .filter((f) => f.endsWith(\".txt\"))\n .sort((a, b) => {\n const numA = parseInt(a.replace(\".txt\", \"\"), 10);\n const numB = parseInt(b.replace(\".txt\", \"\"), 10);\n return numA - numB;\n })\n .map((f) => path.join(state.conversationPath, f));\n }\n\n async getFullConversation(sessionId: string): Promise<string> {\n const files = await this.getConversationFiles(sessionId);\n const contents: string[] = [];\n\n for (const file of files) {\n const content = await readTextFile(file);\n if (content) {\n contents.push(content);\n }\n }\n\n return contents.join(\"\\n\\n---\\n\\n\");\n }\n\n async rotateConversation(sessionId: string): Promise<void> {\n const state = await this.getSessionState(sessionId);\n if (!state) {\n throw new Error(`No session found for: ${sessionId}`);\n }\n\n const newIndex = state.currentFileIndex + 1;\n const newFile = path.join(state.conversationPath, `${newIndex}.txt`);\n\n await writeTextFile(\n newFile,\n `# Conversation: ${sessionId} (continued)\\n# Compaction: ${newIndex}\\n# Time: ${getCurrentTimestamp()}\\n\\n`\n );\n\n await this.updateSessionState({\n ...state,\n currentFileIndex: newIndex,\n });\n }\n\n async getOrCreateSession(transcriptPath?: string): Promise<string> {\n // Try to find existing session by transcript path\n if (transcriptPath) {\n const sessionMap = await this.loadSessionMap();\n const existing = sessionMap[transcriptPath];\n if (existing) {\n return existing.sessionId;\n }\n }\n\n // Create new session\n const sessionId = generateSessionId();\n await this.createConversation(sessionId);\n\n // Map transcript path to session if provided\n if (transcriptPath) {\n const sessionMap = await this.loadSessionMap();\n const state = await this.getSessionState(sessionId);\n if (state) {\n sessionMap[transcriptPath] = state;\n await this.saveSessionMap(sessionMap);\n }\n }\n\n return sessionId;\n }\n\n async getActiveSessionId(): Promise<string | null> {\n const statePath = getStatePath(this.projectPath);\n const activeSessionFile = path.join(statePath, \"active-session.json\");\n\n const data = await readJSON<{ sessionId: string }>(activeSessionFile);\n return data?.sessionId || null;\n }\n\n async setActiveSession(sessionId: string): Promise<void> {\n const statePath = getStatePath(this.projectPath);\n const activeSessionFile = path.join(statePath, \"active-session.json\");\n\n await ensureDir(statePath);\n await writeJSON(activeSessionFile, { sessionId });\n }\n\n private async checkRotation(sessionId: string): Promise<void> {\n const config = await loadConfig(this.projectPath);\n const state = await this.getSessionState(sessionId);\n if (!state) return;\n\n const currentFile = path.join(\n state.conversationPath,\n `${state.currentFileIndex}.txt`\n );\n const size = await getFileSize(currentFile);\n\n const thresholdBytes = config.compaction.sizeThresholdKB * 1024;\n\n if (size >= thresholdBytes) {\n await this.rotateConversation(sessionId);\n }\n }\n\n private formatEntry(entry: ConversationEntry): string {\n let content = `[${entry.type}][${entry.timestamp}]`;\n\n if (entry.tool) {\n content += `[TOOL:${entry.tool.name}]`;\n }\n\n content += `\\n${entry.content}\\n---\\n\\n`;\n\n return content;\n }\n\n private async getSessionState(\n sessionId: string\n ): Promise<SessionState | null> {\n const sessionMap = await this.loadSessionMap();\n\n for (const state of Object.values(sessionMap)) {\n if (state.sessionId === sessionId) {\n return state;\n }\n }\n\n // Try to find by looking at conversations directory\n const conversationsPath = getConversationsPath(this.projectPath);\n const conversationPath = path.join(conversationsPath, sessionId);\n\n if (await fileExists(conversationPath)) {\n // Reconstruct state\n const files = await listDir(conversationPath);\n const txtFiles = files.filter((f) => f.endsWith(\".txt\"));\n const maxIndex = Math.max(\n ...txtFiles.map((f) => parseInt(f.replace(\".txt\", \"\"), 10))\n );\n\n return {\n sessionId,\n conversationPath,\n currentFileIndex: maxIndex >= 0 ? maxIndex : 0,\n startedAt: getCurrentTimestamp(),\n };\n }\n\n return null;\n }\n\n private async updateSessionState(state: SessionState): Promise<void> {\n const sessionMap = await this.loadSessionMap();\n\n // Find and update or add\n let found = false;\n for (const [key, existing] of Object.entries(sessionMap)) {\n if (existing.sessionId === state.sessionId) {\n sessionMap[key] = state;\n found = true;\n break;\n }\n }\n\n if (!found) {\n sessionMap[state.conversationPath] = state;\n }\n\n await this.saveSessionMap(sessionMap);\n }\n\n private async loadSessionMap(): Promise<SessionMap> {\n const statePath = getStatePath(this.projectPath);\n const mapPath = path.join(statePath, \"session-map.json\");\n\n return (await readJSON<SessionMap>(mapPath)) || {};\n }\n\n private async saveSessionMap(map: SessionMap): Promise<void> {\n const statePath = getStatePath(this.projectPath);\n const mapPath = path.join(statePath, \"session-map.json\");\n\n await ensureDir(statePath);\n await writeJSON(mapPath, map);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// src/utils/file-utils.ts
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
async function ensureDir(dirPath) {
|
|
5
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
6
|
+
}
|
|
7
|
+
async function fileExists(filePath) {
|
|
8
|
+
try {
|
|
9
|
+
await fs.access(filePath);
|
|
10
|
+
return true;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async function readJSON(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
18
|
+
return JSON.parse(content);
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function writeJSON(filePath, data, pretty = true) {
|
|
24
|
+
await ensureDir(path.dirname(filePath));
|
|
25
|
+
const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
26
|
+
await fs.writeFile(filePath, content);
|
|
27
|
+
}
|
|
28
|
+
async function appendToFile(filePath, content) {
|
|
29
|
+
await ensureDir(path.dirname(filePath));
|
|
30
|
+
await fs.appendFile(filePath, content);
|
|
31
|
+
}
|
|
32
|
+
async function readTextFile(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
return await fs.readFile(filePath, "utf-8");
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function writeTextFile(filePath, content) {
|
|
40
|
+
await ensureDir(path.dirname(filePath));
|
|
41
|
+
await fs.writeFile(filePath, content);
|
|
42
|
+
}
|
|
43
|
+
async function listDir(dirPath) {
|
|
44
|
+
try {
|
|
45
|
+
return await fs.readdir(dirPath);
|
|
46
|
+
} catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function getFileSize(filePath) {
|
|
51
|
+
try {
|
|
52
|
+
const stats = await fs.stat(filePath);
|
|
53
|
+
return stats.size;
|
|
54
|
+
} catch {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function generateSessionId() {
|
|
59
|
+
const timestamp = Date.now().toString(36);
|
|
60
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
61
|
+
return `${timestamp}-${random}`;
|
|
62
|
+
}
|
|
63
|
+
function getCurrentTimestamp() {
|
|
64
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/utils/config.ts
|
|
68
|
+
import * as fs2 from "fs/promises";
|
|
69
|
+
import * as path2 from "path";
|
|
70
|
+
var DEFAULT_CONFIG = {
|
|
71
|
+
compaction: {
|
|
72
|
+
trigger: "session_end",
|
|
73
|
+
sizeThresholdKB: 50,
|
|
74
|
+
messageThreshold: 100
|
|
75
|
+
},
|
|
76
|
+
capture: {
|
|
77
|
+
tools: ["Write", "Edit", "MultiEdit", "Bash", "Task"],
|
|
78
|
+
maxContentLength: 500
|
|
79
|
+
},
|
|
80
|
+
vault: {
|
|
81
|
+
autoIntegrate: true,
|
|
82
|
+
maxDepth: 5
|
|
83
|
+
},
|
|
84
|
+
feedback: {
|
|
85
|
+
enabled: true,
|
|
86
|
+
contextDepth: 2
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
async function loadConfig(projectPath) {
|
|
90
|
+
const configPath = path2.join(
|
|
91
|
+
projectPath,
|
|
92
|
+
"cc-knowledge-base",
|
|
93
|
+
".cckb-config.json"
|
|
94
|
+
);
|
|
95
|
+
try {
|
|
96
|
+
const content = await fs2.readFile(configPath, "utf-8");
|
|
97
|
+
const userConfig = JSON.parse(content);
|
|
98
|
+
return { ...DEFAULT_CONFIG, ...userConfig };
|
|
99
|
+
} catch {
|
|
100
|
+
return DEFAULT_CONFIG;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function getConversationsPath(projectPath) {
|
|
104
|
+
return path2.join(projectPath, "cc-knowledge-base", "conversations");
|
|
105
|
+
}
|
|
106
|
+
function getVaultPath(projectPath) {
|
|
107
|
+
return path2.join(projectPath, "cc-knowledge-base", "vault");
|
|
108
|
+
}
|
|
109
|
+
function getStatePath(projectPath) {
|
|
110
|
+
return path2.join(projectPath, "cc-knowledge-base", ".cckb-state");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
ensureDir,
|
|
115
|
+
fileExists,
|
|
116
|
+
readJSON,
|
|
117
|
+
writeJSON,
|
|
118
|
+
appendToFile,
|
|
119
|
+
readTextFile,
|
|
120
|
+
writeTextFile,
|
|
121
|
+
listDir,
|
|
122
|
+
getFileSize,
|
|
123
|
+
generateSessionId,
|
|
124
|
+
getCurrentTimestamp,
|
|
125
|
+
DEFAULT_CONFIG,
|
|
126
|
+
loadConfig,
|
|
127
|
+
getConversationsPath,
|
|
128
|
+
getVaultPath,
|
|
129
|
+
getStatePath
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=chunk-TFFLX3YY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/file-utils.ts","../src/utils/config.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await fs.mkdir(dirPath, { recursive: true });\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readJSON<T>(filePath: string): Promise<T | null> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nexport async function writeJSON(\n filePath: string,\n data: unknown,\n pretty = true\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);\n await fs.writeFile(filePath, content);\n}\n\nexport async function appendToFile(\n filePath: string,\n content: string\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await fs.appendFile(filePath, content);\n}\n\nexport async function readTextFile(filePath: string): Promise<string | null> {\n try {\n return await fs.readFile(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport async function writeTextFile(\n filePath: string,\n content: string\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await fs.writeFile(filePath, content);\n}\n\nexport async function copyFile(src: string, dest: string): Promise<void> {\n await ensureDir(path.dirname(dest));\n await fs.copyFile(src, dest);\n}\n\nexport async function listDir(dirPath: string): Promise<string[]> {\n try {\n return await fs.readdir(dirPath);\n } catch {\n return [];\n }\n}\n\nexport async function getFileSize(filePath: string): Promise<number> {\n try {\n const stats = await fs.stat(filePath);\n return stats.size;\n } catch {\n return 0;\n }\n}\n\nexport function generateSessionId(): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${timestamp}-${random}`;\n}\n\nexport function getCurrentTimestamp(): string {\n return new Date().toISOString();\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport interface CCKBConfig {\n compaction: {\n trigger: \"session_end\" | \"size\" | \"messages\" | \"manual\";\n sizeThresholdKB: number;\n messageThreshold: number;\n };\n capture: {\n tools: string[];\n maxContentLength: number;\n };\n vault: {\n autoIntegrate: boolean;\n maxDepth: number;\n };\n feedback: {\n enabled: boolean;\n contextDepth: number;\n };\n}\n\nexport const DEFAULT_CONFIG: CCKBConfig = {\n compaction: {\n trigger: \"session_end\",\n sizeThresholdKB: 50,\n messageThreshold: 100,\n },\n capture: {\n tools: [\"Write\", \"Edit\", \"MultiEdit\", \"Bash\", \"Task\"],\n maxContentLength: 500,\n },\n vault: {\n autoIntegrate: true,\n maxDepth: 5,\n },\n feedback: {\n enabled: true,\n contextDepth: 2,\n },\n};\n\nexport async function loadConfig(projectPath: string): Promise<CCKBConfig> {\n const configPath = path.join(\n projectPath,\n \"cc-knowledge-base\",\n \".cckb-config.json\"\n );\n\n try {\n const content = await fs.readFile(configPath, \"utf-8\");\n const userConfig = JSON.parse(content);\n return { ...DEFAULT_CONFIG, ...userConfig };\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n\nexport async function saveConfig(\n projectPath: string,\n config: CCKBConfig\n): Promise<void> {\n const configPath = path.join(\n projectPath,\n \"cc-knowledge-base\",\n \".cckb-config.json\"\n );\n\n await fs.writeFile(configPath, JSON.stringify(config, null, 2));\n}\n\nexport function getKnowledgeBasePath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\");\n}\n\nexport function getConversationsPath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \"conversations\");\n}\n\nexport function getVaultPath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \"vault\");\n}\n\nexport function getStatePath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \".cckb-state\");\n}\n"],"mappings":";AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEtB,eAAsB,UAAU,SAAgC;AAC9D,QAAS,SAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAC7C;AAEA,eAAsB,WAAW,UAAoC;AACnE,MAAI;AACF,UAAS,UAAO,QAAQ;AACxB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,SAAY,UAAqC;AACrE,MAAI;AACF,UAAM,UAAU,MAAS,YAAS,UAAU,OAAO;AACnD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,UACpB,UACA,MACA,SAAS,MACM;AACf,QAAM,UAAe,aAAQ,QAAQ,CAAC;AACtC,QAAM,UAAU,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI;AAC5E,QAAS,aAAU,UAAU,OAAO;AACtC;AAEA,eAAsB,aACpB,UACA,SACe;AACf,QAAM,UAAe,aAAQ,QAAQ,CAAC;AACtC,QAAS,cAAW,UAAU,OAAO;AACvC;AAEA,eAAsB,aAAa,UAA0C;AAC3E,MAAI;AACF,WAAO,MAAS,YAAS,UAAU,OAAO;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,cACpB,UACA,SACe;AACf,QAAM,UAAe,aAAQ,QAAQ,CAAC;AACtC,QAAS,aAAU,UAAU,OAAO;AACtC;AAOA,eAAsB,QAAQ,SAAoC;AAChE,MAAI;AACF,WAAO,MAAS,WAAQ,OAAO;AAAA,EACjC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,YAAY,UAAmC;AACnE,MAAI;AACF,UAAM,QAAQ,MAAS,QAAK,QAAQ;AACpC,WAAO,MAAM;AAAA,EACf,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAA4B;AAC1C,QAAM,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE;AACxC,QAAM,SAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC;AACxD,SAAO,GAAG,SAAS,IAAI,MAAM;AAC/B;AAEO,SAAS,sBAA8B;AAC5C,UAAO,oBAAI,KAAK,GAAE,YAAY;AAChC;;;ACzFA,YAAYA,SAAQ;AACpB,YAAYC,WAAU;AAsBf,IAAM,iBAA6B;AAAA,EACxC,YAAY;AAAA,IACV,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,EACpB;AAAA,EACA,SAAS;AAAA,IACP,OAAO,CAAC,SAAS,QAAQ,aAAa,QAAQ,MAAM;AAAA,IACpD,kBAAkB;AAAA,EACpB;AAAA,EACA,OAAO;AAAA,IACL,eAAe;AAAA,IACf,UAAU;AAAA,EACZ;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AACF;AAEA,eAAsB,WAAW,aAA0C;AACzE,QAAM,aAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,MAAS,aAAS,YAAY,OAAO;AACrD,UAAM,aAAa,KAAK,MAAM,OAAO;AACrC,WAAO,EAAE,GAAG,gBAAgB,GAAG,WAAW;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAmBO,SAAS,qBAAqB,aAA6B;AAChE,SAAY,WAAK,aAAa,qBAAqB,eAAe;AACpE;AAEO,SAAS,aAAa,aAA6B;AACxD,SAAY,WAAK,aAAa,qBAAqB,OAAO;AAC5D;AAEO,SAAS,aAAa,aAA6B;AACxD,SAAY,WAAK,aAAa,qBAAqB,aAAa;AAClE;","names":["fs","path"]}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureDir,
|
|
3
|
+
fileExists,
|
|
4
|
+
listDir,
|
|
5
|
+
readTextFile,
|
|
6
|
+
writeTextFile
|
|
7
|
+
} from "./chunk-TFFLX3YY.js";
|
|
8
|
+
|
|
9
|
+
// src/core/index-manager.ts
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
var IndexManager = class {
|
|
12
|
+
vaultPath;
|
|
13
|
+
constructor(vaultPath) {
|
|
14
|
+
this.vaultPath = vaultPath;
|
|
15
|
+
}
|
|
16
|
+
async getVaultOverview() {
|
|
17
|
+
const indexPath = path.join(this.vaultPath, "INDEX.md");
|
|
18
|
+
const content = await readTextFile(indexPath);
|
|
19
|
+
if (!content) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const tableMatch = content.match(/## Contents\n\n\|[\s\S]*?\|\n(?:\|[\s\S]*?\|\n)*/);
|
|
23
|
+
if (tableMatch) {
|
|
24
|
+
return `Vault contents: ${this.parseTableToOverview(tableMatch[0])}`;
|
|
25
|
+
}
|
|
26
|
+
return "Vault available at cc-knowledge-base/vault/";
|
|
27
|
+
}
|
|
28
|
+
async listEntities() {
|
|
29
|
+
const entitiesPath = path.join(this.vaultPath, "entities");
|
|
30
|
+
if (!await fileExists(entitiesPath)) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const items = await listDir(entitiesPath);
|
|
34
|
+
return items.filter((item) => !item.endsWith(".md"));
|
|
35
|
+
}
|
|
36
|
+
async readIndex(relativePath = "") {
|
|
37
|
+
const indexPath = path.join(this.vaultPath, relativePath, "INDEX.md");
|
|
38
|
+
const content = await readTextFile(indexPath);
|
|
39
|
+
if (!content) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return this.parseIndex(content);
|
|
43
|
+
}
|
|
44
|
+
async updateIndex(relativePath, entries) {
|
|
45
|
+
const indexPath = path.join(this.vaultPath, relativePath, "INDEX.md");
|
|
46
|
+
const existingContent = await readTextFile(indexPath);
|
|
47
|
+
if (!existingContent) {
|
|
48
|
+
await this.createIndex(relativePath, entries);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const existingEntries = this.parseIndex(existingContent);
|
|
52
|
+
const mergedMap = /* @__PURE__ */ new Map();
|
|
53
|
+
for (const entry of existingEntries) {
|
|
54
|
+
mergedMap.set(entry.name, entry);
|
|
55
|
+
}
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
mergedMap.set(entry.name, entry);
|
|
58
|
+
}
|
|
59
|
+
const mergedEntries = Array.from(mergedMap.values());
|
|
60
|
+
const newTable = this.buildTable(mergedEntries);
|
|
61
|
+
const updatedContent = existingContent.replace(
|
|
62
|
+
/## (?:Contents|Entity List)\n\n\|[\s\S]*?(?=\n\n|$)/,
|
|
63
|
+
`## Contents
|
|
64
|
+
|
|
65
|
+
${newTable}`
|
|
66
|
+
);
|
|
67
|
+
await writeTextFile(indexPath, updatedContent);
|
|
68
|
+
}
|
|
69
|
+
async createIndex(relativePath, entries, title) {
|
|
70
|
+
const indexPath = path.join(this.vaultPath, relativePath, "INDEX.md");
|
|
71
|
+
const folderName = path.basename(relativePath) || "Vault";
|
|
72
|
+
const displayTitle = title || folderName.charAt(0).toUpperCase() + folderName.slice(1);
|
|
73
|
+
const content = `# ${displayTitle}
|
|
74
|
+
|
|
75
|
+
## Contents
|
|
76
|
+
|
|
77
|
+
${this.buildTable(entries)}
|
|
78
|
+
|
|
79
|
+
_Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}_
|
|
80
|
+
`;
|
|
81
|
+
await ensureDir(path.dirname(indexPath));
|
|
82
|
+
await writeTextFile(indexPath, content);
|
|
83
|
+
}
|
|
84
|
+
async addEntry(relativePath, entry) {
|
|
85
|
+
await this.updateIndex(relativePath, [entry]);
|
|
86
|
+
}
|
|
87
|
+
async ensureEntityFolder(entityName) {
|
|
88
|
+
const entityPath = path.join("entities", entityName);
|
|
89
|
+
const fullPath = path.join(this.vaultPath, entityPath);
|
|
90
|
+
await ensureDir(fullPath);
|
|
91
|
+
const indexPath = path.join(fullPath, "INDEX.md");
|
|
92
|
+
if (!await fileExists(indexPath)) {
|
|
93
|
+
await this.createIndex(entityPath, [], entityName);
|
|
94
|
+
}
|
|
95
|
+
await this.addEntry("entities", {
|
|
96
|
+
name: entityName,
|
|
97
|
+
path: `./${entityName}/INDEX.md`,
|
|
98
|
+
description: `Documentation for ${entityName} entity`,
|
|
99
|
+
type: "folder"
|
|
100
|
+
});
|
|
101
|
+
return entityPath;
|
|
102
|
+
}
|
|
103
|
+
parseIndex(content) {
|
|
104
|
+
const entries = [];
|
|
105
|
+
const tableMatch = content.match(/\|[\s\S]*?\|/g);
|
|
106
|
+
if (!tableMatch) {
|
|
107
|
+
return entries;
|
|
108
|
+
}
|
|
109
|
+
const dataRows = tableMatch.slice(2);
|
|
110
|
+
for (const row of dataRows) {
|
|
111
|
+
const cells = row.split("|").filter((c) => c.trim()).map((c) => c.trim());
|
|
112
|
+
if (cells.length >= 2) {
|
|
113
|
+
const linkMatch = cells[0].match(/\[([^\]]+)\]\(([^)]+)\)/);
|
|
114
|
+
const name = linkMatch ? linkMatch[1] : cells[0];
|
|
115
|
+
const linkPath = linkMatch ? linkMatch[2] : "";
|
|
116
|
+
const type = linkPath.includes("/") ? "folder" : "file";
|
|
117
|
+
const description = cells[cells.length - 1] || "";
|
|
118
|
+
entries.push({
|
|
119
|
+
name,
|
|
120
|
+
path: linkPath,
|
|
121
|
+
description,
|
|
122
|
+
type
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return entries;
|
|
127
|
+
}
|
|
128
|
+
buildTable(entries) {
|
|
129
|
+
if (entries.length === 0) {
|
|
130
|
+
return "| Item | Description |\n|------|-------------|\n\n_No entries yet._";
|
|
131
|
+
}
|
|
132
|
+
let table = "| Item | Description |\n|------|-------------|\n";
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
const link = `[${entry.name}](${entry.path})`;
|
|
135
|
+
table += `| ${link} | ${entry.description} |
|
|
136
|
+
`;
|
|
137
|
+
}
|
|
138
|
+
return table;
|
|
139
|
+
}
|
|
140
|
+
parseTableToOverview(table) {
|
|
141
|
+
const entries = this.parseIndex(table);
|
|
142
|
+
return entries.map((e) => e.name).join(", ");
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export {
|
|
147
|
+
IndexManager
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=chunk-XAY6TTXB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/index-manager.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport {\n readTextFile,\n writeTextFile,\n listDir,\n fileExists,\n ensureDir,\n} from \"../utils/file-utils.js\";\n\nexport interface IndexEntry {\n name: string;\n path: string;\n description: string;\n type: \"file\" | \"folder\";\n}\n\nexport class IndexManager {\n private vaultPath: string;\n\n constructor(vaultPath: string) {\n this.vaultPath = vaultPath;\n }\n\n async getVaultOverview(): Promise<string | null> {\n const indexPath = path.join(this.vaultPath, \"INDEX.md\");\n const content = await readTextFile(indexPath);\n\n if (!content) {\n return null;\n }\n\n // Extract the Contents table\n const tableMatch = content.match(/## Contents\\n\\n\\|[\\s\\S]*?\\|\\n(?:\\|[\\s\\S]*?\\|\\n)*/);\n if (tableMatch) {\n return `Vault contents: ${this.parseTableToOverview(tableMatch[0])}`;\n }\n\n return \"Vault available at cc-knowledge-base/vault/\";\n }\n\n async listEntities(): Promise<string[]> {\n const entitiesPath = path.join(this.vaultPath, \"entities\");\n\n if (!(await fileExists(entitiesPath))) {\n return [];\n }\n\n const items = await listDir(entitiesPath);\n return items.filter((item) => !item.endsWith(\".md\"));\n }\n\n async readIndex(relativePath: string = \"\"): Promise<IndexEntry[]> {\n const indexPath = path.join(this.vaultPath, relativePath, \"INDEX.md\");\n const content = await readTextFile(indexPath);\n\n if (!content) {\n return [];\n }\n\n return this.parseIndex(content);\n }\n\n async updateIndex(\n relativePath: string,\n entries: IndexEntry[]\n ): Promise<void> {\n const indexPath = path.join(this.vaultPath, relativePath, \"INDEX.md\");\n const existingContent = await readTextFile(indexPath);\n\n if (!existingContent) {\n await this.createIndex(relativePath, entries);\n return;\n }\n\n // Parse existing entries\n const existingEntries = this.parseIndex(existingContent);\n\n // Merge entries (new entries override existing with same name)\n const mergedMap = new Map<string, IndexEntry>();\n for (const entry of existingEntries) {\n mergedMap.set(entry.name, entry);\n }\n for (const entry of entries) {\n mergedMap.set(entry.name, entry);\n }\n\n const mergedEntries = Array.from(mergedMap.values());\n\n // Rebuild the table\n const newTable = this.buildTable(mergedEntries);\n\n // Replace the table in the content\n const updatedContent = existingContent.replace(\n /## (?:Contents|Entity List)\\n\\n\\|[\\s\\S]*?(?=\\n\\n|$)/,\n `## Contents\\n\\n${newTable}`\n );\n\n await writeTextFile(indexPath, updatedContent);\n }\n\n async createIndex(\n relativePath: string,\n entries: IndexEntry[],\n title?: string\n ): Promise<void> {\n const indexPath = path.join(this.vaultPath, relativePath, \"INDEX.md\");\n const folderName = path.basename(relativePath) || \"Vault\";\n const displayTitle = title || folderName.charAt(0).toUpperCase() + folderName.slice(1);\n\n const content = `# ${displayTitle}\n\n## Contents\n\n${this.buildTable(entries)}\n\n_Last updated: ${new Date().toISOString()}_\n`;\n\n await ensureDir(path.dirname(indexPath));\n await writeTextFile(indexPath, content);\n }\n\n async addEntry(\n relativePath: string,\n entry: IndexEntry\n ): Promise<void> {\n await this.updateIndex(relativePath, [entry]);\n }\n\n async ensureEntityFolder(entityName: string): Promise<string> {\n const entityPath = path.join(\"entities\", entityName);\n const fullPath = path.join(this.vaultPath, entityPath);\n\n await ensureDir(fullPath);\n\n // Create entity INDEX.md if it doesn't exist\n const indexPath = path.join(fullPath, \"INDEX.md\");\n if (!(await fileExists(indexPath))) {\n await this.createIndex(entityPath, [], entityName);\n }\n\n // Ensure entity is in entities/INDEX.md\n await this.addEntry(\"entities\", {\n name: entityName,\n path: `./${entityName}/INDEX.md`,\n description: `Documentation for ${entityName} entity`,\n type: \"folder\",\n });\n\n return entityPath;\n }\n\n private parseIndex(content: string): IndexEntry[] {\n const entries: IndexEntry[] = [];\n\n // Find table rows (skip header rows)\n const tableMatch = content.match(/\\|[\\s\\S]*?\\|/g);\n if (!tableMatch) {\n return entries;\n }\n\n // Skip header and separator rows\n const dataRows = tableMatch.slice(2);\n\n for (const row of dataRows) {\n const cells = row\n .split(\"|\")\n .filter((c) => c.trim())\n .map((c) => c.trim());\n\n if (cells.length >= 2) {\n // Parse name and link\n const linkMatch = cells[0].match(/\\[([^\\]]+)\\]\\(([^)]+)\\)/);\n const name = linkMatch ? linkMatch[1] : cells[0];\n const linkPath = linkMatch ? linkMatch[2] : \"\";\n const type = linkPath.includes(\"/\") ? \"folder\" : \"file\";\n const description = cells[cells.length - 1] || \"\";\n\n entries.push({\n name,\n path: linkPath,\n description,\n type: type as \"file\" | \"folder\",\n });\n }\n }\n\n return entries;\n }\n\n private buildTable(entries: IndexEntry[]): string {\n if (entries.length === 0) {\n return \"| Item | Description |\\n|------|-------------|\\n\\n_No entries yet._\";\n }\n\n let table = \"| Item | Description |\\n|------|-------------|\\n\";\n\n for (const entry of entries) {\n const link = `[${entry.name}](${entry.path})`;\n table += `| ${link} | ${entry.description} |\\n`;\n }\n\n return table;\n }\n\n private parseTableToOverview(table: string): string {\n const entries = this.parseIndex(table);\n return entries.map((e) => e.name).join(\", \");\n }\n}\n"],"mappings":"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","names":[]}
|