mcp-taskflow 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.
Files changed (187) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +275 -0
  4. package/dist/config/pathResolver.d.ts +33 -0
  5. package/dist/config/pathResolver.d.ts.map +1 -0
  6. package/dist/config/pathResolver.js +130 -0
  7. package/dist/config/pathResolver.js.map +1 -0
  8. package/dist/data/fileOperations.d.ts +43 -0
  9. package/dist/data/fileOperations.d.ts.map +1 -0
  10. package/dist/data/fileOperations.js +248 -0
  11. package/dist/data/fileOperations.js.map +1 -0
  12. package/dist/data/memoryStore.d.ts +86 -0
  13. package/dist/data/memoryStore.d.ts.map +1 -0
  14. package/dist/data/memoryStore.js +216 -0
  15. package/dist/data/memoryStore.js.map +1 -0
  16. package/dist/data/rulesStore.d.ts +63 -0
  17. package/dist/data/rulesStore.d.ts.map +1 -0
  18. package/dist/data/rulesStore.js +196 -0
  19. package/dist/data/rulesStore.js.map +1 -0
  20. package/dist/data/schemas.d.ts +840 -0
  21. package/dist/data/schemas.d.ts.map +1 -0
  22. package/dist/data/schemas.js +265 -0
  23. package/dist/data/schemas.js.map +1 -0
  24. package/dist/data/taskSearchService.d.ts +110 -0
  25. package/dist/data/taskSearchService.d.ts.map +1 -0
  26. package/dist/data/taskSearchService.js +165 -0
  27. package/dist/data/taskSearchService.js.map +1 -0
  28. package/dist/data/taskStore.d.ts +192 -0
  29. package/dist/data/taskStore.d.ts.map +1 -0
  30. package/dist/data/taskStore.js +347 -0
  31. package/dist/data/taskStore.js.map +1 -0
  32. package/dist/index.d.ts +23 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +86 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/prompts/index.d.ts +12 -0
  37. package/dist/prompts/index.d.ts.map +1 -0
  38. package/dist/prompts/index.js +17 -0
  39. package/dist/prompts/index.js.map +1 -0
  40. package/dist/prompts/projectPromptBuilder.d.ts +13 -0
  41. package/dist/prompts/projectPromptBuilder.d.ts.map +1 -0
  42. package/dist/prompts/projectPromptBuilder.js +29 -0
  43. package/dist/prompts/projectPromptBuilder.js.map +1 -0
  44. package/dist/prompts/researchPromptBuilder.d.ts +10 -0
  45. package/dist/prompts/researchPromptBuilder.d.ts.map +1 -0
  46. package/dist/prompts/researchPromptBuilder.js +20 -0
  47. package/dist/prompts/researchPromptBuilder.js.map +1 -0
  48. package/dist/prompts/taskPromptBuilders.d.ts +87 -0
  49. package/dist/prompts/taskPromptBuilders.d.ts.map +1 -0
  50. package/dist/prompts/taskPromptBuilders.js +529 -0
  51. package/dist/prompts/taskPromptBuilders.js.map +1 -0
  52. package/dist/prompts/templateEngine.d.ts +102 -0
  53. package/dist/prompts/templateEngine.d.ts.map +1 -0
  54. package/dist/prompts/templateEngine.js +145 -0
  55. package/dist/prompts/templateEngine.js.map +1 -0
  56. package/dist/prompts/templateLoader.d.ts +61 -0
  57. package/dist/prompts/templateLoader.d.ts.map +1 -0
  58. package/dist/prompts/templateLoader.js +129 -0
  59. package/dist/prompts/templateLoader.js.map +1 -0
  60. package/dist/prompts/templates/v1/templates_en/analyzeTask/index.md +65 -0
  61. package/dist/prompts/templates/v1/templates_en/analyzeTask/iteration.md +12 -0
  62. package/dist/prompts/templates/v1/templates_en/clearAllTasks/backupInfo.md +1 -0
  63. package/dist/prompts/templates/v1/templates_en/clearAllTasks/cancel.md +7 -0
  64. package/dist/prompts/templates/v1/templates_en/clearAllTasks/empty.md +5 -0
  65. package/dist/prompts/templates/v1/templates_en/clearAllTasks/index.md +5 -0
  66. package/dist/prompts/templates/v1/templates_en/clearAllTasks/result.md +7 -0
  67. package/dist/prompts/templates/v1/templates_en/clearAllTasks/success.md +5 -0
  68. package/dist/prompts/templates/v1/templates_en/deleteTask/completed.md +5 -0
  69. package/dist/prompts/templates/v1/templates_en/deleteTask/index.md +5 -0
  70. package/dist/prompts/templates/v1/templates_en/deleteTask/notFound.md +5 -0
  71. package/dist/prompts/templates/v1/templates_en/deleteTask/result.md +5 -0
  72. package/dist/prompts/templates/v1/templates_en/deleteTask/success.md +5 -0
  73. package/dist/prompts/templates/v1/templates_en/executeTask/analysisResult.md +3 -0
  74. package/dist/prompts/templates/v1/templates_en/executeTask/complexity.md +15 -0
  75. package/dist/prompts/templates/v1/templates_en/executeTask/dependencies.md +3 -0
  76. package/dist/prompts/templates/v1/templates_en/executeTask/dependencyTasks.md +3 -0
  77. package/dist/prompts/templates/v1/templates_en/executeTask/implementationGuide.md +3 -0
  78. package/dist/prompts/templates/v1/templates_en/executeTask/index.md +39 -0
  79. package/dist/prompts/templates/v1/templates_en/executeTask/notes.md +1 -0
  80. package/dist/prompts/templates/v1/templates_en/executeTask/relatedFilesSummary.md +5 -0
  81. package/dist/prompts/templates/v1/templates_en/executeTask/verificationCriteria.md +3 -0
  82. package/dist/prompts/templates/v1/templates_en/getTaskDetail/complatedSummary.md +5 -0
  83. package/dist/prompts/templates/v1/templates_en/getTaskDetail/dependencies.md +1 -0
  84. package/dist/prompts/templates/v1/templates_en/getTaskDetail/error.md +3 -0
  85. package/dist/prompts/templates/v1/templates_en/getTaskDetail/implementationGuide.md +3 -0
  86. package/dist/prompts/templates/v1/templates_en/getTaskDetail/index.md +25 -0
  87. package/dist/prompts/templates/v1/templates_en/getTaskDetail/notFound.md +3 -0
  88. package/dist/prompts/templates/v1/templates_en/getTaskDetail/notes.md +1 -0
  89. package/dist/prompts/templates/v1/templates_en/getTaskDetail/relatedFiles.md +3 -0
  90. package/dist/prompts/templates/v1/templates_en/getTaskDetail/verificationCriteria.md +3 -0
  91. package/dist/prompts/templates/v1/templates_en/initProjectRules/index.md +81 -0
  92. package/dist/prompts/templates/v1/templates_en/listTasks/index.md +7 -0
  93. package/dist/prompts/templates/v1/templates_en/listTasks/notFound.md +3 -0
  94. package/dist/prompts/templates/v1/templates_en/listTasks/taskDetails.md +13 -0
  95. package/dist/prompts/templates/v1/templates_en/planTask/hasThought.md +4 -0
  96. package/dist/prompts/templates/v1/templates_en/planTask/index.md +96 -0
  97. package/dist/prompts/templates/v1/templates_en/planTask/noThought.md +4 -0
  98. package/dist/prompts/templates/v1/templates_en/planTask/tasks.md +17 -0
  99. package/dist/prompts/templates/v1/templates_en/processThought/complatedThought.md +6 -0
  100. package/dist/prompts/templates/v1/templates_en/processThought/index.md +13 -0
  101. package/dist/prompts/templates/v1/templates_en/processThought/moreThought.md +1 -0
  102. package/dist/prompts/templates/v1/templates_en/queryTask/index.md +24 -0
  103. package/dist/prompts/templates/v1/templates_en/queryTask/notFound.md +15 -0
  104. package/dist/prompts/templates/v1/templates_en/queryTask/taskDetails.md +5 -0
  105. package/dist/prompts/templates/v1/templates_en/reflectTask/index.md +57 -0
  106. package/dist/prompts/templates/v1/templates_en/researchMode/index.md +95 -0
  107. package/dist/prompts/templates/v1/templates_en/researchMode/previousState.md +9 -0
  108. package/dist/prompts/templates/v1/templates_en/splitTasks/index.md +34 -0
  109. package/dist/prompts/templates/v1/templates_en/splitTasks/taskDetails.md +12 -0
  110. package/dist/prompts/templates/v1/templates_en/tests/basic.md +1 -0
  111. package/dist/prompts/templates/v1/templates_en/toolsDescription/analyzeTask.md +1 -0
  112. package/dist/prompts/templates/v1/templates_en/toolsDescription/clearAllTasks.md +1 -0
  113. package/dist/prompts/templates/v1/templates_en/toolsDescription/deleteTask.md +1 -0
  114. package/dist/prompts/templates/v1/templates_en/toolsDescription/executeTask.md +1 -0
  115. package/dist/prompts/templates/v1/templates_en/toolsDescription/getTaskDetail.md +1 -0
  116. package/dist/prompts/templates/v1/templates_en/toolsDescription/initProjectRules.md +1 -0
  117. package/dist/prompts/templates/v1/templates_en/toolsDescription/listTasks.md +1 -0
  118. package/dist/prompts/templates/v1/templates_en/toolsDescription/planTask.md +3 -0
  119. package/dist/prompts/templates/v1/templates_en/toolsDescription/processThought.md +1 -0
  120. package/dist/prompts/templates/v1/templates_en/toolsDescription/queryTask.md +1 -0
  121. package/dist/prompts/templates/v1/templates_en/toolsDescription/reflectTask.md +1 -0
  122. package/dist/prompts/templates/v1/templates_en/toolsDescription/researchMode.md +1 -0
  123. package/dist/prompts/templates/v1/templates_en/toolsDescription/splitTasks.md +83 -0
  124. package/dist/prompts/templates/v1/templates_en/toolsDescription/updateTask.md +1 -0
  125. package/dist/prompts/templates/v1/templates_en/toolsDescription/verifyTask.md +37 -0
  126. package/dist/prompts/templates/v1/templates_en/updateTaskContent/emptyUpdate.md +5 -0
  127. package/dist/prompts/templates/v1/templates_en/updateTaskContent/fileDetails.md +1 -0
  128. package/dist/prompts/templates/v1/templates_en/updateTaskContent/index.md +7 -0
  129. package/dist/prompts/templates/v1/templates_en/updateTaskContent/notFound.md +5 -0
  130. package/dist/prompts/templates/v1/templates_en/updateTaskContent/success.md +9 -0
  131. package/dist/prompts/templates/v1/templates_en/updateTaskContent/successDetails.md +3 -0
  132. package/dist/prompts/templates/v1/templates_en/updateTaskContent/validation.md +5 -0
  133. package/dist/prompts/templates/v1/templates_en/verifyTask/index.md +19 -0
  134. package/dist/prompts/templates/v1/templates_en/verifyTask/noPass.md +12 -0
  135. package/dist/prompts/thoughtPromptBuilder.d.ts +11 -0
  136. package/dist/prompts/thoughtPromptBuilder.d.ts.map +1 -0
  137. package/dist/prompts/thoughtPromptBuilder.js +30 -0
  138. package/dist/prompts/thoughtPromptBuilder.js.map +1 -0
  139. package/dist/server/container.d.ts +111 -0
  140. package/dist/server/container.d.ts.map +1 -0
  141. package/dist/server/container.js +135 -0
  142. package/dist/server/container.js.map +1 -0
  143. package/dist/server/logger.d.ts +98 -0
  144. package/dist/server/logger.d.ts.map +1 -0
  145. package/dist/server/logger.js +295 -0
  146. package/dist/server/logger.js.map +1 -0
  147. package/dist/server/mcpServer.d.ts +162 -0
  148. package/dist/server/mcpServer.d.ts.map +1 -0
  149. package/dist/server/mcpServer.js +236 -0
  150. package/dist/server/mcpServer.js.map +1 -0
  151. package/dist/tools/project/index.d.ts +7 -0
  152. package/dist/tools/project/index.d.ts.map +1 -0
  153. package/dist/tools/project/index.js +7 -0
  154. package/dist/tools/project/index.js.map +1 -0
  155. package/dist/tools/project/projectTools.d.ts +17 -0
  156. package/dist/tools/project/projectTools.d.ts.map +1 -0
  157. package/dist/tools/project/projectTools.js +73 -0
  158. package/dist/tools/project/projectTools.js.map +1 -0
  159. package/dist/tools/research/index.d.ts +7 -0
  160. package/dist/tools/research/index.d.ts.map +1 -0
  161. package/dist/tools/research/index.js +7 -0
  162. package/dist/tools/research/index.js.map +1 -0
  163. package/dist/tools/research/researchTools.d.ts +16 -0
  164. package/dist/tools/research/researchTools.d.ts.map +1 -0
  165. package/dist/tools/research/researchTools.js +41 -0
  166. package/dist/tools/research/researchTools.js.map +1 -0
  167. package/dist/tools/task/index.d.ts +8 -0
  168. package/dist/tools/task/index.d.ts.map +1 -0
  169. package/dist/tools/task/index.js +8 -0
  170. package/dist/tools/task/index.js.map +1 -0
  171. package/dist/tools/task/taskTools.d.ts +32 -0
  172. package/dist/tools/task/taskTools.d.ts.map +1 -0
  173. package/dist/tools/task/taskTools.js +542 -0
  174. package/dist/tools/task/taskTools.js.map +1 -0
  175. package/dist/tools/thought/index.d.ts +7 -0
  176. package/dist/tools/thought/index.d.ts.map +1 -0
  177. package/dist/tools/thought/index.js +7 -0
  178. package/dist/tools/thought/index.js.map +1 -0
  179. package/dist/tools/thought/thoughtTools.d.ts +16 -0
  180. package/dist/tools/thought/thoughtTools.d.ts.map +1 -0
  181. package/dist/tools/thought/thoughtTools.js +47 -0
  182. package/dist/tools/thought/thoughtTools.js.map +1 -0
  183. package/docs/API.md +32 -0
  184. package/docs/ARCHITECTURE.md +44 -0
  185. package/docs/COMPATIBILITY_REPORT.md +26 -0
  186. package/docs/PERFORMANCE.md +66 -0
  187. package/package.json +77 -0
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Atomic JSON File Operations
3
+ *
4
+ * Implements atomic write using temp file + fsync + rename.
5
+ */
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ import { randomUUID } from 'crypto';
9
+ import { z } from 'zod';
10
+ import { ensureDirectory } from '../config/pathResolver.js';
11
+ /**
12
+ * Maximum number of retry attempts for file operations.
13
+ */
14
+ const MAX_RETRIES = 5;
15
+ /**
16
+ * Base delay in milliseconds for exponential backoff.
17
+ */
18
+ const BASE_DELAY_MS = 100;
19
+ /**
20
+ * File operation errors with detailed context
21
+ */
22
+ export class FileOperationError extends Error {
23
+ filePath;
24
+ operation;
25
+ originalError;
26
+ constructor(message, filePath, operation, originalError) {
27
+ super(message);
28
+ this.filePath = filePath;
29
+ this.operation = operation;
30
+ this.originalError = originalError;
31
+ this.name = 'FileOperationError';
32
+ }
33
+ }
34
+ /**
35
+ * Parse JSON string and wrap errors
36
+ */
37
+ function parseJson(content, filePath) {
38
+ try {
39
+ return JSON.parse(content);
40
+ }
41
+ catch (error) {
42
+ throw new FileOperationError(`Invalid JSON in file: ${filePath}`, filePath, 'read', error);
43
+ }
44
+ }
45
+ /**
46
+ * Validate data with Zod schema and wrap errors
47
+ */
48
+ function validateWithSchema(data, schema, filePath) {
49
+ const result = schema.safeParse(data);
50
+ if (!result.success) {
51
+ throw new FileOperationError(`Schema validation failed for ${filePath}: ${result.error.message}`, filePath, 'read', result.error);
52
+ }
53
+ return result.data;
54
+ }
55
+ /**
56
+ * Check if error should not be retried
57
+ */
58
+ function isNonRetryableError(error) {
59
+ return (error instanceof FileOperationError ||
60
+ (error instanceof Error && 'code' in error && error.code === 'ENOENT'));
61
+ }
62
+ /**
63
+ * Read and parse JSON file with Zod schema validation.
64
+ */
65
+ export async function readJsonFile(filePath, schema) {
66
+ let lastError;
67
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
68
+ try {
69
+ // Read file content
70
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
71
+ const content = await fs.readFile(filePath, 'utf-8');
72
+ // Parse and validate (these throw FileOperationError on failure)
73
+ const parsed = parseJson(content, filePath);
74
+ const validated = validateWithSchema(parsed, schema, filePath);
75
+ return validated;
76
+ }
77
+ catch (error) {
78
+ lastError = error;
79
+ // Don't retry validation errors or ENOENT (file doesn't exist)
80
+ if (isNonRetryableError(error)) {
81
+ // If it's already a FileOperationError, throw it as-is
82
+ if (error instanceof FileOperationError) {
83
+ throw error;
84
+ }
85
+ // Wrap ENOENT in FileOperationError
86
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
87
+ throw new FileOperationError(`File not found: ${filePath}`, filePath, 'read', error);
88
+ }
89
+ throw error;
90
+ }
91
+ // Retry on transient errors (EAGAIN, EBUSY, etc.)
92
+ if (attempt < MAX_RETRIES - 1) {
93
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt);
94
+ await new Promise(resolve => setTimeout(resolve, delay));
95
+ continue;
96
+ }
97
+ }
98
+ }
99
+ // All retries exhausted
100
+ throw new FileOperationError(`Failed to read file after ${MAX_RETRIES} attempts: ${filePath}`, filePath, 'read', lastError);
101
+ }
102
+ /**
103
+ * Write JSON file atomically using temp file + rename pattern.
104
+ */
105
+ export async function writeJsonFile(filePath, data, schema) {
106
+ // Validate data before any file operations
107
+ const validationResult = schema.safeParse(data);
108
+ if (!validationResult.success) {
109
+ throw new FileOperationError(`Schema validation failed before write: ${validationResult.error.message}`, filePath, 'write', validationResult.error);
110
+ }
111
+ // Ensure parent directory exists
112
+ const dirPath = path.dirname(filePath);
113
+ await ensureDirectory(dirPath);
114
+ // Generate unique temp file path
115
+ const tempPath = `${filePath}.tmp.${randomUUID().replace(/-/g, '')}`;
116
+ let lastError;
117
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
118
+ try {
119
+ // Write to temporary file
120
+ await writeJsonCore(tempPath, data);
121
+ // Atomic rename
122
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
123
+ await fs.rename(tempPath, filePath);
124
+ // Success - clean exit
125
+ return;
126
+ }
127
+ catch (error) {
128
+ lastError = error;
129
+ // Clean up temp file on failure
130
+ await tryDeleteFile(tempPath);
131
+ // Retry on transient errors
132
+ if (attempt < MAX_RETRIES - 1) {
133
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt);
134
+ await new Promise(resolve => setTimeout(resolve, delay));
135
+ continue;
136
+ }
137
+ }
138
+ }
139
+ // All retries exhausted
140
+ throw new FileOperationError(`Failed to write file after ${MAX_RETRIES} attempts: ${filePath}`, filePath, 'write', lastError);
141
+ }
142
+ /**
143
+ * Core write operation: serialize JSON and flush to disk.
144
+ */
145
+ async function writeJsonCore(filePath, data) {
146
+ // Serialize with pretty-printing (2-space indentation)
147
+ const json = JSON.stringify(data, null, 2);
148
+ // Write to file
149
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
150
+ const fileHandle = await fs.open(filePath, 'w');
151
+ try {
152
+ await fileHandle.writeFile(json, 'utf-8');
153
+ // Flush OS buffers to disk
154
+ await fileHandle.sync();
155
+ }
156
+ finally {
157
+ await fileHandle.close();
158
+ }
159
+ }
160
+ /**
161
+ * Safely delete a file, ignoring errors.
162
+ */
163
+ async function tryDeleteFile(filePath) {
164
+ try {
165
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
166
+ await fs.unlink(filePath);
167
+ }
168
+ catch {
169
+ // Ignore all errors - this is best-effort cleanup
170
+ }
171
+ }
172
+ /**
173
+ * Check if a file exists
174
+ *
175
+ * @param filePath - Absolute file path
176
+ * @returns true if file exists, false otherwise
177
+ */
178
+ export async function fileExists(filePath) {
179
+ try {
180
+ await fs.access(filePath);
181
+ return true;
182
+ }
183
+ catch {
184
+ return false;
185
+ }
186
+ }
187
+ /**
188
+ * Read JSON file or return default value if file doesn't exist.
189
+ */
190
+ export async function readJsonFileOrDefault(filePath, schema, defaultValue) {
191
+ try {
192
+ return await readJsonFile(filePath, schema);
193
+ }
194
+ catch (error) {
195
+ // Return default if file doesn't exist
196
+ if (error instanceof FileOperationError &&
197
+ error.originalError instanceof Error &&
198
+ 'code' in error.originalError &&
199
+ error.originalError.code === 'ENOENT') {
200
+ return defaultValue;
201
+ }
202
+ // Re-throw other errors (validation failures, permission errors, etc.)
203
+ throw error;
204
+ }
205
+ }
206
+ /**
207
+ * List all files in a directory.
208
+ */
209
+ export async function listFiles(dirPath) {
210
+ try {
211
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
212
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
213
+ return entries
214
+ .filter(entry => entry.isFile())
215
+ .map(entry => entry.name);
216
+ }
217
+ catch (error) {
218
+ throw new FileOperationError(`Failed to list files in directory: ${dirPath}`, dirPath, 'read', error);
219
+ }
220
+ }
221
+ /**
222
+ * Delete a file with retry logic.
223
+ */
224
+ export async function deleteFile(filePath) {
225
+ let lastError;
226
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
227
+ try {
228
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
229
+ await fs.unlink(filePath);
230
+ return;
231
+ }
232
+ catch (error) {
233
+ lastError = error;
234
+ // Don't retry if file doesn't exist (already deleted)
235
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
236
+ return;
237
+ }
238
+ // Retry on transient errors
239
+ if (attempt < MAX_RETRIES - 1) {
240
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt);
241
+ await new Promise(resolve => setTimeout(resolve, delay));
242
+ continue;
243
+ }
244
+ }
245
+ }
246
+ throw new FileOperationError(`Failed to delete file after ${MAX_RETRIES} attempts: ${filePath}`, filePath, 'delete', lastError);
247
+ }
248
+ //# sourceMappingURL=fileOperations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileOperations.js","sourceRoot":"","sources":["../../src/data/fileOperations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * MemoryStore - Task snapshot and history management
3
+ *
4
+ * Provides:
5
+ * - Task snapshot creation and retrieval
6
+ * - Historical task state management
7
+ * - Backup functionality for task clearing
8
+ *
9
+ */
10
+ import { type TaskDocument } from './schemas.js';
11
+ /**
12
+ * Snapshot metadata
13
+ */
14
+ export interface SnapshotInfo {
15
+ /** Snapshot filename (without extension) */
16
+ name: string;
17
+ /** Full file path */
18
+ path: string;
19
+ /** Snapshot creation timestamp */
20
+ timestamp: Date;
21
+ /** Number of tasks in snapshot */
22
+ taskCount: number;
23
+ }
24
+ /**
25
+ * MemoryStore - Manages task snapshots and historical data
26
+ */
27
+ export declare class MemoryStore {
28
+ private readonly memoryDir;
29
+ private readonly backupDir;
30
+ /**
31
+ * Create a new MemoryStore
32
+ *
33
+ * @param dataDir - Optional data directory override (for testing)
34
+ */
35
+ constructor(dataDir?: string);
36
+ /**
37
+ * Save a task snapshot with a descriptive name
38
+ *
39
+ * @param name - Snapshot name (will be sanitized)
40
+ * @param document - Task document to snapshot
41
+ * @returns Path to saved snapshot
42
+ */
43
+ saveSnapshotAsync(name: string, document: TaskDocument): Promise<string>;
44
+ /**
45
+ * Load a task snapshot by name
46
+ *
47
+ * @param name - Snapshot filename (with or without .json extension)
48
+ * @returns Task document from snapshot
49
+ */
50
+ loadSnapshotAsync(name: string): Promise<TaskDocument>;
51
+ /**
52
+ * List all available snapshots
53
+ *
54
+ * @returns Array of snapshot metadata
55
+ */
56
+ listSnapshotsAsync(): Promise<SnapshotInfo[]>;
57
+ /**
58
+ * Delete a snapshot by name
59
+ *
60
+ * @param name - Snapshot filename (with or without .json extension)
61
+ * @returns True if deleted, false if not found
62
+ */
63
+ deleteSnapshotAsync(name: string): Promise<boolean>;
64
+ /**
65
+ * Create a backup of completed tasks
66
+ * Used when clearing all tasks to preserve history
67
+ *
68
+ * @param document - Task document to backup
69
+ * @returns Path to backup file
70
+ */
71
+ createBackupAsync(document: TaskDocument): Promise<string>;
72
+ /**
73
+ * Sanitize filename to prevent path traversal and filesystem issues
74
+ *
75
+ * Removes:
76
+ * - Path separators (/, \)
77
+ * - Null bytes
78
+ * - Control characters
79
+ * - Leading/trailing dots and spaces
80
+ *
81
+ * @param filename - Raw filename
82
+ * @returns Sanitized filename safe for filesystem
83
+ */
84
+ private sanitizeFilename;
85
+ }
86
+ //# sourceMappingURL=memoryStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryStore.d.ts","sourceRoot":"","sources":["../../src/data/memoryStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,cAAc,CAAC;AAIrE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,SAAS,EAAE,IAAI,CAAC;IAChB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAsBD;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC;;;;OAIG;gBACS,OAAO,CAAC,EAAE,MAAM;IAM5B;;;;;;OAMG;IACU,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAiCrF;;;;;OAKG;IACU,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAyBnE;;;;OAIG;IACU,kBAAkB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAwC1D;;;;;OAKG;IACU,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBhE;;;;;;OAMG;IACU,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBvE;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;CAezB"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * MemoryStore - Task snapshot and history management
3
+ *
4
+ * Provides:
5
+ * - Task snapshot creation and retrieval
6
+ * - Historical task state management
7
+ * - Backup functionality for task clearing
8
+ *
9
+ */
10
+ import { writeJsonFile, readJsonFile, fileExists, listFiles, deleteFile } from './fileOperations.js';
11
+ import { TaskDocumentSchema } from './schemas.js';
12
+ import { getDataPath } from '../config/pathResolver.js';
13
+ import path from 'path';
14
+ /**
15
+ * Maximum filename length to prevent filesystem issues
16
+ */
17
+ const MAX_FILENAME_LENGTH = 100;
18
+ /**
19
+ * Maximum snapshot file size (10MB) to prevent DoS
20
+ */
21
+ const MAX_SNAPSHOT_SIZE_BYTES = 10 * 1024 * 1024;
22
+ /**
23
+ * Memory subdirectory name
24
+ */
25
+ const MEMORY_DIR = 'memory';
26
+ /**
27
+ * Backup subdirectory name
28
+ */
29
+ const BACKUP_DIR = 'backups';
30
+ /**
31
+ * MemoryStore - Manages task snapshots and historical data
32
+ */
33
+ export class MemoryStore {
34
+ memoryDir;
35
+ backupDir;
36
+ /**
37
+ * Create a new MemoryStore
38
+ *
39
+ * @param dataDir - Optional data directory override (for testing)
40
+ */
41
+ constructor(dataDir) {
42
+ const baseDir = dataDir ?? getDataPath('', dataDir);
43
+ this.memoryDir = path.join(baseDir, MEMORY_DIR);
44
+ this.backupDir = path.join(baseDir, BACKUP_DIR);
45
+ }
46
+ /**
47
+ * Save a task snapshot with a descriptive name
48
+ *
49
+ * @param name - Snapshot name (will be sanitized)
50
+ * @param document - Task document to snapshot
51
+ * @returns Path to saved snapshot
52
+ */
53
+ async saveSnapshotAsync(name, document) {
54
+ // Sanitize and validate filename
55
+ const sanitizedName = this.sanitizeFilename(name);
56
+ if (sanitizedName.length === 0) {
57
+ throw new Error('Snapshot name cannot be empty after sanitization');
58
+ }
59
+ if (sanitizedName.length > MAX_FILENAME_LENGTH) {
60
+ throw new Error(`Snapshot name too long (max ${MAX_FILENAME_LENGTH} characters after sanitization)`);
61
+ }
62
+ // Create snapshot filename with timestamp
63
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
64
+ const filename = `${sanitizedName}_${timestamp}.json`;
65
+ const filePath = path.join(this.memoryDir, filename);
66
+ // Validate file size before writing
67
+ const jsonSize = JSON.stringify(document).length;
68
+ if (jsonSize > MAX_SNAPSHOT_SIZE_BYTES) {
69
+ throw new Error(`Snapshot too large (${jsonSize} bytes, max ${MAX_SNAPSHOT_SIZE_BYTES})`);
70
+ }
71
+ // Write snapshot atomically
72
+ await writeJsonFile(filePath, document, TaskDocumentSchema);
73
+ return filePath;
74
+ }
75
+ /**
76
+ * Load a task snapshot by name
77
+ *
78
+ * @param name - Snapshot filename (with or without .json extension)
79
+ * @returns Task document from snapshot
80
+ */
81
+ async loadSnapshotAsync(name) {
82
+ // Add .json extension if not present
83
+ const filename = name.endsWith('.json') ? name : `${name}.json`;
84
+ const filePath = path.join(this.memoryDir, filename);
85
+ // Validate path doesn't escape memory directory
86
+ const resolvedPath = path.resolve(filePath);
87
+ const resolvedMemoryDir = path.resolve(this.memoryDir);
88
+ if (!resolvedPath.startsWith(resolvedMemoryDir)) {
89
+ throw new Error('Invalid snapshot path: directory traversal detected');
90
+ }
91
+ // Check if file exists
92
+ if (!(await fileExists(filePath))) {
93
+ throw new Error(`Snapshot not found: ${name}`);
94
+ }
95
+ // Read and validate snapshot
96
+ // Zod parsing ensures all defaults are applied
97
+ const document = await readJsonFile(filePath, TaskDocumentSchema);
98
+ // Return as TaskDocument (Zod ensures schema compliance including defaults)
99
+ return document;
100
+ }
101
+ /**
102
+ * List all available snapshots
103
+ *
104
+ * @returns Array of snapshot metadata
105
+ */
106
+ async listSnapshotsAsync() {
107
+ // Ensure memory directory exists
108
+ if (!(await fileExists(this.memoryDir))) {
109
+ return [];
110
+ }
111
+ // List all JSON files in memory directory
112
+ const allFiles = await listFiles(this.memoryDir);
113
+ const files = allFiles.filter(f => f.endsWith('.json'));
114
+ // Load metadata for each snapshot
115
+ const snapshots = [];
116
+ for (const file of files) {
117
+ try {
118
+ const filePath = path.join(this.memoryDir, file);
119
+ const document = await readJsonFile(filePath, TaskDocumentSchema);
120
+ // Extract timestamp from filename (format: name_YYYY-MM-DDTHH-MM-SS-sssZ.json)
121
+ const timestampMatch = file.match(/_(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z)\.json$/);
122
+ const timestamp = timestampMatch
123
+ ? new Date(timestampMatch[1].replace(/-/g, ':').replace(/T(\d{2}):(\d{2}):(\d{2})/, 'T$1:$2:$3.'))
124
+ : new Date();
125
+ snapshots.push({
126
+ name: file.replace(/\.json$/, ''),
127
+ path: filePath,
128
+ timestamp,
129
+ taskCount: document.tasks?.length ?? 0,
130
+ });
131
+ }
132
+ catch (error) {
133
+ // Skip corrupted snapshots
134
+ console.warn(`Failed to load snapshot ${file}:`, error);
135
+ }
136
+ }
137
+ // Sort by timestamp (newest first)
138
+ return snapshots.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
139
+ }
140
+ /**
141
+ * Delete a snapshot by name
142
+ *
143
+ * @param name - Snapshot filename (with or without .json extension)
144
+ * @returns True if deleted, false if not found
145
+ */
146
+ async deleteSnapshotAsync(name) {
147
+ const filename = name.endsWith('.json') ? name : `${name}.json`;
148
+ const filePath = path.join(this.memoryDir, filename);
149
+ // Validate path doesn't escape memory directory
150
+ const resolvedPath = path.resolve(filePath);
151
+ const resolvedMemoryDir = path.resolve(this.memoryDir);
152
+ if (!resolvedPath.startsWith(resolvedMemoryDir)) {
153
+ throw new Error('Invalid snapshot path: directory traversal detected');
154
+ }
155
+ // Check if file exists
156
+ if (!(await fileExists(filePath))) {
157
+ return false;
158
+ }
159
+ // Delete snapshot
160
+ await deleteFile(filePath);
161
+ return true;
162
+ }
163
+ /**
164
+ * Create a backup of completed tasks
165
+ * Used when clearing all tasks to preserve history
166
+ *
167
+ * @param document - Task document to backup
168
+ * @returns Path to backup file
169
+ */
170
+ async createBackupAsync(document) {
171
+ // Filter to only completed tasks
172
+ const completedTasks = document.tasks.filter(t => t.status === 'completed');
173
+ if (completedTasks.length === 0) {
174
+ throw new Error('No completed tasks to backup');
175
+ }
176
+ const backupDocument = {
177
+ version: document.version,
178
+ tasks: completedTasks,
179
+ };
180
+ // Create backup filename with timestamp
181
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
182
+ const filename = `backup_${timestamp}.json`;
183
+ const filePath = path.join(this.backupDir, filename);
184
+ // Write backup atomically
185
+ await writeJsonFile(filePath, backupDocument, TaskDocumentSchema);
186
+ return filePath;
187
+ }
188
+ /**
189
+ * Sanitize filename to prevent path traversal and filesystem issues
190
+ *
191
+ * Removes:
192
+ * - Path separators (/, \)
193
+ * - Null bytes
194
+ * - Control characters
195
+ * - Leading/trailing dots and spaces
196
+ *
197
+ * @param filename - Raw filename
198
+ * @returns Sanitized filename safe for filesystem
199
+ */
200
+ sanitizeFilename(filename) {
201
+ return filename
202
+ // Remove path separators and null bytes
203
+ .replace(/[/\\:\0]/g, '')
204
+ // Remove control characters (ASCII 0-31, 127)
205
+ .replace(/[\x00-\x1F\x7F]/g, '')
206
+ // Remove characters that are problematic on Windows
207
+ .replace(/[<>:"|?*]/g, '')
208
+ // Replace spaces with underscores
209
+ .replace(/\s+/g, '_')
210
+ // Remove leading/trailing dots and spaces
211
+ .replace(/^[.\s]+|[.\s]+$/g, '')
212
+ // Collapse multiple underscores
213
+ .replace(/_+/g, '_');
214
+ }
215
+ }
216
+ //# sourceMappingURL=memoryStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryStore.js","sourceRoot":"","sources":["../../src/data/memoryStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * RulesStore - Project-specific coding rules management
3
+ *
4
+ * Provides:
5
+ * - Loading and saving project coding standards
6
+ * - Markdown-based rules storage
7
+ * - Validation and size limits
8
+ *
9
+ */
10
+ /**
11
+ * RulesStore - Manages project-specific coding rules
12
+ */
13
+ export declare class RulesStore {
14
+ private readonly rulesFilePath;
15
+ /**
16
+ * Create a new RulesStore
17
+ *
18
+ * @param dataDir - Optional data directory override (for testing)
19
+ */
20
+ constructor(dataDir?: string);
21
+ /**
22
+ * Check if project rules exist
23
+ *
24
+ * @returns True if rules file exists
25
+ */
26
+ rulesExistAsync(): Promise<boolean>;
27
+ /**
28
+ * Load project rules
29
+ *
30
+ * @returns Rules content as markdown string
31
+ */
32
+ loadRulesAsync(): Promise<string>;
33
+ /**
34
+ * Save project rules
35
+ *
36
+ * Validates markdown content and size before saving
37
+ *
38
+ * @param content - Rules content as markdown string
39
+ */
40
+ saveRulesAsync(content: string): Promise<void>;
41
+ /**
42
+ * Initialize project rules with default template
43
+ *
44
+ * Only creates file if it doesn't already exist
45
+ *
46
+ * @returns True if created, false if already exists
47
+ */
48
+ initializeRulesAsync(): Promise<boolean>;
49
+ /**
50
+ * Update project rules (append or replace sections)
51
+ *
52
+ * @param updates - Markdown content to append or section replacements
53
+ * @param mode - 'append' to add to end, 'replace' to overwrite
54
+ */
55
+ updateRulesAsync(updates: string, mode?: 'append' | 'replace'): Promise<void>;
56
+ /**
57
+ * Delete project rules
58
+ *
59
+ * @returns True if deleted, false if didn't exist
60
+ */
61
+ deleteRulesAsync(): Promise<boolean>;
62
+ }
63
+ //# sourceMappingURL=rulesStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rulesStore.d.ts","sourceRoot":"","sources":["../../src/data/rulesStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuDH;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;;;OAIG;gBACS,OAAO,CAAC,EAAE,MAAM;IAI5B;;;;OAIG;IACU,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAIhD;;;;OAIG;IACU,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAmB9C;;;;;;OAMG;IACU,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4C3D;;;;;;OAMG;IACU,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;IASrD;;;;;OAKG;IACU,gBAAgB,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,QAAQ,GAAG,SAAoB,GACpC,OAAO,CAAC,IAAI,CAAC;IAuBhB;;;;OAIG;IACU,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;CASlD"}