@jmylchreest/aide-plugin 0.0.60 → 0.0.62
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/package.json +1 -1
- package/src/cli/codex-config.ts +15 -0
- package/src/cli/hook.ts +2 -0
- package/src/core/mcp-sync.ts +0 -16
- package/src/core/read-tracking.ts +38 -4
- package/src/core/search-enrichment.ts +208 -0
- package/src/core/tool-observe.ts +284 -0
- package/src/hooks/hud-updater.ts +0 -23
- package/src/hooks/search-enrichment.ts +111 -0
- package/src/hooks/session-start.ts +23 -0
- package/src/hooks/skill-injector.ts +30 -1
- package/src/hooks/subagent-tracker.ts +7 -85
- package/src/hooks/task-completed.ts +0 -17
- package/src/hooks/tool-observe.ts +97 -0
- package/src/lib/aide-downloader.ts +0 -8
- package/src/lib/hook-utils.ts +0 -13
- package/src/lib/logger.ts +1 -68
- package/src/lib/usage.ts +1 -30
- package/src/opencode/hooks.ts +36 -14
- package/src/lib/worktree.ts +0 -457
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Search Enrichment Hook (PreToolUse)
|
|
4
|
+
*
|
|
5
|
+
* Enriches Grep tool calls with code index context — symbol definitions,
|
|
6
|
+
* file locations, and reference counts. This gives the agent structural
|
|
7
|
+
* awareness without additional tool calls.
|
|
8
|
+
*
|
|
9
|
+
* This is a soft advisory — it never blocks, only injects additive context.
|
|
10
|
+
*
|
|
11
|
+
* Core logic is in src/core/search-enrichment.ts for cross-platform reuse.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readStdin } from "../lib/hook-utils.js";
|
|
15
|
+
import { debug } from "../lib/logger.js";
|
|
16
|
+
import { checkSearchEnrichment } from "../core/search-enrichment.js";
|
|
17
|
+
import { findAideBinary } from "../core/aide-client.js";
|
|
18
|
+
import { recordTokenEvent } from "../core/read-tracking.js";
|
|
19
|
+
|
|
20
|
+
const SOURCE = "search-enrichment";
|
|
21
|
+
|
|
22
|
+
interface HookInput {
|
|
23
|
+
hook_event_name: string;
|
|
24
|
+
session_id: string;
|
|
25
|
+
cwd: string;
|
|
26
|
+
tool_name?: string;
|
|
27
|
+
agent_name?: string;
|
|
28
|
+
agent_id?: string;
|
|
29
|
+
tool_input?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface HookOutput {
|
|
33
|
+
continue: boolean;
|
|
34
|
+
message?: string;
|
|
35
|
+
hookSpecificOutput?: {
|
|
36
|
+
hookEventName: string;
|
|
37
|
+
additionalContext?: string;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function main(): Promise<void> {
|
|
42
|
+
try {
|
|
43
|
+
const input = await readStdin();
|
|
44
|
+
if (!input.trim()) {
|
|
45
|
+
console.log(JSON.stringify({ continue: true }));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const data: HookInput = JSON.parse(input);
|
|
50
|
+
const toolName = data.tool_name || "";
|
|
51
|
+
const toolInput = data.tool_input || {};
|
|
52
|
+
const cwd = data.cwd || process.cwd();
|
|
53
|
+
|
|
54
|
+
const binary = findAideBinary({
|
|
55
|
+
cwd,
|
|
56
|
+
pluginRoot:
|
|
57
|
+
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const result = checkSearchEnrichment(toolName, toolInput, cwd, binary);
|
|
61
|
+
|
|
62
|
+
if (result.shouldEnrich && result.enrichment) {
|
|
63
|
+
debug(SOURCE, `Enriching grep with code index context`);
|
|
64
|
+
|
|
65
|
+
// Record token event for search enrichment
|
|
66
|
+
if (binary) {
|
|
67
|
+
try {
|
|
68
|
+
const tokens = Math.round(result.enrichment.length / 3.0);
|
|
69
|
+
recordTokenEvent(binary, cwd, "context_injected", "enrichment", "search-enrichment", tokens);
|
|
70
|
+
} catch {
|
|
71
|
+
// Non-fatal
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const output: HookOutput = {
|
|
76
|
+
continue: true,
|
|
77
|
+
hookSpecificOutput: {
|
|
78
|
+
hookEventName: "PreToolUse",
|
|
79
|
+
additionalContext: result.enrichment,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
console.log(JSON.stringify(output));
|
|
83
|
+
} else {
|
|
84
|
+
console.log(JSON.stringify({ continue: true }));
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
debug(SOURCE, `Hook error: ${error}`);
|
|
88
|
+
console.log(JSON.stringify({ continue: true }));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
process.on("uncaughtException", (err) => {
|
|
93
|
+
debug(SOURCE, `UNCAUGHT EXCEPTION: ${err}`);
|
|
94
|
+
try {
|
|
95
|
+
console.log(JSON.stringify({ continue: true }));
|
|
96
|
+
} catch {
|
|
97
|
+
console.log('{"continue":true}');
|
|
98
|
+
}
|
|
99
|
+
process.exit(0);
|
|
100
|
+
});
|
|
101
|
+
process.on("unhandledRejection", (reason) => {
|
|
102
|
+
debug(SOURCE, `UNHANDLED REJECTION: ${reason}`);
|
|
103
|
+
try {
|
|
104
|
+
console.log(JSON.stringify({ continue: true }));
|
|
105
|
+
} catch {
|
|
106
|
+
console.log('{"continue":true}');
|
|
107
|
+
}
|
|
108
|
+
process.exit(0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
main();
|
|
@@ -26,6 +26,7 @@ import { homedir } from "os";
|
|
|
26
26
|
import { Logger, debug, setDebugCwd } from "../lib/logger.js";
|
|
27
27
|
import { readStdin, detectPlatform } from "../lib/hook-utils.js";
|
|
28
28
|
import { findAideBinary, ensureAideBinary } from "../lib/aide-downloader.js";
|
|
29
|
+
import { recordTokenEvent } from "../core/read-tracking.js";
|
|
29
30
|
import {
|
|
30
31
|
ensureDirectories as coreEnsureDirectories,
|
|
31
32
|
loadConfig as coreLoadConfig,
|
|
@@ -458,6 +459,28 @@ async function main(): Promise<void> {
|
|
|
458
459
|
log.end("buildWelcomeContext");
|
|
459
460
|
debugLog(`buildWelcomeContext complete (${Date.now() - hookStart}ms)`);
|
|
460
461
|
|
|
462
|
+
// Record token events for context injection
|
|
463
|
+
try {
|
|
464
|
+
const binary = findAideBinary(cwd);
|
|
465
|
+
if (binary && context) {
|
|
466
|
+
const memoryTokens = Math.round(
|
|
467
|
+
([...memories.static.global, ...memories.static.project, ...memories.dynamic.sessions]
|
|
468
|
+
.join("").length) / 3.0
|
|
469
|
+
);
|
|
470
|
+
const decisionTokens = Math.round(
|
|
471
|
+
memories.static.decisions.join("").length / 3.0
|
|
472
|
+
);
|
|
473
|
+
if (memoryTokens > 0) {
|
|
474
|
+
recordTokenEvent(binary, cwd, "context_injected", "memory", "session-start", memoryTokens);
|
|
475
|
+
}
|
|
476
|
+
if (decisionTokens > 0) {
|
|
477
|
+
recordTokenEvent(binary, cwd, "context_injected", "decision", "session-start", decisionTokens);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} catch {
|
|
481
|
+
// Non-fatal — don't break session start for token tracking
|
|
482
|
+
}
|
|
483
|
+
|
|
461
484
|
log.end("total");
|
|
462
485
|
log.info("Session start complete");
|
|
463
486
|
debugLog(`Flushing logs to ${log.getLogFile()}...`);
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
formatSkillsContext as coreFormatSkillsContext,
|
|
26
26
|
} from "../core/skill-matcher.js";
|
|
27
27
|
import type { Skill } from "../core/types.js";
|
|
28
|
+
import { findAideBinary } from "../core/aide-client.js";
|
|
29
|
+
import { recordObserveEvent } from "../core/read-tracking.js";
|
|
28
30
|
|
|
29
31
|
const SOURCE = "skill-injector";
|
|
30
32
|
|
|
@@ -201,6 +203,33 @@ async function main(): Promise<void> {
|
|
|
201
203
|
log.info(
|
|
202
204
|
`Injecting ${matched.length} skills: ${matched.map((s) => s.name).join(", ")}`,
|
|
203
205
|
);
|
|
206
|
+
|
|
207
|
+
const skillContext = formatSkillsContext(matched);
|
|
208
|
+
|
|
209
|
+
// Record one observe event per matched skill so the dashboard can
|
|
210
|
+
// attribute injected tokens to the specific skills (not just a
|
|
211
|
+
// single "skill-injector" aggregate). Subtype="skill" keeps the
|
|
212
|
+
// category roll-up; Name carries the per-skill identifier.
|
|
213
|
+
try {
|
|
214
|
+
const binary = findAideBinary({ cwd });
|
|
215
|
+
if (binary) {
|
|
216
|
+
for (const skill of matched) {
|
|
217
|
+
const text = `### ${skill.name}\n${skill.description ?? ""}\n${skill.content}`;
|
|
218
|
+
const tokens = Math.round(text.length / 3.0);
|
|
219
|
+
recordObserveEvent(binary, cwd, {
|
|
220
|
+
kind: "injection",
|
|
221
|
+
name: skill.name,
|
|
222
|
+
category: "inject",
|
|
223
|
+
subtype: "skill",
|
|
224
|
+
tokens,
|
|
225
|
+
file: SOURCE,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} catch {
|
|
230
|
+
// Non-fatal
|
|
231
|
+
}
|
|
232
|
+
|
|
204
233
|
debugLog(`Flushing logs...`);
|
|
205
234
|
log.flush();
|
|
206
235
|
debugLog(`Hook complete (${Date.now() - hookStart}ms total)`);
|
|
@@ -209,7 +238,7 @@ async function main(): Promise<void> {
|
|
|
209
238
|
continue: true,
|
|
210
239
|
hookSpecificOutput: {
|
|
211
240
|
hookEventName: "UserPromptSubmit",
|
|
212
|
-
additionalContext:
|
|
241
|
+
additionalContext: skillContext,
|
|
213
242
|
},
|
|
214
243
|
};
|
|
215
244
|
console.log(JSON.stringify(output));
|
|
@@ -20,12 +20,6 @@ import { Logger } from "../lib/logger.js";
|
|
|
20
20
|
import { readStdin, setMemoryState } from "../lib/hook-utils.js";
|
|
21
21
|
import { findAideBinary } from "../core/aide-client.js";
|
|
22
22
|
import { refreshHud } from "../lib/hud.js";
|
|
23
|
-
import {
|
|
24
|
-
getWorktreeForAgent,
|
|
25
|
-
markWorktreeComplete,
|
|
26
|
-
discoverWorktrees,
|
|
27
|
-
Worktree,
|
|
28
|
-
} from "../lib/worktree.js";
|
|
29
23
|
|
|
30
24
|
// Global logger instance
|
|
31
25
|
let log: Logger | null = null;
|
|
@@ -207,36 +201,15 @@ function fetchSubagentMemories(cwd: string): {
|
|
|
207
201
|
/**
|
|
208
202
|
* Build context for subagent injection
|
|
209
203
|
*/
|
|
210
|
-
function buildSubagentContext(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
},
|
|
216
|
-
worktree?: Worktree,
|
|
217
|
-
): string {
|
|
204
|
+
function buildSubagentContext(memories: {
|
|
205
|
+
global: string[];
|
|
206
|
+
project: string[];
|
|
207
|
+
decisions: string[];
|
|
208
|
+
}): string {
|
|
218
209
|
const lines: string[] = [];
|
|
219
210
|
|
|
220
211
|
lines.push("<aide-subagent-context>");
|
|
221
212
|
|
|
222
|
-
// Inject worktree information if this is a swarm agent
|
|
223
|
-
if (worktree) {
|
|
224
|
-
lines.push("");
|
|
225
|
-
lines.push("## Swarm Worktree");
|
|
226
|
-
lines.push("");
|
|
227
|
-
lines.push(`You are working in an isolated git worktree for swarm mode.`);
|
|
228
|
-
lines.push(`- **Worktree Path**: ${worktree.path}`);
|
|
229
|
-
lines.push(`- **Branch**: ${worktree.branch}`);
|
|
230
|
-
lines.push(`- **Story ID**: ${worktree.taskId || "unknown"}`);
|
|
231
|
-
lines.push("");
|
|
232
|
-
lines.push(
|
|
233
|
-
`**IMPORTANT**: All file operations should be performed in: ${worktree.path}`,
|
|
234
|
-
);
|
|
235
|
-
lines.push(
|
|
236
|
-
`Commit your changes to the ${worktree.branch} branch when complete.`,
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
213
|
if (memories.global.length > 0) {
|
|
241
214
|
lines.push("");
|
|
242
215
|
lines.push("## User Preferences");
|
|
@@ -349,46 +322,6 @@ async function processSubagentStart(
|
|
|
349
322
|
refreshHud(cwd, session_id);
|
|
350
323
|
log?.end("refreshHud");
|
|
351
324
|
|
|
352
|
-
// Auto-discover any worktrees created by the orchestrator via git commands
|
|
353
|
-
// This ensures we track worktrees even if they weren't created via our library
|
|
354
|
-
log?.start("discoverWorktrees");
|
|
355
|
-
const discovered = discoverWorktrees(cwd);
|
|
356
|
-
if (discovered.length > 0) {
|
|
357
|
-
log?.info(`Auto-discovered ${discovered.length} worktrees`);
|
|
358
|
-
}
|
|
359
|
-
log?.end("discoverWorktrees", { discovered: discovered.length });
|
|
360
|
-
|
|
361
|
-
// Check if this agent has an associated worktree (swarm mode)
|
|
362
|
-
// Match by agent_id or by pattern in worktree name
|
|
363
|
-
log?.start("checkWorktree");
|
|
364
|
-
let worktree = getWorktreeForAgent(cwd, agent_id);
|
|
365
|
-
|
|
366
|
-
// If no direct match, try to match by agent_id pattern in worktree name
|
|
367
|
-
// This handles cases where worktree was created before agent_id was known
|
|
368
|
-
if (!worktree) {
|
|
369
|
-
const { loadWorktreeState } = await import("../lib/worktree.js");
|
|
370
|
-
const state = loadWorktreeState(cwd);
|
|
371
|
-
// Look for worktree with matching name pattern (e.g., "story-auth" matches "agent-auth")
|
|
372
|
-
const agentPattern = agent_id.replace(/^agent-/, "");
|
|
373
|
-
worktree = state.active.find(
|
|
374
|
-
(w) => w.name.includes(agentPattern) && !w.agentId,
|
|
375
|
-
);
|
|
376
|
-
if (worktree) {
|
|
377
|
-
// Assign this agent to the worktree
|
|
378
|
-
worktree.agentId = agent_id;
|
|
379
|
-
const { saveWorktreeState } = await import("../lib/worktree.js");
|
|
380
|
-
saveWorktreeState(cwd, state);
|
|
381
|
-
log?.info(`Assigned worktree ${worktree.name} to agent ${agent_id}`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (worktree) {
|
|
386
|
-
log?.info(
|
|
387
|
-
`Found worktree for agent ${agent_id}: ${worktree.path} (branch: ${worktree.branch})`,
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
log?.end("checkWorktree", { hasWorktree: !!worktree });
|
|
391
|
-
|
|
392
325
|
// Fetch memories for subagent context injection
|
|
393
326
|
log?.start("fetchMemories");
|
|
394
327
|
const memories = fetchSubagentMemories(cwd);
|
|
@@ -399,9 +332,9 @@ async function processSubagentStart(
|
|
|
399
332
|
});
|
|
400
333
|
|
|
401
334
|
// Always build and inject context (messaging section is unconditional)
|
|
402
|
-
const context = buildSubagentContext(memories
|
|
335
|
+
const context = buildSubagentContext(memories);
|
|
403
336
|
log?.info(
|
|
404
|
-
`Injecting context for subagent: ${memories.global.length} preferences, ${memories.project.length} project, ${memories.decisions.length} decisions
|
|
337
|
+
`Injecting context for subagent: ${memories.global.length} preferences, ${memories.project.length} project, ${memories.decisions.length} decisions`,
|
|
405
338
|
);
|
|
406
339
|
return context;
|
|
407
340
|
}
|
|
@@ -422,17 +355,6 @@ async function processSubagentStop(data: SubagentStopInput): Promise<void> {
|
|
|
422
355
|
setAgentState(cwd, agent_id, "endedAt", new Date().toISOString());
|
|
423
356
|
log?.end("updateAgentStatus");
|
|
424
357
|
|
|
425
|
-
// Mark worktree as agent-complete if this agent had one (swarm mode)
|
|
426
|
-
// The worktree stays for merge review - cleanup happens after worktree-resolve
|
|
427
|
-
log?.start("checkWorktreeComplete");
|
|
428
|
-
const worktreeMarked = markWorktreeComplete(cwd, agent_id);
|
|
429
|
-
if (worktreeMarked) {
|
|
430
|
-
log?.info(
|
|
431
|
-
`Marked worktree as agent-complete for ${agent_id} - ready for merge review`,
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
log?.end("checkWorktreeComplete", { worktreeMarked });
|
|
435
|
-
|
|
436
358
|
// Refresh HUD to remove the completed agent
|
|
437
359
|
log?.start("refreshHud");
|
|
438
360
|
refreshHud(cwd, session_id);
|
|
@@ -98,23 +98,6 @@ function commandSucceeds(cmd: string, cwd: string): boolean {
|
|
|
98
98
|
return true;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
/**
|
|
102
|
-
* Get command output or null on failure (cross-platform via cross-spawn)
|
|
103
|
-
*/
|
|
104
|
-
function getCommandOutput(cmd: string, cwd: string): string | null {
|
|
105
|
-
const [bin, args] = splitCommand(cmd);
|
|
106
|
-
if (!which.sync(bin, { nothrow: true })) {
|
|
107
|
-
debug(SOURCE, `Binary not found in PATH: ${bin}`);
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
const result = spawn.sync(bin, args, { cwd, stdio: "pipe", timeout: 30000 });
|
|
111
|
-
if (result.status !== 0 || !result.stdout) {
|
|
112
|
-
debug(SOURCE, `getCommandOutput failed for: ${cmd}: exit ${result.status}`);
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
return result.stdout.toString().trim();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
101
|
/**
|
|
119
102
|
* Detect project type (typescript, go, python)
|
|
120
103
|
*/
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Tool Observe Hook (PostToolUse)
|
|
4
|
+
*
|
|
5
|
+
* Single-purpose: record every Claude-native tool invocation as an
|
|
6
|
+
* observe.KindToolCall event. Mirror image of the MCP middleware on the Go
|
|
7
|
+
* side — together they give the dashboard complete tool-call coverage.
|
|
8
|
+
*
|
|
9
|
+
* All taxonomy (tool → category/subtype) and the recording itself live in
|
|
10
|
+
* src/core/tool-observe.ts so the OpenCode tool.execute.after handler can
|
|
11
|
+
* reuse the same logic.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readStdin } from "../lib/hook-utils.js";
|
|
15
|
+
import { findAideBinary } from "../core/aide-client.js";
|
|
16
|
+
import { recordToolEvent } from "../core/tool-observe.js";
|
|
17
|
+
import { debug } from "../lib/logger.js";
|
|
18
|
+
|
|
19
|
+
const SOURCE = "tool-observe";
|
|
20
|
+
|
|
21
|
+
interface HookInput {
|
|
22
|
+
hook_event_name: string;
|
|
23
|
+
session_id: string;
|
|
24
|
+
cwd: string;
|
|
25
|
+
tool_name?: string;
|
|
26
|
+
tool_input?: Record<string, unknown>;
|
|
27
|
+
tool_result?: { success: boolean };
|
|
28
|
+
// Claude Code passes the tool's actual output payload as tool_response.
|
|
29
|
+
// Shape varies per tool (string for Bash, object for others).
|
|
30
|
+
tool_response?: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function outputContinue(): void {
|
|
34
|
+
try {
|
|
35
|
+
console.log(JSON.stringify({ continue: true }));
|
|
36
|
+
} catch {
|
|
37
|
+
console.log('{"continue":true}');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function main(): Promise<void> {
|
|
42
|
+
try {
|
|
43
|
+
const input = await readStdin();
|
|
44
|
+
if (!input.trim()) {
|
|
45
|
+
outputContinue();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const data: HookInput = JSON.parse(input);
|
|
49
|
+
const cwd = data.cwd || process.cwd();
|
|
50
|
+
const toolName = data.tool_name;
|
|
51
|
+
if (!toolName) {
|
|
52
|
+
outputContinue();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const binary = findAideBinary({
|
|
56
|
+
cwd,
|
|
57
|
+
pluginRoot:
|
|
58
|
+
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
59
|
+
});
|
|
60
|
+
if (!binary) {
|
|
61
|
+
outputContinue();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
recordToolEvent(binary, cwd, {
|
|
65
|
+
toolName,
|
|
66
|
+
toolInput: data.tool_input as ToolInput,
|
|
67
|
+
toolResponse: data.tool_response,
|
|
68
|
+
success: data.tool_result?.success,
|
|
69
|
+
sessionId: data.session_id,
|
|
70
|
+
});
|
|
71
|
+
} catch (err) {
|
|
72
|
+
debug(SOURCE, `Hook error: ${err}`);
|
|
73
|
+
}
|
|
74
|
+
outputContinue();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type ToolInput = {
|
|
78
|
+
file_path?: string;
|
|
79
|
+
offset?: number;
|
|
80
|
+
limit?: number;
|
|
81
|
+
command?: string;
|
|
82
|
+
pattern?: string;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
process.on("uncaughtException", (err) => {
|
|
86
|
+
debug(SOURCE, `UNCAUGHT EXCEPTION: ${err}`);
|
|
87
|
+
outputContinue();
|
|
88
|
+
process.exit(0);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
process.on("unhandledRejection", (reason) => {
|
|
92
|
+
debug(SOURCE, `UNHANDLED REJECTION: ${reason}`);
|
|
93
|
+
outputContinue();
|
|
94
|
+
process.exit(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
main();
|
|
@@ -193,14 +193,6 @@ export function getDownloadUrls(): string[] {
|
|
|
193
193
|
return [`https://github.com/jmylchreest/aide/releases/latest/download/${binaryName}`];
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
/**
|
|
197
|
-
* Get the download URL for the current platform (first priority URL).
|
|
198
|
-
* @deprecated Use getDownloadUrls() for fallback support.
|
|
199
|
-
*/
|
|
200
|
-
export function getDownloadUrl(): string {
|
|
201
|
-
return getDownloadUrls()[0];
|
|
202
|
-
}
|
|
203
|
-
|
|
204
196
|
/**
|
|
205
197
|
* Get the plugin root directory (where package.json lives)
|
|
206
198
|
*/
|
package/src/lib/hook-utils.ts
CHANGED
|
@@ -146,19 +146,6 @@ export function getMemoryState(
|
|
|
146
146
|
return getState(binary, cwd, key, agentId);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
/**
|
|
150
|
-
* Delete a state key from aide
|
|
151
|
-
*/
|
|
152
|
-
export function deleteMemoryState(
|
|
153
|
-
cwd: string,
|
|
154
|
-
key: string,
|
|
155
|
-
agentId?: string,
|
|
156
|
-
): boolean {
|
|
157
|
-
const binary = findAideBinary(cwd);
|
|
158
|
-
if (!binary) return false;
|
|
159
|
-
return deleteState(binary, cwd, key, agentId);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
149
|
/**
|
|
163
150
|
* Clear all state for an agent
|
|
164
151
|
*/
|
package/src/lib/logger.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* touch .aide/.debug # sentinel file (live toggle)
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { existsSync, mkdirSync, appendFileSync
|
|
22
|
+
import { existsSync, mkdirSync, appendFileSync } from "fs";
|
|
23
23
|
import { join } from "path";
|
|
24
24
|
|
|
25
25
|
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
@@ -195,21 +195,6 @@ export class Logger {
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
/**
|
|
199
|
-
* Time a synchronous operation
|
|
200
|
-
*/
|
|
201
|
-
timeSync<T>(label: string, fn: () => T): T {
|
|
202
|
-
this.start(label);
|
|
203
|
-
try {
|
|
204
|
-
const result = fn();
|
|
205
|
-
this.end(label);
|
|
206
|
-
return result;
|
|
207
|
-
} catch (err) {
|
|
208
|
-
this.end(label, { error: String(err) });
|
|
209
|
-
throw err;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
198
|
/**
|
|
214
199
|
* Format entries for file output
|
|
215
200
|
*/
|
|
@@ -265,43 +250,6 @@ export class Logger {
|
|
|
265
250
|
}
|
|
266
251
|
}
|
|
267
252
|
|
|
268
|
-
/**
|
|
269
|
-
* Write to a custom log file (relative to .aide/_logs/)
|
|
270
|
-
*/
|
|
271
|
-
writeToFile(filename: string, content: string): void {
|
|
272
|
-
if (!this.enabled) return;
|
|
273
|
-
|
|
274
|
-
try {
|
|
275
|
-
this.ensureLogDir();
|
|
276
|
-
const filepath = join(this.logDir, filename);
|
|
277
|
-
writeFileSync(filepath, content);
|
|
278
|
-
} catch {
|
|
279
|
-
// Silently fail
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Append to a custom log file (relative to .aide/_logs/)
|
|
285
|
-
*/
|
|
286
|
-
appendToFile(filename: string, content: string): void {
|
|
287
|
-
if (!this.enabled) return;
|
|
288
|
-
|
|
289
|
-
try {
|
|
290
|
-
this.ensureLogDir();
|
|
291
|
-
const filepath = join(this.logDir, filename);
|
|
292
|
-
appendFileSync(filepath, content);
|
|
293
|
-
} catch {
|
|
294
|
-
// Silently fail
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Get the log directory path
|
|
300
|
-
*/
|
|
301
|
-
getLogDir(): string {
|
|
302
|
-
return this.logDir;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
253
|
/**
|
|
306
254
|
* Get the main log file path
|
|
307
255
|
*/
|
|
@@ -318,21 +266,6 @@ export class Logger {
|
|
|
318
266
|
}
|
|
319
267
|
}
|
|
320
268
|
|
|
321
|
-
/**
|
|
322
|
-
* Create a singleton logger instance for quick use
|
|
323
|
-
*
|
|
324
|
-
* Note: Currently not used by hooks. Hooks create Logger instances
|
|
325
|
-
* directly for more control. Exported for potential future use.
|
|
326
|
-
*/
|
|
327
|
-
let defaultLogger: Logger | null = null;
|
|
328
|
-
|
|
329
|
-
export function getLogger(source: string, cwd?: string): Logger {
|
|
330
|
-
if (!defaultLogger || defaultLogger["source"] !== source) {
|
|
331
|
-
defaultLogger = new Logger(source, cwd);
|
|
332
|
-
}
|
|
333
|
-
return defaultLogger;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
269
|
// Debug log state - tracks cwd for file-based logging
|
|
337
270
|
let debugLogCwd = process.cwd();
|
|
338
271
|
|
package/src/lib/usage.ts
CHANGED
|
@@ -50,12 +50,6 @@ export interface RealtimeUsage {
|
|
|
50
50
|
messagesToday: number;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export interface UsageSummary {
|
|
54
|
-
limits: APILimits | null;
|
|
55
|
-
realtime: RealtimeUsage;
|
|
56
|
-
timestamp: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
53
|
interface OAuthCredentials {
|
|
60
54
|
claudeAiOauth?: {
|
|
61
55
|
accessToken: string;
|
|
@@ -89,14 +83,7 @@ let apiLimitsCache: CacheEntry<APILimits> | null = null;
|
|
|
89
83
|
const API_CACHE_TTL = 30_000; // 30 seconds
|
|
90
84
|
|
|
91
85
|
let realtimeCache: CacheEntry<RealtimeUsage> | null = null;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Set the cache TTL for realtime usage data (JSONL scanning).
|
|
96
|
-
*/
|
|
97
|
-
export function setRealtimeCacheTTL(ms: number): void {
|
|
98
|
-
realtimeCacheTTL = ms;
|
|
99
|
-
}
|
|
86
|
+
const realtimeCacheTTL = 60_000;
|
|
100
87
|
|
|
101
88
|
// =============================================================================
|
|
102
89
|
// OAuth API
|
|
@@ -430,22 +417,6 @@ async function scanSingleFile(
|
|
|
430
417
|
// Combined Usage
|
|
431
418
|
// =============================================================================
|
|
432
419
|
|
|
433
|
-
/**
|
|
434
|
-
* Get combined usage data: API limits + local token counts.
|
|
435
|
-
*/
|
|
436
|
-
export async function getUsage(): Promise<UsageSummary> {
|
|
437
|
-
const [limits, realtime] = await Promise.all([
|
|
438
|
-
fetchAPILimits(),
|
|
439
|
-
scanTokenUsage(),
|
|
440
|
-
]);
|
|
441
|
-
|
|
442
|
-
return {
|
|
443
|
-
limits: limits.error ? null : limits,
|
|
444
|
-
realtime,
|
|
445
|
-
timestamp: new Date().toISOString(),
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
|
|
449
420
|
/**
|
|
450
421
|
* Get usage formatted for HUD display.
|
|
451
422
|
* Prioritizes API percentages, falls back to token counts.
|