@codemieai/code 0.0.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/.claude/agents/README.md +298 -0
- package/.claude/agents/release-manager.md +857 -0
- package/CLAUDE.md +856 -0
- package/LICENSE +201 -0
- package/README.md +917 -0
- package/bin/codemie-code.js +23 -0
- package/bin/codemie.js +8 -0
- package/dist/agents/adapters/aider.d.ts +12 -0
- package/dist/agents/adapters/aider.d.ts.map +1 -0
- package/dist/agents/adapters/aider.js +80 -0
- package/dist/agents/adapters/aider.js.map +1 -0
- package/dist/agents/adapters/claude-code.d.ts +12 -0
- package/dist/agents/adapters/claude-code.d.ts.map +1 -0
- package/dist/agents/adapters/claude-code.js +98 -0
- package/dist/agents/adapters/claude-code.js.map +1 -0
- package/dist/agents/adapters/codemie-code.d.ts +12 -0
- package/dist/agents/adapters/codemie-code.d.ts.map +1 -0
- package/dist/agents/adapters/codemie-code.js +42 -0
- package/dist/agents/adapters/codemie-code.js.map +1 -0
- package/dist/agents/adapters/codex.d.ts +12 -0
- package/dist/agents/adapters/codex.d.ts.map +1 -0
- package/dist/agents/adapters/codex.js +80 -0
- package/dist/agents/adapters/codex.js.map +1 -0
- package/dist/agents/registry.d.ts +18 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +35 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +107 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +128 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/install.d.ts +3 -0
- package/dist/cli/commands/install.d.ts.map +1 -0
- package/dist/cli/commands/install.js +76 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/list.d.ts +3 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +50 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +3 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +459 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/run.d.ts +3 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +41 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/uninstall.d.ts +3 -0
- package/dist/cli/commands/uninstall.d.ts.map +1 -0
- package/dist/cli/commands/uninstall.js +71 -0
- package/dist/cli/commands/uninstall.js.map +1 -0
- package/dist/cli/commands/version.d.ts +3 -0
- package/dist/cli/commands/version.d.ts.map +1 -0
- package/dist/cli/commands/version.js +28 -0
- package/dist/cli/commands/version.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +50 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/code/agent-events.d.ts +39 -0
- package/dist/code/agent-events.d.ts.map +1 -0
- package/dist/code/agent-events.js +4 -0
- package/dist/code/agent-events.js.map +1 -0
- package/dist/code/agent.d.ts +19 -0
- package/dist/code/agent.d.ts.map +1 -0
- package/dist/code/agent.js +144 -0
- package/dist/code/agent.js.map +1 -0
- package/dist/code/config.d.ts +13 -0
- package/dist/code/config.d.ts.map +1 -0
- package/dist/code/config.js +41 -0
- package/dist/code/config.js.map +1 -0
- package/dist/code/index.d.ts +19 -0
- package/dist/code/index.d.ts.map +1 -0
- package/dist/code/index.js +400 -0
- package/dist/code/index.js.map +1 -0
- package/dist/code/prompts.d.ts +2 -0
- package/dist/code/prompts.d.ts.map +1 -0
- package/dist/code/prompts.js +45 -0
- package/dist/code/prompts.js.map +1 -0
- package/dist/code/tools/command.d.ts +8 -0
- package/dist/code/tools/command.d.ts.map +1 -0
- package/dist/code/tools/command.js +83 -0
- package/dist/code/tools/command.js.map +1 -0
- package/dist/code/tools/diff-utils.d.ts +2 -0
- package/dist/code/tools/diff-utils.d.ts.map +1 -0
- package/dist/code/tools/diff-utils.js +45 -0
- package/dist/code/tools/diff-utils.js.map +1 -0
- package/dist/code/tools/filesystem.d.ts +11 -0
- package/dist/code/tools/filesystem.d.ts.map +1 -0
- package/dist/code/tools/filesystem.js +442 -0
- package/dist/code/tools/filesystem.js.map +1 -0
- package/dist/code/tools/git.d.ts +7 -0
- package/dist/code/tools/git.d.ts.map +1 -0
- package/dist/code/tools/git.js +111 -0
- package/dist/code/tools/git.js.map +1 -0
- package/dist/code/tools/mcp.d.ts +13 -0
- package/dist/code/tools/mcp.d.ts.map +1 -0
- package/dist/code/tools/mcp.js +230 -0
- package/dist/code/tools/mcp.js.map +1 -0
- package/dist/data/tips.json +118 -0
- package/dist/env/manager.d.ts +14 -0
- package/dist/env/manager.d.ts.map +1 -0
- package/dist/env/manager.js +99 -0
- package/dist/env/manager.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/ui/terminal-ui.d.ts +73 -0
- package/dist/ui/terminal-ui.d.ts.map +1 -0
- package/dist/ui/terminal-ui.js +900 -0
- package/dist/ui/terminal-ui.js.map +1 -0
- package/dist/utils/async-tips.d.ts +64 -0
- package/dist/utils/async-tips.d.ts.map +1 -0
- package/dist/utils/async-tips.js +242 -0
- package/dist/utils/async-tips.js.map +1 -0
- package/dist/utils/env-mapper.d.ts +40 -0
- package/dist/utils/env-mapper.d.ts.map +1 -0
- package/dist/utils/env-mapper.js +122 -0
- package/dist/utils/env-mapper.js.map +1 -0
- package/dist/utils/errors.d.ts +25 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +58 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/exec.d.ts +12 -0
- package/dist/utils/exec.d.ts.map +1 -0
- package/dist/utils/exec.js +49 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +53 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/tips.d.ts +35 -0
- package/dist/utils/tips.d.ts.map +1 -0
- package/dist/utils/tips.js +132 -0
- package/dist/utils/tips.js.map +1 -0
- package/docs/USER_GUIDE.md +573 -0
- package/eslint.config.mjs +43 -0
- package/package.json +66 -0
- package/tests/agent-direct.test.mjs +45 -0
- package/tests/agent-output.test.mjs +64 -0
- package/tests/codemie-code.test.mjs +42 -0
- package/tests/context7-only.test.mjs +42 -0
- package/tests/conversation-flow.test.mjs +63 -0
- package/tests/interactive-simulation.test.mjs +60 -0
- package/tests/live-output.test.mjs +53 -0
- package/tests/mcp-context7.test.mjs +105 -0
- package/tests/mcp-e2e.test.mjs +109 -0
- package/tests/mcp-time-server.test.mjs +58 -0
- package/tests/streaming.test.mjs +57 -0
- package/tests/test-helpers.mjs +94 -0
- package/tests/text-wrapping.test.mjs +33 -0
- package/tests/tool-count.test.mjs +81 -0
- package/tests/ui-format.test.mjs +39 -0
- package/tests/ui-state.test.mjs +72 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: Direct agent tool calling
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { CodeMieAgent } from '../dist/code/agent.js';
|
|
9
|
+
import { loadConfig } from '../dist/code/config.js';
|
|
10
|
+
import { FilesystemTools } from '../dist/code/tools/filesystem.js';
|
|
11
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
12
|
+
|
|
13
|
+
describe('Direct Agent Tool Calling', () => {
|
|
14
|
+
let agent;
|
|
15
|
+
let config;
|
|
16
|
+
|
|
17
|
+
before(() => {
|
|
18
|
+
if (skipIfNoBaseUrl()) return;
|
|
19
|
+
|
|
20
|
+
config = loadConfig(process.cwd());
|
|
21
|
+
const filesystemTools = new FilesystemTools({
|
|
22
|
+
allowedDirectories: [process.cwd()]
|
|
23
|
+
});
|
|
24
|
+
const tools = filesystemTools.getTools();
|
|
25
|
+
agent = new CodeMieAgent(config, tools);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should load configuration correctly', () => {
|
|
29
|
+
if (skipIfNoBaseUrl()) return;
|
|
30
|
+
|
|
31
|
+
assert.ok(config, 'Config should be loaded');
|
|
32
|
+
assert.ok(config.model, 'Config should have a model');
|
|
33
|
+
assert.ok(config.provider, 'Config should have a provider');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should list files in current directory', async () => {
|
|
37
|
+
if (skipIfNoBaseUrl()) return;
|
|
38
|
+
|
|
39
|
+
const response = await agent.chat('list all files in the current directory');
|
|
40
|
+
|
|
41
|
+
assert.ok(response, 'Response should be defined');
|
|
42
|
+
assert.ok(typeof response === 'string', 'Response should be a string');
|
|
43
|
+
assert.ok(response.length > 0, 'Response should not be empty');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: Agent output format
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { CodeMieCode } from '../dist/code/index.js';
|
|
9
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
10
|
+
|
|
11
|
+
describe('Agent Output Format', () => {
|
|
12
|
+
let assistant;
|
|
13
|
+
|
|
14
|
+
before(async () => {
|
|
15
|
+
if (skipIfNoBaseUrl()) return;
|
|
16
|
+
|
|
17
|
+
assistant = new CodeMieCode(process.cwd());
|
|
18
|
+
await assistant.initialize({ showTips: false });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(async () => {
|
|
22
|
+
if (assistant) {
|
|
23
|
+
await assistant.dispose();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should produce correctly formatted output', async () => {
|
|
28
|
+
if (skipIfNoBaseUrl()) return;
|
|
29
|
+
const capturedOutput = {
|
|
30
|
+
contentChunks: [],
|
|
31
|
+
toolCalls: [],
|
|
32
|
+
toolResults: [],
|
|
33
|
+
errors: []
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
await assistant.agent.chatStream('list files in current directory', (event) => {
|
|
37
|
+
switch (event.type) {
|
|
38
|
+
case 'content_chunk':
|
|
39
|
+
capturedOutput.contentChunks.push(event.content);
|
|
40
|
+
break;
|
|
41
|
+
|
|
42
|
+
case 'tool_call_start':
|
|
43
|
+
const args = Object.entries(event.toolArgs)
|
|
44
|
+
.map(([k, v]) => typeof v === 'string' ? v : JSON.stringify(v))
|
|
45
|
+
.join(', ');
|
|
46
|
+
capturedOutput.toolCalls.push(`${event.toolName}(${args})`);
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case 'tool_call_result':
|
|
50
|
+
capturedOutput.toolResults.push(event.result);
|
|
51
|
+
break;
|
|
52
|
+
|
|
53
|
+
case 'tool_call_error':
|
|
54
|
+
capturedOutput.errors.push(event.error);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Validate output format
|
|
60
|
+
assert.ok(capturedOutput.contentChunks.length > 0, 'Should have content chunks');
|
|
61
|
+
assert.ok(capturedOutput.toolCalls.length > 0, 'Should have tool calls');
|
|
62
|
+
assert.ok(capturedOutput.toolResults.length > 0, 'Should have tool results');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: CodeMieCode class tool calling
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { CodeMieCode } from '../dist/code/index.js';
|
|
9
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
10
|
+
|
|
11
|
+
describe('CodeMieCode Tool Calling', () => {
|
|
12
|
+
let assistant;
|
|
13
|
+
|
|
14
|
+
before(async () => {
|
|
15
|
+
if (skipIfNoBaseUrl()) return;
|
|
16
|
+
|
|
17
|
+
assistant = new CodeMieCode(process.cwd());
|
|
18
|
+
await assistant.initialize({ showTips: false });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(async () => {
|
|
22
|
+
if (assistant) {
|
|
23
|
+
await assistant.dispose();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should initialize successfully', () => {
|
|
28
|
+
if (skipIfNoBaseUrl()) return;
|
|
29
|
+
|
|
30
|
+
assert.ok(assistant, 'Assistant should be initialized');
|
|
31
|
+
assert.ok(assistant.agent, 'Assistant should have an agent');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should list files in current directory', async () => {
|
|
35
|
+
if (skipIfNoBaseUrl()) return;
|
|
36
|
+
const response = await assistant.chat('list files in the current directory');
|
|
37
|
+
|
|
38
|
+
assert.ok(response, 'Response should be defined');
|
|
39
|
+
assert.ok(typeof response === 'string', 'Response should be a string');
|
|
40
|
+
assert.ok(response.length > 0, 'Response should not be empty');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: Context7 MCP Server Only
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { MCPTools } from '../dist/code/tools/mcp.js';
|
|
9
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
10
|
+
|
|
11
|
+
describe('Context7 MCP Server Only', () => {
|
|
12
|
+
let mcpTools;
|
|
13
|
+
|
|
14
|
+
before(async () => {
|
|
15
|
+
if (skipIfNoBaseUrl()) return;
|
|
16
|
+
|
|
17
|
+
mcpTools = new MCPTools(process.cwd());
|
|
18
|
+
await mcpTools.initialize(['context7']);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(async () => {
|
|
22
|
+
if (mcpTools) {
|
|
23
|
+
await mcpTools.dispose();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should load tools from context7 server', async () => {
|
|
28
|
+
if (skipIfNoBaseUrl()) return;
|
|
29
|
+
const tools = await mcpTools.getTools();
|
|
30
|
+
assert.ok(tools.length > 0, 'Should have loaded tools from context7');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should have library-related tools', async () => {
|
|
34
|
+
if (skipIfNoBaseUrl()) return;
|
|
35
|
+
const tools = await mcpTools.getTools();
|
|
36
|
+
const hasLibraryTools = tools.some(tool =>
|
|
37
|
+
tool.name.includes('library') || tool.name.includes('resolve')
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
assert.ok(hasLibraryTools, 'Should have library-related tools');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: Conversation flow with multiple questions
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { CodeMieCode } from '../dist/code/index.js';
|
|
9
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
10
|
+
|
|
11
|
+
describe('Conversation Flow', () => {
|
|
12
|
+
let assistant;
|
|
13
|
+
|
|
14
|
+
before(async () => {
|
|
15
|
+
if (skipIfNoBaseUrl()) return;
|
|
16
|
+
|
|
17
|
+
assistant = new CodeMieCode(process.cwd());
|
|
18
|
+
await assistant.initialize({ showTips: false });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(async () => {
|
|
22
|
+
if (assistant) {
|
|
23
|
+
await assistant.dispose();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should handle 3 consecutive questions', async () => {
|
|
28
|
+
if (skipIfNoBaseUrl()) return;
|
|
29
|
+
// Question 1
|
|
30
|
+
const response1 = await assistant.chat('What is 2+2?');
|
|
31
|
+
assert.ok(response1, 'Response 1 should be defined');
|
|
32
|
+
assert.ok(response1.includes('4'), 'Response 1 should contain "4"');
|
|
33
|
+
|
|
34
|
+
// Question 2
|
|
35
|
+
const response2 = await assistant.chat('What is the capital of France?');
|
|
36
|
+
assert.ok(response2, 'Response 2 should be defined');
|
|
37
|
+
assert.ok(response2.toLowerCase().includes('paris'), 'Response 2 should contain "paris"');
|
|
38
|
+
|
|
39
|
+
// Question 3
|
|
40
|
+
const response3 = await assistant.chat('List the files in the current directory');
|
|
41
|
+
assert.ok(response3, 'Response 3 should be defined');
|
|
42
|
+
|
|
43
|
+
// Verify conversation history
|
|
44
|
+
const history = assistant.agent.getHistory();
|
|
45
|
+
assert.ok(history.length > 0, 'History should not be empty');
|
|
46
|
+
|
|
47
|
+
// Count user messages - should be exactly 3
|
|
48
|
+
const userMessages = history.filter(m => m.role === 'user');
|
|
49
|
+
assert.strictEqual(userMessages.length, 3, 'Should have exactly 3 user messages');
|
|
50
|
+
|
|
51
|
+
// Verify all 3 questions are in history
|
|
52
|
+
const questions = [
|
|
53
|
+
'What is 2+2?',
|
|
54
|
+
'What is the capital of France?',
|
|
55
|
+
'List the files in the current directory'
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < questions.length; i++) {
|
|
59
|
+
assert.ok(userMessages[i], `User message ${i} should exist`);
|
|
60
|
+
assert.strictEqual(userMessages[i].content, questions[i], `User message ${i} should match question`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: Interactive conversation simulation
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { CodeMieCode } from '../dist/code/index.js';
|
|
9
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
10
|
+
|
|
11
|
+
describe('Interactive Conversation Simulation', () => {
|
|
12
|
+
let assistant;
|
|
13
|
+
|
|
14
|
+
before(async () => {
|
|
15
|
+
if (skipIfNoBaseUrl()) return;
|
|
16
|
+
|
|
17
|
+
assistant = new CodeMieCode(process.cwd());
|
|
18
|
+
await assistant.initialize({ showTips: false });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(async () => {
|
|
22
|
+
if (assistant) {
|
|
23
|
+
await assistant.dispose();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should handle 3 consecutive interactive questions', async () => {
|
|
28
|
+
if (skipIfNoBaseUrl()) return;
|
|
29
|
+
const questions = [
|
|
30
|
+
'What is 2+2?',
|
|
31
|
+
'What is the capital of France?',
|
|
32
|
+
'List files in the current directory'
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
let completedQuestions = 0;
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < questions.length; i++) {
|
|
38
|
+
const question = questions[i];
|
|
39
|
+
let hasCompleted = false;
|
|
40
|
+
let hasError = false;
|
|
41
|
+
|
|
42
|
+
await assistant.agent.chatStream(question, (event) => {
|
|
43
|
+
if (event.type === 'complete') {
|
|
44
|
+
hasCompleted = true;
|
|
45
|
+
}
|
|
46
|
+
if (event.type === 'error') {
|
|
47
|
+
hasError = true;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
assert.ok(hasCompleted || !hasError, `Question ${i + 1} should complete successfully`);
|
|
52
|
+
|
|
53
|
+
if (hasCompleted) {
|
|
54
|
+
completedQuestions++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
assert.strictEqual(completedQuestions, 3, 'All 3 questions should complete');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: Live output format
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { CodeMieAgent } from '../dist/code/agent.js';
|
|
9
|
+
import { loadConfig } from '../dist/code/config.js';
|
|
10
|
+
import { FilesystemTools } from '../dist/code/tools/filesystem.js';
|
|
11
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
12
|
+
|
|
13
|
+
describe('Live Output Format', () => {
|
|
14
|
+
let agent;
|
|
15
|
+
|
|
16
|
+
before(() => {
|
|
17
|
+
if (skipIfNoBaseUrl()) return;
|
|
18
|
+
|
|
19
|
+
const config = loadConfig(process.cwd());
|
|
20
|
+
const filesystemTools = new FilesystemTools({
|
|
21
|
+
allowedDirectories: [process.cwd()]
|
|
22
|
+
});
|
|
23
|
+
const tools = filesystemTools.getTools();
|
|
24
|
+
agent = new CodeMieAgent(config, tools);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should produce correctly formatted live output', async () => {
|
|
28
|
+
if (skipIfNoBaseUrl()) return;
|
|
29
|
+
let hasToolCall = false;
|
|
30
|
+
let hasToolResult = false;
|
|
31
|
+
let hasContent = false;
|
|
32
|
+
|
|
33
|
+
await agent.chatStream('list files in current directory', (event) => {
|
|
34
|
+
switch (event.type) {
|
|
35
|
+
case 'content_chunk':
|
|
36
|
+
hasContent = true;
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case 'tool_call_start':
|
|
40
|
+
hasToolCall = true;
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case 'tool_call_result':
|
|
44
|
+
hasToolResult = true;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
assert.ok(hasContent, 'Should have content chunks');
|
|
50
|
+
assert.ok(hasToolCall, 'Should have tool calls');
|
|
51
|
+
assert.ok(hasToolResult, 'Should have tool results');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: Context7 MCP Server Integration
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { MCPTools } from '../dist/code/tools/mcp.js';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import * as fs from 'fs/promises';
|
|
12
|
+
import * as os from 'os';
|
|
13
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
describe('MCP Context7 Server', () => {
|
|
19
|
+
let mcpTools;
|
|
20
|
+
let configPath;
|
|
21
|
+
let originalConfig;
|
|
22
|
+
|
|
23
|
+
before(async () => {
|
|
24
|
+
if (skipIfNoBaseUrl()) return;
|
|
25
|
+
configPath = path.join(os.homedir(), '.codemie', 'config.json');
|
|
26
|
+
|
|
27
|
+
// Backup original config
|
|
28
|
+
try {
|
|
29
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
30
|
+
originalConfig = JSON.parse(content);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
33
|
+
originalConfig = {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Ensure context7 and time servers are configured
|
|
37
|
+
const config = { ...originalConfig };
|
|
38
|
+
if (!config.mcpServers) {
|
|
39
|
+
config.mcpServers = {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
config.mcpServers.context7 = {
|
|
43
|
+
command: 'npx',
|
|
44
|
+
args: ['-y', '@upstash/context7-mcp']
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
config.mcpServers.time = {
|
|
48
|
+
transport: 'stdio',
|
|
49
|
+
command: 'uvx',
|
|
50
|
+
args: ['mcp-server-time']
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
54
|
+
|
|
55
|
+
mcpTools = new MCPTools(process.cwd());
|
|
56
|
+
await mcpTools.initialize(['time', 'context7']);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
after(async () => {
|
|
60
|
+
if (mcpTools) {
|
|
61
|
+
await mcpTools.dispose();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Restore original config
|
|
65
|
+
if (originalConfig) {
|
|
66
|
+
await fs.writeFile(configPath, JSON.stringify(originalConfig, null, 2), 'utf-8');
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should load tools from both time and context7 servers', async () => {
|
|
71
|
+
if (skipIfNoBaseUrl()) return;
|
|
72
|
+
const tools = await mcpTools.getTools();
|
|
73
|
+
assert.ok(tools.length > 0, 'Should have loaded tools');
|
|
74
|
+
|
|
75
|
+
const timeTools = tools.filter(t => t.name.includes('time'));
|
|
76
|
+
const context7Tools = tools.filter(t => t.name.includes('library') || t.name.includes('resolve'));
|
|
77
|
+
|
|
78
|
+
assert.ok(timeTools.length > 0, 'Should have time tools');
|
|
79
|
+
assert.ok(context7Tools.length > 0, 'Should have context7 tools');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should query time in Hong Kong', async () => {
|
|
83
|
+
if (skipIfNoBaseUrl()) return;
|
|
84
|
+
const tools = await mcpTools.getTools();
|
|
85
|
+
const getTimeTool = tools.find(t => t.name === 'mcp_get_current_time');
|
|
86
|
+
|
|
87
|
+
const timeResult = await getTimeTool.invoke({ timezone: 'Asia/Hong_Kong' });
|
|
88
|
+
const timeData = typeof timeResult === 'string' ? JSON.parse(timeResult) : timeResult;
|
|
89
|
+
|
|
90
|
+
assert.ok(timeData.timezone && timeData.timezone.includes('Hong_Kong'), 'Should return Hong Kong timezone');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should resolve library ID for langchain', async () => {
|
|
94
|
+
if (skipIfNoBaseUrl()) return;
|
|
95
|
+
const tools = await mcpTools.getTools();
|
|
96
|
+
const resolveTool = tools.find(t => t.name.includes('resolve') && t.name.includes('library'));
|
|
97
|
+
|
|
98
|
+
assert.ok(resolveTool, 'Should have resolve-library-id tool');
|
|
99
|
+
|
|
100
|
+
const resolveResult = await resolveTool.invoke({ libraryName: 'langchain' });
|
|
101
|
+
|
|
102
|
+
assert.ok(resolveResult, 'Should return a result');
|
|
103
|
+
assert.ok(typeof resolveResult === 'string' && resolveResult.length > 0, 'Result should be non-empty string');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: End-to-End Time MCP Server Integration
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { MCPTools } from '../dist/code/tools/mcp.js';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import * as fs from 'fs/promises';
|
|
12
|
+
import * as os from 'os';
|
|
13
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
describe('MCP E2E Time Server Integration', () => {
|
|
19
|
+
let mcpTools;
|
|
20
|
+
let configPath;
|
|
21
|
+
let originalConfig;
|
|
22
|
+
|
|
23
|
+
before(async () => {
|
|
24
|
+
if (skipIfNoBaseUrl()) return;
|
|
25
|
+
configPath = path.join(os.homedir(), '.codemie', 'config.json');
|
|
26
|
+
|
|
27
|
+
// Backup original config
|
|
28
|
+
try {
|
|
29
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
30
|
+
originalConfig = JSON.parse(content);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
33
|
+
originalConfig = {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Ensure time server is configured
|
|
37
|
+
const config = { ...originalConfig };
|
|
38
|
+
if (!config.mcpServers) {
|
|
39
|
+
config.mcpServers = {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
config.mcpServers.time = {
|
|
43
|
+
transport: 'stdio',
|
|
44
|
+
command: 'uvx',
|
|
45
|
+
args: ['mcp-server-time']
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
49
|
+
|
|
50
|
+
mcpTools = new MCPTools(process.cwd());
|
|
51
|
+
await mcpTools.initialize(['time']);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
after(async () => {
|
|
55
|
+
if (mcpTools) {
|
|
56
|
+
await mcpTools.dispose();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Restore original config
|
|
60
|
+
if (originalConfig) {
|
|
61
|
+
await fs.writeFile(configPath, JSON.stringify(originalConfig, null, 2), 'utf-8');
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should load tools from configured time server', async () => {
|
|
66
|
+
if (skipIfNoBaseUrl()) return;
|
|
67
|
+
const tools = await mcpTools.getTools();
|
|
68
|
+
assert.ok(tools.length > 0, 'Should have loaded tools');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should query time in Hong Kong', async () => {
|
|
72
|
+
if (skipIfNoBaseUrl()) return;
|
|
73
|
+
const tools = await mcpTools.getTools();
|
|
74
|
+
const getTimeTool = tools.find(t => t.name === 'mcp_get_current_time');
|
|
75
|
+
|
|
76
|
+
assert.ok(getTimeTool, 'get_current_time tool should exist');
|
|
77
|
+
|
|
78
|
+
const result = await getTimeTool.invoke({ timezone: 'Asia/Hong_Kong' });
|
|
79
|
+
const data = typeof result === 'string' ? JSON.parse(result) : result;
|
|
80
|
+
|
|
81
|
+
assert.ok(data.timezone && data.timezone.includes('Hong_Kong'), 'Should return Hong Kong timezone');
|
|
82
|
+
assert.ok(data.datetime, 'Should return datetime');
|
|
83
|
+
assert.ok(data.day_of_week, 'Should return day of week');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should convert time between timezones', async () => {
|
|
87
|
+
if (skipIfNoBaseUrl()) return;
|
|
88
|
+
const tools = await mcpTools.getTools();
|
|
89
|
+
const convertTimeTool = tools.find(t => t.name === 'mcp_convert_time');
|
|
90
|
+
|
|
91
|
+
assert.ok(convertTimeTool, 'convert_time tool should be available');
|
|
92
|
+
|
|
93
|
+
const convertResult = await convertTimeTool.invoke({
|
|
94
|
+
source_timezone: 'Asia/Hong_Kong',
|
|
95
|
+
time: '14:00',
|
|
96
|
+
target_timezone: 'America/New_York'
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
assert.ok(convertResult, 'Should return a result');
|
|
100
|
+
|
|
101
|
+
const convertData = typeof convertResult === 'string' ? JSON.parse(convertResult) : convertResult;
|
|
102
|
+
|
|
103
|
+
assert.ok(convertData.source, 'Should have source timezone data');
|
|
104
|
+
assert.ok(convertData.target, 'Should have target timezone data');
|
|
105
|
+
assert.ok(convertData.target.datetime, 'Should have target datetime');
|
|
106
|
+
assert.ok(convertData.target.timezone.includes('New_York'), 'Should convert to New York timezone');
|
|
107
|
+
assert.ok(convertData.time_difference, 'Should have time difference');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test: Time MCP Server
|
|
3
|
+
* Uses Node.js native test runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, before, after } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { MCPTools } from '../dist/code/tools/mcp.js';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { skipIfNoBaseUrl } from './test-helpers.mjs';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
|
|
16
|
+
describe('MCP Time Server', () => {
|
|
17
|
+
let mcpTools;
|
|
18
|
+
|
|
19
|
+
before(async () => {
|
|
20
|
+
if (skipIfNoBaseUrl()) return;
|
|
21
|
+
|
|
22
|
+
mcpTools = new MCPTools(process.cwd());
|
|
23
|
+
await mcpTools.initialize(['time']);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
after(async () => {
|
|
27
|
+
if (mcpTools) {
|
|
28
|
+
await mcpTools.dispose();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should load tools from time server', async () => {
|
|
33
|
+
if (skipIfNoBaseUrl()) return;
|
|
34
|
+
const tools = await mcpTools.getTools();
|
|
35
|
+
assert.ok(tools.length > 0, 'Should have loaded tools from time server');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should have get_current_time tool', async () => {
|
|
39
|
+
if (skipIfNoBaseUrl()) return;
|
|
40
|
+
const tools = await mcpTools.getTools();
|
|
41
|
+
const getTimeTool = tools.find(t => t.name === 'mcp_get_current_time');
|
|
42
|
+
assert.ok(getTimeTool, 'get_current_time tool should be available');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should execute get_current_time tool', async () => {
|
|
46
|
+
if (skipIfNoBaseUrl()) return;
|
|
47
|
+
const tools = await mcpTools.getTools();
|
|
48
|
+
const getTimeTool = tools.find(t => t.name === 'mcp_get_current_time');
|
|
49
|
+
|
|
50
|
+
const result = await getTimeTool.invoke({ timezone: 'Europe/Vilnius' });
|
|
51
|
+
|
|
52
|
+
assert.ok(result, 'Tool should return a result');
|
|
53
|
+
|
|
54
|
+
// Try to parse the result
|
|
55
|
+
const data = typeof result === 'string' ? JSON.parse(result) : result;
|
|
56
|
+
assert.ok(data.datetime || data.timezone, 'Result should contain datetime or timezone');
|
|
57
|
+
});
|
|
58
|
+
});
|