@lumenflow/memory 1.0.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 (55) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +181 -0
  3. package/dist/index.d.ts +16 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +16 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/mem-checkpoint-core.d.ts +91 -0
  8. package/dist/mem-checkpoint-core.d.ts.map +1 -0
  9. package/dist/mem-checkpoint-core.js +175 -0
  10. package/dist/mem-checkpoint-core.js.map +1 -0
  11. package/dist/mem-cleanup-core.d.ts +202 -0
  12. package/dist/mem-cleanup-core.d.ts.map +1 -0
  13. package/dist/mem-cleanup-core.js +364 -0
  14. package/dist/mem-cleanup-core.js.map +1 -0
  15. package/dist/mem-create-core.d.ts +93 -0
  16. package/dist/mem-create-core.d.ts.map +1 -0
  17. package/dist/mem-create-core.js +369 -0
  18. package/dist/mem-create-core.js.map +1 -0
  19. package/dist/mem-id.d.ts +91 -0
  20. package/dist/mem-id.d.ts.map +1 -0
  21. package/dist/mem-id.js +136 -0
  22. package/dist/mem-id.js.map +1 -0
  23. package/dist/mem-init-core.d.ts +91 -0
  24. package/dist/mem-init-core.d.ts.map +1 -0
  25. package/dist/mem-init-core.js +138 -0
  26. package/dist/mem-init-core.js.map +1 -0
  27. package/dist/mem-ready-core.d.ts +56 -0
  28. package/dist/mem-ready-core.d.ts.map +1 -0
  29. package/dist/mem-ready-core.js +232 -0
  30. package/dist/mem-ready-core.js.map +1 -0
  31. package/dist/mem-signal-core.d.ts +132 -0
  32. package/dist/mem-signal-core.d.ts.map +1 -0
  33. package/dist/mem-signal-core.js +249 -0
  34. package/dist/mem-signal-core.js.map +1 -0
  35. package/dist/mem-start-core.d.ts +76 -0
  36. package/dist/mem-start-core.d.ts.map +1 -0
  37. package/dist/mem-start-core.js +133 -0
  38. package/dist/mem-start-core.js.map +1 -0
  39. package/dist/mem-summarize-core.d.ts +105 -0
  40. package/dist/mem-summarize-core.d.ts.map +1 -0
  41. package/dist/mem-summarize-core.js +263 -0
  42. package/dist/mem-summarize-core.js.map +1 -0
  43. package/dist/mem-triage-core.d.ts +127 -0
  44. package/dist/mem-triage-core.d.ts.map +1 -0
  45. package/dist/mem-triage-core.js +332 -0
  46. package/dist/mem-triage-core.js.map +1 -0
  47. package/dist/memory-schema.d.ts +150 -0
  48. package/dist/memory-schema.d.ts.map +1 -0
  49. package/dist/memory-schema.js +149 -0
  50. package/dist/memory-schema.js.map +1 -0
  51. package/dist/memory-store.d.ts +108 -0
  52. package/dist/memory-store.d.ts.map +1 -0
  53. package/dist/memory-store.js +234 -0
  54. package/dist/memory-store.js.map +1 -0
  55. package/package.json +76 -0
