Skip to content

Commit 5b6036b

Browse files
authored
Merge pull request #40 from bomanaps/add-AIAgentHooks
Implement AI Agent Hooks interface for VSCode extension integration
2 parents e1abca8 + 0118f2b commit 5b6036b

File tree

7 files changed

+649
-0
lines changed

7 files changed

+649
-0
lines changed

packages/extension-core/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,10 @@ export type {
4444
ProgressUpdate,
4545
AIContext,
4646
WorkspaceContext,
47+
AICommand,
48+
AICommandResult,
49+
AICommandHandlerFunction,
50+
AICommandDefinition,
51+
AICommandParameter,
52+
AICommandExample,
4753
} from "./types/index.js";
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# How to Verify AI Agent Hooks Work
2+
3+
## Quick Verification Steps
4+
5+
### 1. Build Check
6+
7+
```bash
8+
# From root directory
9+
pnpm --filter lighthouse-extension run build
10+
pnpm --filter lighthouse-extension run build:tsc
11+
```
12+
13+
If builds succeed, the hooks are properly integrated.
14+
15+
### 2. Type Check
16+
17+
```bash
18+
pnpm --filter lighthouse-extension run build:tsc
19+
```
20+
21+
No TypeScript errors = hooks are correctly typed.
22+
23+
### 3. Test in VSCode Extension Host
24+
25+
#### Option A: Unit Test (Run existing extension test)
26+
27+
```bash
28+
pnpm --filter lighthouse-extension run test -- extension.test.ts
29+
```
30+
31+
Look for the "AI Agent Hooks" test section - it verifies:
32+
33+
- Hooks are exposed via `getAIAgentHooks()`
34+
- All 4 methods are available
35+
- Workspace context can be retrieved
36+
37+
#### Option B: Manual Verification in VSCode
38+
39+
1. **Package the extension:**
40+
41+
```bash
42+
cd packages/vscode-extension
43+
npm run package
44+
```
45+
46+
2. **Install in VSCode:**
47+
- Open VSCode
48+
- Go to Extensions view
49+
- Install from VSIX file
50+
51+
3. **Open Developer Console:**
52+
- Press `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux)
53+
- Type "Developer: Toggle Developer Tools"
54+
- Go to Console tab
55+
56+
4. **Test the hooks:**
57+
58+
```javascript
59+
// Get the extension instance
60+
const ext = vscode.extensions.getExtension("lighthouse-web3.lighthouse-storage-for-vscode");
61+
if (ext && ext.isActive) {
62+
const extension = ext.exports?.extension;
63+
64+
// Get AI hooks
65+
const aiHooks = extension?.getAIAgentHooks();
66+
67+
// Test getWorkspaceContext
68+
const context = await aiHooks?.getWorkspaceContext();
69+
console.log("Workspace context:", context);
70+
71+
// Test onProgress
72+
const unsubscribe = aiHooks?.onProgress((progress) => {
73+
console.log("Progress update:", progress);
74+
});
75+
76+
// Test onAICommand (workspace context command)
77+
const result = await aiHooks?.onAICommand("lighthouse.workspace.context", {});
78+
console.log("Command result:", result);
79+
80+
// Cleanup
81+
unsubscribe?.();
82+
}
83+
```
84+
85+
### 4. Programmatic Test Script
86+
87+
Create a test file `test-hooks-manual.ts`:
88+
89+
```typescript
90+
import { createExtensionCore } from "@lighthouse-tooling/extension-core";
91+
import { AIAgentHooksImpl } from "./src/ai/ai-agent-hooks";
92+
93+
async function testAIAgentHooks() {
94+
console.log("Testing AI Agent Hooks...\n");
95+
96+
// Set API key
97+
process.env.LIGHTHOUSE_API_KEY = "test-key";
98+
99+
// Create extension core
100+
const extensionCore = createExtensionCore();
101+
await extensionCore.initialize();
102+
103+
// Create AI hooks
104+
const aiHooks = new AIAgentHooksImpl(extensionCore);
105+
106+
try {
107+
// Test 1: getWorkspaceContext
108+
console.log("Test 1: getWorkspaceContext()");
109+
const context = await aiHooks.getWorkspaceContext();
110+
console.log("✓ Workspace context retrieved:", !!context);
111+
console.log(" - Project path:", context.projectPath || "N/A");
112+
console.log(" - Files:", context.files?.length || 0);
113+
console.log("");
114+
115+
// Test 2: onAICommand
116+
console.log("Test 2: onAICommand()");
117+
const result = await aiHooks.onAICommand("lighthouse.workspace.context", {});
118+
console.log("✓ Command executed successfully:", !!result);
119+
console.log("");
120+
121+
// Test 3: registerAIFunction
122+
console.log("Test 3: registerAIFunction()");
123+
let customFuncCalled = false;
124+
aiHooks.registerAIFunction("test.custom", async (cmd) => {
125+
customFuncCalled = true;
126+
return { success: true, data: { message: "Custom function called" } };
127+
});
128+
const customResult = await aiHooks.onAICommand("test.custom", {});
129+
console.log("✓ Custom function registered and called:", customFuncCalled);
130+
console.log("");
131+
132+
// Test 4: onProgress
133+
console.log("Test 4: onProgress()");
134+
let progressReceived = false;
135+
const unsubscribe = aiHooks.onProgress((progress) => {
136+
progressReceived = true;
137+
console.log(" Progress update received:", progress.title || "N/A");
138+
});
139+
console.log("✓ Progress callback registered");
140+
unsubscribe();
141+
console.log("✓ Unsubscribe function works");
142+
console.log("");
143+
144+
console.log("✅ All tests passed!");
145+
} catch (error) {
146+
console.error("❌ Test failed:", error);
147+
} finally {
148+
aiHooks.dispose();
149+
await extensionCore.dispose();
150+
}
151+
}
152+
153+
testAIAgentHooks();
154+
```
155+
156+
Run with:
157+
158+
```bash
159+
ts-node packages/vscode-extension/test-hooks-manual.ts
160+
```
161+
162+
### 5. Check Build Output
163+
164+
Verify hooks are in the built code:
165+
166+
```bash
167+
grep -i "getAIAgentHooks\|AIAgentHooks" packages/vscode-extension/dist/extension.js
168+
```
169+
170+
Should show:
171+
172+
- `getAIAgentHooks()` method
173+
- `AIAgentHooksImpl` class
174+
- Import statements
175+
176+
### 6. Lint Check
177+
178+
```bash
179+
pnpm --filter lighthouse-extension run lint
180+
```
181+
182+
Should only show warnings (not errors) - errors mean something is broken.
183+
184+
## Expected Results
185+
186+
✅ **All good if:**
187+
188+
- Build succeeds without errors
189+
- TypeScript compilation succeeds
190+
- `getAIAgentHooks()` returns an object with 4 methods
191+
- No runtime errors when calling hook methods
192+
- Progress callbacks can be registered/unregistered
193+
194+
❌ **Something wrong if:**
195+
196+
- Build fails
197+
- TypeScript errors
198+
- Methods are undefined
199+
- Runtime errors when using hooks
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
* AI Agent Hooks Tests
3+
* @fileoverview Tests for AI Agent Hooks implementation
4+
*/
5+
6+
import { AIAgentHooksImpl } from "../ai/ai-agent-hooks";
7+
import { createExtensionCore, type ExtensionCore } from "@lighthouse-tooling/extension-core";
8+
9+
describe("AIAgentHooksImpl", () => {
10+
let extensionCore: ExtensionCore;
11+
let aiHooks: AIAgentHooksImpl;
12+
13+
beforeEach(() => {
14+
// Set API key for ExtensionCore's AI command handler
15+
process.env.LIGHTHOUSE_API_KEY = "test-api-key";
16+
17+
// Create real extension core
18+
extensionCore = createExtensionCore();
19+
aiHooks = new AIAgentHooksImpl(extensionCore);
20+
});
21+
22+
afterEach(async () => {
23+
// Clean up
24+
if (aiHooks) {
25+
aiHooks.dispose();
26+
}
27+
if (extensionCore && extensionCore.isInitialized()) {
28+
await extensionCore.dispose();
29+
}
30+
});
31+
32+
describe("initialization", () => {
33+
it("should create AI hooks instance", () => {
34+
expect(aiHooks).toBeDefined();
35+
expect(aiHooks).toBeInstanceOf(AIAgentHooksImpl);
36+
});
37+
});
38+
39+
describe("getWorkspaceContext", () => {
40+
it("should get workspace context", async () => {
41+
// Initialize extension core first
42+
await extensionCore.initialize();
43+
44+
const context = await aiHooks.getWorkspaceContext();
45+
46+
expect(context).toBeDefined();
47+
expect(context).toHaveProperty("projectPath");
48+
expect(context).toHaveProperty("files");
49+
expect(context).toHaveProperty("datasets");
50+
});
51+
});
52+
53+
describe("onAICommand", () => {
54+
beforeEach(async () => {
55+
await extensionCore.initialize();
56+
});
57+
58+
it("should handle workspace context command", async () => {
59+
const result = await aiHooks.onAICommand("lighthouse.workspace.context", {});
60+
61+
expect(result).toBeDefined();
62+
expect(result).toHaveProperty("projectPath");
63+
});
64+
65+
it("should handle invalid command gracefully", async () => {
66+
await expect(aiHooks.onAICommand("invalid.command", {})).rejects.toThrow();
67+
});
68+
});
69+
70+
describe("registerAIFunction", () => {
71+
beforeEach(async () => {
72+
await extensionCore.initialize();
73+
});
74+
75+
it("should register custom AI function", async () => {
76+
const handler = jest.fn().mockResolvedValue({
77+
success: true,
78+
data: { result: "test" },
79+
});
80+
81+
aiHooks.registerAIFunction("custom.test", handler);
82+
83+
const result = await aiHooks.onAICommand("custom.test", { test: "value" });
84+
85+
expect(handler).toHaveBeenCalled();
86+
expect(result).toEqual({ result: "test" });
87+
});
88+
});
89+
90+
describe("onProgress", () => {
91+
beforeEach(async () => {
92+
await extensionCore.initialize();
93+
});
94+
95+
it("should subscribe to progress updates", () => {
96+
const callback = jest.fn();
97+
const unsubscribe = aiHooks.onProgress(callback);
98+
99+
expect(typeof unsubscribe).toBe("function");
100+
101+
// Unsubscribe
102+
unsubscribe();
103+
});
104+
105+
it("should allow multiple progress callbacks", () => {
106+
const callback1 = jest.fn();
107+
const callback2 = jest.fn();
108+
109+
const unsubscribe1 = aiHooks.onProgress(callback1);
110+
const unsubscribe2 = aiHooks.onProgress(callback2);
111+
112+
expect(typeof unsubscribe1).toBe("function");
113+
expect(typeof unsubscribe2).toBe("function");
114+
115+
unsubscribe1();
116+
unsubscribe2();
117+
});
118+
});
119+
120+
describe("dispose", () => {
121+
it("should dispose resources", () => {
122+
const callback = jest.fn();
123+
aiHooks.onProgress(callback);
124+
125+
expect(() => aiHooks.dispose()).not.toThrow();
126+
});
127+
});
128+
});

packages/vscode-extension/src/__tests__/extension.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,29 @@ describe("LighthouseVSCodeExtension", () => {
125125
expect(getLanguageFromExtension("test")).toBe("plaintext");
126126
});
127127
});
128+
129+
describe("AI Agent Hooks", () => {
130+
beforeEach(async () => {
131+
await extension.activate();
132+
});
133+
134+
it("should expose AI agent hooks", () => {
135+
const aiHooks = extension.getAIAgentHooks();
136+
137+
expect(aiHooks).toBeDefined();
138+
expect(typeof aiHooks.onAICommand).toBe("function");
139+
expect(typeof aiHooks.getWorkspaceContext).toBe("function");
140+
expect(typeof aiHooks.registerAIFunction).toBe("function");
141+
expect(typeof aiHooks.onProgress).toBe("function");
142+
});
143+
144+
it("should get workspace context via hooks", async () => {
145+
const aiHooks = extension.getAIAgentHooks();
146+
const context = await aiHooks.getWorkspaceContext();
147+
148+
expect(context).toBeDefined();
149+
expect(context).toHaveProperty("projectPath");
150+
expect(context).toHaveProperty("files");
151+
});
152+
});
128153
});

0 commit comments

Comments
 (0)