@lumenflow/memory 2.2.2 → 2.3.2
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 +332 -0
- package/dist/decay/access-tracking.js +171 -0
- package/dist/decay/archival.js +164 -0
- package/dist/decay/scoring.js +143 -0
- package/dist/index.js +10 -0
- package/dist/mem-checkpoint-core.js +3 -3
- package/dist/mem-cleanup-core.js +38 -8
- package/dist/mem-context-core.js +347 -0
- package/dist/mem-create-core.js +4 -4
- package/dist/mem-delete-core.js +277 -0
- package/dist/mem-id.js +4 -4
- package/dist/mem-index-core.js +307 -0
- package/dist/mem-init-core.js +3 -3
- package/dist/mem-profile-core.js +184 -0
- package/dist/mem-promote-core.js +233 -0
- package/dist/mem-ready-core.js +2 -2
- package/dist/mem-signal-core.js +3 -3
- package/dist/mem-start-core.js +3 -3
- package/dist/mem-summarize-core.js +2 -2
- package/dist/mem-triage-core.js +5 -7
- package/dist/memory-schema.js +1 -1
- package/dist/memory-store.js +114 -53
- package/dist/signal-cleanup-core.js +355 -0
- package/package.json +4 -2
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Profile Core (WU-1237)
|
|
3
|
+
*
|
|
4
|
+
* Generates project knowledge profiles for agent context injection.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Filters to lifecycle=project nodes only
|
|
8
|
+
* - Configurable limit (default N=20)
|
|
9
|
+
* - Tag-based filtering
|
|
10
|
+
* - Output format compatible with mem:context
|
|
11
|
+
* - Deterministic ordering by recency
|
|
12
|
+
*
|
|
13
|
+
* @see {@link packages/@lumenflow/cli/src/mem-profile.ts} - CLI wrapper
|
|
14
|
+
* @see {@link packages/@lumenflow/memory/__tests__/mem-profile-core.test.ts} - Tests
|
|
15
|
+
*/
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { loadMemory } from './memory-store.js';
|
|
18
|
+
import { LUMENFLOW_MEMORY_PATHS } from './paths.js';
|
|
19
|
+
/**
|
|
20
|
+
* Default maximum number of nodes to include in profile
|
|
21
|
+
*/
|
|
22
|
+
export const DEFAULT_PROFILE_LIMIT = 20;
|
|
23
|
+
/**
|
|
24
|
+
* Section header for profile output
|
|
25
|
+
*/
|
|
26
|
+
const PROFILE_SECTION_HEADER = '## Project Profile';
|
|
27
|
+
/**
|
|
28
|
+
* Comparator for sorting nodes by recency (most recent first).
|
|
29
|
+
* Uses created_at as primary sort, id as secondary for stability.
|
|
30
|
+
*
|
|
31
|
+
* @param a - First node
|
|
32
|
+
* @param b - Second node
|
|
33
|
+
* @returns Comparison result
|
|
34
|
+
*/
|
|
35
|
+
function compareByRecency(a, b) {
|
|
36
|
+
const aTime = new Date(a.created_at).getTime();
|
|
37
|
+
const bTime = new Date(b.created_at).getTime();
|
|
38
|
+
// Most recent first
|
|
39
|
+
if (aTime !== bTime) {
|
|
40
|
+
return bTime - aTime;
|
|
41
|
+
}
|
|
42
|
+
// Stable sort by ID for identical timestamps
|
|
43
|
+
return a.id.localeCompare(b.id);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Filters nodes by lifecycle=project
|
|
47
|
+
*
|
|
48
|
+
* @param nodes - All nodes
|
|
49
|
+
* @returns Only project-level nodes
|
|
50
|
+
*/
|
|
51
|
+
function filterProjectNodes(nodes) {
|
|
52
|
+
return nodes.filter((node) => node.lifecycle === 'project');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Filters nodes by tag
|
|
56
|
+
*
|
|
57
|
+
* @param nodes - Nodes to filter
|
|
58
|
+
* @param tag - Tag to filter by
|
|
59
|
+
* @returns Nodes with the specified tag
|
|
60
|
+
*/
|
|
61
|
+
function filterByTag(nodes, tag) {
|
|
62
|
+
return nodes.filter((node) => node.tags?.includes(tag));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Formats a single node for profile output
|
|
66
|
+
*
|
|
67
|
+
* @param node - Node to format
|
|
68
|
+
* @returns Formatted line
|
|
69
|
+
*/
|
|
70
|
+
function formatNode(node) {
|
|
71
|
+
const timestamp = new Date(node.created_at).toISOString().split('T')[0];
|
|
72
|
+
return `- [${node.id}] (${timestamp}): ${node.content}`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Formats all nodes into a profile block
|
|
76
|
+
*
|
|
77
|
+
* @param nodes - Nodes to format
|
|
78
|
+
* @returns Formatted profile block
|
|
79
|
+
*/
|
|
80
|
+
function formatProfileBlock(nodes) {
|
|
81
|
+
if (nodes.length === 0) {
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
const lines = [PROFILE_SECTION_HEADER, ''];
|
|
85
|
+
for (const node of nodes) {
|
|
86
|
+
lines.push(formatNode(node));
|
|
87
|
+
}
|
|
88
|
+
lines.push('');
|
|
89
|
+
return lines.join('\n');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Calculates tag statistics for nodes
|
|
93
|
+
*
|
|
94
|
+
* @param nodes - Nodes to analyze
|
|
95
|
+
* @returns Tag counts
|
|
96
|
+
*/
|
|
97
|
+
function calculateTagStats(nodes) {
|
|
98
|
+
const tagCounts = {};
|
|
99
|
+
for (const node of nodes) {
|
|
100
|
+
if (node.tags) {
|
|
101
|
+
for (const tag of node.tags) {
|
|
102
|
+
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return tagCounts;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Generates a project knowledge profile for context injection.
|
|
110
|
+
*
|
|
111
|
+
* Filters to project-level nodes, applies tag filter if specified,
|
|
112
|
+
* sorts by recency, and limits to the configured maximum.
|
|
113
|
+
*
|
|
114
|
+
* Output is formatted for integration with mem:context.
|
|
115
|
+
*
|
|
116
|
+
* @param baseDir - Base directory containing .lumenflow/memory
|
|
117
|
+
* @param options - Profile generation options
|
|
118
|
+
* @returns Result with nodes and formatted profile block
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* // Get top 20 project memories
|
|
122
|
+
* const result = await generateProfile('/path/to/project');
|
|
123
|
+
* console.log(result.profileBlock);
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* // Get top 10 decisions
|
|
127
|
+
* const result = await generateProfile('/path/to/project', {
|
|
128
|
+
* limit: 10,
|
|
129
|
+
* tag: 'decision',
|
|
130
|
+
* });
|
|
131
|
+
*/
|
|
132
|
+
export async function generateProfile(baseDir, options = {}) {
|
|
133
|
+
const { limit = DEFAULT_PROFILE_LIMIT, tag } = options;
|
|
134
|
+
const memoryDir = path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR);
|
|
135
|
+
// Load all memory nodes
|
|
136
|
+
let allNodes = [];
|
|
137
|
+
try {
|
|
138
|
+
const memory = await loadMemory(memoryDir);
|
|
139
|
+
allNodes = memory.nodes;
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// If memory directory doesn't exist or is empty, return empty result
|
|
143
|
+
const err = error;
|
|
144
|
+
if (err.code === 'ENOENT') {
|
|
145
|
+
return {
|
|
146
|
+
success: true,
|
|
147
|
+
nodes: [],
|
|
148
|
+
profileBlock: '',
|
|
149
|
+
stats: {
|
|
150
|
+
totalProjectNodes: 0,
|
|
151
|
+
includedNodes: 0,
|
|
152
|
+
byTag: {},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
// Filter to project lifecycle only
|
|
159
|
+
let projectNodes = filterProjectNodes(allNodes);
|
|
160
|
+
// Store total before any additional filtering
|
|
161
|
+
const totalProjectNodes = projectNodes.length;
|
|
162
|
+
// Apply tag filter if specified
|
|
163
|
+
if (tag) {
|
|
164
|
+
projectNodes = filterByTag(projectNodes, tag);
|
|
165
|
+
}
|
|
166
|
+
// Sort by recency (most recent first)
|
|
167
|
+
const sortedNodes = [...projectNodes].sort(compareByRecency);
|
|
168
|
+
// Apply limit
|
|
169
|
+
const limitedNodes = sortedNodes.slice(0, limit);
|
|
170
|
+
// Calculate statistics
|
|
171
|
+
const stats = {
|
|
172
|
+
totalProjectNodes,
|
|
173
|
+
includedNodes: limitedNodes.length,
|
|
174
|
+
byTag: calculateTagStats(limitedNodes),
|
|
175
|
+
};
|
|
176
|
+
// Format the profile block
|
|
177
|
+
const profileBlock = formatProfileBlock(limitedNodes);
|
|
178
|
+
return {
|
|
179
|
+
success: true,
|
|
180
|
+
nodes: limitedNodes,
|
|
181
|
+
profileBlock,
|
|
182
|
+
stats,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Promote Core (WU-1237)
|
|
3
|
+
*
|
|
4
|
+
* Promotes session/WU learnings into project-level knowledge nodes.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Promote individual nodes to project lifecycle
|
|
8
|
+
* - Promote all summaries from a WU
|
|
9
|
+
* - Enforced taxonomy tags (decision, convention, pattern, etc.)
|
|
10
|
+
* - Creates discovered_from relationships for provenance
|
|
11
|
+
* - Dry-run mode for preview without writes
|
|
12
|
+
*
|
|
13
|
+
* @see {@link packages/@lumenflow/cli/src/mem-promote.ts} - CLI wrapper
|
|
14
|
+
* @see {@link packages/@lumenflow/memory/__tests__/mem-promote-core.test.ts} - Tests
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs/promises';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { loadMemory, appendNode } from './memory-store.js';
|
|
19
|
+
import { MEMORY_PATTERNS } from './memory-schema.js';
|
|
20
|
+
import { generateMemId } from './mem-id.js';
|
|
21
|
+
import { LUMENFLOW_MEMORY_PATHS } from './paths.js';
|
|
22
|
+
/**
|
|
23
|
+
* Relationships file name
|
|
24
|
+
*/
|
|
25
|
+
const RELATIONSHIPS_FILE_NAME = 'relationships.jsonl';
|
|
26
|
+
/**
|
|
27
|
+
* Allowed tags for promoted nodes.
|
|
28
|
+
* These form the project knowledge taxonomy.
|
|
29
|
+
*/
|
|
30
|
+
export const ALLOWED_PROMOTION_TAGS = [
|
|
31
|
+
'decision',
|
|
32
|
+
'convention',
|
|
33
|
+
'pattern',
|
|
34
|
+
'pitfall',
|
|
35
|
+
'interface',
|
|
36
|
+
'invariant',
|
|
37
|
+
'faq',
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Error messages for validation
|
|
41
|
+
*/
|
|
42
|
+
const ERROR_MESSAGES = {
|
|
43
|
+
NODE_NOT_FOUND: 'Node not found',
|
|
44
|
+
ALREADY_PROJECT: 'Node is already at project lifecycle',
|
|
45
|
+
INVALID_TAG: `Tag must be one of: ${ALLOWED_PROMOTION_TAGS.join(', ')}`,
|
|
46
|
+
INVALID_WU_ID: 'Invalid WU ID format. Expected pattern: WU-XXX (e.g., WU-1234)',
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Validates the promotion tag against allowed taxonomy
|
|
50
|
+
*
|
|
51
|
+
* @param tag - Tag to validate
|
|
52
|
+
* @throws If tag is not in ALLOWED_PROMOTION_TAGS
|
|
53
|
+
*/
|
|
54
|
+
function validateTag(tag) {
|
|
55
|
+
if (!ALLOWED_PROMOTION_TAGS.includes(tag)) {
|
|
56
|
+
throw new Error(ERROR_MESSAGES.INVALID_TAG);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Validates WU ID format
|
|
61
|
+
*
|
|
62
|
+
* @param wuId - WU ID to validate
|
|
63
|
+
* @throws If WU ID is invalid
|
|
64
|
+
*/
|
|
65
|
+
function validateWuId(wuId) {
|
|
66
|
+
if (!MEMORY_PATTERNS.WU_ID.test(wuId)) {
|
|
67
|
+
throw new Error(ERROR_MESSAGES.INVALID_WU_ID);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Appends a relationship to the relationships.jsonl file
|
|
72
|
+
*
|
|
73
|
+
* @param memoryDir - Memory directory path
|
|
74
|
+
* @param relationship - Relationship to append
|
|
75
|
+
*/
|
|
76
|
+
async function appendRelationship(memoryDir, relationship) {
|
|
77
|
+
const filePath = path.join(memoryDir, RELATIONSHIPS_FILE_NAME);
|
|
78
|
+
const line = JSON.stringify(relationship) + '\n';
|
|
79
|
+
await fs.appendFile(filePath, line, { encoding: 'utf-8' });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Creates a promoted node from a source node
|
|
83
|
+
*
|
|
84
|
+
* @param sourceNode - Source node to promote
|
|
85
|
+
* @param tag - Taxonomy tag to apply
|
|
86
|
+
* @returns New node with project lifecycle
|
|
87
|
+
*/
|
|
88
|
+
function createPromotedNode(sourceNode, tag) {
|
|
89
|
+
const now = new Date().toISOString();
|
|
90
|
+
// Generate new ID based on content + timestamp for uniqueness
|
|
91
|
+
const newId = generateMemId(`${sourceNode.content}${now}`);
|
|
92
|
+
// Build tags array, ensuring the promotion tag is included
|
|
93
|
+
const existingTags = sourceNode.tags ?? [];
|
|
94
|
+
const tags = existingTags.includes(tag) ? existingTags : [...existingTags, tag];
|
|
95
|
+
return {
|
|
96
|
+
id: newId,
|
|
97
|
+
type: sourceNode.type,
|
|
98
|
+
lifecycle: 'project',
|
|
99
|
+
content: sourceNode.content,
|
|
100
|
+
created_at: now,
|
|
101
|
+
wu_id: sourceNode.wu_id,
|
|
102
|
+
session_id: sourceNode.session_id,
|
|
103
|
+
metadata: {
|
|
104
|
+
...sourceNode.metadata,
|
|
105
|
+
promoted_from: sourceNode.id,
|
|
106
|
+
promoted_at: now,
|
|
107
|
+
},
|
|
108
|
+
tags,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Creates a discovered_from relationship between promoted and source nodes
|
|
113
|
+
*
|
|
114
|
+
* @param promotedNodeId - ID of the promoted node
|
|
115
|
+
* @param sourceNodeId - ID of the source node
|
|
116
|
+
* @returns Relationship object
|
|
117
|
+
*/
|
|
118
|
+
function createRelationship(promotedNodeId, sourceNodeId) {
|
|
119
|
+
return {
|
|
120
|
+
from_id: promotedNodeId,
|
|
121
|
+
to_id: sourceNodeId,
|
|
122
|
+
type: 'discovered_from',
|
|
123
|
+
created_at: new Date().toISOString(),
|
|
124
|
+
metadata: {},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Promotes a single node to project lifecycle.
|
|
129
|
+
*
|
|
130
|
+
* Creates a new project-level node with the same content and a
|
|
131
|
+
* discovered_from relationship back to the source node.
|
|
132
|
+
*
|
|
133
|
+
* @param baseDir - Base directory containing .lumenflow/memory
|
|
134
|
+
* @param options - Promotion options
|
|
135
|
+
* @returns Result with the promoted node
|
|
136
|
+
* @throws If node not found, already project level, or invalid tag
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* const result = await promoteNode('/path/to/project', {
|
|
140
|
+
* nodeId: 'mem-abc1',
|
|
141
|
+
* tag: 'pattern',
|
|
142
|
+
* });
|
|
143
|
+
* console.log(`Promoted to ${result.promotedNode.id}`);
|
|
144
|
+
*/
|
|
145
|
+
export async function promoteNode(baseDir, options) {
|
|
146
|
+
const { nodeId, tag, dryRun = false } = options;
|
|
147
|
+
// Validate tag
|
|
148
|
+
validateTag(tag);
|
|
149
|
+
const memoryDir = path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR);
|
|
150
|
+
// Load memory and find the source node
|
|
151
|
+
const memory = await loadMemory(memoryDir);
|
|
152
|
+
const sourceNode = memory.byId.get(nodeId);
|
|
153
|
+
if (!sourceNode) {
|
|
154
|
+
throw new Error(`${ERROR_MESSAGES.NODE_NOT_FOUND}: ${nodeId}`);
|
|
155
|
+
}
|
|
156
|
+
if (sourceNode.lifecycle === 'project') {
|
|
157
|
+
throw new Error(`${ERROR_MESSAGES.ALREADY_PROJECT}: ${nodeId}`);
|
|
158
|
+
}
|
|
159
|
+
// Create the promoted node
|
|
160
|
+
const promotedNode = createPromotedNode(sourceNode, tag);
|
|
161
|
+
// Create the relationship
|
|
162
|
+
const relationship = createRelationship(promotedNode.id, sourceNode.id);
|
|
163
|
+
// Write if not dry run
|
|
164
|
+
if (!dryRun) {
|
|
165
|
+
await appendNode(memoryDir, promotedNode);
|
|
166
|
+
await appendRelationship(memoryDir, relationship);
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
promotedNode,
|
|
171
|
+
relationship,
|
|
172
|
+
dryRun,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Promotes all summaries from a WU to project lifecycle.
|
|
177
|
+
*
|
|
178
|
+
* Finds all summary-type nodes linked to the WU and promotes each one
|
|
179
|
+
* to project level with discovered_from relationships.
|
|
180
|
+
*
|
|
181
|
+
* @param baseDir - Base directory containing .lumenflow/memory
|
|
182
|
+
* @param options - Promotion options
|
|
183
|
+
* @returns Result with all promoted nodes
|
|
184
|
+
* @throws If WU ID is invalid or tag is invalid
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* const result = await promoteFromWu('/path/to/project', {
|
|
188
|
+
* wuId: 'WU-1234',
|
|
189
|
+
* tag: 'decision',
|
|
190
|
+
* });
|
|
191
|
+
* console.log(`Promoted ${result.promotedNodes.length} summaries`);
|
|
192
|
+
*/
|
|
193
|
+
export async function promoteFromWu(baseDir, options) {
|
|
194
|
+
const { wuId, tag, dryRun = false } = options;
|
|
195
|
+
// Validate inputs
|
|
196
|
+
validateWuId(wuId);
|
|
197
|
+
validateTag(tag);
|
|
198
|
+
const memoryDir = path.join(baseDir, LUMENFLOW_MEMORY_PATHS.MEMORY_DIR);
|
|
199
|
+
// Load memory
|
|
200
|
+
const memory = await loadMemory(memoryDir);
|
|
201
|
+
// Find all summaries for this WU that aren't already project level
|
|
202
|
+
const wuNodes = memory.byWu.get(wuId) ?? [];
|
|
203
|
+
const summaries = wuNodes.filter((node) => node.type === 'summary' && node.lifecycle !== 'project');
|
|
204
|
+
// If no summaries, return empty result
|
|
205
|
+
if (summaries.length === 0) {
|
|
206
|
+
return {
|
|
207
|
+
success: true,
|
|
208
|
+
promotedNodes: [],
|
|
209
|
+
relationships: [],
|
|
210
|
+
dryRun,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const promotedNodes = [];
|
|
214
|
+
const relationships = [];
|
|
215
|
+
// Promote each summary
|
|
216
|
+
for (const summary of summaries) {
|
|
217
|
+
const promotedNode = createPromotedNode(summary, tag);
|
|
218
|
+
const relationship = createRelationship(promotedNode.id, summary.id);
|
|
219
|
+
promotedNodes.push(promotedNode);
|
|
220
|
+
relationships.push(relationship);
|
|
221
|
+
// Write if not dry run
|
|
222
|
+
if (!dryRun) {
|
|
223
|
+
await appendNode(memoryDir, promotedNode);
|
|
224
|
+
await appendRelationship(memoryDir, relationship);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
success: true,
|
|
229
|
+
promotedNodes,
|
|
230
|
+
relationships,
|
|
231
|
+
dryRun,
|
|
232
|
+
};
|
|
233
|
+
}
|
package/dist/mem-ready-core.js
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
* - Lifecycle is not `ephemeral`
|
|
17
17
|
* - Status is not `closed` (metadata.status !== 'closed')
|
|
18
18
|
*
|
|
19
|
-
* @see {@link
|
|
20
|
-
* @see {@link
|
|
19
|
+
* @see {@link packages/@lumenflow/cli/src/mem-ready.ts} - CLI implementation
|
|
20
|
+
* @see {@link packages/@lumenflow/cli/src/__tests__/mem-ready.test.ts} - Tests
|
|
21
21
|
*/
|
|
22
22
|
import fs from 'node:fs/promises';
|
|
23
23
|
import path from 'node:path';
|
package/dist/mem-signal-core.js
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* - Lane-targeted signals for cross-team communication
|
|
11
11
|
* - Read/unread tracking for mem:inbox integration
|
|
12
12
|
*
|
|
13
|
-
* @see {@link
|
|
14
|
-
* @see {@link
|
|
13
|
+
* @see {@link packages/@lumenflow/cli/src/mem-signal.ts} - CLI wrapper
|
|
14
|
+
* @see {@link packages/@lumenflow/cli/src/__tests__/mem-signal.test.ts} - Tests
|
|
15
15
|
*/
|
|
16
16
|
import { randomBytes } from 'node:crypto';
|
|
17
17
|
import fs from 'node:fs/promises';
|
|
@@ -22,7 +22,7 @@ import { LUMENFLOW_MEMORY_PATHS } from './paths.js';
|
|
|
22
22
|
*/
|
|
23
23
|
export const SIGNAL_FILE_NAME = 'signals.jsonl';
|
|
24
24
|
/**
|
|
25
|
-
* WU ID validation pattern (from memory-schema.
|
|
25
|
+
* WU ID validation pattern (from memory-schema.ts)
|
|
26
26
|
*/
|
|
27
27
|
const WU_ID_PATTERN = /^WU-\d+$/;
|
|
28
28
|
/**
|
package/dist/mem-start-core.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* - Idempotent: multiple starts create separate sessions
|
|
11
11
|
* - Auto-initializes memory layer if not present
|
|
12
12
|
*
|
|
13
|
-
* @see {@link
|
|
14
|
-
* @see {@link
|
|
15
|
-
* @see {@link
|
|
13
|
+
* @see {@link packages/@lumenflow/cli/src/mem-start.ts} - CLI wrapper
|
|
14
|
+
* @see {@link packages/@lumenflow/cli/src/__tests__/mem-start.test.ts} - Tests
|
|
15
|
+
* @see {@link packages/@lumenflow/cli/src/lib/memory-schema.ts} - Schema definitions
|
|
16
16
|
*/
|
|
17
17
|
import { randomUUID } from 'node:crypto';
|
|
18
18
|
import fs from 'node:fs/promises';
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* - Respect lifecycle TTL (ephemeral, session, wu, project)
|
|
11
11
|
* - Support dry-run mode for preview
|
|
12
12
|
*
|
|
13
|
-
* @see {@link
|
|
14
|
-
* @see {@link
|
|
13
|
+
* @see {@link packages/@lumenflow/cli/src/mem-summarize.ts} - CLI wrapper
|
|
14
|
+
* @see {@link packages/@lumenflow/cli/src/__tests__/mem-summarize.test.ts} - Tests
|
|
15
15
|
*/
|
|
16
16
|
import { loadMemory, appendNode } from './memory-store.js';
|
|
17
17
|
import { generateMemId } from './mem-id.js';
|
package/dist/mem-triage-core.js
CHANGED
|
@@ -8,18 +8,15 @@
|
|
|
8
8
|
* - Promote discovery to WU (integrates with wu:create)
|
|
9
9
|
* - Archive discovery without promotion
|
|
10
10
|
*
|
|
11
|
-
* @see {@link
|
|
12
|
-
* @see {@link
|
|
11
|
+
* @see {@link packages/@lumenflow/cli/src/mem-triage.ts} - CLI wrapper
|
|
12
|
+
* @see {@link packages/@lumenflow/cli/src/__tests__/mem-triage.test.ts} - Tests
|
|
13
13
|
*/
|
|
14
14
|
import fs from 'node:fs/promises';
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import { loadMemory, appendNode } from './memory-store.js';
|
|
17
17
|
import { validateLaneFormat } from '@lumenflow/core/lane-checker';
|
|
18
|
+
import { createWuPaths } from '@lumenflow/core/lib/wu-paths.js';
|
|
18
19
|
import { LUMENFLOW_MEMORY_PATHS } from './paths.js';
|
|
19
|
-
/**
|
|
20
|
-
* WU directory path relative to base
|
|
21
|
-
*/
|
|
22
|
-
const WU_DIR = 'docs/04-operations/tasks/wu';
|
|
23
20
|
/**
|
|
24
21
|
* Relationships file name
|
|
25
22
|
*/
|
|
@@ -244,7 +241,8 @@ export async function archiveDiscovery(baseDir, options) {
|
|
|
244
241
|
* @returns Next WU ID (e.g., 'WU-1502')
|
|
245
242
|
*/
|
|
246
243
|
async function getNextWuId(baseDir) {
|
|
247
|
-
const
|
|
244
|
+
const paths = createWuPaths({ projectRoot: baseDir });
|
|
245
|
+
const wuDir = path.join(baseDir, paths.WU_DIR());
|
|
248
246
|
let maxId = 0;
|
|
249
247
|
try {
|
|
250
248
|
const files = await fs.readdir(wuDir);
|
package/dist/memory-schema.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Lifecycles (4 levels: ephemeral, session, wu, project)
|
|
10
10
|
* - Relationships (4 types: blocks, parent_child, related, discovered_from)
|
|
11
11
|
*
|
|
12
|
-
* @see {@link
|
|
12
|
+
* @see {@link packages/@lumenflow/cli/src/lib/__tests__/memory-schema.test.ts} - Tests
|
|
13
13
|
*/
|
|
14
14
|
import { z } from 'zod';
|
|
15
15
|
/**
|