@@ -0,0 +1,369 @@
1
+ /**
2
+ * Memory Create Core (WU-1469)
3
+ *
4
+ * Core logic for creating memory nodes with discovered-from provenance.
5
+ * KEY DIFFERENTIATOR: supports discovered-from relationship for scope-creep
6
+ * forensics. Creates audit trail of WHY work expanded, not just WHAT changed.
7
+ *
8
+ * Features:
9
+ * - Creates all 5 node types: session, discovery, checkpoint, note, summary
10
+ * - Auto-generates hash-based ID using mem-id
11
+ * - Validates node against memory-schema
12
+ * - Supports discovered-from relationship for provenance tracking
13
+ *
14
+ * @see {@link tools/mem-create.mjs} - CLI wrapper
15
+ * @see {@link tools/__tests__/mem-create.test.mjs} - Tests
16
+ * @see {@link tools/lib/memory-schema.mjs} - Schema definitions
17
+ */
18
+ import fs from 'node:fs/promises';
19
+ import path from 'node:path';
20
+ import { generateMemId } from './mem-id.js';
21
+ import { appendNode } from './memory-store.js';
22
+ import { MEMORY_NODE_TYPES, MEMORY_PATTERNS, validateMemoryNode, validateRelationship, } from './memory-schema.js';
23
+ /**
24
+ * Memory directory path relative to base directory
25
+ */
26
+ const MEMORY_DIR = '.beacon/memory';
27
+ /**
28
+ * Relationships file name
29
+ */
30
+ const RELATIONSHIPS_FILE_NAME = 'relationships.jsonl';
31
+ /**
32
+ * Default node type
33
+ */
34
+ const DEFAULT_NODE_TYPE = 'discovery';
35
+ /**
36
+ * Session file path relative to main checkout
37
+ */
38
+ const SESSION_FILE_PATH = '.beacon/sessions/current.json';
39
+ /**
40
+ * Type aliases for user-friendly CLI experience (WU-1762)
41
+ * Maps alias names to canonical types and additional tags
42
+ */
43
+ const TYPE_ALIASES = {
44
+ bug: { type: 'discovery', tag: 'bug' },
45
+ idea: { type: 'discovery', tag: 'idea' },
46
+ question: { type: 'discovery', tag: 'question' },
47
+ dependency: { type: 'discovery', tag: 'dependency' },
48
+ };
49
+ /**
50
+ * Lifecycle mapping by node type
51
+ * - session: Lives for WU duration (wu)
52
+ * - discovery: Lives for WU duration (wu)
53
+ * - checkpoint: Lives for session (session)
54
+ * - note: Lives for session (session)
55
+ * - summary: Persists across WUs (project)
56
+ */
57
+ const LIFECYCLE_BY_TYPE = {
58
+ session: 'wu',
59
+ discovery: 'wu',
60
+ checkpoint: 'session',
61
+ note: 'session',
62
+ summary: 'project',
63
+ };
64
+ /**
65
+ * Error messages for validation
66
+ */
67
+ const ERROR_MESSAGES = {
68
+ TITLE_REQUIRED: 'title is required',
69
+ TITLE_EMPTY: 'title cannot be empty',
70
+ INVALID_TYPE: `Invalid node type. Must be one of: ${MEMORY_NODE_TYPES.join(', ')}`,
71
+ WU_ID_INVALID: 'Invalid WU ID format. Expected pattern: WU-XXX (e.g., WU-123)',
72
+ MEMORY_ID_INVALID: 'Invalid memory ID format. Expected pattern: mem-XXXX (e.g., mem-a1b2)',
73
+ };
74
+ /**
75
+ * Normalizes type aliases to canonical types (WU-1762)
76
+ *
77
+ * Converts user-friendly type aliases (bug, idea) to canonical types (discovery)
78
+ * and returns the tag to be added.
79
+ *
80
+ * @param inputType - User-provided type (may be alias)
81
+ * @returns Normalized type and optional tag
82
+ */
83
+ function normalizeType(inputType) {
84
+ const alias = TYPE_ALIASES[inputType];
85
+ if (alias) {
86
+ return { type: alias.type, aliasTag: alias.tag };
87
+ }
88
+ return { type: inputType, aliasTag: null };
89
+ }
90
+ /**
91
+ * Checks if a path is a git worktree (has .git file pointing to main)
92
+ *
93
+ * @param dir - Directory to check
94
+ * @returns Path to main checkout or null if not a worktree
95
+ */
96
+ async function getMainCheckoutFromWorktree(dir) {
97
+ const gitPath = path.join(dir, '.git');
98
+ try {
99
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Known path
100
+ const stat = await fs.stat(gitPath);
101
+ if (stat.isFile()) {
102
+ // .git is a file = we're in a worktree
103
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Known path
104
+ const gitContent = await fs.readFile(gitPath, { encoding: 'utf-8' });
105
+ // Format: "gitdir: /path/to/main/.git/worktrees/name"
106
+ const match = gitContent.match(/^gitdir:\s*(.+)/);
107
+ if (match && match[1]) {
108
+ const gitDir = match[1].trim();
109
+ // Extract main checkout: /path/to/main/.git/worktrees/name → /path/to/main
110
+ const worktreesIndex = gitDir.indexOf('/.git/worktrees/');
111
+ if (worktreesIndex !== -1) {
112
+ return gitDir.substring(0, worktreesIndex);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ catch {
118
+ // Not a worktree or .git doesn't exist
119
+ }
120
+ return null;
121
+ }
122
+ /**
123
+ * Reads the current session from session file
124
+ *
125
+ * @param baseDir - Base directory (main checkout)
126
+ * @returns Session data or null
127
+ */
128
+ async function readCurrentSession(baseDir) {
129
+ const sessionPath = path.join(baseDir, SESSION_FILE_PATH);
130
+ try {
131
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Known session path
132
+ const content = await fs.readFile(sessionPath, { encoding: 'utf-8' });
133
+ return JSON.parse(content);
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ }
139
+ /**
140
+ * Auto-infers WU ID from current session (WU-1762)
141
+ *
142
+ * Checks for session file in:
143
+ * 1. Current baseDir (if running in main checkout)
144
+ * 2. Main checkout (if running in worktree)
145
+ *
146
+ * @param baseDir - Current working directory
147
+ * @returns Session data from session file or null
148
+ */
149
+ async function inferSessionFromSessionFile(baseDir) {
150
+ // First, try to read session from current directory
151
+ let session = await readCurrentSession(baseDir);
152
+ if (session) {
153
+ return session;
154
+ }
155
+ // If not found, check if we're in a worktree and look in main checkout
156
+ const mainCheckout = await getMainCheckoutFromWorktree(baseDir);
157
+ if (mainCheckout) {
158
+ session = await readCurrentSession(mainCheckout);
159
+ if (session) {
160
+ return session;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ function isValidSessionId(value) {
166
+ if (!value)
167
+ return false;
168
+ if (typeof value !== 'string')
169
+ return false;
170
+ // UUID v4 format (sufficient for validation here; actual schema validates uuid too)
171
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
172
+ }
173
+ /**
174
+ * Validates WU ID format if provided
175
+ *
176
+ * @param wuId - WU ID to validate
177
+ * @returns True if valid or not provided
178
+ */
179
+ function isValidWuId(wuId) {
180
+ if (!wuId)
181
+ return true;
182
+ return MEMORY_PATTERNS.WU_ID.test(wuId);
183
+ }
184
+ /**
185
+ * Validates memory ID format
186
+ *
187
+ * @param memId - Memory ID to validate
188
+ * @returns True if valid
189
+ */
190
+ function isValidMemoryId(memId) {
191
+ return MEMORY_PATTERNS.MEMORY_ID.test(memId);
192
+ }
193
+ /**
194
+ * Ensures the memory directory exists
195
+ *
196
+ * @param baseDir - Base directory
197
+ * @returns Memory directory path
198
+ */
199
+ async function ensureMemoryDir(baseDir) {
200
+ const memoryDir = path.join(baseDir, MEMORY_DIR);
201
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Known directory path
202
+ await fs.mkdir(memoryDir, { recursive: true });
203
+ return memoryDir;
204
+ }
205
+ /**
206
+ * Appends a relationship to the relationships.jsonl file
207
+ *
208
+ * @param memoryDir - Memory directory path
209
+ * @param relationship - Relationship object
210
+ * @returns The appended relationship
211
+ */
212
+ async function appendRelationship(memoryDir, relationship) {
213
+ // Validate relationship before appending
214
+ const validation = validateRelationship(relationship);
215
+ if (!validation.success) {
216
+ const issues = validation.error.issues
217
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
218
+ .join(', ');
219
+ throw new Error(`Relationship validation error: ${issues}`);
220
+ }
221
+ const filePath = path.join(memoryDir, RELATIONSHIPS_FILE_NAME);
222
+ const line = `${JSON.stringify(relationship)}\n`;
223
+ // Use append flag to avoid rewriting the file
224
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes known file
225
+ await fs.appendFile(filePath, line, { encoding: 'utf-8' });
226
+ return relationship;
227
+ }
228
+ /**
229
+ * Gets the lifecycle for a node type
230
+ *
231
+ * @param type - Node type
232
+ * @returns Lifecycle value
233
+ */
234
+ function getLifecycleForType(type) {
235
+ return LIFECYCLE_BY_TYPE[type];
236
+ }
237
+ /**
238
+ * Creates a new memory node with optional discovered-from provenance.
239
+ *
240
+ * Creates a memory node with:
241
+ * - Unique ID (mem-XXXX format) generated from content hash
242
+ * - User-provided title as content
243
+ * - Type-appropriate lifecycle
244
+ * - Optional discovered-from relationship for provenance tracking
245
+ *
246
+ * @param {string} baseDir - Base directory containing .beacon/memory/
247
+ * @param {CreateMemoryNodeOptions} options - Node creation options
248
+ * @returns {Promise<CreateMemoryNodeResult>} Result with created node and optional relationship
249
+ * @throws {Error} If title is missing, type is invalid, or IDs are malformed
250
+ *
251
+ * @example
252
+ * // Create a simple discovery node
253
+ * const result = await createMemoryNode(baseDir, {
254
+ * title: 'Found relevant file at src/utils.mjs',
255
+ * type: 'discovery',
256
+ * wuId: 'WU-1469',
257
+ * });
258
+ *
259
+ * @example
260
+ * // Create a node with provenance (scope-creep tracking)
261
+ * const parent = await createMemoryNode(baseDir, {
262
+ * title: 'Found src/components/',
263
+ * type: 'discovery',
264
+ * });
265
+ * const child = await createMemoryNode(baseDir, {
266
+ * title: 'Found src/components/Button.tsx',
267
+ * type: 'discovery',
268
+ * discoveredFrom: parent.node.id, // Track where this came from
269
+ * });
270
+ */
271
+ export async function createMemoryNode(baseDir, options) {
272
+ const { title, type: inputType = DEFAULT_NODE_TYPE, wuId: explicitWuId, sessionId: explicitSessionId, discoveredFrom, tags: inputTags, priority, } = options;
273
+ // Validate required fields
274
+ if (title === undefined || title === null) {
275
+ throw new Error(ERROR_MESSAGES.TITLE_REQUIRED);
276
+ }
277
+ if (title === '') {
278
+ throw new Error(ERROR_MESSAGES.TITLE_EMPTY);
279
+ }
280
+ // Normalize type aliases (WU-1762): bug → discovery + tag, idea → discovery + tag
281
+ const { type: normalizedType, aliasTag } = normalizeType(inputType);
282
+ // Validate node type (after alias normalization) and narrow type
283
+ if (!MEMORY_NODE_TYPES.includes(normalizedType)) {
284
+ throw new Error(ERROR_MESSAGES.INVALID_TYPE);
285
+ }
286
+ // Type is now validated - safe to cast
287
+ const type = normalizedType;
288
+ // Merge tags: alias tag + user-provided tags, deduplicated (WU-1762)
289
+ let tags = inputTags ? [...inputTags] : [];
290
+ if (aliasTag && !tags.includes(aliasTag)) {
291
+ tags = [aliasTag, ...tags];
292
+ }
293
+ // Remove duplicates while preserving order
294
+ tags = [...new Set(tags)];
295
+ // Auto-infer WU ID and session ID from current session if not provided (WU-1762)
296
+ const inferredSession = explicitWuId && explicitSessionId ? null : await inferSessionFromSessionFile(baseDir);
297
+ const wuId = explicitWuId || inferredSession?.wu_id;
298
+ const sessionId = explicitSessionId ||
299
+ (isValidSessionId(inferredSession?.session_id) ? inferredSession.session_id : undefined);
300
+ // Validate WU ID format if provided
301
+ if (wuId && !isValidWuId(wuId)) {
302
+ throw new Error(ERROR_MESSAGES.WU_ID_INVALID);
303
+ }
304
+ // Validate discovered-from ID format if provided
305
+ if (discoveredFrom && !isValidMemoryId(discoveredFrom)) {
306
+ throw new Error(ERROR_MESSAGES.MEMORY_ID_INVALID);
307
+ }
308
+ // Ensure memory directory exists
309
+ const memoryDir = await ensureMemoryDir(baseDir);
310
+ // Generate node
311
+ const timestamp = new Date().toISOString();
312
+ // Generate deterministic ID from content + timestamp for uniqueness
313
+ const idContent = `${title}-${timestamp}`;
314
+ const id = generateMemId(idContent);
315
+ // Get lifecycle for this type
316
+ const lifecycle = getLifecycleForType(type);
317
+ // Build metadata object
318
+ const metadata = {};
319
+ if (priority) {
320
+ metadata.priority = priority;
321
+ }
322
+ const node = {
323
+ id,
324
+ type,
325
+ lifecycle,
326
+ content: title,
327
+ created_at: timestamp,
328
+ };
329
+ // Add optional fields
330
+ if (wuId) {
331
+ node.wu_id = wuId;
332
+ }
333
+ if (sessionId) {
334
+ node.session_id = sessionId;
335
+ }
336
+ if (Object.keys(metadata).length > 0) {
337
+ node.metadata = metadata;
338
+ }
339
+ if (tags && tags.length > 0) {
340
+ node.tags = tags;
341
+ }
342
+ // Validate node against schema
343
+ const nodeValidation = validateMemoryNode(node);
344
+ if (!nodeValidation.success) {
345
+ const issues = nodeValidation.error.issues
346
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
347
+ .join(', ');
348
+ throw new Error(`Node validation error: ${issues}`);
349
+ }
350
+ // Persist node to memory store
351
+ await appendNode(memoryDir, node);
352
+ // Build result
353
+ const result = {
354
+ success: true,
355
+ node,
356
+ };
357
+ // Create discovered-from relationship if parent specified
358
+ if (discoveredFrom) {
359
+ const relationship = {
360
+ from_id: node.id,
361
+ to_id: discoveredFrom,
362
+ type: 'discovered_from',
363
+ created_at: timestamp,
364
+ };
365
+ await appendRelationship(memoryDir, relationship);
366
+ result.relationship = relationship;
367
+ }
368
+ return result;
369
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mem-create-core.js","sourceRoot":"","sources":["../src/mem-create-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Memory ID Generator (WU-1465)
3
+ *
4
+ * Hash-based collision-free ID generation for memory nodes.
5
+ * Format: mem-[4 hex chars] derived from content hash.
6
+ * Supports hierarchical IDs (mem-a1b2.1.2) for sub-task decomposition.
7
+ *
8
+ * @see {@link tools/lib/__tests__/mem-id.test.mjs} - Tests
9
+ * @see {@link tools/lib/memory-schema.mjs} - Schema definitions
10
+ */
11
+ /**
12
+ * Regex patterns for memory ID validation
13
+ */
14
+ export declare const MEM_ID_PATTERNS: {
15
+ /**
16
+ * Base memory ID format: mem-[a-f0-9]{4}
17
+ * Only lowercase hex characters (0-9, a-f)
18
+ */
19
+ BASE_ID: RegExp;
20
+ /**
21
+ * Hierarchical memory ID format: mem-[a-f0-9]{4}(.[1-9][0-9]*)*
22
+ * Base ID followed by optional dot-separated positive integers
23
+ * Examples: mem-a1b2, mem-a1b2.1, mem-a1b2.1.2
24
+ */
25
+ HIERARCHICAL_ID: RegExp;
26
+ };
27
+ /**
28
+ * Generates a deterministic memory ID from content.
29
+ *
30
+ * Uses SHA-256 hash of the content, taking the first 4 hex characters.
31
+ * Same content always produces the same ID (deterministic).
32
+ *
33
+ * @param content - Content to hash for ID generation
34
+ * @returns Memory ID in format mem-[a-f0-9]{4}
35
+ *
36
+ * @example
37
+ * const id = generateMemId('discovered file at src/utils.mjs');
38
+ * // Returns something like 'mem-a3f2'
39
+ */
40
+ export declare function generateMemId(content: string): string;
41
+ /**
42
+ * Generates a hierarchical memory ID by appending an index to a parent ID.
43
+ *
44
+ * Used for sub-task decomposition where a parent task (mem-a1b2) has
45
+ * child tasks (mem-a1b2.1, mem-a1b2.2) and grandchildren (mem-a1b2.1.1).
46
+ *
47
+ * @param parentId - Parent memory ID (base or hierarchical)
48
+ * @param index - Positive integer index (1-based)
49
+ * @returns Hierarchical memory ID
50
+ * @throws If parentId is invalid or index is not positive
51
+ *
52
+ * @example
53
+ * generateHierarchicalId('mem-a1b2', 1); // 'mem-a1b2.1'
54
+ * generateHierarchicalId('mem-a1b2.1', 2); // 'mem-a1b2.1.2'
55
+ */
56
+ export declare function generateHierarchicalId(parentId: string, index: number): string;
57
+ /**
58
+ * Validation result from validateMemId
59
+ */
60
+ export interface MemIdValidationResult {
61
+ /** Whether the ID is valid */
62
+ valid: boolean;
63
+ /** Type of ID if valid */
64
+ type?: 'base' | 'hierarchical';
65
+ /** Base ID portion if hierarchical */
66
+ baseId?: string;
67
+ /** Hierarchical indices (empty for base IDs) */
68
+ indices: number[];
69
+ /** Error message if invalid */
70
+ error?: string;
71
+ }
72
+ /**
73
+ * Validates a memory ID and extracts its components.
74
+ *
75
+ * Returns validation result with type classification and parsed components.
76
+ * Compatible with MEMORY_PATTERNS.MEMORY_ID from memory-schema.mjs.
77
+ *
78
+ * @param id - Memory ID to validate
79
+ * @returns Validation result with parsed components
80
+ *
81
+ * @example
82
+ * validateMemId('mem-a1b2');
83
+ * // { valid: true, type: 'base', baseId: 'mem-a1b2', indices: [] }
84
+ *
85
+ * validateMemId('mem-a1b2.1.2');
86
+ * // { valid: true, type: 'hierarchical', baseId: 'mem-a1b2', indices: [1, 2] }
87
+ *
88
+ * validateMemId('invalid');
89
+ * // { valid: false, error: 'Invalid memory ID format', indices: [] }
90
+ */
91
+ export declare function validateMemId(id: string): MemIdValidationResult;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mem-id.d.ts","sourceRoot":"","sources":["../src/mem-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAcH;;GAEG;AACH,eAAO,MAAM,eAAe;IAC1B;;;OAGG;;IAGH;;;;OAIG;;CAEJ,CAAC;AAWF;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,OAAO,KAAA,UAOpC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,KAAA,EAAE,KAAK,KAAA,UAYrD;AAED;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAA;;;;;;;;;;;;EA+B/B"}
package/dist/mem-id.js ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Memory ID Generator (WU-1465)
3
+ *
4
+ * Hash-based collision-free ID generation for memory nodes.
5
+ * Format: mem-[4 hex chars] derived from content hash.
6
+ * Supports hierarchical IDs (mem-a1b2.1.2) for sub-task decomposition.
7
+ *
8
+ * @see {@link tools/lib/__tests__/mem-id.test.mjs} - Tests
9
+ * @see {@link tools/lib/memory-schema.mjs} - Schema definitions
10
+ */
11
+ import { createHash } from 'node:crypto';
12
+ /**
13
+ * Memory ID prefix constant
14
+ */
15
+ const MEM_ID_PREFIX = 'mem-';
16
+ /**
17
+ * Number of hex characters in base ID suffix
18
+ */
19
+ const HEX_SUFFIX_LENGTH = 4;
20
+ /**
21
+ * Regex patterns for memory ID validation
22
+ */
23
+ export const MEM_ID_PATTERNS = {
24
+ /**
25
+ * Base memory ID format: mem-[a-f0-9]{4}
26
+ * Only lowercase hex characters (0-9, a-f)
27
+ */
28
+ BASE_ID: /^mem-[a-f0-9]{4}$/,
29
+ /**
30
+ * Hierarchical memory ID format: mem-[a-f0-9]{4}(.[1-9][0-9]*)*
31
+ * Base ID followed by optional dot-separated positive integers
32
+ * Examples: mem-a1b2, mem-a1b2.1, mem-a1b2.1.2
33
+ */
34
+ HIERARCHICAL_ID: /^mem-[a-f0-9]{4}(\.[1-9][0-9]*)*$/,
35
+ };
36
+ /**
37
+ * Error messages for validation and exceptions
38
+ */
39
+ const ERROR_MESSAGES = {
40
+ INVALID_ID: 'Invalid memory ID format',
41
+ INVALID_BASE_ID: 'Invalid base memory ID format',
42
+ INVALID_INDEX: 'Index must be a positive integer',
43
+ };
44
+ /**
45
+ * Generates a deterministic memory ID from content.
46
+ *
47
+ * Uses SHA-256 hash of the content, taking the first 4 hex characters.
48
+ * Same content always produces the same ID (deterministic).
49
+ *
50
+ * @param content - Content to hash for ID generation
51
+ * @returns Memory ID in format mem-[a-f0-9]{4}
52
+ *
53
+ * @example
54
+ * const id = generateMemId('discovered file at src/utils.mjs');
55
+ * // Returns something like 'mem-a3f2'
56
+ */
57
+ export function generateMemId(content) {
58
+ const hash = createHash('sha256').update(content, 'utf-8').digest('hex');
59
+ // Take first 4 hex characters from hash
60
+ const suffix = hash.slice(0, HEX_SUFFIX_LENGTH);
61
+ return `${MEM_ID_PREFIX}${suffix}`;
62
+ }
63
+ /**
64
+ * Generates a hierarchical memory ID by appending an index to a parent ID.
65
+ *
66
+ * Used for sub-task decomposition where a parent task (mem-a1b2) has
67
+ * child tasks (mem-a1b2.1, mem-a1b2.2) and grandchildren (mem-a1b2.1.1).
68
+ *
69
+ * @param parentId - Parent memory ID (base or hierarchical)
70
+ * @param index - Positive integer index (1-based)
71
+ * @returns Hierarchical memory ID
72
+ * @throws If parentId is invalid or index is not positive
73
+ *
74
+ * @example
75
+ * generateHierarchicalId('mem-a1b2', 1); // 'mem-a1b2.1'
76
+ * generateHierarchicalId('mem-a1b2.1', 2); // 'mem-a1b2.1.2'
77
+ */
78
+ export function generateHierarchicalId(parentId, index) {
79
+ // Validate parent ID
80
+ if (!MEM_ID_PATTERNS.HIERARCHICAL_ID.test(parentId)) {
81
+ throw new Error(`${ERROR_MESSAGES.INVALID_BASE_ID}: ${parentId}`);
82
+ }
83
+ // Validate index is positive integer
84
+ if (!Number.isInteger(index) || index < 1) {
85
+ throw new Error(`${ERROR_MESSAGES.INVALID_INDEX}: ${index}`);
86
+ }
87
+ return `${parentId}.${index}`;
88
+ }
89
+ /**
90
+ * Validates a memory ID and extracts its components.
91
+ *
92
+ * Returns validation result with type classification and parsed components.
93
+ * Compatible with MEMORY_PATTERNS.MEMORY_ID from memory-schema.mjs.
94
+ *
95
+ * @param id - Memory ID to validate
96
+ * @returns Validation result with parsed components
97
+ *
98
+ * @example
99
+ * validateMemId('mem-a1b2');
100
+ * // { valid: true, type: 'base', baseId: 'mem-a1b2', indices: [] }
101
+ *
102
+ * validateMemId('mem-a1b2.1.2');
103
+ * // { valid: true, type: 'hierarchical', baseId: 'mem-a1b2', indices: [1, 2] }
104
+ *
105
+ * validateMemId('invalid');
106
+ * // { valid: false, error: 'Invalid memory ID format', indices: [] }
107
+ */
108
+ export function validateMemId(id) {
109
+ // Quick validation using regex
110
+ if (!MEM_ID_PATTERNS.HIERARCHICAL_ID.test(id)) {
111
+ return {
112
+ valid: false,
113
+ error: ERROR_MESSAGES.INVALID_ID,
114
+ indices: [],
115
+ };
116
+ }
117
+ // Check if it's a base ID (no dots after the prefix)
118
+ if (MEM_ID_PATTERNS.BASE_ID.test(id)) {
119
+ return {
120
+ valid: true,
121
+ type: 'base',
122
+ baseId: id,
123
+ indices: [],
124
+ };
125
+ }
126
+ // It's a hierarchical ID - parse components
127
+ const parts = id.split('.');
128
+ const baseId = parts[0];
129
+ const indices = parts.slice(1).map((part) => parseInt(part, 10));
130
+ return {
131
+ valid: true,
132
+ type: 'hierarchical',
133
+ baseId,
134
+ indices,
135
+ };
136
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mem-id.js","sourceRoot":"","sources":["../src/mem-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;GAEG;AACH,MAAM,aAAa,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B;;;OAGG;IACH,OAAO,EAAE,mBAAmB;IAE5B;;;;OAIG;IACH,eAAe,EAAE,mCAAmC;CACrD,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAG;IACrB,UAAU,EAAE,0BAA0B;IACtC,eAAe,EAAE,+BAA+B;IAChD,aAAa,EAAE,kCAAkC;CAClD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,OAAO;IACnC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEzE,wCAAwC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAEhD,OAAO,GAAG,aAAa,GAAG,MAAM,EAAE,CAAC;AACrC,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAQ,EAAE,KAAK;IACpD,qBAAqB;IACrB,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,GAAG,cAAc,CAAC,eAAe,KAAK,QAAQ,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,qCAAqC;IACrC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,GAAG,cAAc,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,OAAO,GAAG,QAAQ,IAAI,KAAK,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE;IAC9B,+BAA+B;IAC/B,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9C,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,cAAc,CAAC,UAAU;YAChC,OAAO,EAAE,EAAE;SACZ,CAAC;IACJ,CAAC;IAED,qDAAqD;IACrD,IAAI,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,KAAK,EAAE,IAAI;YACX,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,EAAE;SACZ,CAAC;IACJ,CAAC;IAED,4CAA4C;IAC5C,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAEjE,OAAO;QACL,KAAK,EAAE,IAAI;QACX,IAAI,EAAE,cAAc;QACpB,MAAM;QACN,OAAO;KACR,CAAC;AACJ,CAAC"}