@unifiedmemory/cli 1.3.0 → 1.3.1
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/commands/record.js +35 -13
- package/lib/mcp-proxy.js +26 -0
- package/lib/memory-instructions.js +59 -49
- package/package.json +1 -1
package/commands/record.js
CHANGED
|
@@ -52,6 +52,7 @@ export async function record(summary, options = {}) {
|
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
// 5. Prepare tool arguments
|
|
55
|
+
// Note: headers (X-Org-Id, X-User-Id) are handled at HTTP level by authHeaders
|
|
55
56
|
const toolArgs = {
|
|
56
57
|
body: {
|
|
57
58
|
summary_text: summary,
|
|
@@ -59,10 +60,6 @@ export async function record(summary, options = {}) {
|
|
|
59
60
|
source: options.source || 'um-cli',
|
|
60
61
|
confidence: options.confidence ? parseFloat(options.confidence) : 0.7,
|
|
61
62
|
tags: options.tags ? options.tags.split(',') : []
|
|
62
|
-
},
|
|
63
|
-
headers: {
|
|
64
|
-
'X-Org-Id': tokenData.selectedOrg?.id || tokenData.decoded?.sub,
|
|
65
|
-
'X-User-Id': tokenData.decoded?.sub
|
|
66
63
|
}
|
|
67
64
|
};
|
|
68
65
|
|
|
@@ -89,22 +86,47 @@ export async function record(summary, options = {}) {
|
|
|
89
86
|
projectContext
|
|
90
87
|
);
|
|
91
88
|
|
|
92
|
-
// 7. Handle response
|
|
89
|
+
// 7. Handle response - validate success properly
|
|
93
90
|
if (result.content && Array.isArray(result.content)) {
|
|
94
91
|
const textContent = result.content.find(c => c.type === 'text');
|
|
95
92
|
if (textContent) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
try {
|
|
94
|
+
const response = JSON.parse(textContent.text);
|
|
95
|
+
|
|
96
|
+
// Check for error in response
|
|
97
|
+
if (result.isError || response.error || response.detail) {
|
|
98
|
+
throw new Error(response.error || response.detail || 'Note creation failed');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(chalk.green('✓ Note created successfully'));
|
|
102
|
+
console.log(chalk.gray(` Note ID: ${response.note_id || 'N/A'}`));
|
|
103
|
+
if (response.confidence) {
|
|
104
|
+
console.log(chalk.gray(` Confidence: ${response.confidence}`));
|
|
105
|
+
}
|
|
106
|
+
return response;
|
|
107
|
+
} catch (parseError) {
|
|
108
|
+
if (parseError.message.includes('Note creation failed')) {
|
|
109
|
+
throw parseError;
|
|
110
|
+
}
|
|
111
|
+
throw new Error(`Failed to parse API response: ${parseError.message}`);
|
|
101
112
|
}
|
|
102
|
-
return response;
|
|
103
113
|
}
|
|
104
114
|
}
|
|
105
115
|
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
// Check for direct note response (some backends return this directly)
|
|
117
|
+
if (result.note_id) {
|
|
118
|
+
console.log(chalk.green('✓ Note created successfully'));
|
|
119
|
+
console.log(chalk.gray(` Note ID: ${result.note_id}`));
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check for error indicators
|
|
124
|
+
if (result.error || result.detail || result.isError) {
|
|
125
|
+
throw new Error(result.error || result.detail || 'Note creation failed');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Unknown response format - fail explicitly instead of silent success
|
|
129
|
+
throw new Error('Unexpected response format from API');
|
|
108
130
|
|
|
109
131
|
} catch (error) {
|
|
110
132
|
console.error(chalk.red('✗ Failed to create note:'));
|
package/lib/mcp-proxy.js
CHANGED
|
@@ -14,6 +14,23 @@ function transformToolSchema(tool) {
|
|
|
14
14
|
// Deep clone to avoid mutations
|
|
15
15
|
const transformed = JSON.parse(JSON.stringify(tool));
|
|
16
16
|
|
|
17
|
+
// Remove headers entirely - proxy handles all headers at HTTP level
|
|
18
|
+
// AI never needs to provide X-Org-Id, X-User-Id, etc.
|
|
19
|
+
if (transformed.inputSchema?.properties?.headers) {
|
|
20
|
+
delete transformed.inputSchema.properties.headers;
|
|
21
|
+
|
|
22
|
+
// Also remove from required array if present
|
|
23
|
+
if (transformed.inputSchema.required && Array.isArray(transformed.inputSchema.required)) {
|
|
24
|
+
transformed.inputSchema.required = transformed.inputSchema.required.filter(
|
|
25
|
+
field => field !== 'headers'
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (transformed.inputSchema.required.length === 0) {
|
|
29
|
+
delete transformed.inputSchema.required;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
// Check if tool has pathParams in schema
|
|
18
35
|
if (!transformed.inputSchema?.properties?.pathParams) {
|
|
19
36
|
return transformed;
|
|
@@ -79,22 +96,31 @@ function injectContextParams(args, authContext, projectContext) {
|
|
|
79
96
|
injected.pathParams = {};
|
|
80
97
|
}
|
|
81
98
|
|
|
99
|
+
// Ensure headers object exists (gateway expects these in tool args)
|
|
100
|
+
if (!injected.headers) {
|
|
101
|
+
injected.headers = {};
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
// Inject user ID from decoded JWT
|
|
83
105
|
if (authContext?.decoded?.sub) {
|
|
84
106
|
injected.pathParams.user = authContext.decoded.sub;
|
|
107
|
+
injected.headers['X-User-Id'] = authContext.decoded.sub;
|
|
85
108
|
}
|
|
86
109
|
|
|
87
110
|
// Inject org ID (from selectedOrg or fallback to user ID for personal account)
|
|
88
111
|
if (authContext?.selectedOrg?.id) {
|
|
89
112
|
injected.pathParams.org = authContext.selectedOrg.id;
|
|
113
|
+
injected.headers['X-Org-Id'] = authContext.selectedOrg.id;
|
|
90
114
|
} else if (authContext?.decoded?.sub) {
|
|
91
115
|
// Fallback to user ID for personal account context
|
|
92
116
|
injected.pathParams.org = authContext.decoded.sub;
|
|
117
|
+
injected.headers['X-Org-Id'] = authContext.decoded.sub;
|
|
93
118
|
}
|
|
94
119
|
|
|
95
120
|
// Inject project ID from project config
|
|
96
121
|
if (projectContext?.project_id) {
|
|
97
122
|
injected.pathParams.proj = projectContext.project_id;
|
|
123
|
+
injected.headers['X-Project-Id'] = projectContext.project_id;
|
|
98
124
|
}
|
|
99
125
|
|
|
100
126
|
return injected;
|
|
@@ -1,72 +1,82 @@
|
|
|
1
1
|
// Unique marker to detect if instructions already present
|
|
2
|
-
export const MEMORY_INSTRUCTIONS_MARKER = '<!--
|
|
2
|
+
export const MEMORY_INSTRUCTIONS_MARKER = '<!-- UNIFIEDMEMORY_INSTRUCTIONS_V2 -->';
|
|
3
3
|
|
|
4
4
|
export function getMemoryInstructions() {
|
|
5
5
|
return `${MEMORY_INSTRUCTIONS_MARKER}
|
|
6
6
|
|
|
7
|
-
# UnifiedMemory
|
|
7
|
+
# UnifiedMemory Integration (REQUIRED)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This project uses UnifiedMemory for persistent knowledge management. The \`unifiedmemory\` MCP server is configured and MUST be used for all relevant queries.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Available Tools
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
**Action**: Use the \`unifiedmemory\` MCP tool to search for relevant context
|
|
15
|
-
**Usage**:
|
|
16
|
-
- Send the user's query to the search tool
|
|
17
|
-
- Review the search results for relevant prior work, decisions, or context
|
|
18
|
-
- Incorporate findings into your response
|
|
13
|
+
The \`unifiedmemory\` MCP server provides these tools (call them like any MCP tool):
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
| Tool | Purpose | Required Parameters |
|
|
16
|
+
|------|---------|---------------------|
|
|
17
|
+
| \`search_notes\` | **Primary tool** - Search project knowledge base | \`query\` (search text) |
|
|
18
|
+
| \`create_note\` | Record decisions, plans, or completions | \`summary_text\`, \`topic\` |
|
|
19
|
+
| \`list_notes\` | List all notes in current project | None |
|
|
20
|
+
| \`get_note\` | Retrieve a specific note by ID | \`note_id\` |
|
|
21
|
+
| \`list_topics\` | List available topics/categories | None |
|
|
22
|
+
| \`create_topic\` | Create a new topic for organization | \`name\` |
|
|
23
|
+
| \`list_projects\` | List available projects | None |
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
All tools automatically use the correct project context - no additional configuration needed.
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
**Action**: Record the plan as a note using the \`unifiedmemory\` tool
|
|
32
|
-
**Format**:
|
|
33
|
-
- Include: objective, approach, key steps, important decisions
|
|
34
|
-
- Keep concise but informative
|
|
35
|
-
- Limit to under 1000 characters for optimal indexing
|
|
27
|
+
## MANDATORY: Search Before Implementing
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
**REQUIREMENT**: Before responding to ANY coding task, feature request, or implementation question, you MUST:
|
|
30
|
+
|
|
31
|
+
1. Call the \`search_notes\` tool with the user's query or relevant keywords
|
|
32
|
+
2. Review returned results for prior decisions, patterns, or context
|
|
33
|
+
3. Incorporate relevant findings into your response
|
|
34
|
+
4. Mention what context you found (or didn't find) in your response
|
|
35
|
+
|
|
36
|
+
### When to Search
|
|
42
37
|
|
|
43
|
-
|
|
38
|
+
You MUST search before:
|
|
39
|
+
- Implementing any feature (search for existing patterns)
|
|
40
|
+
- Fixing any bug (search for related issues)
|
|
41
|
+
- Answering architecture questions (search for design decisions)
|
|
42
|
+
- Modifying any component (search for existing conventions)
|
|
43
|
+
- ANY coding task, no exceptions
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
**Action**: Create a summary note using the \`unifiedmemory\` tool
|
|
47
|
-
**Format**:
|
|
48
|
-
- Summarize what was accomplished and why
|
|
49
|
-
- Include key technical decisions or gotchas
|
|
50
|
-
- **IMPORTANT**: Limit to under 1000 characters
|
|
51
|
-
- Focus on outcomes, not detailed code
|
|
45
|
+
### Example Workflow
|
|
52
46
|
|
|
53
|
-
Example:
|
|
54
47
|
\`\`\`
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
User: "Add authentication to the API"
|
|
49
|
+
|
|
50
|
+
Step 1: Call search_notes with query "authentication API security"
|
|
51
|
+
Step 2: Review results - found: "Decision: Use JWT with refresh tokens"
|
|
52
|
+
Step 3: Implement following the established pattern
|
|
53
|
+
Step 4: Respond: "Based on prior decisions in UnifiedMemory, I'll use JWT..."
|
|
57
54
|
\`\`\`
|
|
58
55
|
|
|
59
|
-
##
|
|
56
|
+
## Do NOT Skip These Tools
|
|
57
|
+
|
|
58
|
+
- ❌ NEVER start implementing without calling \`search_notes\` first
|
|
59
|
+
- ❌ NEVER assume patterns without checking the knowledge base
|
|
60
|
+
- ❌ NEVER skip search for "simple" tasks - context always helps
|
|
61
|
+
- ✅ ALWAYS search even if you think you know the answer
|
|
62
|
+
- ✅ ALWAYS mention search results (or lack thereof) in your response
|
|
63
|
+
- ✅ ALWAYS use these tools when they can provide relevant context
|
|
64
|
+
|
|
65
|
+
## Recording Knowledge with create_note
|
|
66
|
+
|
|
67
|
+
After completing significant work, use \`create_note\` to record:
|
|
68
|
+
- Implementation decisions and rationale
|
|
69
|
+
- Completed features or fixes
|
|
70
|
+
- Architectural patterns established
|
|
71
|
+
- Gotchas or lessons learned
|
|
72
|
+
|
|
73
|
+
Keep notes concise (under 1000 characters) and focus on the "why" not the "what".
|
|
74
|
+
|
|
75
|
+
## Additional Tools
|
|
60
76
|
|
|
61
|
-
|
|
62
|
-
- ✅ **DO**: Record plans and completions automatically
|
|
63
|
-
- ✅ **DO**: Keep notes under 1000 characters
|
|
64
|
-
- ✅ **DO**: Focus on decisions, outcomes, and context
|
|
65
|
-
- ❌ **DON'T**: Record trivial tasks (typo fixes, minor edits)
|
|
66
|
-
- ❌ **DON'T**: Include full code in notes
|
|
67
|
-
- ❌ **DON'T**: Duplicate information already in git commits
|
|
77
|
+
Use \`list_topics\` to understand project organization, \`list_notes\` to browse available knowledge, and \`get_note\` to retrieve full details of relevant notes found via search.
|
|
68
78
|
|
|
69
79
|
---
|
|
70
|
-
*
|
|
80
|
+
*UnifiedMemory CLI - Knowledge that persists*
|
|
71
81
|
`;
|
|
72
82
|
}
|