gibi-bot 1.0.0 → 1.1.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/.context.json +47 -3
- package/.github/workflows/npm-publish.yml +33 -0
- package/.github/workflows/release.yml +45 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +2 -0
- package/.prettierignore +3 -0
- package/.prettierrc +7 -0
- package/CHANGELOG.md +45 -0
- package/DISTRIBUTION.md +9 -1
- package/GEMINI.md +28 -9
- package/README.md +55 -28
- package/commitlint.config.js +3 -0
- package/conductor/code_styleguides/general.md +6 -1
- package/conductor/code_styleguides/ts.md +42 -35
- package/conductor/product-guidelines.md +16 -12
- package/conductor/product.md +12 -7
- package/conductor/setup_state.json +1 -1
- package/conductor/tech-stack.md +4 -1
- package/conductor/tracks/slack_bot_20260107/metadata.json +1 -1
- package/conductor/tracks/slack_bot_20260107/plan.md +6 -1
- package/conductor/tracks/slack_bot_20260107/spec.md +9 -6
- package/conductor/tracks.md +2 -1
- package/conductor/workflow.md +74 -53
- package/dist/agents.js +7 -10
- package/dist/agents.test.js +17 -12
- package/dist/app.js +212 -135
- package/dist/config.js +5 -5
- package/dist/context.js +4 -3
- package/dist/context.test.js +2 -4
- package/eslint.config.mjs +17 -0
- package/jest.config.js +4 -3
- package/nodemon.json +5 -9
- package/package.json +34 -4
- package/release.config.js +22 -0
- package/src/agents.test.ts +62 -57
- package/src/agents.ts +94 -82
- package/src/app.d.ts +1 -1
- package/src/app.ts +298 -178
- package/src/config.ts +48 -48
- package/src/context.test.ts +54 -56
- package/src/context.ts +123 -114
- package/test_gemini.js +13 -9
- package/test_gemini_approval.js +13 -9
- package/test_gemini_write.js +19 -9
- package/tests/context.test.ts +145 -0
- package/tsconfig.json +1 -1
- package/src/app.js +0 -55
- package/src/app.js.map +0 -1
package/dist/config.js
CHANGED
|
@@ -46,7 +46,7 @@ const SERVICE_NAME = 'gibi-slack-bot';
|
|
|
46
46
|
const REQUIRED_VARS = [
|
|
47
47
|
{ name: 'SLACK_BOT_TOKEN', message: 'Enter your Slack Bot Token (xoxb-...):' },
|
|
48
48
|
{ name: 'SLACK_SIGNING_SECRET', message: 'Enter your Slack Signing Secret:' },
|
|
49
|
-
{ name: 'SLACK_APP_TOKEN', message: 'Enter your Slack App Token (xapp-...):' }
|
|
49
|
+
{ name: 'SLACK_APP_TOKEN', message: 'Enter your Slack App Token (xapp-...):' },
|
|
50
50
|
];
|
|
51
51
|
async function loadConfig() {
|
|
52
52
|
let updated = false;
|
|
@@ -72,8 +72,8 @@ async function loadConfig() {
|
|
|
72
72
|
type: 'password',
|
|
73
73
|
name: 'value',
|
|
74
74
|
message: message,
|
|
75
|
-
mask: '*'
|
|
76
|
-
}
|
|
75
|
+
mask: '*',
|
|
76
|
+
},
|
|
77
77
|
]);
|
|
78
78
|
const value = response.value;
|
|
79
79
|
if (value) {
|
|
@@ -92,11 +92,11 @@ async function loadConfig() {
|
|
|
92
92
|
}
|
|
93
93
|
async function clearConfig(keysToClear) {
|
|
94
94
|
const varsToProcess = keysToClear
|
|
95
|
-
? REQUIRED_VARS.filter(v => keysToClear.includes(v.name))
|
|
95
|
+
? REQUIRED_VARS.filter((v) => keysToClear.includes(v.name))
|
|
96
96
|
: REQUIRED_VARS;
|
|
97
97
|
for (const { name } of varsToProcess) {
|
|
98
98
|
await keytar.deletePassword(SERVICE_NAME, name);
|
|
99
99
|
delete process.env[name];
|
|
100
100
|
}
|
|
101
|
-
console.log(`Cleared stored configuration for: ${varsToProcess.map(v => v.name).join(', ')}`);
|
|
101
|
+
console.log(`Cleared stored configuration for: ${varsToProcess.map((v) => v.name).join(', ')}`);
|
|
102
102
|
}
|
package/dist/context.js
CHANGED
|
@@ -50,12 +50,13 @@ class ContextManager {
|
|
|
50
50
|
const rawData = fs.readFileSync(this.storagePath, 'utf8');
|
|
51
51
|
const parsed = JSON.parse(rawData);
|
|
52
52
|
// Convert plain objects back to GibiContext with Dates
|
|
53
|
-
Object.values(parsed).forEach((
|
|
53
|
+
Object.values(parsed).forEach((c) => {
|
|
54
|
+
const ctx = c;
|
|
54
55
|
this.contexts.set(ctx.id, {
|
|
55
56
|
...ctx,
|
|
56
57
|
createdAt: new Date(ctx.createdAt),
|
|
57
58
|
lastActiveAt: new Date(ctx.lastActiveAt),
|
|
58
|
-
messages: ctx.messages || [] // Ensure messages exists for old contexts
|
|
59
|
+
messages: ctx.messages || [], // Ensure messages exists for old contexts
|
|
59
60
|
});
|
|
60
61
|
});
|
|
61
62
|
console.log(`[ContextManager] Loaded ${this.contexts.size} contexts from disk`);
|
|
@@ -98,7 +99,7 @@ class ContextManager {
|
|
|
98
99
|
createdAt: new Date(),
|
|
99
100
|
lastActiveAt: new Date(),
|
|
100
101
|
data: {},
|
|
101
|
-
messages: []
|
|
102
|
+
messages: [],
|
|
102
103
|
};
|
|
103
104
|
this.contexts.set(id, context);
|
|
104
105
|
console.log(`[ContextManager] Created new context: ${id}`);
|
package/dist/context.test.js
CHANGED
|
@@ -35,11 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const context_1 = require("./context");
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
-
const path = __importStar(require("path"));
|
|
39
38
|
jest.mock('fs');
|
|
40
39
|
describe('ContextManager', () => {
|
|
41
40
|
let contextManager;
|
|
42
|
-
const mockStoragePath = path.join(process.cwd(), '.context.json');
|
|
43
41
|
beforeEach(() => {
|
|
44
42
|
jest.clearAllMocks();
|
|
45
43
|
fs.existsSync.mockReturnValue(false);
|
|
@@ -73,8 +71,8 @@ describe('ContextManager', () => {
|
|
|
73
71
|
createdAt: new Date().toISOString(),
|
|
74
72
|
lastActiveAt: new Date().toISOString(),
|
|
75
73
|
data: { foo: 'bar' },
|
|
76
|
-
messages: [{ role: 'user', content: 'hello' }]
|
|
77
|
-
}
|
|
74
|
+
messages: [{ role: 'user', content: 'hello' }],
|
|
75
|
+
},
|
|
78
76
|
};
|
|
79
77
|
fs.existsSync.mockReturnValue(true);
|
|
80
78
|
fs.readFileSync.mockReturnValue(JSON.stringify(mockData));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import eslint from '@eslint/js';
|
|
2
|
+
import tseslint from 'typescript-eslint';
|
|
3
|
+
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
4
|
+
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
5
|
+
|
|
6
|
+
export default tseslint.config(
|
|
7
|
+
eslint.configs.recommended,
|
|
8
|
+
...tseslint.configs.recommended,
|
|
9
|
+
eslintPluginPrettierRecommended,
|
|
10
|
+
{
|
|
11
|
+
rules: {
|
|
12
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
13
|
+
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
|
14
|
+
},
|
|
15
|
+
ignores: ['dist/', 'node_modules/', 'coverage/'],
|
|
16
|
+
},
|
|
17
|
+
);
|
package/jest.config.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
const { createDefaultPreset } = require(
|
|
1
|
+
const { createDefaultPreset } = require('ts-jest');
|
|
2
2
|
|
|
3
3
|
const tsJestTransformCfg = createDefaultPreset().transform;
|
|
4
4
|
|
|
5
5
|
/** @type {import("jest").Config} **/
|
|
6
6
|
module.exports = {
|
|
7
|
-
testEnvironment:
|
|
7
|
+
testEnvironment: 'node',
|
|
8
8
|
transform: {
|
|
9
9
|
...tsJestTransformCfg,
|
|
10
10
|
},
|
|
11
|
-
|
|
11
|
+
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
|
|
12
|
+
};
|
package/nodemon.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gibi-bot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/app.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,13 @@
|
|
|
10
10
|
"start": "ts-node src/app.ts",
|
|
11
11
|
"dev": "nodemon",
|
|
12
12
|
"build": "tsc",
|
|
13
|
-
"test": "jest"
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"lint": "eslint 'src/**/*.{ts,js}'",
|
|
15
|
+
"lint:fix": "eslint 'src/**/*.{ts,js}' --fix",
|
|
16
|
+
"format": "prettier --write .",
|
|
17
|
+
"format:check": "prettier --check .",
|
|
18
|
+
"release": "semantic-release",
|
|
19
|
+
"prepare": "husky"
|
|
14
20
|
},
|
|
15
21
|
"repository": {
|
|
16
22
|
"type": "git",
|
|
@@ -33,12 +39,36 @@
|
|
|
33
39
|
"ts-node": "^10.9.2",
|
|
34
40
|
"typescript": "^5.9.3"
|
|
35
41
|
},
|
|
42
|
+
"lint-staged": {
|
|
43
|
+
"src/**/*.{ts,js}": [
|
|
44
|
+
"eslint --fix",
|
|
45
|
+
"prettier --write"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
36
48
|
"devDependencies": {
|
|
49
|
+
"@commitlint/cli": "^20.3.0",
|
|
50
|
+
"@commitlint/config-conventional": "^20.3.0",
|
|
51
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
52
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
53
|
+
"@semantic-release/git": "^10.0.1",
|
|
54
|
+
"@semantic-release/github": "^12.0.2",
|
|
55
|
+
"@semantic-release/npm": "^13.1.3",
|
|
56
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
37
57
|
"@types/inquirer": "^9.0.9",
|
|
38
58
|
"@types/jest": "^30.0.0",
|
|
39
59
|
"@types/keytar": "^4.4.0",
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^8.52.0",
|
|
61
|
+
"@typescript-eslint/parser": "^8.52.0",
|
|
62
|
+
"eslint": "^9.39.2",
|
|
63
|
+
"eslint-config-prettier": "^10.1.8",
|
|
64
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
65
|
+
"husky": "^9.1.7",
|
|
40
66
|
"jest": "^30.2.0",
|
|
67
|
+
"lint-staged": "^16.2.7",
|
|
41
68
|
"nodemon": "^3.1.11",
|
|
42
|
-
"
|
|
69
|
+
"prettier": "^3.7.4",
|
|
70
|
+
"semantic-release": "^25.0.2",
|
|
71
|
+
"ts-jest": "^29.4.6",
|
|
72
|
+
"typescript-eslint": "^8.52.0"
|
|
43
73
|
}
|
|
44
|
-
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
branches: ['main'],
|
|
3
|
+
plugins: [
|
|
4
|
+
'@semantic-release/commit-analyzer',
|
|
5
|
+
'@semantic-release/release-notes-generator',
|
|
6
|
+
'@semantic-release/changelog',
|
|
7
|
+
[
|
|
8
|
+
'@semantic-release/npm',
|
|
9
|
+
{
|
|
10
|
+
provenance: true,
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
'@semantic-release/git',
|
|
15
|
+
{
|
|
16
|
+
assets: ['package.json', 'CHANGELOG.md'],
|
|
17
|
+
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
'@semantic-release/github',
|
|
21
|
+
],
|
|
22
|
+
};
|
package/src/agents.test.ts
CHANGED
|
@@ -5,81 +5,86 @@ import { EventEmitter } from 'events';
|
|
|
5
5
|
jest.mock('child_process');
|
|
6
6
|
|
|
7
7
|
describe('GeminiAgent', () => {
|
|
8
|
-
|
|
8
|
+
let agent: GeminiAgent;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
agent = new GeminiAgent();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should run gemini with the correct arguments', async () => {
|
|
16
|
+
const mockChild = Object.assign(new EventEmitter(), {
|
|
17
|
+
stdout: new EventEmitter(),
|
|
18
|
+
stderr: new EventEmitter(),
|
|
13
19
|
});
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
(spawn as jest.Mock).mockReturnValue(mockChild);
|
|
21
|
+
(spawn as jest.Mock).mockReturnValue(mockChild);
|
|
22
|
+
|
|
23
|
+
const messages = [{ role: 'user', content: 'hello' }];
|
|
24
|
+
const options = { model: 'test-model' };
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
const options = { model: 'test-model' };
|
|
26
|
+
const promise = agent.run(messages, options);
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
// Simulate CLI output
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
mockChild.stdout.emit('data', Buffer.from('hi there'));
|
|
31
|
+
mockChild.emit('close', 0);
|
|
32
|
+
}, 10);
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
setTimeout(() => {
|
|
29
|
-
mockChild.stdout.emit('data', Buffer.from('hi there'));
|
|
30
|
-
mockChild.emit('close', 0);
|
|
31
|
-
}, 10);
|
|
34
|
+
const result = await promise;
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
expect(result).toBe('hi there');
|
|
37
|
+
expect(spawn).toHaveBeenCalledWith('gemini', [
|
|
38
|
+
'-m',
|
|
39
|
+
'test-model',
|
|
40
|
+
'--approval-mode',
|
|
41
|
+
'yolo',
|
|
42
|
+
'User: hello\nAssistant:',
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
34
45
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'User: hello\nAssistant:'
|
|
40
|
-
]);
|
|
46
|
+
it('should handle errors from the CLI', async () => {
|
|
47
|
+
const mockChild = Object.assign(new EventEmitter(), {
|
|
48
|
+
stdout: new EventEmitter(),
|
|
49
|
+
stderr: new EventEmitter(),
|
|
41
50
|
});
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
const mockChild = new EventEmitter() as any;
|
|
45
|
-
mockChild.stdout = new EventEmitter();
|
|
46
|
-
mockChild.stderr = new EventEmitter();
|
|
47
|
-
|
|
48
|
-
(spawn as jest.Mock).mockReturnValue(mockChild);
|
|
52
|
+
(spawn as jest.Mock).mockReturnValue(mockChild);
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
const messages = [{ role: 'user', content: 'hello' }];
|
|
55
|
+
const promise = agent.run(messages);
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
mockChild.stderr.emit('data', Buffer.from('something went wrong'));
|
|
59
|
+
mockChild.emit('close', 1);
|
|
60
|
+
}, 10);
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
await expect(promise).rejects.toThrow('gemini CLI exited with code 1: something went wrong');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should include PLAN_MODE_PROMPT in plan mode', async () => {
|
|
66
|
+
const mockChild = Object.assign(new EventEmitter(), {
|
|
67
|
+
stdout: new EventEmitter(),
|
|
68
|
+
stderr: new EventEmitter(),
|
|
59
69
|
});
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
const mockChild = new EventEmitter() as any;
|
|
63
|
-
mockChild.stdout = new EventEmitter();
|
|
64
|
-
mockChild.stderr = new EventEmitter();
|
|
65
|
-
|
|
66
|
-
(spawn as jest.Mock).mockReturnValue(mockChild);
|
|
71
|
+
(spawn as jest.Mock).mockReturnValue(mockChild);
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
const messages = [{ role: 'user', content: 'plan this' }];
|
|
74
|
+
const options = { mode: 'plan' };
|
|
70
75
|
|
|
71
|
-
|
|
76
|
+
const promise = agent.run(messages, options);
|
|
72
77
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
mockChild.stdout.emit('data', Buffer.from('here is the plan'));
|
|
80
|
+
mockChild.emit('close', 0);
|
|
81
|
+
}, 10);
|
|
77
82
|
|
|
78
|
-
|
|
83
|
+
await promise;
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
const callArgs = (spawn as jest.Mock).mock.calls[0][1];
|
|
86
|
+
const lastArg = callArgs[callArgs.length - 1];
|
|
87
|
+
expect(lastArg).toContain('expert software architect');
|
|
88
|
+
expect(lastArg).toContain('User: plan this');
|
|
89
|
+
});
|
|
85
90
|
});
|
package/src/agents.ts
CHANGED
|
@@ -2,111 +2,123 @@ import { spawn } from 'child_process';
|
|
|
2
2
|
import { PLAN_MODE_PROMPT } from './prompts';
|
|
3
3
|
|
|
4
4
|
export interface Agent {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
name: string;
|
|
6
|
+
run(
|
|
7
|
+
messages: Array<{ role: string; content: string }>,
|
|
8
|
+
options?: Record<string, unknown>,
|
|
9
|
+
): Promise<string>;
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export class GeminiAgent implements Agent {
|
|
10
|
-
|
|
13
|
+
name = 'gemini';
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
async run(
|
|
16
|
+
messages: Array<{ role: string; content: string }>,
|
|
17
|
+
options: Record<string, unknown> = {},
|
|
18
|
+
): Promise<string> {
|
|
19
|
+
const { model, mode } = options as { model?: string; mode?: string };
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
let prompt =
|
|
22
|
+
messages.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`).join('\n') +
|
|
23
|
+
'\nAssistant:';
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const args = [];
|
|
24
|
-
if (model) {
|
|
25
|
-
args.push('-m', model);
|
|
26
|
-
}
|
|
27
|
-
args.push('--approval-mode', 'yolo');
|
|
28
|
-
args.push(prompt);
|
|
29
|
-
|
|
30
|
-
console.log(`[GeminiAgent] Spawning gemini with args: ${JSON.stringify(args)}`);
|
|
31
|
-
return this.spawnProcess('gemini', args);
|
|
25
|
+
if (mode === 'plan') {
|
|
26
|
+
prompt = `${PLAN_MODE_PROMPT}\n\n${prompt}`;
|
|
32
27
|
}
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let responseText = '';
|
|
38
|
-
let errorText = '';
|
|
39
|
-
|
|
40
|
-
child.stdout.on('data', (data: any) => {
|
|
41
|
-
responseText += data.toString();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
child.stderr.on('data', (data: any) => {
|
|
45
|
-
errorText += data.toString();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
child.on('close', (code: number) => {
|
|
49
|
-
if (code !== 0) {
|
|
50
|
-
reject(new Error(`${command} CLI exited with code ${code}: ${errorText}`));
|
|
51
|
-
} else {
|
|
52
|
-
resolve(responseText.trim());
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
child.on('error', (err: any) => reject(err));
|
|
57
|
-
});
|
|
29
|
+
const args = [];
|
|
30
|
+
if (model) {
|
|
31
|
+
args.push('-m', model);
|
|
58
32
|
}
|
|
33
|
+
args.push('--approval-mode', 'yolo');
|
|
34
|
+
args.push(prompt);
|
|
35
|
+
|
|
36
|
+
console.log(`[GeminiAgent] Spawning gemini with args: ${JSON.stringify(args)}`);
|
|
37
|
+
return this.spawnProcess('gemini', args);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected spawnProcess(command: string, args: string[]): Promise<string> {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const child = spawn(command, args);
|
|
43
|
+
let responseText = '';
|
|
44
|
+
let errorText = '';
|
|
45
|
+
|
|
46
|
+
child.stdout.on('data', (data: Buffer) => {
|
|
47
|
+
responseText += data.toString();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
child.stderr.on('data', (data: Buffer) => {
|
|
51
|
+
errorText += data.toString();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
child.on('close', (code: number) => {
|
|
55
|
+
if (code !== 0) {
|
|
56
|
+
reject(new Error(`${command} CLI exited with code ${code}: ${errorText}`));
|
|
57
|
+
} else {
|
|
58
|
+
resolve(responseText.trim());
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
child.on('error', (err: Error) => reject(err));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
export class ClaudeAgent extends GeminiAgent {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
name = 'claude';
|
|
69
|
+
|
|
70
|
+
async run(
|
|
71
|
+
messages: Array<{ role: string; content: string }>,
|
|
72
|
+
options: Record<string, unknown> = {},
|
|
73
|
+
): Promise<string> {
|
|
74
|
+
// Basic implementation for Claude - similar structure, different CLI
|
|
75
|
+
// Assuming 'claude' CLI accepts similar prompt structure or just a string
|
|
76
|
+
const { mode } = options as { mode?: string };
|
|
77
|
+
|
|
78
|
+
let prompt =
|
|
79
|
+
messages.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`).join('\n') +
|
|
80
|
+
'\nAssistant:';
|
|
81
|
+
|
|
82
|
+
if (mode === 'plan') {
|
|
83
|
+
prompt = `${PLAN_MODE_PROMPT}\n\n${prompt}`;
|
|
84
|
+
}
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
const args = [prompt];
|
|
87
|
+
// Add specific arguments if Claude CLI requires them, e.g. -p or similar.
|
|
88
|
+
// logic here assumes usage: claude "prompt"
|
|
80
89
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
console.log(`[ClaudeAgent] Spawning claude with args length: ${args.length}`);
|
|
91
|
+
return this.spawnProcess('claude', args);
|
|
92
|
+
}
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
export class CursorAgent extends GeminiAgent {
|
|
87
|
-
|
|
96
|
+
name = 'cursor';
|
|
88
97
|
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
async run(
|
|
99
|
+
messages: Array<{ role: string; content: string }>,
|
|
100
|
+
options: Record<string, unknown> = {},
|
|
101
|
+
): Promise<string> {
|
|
102
|
+
const { mode } = options as { mode?: string };
|
|
91
103
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
104
|
+
let prompt =
|
|
105
|
+
messages.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`).join('\n') +
|
|
106
|
+
'\nAssistant:';
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
if (mode === 'plan') {
|
|
109
|
+
prompt = `${PLAN_MODE_PROMPT}\n\n${prompt}`;
|
|
110
|
+
}
|
|
99
111
|
|
|
100
|
-
|
|
101
|
-
|
|
112
|
+
const args = [prompt];
|
|
113
|
+
// Logic assumes usage: cursor "prompt" - this is hypothetical as 'cursor' CLI might differ
|
|
102
114
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
console.log(`[CursorAgent] Spawning cursor with args length: ${args.length}`);
|
|
116
|
+
return this.spawnProcess('cursor', args);
|
|
117
|
+
}
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
export const agentRegistry: Record<string, Agent> = {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
gemini: new GeminiAgent(),
|
|
122
|
+
claude: new ClaudeAgent(),
|
|
123
|
+
cursor: new CursorAgent(),
|
|
112
124
|
};
|
package/src/app.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export {};
|
|
2
|
-
//# sourceMappingURL=app.d.ts.map
|
|
2
|
+
//# sourceMappingURL=app.d.ts.map
|