@cleocode/adapters 2026.3.37 → 2026.3.40
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/LICENSE +21 -0
- package/README.md +503 -0
- package/dist/index.js +621 -496
- package/dist/index.js.map +4 -4
- package/package.json +12 -7
- package/src/__tests__/claude-code-adapter.test.d.ts +10 -0
- package/src/__tests__/claude-code-adapter.test.d.ts.map +1 -0
- package/src/__tests__/claude-code-adapter.test.js +192 -0
- package/src/__tests__/claude-code-adapter.test.js.map +1 -0
- package/src/__tests__/claude-code-adapter.test.ts +235 -0
- package/src/__tests__/cursor-adapter.test.d.ts +10 -0
- package/src/__tests__/cursor-adapter.test.d.ts.map +1 -0
- package/src/__tests__/cursor-adapter.test.js +196 -0
- package/src/__tests__/cursor-adapter.test.js.map +1 -0
- package/src/__tests__/cursor-adapter.test.ts +246 -0
- package/src/__tests__/opencode-adapter.test.d.ts +10 -0
- package/src/__tests__/opencode-adapter.test.d.ts.map +1 -0
- package/src/__tests__/opencode-adapter.test.js +204 -0
- package/src/__tests__/opencode-adapter.test.js.map +1 -0
- package/src/__tests__/opencode-adapter.test.ts +250 -0
- package/src/index.d.ts +28 -20
- package/src/index.d.ts.map +1 -1
- package/src/index.js +4 -19
- package/src/index.js.map +1 -1
- package/src/index.ts +25 -25
- package/src/providers/claude-code/__tests__/adapter.test.d.ts +1 -1
- package/src/providers/claude-code/__tests__/adapter.test.d.ts.map +0 -0
- package/src/providers/claude-code/__tests__/adapter.test.js +2 -2
- package/src/providers/claude-code/__tests__/adapter.test.js.map +1 -1
- package/src/providers/claude-code/__tests__/adapter.test.ts +2 -2
- package/src/providers/claude-code/adapter.d.ts +54 -50
- package/src/providers/claude-code/adapter.d.ts.map +0 -0
- package/src/providers/claude-code/adapter.js +1 -1
- package/src/providers/claude-code/adapter.js.map +1 -1
- package/src/providers/claude-code/adapter.ts +1 -1
- package/src/providers/claude-code/context-monitor.d.ts +6 -6
- package/src/providers/claude-code/context-monitor.d.ts.map +0 -0
- package/src/providers/claude-code/context-monitor.js +0 -0
- package/src/providers/claude-code/context-monitor.js.map +0 -0
- package/src/providers/claude-code/context-monitor.ts +0 -0
- package/src/providers/claude-code/hooks.d.ts +36 -36
- package/src/providers/claude-code/hooks.d.ts.map +0 -0
- package/src/providers/claude-code/hooks.js +0 -0
- package/src/providers/claude-code/hooks.js.map +0 -0
- package/src/providers/claude-code/hooks.ts +0 -0
- package/src/providers/claude-code/index.d.ts +6 -2
- package/src/providers/claude-code/index.d.ts.map +1 -1
- package/src/providers/claude-code/index.js +1 -1
- package/src/providers/claude-code/index.js.map +1 -1
- package/src/providers/claude-code/index.ts +2 -2
- package/src/providers/claude-code/install.d.ts +52 -52
- package/src/providers/claude-code/install.d.ts.map +1 -1
- package/src/providers/claude-code/install.js +1 -4
- package/src/providers/claude-code/install.js.map +1 -1
- package/src/providers/claude-code/install.ts +1 -4
- package/src/providers/claude-code/manifest.json +0 -0
- package/src/providers/claude-code/paths.d.ts +5 -5
- package/src/providers/claude-code/paths.d.ts.map +0 -0
- package/src/providers/claude-code/paths.js +0 -0
- package/src/providers/claude-code/paths.js.map +0 -0
- package/src/providers/claude-code/paths.ts +0 -0
- package/src/providers/claude-code/spawn.d.ts +37 -37
- package/src/providers/claude-code/spawn.d.ts.map +1 -1
- package/src/providers/claude-code/spawn.js +4 -0
- package/src/providers/claude-code/spawn.js.map +1 -1
- package/src/providers/claude-code/spawn.ts +5 -0
- package/src/providers/claude-code/statusline.d.ts +1 -2
- package/src/providers/claude-code/statusline.d.ts.map +0 -0
- package/src/providers/claude-code/statusline.js +2 -1
- package/src/providers/claude-code/statusline.js.map +1 -1
- package/src/providers/claude-code/statusline.ts +3 -3
- package/src/providers/claude-code/task-sync.js +1 -3
- package/src/providers/claude-code/task-sync.js.map +1 -1
- package/src/providers/claude-code/task-sync.ts +5 -22
- package/src/providers/claude-code/transport.d.ts +3 -3
- package/src/providers/claude-code/transport.d.ts.map +0 -0
- package/src/providers/claude-code/transport.js +0 -0
- package/src/providers/claude-code/transport.js.map +0 -0
- package/src/providers/claude-code/transport.ts +0 -0
- package/src/providers/cursor/__tests__/adapter.test.d.ts +1 -1
- package/src/providers/cursor/__tests__/adapter.test.d.ts.map +0 -0
- package/src/providers/cursor/__tests__/adapter.test.js +2 -2
- package/src/providers/cursor/__tests__/adapter.test.js.map +1 -1
- package/src/providers/cursor/__tests__/adapter.test.ts +2 -2
- package/src/providers/cursor/adapter.d.ts +44 -40
- package/src/providers/cursor/adapter.d.ts.map +0 -0
- package/src/providers/cursor/adapter.js +0 -0
- package/src/providers/cursor/adapter.js.map +0 -0
- package/src/providers/cursor/adapter.ts +0 -0
- package/src/providers/cursor/hooks.d.ts +29 -29
- package/src/providers/cursor/hooks.d.ts.map +0 -0
- package/src/providers/cursor/hooks.js +0 -0
- package/src/providers/cursor/hooks.js.map +0 -0
- package/src/providers/cursor/hooks.ts +0 -0
- package/src/providers/cursor/index.d.ts +1 -1
- package/src/providers/cursor/index.d.ts.map +0 -0
- package/src/providers/cursor/index.js +0 -0
- package/src/providers/cursor/index.js.map +0 -0
- package/src/providers/cursor/index.ts +0 -0
- package/src/providers/cursor/install.d.ts +67 -67
- package/src/providers/cursor/install.d.ts.map +1 -1
- package/src/providers/cursor/install.js +1 -4
- package/src/providers/cursor/install.js.map +1 -1
- package/src/providers/cursor/install.ts +1 -4
- package/src/providers/cursor/manifest.json +0 -0
- package/src/providers/cursor/spawn.d.ts +31 -31
- package/src/providers/cursor/spawn.d.ts.map +0 -0
- package/src/providers/cursor/spawn.js +0 -0
- package/src/providers/cursor/spawn.js.map +1 -1
- package/src/providers/cursor/spawn.ts +2 -2
- package/src/providers/opencode/__tests__/adapter.test.d.ts +1 -1
- package/src/providers/opencode/__tests__/adapter.test.d.ts.map +0 -0
- package/src/providers/opencode/__tests__/adapter.test.js +2 -2
- package/src/providers/opencode/__tests__/adapter.test.js.map +1 -1
- package/src/providers/opencode/__tests__/adapter.test.ts +2 -2
- package/src/providers/opencode/adapter.d.ts +50 -46
- package/src/providers/opencode/adapter.d.ts.map +0 -0
- package/src/providers/opencode/adapter.js +0 -0
- package/src/providers/opencode/adapter.js.map +0 -0
- package/src/providers/opencode/adapter.ts +0 -0
- package/src/providers/opencode/hooks.d.ts +36 -36
- package/src/providers/opencode/hooks.d.ts.map +0 -0
- package/src/providers/opencode/hooks.js +1 -1
- package/src/providers/opencode/hooks.js.map +1 -1
- package/src/providers/opencode/hooks.ts +1 -1
- package/src/providers/opencode/index.d.ts +2 -2
- package/src/providers/opencode/index.d.ts.map +1 -1
- package/src/providers/opencode/index.js +1 -1
- package/src/providers/opencode/index.js.map +1 -1
- package/src/providers/opencode/index.ts +1 -1
- package/src/providers/opencode/install.d.ts +46 -46
- package/src/providers/opencode/install.d.ts.map +1 -1
- package/src/providers/opencode/install.js +1 -4
- package/src/providers/opencode/install.js.map +1 -1
- package/src/providers/opencode/install.ts +1 -4
- package/src/providers/opencode/manifest.json +0 -0
- package/src/providers/opencode/spawn.d.ts +42 -39
- package/src/providers/opencode/spawn.d.ts.map +1 -1
- package/src/providers/opencode/spawn.js +0 -0
- package/src/providers/opencode/spawn.js.map +1 -1
- package/src/providers/opencode/spawn.ts +2 -7
- package/src/registry.d.ts +12 -12
- package/src/registry.d.ts.map +1 -1
- package/src/registry.js +0 -0
- package/src/registry.js.map +1 -1
- package/src/registry.ts +1 -3
- package/dist/index.d.ts +0 -27
- package/dist/index.d.ts.map +0 -1
- package/dist/providers/claude-code/adapter.d.ts +0 -75
- package/dist/providers/claude-code/adapter.d.ts.map +0 -1
- package/dist/providers/claude-code/adapter.js +0 -154
- package/dist/providers/claude-code/adapter.js.map +0 -1
- package/dist/providers/claude-code/context-monitor.d.ts +0 -24
- package/dist/providers/claude-code/context-monitor.d.ts.map +0 -1
- package/dist/providers/claude-code/context-monitor.js +0 -148
- package/dist/providers/claude-code/context-monitor.js.map +0 -1
- package/dist/providers/claude-code/hooks.d.ts +0 -59
- package/dist/providers/claude-code/hooks.d.ts.map +0 -1
- package/dist/providers/claude-code/hooks.js +0 -77
- package/dist/providers/claude-code/hooks.js.map +0 -1
- package/dist/providers/claude-code/index.d.ts +0 -24
- package/dist/providers/claude-code/index.d.ts.map +0 -1
- package/dist/providers/claude-code/index.js +0 -26
- package/dist/providers/claude-code/index.js.map +0 -1
- package/dist/providers/claude-code/install.d.ts +0 -75
- package/dist/providers/claude-code/install.d.ts.map +0 -1
- package/dist/providers/claude-code/install.js +0 -237
- package/dist/providers/claude-code/install.js.map +0 -1
- package/dist/providers/claude-code/paths.d.ts +0 -24
- package/dist/providers/claude-code/paths.d.ts.map +0 -1
- package/dist/providers/claude-code/paths.js +0 -33
- package/dist/providers/claude-code/paths.js.map +0 -1
- package/dist/providers/claude-code/spawn.d.ts +0 -60
- package/dist/providers/claude-code/spawn.d.ts.map +0 -1
- package/dist/providers/claude-code/spawn.js +0 -160
- package/dist/providers/claude-code/spawn.js.map +0 -1
- package/dist/providers/claude-code/statusline.d.ts +0 -24
- package/dist/providers/claude-code/statusline.d.ts.map +0 -1
- package/dist/providers/claude-code/statusline.js +0 -85
- package/dist/providers/claude-code/statusline.js.map +0 -1
- package/dist/providers/claude-code/task-sync.d.ts +0 -27
- package/dist/providers/claude-code/task-sync.d.ts.map +0 -1
- package/dist/providers/claude-code/task-sync.js +0 -124
- package/dist/providers/claude-code/task-sync.js.map +0 -1
- package/dist/providers/claude-code/transport.d.ts +0 -14
- package/dist/providers/claude-code/transport.d.ts.map +0 -1
- package/dist/providers/claude-code/transport.js +0 -18
- package/dist/providers/claude-code/transport.js.map +0 -1
- package/dist/providers/cursor/adapter.d.ts +0 -62
- package/dist/providers/cursor/adapter.d.ts.map +0 -1
- package/dist/providers/cursor/adapter.js +0 -124
- package/dist/providers/cursor/adapter.js.map +0 -1
- package/dist/providers/cursor/hooks.d.ts +0 -48
- package/dist/providers/cursor/hooks.d.ts.map +0 -1
- package/dist/providers/cursor/hooks.js +0 -55
- package/dist/providers/cursor/hooks.js.map +0 -1
- package/dist/providers/cursor/index.d.ts +0 -19
- package/dist/providers/cursor/index.d.ts.map +0 -1
- package/dist/providers/cursor/index.js +0 -21
- package/dist/providers/cursor/index.js.map +0 -1
- package/dist/providers/cursor/install.d.ts +0 -94
- package/dist/providers/cursor/install.d.ts.map +0 -1
- package/dist/providers/cursor/install.js +0 -241
- package/dist/providers/cursor/install.js.map +0 -1
- package/dist/providers/cursor/spawn.d.ts +0 -50
- package/dist/providers/cursor/spawn.d.ts.map +0 -1
- package/dist/providers/cursor/spawn.js +0 -59
- package/dist/providers/cursor/spawn.js.map +0 -1
- package/dist/providers/opencode/adapter.d.ts +0 -67
- package/dist/providers/opencode/adapter.d.ts.map +0 -1
- package/dist/providers/opencode/adapter.js +0 -144
- package/dist/providers/opencode/adapter.js.map +0 -1
- package/dist/providers/opencode/hooks.d.ts +0 -66
- package/dist/providers/opencode/hooks.d.ts.map +0 -1
- package/dist/providers/opencode/hooks.js +0 -89
- package/dist/providers/opencode/hooks.js.map +0 -1
- package/dist/providers/opencode/index.d.ts +0 -20
- package/dist/providers/opencode/index.d.ts.map +0 -1
- package/dist/providers/opencode/index.js +0 -22
- package/dist/providers/opencode/index.js.map +0 -1
- package/dist/providers/opencode/install.d.ts +0 -65
- package/dist/providers/opencode/install.d.ts.map +0 -1
- package/dist/providers/opencode/install.js +0 -183
- package/dist/providers/opencode/install.js.map +0 -1
- package/dist/providers/opencode/spawn.d.ts +0 -72
- package/dist/providers/opencode/spawn.d.ts.map +0 -1
- package/dist/providers/opencode/spawn.js +0 -219
- package/dist/providers/opencode/spawn.js.map +0 -1
- package/dist/registry.d.ts +0 -36
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js +0 -55
- package/dist/registry.js.map +0 -1
- package/src/providers/claude-code/task-sync.d.ts +0 -27
- package/src/providers/claude-code/task-sync.d.ts.map +0 -1
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for @cleocode/adapter-cursor
|
|
3
|
+
*
|
|
4
|
+
* Tests the CursorAdapter, CursorHookProvider, and CursorInstallProvider.
|
|
5
|
+
* Cursor has no spawn support, so no spawn provider tests.
|
|
6
|
+
*
|
|
7
|
+
* @task T5240
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { CursorAdapter, CursorHookProvider, CursorInstallProvider, createCursorAdapter as createAdapter, } from '@cleocode/adapters';
|
|
13
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
14
|
+
describe('CursorAdapter', () => {
|
|
15
|
+
let adapter;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
adapter = new CursorAdapter();
|
|
18
|
+
});
|
|
19
|
+
it('has correct identity fields', () => {
|
|
20
|
+
expect(adapter.id).toBe('cursor');
|
|
21
|
+
expect(adapter.name).toBe('Cursor');
|
|
22
|
+
expect(adapter.version).toBe('1.0.0');
|
|
23
|
+
});
|
|
24
|
+
it('does not support hooks', () => {
|
|
25
|
+
expect(adapter.capabilities.supportsHooks).toBe(false);
|
|
26
|
+
expect(adapter.capabilities.supportedHookEvents).toHaveLength(0);
|
|
27
|
+
});
|
|
28
|
+
it('does not support spawn', () => {
|
|
29
|
+
expect(adapter.capabilities.supportsSpawn).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it('supports install, MCP, and instruction files', () => {
|
|
32
|
+
expect(adapter.capabilities.supportsInstall).toBe(true);
|
|
33
|
+
expect(adapter.capabilities.supportsMcp).toBe(true);
|
|
34
|
+
expect(adapter.capabilities.supportsInstructionFiles).toBe(true);
|
|
35
|
+
expect(adapter.capabilities.instructionFilePattern).toBe('.cursor/rules/*.mdc');
|
|
36
|
+
});
|
|
37
|
+
it('has no spawn provider', () => {
|
|
38
|
+
expect(adapter.spawn).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
it('exposes hooks and install providers', () => {
|
|
41
|
+
expect(adapter.hooks).toBeInstanceOf(CursorHookProvider);
|
|
42
|
+
expect(adapter.install).toBeInstanceOf(CursorInstallProvider);
|
|
43
|
+
});
|
|
44
|
+
it('tracks initialization state', async () => {
|
|
45
|
+
expect(adapter.isInitialized()).toBe(false);
|
|
46
|
+
await adapter.initialize('/tmp/test-project');
|
|
47
|
+
expect(adapter.isInitialized()).toBe(true);
|
|
48
|
+
expect(adapter.getProjectDir()).toBe('/tmp/test-project');
|
|
49
|
+
});
|
|
50
|
+
it('clears state on dispose', async () => {
|
|
51
|
+
await adapter.initialize('/tmp/test-project');
|
|
52
|
+
await adapter.dispose();
|
|
53
|
+
expect(adapter.isInitialized()).toBe(false);
|
|
54
|
+
expect(adapter.getProjectDir()).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
it('reports unhealthy when not initialized', async () => {
|
|
57
|
+
const health = await adapter.healthCheck();
|
|
58
|
+
expect(health.healthy).toBe(false);
|
|
59
|
+
expect(health.provider).toBe('cursor');
|
|
60
|
+
expect(health.details?.error).toBe('Adapter not initialized');
|
|
61
|
+
});
|
|
62
|
+
it('reports health based on .cursor/ presence', async () => {
|
|
63
|
+
const testDir = join(tmpdir(), `cursor-health-${Date.now()}`);
|
|
64
|
+
mkdirSync(join(testDir, '.cursor'), { recursive: true });
|
|
65
|
+
try {
|
|
66
|
+
await adapter.initialize(testDir);
|
|
67
|
+
const health = await adapter.healthCheck();
|
|
68
|
+
expect(health.provider).toBe('cursor');
|
|
69
|
+
expect(health.details?.configDirExists).toBe(true);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('CursorHookProvider', () => {
|
|
77
|
+
let hooks;
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
hooks = new CursorHookProvider();
|
|
80
|
+
});
|
|
81
|
+
it('returns null for all events (no hook support)', () => {
|
|
82
|
+
expect(hooks.mapProviderEvent('SessionStart')).toBeNull();
|
|
83
|
+
expect(hooks.mapProviderEvent('PostToolUse')).toBeNull();
|
|
84
|
+
expect(hooks.mapProviderEvent('Stop')).toBeNull();
|
|
85
|
+
expect(hooks.mapProviderEvent('')).toBeNull();
|
|
86
|
+
expect(hooks.mapProviderEvent('anything')).toBeNull();
|
|
87
|
+
});
|
|
88
|
+
it('tracks registration state', async () => {
|
|
89
|
+
expect(hooks.isRegistered()).toBe(false);
|
|
90
|
+
await hooks.registerNativeHooks('/tmp/test');
|
|
91
|
+
expect(hooks.isRegistered()).toBe(true);
|
|
92
|
+
await hooks.unregisterNativeHooks();
|
|
93
|
+
expect(hooks.isRegistered()).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('CursorInstallProvider', () => {
|
|
97
|
+
let install;
|
|
98
|
+
let testDir;
|
|
99
|
+
beforeEach(() => {
|
|
100
|
+
install = new CursorInstallProvider();
|
|
101
|
+
testDir = join(tmpdir(), `cleo-cursor-test-${Date.now()}`);
|
|
102
|
+
mkdirSync(testDir, { recursive: true });
|
|
103
|
+
});
|
|
104
|
+
afterEach(() => {
|
|
105
|
+
try {
|
|
106
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// cleanup best-effort
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
it('creates .cursor/rules/cleo.mdc with @-references', async () => {
|
|
113
|
+
await install.ensureInstructionReferences(testDir);
|
|
114
|
+
const mdcPath = join(testDir, '.cursor', 'rules', 'cleo.mdc');
|
|
115
|
+
expect(existsSync(mdcPath)).toBe(true);
|
|
116
|
+
const content = readFileSync(mdcPath, 'utf-8');
|
|
117
|
+
expect(content).toContain('alwaysApply: true');
|
|
118
|
+
expect(content).toContain('@~/.cleo/templates/CLEO-INJECTION.md');
|
|
119
|
+
expect(content).toContain('@.cleo/memory-bridge.md');
|
|
120
|
+
});
|
|
121
|
+
it('uses MDC frontmatter format', async () => {
|
|
122
|
+
await install.ensureInstructionReferences(testDir);
|
|
123
|
+
const mdcPath = join(testDir, '.cursor', 'rules', 'cleo.mdc');
|
|
124
|
+
const content = readFileSync(mdcPath, 'utf-8');
|
|
125
|
+
expect(content).toMatch(/^---\n/);
|
|
126
|
+
expect(content).toContain('description: CLEO task management protocol references');
|
|
127
|
+
expect(content).toContain('globs: "**/*"');
|
|
128
|
+
});
|
|
129
|
+
it('is idempotent for modern rules', async () => {
|
|
130
|
+
await install.ensureInstructionReferences(testDir);
|
|
131
|
+
const mdcPath = join(testDir, '.cursor', 'rules', 'cleo.mdc');
|
|
132
|
+
const first = readFileSync(mdcPath, 'utf-8');
|
|
133
|
+
await install.ensureInstructionReferences(testDir);
|
|
134
|
+
const second = readFileSync(mdcPath, 'utf-8');
|
|
135
|
+
expect(first).toBe(second);
|
|
136
|
+
});
|
|
137
|
+
it('appends to legacy .cursorrules if it exists', async () => {
|
|
138
|
+
const legacyPath = join(testDir, '.cursorrules');
|
|
139
|
+
writeFileSync(legacyPath, '# Project Rules\nUse TypeScript.\n', 'utf-8');
|
|
140
|
+
await install.ensureInstructionReferences(testDir);
|
|
141
|
+
const content = readFileSync(legacyPath, 'utf-8');
|
|
142
|
+
expect(content).toContain('# Project Rules');
|
|
143
|
+
expect(content).toContain('Use TypeScript.');
|
|
144
|
+
expect(content).toContain('@~/.cleo/templates/CLEO-INJECTION.md');
|
|
145
|
+
expect(content).toContain('@.cleo/memory-bridge.md');
|
|
146
|
+
});
|
|
147
|
+
it('does not create legacy .cursorrules if it does not exist', async () => {
|
|
148
|
+
await install.ensureInstructionReferences(testDir);
|
|
149
|
+
expect(existsSync(join(testDir, '.cursorrules'))).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
it('does not duplicate references in legacy .cursorrules', async () => {
|
|
152
|
+
const legacyPath = join(testDir, '.cursorrules');
|
|
153
|
+
writeFileSync(legacyPath, '@~/.cleo/templates/CLEO-INJECTION.md\n@.cleo/memory-bridge.md\n', 'utf-8');
|
|
154
|
+
await install.ensureInstructionReferences(testDir);
|
|
155
|
+
const content = readFileSync(legacyPath, 'utf-8');
|
|
156
|
+
const injectionCount = content.split('@~/.cleo/templates/CLEO-INJECTION.md').length - 1;
|
|
157
|
+
expect(injectionCount).toBe(1);
|
|
158
|
+
});
|
|
159
|
+
it('registers MCP server in .cursor/mcp.json', async () => {
|
|
160
|
+
const result = await install.install({
|
|
161
|
+
projectDir: testDir,
|
|
162
|
+
mcpServerPath: '/path/to/cleo-mcp.js',
|
|
163
|
+
});
|
|
164
|
+
expect(result.success).toBe(true);
|
|
165
|
+
expect(result.mcpRegistered).toBe(true);
|
|
166
|
+
expect(result.instructionFileUpdated).toBe(true);
|
|
167
|
+
const mcpPath = join(testDir, '.cursor', 'mcp.json');
|
|
168
|
+
expect(existsSync(mcpPath)).toBe(true);
|
|
169
|
+
const config = JSON.parse(readFileSync(mcpPath, 'utf-8'));
|
|
170
|
+
expect(config.mcpServers.cleo).toEqual({
|
|
171
|
+
command: 'node',
|
|
172
|
+
args: ['/path/to/cleo-mcp.js'],
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
it('uninstall removes MCP server from .cursor/mcp.json', async () => {
|
|
176
|
+
await install.install({
|
|
177
|
+
projectDir: testDir,
|
|
178
|
+
mcpServerPath: '/path/to/cleo-mcp.js',
|
|
179
|
+
});
|
|
180
|
+
await install.uninstall();
|
|
181
|
+
const mcpPath = join(testDir, '.cursor', 'mcp.json');
|
|
182
|
+
const config = JSON.parse(readFileSync(mcpPath, 'utf-8'));
|
|
183
|
+
expect(config.mcpServers.cleo).toBeUndefined();
|
|
184
|
+
});
|
|
185
|
+
it('uninstall is no-op when not installed', async () => {
|
|
186
|
+
await expect(install.uninstall()).resolves.toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe('createAdapter factory', () => {
|
|
190
|
+
it('returns a CursorAdapter instance', () => {
|
|
191
|
+
const adapter = createAdapter();
|
|
192
|
+
expect(adapter).toBeInstanceOf(CursorAdapter);
|
|
193
|
+
expect(adapter.id).toBe('cursor');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
//# sourceMappingURL=cursor-adapter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-adapter.test.js","sourceRoot":"","sources":["cursor-adapter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for @cleocode/adapter-cursor
|
|
3
|
+
*
|
|
4
|
+
* Tests the CursorAdapter, CursorHookProvider, and CursorInstallProvider.
|
|
5
|
+
* Cursor has no spawn support, so no spawn provider tests.
|
|
6
|
+
*
|
|
7
|
+
* @task T5240
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import {
|
|
14
|
+
CursorAdapter,
|
|
15
|
+
CursorHookProvider,
|
|
16
|
+
CursorInstallProvider,
|
|
17
|
+
createCursorAdapter as createAdapter,
|
|
18
|
+
} from '@cleocode/adapters';
|
|
19
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
20
|
+
|
|
21
|
+
describe('CursorAdapter', () => {
|
|
22
|
+
let adapter: CursorAdapter;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
adapter = new CursorAdapter();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('has correct identity fields', () => {
|
|
29
|
+
expect(adapter.id).toBe('cursor');
|
|
30
|
+
expect(adapter.name).toBe('Cursor');
|
|
31
|
+
expect(adapter.version).toBe('1.0.0');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('does not support hooks', () => {
|
|
35
|
+
expect(adapter.capabilities.supportsHooks).toBe(false);
|
|
36
|
+
expect(adapter.capabilities.supportedHookEvents).toHaveLength(0);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('does not support spawn', () => {
|
|
40
|
+
expect(adapter.capabilities.supportsSpawn).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('supports install, MCP, and instruction files', () => {
|
|
44
|
+
expect(adapter.capabilities.supportsInstall).toBe(true);
|
|
45
|
+
expect(adapter.capabilities.supportsMcp).toBe(true);
|
|
46
|
+
expect(adapter.capabilities.supportsInstructionFiles).toBe(true);
|
|
47
|
+
expect(adapter.capabilities.instructionFilePattern).toBe('.cursor/rules/*.mdc');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('has no spawn provider', () => {
|
|
51
|
+
expect((adapter as unknown as Record<string, unknown>).spawn).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('exposes hooks and install providers', () => {
|
|
55
|
+
expect(adapter.hooks).toBeInstanceOf(CursorHookProvider);
|
|
56
|
+
expect(adapter.install).toBeInstanceOf(CursorInstallProvider);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('tracks initialization state', async () => {
|
|
60
|
+
expect(adapter.isInitialized()).toBe(false);
|
|
61
|
+
await adapter.initialize('/tmp/test-project');
|
|
62
|
+
expect(adapter.isInitialized()).toBe(true);
|
|
63
|
+
expect(adapter.getProjectDir()).toBe('/tmp/test-project');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('clears state on dispose', async () => {
|
|
67
|
+
await adapter.initialize('/tmp/test-project');
|
|
68
|
+
await adapter.dispose();
|
|
69
|
+
expect(adapter.isInitialized()).toBe(false);
|
|
70
|
+
expect(adapter.getProjectDir()).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('reports unhealthy when not initialized', async () => {
|
|
74
|
+
const health = await adapter.healthCheck();
|
|
75
|
+
expect(health.healthy).toBe(false);
|
|
76
|
+
expect(health.provider).toBe('cursor');
|
|
77
|
+
expect(health.details?.error).toBe('Adapter not initialized');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('reports health based on .cursor/ presence', async () => {
|
|
81
|
+
const testDir = join(tmpdir(), `cursor-health-${Date.now()}`);
|
|
82
|
+
mkdirSync(join(testDir, '.cursor'), { recursive: true });
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await adapter.initialize(testDir);
|
|
86
|
+
const health = await adapter.healthCheck();
|
|
87
|
+
expect(health.provider).toBe('cursor');
|
|
88
|
+
expect(health.details?.configDirExists).toBe(true);
|
|
89
|
+
} finally {
|
|
90
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('CursorHookProvider', () => {
|
|
96
|
+
let hooks: CursorHookProvider;
|
|
97
|
+
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
hooks = new CursorHookProvider();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('returns null for all events (no hook support)', () => {
|
|
103
|
+
expect(hooks.mapProviderEvent('SessionStart')).toBeNull();
|
|
104
|
+
expect(hooks.mapProviderEvent('PostToolUse')).toBeNull();
|
|
105
|
+
expect(hooks.mapProviderEvent('Stop')).toBeNull();
|
|
106
|
+
expect(hooks.mapProviderEvent('')).toBeNull();
|
|
107
|
+
expect(hooks.mapProviderEvent('anything')).toBeNull();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('tracks registration state', async () => {
|
|
111
|
+
expect(hooks.isRegistered()).toBe(false);
|
|
112
|
+
await hooks.registerNativeHooks('/tmp/test');
|
|
113
|
+
expect(hooks.isRegistered()).toBe(true);
|
|
114
|
+
await hooks.unregisterNativeHooks();
|
|
115
|
+
expect(hooks.isRegistered()).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('CursorInstallProvider', () => {
|
|
120
|
+
let install: CursorInstallProvider;
|
|
121
|
+
let testDir: string;
|
|
122
|
+
|
|
123
|
+
beforeEach(() => {
|
|
124
|
+
install = new CursorInstallProvider();
|
|
125
|
+
testDir = join(tmpdir(), `cleo-cursor-test-${Date.now()}`);
|
|
126
|
+
mkdirSync(testDir, { recursive: true });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
afterEach(() => {
|
|
130
|
+
try {
|
|
131
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
132
|
+
} catch {
|
|
133
|
+
// cleanup best-effort
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('creates .cursor/rules/cleo.mdc with @-references', async () => {
|
|
138
|
+
await install.ensureInstructionReferences(testDir);
|
|
139
|
+
|
|
140
|
+
const mdcPath = join(testDir, '.cursor', 'rules', 'cleo.mdc');
|
|
141
|
+
expect(existsSync(mdcPath)).toBe(true);
|
|
142
|
+
|
|
143
|
+
const content = readFileSync(mdcPath, 'utf-8');
|
|
144
|
+
expect(content).toContain('alwaysApply: true');
|
|
145
|
+
expect(content).toContain('@~/.cleo/templates/CLEO-INJECTION.md');
|
|
146
|
+
expect(content).toContain('@.cleo/memory-bridge.md');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('uses MDC frontmatter format', async () => {
|
|
150
|
+
await install.ensureInstructionReferences(testDir);
|
|
151
|
+
|
|
152
|
+
const mdcPath = join(testDir, '.cursor', 'rules', 'cleo.mdc');
|
|
153
|
+
const content = readFileSync(mdcPath, 'utf-8');
|
|
154
|
+
expect(content).toMatch(/^---\n/);
|
|
155
|
+
expect(content).toContain('description: CLEO task management protocol references');
|
|
156
|
+
expect(content).toContain('globs: "**/*"');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('is idempotent for modern rules', async () => {
|
|
160
|
+
await install.ensureInstructionReferences(testDir);
|
|
161
|
+
const mdcPath = join(testDir, '.cursor', 'rules', 'cleo.mdc');
|
|
162
|
+
const first = readFileSync(mdcPath, 'utf-8');
|
|
163
|
+
|
|
164
|
+
await install.ensureInstructionReferences(testDir);
|
|
165
|
+
const second = readFileSync(mdcPath, 'utf-8');
|
|
166
|
+
|
|
167
|
+
expect(first).toBe(second);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('appends to legacy .cursorrules if it exists', async () => {
|
|
171
|
+
const legacyPath = join(testDir, '.cursorrules');
|
|
172
|
+
writeFileSync(legacyPath, '# Project Rules\nUse TypeScript.\n', 'utf-8');
|
|
173
|
+
|
|
174
|
+
await install.ensureInstructionReferences(testDir);
|
|
175
|
+
|
|
176
|
+
const content = readFileSync(legacyPath, 'utf-8');
|
|
177
|
+
expect(content).toContain('# Project Rules');
|
|
178
|
+
expect(content).toContain('Use TypeScript.');
|
|
179
|
+
expect(content).toContain('@~/.cleo/templates/CLEO-INJECTION.md');
|
|
180
|
+
expect(content).toContain('@.cleo/memory-bridge.md');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('does not create legacy .cursorrules if it does not exist', async () => {
|
|
184
|
+
await install.ensureInstructionReferences(testDir);
|
|
185
|
+
expect(existsSync(join(testDir, '.cursorrules'))).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('does not duplicate references in legacy .cursorrules', async () => {
|
|
189
|
+
const legacyPath = join(testDir, '.cursorrules');
|
|
190
|
+
writeFileSync(
|
|
191
|
+
legacyPath,
|
|
192
|
+
'@~/.cleo/templates/CLEO-INJECTION.md\n@.cleo/memory-bridge.md\n',
|
|
193
|
+
'utf-8',
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
await install.ensureInstructionReferences(testDir);
|
|
197
|
+
|
|
198
|
+
const content = readFileSync(legacyPath, 'utf-8');
|
|
199
|
+
const injectionCount = content.split('@~/.cleo/templates/CLEO-INJECTION.md').length - 1;
|
|
200
|
+
expect(injectionCount).toBe(1);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('registers MCP server in .cursor/mcp.json', async () => {
|
|
204
|
+
const result = await install.install({
|
|
205
|
+
projectDir: testDir,
|
|
206
|
+
mcpServerPath: '/path/to/cleo-mcp.js',
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
expect(result.success).toBe(true);
|
|
210
|
+
expect(result.mcpRegistered).toBe(true);
|
|
211
|
+
expect(result.instructionFileUpdated).toBe(true);
|
|
212
|
+
|
|
213
|
+
const mcpPath = join(testDir, '.cursor', 'mcp.json');
|
|
214
|
+
expect(existsSync(mcpPath)).toBe(true);
|
|
215
|
+
const config = JSON.parse(readFileSync(mcpPath, 'utf-8'));
|
|
216
|
+
expect(config.mcpServers.cleo).toEqual({
|
|
217
|
+
command: 'node',
|
|
218
|
+
args: ['/path/to/cleo-mcp.js'],
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('uninstall removes MCP server from .cursor/mcp.json', async () => {
|
|
223
|
+
await install.install({
|
|
224
|
+
projectDir: testDir,
|
|
225
|
+
mcpServerPath: '/path/to/cleo-mcp.js',
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
await install.uninstall();
|
|
229
|
+
|
|
230
|
+
const mcpPath = join(testDir, '.cursor', 'mcp.json');
|
|
231
|
+
const config = JSON.parse(readFileSync(mcpPath, 'utf-8'));
|
|
232
|
+
expect(config.mcpServers.cleo).toBeUndefined();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('uninstall is no-op when not installed', async () => {
|
|
236
|
+
await expect(install.uninstall()).resolves.toBeUndefined();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('createAdapter factory', () => {
|
|
241
|
+
it('returns a CursorAdapter instance', () => {
|
|
242
|
+
const adapter = createAdapter();
|
|
243
|
+
expect(adapter).toBeInstanceOf(CursorAdapter);
|
|
244
|
+
expect(adapter.id).toBe('cursor');
|
|
245
|
+
});
|
|
246
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-adapter.test.d.ts","sourceRoot":"","sources":["opencode-adapter.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for @cleocode/adapter-opencode
|
|
3
|
+
*
|
|
4
|
+
* Tests the OpenCodeAdapter, OpenCodeHookProvider,
|
|
5
|
+
* OpenCodeSpawnProvider, and OpenCodeInstallProvider.
|
|
6
|
+
*
|
|
7
|
+
* @task T5240
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { createOpenCodeAdapter as createAdapter, OpenCodeAdapter, OpenCodeHookProvider, OpenCodeInstallProvider, OpenCodeSpawnProvider, } from '@cleocode/adapters';
|
|
13
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
14
|
+
describe('OpenCodeAdapter', () => {
|
|
15
|
+
let adapter;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
adapter = new OpenCodeAdapter();
|
|
18
|
+
});
|
|
19
|
+
it('has correct identity fields', () => {
|
|
20
|
+
expect(adapter.id).toBe('opencode');
|
|
21
|
+
expect(adapter.name).toBe('OpenCode');
|
|
22
|
+
expect(adapter.version).toBe('1.0.0');
|
|
23
|
+
});
|
|
24
|
+
it('has correct capabilities', () => {
|
|
25
|
+
expect(adapter.capabilities.supportsHooks).toBe(true);
|
|
26
|
+
expect(adapter.capabilities.supportsSpawn).toBe(true);
|
|
27
|
+
expect(adapter.capabilities.supportsInstall).toBe(true);
|
|
28
|
+
expect(adapter.capabilities.supportsMcp).toBe(true);
|
|
29
|
+
expect(adapter.capabilities.supportsInstructionFiles).toBe(true);
|
|
30
|
+
expect(adapter.capabilities.instructionFilePattern).toBe('AGENTS.md');
|
|
31
|
+
});
|
|
32
|
+
it('supports 6 hook events (6/8 CAAMP)', () => {
|
|
33
|
+
const events = adapter.capabilities.supportedHookEvents;
|
|
34
|
+
expect(events).toHaveLength(6);
|
|
35
|
+
expect(events).toContain('onSessionStart');
|
|
36
|
+
expect(events).toContain('onSessionEnd');
|
|
37
|
+
expect(events).toContain('onToolStart');
|
|
38
|
+
expect(events).toContain('onToolComplete');
|
|
39
|
+
expect(events).toContain('onError');
|
|
40
|
+
expect(events).toContain('onPromptSubmit');
|
|
41
|
+
});
|
|
42
|
+
it('exposes hooks, spawn, and install providers', () => {
|
|
43
|
+
expect(adapter.hooks).toBeInstanceOf(OpenCodeHookProvider);
|
|
44
|
+
expect(adapter.spawn).toBeInstanceOf(OpenCodeSpawnProvider);
|
|
45
|
+
expect(adapter.install).toBeInstanceOf(OpenCodeInstallProvider);
|
|
46
|
+
});
|
|
47
|
+
it('tracks initialization state', async () => {
|
|
48
|
+
expect(adapter.isInitialized()).toBe(false);
|
|
49
|
+
await adapter.initialize('/tmp/test-project');
|
|
50
|
+
expect(adapter.isInitialized()).toBe(true);
|
|
51
|
+
expect(adapter.getProjectDir()).toBe('/tmp/test-project');
|
|
52
|
+
});
|
|
53
|
+
it('clears state on dispose', async () => {
|
|
54
|
+
await adapter.initialize('/tmp/test-project');
|
|
55
|
+
await adapter.dispose();
|
|
56
|
+
expect(adapter.isInitialized()).toBe(false);
|
|
57
|
+
expect(adapter.getProjectDir()).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
it('reports unhealthy when not initialized', async () => {
|
|
60
|
+
const health = await adapter.healthCheck();
|
|
61
|
+
expect(health.healthy).toBe(false);
|
|
62
|
+
expect(health.provider).toBe('opencode');
|
|
63
|
+
expect(health.details?.error).toBe('Adapter not initialized');
|
|
64
|
+
});
|
|
65
|
+
it('reports health after initialization', async () => {
|
|
66
|
+
await adapter.initialize('/tmp/test-project');
|
|
67
|
+
const health = await adapter.healthCheck();
|
|
68
|
+
expect(health.provider).toBe('opencode');
|
|
69
|
+
expect(typeof health.healthy).toBe('boolean');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('OpenCodeHookProvider', () => {
|
|
73
|
+
let hooks;
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
hooks = new OpenCodeHookProvider();
|
|
76
|
+
});
|
|
77
|
+
it('maps session.start to onSessionStart', () => {
|
|
78
|
+
expect(hooks.mapProviderEvent('session.start')).toBe('onSessionStart');
|
|
79
|
+
});
|
|
80
|
+
it('maps session.end to onSessionEnd', () => {
|
|
81
|
+
expect(hooks.mapProviderEvent('session.end')).toBe('onSessionEnd');
|
|
82
|
+
});
|
|
83
|
+
it('maps tool.start to onToolStart', () => {
|
|
84
|
+
expect(hooks.mapProviderEvent('tool.start')).toBe('onToolStart');
|
|
85
|
+
});
|
|
86
|
+
it('maps tool.complete to onToolComplete', () => {
|
|
87
|
+
expect(hooks.mapProviderEvent('tool.complete')).toBe('onToolComplete');
|
|
88
|
+
});
|
|
89
|
+
it('maps error to onError', () => {
|
|
90
|
+
expect(hooks.mapProviderEvent('error')).toBe('onError');
|
|
91
|
+
});
|
|
92
|
+
it('maps prompt.submit to onPromptSubmit', () => {
|
|
93
|
+
expect(hooks.mapProviderEvent('prompt.submit')).toBe('onPromptSubmit');
|
|
94
|
+
});
|
|
95
|
+
it('returns null for unknown events', () => {
|
|
96
|
+
expect(hooks.mapProviderEvent('UnknownEvent')).toBeNull();
|
|
97
|
+
expect(hooks.mapProviderEvent('')).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
it('tracks registration state', async () => {
|
|
100
|
+
expect(hooks.isRegistered()).toBe(false);
|
|
101
|
+
await hooks.registerNativeHooks('/tmp/test');
|
|
102
|
+
expect(hooks.isRegistered()).toBe(true);
|
|
103
|
+
await hooks.unregisterNativeHooks();
|
|
104
|
+
expect(hooks.isRegistered()).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it('exposes event map with 6 entries', () => {
|
|
107
|
+
const map = hooks.getEventMap();
|
|
108
|
+
expect(Object.keys(map)).toHaveLength(6);
|
|
109
|
+
expect(map['session.start']).toBe('onSessionStart');
|
|
110
|
+
expect(map['tool.complete']).toBe('onToolComplete');
|
|
111
|
+
expect(map['prompt.submit']).toBe('onPromptSubmit');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe('OpenCodeSpawnProvider', () => {
|
|
115
|
+
let spawn;
|
|
116
|
+
beforeEach(() => {
|
|
117
|
+
spawn = new OpenCodeSpawnProvider();
|
|
118
|
+
});
|
|
119
|
+
it('canSpawn returns boolean', async () => {
|
|
120
|
+
const result = await spawn.canSpawn();
|
|
121
|
+
expect(typeof result).toBe('boolean');
|
|
122
|
+
});
|
|
123
|
+
it('listRunning returns empty array initially', async () => {
|
|
124
|
+
const running = await spawn.listRunning();
|
|
125
|
+
expect(running).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
it('terminate is no-op for unknown instance', async () => {
|
|
128
|
+
await expect(spawn.terminate('nonexistent')).resolves.toBeUndefined();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('OpenCodeInstallProvider', () => {
|
|
132
|
+
let install;
|
|
133
|
+
let testDir;
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
install = new OpenCodeInstallProvider();
|
|
136
|
+
testDir = join(tmpdir(), `cleo-opencode-test-${Date.now()}`);
|
|
137
|
+
mkdirSync(testDir, { recursive: true });
|
|
138
|
+
});
|
|
139
|
+
afterEach(() => {
|
|
140
|
+
try {
|
|
141
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// cleanup best-effort
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
it('creates AGENTS.md with @-references', async () => {
|
|
148
|
+
await install.ensureInstructionReferences(testDir);
|
|
149
|
+
const content = readFileSync(join(testDir, 'AGENTS.md'), 'utf-8');
|
|
150
|
+
expect(content).toContain('@~/.cleo/templates/CLEO-INJECTION.md');
|
|
151
|
+
expect(content).toContain('@.cleo/memory-bridge.md');
|
|
152
|
+
});
|
|
153
|
+
it('appends missing references to existing AGENTS.md', async () => {
|
|
154
|
+
const existing = '# Project Agents\n\nSome content.\n';
|
|
155
|
+
writeFileSync(join(testDir, 'AGENTS.md'), existing, 'utf-8');
|
|
156
|
+
await install.ensureInstructionReferences(testDir);
|
|
157
|
+
const content = readFileSync(join(testDir, 'AGENTS.md'), 'utf-8');
|
|
158
|
+
expect(content).toContain('# Project Agents');
|
|
159
|
+
expect(content).toContain('@~/.cleo/templates/CLEO-INJECTION.md');
|
|
160
|
+
expect(content).toContain('@.cleo/memory-bridge.md');
|
|
161
|
+
});
|
|
162
|
+
it('does not duplicate existing references', async () => {
|
|
163
|
+
const existing = '@~/.cleo/templates/CLEO-INJECTION.md\n@.cleo/memory-bridge.md\n';
|
|
164
|
+
writeFileSync(join(testDir, 'AGENTS.md'), existing, 'utf-8');
|
|
165
|
+
await install.ensureInstructionReferences(testDir);
|
|
166
|
+
const content = readFileSync(join(testDir, 'AGENTS.md'), 'utf-8');
|
|
167
|
+
const injectionCount = content.split('@~/.cleo/templates/CLEO-INJECTION.md').length - 1;
|
|
168
|
+
expect(injectionCount).toBe(1);
|
|
169
|
+
});
|
|
170
|
+
it('registers MCP server in .opencode/config.json', async () => {
|
|
171
|
+
const result = await install.install({
|
|
172
|
+
projectDir: testDir,
|
|
173
|
+
mcpServerPath: '/path/to/cleo-mcp.js',
|
|
174
|
+
});
|
|
175
|
+
expect(result.success).toBe(true);
|
|
176
|
+
expect(result.mcpRegistered).toBe(true);
|
|
177
|
+
expect(result.instructionFileUpdated).toBe(true);
|
|
178
|
+
const configPath = join(testDir, '.opencode', 'config.json');
|
|
179
|
+
expect(existsSync(configPath)).toBe(true);
|
|
180
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
181
|
+
expect(config.mcpServers.cleo).toEqual({
|
|
182
|
+
command: 'node',
|
|
183
|
+
args: ['/path/to/cleo-mcp.js'],
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
it('uninstall removes MCP server from .opencode/config.json', async () => {
|
|
187
|
+
await install.install({
|
|
188
|
+
projectDir: testDir,
|
|
189
|
+
mcpServerPath: '/path/to/cleo-mcp.js',
|
|
190
|
+
});
|
|
191
|
+
await install.uninstall();
|
|
192
|
+
const configPath = join(testDir, '.opencode', 'config.json');
|
|
193
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
194
|
+
expect(config.mcpServers.cleo).toBeUndefined();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe('createAdapter factory', () => {
|
|
198
|
+
it('returns an OpenCodeAdapter instance', () => {
|
|
199
|
+
const adapter = createAdapter();
|
|
200
|
+
expect(adapter).toBeInstanceOf(OpenCodeAdapter);
|
|
201
|
+
expect(adapter.id).toBe('opencode');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
//# sourceMappingURL=opencode-adapter.test.js.map
|