@jmylchreest/aide-plugin 0.0.39 → 0.0.42

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.
@@ -1,400 +0,0 @@
1
- /**
2
- * TypeScript bindings for aide
3
- *
4
- * STATUS: UTILITY LIBRARY - Not yet integrated into hooks
5
- *
6
- * This is a high-level TypeScript client for the aide backend, intended
7
- * for future use by tools or plugins that need programmatic access to
8
- * aide memory, tasks, decisions, and messaging features.
9
- *
10
- * Currently, hooks use the lower-level functions in hook-utils.ts which
11
- * directly call the aide CLI via execFileSync. This library provides a
12
- * cleaner, class-based API that could replace those calls in the future.
13
- *
14
- * Provides two modes:
15
- * 1. CLI mode (default) - spawns aide CLI commands
16
- * 2. HTTP mode - connects to running aide server
17
- *
18
- * Future integration:
19
- * - Replace raw CLI calls in hooks with AideMemory class methods
20
- * - Enable HTTP mode for faster IPC when aide server is running
21
- *
22
- * FFI mode (native bindings) requires building the shared library:
23
- * cd aide && go build -buildmode=c-shared -o libaide.so ./ffi
24
- */
25
-
26
- import { execFileSync } from "child_process";
27
- import { join } from "path";
28
-
29
- export interface Memory {
30
- id: string;
31
- category: string;
32
- content: string;
33
- tags: string[];
34
- priority: number;
35
- plan: string;
36
- agent: string;
37
- createdAt: string;
38
- updatedAt: string;
39
- }
40
-
41
- export interface Task {
42
- id: string;
43
- title: string;
44
- description: string;
45
- status: "pending" | "claimed" | "done" | "blocked";
46
- claimedBy: string;
47
- claimedAt: string;
48
- completedAt: string;
49
- result: string;
50
- }
51
-
52
- export interface Decision {
53
- topic: string;
54
- decision: string;
55
- rationale: string;
56
- decidedBy: string;
57
- createdAt: string;
58
- }
59
-
60
- export interface Message {
61
- id: number;
62
- from: string;
63
- to: string;
64
- content: string;
65
- readBy: string[];
66
- createdAt: string;
67
- }
68
-
69
- export type Category =
70
- | "learning"
71
- | "decision"
72
- | "issue"
73
- | "discovery"
74
- | "blocker";
75
-
76
- /**
77
- * AideMemory client - interfaces with aide via CLI or HTTP
78
- */
79
- export class AideMemory {
80
- private mode: "cli" | "http";
81
- private serverUrl?: string;
82
- private cliPath: string;
83
-
84
- constructor(
85
- options: {
86
- mode?: "cli" | "http";
87
- serverUrl?: string;
88
- cliPath?: string;
89
- } = {},
90
- ) {
91
- this.mode = options.mode || "cli";
92
- this.serverUrl = options.serverUrl || "http://localhost:9876";
93
- this.cliPath = options.cliPath || this.findCli();
94
- }
95
-
96
- private findCli(): string {
97
- // Look in common locations
98
- const locations = [
99
- join(process.cwd(), "bin", "aide"),
100
- join(process.cwd(), "aide", "aide"),
101
- "aide", // PATH
102
- ];
103
-
104
- for (const loc of locations) {
105
- try {
106
- execFileSync(loc, ["--help"], { stdio: "ignore" });
107
- return loc;
108
- } catch {
109
- continue;
110
- }
111
- }
112
-
113
- return "aide";
114
- }
115
-
116
- // --- Memory Operations ---
117
-
118
- async addMemory(
119
- content: string,
120
- options: { category?: Category; tags?: string[]; plan?: string } = {},
121
- ): Promise<Memory> {
122
- if (this.mode === "http") {
123
- return this.httpPost("/api/memories", {
124
- content,
125
- category: options.category || "learning",
126
- tags: options.tags || [],
127
- plan: options.plan || "",
128
- });
129
- }
130
-
131
- const args = ["memory", "add"];
132
- if (options.category) args.push(`--category=${options.category}`);
133
- if (options.tags?.length) args.push(`--tags=${options.tags.join(",")}`);
134
- if (options.plan) args.push(`--plan=${options.plan}`);
135
- args.push(content);
136
-
137
- const output = this.runCli(args);
138
- const match = output.match(/Added memory: (\S+)/);
139
- return {
140
- id: match?.[1] || "",
141
- content,
142
- category: options.category || "learning",
143
- } as Memory;
144
- }
145
-
146
- async searchMemories(query: string, limit = 20): Promise<Memory[]> {
147
- if (this.mode === "http") {
148
- return this.httpGet(
149
- `/api/memories/search?q=${encodeURIComponent(query)}&limit=${limit}`,
150
- );
151
- }
152
-
153
- // CLI mode - parse output
154
- const output = this.runCli(["memory", "search", query]);
155
- return this.parseMemoryList(output);
156
- }
157
-
158
- async listMemories(
159
- options: { category?: Category; plan?: string; limit?: number } = {},
160
- ): Promise<Memory[]> {
161
- if (this.mode === "http") {
162
- const params = new URLSearchParams();
163
- if (options.category) params.set("category", options.category);
164
- return this.httpGet(`/api/memories?${params}`);
165
- }
166
-
167
- const args = ["memory", "list"];
168
- if (options.category) args.push(`--category=${options.category}`);
169
- if (options.plan) args.push(`--plan=${options.plan}`);
170
-
171
- const output = this.runCli(args);
172
- return this.parseMemoryList(output);
173
- }
174
-
175
- // --- Task Operations ---
176
-
177
- async createTask(title: string, description = ""): Promise<Task> {
178
- if (this.mode === "http") {
179
- return this.httpPost("/api/tasks", {
180
- title,
181
- description,
182
- status: "pending",
183
- });
184
- }
185
-
186
- const args = ["task", "create", title];
187
- if (description) args.push(`--description=${description}`);
188
-
189
- const output = this.runCli(args);
190
- const match = output.match(/Created task: (\S+)/);
191
- return {
192
- id: match?.[1] || "",
193
- title,
194
- description,
195
- status: "pending",
196
- } as Task;
197
- }
198
-
199
- async claimTask(taskId: string, agentId: string): Promise<Task> {
200
- if (this.mode === "http") {
201
- return this.httpPost("/api/tasks/claim", { taskId, agentId });
202
- }
203
-
204
- this.runCli(["task", "claim", taskId, `--agent=${agentId}`]);
205
- return { id: taskId, claimedBy: agentId, status: "claimed" } as Task;
206
- }
207
-
208
- async completeTask(taskId: string, result = ""): Promise<void> {
209
- if (this.mode === "http") {
210
- await this.httpPatch(`/api/tasks/${taskId}`, { status: "done", result });
211
- return;
212
- }
213
-
214
- const args = ["task", "complete", taskId];
215
- if (result) args.push(`--result=${result}`);
216
- this.runCli(args);
217
- }
218
-
219
- async listTasks(status?: string): Promise<Task[]> {
220
- if (this.mode === "http") {
221
- const params = status ? `?status=${status}` : "";
222
- return this.httpGet(`/api/tasks${params}`);
223
- }
224
-
225
- const args = ["task", "list"];
226
- if (status) args.push(`--status=${status}`);
227
-
228
- const output = this.runCli(args);
229
- return this.parseTaskList(output);
230
- }
231
-
232
- // --- Decision Operations ---
233
-
234
- async setDecision(
235
- topic: string,
236
- decision: string,
237
- rationale = "",
238
- ): Promise<Decision> {
239
- if (this.mode === "http") {
240
- return this.httpPost("/api/decisions", { topic, decision, rationale });
241
- }
242
-
243
- const args = ["decision", "set", topic, decision];
244
- if (rationale) args.push(`--rationale=${rationale}`);
245
- this.runCli(args);
246
-
247
- return { topic, decision, rationale } as Decision;
248
- }
249
-
250
- async getDecision(topic: string): Promise<Decision | null> {
251
- if (this.mode === "http") {
252
- try {
253
- return await this.httpGet(
254
- `/api/decisions/${encodeURIComponent(topic)}`,
255
- );
256
- } catch {
257
- return null;
258
- }
259
- }
260
-
261
- try {
262
- const output = this.runCli(["decision", "get", topic]);
263
- const match = output.match(/^(.+): (.+)$/m);
264
- if (match) {
265
- return { topic: match[1], decision: match[2] } as Decision;
266
- }
267
- } catch {
268
- return null;
269
- }
270
- return null;
271
- }
272
-
273
- // --- Message Operations ---
274
-
275
- async sendMessage(
276
- content: string,
277
- from: string,
278
- to?: string,
279
- ): Promise<Message> {
280
- if (this.mode === "http") {
281
- return this.httpPost("/api/messages", { content, from, to: to || "" });
282
- }
283
-
284
- const args = ["message", "send", content, `--from=${from}`];
285
- if (to) args.push(`--to=${to}`);
286
- this.runCli(args);
287
-
288
- return { content, from, to: to || "" } as Message;
289
- }
290
-
291
- async getMessages(agentId?: string): Promise<Message[]> {
292
- if (this.mode === "http") {
293
- const params = agentId ? `?agent=${agentId}` : "";
294
- return this.httpGet(`/api/messages${params}`);
295
- }
296
-
297
- const args = ["message", "list"];
298
- if (agentId) args.push(`--agent=${agentId}`);
299
-
300
- const output = this.runCli(args);
301
- return this.parseMessageList(output);
302
- }
303
-
304
- // --- Helper Methods ---
305
-
306
- private runCli(args: string[]): string {
307
- try {
308
- // Use execFileSync to avoid shell interpretation of arguments
309
- // This prevents command injection from user-provided content
310
- return execFileSync(this.cliPath, args, {
311
- encoding: "utf-8",
312
- timeout: 10000,
313
- });
314
- } catch (error: any) {
315
- throw new Error(`aide CLI error: ${error.message}`);
316
- }
317
- }
318
-
319
- private async httpGet<T>(path: string): Promise<T> {
320
- const response = await fetch(`${this.serverUrl}${path}`);
321
- if (!response.ok) {
322
- throw new Error(`HTTP ${response.status}: ${await response.text()}`);
323
- }
324
- return response.json() as Promise<T>;
325
- }
326
-
327
- private async httpPost<T>(path: string, body: object): Promise<T> {
328
- const response = await fetch(`${this.serverUrl}${path}`, {
329
- method: "POST",
330
- headers: { "Content-Type": "application/json" },
331
- body: JSON.stringify(body),
332
- });
333
- if (!response.ok) {
334
- throw new Error(`HTTP ${response.status}: ${await response.text()}`);
335
- }
336
- return response.json() as Promise<T>;
337
- }
338
-
339
- private async httpPatch<T>(path: string, body: object): Promise<T> {
340
- const response = await fetch(`${this.serverUrl}${path}`, {
341
- method: "PATCH",
342
- headers: { "Content-Type": "application/json" },
343
- body: JSON.stringify(body),
344
- });
345
- if (!response.ok) {
346
- throw new Error(`HTTP ${response.status}: ${await response.text()}`);
347
- }
348
- return response.json() as Promise<T>;
349
- }
350
-
351
- private parseMemoryList(output: string): Memory[] {
352
- const memories: Memory[] = [];
353
- const lines = output.trim().split("\n");
354
- for (const line of lines) {
355
- const match = line.match(/\[(\w+)\] (\S+): (.+)/);
356
- if (match) {
357
- memories.push({
358
- category: match[1],
359
- id: match[2],
360
- content: match[3],
361
- } as Memory);
362
- }
363
- }
364
- return memories;
365
- }
366
-
367
- private parseTaskList(output: string): Task[] {
368
- const tasks: Task[] = [];
369
- const lines = output.trim().split("\n");
370
- for (const line of lines) {
371
- const match = line.match(/\[(\w+)\] (\S+): (.+)/);
372
- if (match) {
373
- tasks.push({
374
- status: match[1] as Task["status"],
375
- id: match[2],
376
- title: match[3],
377
- } as Task);
378
- }
379
- }
380
- return tasks;
381
- }
382
-
383
- private parseMessageList(output: string): Message[] {
384
- const messages: Message[] = [];
385
- const lines = output.trim().split("\n");
386
- for (const line of lines) {
387
- const match = line.match(/\[(.+?)\] (.+)/);
388
- if (match) {
389
- messages.push({
390
- from: match[1],
391
- content: match[2],
392
- } as Message);
393
- }
394
- }
395
- return messages;
396
- }
397
- }
398
-
399
- // Default export for convenience
400
- export const aideMemory = new AideMemory();