@unifiedmemory/cli 1.3.14 → 1.3.16
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/.env.example +7 -7
- package/lib/config.js +5 -3
- package/lib/mcp-proxy.js +3 -1
- package/lib/memory-instructions.js +48 -54
- package/package.json +1 -1
- package/tests/unit/config.test.js +10 -5
- package/tests/unit/mcp-proxy.test.js +13 -2
package/.env.example
CHANGED
|
@@ -17,17 +17,17 @@
|
|
|
17
17
|
# ============================================
|
|
18
18
|
# Clerk OAuth Configuration
|
|
19
19
|
# ============================================
|
|
20
|
-
# Production defaults are included in the CLI
|
|
21
|
-
#
|
|
22
|
-
# CLERK_CLIENT_ID=
|
|
23
|
-
# CLERK_DOMAIN=
|
|
20
|
+
# Production defaults are included in the CLI.
|
|
21
|
+
# For local development, uncomment and use dev Clerk values:
|
|
22
|
+
# CLERK_CLIENT_ID=nULlnomaKB9rRGP2
|
|
23
|
+
# CLERK_DOMAIN=clear-caiman-45.clerk.accounts.dev
|
|
24
24
|
|
|
25
25
|
# ============================================
|
|
26
26
|
# API Configuration
|
|
27
27
|
# ============================================
|
|
28
|
-
# Production default is included in the CLI
|
|
29
|
-
#
|
|
30
|
-
# API_ENDPOINT=https://
|
|
28
|
+
# Production default is included in the CLI.
|
|
29
|
+
# For local development, uncomment and use dev gateway:
|
|
30
|
+
# API_ENDPOINT=https://rose-asp-main-1c0b114.d2.zuplo.dev
|
|
31
31
|
|
|
32
32
|
# ============================================
|
|
33
33
|
# OAuth Flow Configuration
|
package/lib/config.js
CHANGED
|
@@ -10,12 +10,14 @@ dotenvConfig({ path: join(__dirname, '..', '.env') });
|
|
|
10
10
|
|
|
11
11
|
export const config = {
|
|
12
12
|
// Clerk OAuth configuration (production defaults, can be overridden via env vars)
|
|
13
|
-
|
|
13
|
+
// TODO: Replace clerkClientId with production Clerk OAuth client ID once created
|
|
14
|
+
clerkClientId: process.env.CLERK_CLIENT_ID || 'bNpTWw0hP3V7ueqN',
|
|
14
15
|
clerkClientSecret: process.env.CLERK_CLIENT_SECRET, // Optional for PKCE flow
|
|
15
|
-
clerkDomain: process.env.CLERK_DOMAIN || '
|
|
16
|
+
clerkDomain: process.env.CLERK_DOMAIN || 'clerk.unifiedmemory.ai',
|
|
16
17
|
|
|
17
18
|
// API configuration (production default, can be overridden via env var)
|
|
18
|
-
|
|
19
|
+
// TODO: Replace with production Zuplo gateway URL once deployed
|
|
20
|
+
apiEndpoint: process.env.API_ENDPOINT || 'https://api.unifiedmemory.ai',
|
|
19
21
|
|
|
20
22
|
// OAuth flow configuration (localhost defaults for callback server)
|
|
21
23
|
redirectUri: process.env.REDIRECT_URI || 'http://localhost:3333/callback',
|
package/lib/mcp-proxy.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* MCP Proxy Client - Forwards MCP requests to the gateway HTTP endpoint
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import { config } from './config.js';
|
|
6
|
+
|
|
7
|
+
const GATEWAY_MCP_URL = `${config.apiEndpoint}/mcp`;
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Transform tool schema to hide context parameters (org, proj, user)
|
|
@@ -1,82 +1,76 @@
|
|
|
1
1
|
// Unique marker to detect if instructions already present
|
|
2
|
-
export const MEMORY_INSTRUCTIONS_MARKER = '<!--
|
|
2
|
+
export const MEMORY_INSTRUCTIONS_MARKER = '<!-- UNIFIEDMEMORY_INSTRUCTIONS_V3 -->';
|
|
3
3
|
|
|
4
4
|
export function getMemoryInstructions() {
|
|
5
5
|
return `${MEMORY_INSTRUCTIONS_MARKER}
|
|
6
6
|
|
|
7
|
-
#
|
|
7
|
+
# STOP - Read Before ANY Code Task
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**MANDATORY**: Call \`search_notes\` BEFORE writing any code. Always. No exceptions.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Pre-Implementation Checklist
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Before ANY coding task, you MUST:
|
|
14
|
+
- [ ] Call \`search_notes\` with relevant keywords
|
|
15
|
+
- [ ] Review results for existing patterns/decisions
|
|
16
|
+
- [ ] Mention what you found (or didn't) in your response
|
|
17
|
+
- [ ] THEN start implementation
|
|
14
18
|
|
|
15
|
-
|
|
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 |
|
|
19
|
+
---
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
## WRONG
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
\`\`\`
|
|
24
|
+
User: "Create a calculator app"
|
|
25
|
+
Assistant: [immediately writes code] ← NEVER DO THIS
|
|
26
|
+
\`\`\`
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
## CORRECT
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
\`\`\`
|
|
31
|
+
User: "Create a calculator app"
|
|
32
|
+
Assistant: [calls search_notes first]
|
|
33
|
+
Assistant: "No relevant patterns found in UnifiedMemory. Proceeding..."
|
|
34
|
+
Assistant: [writes code]
|
|
35
|
+
\`\`\`
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
---
|
|
37
38
|
|
|
38
|
-
|
|
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
|
|
39
|
+
## When to Search (ALWAYS)
|
|
44
40
|
|
|
45
|
-
|
|
41
|
+
- Before implementing ANY feature ← search first
|
|
42
|
+
- Before fixing ANY bug ← search first
|
|
43
|
+
- Before answering architecture questions ← search first
|
|
44
|
+
- Before modifying ANY component ← search first
|
|
45
|
+
- For "simple" tasks ← search first (context always helps)
|
|
46
46
|
|
|
47
|
-
|
|
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..."
|
|
54
|
-
\`\`\`
|
|
47
|
+
---
|
|
55
48
|
|
|
56
|
-
##
|
|
49
|
+
## MCP Tool Reference
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
| Tool | Purpose | Parameters |
|
|
52
|
+
|------|---------|------------|
|
|
53
|
+
| \`search_notes\` | **Search knowledge base** | \`body: {q: "search text"}\` |
|
|
54
|
+
| \`create_note\` | Record decisions/completions | \`body: {summary_text: "...", topic: "..."}\` |
|
|
55
|
+
| \`list_notes\` | Browse all notes | None |
|
|
56
|
+
| \`get_note\` | Get note by ID | \`body: {note_id: "..."}\` |
|
|
57
|
+
| \`list_topics\` | List categories | None |
|
|
58
|
+
| \`create_topic\` | Create category | \`body: {name: "..."}\` |
|
|
64
59
|
|
|
65
|
-
|
|
60
|
+
Project context is automatic - no extra config needed.
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
- Implementation decisions and rationale
|
|
69
|
-
- Completed features or fixes
|
|
70
|
-
- Architectural patterns established
|
|
71
|
-
- Gotchas or lessons learned
|
|
62
|
+
---
|
|
72
63
|
|
|
73
|
-
|
|
64
|
+
## After Completing Work
|
|
74
65
|
|
|
75
|
-
|
|
66
|
+
Use \`create_note\` to record:
|
|
67
|
+
- Decisions and rationale (the "why")
|
|
68
|
+
- Patterns established
|
|
69
|
+
- Gotchas or lessons learned
|
|
76
70
|
|
|
77
|
-
|
|
71
|
+
Keep notes under 1000 chars. Focus on "why" not "what".
|
|
78
72
|
|
|
79
73
|
---
|
|
80
|
-
*UnifiedMemory
|
|
74
|
+
*UnifiedMemory - \`search_notes\` FIRST, code SECOND*
|
|
81
75
|
`;
|
|
82
76
|
}
|
package/package.json
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
10
|
|
|
11
|
+
// Mock dotenv so the local .env file doesn't interfere with unit tests
|
|
12
|
+
vi.mock('dotenv', () => ({
|
|
13
|
+
config: vi.fn()
|
|
14
|
+
}));
|
|
15
|
+
|
|
11
16
|
describe('config', () => {
|
|
12
17
|
// Store original env vars
|
|
13
18
|
const originalEnv = { ...process.env };
|
|
@@ -35,9 +40,9 @@ describe('config', () => {
|
|
|
35
40
|
// Fresh import to get defaults
|
|
36
41
|
const { config } = await import('../../lib/config.js');
|
|
37
42
|
|
|
38
|
-
expect(config.clerkClientId).toBe('
|
|
39
|
-
expect(config.clerkDomain).toBe('
|
|
40
|
-
expect(config.apiEndpoint).toBe('https://
|
|
43
|
+
expect(config.clerkClientId).toBe('bNpTWw0hP3V7ueqN');
|
|
44
|
+
expect(config.clerkDomain).toBe('clerk.unifiedmemory.ai');
|
|
45
|
+
expect(config.apiEndpoint).toBe('https://api.unifiedmemory.ai');
|
|
41
46
|
expect(config.redirectUri).toBe('http://localhost:3333/callback');
|
|
42
47
|
expect(config.port).toBe(3333);
|
|
43
48
|
});
|
|
@@ -123,7 +128,7 @@ describe('config', () => {
|
|
|
123
128
|
const { config, validateConfig } = await import('../../lib/config.js');
|
|
124
129
|
|
|
125
130
|
// Empty string is falsy, so it falls back to default
|
|
126
|
-
expect(config.clerkDomain).toBe('
|
|
131
|
+
expect(config.clerkDomain).toBe('clerk.unifiedmemory.ai');
|
|
127
132
|
expect(validateConfig()).toBe(true);
|
|
128
133
|
});
|
|
129
134
|
|
|
@@ -142,7 +147,7 @@ describe('config', () => {
|
|
|
142
147
|
const { config, validateConfig } = await import('../../lib/config.js');
|
|
143
148
|
|
|
144
149
|
// Empty string is falsy, so it falls back to default
|
|
145
|
-
expect(config.clerkClientId).toBe('
|
|
150
|
+
expect(config.clerkClientId).toBe('bNpTWw0hP3V7ueqN');
|
|
146
151
|
expect(validateConfig()).toBe(true);
|
|
147
152
|
});
|
|
148
153
|
|
|
@@ -8,11 +8,17 @@
|
|
|
8
8
|
* are tested indirectly through the exported functions.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { describe, it, expect, vi, beforeAll, afterAll, afterEach } from 'vitest';
|
|
11
|
+
import { describe, it, expect, vi, beforeAll, beforeEach, afterAll, afterEach } from 'vitest';
|
|
12
12
|
import { setupServer } from 'msw/node';
|
|
13
13
|
import { http, HttpResponse } from 'msw';
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// Mock dotenv so the local .env file doesn't interfere with unit tests
|
|
16
|
+
vi.mock('dotenv', () => ({
|
|
17
|
+
config: vi.fn()
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
// Must match the fallback default in lib/config.js (used when no .env is present, e.g. CI)
|
|
21
|
+
const GATEWAY_MCP_URL = 'https://api.unifiedmemory.ai/mcp';
|
|
16
22
|
|
|
17
23
|
// Sample tool with pathParams and headers that should be transformed
|
|
18
24
|
const sampleToolWithContext = {
|
|
@@ -59,6 +65,11 @@ const sampleToolNoContext = {
|
|
|
59
65
|
const server = setupServer();
|
|
60
66
|
|
|
61
67
|
beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }));
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
vi.resetModules();
|
|
70
|
+
// Clear API_ENDPOINT so config.js uses the hardcoded default matching GATEWAY_MCP_URL above
|
|
71
|
+
delete process.env.API_ENDPOINT;
|
|
72
|
+
});
|
|
62
73
|
afterEach(() => server.resetHandlers());
|
|
63
74
|
afterAll(() => server.close());
|
|
64
75
|
|