@synth-coder/memhub 0.2.3 → 0.2.4
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/.eslintrc.cjs +45 -45
- package/.factory/commands/opsx-apply.md +150 -150
- package/.factory/commands/opsx-archive.md +155 -155
- package/.factory/commands/opsx-explore.md +171 -171
- package/.factory/commands/opsx-propose.md +104 -104
- package/.factory/skills/openspec-apply-change/SKILL.md +156 -156
- package/.factory/skills/openspec-archive-change/SKILL.md +114 -114
- package/.factory/skills/openspec-explore/SKILL.md +288 -288
- package/.factory/skills/openspec-propose/SKILL.md +110 -110
- package/.github/workflows/ci.yml +110 -74
- package/.github/workflows/release.yml +67 -0
- package/.iflow/commands/opsx-apply.md +152 -152
- package/.iflow/commands/opsx-archive.md +157 -157
- package/.iflow/commands/opsx-explore.md +173 -173
- package/.iflow/commands/opsx-propose.md +106 -106
- package/.iflow/skills/openspec-apply-change/SKILL.md +156 -156
- package/.iflow/skills/openspec-archive-change/SKILL.md +114 -114
- package/.iflow/skills/openspec-explore/SKILL.md +288 -288
- package/.iflow/skills/openspec-propose/SKILL.md +110 -110
- package/.prettierrc +11 -11
- package/AGENTS.md +167 -169
- package/README.md +276 -195
- package/README.zh-CN.md +245 -193
- package/dist/src/cli/agents/claude-code.d.ts +5 -0
- package/dist/src/cli/agents/claude-code.d.ts.map +1 -0
- package/dist/src/cli/agents/claude-code.js +14 -0
- package/dist/src/cli/agents/claude-code.js.map +1 -0
- package/dist/src/cli/agents/cline.d.ts +5 -0
- package/dist/src/cli/agents/cline.d.ts.map +1 -0
- package/dist/src/cli/agents/cline.js +14 -0
- package/dist/src/cli/agents/cline.js.map +1 -0
- package/dist/src/cli/agents/codex.d.ts +5 -0
- package/dist/src/cli/agents/codex.d.ts.map +1 -0
- package/dist/src/cli/agents/codex.js +14 -0
- package/dist/src/cli/agents/codex.js.map +1 -0
- package/dist/src/cli/agents/cursor.d.ts +5 -0
- package/dist/src/cli/agents/cursor.d.ts.map +1 -0
- package/dist/src/cli/agents/cursor.js +14 -0
- package/dist/src/cli/agents/cursor.js.map +1 -0
- package/dist/src/cli/agents/factory-droid.d.ts +5 -0
- package/dist/src/cli/agents/factory-droid.d.ts.map +1 -0
- package/dist/src/cli/agents/factory-droid.js +14 -0
- package/dist/src/cli/agents/factory-droid.js.map +1 -0
- package/dist/src/cli/agents/gemini-cli.d.ts +5 -0
- package/dist/src/cli/agents/gemini-cli.d.ts.map +1 -0
- package/dist/src/cli/agents/gemini-cli.js +14 -0
- package/dist/src/cli/agents/gemini-cli.js.map +1 -0
- package/dist/src/cli/agents/index.d.ts +14 -0
- package/dist/src/cli/agents/index.d.ts.map +1 -0
- package/dist/src/cli/agents/index.js +30 -0
- package/dist/src/cli/agents/index.js.map +1 -0
- package/dist/src/cli/agents/windsurf.d.ts +5 -0
- package/dist/src/cli/agents/windsurf.d.ts.map +1 -0
- package/dist/src/cli/agents/windsurf.js +14 -0
- package/dist/src/cli/agents/windsurf.js.map +1 -0
- package/dist/src/cli/index.d.ts +8 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +168 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/init.d.ts +34 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +160 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +29 -0
- package/dist/src/cli/instructions.d.ts.map +1 -0
- package/dist/src/cli/instructions.js +141 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/types.d.ts +22 -0
- package/dist/src/cli/types.d.ts.map +1 -0
- package/dist/src/cli/types.js +86 -0
- package/dist/src/cli/types.js.map +1 -0
- package/dist/src/contracts/mcp.js +34 -34
- package/dist/src/contracts/schemas.js.map +1 -1
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/mcp-server.js +7 -14
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/services/embedding-service.d.ts.map +1 -1
- package/dist/src/services/embedding-service.js +1 -1
- package/dist/src/services/embedding-service.js.map +1 -1
- package/dist/src/services/memory-service.d.ts.map +1 -1
- package/dist/src/services/memory-service.js.map +1 -1
- package/dist/src/storage/markdown-storage.d.ts.map +1 -1
- package/dist/src/storage/markdown-storage.js +1 -1
- package/dist/src/storage/markdown-storage.js.map +1 -1
- package/dist/src/storage/vector-index.d.ts.map +1 -1
- package/dist/src/storage/vector-index.js +4 -5
- package/dist/src/storage/vector-index.js.map +1 -1
- package/docs/README.md +21 -0
- package/docs/mcp-tools.md +136 -0
- package/docs/user-guide.md +182 -0
- package/package.json +61 -59
- package/src/cli/agents/claude-code.ts +14 -0
- package/src/cli/agents/cline.ts +14 -0
- package/src/cli/agents/codex.ts +14 -0
- package/src/cli/agents/cursor.ts +14 -0
- package/src/cli/agents/factory-droid.ts +14 -0
- package/src/cli/agents/gemini-cli.ts +14 -0
- package/src/cli/agents/index.ts +36 -0
- package/src/cli/agents/windsurf.ts +14 -0
- package/src/cli/index.ts +192 -0
- package/src/cli/init.ts +218 -0
- package/src/cli/instructions.ts +156 -0
- package/src/cli/types.ts +112 -0
- package/src/contracts/index.ts +12 -12
- package/src/contracts/mcp.ts +223 -223
- package/src/contracts/schemas.ts +307 -307
- package/src/contracts/types.ts +410 -410
- package/src/index.ts +8 -8
- package/src/server/index.ts +5 -5
- package/src/server/mcp-server.ts +169 -186
- package/src/services/embedding-service.ts +114 -114
- package/src/services/index.ts +5 -5
- package/src/services/memory-service.ts +656 -663
- package/src/storage/frontmatter-parser.ts +243 -243
- package/src/storage/index.ts +6 -6
- package/src/storage/markdown-storage.ts +228 -236
- package/src/storage/vector-index.ts +159 -160
- package/src/utils/index.ts +5 -5
- package/src/utils/slugify.ts +63 -63
- package/test/cli/init.test.ts +380 -0
- package/test/contracts/schemas.test.ts +313 -313
- package/test/contracts/types.test.ts +21 -21
- package/test/frontmatter-parser-more.test.ts +94 -94
- package/test/server/mcp-server.test.ts +211 -210
- package/test/services/memory-service-edge.test.ts +248 -248
- package/test/services/memory-service.test.ts +291 -279
- package/test/storage/frontmatter-parser.test.ts +223 -223
- package/test/storage/markdown-storage.test.ts +226 -217
- package/test/storage/storage-edge.test.ts +238 -238
- package/test/storage/vector-index.test.ts +149 -153
- package/test/utils/slugify-edge.test.ts +94 -94
- package/test/utils/slugify.test.ts +72 -68
- package/tsconfig.json +25 -25
- package/tsconfig.test.json +8 -8
- package/vitest.config.ts +29 -29
- package/docs/architecture-diagrams.md +0 -368
- package/docs/architecture.md +0 -381
- package/docs/contracts.md +0 -190
- package/docs/prompt-template.md +0 -33
- package/docs/proposals/mcp-typescript-sdk-refactor.md +0 -568
- package/docs/proposals/proposal-close-gates.md +0 -58
- package/docs/tool-calling-policy.md +0 -101
- package/docs/vector-search.md +0 -306
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for CLI init command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { mkdirSync, rmSync, existsSync, readFileSync, writeFileSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { initAgent } from '../../src/cli/init.js';
|
|
9
|
+
import { AGENTS, type AgentType } from '../../src/cli/types.js';
|
|
10
|
+
import {
|
|
11
|
+
extractMemHubVersion,
|
|
12
|
+
needsUpdate,
|
|
13
|
+
updateInstructionsContent,
|
|
14
|
+
} from '../../src/cli/instructions.js';
|
|
15
|
+
|
|
16
|
+
const TEST_DIR = join(process.cwd(), 'test-temp-cli');
|
|
17
|
+
|
|
18
|
+
describe('CLI Init Command', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
if (!existsSync(TEST_DIR)) {
|
|
21
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
if (existsSync(TEST_DIR)) {
|
|
27
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('initAgent', () => {
|
|
32
|
+
it('should create local config and instructions for cursor', () => {
|
|
33
|
+
const result = initAgent({
|
|
34
|
+
agent: 'cursor',
|
|
35
|
+
local: true,
|
|
36
|
+
projectPath: TEST_DIR,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(result.success).toBe(true);
|
|
40
|
+
if (result.success) {
|
|
41
|
+
expect(result.agent.id).toBe('cursor');
|
|
42
|
+
expect(result.configPath).toBe('.cursor/mcp.json');
|
|
43
|
+
expect(result.instructionsPath).toBe('.cursorrules');
|
|
44
|
+
expect(existsSync(join(TEST_DIR, '.cursor/mcp.json'))).toBe(true);
|
|
45
|
+
expect(existsSync(join(TEST_DIR, '.cursorrules'))).toBe(true);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should create local config and instructions for claude-code', () => {
|
|
50
|
+
const result = initAgent({
|
|
51
|
+
agent: 'claude-code',
|
|
52
|
+
local: true,
|
|
53
|
+
projectPath: TEST_DIR,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(result.success).toBe(true);
|
|
57
|
+
if (result.success) {
|
|
58
|
+
expect(result.agent.id).toBe('claude-code');
|
|
59
|
+
expect(result.configPath).toBe('.mcp.json');
|
|
60
|
+
expect(result.instructionsPath).toBe('CLAUDE.md');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should create local config and instructions for cline', () => {
|
|
65
|
+
const result = initAgent({
|
|
66
|
+
agent: 'cline',
|
|
67
|
+
local: true,
|
|
68
|
+
projectPath: TEST_DIR,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(result.success).toBe(true);
|
|
72
|
+
if (result.success) {
|
|
73
|
+
expect(result.agent.id).toBe('cline');
|
|
74
|
+
expect(result.configPath).toBe('.cline/mcp.json');
|
|
75
|
+
expect(result.instructionsPath).toBe('.clinerules');
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should create local config and instructions for windsurf', () => {
|
|
80
|
+
const result = initAgent({
|
|
81
|
+
agent: 'windsurf',
|
|
82
|
+
local: true,
|
|
83
|
+
projectPath: TEST_DIR,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(result.success).toBe(true);
|
|
87
|
+
if (result.success) {
|
|
88
|
+
expect(result.agent.id).toBe('windsurf');
|
|
89
|
+
expect(result.configPath).toBe('.codeium/windsurf/mcp_config.json');
|
|
90
|
+
expect(result.instructionsPath).toBe('.windsurfrules');
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should create local config and instructions for factory-droid', () => {
|
|
95
|
+
const result = initAgent({
|
|
96
|
+
agent: 'factory-droid',
|
|
97
|
+
local: true,
|
|
98
|
+
projectPath: TEST_DIR,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(result.success).toBe(true);
|
|
102
|
+
if (result.success) {
|
|
103
|
+
expect(result.agent.id).toBe('factory-droid');
|
|
104
|
+
expect(result.configPath).toBe('.factory/mcp.json');
|
|
105
|
+
expect(result.instructionsPath).toBe('.factory/AGENTS.md');
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should create local config and instructions for gemini-cli', () => {
|
|
110
|
+
const result = initAgent({
|
|
111
|
+
agent: 'gemini-cli',
|
|
112
|
+
local: true,
|
|
113
|
+
projectPath: TEST_DIR,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
expect(result.success).toBe(true);
|
|
117
|
+
if (result.success) {
|
|
118
|
+
expect(result.agent.id).toBe('gemini-cli');
|
|
119
|
+
expect(result.configPath).toBe('.gemini/settings.json');
|
|
120
|
+
expect(result.instructionsPath).toBe('GEMINI.md');
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should create local config and instructions for codex', () => {
|
|
125
|
+
const result = initAgent({
|
|
126
|
+
agent: 'codex',
|
|
127
|
+
local: true,
|
|
128
|
+
projectPath: TEST_DIR,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(result.success).toBe(true);
|
|
132
|
+
if (result.success) {
|
|
133
|
+
expect(result.agent.id).toBe('codex');
|
|
134
|
+
expect(result.configPath).toBe('.codex/config.toml');
|
|
135
|
+
expect(result.instructionsPath).toBe('AGENTS.md');
|
|
136
|
+
expect(existsSync(join(TEST_DIR, '.codex/config.toml'))).toBe(true);
|
|
137
|
+
expect(existsSync(join(TEST_DIR, 'AGENTS.md'))).toBe(true);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should fail for unknown agent', () => {
|
|
142
|
+
const result = initAgent({
|
|
143
|
+
agent: 'unknown-agent' as AgentType,
|
|
144
|
+
local: true,
|
|
145
|
+
projectPath: TEST_DIR,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(result.success).toBe(false);
|
|
149
|
+
if (!result.success) {
|
|
150
|
+
expect(result.error).toContain('Unknown agent');
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should fail if memhub already configured without force', () => {
|
|
155
|
+
// First init
|
|
156
|
+
initAgent({
|
|
157
|
+
agent: 'claude-code',
|
|
158
|
+
local: true,
|
|
159
|
+
projectPath: TEST_DIR,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Second init without force
|
|
163
|
+
const result = initAgent({
|
|
164
|
+
agent: 'claude-code',
|
|
165
|
+
local: true,
|
|
166
|
+
projectPath: TEST_DIR,
|
|
167
|
+
force: false,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(result.success).toBe(false);
|
|
171
|
+
if (!result.success) {
|
|
172
|
+
expect(result.error).toContain('already configured');
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should update config with force', () => {
|
|
177
|
+
// First init
|
|
178
|
+
initAgent({
|
|
179
|
+
agent: 'claude-code',
|
|
180
|
+
local: true,
|
|
181
|
+
projectPath: TEST_DIR,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Second init with force
|
|
185
|
+
const result = initAgent({
|
|
186
|
+
agent: 'claude-code',
|
|
187
|
+
local: true,
|
|
188
|
+
projectPath: TEST_DIR,
|
|
189
|
+
force: true,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(result.success).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should generate valid JSON config', () => {
|
|
196
|
+
initAgent({
|
|
197
|
+
agent: 'cursor',
|
|
198
|
+
local: true,
|
|
199
|
+
projectPath: TEST_DIR,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const configPath = join(TEST_DIR, '.cursor/mcp.json');
|
|
203
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
204
|
+
const config = JSON.parse(content);
|
|
205
|
+
|
|
206
|
+
expect(config).toHaveProperty('mcpServers');
|
|
207
|
+
expect(config.mcpServers).toHaveProperty('memhub');
|
|
208
|
+
expect(config.mcpServers.memhub).toHaveProperty('command');
|
|
209
|
+
expect(config.mcpServers.memhub.command).toBe('npx');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should generate instructions with version tag', () => {
|
|
213
|
+
initAgent({
|
|
214
|
+
agent: 'claude-code',
|
|
215
|
+
local: true,
|
|
216
|
+
projectPath: TEST_DIR,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const instructionsPath = join(TEST_DIR, 'CLAUDE.md');
|
|
220
|
+
const content = readFileSync(instructionsPath, 'utf-8');
|
|
221
|
+
|
|
222
|
+
expect(content).toContain('<!-- MEMHUB:v');
|
|
223
|
+
expect(content).toContain('<!-- MEMHUB:END -->');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should merge with existing config preserving other servers', () => {
|
|
227
|
+
// Create existing config with other servers
|
|
228
|
+
const configDir = join(TEST_DIR, '.mcp.json');
|
|
229
|
+
const existingConfig = {
|
|
230
|
+
mcpServers: {
|
|
231
|
+
github: {
|
|
232
|
+
command: 'npx',
|
|
233
|
+
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
writeFileSync(configDir, JSON.stringify(existingConfig, null, 2), 'utf-8');
|
|
238
|
+
|
|
239
|
+
// Run init
|
|
240
|
+
const result = initAgent({
|
|
241
|
+
agent: 'claude-code',
|
|
242
|
+
local: true,
|
|
243
|
+
projectPath: TEST_DIR,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
expect(result.success).toBe(true);
|
|
247
|
+
|
|
248
|
+
// Verify both servers exist
|
|
249
|
+
const content = readFileSync(configDir, 'utf-8');
|
|
250
|
+
const config = JSON.parse(content);
|
|
251
|
+
|
|
252
|
+
expect(config.mcpServers).toHaveProperty('github');
|
|
253
|
+
expect(config.mcpServers).toHaveProperty('memhub');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should fail if memhub already in config without force', () => {
|
|
257
|
+
// Create config with memhub already configured
|
|
258
|
+
const configDir = join(TEST_DIR, '.mcp.json');
|
|
259
|
+
const existingConfig = {
|
|
260
|
+
mcpServers: {
|
|
261
|
+
memhub: {
|
|
262
|
+
command: 'npx',
|
|
263
|
+
args: ['-y', '@synth-coder/memhub'],
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
writeFileSync(configDir, JSON.stringify(existingConfig, null, 2), 'utf-8');
|
|
268
|
+
|
|
269
|
+
// Run init without force
|
|
270
|
+
const result = initAgent({
|
|
271
|
+
agent: 'claude-code',
|
|
272
|
+
local: true,
|
|
273
|
+
projectPath: TEST_DIR,
|
|
274
|
+
force: false,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
expect(result.success).toBe(false);
|
|
278
|
+
if (!result.success) {
|
|
279
|
+
expect(result.error).toContain('already configured');
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should update memhub with force preserving other servers', () => {
|
|
284
|
+
// Create config with existing servers including memhub
|
|
285
|
+
const configDir = join(TEST_DIR, '.mcp.json');
|
|
286
|
+
const existingConfig = {
|
|
287
|
+
mcpServers: {
|
|
288
|
+
github: {
|
|
289
|
+
command: 'npx',
|
|
290
|
+
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
291
|
+
},
|
|
292
|
+
memhub: {
|
|
293
|
+
command: 'old-command',
|
|
294
|
+
args: [],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
writeFileSync(configDir, JSON.stringify(existingConfig, null, 2), 'utf-8');
|
|
299
|
+
|
|
300
|
+
// Run init with force
|
|
301
|
+
const result = initAgent({
|
|
302
|
+
agent: 'claude-code',
|
|
303
|
+
local: true,
|
|
304
|
+
projectPath: TEST_DIR,
|
|
305
|
+
force: true,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
expect(result.success).toBe(true);
|
|
309
|
+
|
|
310
|
+
// Verify github preserved, memhub updated
|
|
311
|
+
const content = readFileSync(configDir, 'utf-8');
|
|
312
|
+
const config = JSON.parse(content);
|
|
313
|
+
|
|
314
|
+
expect(config.mcpServers).toHaveProperty('github');
|
|
315
|
+
expect(config.mcpServers).toHaveProperty('memhub');
|
|
316
|
+
expect(config.mcpServers.memhub.command).toBe('npx');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('Instructions Update', () => {
|
|
321
|
+
it('should prepend instructions to empty file', () => {
|
|
322
|
+
const result = updateInstructionsContent('', AGENTS[1]); // claude-code
|
|
323
|
+
|
|
324
|
+
expect(result.updated).toBe(true);
|
|
325
|
+
expect(result.content).toContain('<!-- MEMHUB:v');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should prepend instructions to existing content', () => {
|
|
329
|
+
const existing = '# My Project\n\nSome instructions';
|
|
330
|
+
const result = updateInstructionsContent(existing, AGENTS[1]);
|
|
331
|
+
|
|
332
|
+
expect(result.updated).toBe(true);
|
|
333
|
+
expect(result.content).toContain('<!-- MEMHUB:v');
|
|
334
|
+
expect(result.content).toContain('# My Project');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should not update if already current version', () => {
|
|
338
|
+
const content = '<!-- MEMHUB:v0.2.3:START -->\nContent\n<!-- MEMHUB:END -->';
|
|
339
|
+
const result = updateInstructionsContent(content, AGENTS[1]);
|
|
340
|
+
|
|
341
|
+
expect(result.updated).toBe(false);
|
|
342
|
+
expect(result.reason).toBe('Already up to date');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should extract version correctly', () => {
|
|
346
|
+
expect(extractMemHubVersion('<!-- MEMHUB:v0.2.3:START -->')).toBe('0.2.3');
|
|
347
|
+
expect(extractMemHubVersion('No version here')).toBeNull();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should detect when update is needed', () => {
|
|
351
|
+
expect(needsUpdate('No MemHub content')).toBe(true);
|
|
352
|
+
expect(needsUpdate('<!-- MEMHUB:v0.1.0:START -->')).toBe(true);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('Agent Types', () => {
|
|
358
|
+
it('should have all expected agents', () => {
|
|
359
|
+
const agentIds = AGENTS.map(a => a.id);
|
|
360
|
+
expect(agentIds).toContain('cursor');
|
|
361
|
+
expect(agentIds).toContain('claude-code');
|
|
362
|
+
expect(agentIds).toContain('cline');
|
|
363
|
+
expect(agentIds).toContain('windsurf');
|
|
364
|
+
expect(agentIds).toContain('factory-droid');
|
|
365
|
+
expect(agentIds).toContain('gemini-cli');
|
|
366
|
+
expect(agentIds).toContain('codex');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should have valid config for each agent', () => {
|
|
370
|
+
AGENTS.forEach(agent => {
|
|
371
|
+
expect(agent.configFile).toBeTruthy();
|
|
372
|
+
expect(agent.globalConfigFile).toBeTruthy();
|
|
373
|
+
expect(agent.name).toBeTruthy();
|
|
374
|
+
expect(agent.configFormat).toMatch(/^(json|markdown|toml)$/);
|
|
375
|
+
expect(agent.instructionsFile).toBeTruthy();
|
|
376
|
+
expect(agent.globalInstructionsFile).toBeTruthy();
|
|
377
|
+
expect(agent.instructionsFormat).toMatch(/^(markdown|plain)$/);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
});
|