@synth-coder/memhub 0.2.3 → 0.2.4
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/.eslintrc.cjs +45 -45
- package/.factory/commands/opsx-apply.md +150 -150
- package/.factory/commands/opsx-archive.md +155 -155
- package/.factory/commands/opsx-explore.md +171 -171
- package/.factory/commands/opsx-propose.md +104 -104
- package/.factory/skills/openspec-apply-change/SKILL.md +156 -156
- package/.factory/skills/openspec-archive-change/SKILL.md +114 -114
- package/.factory/skills/openspec-explore/SKILL.md +288 -288
- package/.factory/skills/openspec-propose/SKILL.md +110 -110
- package/.github/workflows/ci.yml +110 -74
- package/.github/workflows/release.yml +67 -0
- package/.iflow/commands/opsx-apply.md +152 -152
- package/.iflow/commands/opsx-archive.md +157 -157
- package/.iflow/commands/opsx-explore.md +173 -173
- package/.iflow/commands/opsx-propose.md +106 -106
- package/.iflow/skills/openspec-apply-change/SKILL.md +156 -156
- package/.iflow/skills/openspec-archive-change/SKILL.md +114 -114
- package/.iflow/skills/openspec-explore/SKILL.md +288 -288
- package/.iflow/skills/openspec-propose/SKILL.md +110 -110
- package/.prettierrc +11 -11
- package/AGENTS.md +167 -169
- package/README.md +276 -195
- package/README.zh-CN.md +245 -193
- package/dist/src/cli/agents/claude-code.d.ts +5 -0
- package/dist/src/cli/agents/claude-code.d.ts.map +1 -0
- package/dist/src/cli/agents/claude-code.js +14 -0
- package/dist/src/cli/agents/claude-code.js.map +1 -0
- package/dist/src/cli/agents/cline.d.ts +5 -0
- package/dist/src/cli/agents/cline.d.ts.map +1 -0
- package/dist/src/cli/agents/cline.js +14 -0
- package/dist/src/cli/agents/cline.js.map +1 -0
- package/dist/src/cli/agents/codex.d.ts +5 -0
- package/dist/src/cli/agents/codex.d.ts.map +1 -0
- package/dist/src/cli/agents/codex.js +14 -0
- package/dist/src/cli/agents/codex.js.map +1 -0
- package/dist/src/cli/agents/cursor.d.ts +5 -0
- package/dist/src/cli/agents/cursor.d.ts.map +1 -0
- package/dist/src/cli/agents/cursor.js +14 -0
- package/dist/src/cli/agents/cursor.js.map +1 -0
- package/dist/src/cli/agents/factory-droid.d.ts +5 -0
- package/dist/src/cli/agents/factory-droid.d.ts.map +1 -0
- package/dist/src/cli/agents/factory-droid.js +14 -0
- package/dist/src/cli/agents/factory-droid.js.map +1 -0
- package/dist/src/cli/agents/gemini-cli.d.ts +5 -0
- package/dist/src/cli/agents/gemini-cli.d.ts.map +1 -0
- package/dist/src/cli/agents/gemini-cli.js +14 -0
- package/dist/src/cli/agents/gemini-cli.js.map +1 -0
- package/dist/src/cli/agents/index.d.ts +14 -0
- package/dist/src/cli/agents/index.d.ts.map +1 -0
- package/dist/src/cli/agents/index.js +30 -0
- package/dist/src/cli/agents/index.js.map +1 -0
- package/dist/src/cli/agents/windsurf.d.ts +5 -0
- package/dist/src/cli/agents/windsurf.d.ts.map +1 -0
- package/dist/src/cli/agents/windsurf.js +14 -0
- package/dist/src/cli/agents/windsurf.js.map +1 -0
- package/dist/src/cli/index.d.ts +8 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +168 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/init.d.ts +34 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +160 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +29 -0
- package/dist/src/cli/instructions.d.ts.map +1 -0
- package/dist/src/cli/instructions.js +141 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/types.d.ts +22 -0
- package/dist/src/cli/types.d.ts.map +1 -0
- package/dist/src/cli/types.js +86 -0
- package/dist/src/cli/types.js.map +1 -0
- package/dist/src/contracts/mcp.js +34 -34
- package/dist/src/contracts/schemas.js.map +1 -1
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/mcp-server.js +7 -14
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/services/embedding-service.d.ts.map +1 -1
- package/dist/src/services/embedding-service.js +1 -1
- package/dist/src/services/embedding-service.js.map +1 -1
- package/dist/src/services/memory-service.d.ts.map +1 -1
- package/dist/src/services/memory-service.js.map +1 -1
- package/dist/src/storage/markdown-storage.d.ts.map +1 -1
- package/dist/src/storage/markdown-storage.js +1 -1
- package/dist/src/storage/markdown-storage.js.map +1 -1
- package/dist/src/storage/vector-index.d.ts.map +1 -1
- package/dist/src/storage/vector-index.js +4 -5
- package/dist/src/storage/vector-index.js.map +1 -1
- package/docs/README.md +21 -0
- package/docs/mcp-tools.md +136 -0
- package/docs/user-guide.md +182 -0
- package/package.json +61 -59
- package/src/cli/agents/claude-code.ts +14 -0
- package/src/cli/agents/cline.ts +14 -0
- package/src/cli/agents/codex.ts +14 -0
- package/src/cli/agents/cursor.ts +14 -0
- package/src/cli/agents/factory-droid.ts +14 -0
- package/src/cli/agents/gemini-cli.ts +14 -0
- package/src/cli/agents/index.ts +36 -0
- package/src/cli/agents/windsurf.ts +14 -0
- package/src/cli/index.ts +192 -0
- package/src/cli/init.ts +218 -0
- package/src/cli/instructions.ts +156 -0
- package/src/cli/types.ts +112 -0
- package/src/contracts/index.ts +12 -12
- package/src/contracts/mcp.ts +223 -223
- package/src/contracts/schemas.ts +307 -307
- package/src/contracts/types.ts +410 -410
- package/src/index.ts +8 -8
- package/src/server/index.ts +5 -5
- package/src/server/mcp-server.ts +169 -186
- package/src/services/embedding-service.ts +114 -114
- package/src/services/index.ts +5 -5
- package/src/services/memory-service.ts +656 -663
- package/src/storage/frontmatter-parser.ts +243 -243
- package/src/storage/index.ts +6 -6
- package/src/storage/markdown-storage.ts +228 -236
- package/src/storage/vector-index.ts +159 -160
- package/src/utils/index.ts +5 -5
- package/src/utils/slugify.ts +63 -63
- package/test/cli/init.test.ts +380 -0
- package/test/contracts/schemas.test.ts +313 -313
- package/test/contracts/types.test.ts +21 -21
- package/test/frontmatter-parser-more.test.ts +94 -94
- package/test/server/mcp-server.test.ts +211 -210
- package/test/services/memory-service-edge.test.ts +248 -248
- package/test/services/memory-service.test.ts +291 -279
- package/test/storage/frontmatter-parser.test.ts +223 -223
- package/test/storage/markdown-storage.test.ts +226 -217
- package/test/storage/storage-edge.test.ts +238 -238
- package/test/storage/vector-index.test.ts +149 -153
- package/test/utils/slugify-edge.test.ts +94 -94
- package/test/utils/slugify.test.ts +72 -68
- package/tsconfig.json +25 -25
- package/tsconfig.test.json +8 -8
- package/vitest.config.ts +29 -29
- package/docs/architecture-diagrams.md +0 -368
- package/docs/architecture.md +0 -381
- package/docs/contracts.md +0 -190
- package/docs/prompt-template.md +0 -33
- package/docs/proposals/mcp-typescript-sdk-refactor.md +0 -568
- package/docs/proposals/proposal-close-gates.md +0 -58
- package/docs/tool-calling-policy.md +0 -101
- package/docs/vector-search.md +0 -306
|
@@ -1,236 +1,228 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Markdown Storage - Handles file system operations for memory storage
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
*/
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
} else if (entry.isFile() && extname(entry.name) === '.md') {
|
|
230
|
-
files.push(entryPath);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return files;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Storage - Handles file system operations for memory storage
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readFile, writeFile, unlink, readdir, stat, access, mkdir } from 'fs/promises';
|
|
6
|
+
import { join, extname } from 'path';
|
|
7
|
+
import { constants } from 'fs';
|
|
8
|
+
import type { Memory, MemoryFile } from '../contracts/types.js';
|
|
9
|
+
import {
|
|
10
|
+
parseFrontMatter,
|
|
11
|
+
stringifyFrontMatter,
|
|
12
|
+
memoryToFrontMatter,
|
|
13
|
+
frontMatterToMemory,
|
|
14
|
+
FrontMatterError,
|
|
15
|
+
} from './frontmatter-parser.js';
|
|
16
|
+
import { slugify } from '../utils/slugify.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Custom error for storage operations
|
|
20
|
+
*/
|
|
21
|
+
export class StorageError extends Error {
|
|
22
|
+
constructor(
|
|
23
|
+
message: string,
|
|
24
|
+
public readonly cause?: unknown
|
|
25
|
+
) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'StorageError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Storage configuration
|
|
33
|
+
*/
|
|
34
|
+
export interface StorageConfig {
|
|
35
|
+
/** Root directory for memory storage */
|
|
36
|
+
storagePath: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Markdown file storage implementation
|
|
41
|
+
*/
|
|
42
|
+
export class MarkdownStorage {
|
|
43
|
+
private readonly storagePath: string;
|
|
44
|
+
|
|
45
|
+
constructor(config: StorageConfig) {
|
|
46
|
+
this.storagePath = config.storagePath;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Ensures the storage directory exists
|
|
51
|
+
*/
|
|
52
|
+
async initialize(): Promise<void> {
|
|
53
|
+
try {
|
|
54
|
+
await access(this.storagePath, constants.F_OK);
|
|
55
|
+
} catch {
|
|
56
|
+
// Directory doesn't exist, create it
|
|
57
|
+
await mkdir(this.storagePath, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Writes a memory to a markdown file
|
|
63
|
+
*
|
|
64
|
+
* @param memory - The memory to write
|
|
65
|
+
* @returns The file path where the memory was stored
|
|
66
|
+
* @throws StorageError if write fails
|
|
67
|
+
*/
|
|
68
|
+
async write(memory: Memory): Promise<string> {
|
|
69
|
+
await this.initialize();
|
|
70
|
+
|
|
71
|
+
const { directoryPath, filename } = this.generatePathParts(memory);
|
|
72
|
+
const filePath = join(directoryPath, filename);
|
|
73
|
+
const frontMatter = memoryToFrontMatter(memory);
|
|
74
|
+
const content = stringifyFrontMatter(frontMatter, memory.title, memory.content);
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
await mkdir(directoryPath, { recursive: true });
|
|
78
|
+
await writeFile(filePath, content, 'utf-8');
|
|
79
|
+
return filePath;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new StorageError(`Failed to write memory file: ${filename}`, error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Reads a memory by its ID
|
|
87
|
+
*
|
|
88
|
+
* @param id - The memory ID
|
|
89
|
+
* @returns The memory object
|
|
90
|
+
* @throws StorageError if memory not found or read fails
|
|
91
|
+
*/
|
|
92
|
+
async read(id: string): Promise<Memory> {
|
|
93
|
+
const filePath = await this.findById(id);
|
|
94
|
+
|
|
95
|
+
if (!filePath) {
|
|
96
|
+
throw new StorageError(`Memory not found: ${id}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const content = await readFile(filePath, 'utf-8');
|
|
101
|
+
const { frontMatter, title, content: bodyContent } = parseFrontMatter(content);
|
|
102
|
+
return frontMatterToMemory(frontMatter, title, bodyContent);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (error instanceof FrontMatterError) {
|
|
105
|
+
throw new StorageError(`Invalid memory file format: ${filePath}`, error);
|
|
106
|
+
}
|
|
107
|
+
throw new StorageError(`Failed to read memory file: ${filePath}`, error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Deletes a memory by its ID
|
|
113
|
+
*
|
|
114
|
+
* @param id - The memory ID
|
|
115
|
+
* @returns The file path of the deleted memory
|
|
116
|
+
* @throws StorageError if memory not found or delete fails
|
|
117
|
+
*/
|
|
118
|
+
async delete(id: string): Promise<string> {
|
|
119
|
+
const filePath = await this.findById(id);
|
|
120
|
+
|
|
121
|
+
if (!filePath) {
|
|
122
|
+
throw new StorageError(`Memory not found: ${id}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
await unlink(filePath);
|
|
127
|
+
return filePath;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
throw new StorageError(`Failed to delete memory file: ${filePath}`, error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Lists all memory files
|
|
135
|
+
*
|
|
136
|
+
* @returns Array of memory file information
|
|
137
|
+
* @throws StorageError if listing fails
|
|
138
|
+
*/
|
|
139
|
+
async list(): Promise<MemoryFile[]> {
|
|
140
|
+
await this.initialize();
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const markdownPaths = await this.collectMarkdownFiles(this.storagePath);
|
|
144
|
+
const files: MemoryFile[] = [];
|
|
145
|
+
|
|
146
|
+
for (const filePath of markdownPaths) {
|
|
147
|
+
const stats = await stat(filePath);
|
|
148
|
+
const content = await readFile(filePath, 'utf-8');
|
|
149
|
+
|
|
150
|
+
files.push({
|
|
151
|
+
path: filePath,
|
|
152
|
+
filename: filePath.split(/[/\\]/).pop() ?? filePath,
|
|
153
|
+
content,
|
|
154
|
+
modifiedAt: stats.mtime.toISOString(),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return files;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw new StorageError('Failed to list memory files', error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Finds a memory file by ID
|
|
166
|
+
*
|
|
167
|
+
* @param id - The memory ID to find
|
|
168
|
+
* @returns The file path or null if not found
|
|
169
|
+
* @throws StorageError if search fails
|
|
170
|
+
*/
|
|
171
|
+
async findById(id: string): Promise<string | null> {
|
|
172
|
+
await this.initialize();
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const markdownPaths = await this.collectMarkdownFiles(this.storagePath);
|
|
176
|
+
|
|
177
|
+
for (const filePath of markdownPaths) {
|
|
178
|
+
try {
|
|
179
|
+
const content = await readFile(filePath, 'utf-8');
|
|
180
|
+
const { frontMatter } = parseFrontMatter(content);
|
|
181
|
+
if (frontMatter.id === id) {
|
|
182
|
+
return filePath;
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
// Skip files that can't be parsed
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return null;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
throw new StorageError('Failed to search for memory file', error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Generates nested path parts for a memory
|
|
198
|
+
*
|
|
199
|
+
* Format: {storage}/{YYYY-MM-DD}/{session_uuid}/{timestamp}-{slug}.md
|
|
200
|
+
*/
|
|
201
|
+
private generatePathParts(memory: Memory): { directoryPath: string; filename: string } {
|
|
202
|
+
const date = memory.createdAt.split('T')[0];
|
|
203
|
+
const sessionId = memory.sessionId ?? 'default-session';
|
|
204
|
+
const titleSlug = slugify(memory.title) || 'untitled';
|
|
205
|
+
const timestamp = memory.createdAt.replace(/[:.]/g, '-');
|
|
206
|
+
const filename = `${timestamp}-${titleSlug}.md`;
|
|
207
|
+
const directoryPath = join(this.storagePath, date, sessionId);
|
|
208
|
+
|
|
209
|
+
return { directoryPath, filename };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private async collectMarkdownFiles(rootDir: string): Promise<string[]> {
|
|
213
|
+
const entries = await readdir(rootDir, { withFileTypes: true });
|
|
214
|
+
const files: string[] = [];
|
|
215
|
+
|
|
216
|
+
for (const entry of entries) {
|
|
217
|
+
const entryPath = join(rootDir, entry.name);
|
|
218
|
+
if (entry.isDirectory()) {
|
|
219
|
+
const nested = await this.collectMarkdownFiles(entryPath);
|
|
220
|
+
files.push(...nested);
|
|
221
|
+
} else if (entry.isFile() && extname(entry.name) === '.md') {
|
|
222
|
+
files.push(entryPath);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return files;
|
|
227
|
+
}
|
|
228
|
+
}
|