@jmylchreest/aide-plugin 0.1.0 → 0.1.2
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/skills/reflect/SKILL.md +24 -8
- package/src/cli/codex-config.ts +15 -0
- package/src/core/context-pruning/dedup.ts +7 -2
- package/src/core/context-pruning/tracker.ts +0 -23
- package/src/core/memory-query.ts +63 -0
- package/src/core/partial-memory.ts +23 -105
- package/src/core/session-checkpoint-logic.ts +170 -0
- package/src/core/session-resume-logic.ts +104 -0
- package/src/core/session-summary-logic.ts +16 -41
- package/src/core/session-text.ts +60 -0
- package/src/core/tool-observe.ts +53 -14
- package/src/hooks/agent-cleanup.ts +11 -28
- package/src/hooks/agent-signals.ts +11 -18
- package/src/hooks/comment-checker.ts +14 -31
- package/src/hooks/context-guard.ts +17 -31
- package/src/hooks/context-pruning.ts +13 -30
- package/src/hooks/hud-updater.ts +11 -29
- package/src/hooks/permission-handler.ts +14 -25
- package/src/hooks/persistence.ts +15 -31
- package/src/hooks/pre-compact.ts +52 -38
- package/src/hooks/pre-tool-enforcer.ts +14 -30
- package/src/hooks/reflect.ts +15 -8
- package/src/hooks/search-enrichment.ts +12 -29
- package/src/hooks/session-end.ts +60 -11
- package/src/hooks/session-start.ts +68 -30
- package/src/hooks/session-summary.ts +11 -27
- package/src/hooks/skill-injector.ts +15 -30
- package/src/hooks/subagent-tracker.ts +44 -39
- package/src/hooks/task-completed.ts +6 -9
- package/src/hooks/tool-observe.ts +27 -32
- package/src/hooks/tool-tracker.ts +11 -28
- package/src/hooks/write-guard.ts +12 -29
- package/src/lib/hook-utils.ts +48 -0
- package/src/lib/project-root.ts +30 -15
package/src/hooks/session-end.ts
CHANGED
|
@@ -26,8 +26,16 @@ const T0 = performance.now();
|
|
|
26
26
|
// Output continue IMMEDIATELY — before require(), before anything.
|
|
27
27
|
console.log(JSON.stringify({ continue: true }));
|
|
28
28
|
|
|
29
|
-
const { spawn, execFileSync } =
|
|
30
|
-
|
|
29
|
+
const { spawn, execFileSync } =
|
|
30
|
+
require("child_process") as typeof import("child_process");
|
|
31
|
+
const {
|
|
32
|
+
existsSync,
|
|
33
|
+
realpathSync,
|
|
34
|
+
appendFileSync,
|
|
35
|
+
mkdirSync,
|
|
36
|
+
readFileSync,
|
|
37
|
+
statSync,
|
|
38
|
+
} = require("fs") as typeof import("fs");
|
|
31
39
|
const { join, dirname } = require("path") as typeof import("path");
|
|
32
40
|
const whichSync = (require("which") as typeof import("which")).sync;
|
|
33
41
|
|
|
@@ -41,7 +49,9 @@ function resolveRoot(cwd: string): string {
|
|
|
41
49
|
if (override) {
|
|
42
50
|
try {
|
|
43
51
|
if (statSync(override).isDirectory()) return override;
|
|
44
|
-
} catch {
|
|
52
|
+
} catch {
|
|
53
|
+
/* fall through */
|
|
54
|
+
}
|
|
45
55
|
}
|
|
46
56
|
const candidates: { dir: string; hasAide: boolean; hasVCS: boolean }[] = [];
|
|
47
57
|
let dir = cwd;
|
|
@@ -49,7 +59,10 @@ function resolveRoot(cwd: string): string {
|
|
|
49
59
|
const hasAide = existsSync(join(dir, ".aide"));
|
|
50
60
|
let hasVCS = false;
|
|
51
61
|
for (const m of [".git", ".hg", ".svn", ".bzr", ".fossil"]) {
|
|
52
|
-
if (existsSync(join(dir, m))) {
|
|
62
|
+
if (existsSync(join(dir, m))) {
|
|
63
|
+
hasVCS = true;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
53
66
|
}
|
|
54
67
|
if (hasAide || hasVCS) candidates.push({ dir, hasAide, hasVCS });
|
|
55
68
|
const parent = dirname(dir);
|
|
@@ -79,15 +92,22 @@ function log(cwd: string, msg: string): void {
|
|
|
79
92
|
if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });
|
|
80
93
|
const line = `[${new Date().toISOString()}] [session-end] ${ms()} ${msg}\n`;
|
|
81
94
|
appendFileSync(join(logDir, "session-end.log"), line);
|
|
82
|
-
} catch {
|
|
95
|
+
} catch {
|
|
96
|
+
/* best effort */
|
|
97
|
+
}
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
/** Find aide binary — inline, no external module imports. */
|
|
86
101
|
function findBinary(cwd?: string): string | null {
|
|
87
|
-
const pluginRoot =
|
|
102
|
+
const pluginRoot =
|
|
103
|
+
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
88
104
|
let resolvedRoot = pluginRoot;
|
|
89
105
|
if (resolvedRoot) {
|
|
90
|
-
try {
|
|
106
|
+
try {
|
|
107
|
+
resolvedRoot = realpathSync(resolvedRoot);
|
|
108
|
+
} catch {
|
|
109
|
+
/* symlink may not resolve */
|
|
110
|
+
}
|
|
91
111
|
const p = join(resolvedRoot, "bin", "aide");
|
|
92
112
|
if (existsSync(p)) return p;
|
|
93
113
|
}
|
|
@@ -97,7 +117,9 @@ function findBinary(cwd?: string): string | null {
|
|
|
97
117
|
}
|
|
98
118
|
try {
|
|
99
119
|
return whichSync("aide", { nothrow: true });
|
|
100
|
-
} catch {
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
101
123
|
}
|
|
102
124
|
|
|
103
125
|
function main(): void {
|
|
@@ -143,15 +165,40 @@ function main(): void {
|
|
|
143
165
|
return;
|
|
144
166
|
}
|
|
145
167
|
|
|
168
|
+
// Emit a lifecycle trigger so SessionEnd is traceable in the dashboard,
|
|
169
|
+
// symmetric with session-start and subagent-start/stop. Inlined (no ES
|
|
170
|
+
// import) to keep this hook's startup cheap. Fire-and-forget.
|
|
171
|
+
try {
|
|
172
|
+
execFileSync(
|
|
173
|
+
binary,
|
|
174
|
+
[
|
|
175
|
+
"observe",
|
|
176
|
+
"record",
|
|
177
|
+
"--kind=session",
|
|
178
|
+
"--name=session-end",
|
|
179
|
+
"--category=lifecycle",
|
|
180
|
+
`--session=${sessionId}`,
|
|
181
|
+
],
|
|
182
|
+
{ cwd, timeout: 3000, stdio: ["pipe", "pipe", "pipe"] },
|
|
183
|
+
);
|
|
184
|
+
} catch {
|
|
185
|
+
// Non-fatal telemetry — never block session end.
|
|
186
|
+
}
|
|
187
|
+
|
|
146
188
|
// Mode guard: skip cleanup if autopilot is active (session is continuing,
|
|
147
189
|
// not ending). This matters when Codex CLI invokes session-end from Stop
|
|
148
190
|
// hook since there's no separate SessionEnd event.
|
|
149
191
|
try {
|
|
150
192
|
const mode = execFileSync(binary, ["state", "get", "mode"], {
|
|
151
|
-
cwd,
|
|
193
|
+
cwd,
|
|
194
|
+
timeout: 500,
|
|
195
|
+
encoding: "utf-8",
|
|
152
196
|
}).trim();
|
|
153
197
|
if (mode === "autopilot") {
|
|
154
|
-
log(
|
|
198
|
+
log(
|
|
199
|
+
cwd,
|
|
200
|
+
"autopilot mode active, skipping cleanup (session continuing)",
|
|
201
|
+
);
|
|
155
202
|
return;
|
|
156
203
|
}
|
|
157
204
|
} catch {
|
|
@@ -160,7 +207,9 @@ function main(): void {
|
|
|
160
207
|
|
|
161
208
|
log(cwd, `spawning cleanup: session end --session=${sessionId}`);
|
|
162
209
|
const child = spawn(binary, ["session", "end", `--session=${sessionId}`], {
|
|
163
|
-
cwd,
|
|
210
|
+
cwd,
|
|
211
|
+
detached: true,
|
|
212
|
+
stdio: "ignore",
|
|
164
213
|
});
|
|
165
214
|
child.unref();
|
|
166
215
|
log(cwd, "cleanup spawned (detached)");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
3
|
* Session Start Hook (SessionStart)
|
|
4
4
|
*
|
|
@@ -24,10 +24,20 @@ import { join, dirname } from "path";
|
|
|
24
24
|
import { fileURLToPath } from "url";
|
|
25
25
|
import { homedir } from "os";
|
|
26
26
|
import { Logger, debug, setDebugCwd } from "../lib/logger.js";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
readStdin,
|
|
29
|
+
detectPlatform,
|
|
30
|
+
isFalsy,
|
|
31
|
+
emitHookResult,
|
|
32
|
+
installHookSafetyNet,
|
|
33
|
+
} from "../lib/hook-utils.js";
|
|
28
34
|
import { findAideBinary, ensureAideBinary } from "../lib/aide-downloader.js";
|
|
29
35
|
import { findProjectRoot } from "../lib/project-root.js";
|
|
30
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
emitInjectionEvent,
|
|
38
|
+
recordObserveEvent,
|
|
39
|
+
} from "../core/read-tracking.js";
|
|
40
|
+
import { buildResumeContext } from "../core/session-resume-logic.js";
|
|
31
41
|
import {
|
|
32
42
|
ensureDirectories as coreEnsureDirectories,
|
|
33
43
|
loadConfig as coreLoadConfig,
|
|
@@ -58,10 +68,13 @@ interface HookInput {
|
|
|
58
68
|
cwd: string;
|
|
59
69
|
transcript_path?: string;
|
|
60
70
|
permission_mode?: string;
|
|
71
|
+
/** How the session began: startup | resume | clear | compact */
|
|
72
|
+
source?: string;
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
interface HookOutput {
|
|
64
76
|
continue: boolean;
|
|
77
|
+
suppressOutput?: boolean;
|
|
65
78
|
hookSpecificOutput?: {
|
|
66
79
|
hookEventName: string;
|
|
67
80
|
additionalContext?: string;
|
|
@@ -189,7 +202,11 @@ function installHudWrapper(log: Logger): void {
|
|
|
189
202
|
|
|
190
203
|
// Copy wrapper script
|
|
191
204
|
copyFileSync(wrapperSrc, wrapperDest);
|
|
192
|
-
try {
|
|
205
|
+
try {
|
|
206
|
+
chmodSync(wrapperDest, 0o755);
|
|
207
|
+
} catch {
|
|
208
|
+
/* no-op on Windows */
|
|
209
|
+
}
|
|
193
210
|
|
|
194
211
|
log.info(`Installed HUD wrapper to ${wrapperDest}`);
|
|
195
212
|
log.end("installHudWrapper", { success: true, path: wrapperDest });
|
|
@@ -301,28 +318,8 @@ function debugLog(msg: string): void {
|
|
|
301
318
|
debug(SOURCE, msg);
|
|
302
319
|
}
|
|
303
320
|
|
|
304
|
-
// Ensure we always output valid JSON, even on catastrophic errors
|
|
305
|
-
function outputContinue(): void {
|
|
306
|
-
try {
|
|
307
|
-
console.log(JSON.stringify({ continue: true }));
|
|
308
|
-
} catch {
|
|
309
|
-
// Last resort - raw JSON string
|
|
310
|
-
console.log('{"continue":true}');
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
321
|
// Global error handlers to prevent hook crashes without JSON output
|
|
315
|
-
|
|
316
|
-
debugLog(`UNCAUGHT EXCEPTION: ${err}`);
|
|
317
|
-
outputContinue();
|
|
318
|
-
process.exit(0);
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
process.on("unhandledRejection", (reason) => {
|
|
322
|
-
debugLog(`UNHANDLED REJECTION: ${reason}`);
|
|
323
|
-
outputContinue();
|
|
324
|
-
process.exit(0);
|
|
325
|
-
});
|
|
322
|
+
installHookSafetyNet(SOURCE);
|
|
326
323
|
|
|
327
324
|
async function main(): Promise<void> {
|
|
328
325
|
let log: Logger | null = null;
|
|
@@ -337,7 +334,7 @@ async function main(): Promise<void> {
|
|
|
337
334
|
|
|
338
335
|
if (!input.trim()) {
|
|
339
336
|
debugLog("Empty input, exiting");
|
|
340
|
-
|
|
337
|
+
emitHookResult();
|
|
341
338
|
return;
|
|
342
339
|
}
|
|
343
340
|
|
|
@@ -356,7 +353,7 @@ async function main(): Promise<void> {
|
|
|
356
353
|
`Set \`requireGit\`: false in ~/.aide/config/aide.json to allow ` +
|
|
357
354
|
`init in arbitrary directories. Skipping AIDE bootstrap.\n`,
|
|
358
355
|
);
|
|
359
|
-
|
|
356
|
+
emitHookResult();
|
|
360
357
|
return;
|
|
361
358
|
}
|
|
362
359
|
process.stderr.write(
|
|
@@ -485,12 +482,52 @@ async function main(): Promise<void> {
|
|
|
485
482
|
// Build welcome context with injected memories
|
|
486
483
|
debugLog("buildWelcomeContext starting...");
|
|
487
484
|
log.start("buildWelcomeContext");
|
|
488
|
-
|
|
485
|
+
let context = buildWelcomeContext(state, memories, notices);
|
|
489
486
|
log.end("buildWelcomeContext");
|
|
490
487
|
debugLog(`buildWelcomeContext complete (${Date.now() - hookStart}ms)`);
|
|
491
488
|
|
|
489
|
+
// On resume/compact, re-inject the last session checkpoint so the agent
|
|
490
|
+
// rebuilds working context instead of relearning it (MiMo-style context
|
|
491
|
+
// reconstruction, done at the SessionStart boundary).
|
|
492
|
+
try {
|
|
493
|
+
const resumeBinary = findAideBinary(cwd);
|
|
494
|
+
if (resumeBinary) {
|
|
495
|
+
const resume = buildResumeContext(
|
|
496
|
+
resumeBinary,
|
|
497
|
+
cwd,
|
|
498
|
+
sessionId,
|
|
499
|
+
data.source,
|
|
500
|
+
);
|
|
501
|
+
if (resume) {
|
|
502
|
+
context = `${context}\n\n${resume}`;
|
|
503
|
+
debugLog(`Injected resume checkpoint (source=${data.source})`);
|
|
504
|
+
recordObserveEvent(resumeBinary, cwd, {
|
|
505
|
+
kind: "injection",
|
|
506
|
+
name: "resume-checkpoint",
|
|
507
|
+
category: "resume",
|
|
508
|
+
subtype: data.source || "resume",
|
|
509
|
+
session: sessionId,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
} catch (err) {
|
|
514
|
+
debugLog(`Resume checkpoint injection failed (non-fatal): ${err}`);
|
|
515
|
+
}
|
|
516
|
+
|
|
492
517
|
try {
|
|
493
518
|
const binary = findAideBinary(cwd);
|
|
519
|
+
if (binary) {
|
|
520
|
+
// Emit a lifecycle trigger so SessionStart is traceable in the
|
|
521
|
+
// dashboard, symmetric with subagent-start/stop. Fires regardless of
|
|
522
|
+
// whether any memories were injected.
|
|
523
|
+
recordObserveEvent(binary, cwd, {
|
|
524
|
+
kind: "session",
|
|
525
|
+
name: "session-start",
|
|
526
|
+
category: "lifecycle",
|
|
527
|
+
subtype: data.source || "startup",
|
|
528
|
+
session: sessionId,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
494
531
|
if (binary && context && memories.sources) {
|
|
495
532
|
for (const src of memories.sources) {
|
|
496
533
|
const attrs: Record<string, string> = { scope: src.scope };
|
|
@@ -523,13 +560,14 @@ async function main(): Promise<void> {
|
|
|
523
560
|
|
|
524
561
|
const output: HookOutput = {
|
|
525
562
|
continue: true,
|
|
563
|
+
...(process.env.AIDE_DEBUG ? {} : { suppressOutput: true }),
|
|
526
564
|
hookSpecificOutput: {
|
|
527
565
|
hookEventName: "SessionStart",
|
|
528
566
|
additionalContext: context,
|
|
529
567
|
},
|
|
530
568
|
};
|
|
531
569
|
|
|
532
|
-
|
|
570
|
+
emitHookResult(output);
|
|
533
571
|
} catch (error) {
|
|
534
572
|
debugLog(`ERROR: ${error}`);
|
|
535
573
|
// Log error if logger is available
|
|
@@ -538,7 +576,7 @@ async function main(): Promise<void> {
|
|
|
538
576
|
log.flush();
|
|
539
577
|
}
|
|
540
578
|
// On error, allow continuation without context
|
|
541
|
-
|
|
579
|
+
emitHookResult();
|
|
542
580
|
}
|
|
543
581
|
}
|
|
544
582
|
|
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { debug, setDebugCwd } from "../lib/logger.js";
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import {
|
|
14
|
+
readStdin,
|
|
15
|
+
emitHookResult,
|
|
16
|
+
installHookSafetyNet,
|
|
17
|
+
findAideBinary,
|
|
18
|
+
} from "../lib/hook-utils.js";
|
|
15
19
|
import {
|
|
16
20
|
buildSessionSummary,
|
|
17
21
|
getSessionCommits,
|
|
@@ -45,10 +49,7 @@ function captureSessionSummary(
|
|
|
45
49
|
sessionId: string,
|
|
46
50
|
transcriptPath: string,
|
|
47
51
|
): boolean {
|
|
48
|
-
const binary = findAideBinary(
|
|
49
|
-
cwd,
|
|
50
|
-
pluginRoot: process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
51
|
-
});
|
|
52
|
+
const binary = findAideBinary(cwd);
|
|
52
53
|
if (!binary) {
|
|
53
54
|
debug(SOURCE, "aide binary not found, cannot capture session summary");
|
|
54
55
|
return false;
|
|
@@ -98,7 +99,7 @@ async function main(): Promise<void> {
|
|
|
98
99
|
try {
|
|
99
100
|
const input = await readStdin();
|
|
100
101
|
if (!input.trim()) {
|
|
101
|
-
|
|
102
|
+
emitHookResult({ continue: true });
|
|
102
103
|
return;
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -118,30 +119,13 @@ async function main(): Promise<void> {
|
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
|
|
122
|
+
emitHookResult({ continue: true });
|
|
122
123
|
} catch (err) {
|
|
123
124
|
debug(SOURCE, `Error: ${err}`);
|
|
124
|
-
|
|
125
|
+
emitHookResult({ continue: true });
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
debug(SOURCE, `UNCAUGHT EXCEPTION: ${err}`);
|
|
130
|
-
try {
|
|
131
|
-
console.log(JSON.stringify({ continue: true }));
|
|
132
|
-
} catch {
|
|
133
|
-
console.log('{"continue":true}');
|
|
134
|
-
}
|
|
135
|
-
process.exit(0);
|
|
136
|
-
});
|
|
137
|
-
process.on("unhandledRejection", (reason) => {
|
|
138
|
-
debug(SOURCE, `UNHANDLED REJECTION: ${reason}`);
|
|
139
|
-
try {
|
|
140
|
-
console.log(JSON.stringify({ continue: true }));
|
|
141
|
-
} catch {
|
|
142
|
-
console.log('{"continue":true}');
|
|
143
|
-
}
|
|
144
|
-
process.exit(0);
|
|
145
|
-
});
|
|
129
|
+
installHookSafetyNet(SOURCE);
|
|
146
130
|
|
|
147
131
|
main();
|
|
@@ -18,7 +18,13 @@
|
|
|
18
18
|
import { existsSync, mkdirSync } from "fs";
|
|
19
19
|
import { join } from "path";
|
|
20
20
|
import { Logger, debug, setDebugCwd } from "../lib/logger.js";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
readStdin,
|
|
23
|
+
detectPlatform,
|
|
24
|
+
emitHookResult,
|
|
25
|
+
installHookSafetyNet,
|
|
26
|
+
findAideBinary,
|
|
27
|
+
} from "../lib/hook-utils.js";
|
|
22
28
|
import { findProjectRoot } from "../lib/project-root.js";
|
|
23
29
|
import {
|
|
24
30
|
discoverSkills as coreDiscoverSkills,
|
|
@@ -26,7 +32,6 @@ import {
|
|
|
26
32
|
formatSkillsContext as coreFormatSkillsContext,
|
|
27
33
|
} from "../core/skill-matcher.js";
|
|
28
34
|
import type { Skill } from "../core/types.js";
|
|
29
|
-
import { findAideBinary } from "../core/aide-client.js";
|
|
30
35
|
import {
|
|
31
36
|
recordObserveEvent,
|
|
32
37
|
previewContent,
|
|
@@ -124,28 +129,8 @@ function debugLog(msg: string): void {
|
|
|
124
129
|
debug(SOURCE, msg);
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
// Ensure we always output valid JSON, even on catastrophic errors
|
|
128
|
-
function outputContinue(): void {
|
|
129
|
-
try {
|
|
130
|
-
console.log(JSON.stringify({ continue: true }));
|
|
131
|
-
} catch {
|
|
132
|
-
// Last resort - raw JSON string
|
|
133
|
-
console.log('{"continue":true}');
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
132
|
// Global error handlers to prevent hook crashes without JSON output
|
|
138
|
-
|
|
139
|
-
debugLog(`UNCAUGHT EXCEPTION: ${err}`);
|
|
140
|
-
outputContinue();
|
|
141
|
-
process.exit(0);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
process.on("unhandledRejection", (reason) => {
|
|
145
|
-
debugLog(`UNHANDLED REJECTION: ${reason}`);
|
|
146
|
-
outputContinue();
|
|
147
|
-
process.exit(0);
|
|
148
|
-
});
|
|
133
|
+
installHookSafetyNet(SOURCE);
|
|
149
134
|
|
|
150
135
|
async function main(): Promise<void> {
|
|
151
136
|
const hookStart = Date.now();
|
|
@@ -158,7 +143,7 @@ async function main(): Promise<void> {
|
|
|
158
143
|
|
|
159
144
|
if (!input.trim()) {
|
|
160
145
|
debugLog("Empty input, exiting");
|
|
161
|
-
|
|
146
|
+
emitHookResult({ continue: true });
|
|
162
147
|
return;
|
|
163
148
|
}
|
|
164
149
|
|
|
@@ -190,7 +175,7 @@ async function main(): Promise<void> {
|
|
|
190
175
|
// sessions that aren't running reflect don't accrue extra events.
|
|
191
176
|
if (prompt && reflectEnabled(cwd)) {
|
|
192
177
|
try {
|
|
193
|
-
const binary = findAideBinary(
|
|
178
|
+
const binary = findAideBinary(cwd);
|
|
194
179
|
if (binary) {
|
|
195
180
|
recordObserveEvent(binary, cwd, {
|
|
196
181
|
kind: "hook",
|
|
@@ -211,7 +196,7 @@ async function main(): Promise<void> {
|
|
|
211
196
|
log.info("No prompt provided");
|
|
212
197
|
log.end("total");
|
|
213
198
|
log.flush();
|
|
214
|
-
|
|
199
|
+
emitHookResult({ continue: true });
|
|
215
200
|
return;
|
|
216
201
|
}
|
|
217
202
|
|
|
@@ -242,7 +227,7 @@ async function main(): Promise<void> {
|
|
|
242
227
|
// single "skill-injector" aggregate). Subtype="skill" keeps the
|
|
243
228
|
// category roll-up; Name carries the per-skill identifier.
|
|
244
229
|
try {
|
|
245
|
-
const binary = findAideBinary(
|
|
230
|
+
const binary = findAideBinary(cwd);
|
|
246
231
|
if (binary) {
|
|
247
232
|
for (const skill of matched) {
|
|
248
233
|
const text = `### ${skill.name}\n${skill.description ?? ""}\n${skill.content}`;
|
|
@@ -270,13 +255,13 @@ async function main(): Promise<void> {
|
|
|
270
255
|
additionalContext: skillContext,
|
|
271
256
|
},
|
|
272
257
|
};
|
|
273
|
-
|
|
258
|
+
emitHookResult(output);
|
|
274
259
|
} else {
|
|
275
260
|
log.info("No matching skills");
|
|
276
261
|
debugLog(`Flushing logs...`);
|
|
277
262
|
log.flush();
|
|
278
263
|
debugLog(`Hook complete (${Date.now() - hookStart}ms total)`);
|
|
279
|
-
|
|
264
|
+
emitHookResult({ continue: true });
|
|
280
265
|
}
|
|
281
266
|
} catch (error) {
|
|
282
267
|
debugLog(`ERROR: ${error}`);
|
|
@@ -286,7 +271,7 @@ async function main(): Promise<void> {
|
|
|
286
271
|
log.flush();
|
|
287
272
|
}
|
|
288
273
|
// On error, allow continuation
|
|
289
|
-
|
|
274
|
+
emitHookResult({ continue: true });
|
|
290
275
|
}
|
|
291
276
|
}
|
|
292
277
|
|
|
@@ -17,9 +17,18 @@
|
|
|
17
17
|
import { execFileSync } from "child_process";
|
|
18
18
|
import { basename } from "path";
|
|
19
19
|
import { Logger } from "../lib/logger.js";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
import {
|
|
21
|
+
readStdin,
|
|
22
|
+
setMemoryState,
|
|
23
|
+
isFalsy,
|
|
24
|
+
emitHookResult,
|
|
25
|
+
installHookSafetyNet,
|
|
26
|
+
findAideBinary,
|
|
27
|
+
} from "../lib/hook-utils.js";
|
|
28
|
+
import {
|
|
29
|
+
emitInjectionEvent,
|
|
30
|
+
recordObserveEvent,
|
|
31
|
+
} from "../core/read-tracking.js";
|
|
23
32
|
import { refreshHud } from "../lib/hud.js";
|
|
24
33
|
|
|
25
34
|
// Global logger instance
|
|
@@ -117,10 +126,7 @@ function fetchSubagentMemories(cwd: string): {
|
|
|
117
126
|
return result;
|
|
118
127
|
}
|
|
119
128
|
|
|
120
|
-
const binary = findAideBinary(
|
|
121
|
-
cwd,
|
|
122
|
-
pluginRoot: process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
123
|
-
});
|
|
129
|
+
const binary = findAideBinary(cwd);
|
|
124
130
|
if (!binary) {
|
|
125
131
|
return result;
|
|
126
132
|
}
|
|
@@ -341,6 +347,20 @@ async function processSubagentStart(
|
|
|
341
347
|
log?.info(
|
|
342
348
|
`Injecting context for subagent: ${memories.global.length} preferences, ${memories.project.length} project, ${memories.decisions.length} decisions`,
|
|
343
349
|
);
|
|
350
|
+
|
|
351
|
+
// Emit session observe event so SubagentStart is traceable in the dashboard
|
|
352
|
+
const binary = findAideBinary(cwd);
|
|
353
|
+
if (binary) {
|
|
354
|
+
recordObserveEvent(binary, cwd, {
|
|
355
|
+
kind: "session",
|
|
356
|
+
name: "subagent-start",
|
|
357
|
+
category: "lifecycle",
|
|
358
|
+
subtype: type,
|
|
359
|
+
session: session_id,
|
|
360
|
+
attrs: { agent_id, agent_type: type },
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
344
364
|
return context;
|
|
345
365
|
}
|
|
346
366
|
|
|
@@ -365,6 +385,18 @@ async function processSubagentStop(data: SubagentStopInput): Promise<void> {
|
|
|
365
385
|
refreshHud(cwd, session_id);
|
|
366
386
|
log?.end("refreshHud");
|
|
367
387
|
|
|
388
|
+
// Emit session observe event so SubagentStop is traceable in the dashboard
|
|
389
|
+
const binary = findAideBinary(cwd);
|
|
390
|
+
if (binary) {
|
|
391
|
+
recordObserveEvent(binary, cwd, {
|
|
392
|
+
kind: "session",
|
|
393
|
+
name: "subagent-stop",
|
|
394
|
+
category: "lifecycle",
|
|
395
|
+
session: session_id,
|
|
396
|
+
attrs: { agent_id, agent_type: data.agent_type ?? "" },
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
368
400
|
log?.debug(`SubagentStop: agent ${agent_id} marked as completed`);
|
|
369
401
|
}
|
|
370
402
|
|
|
@@ -372,7 +404,7 @@ async function main(): Promise<void> {
|
|
|
372
404
|
try {
|
|
373
405
|
const input = await readStdin();
|
|
374
406
|
if (!input.trim()) {
|
|
375
|
-
|
|
407
|
+
emitHookResult({ continue: true });
|
|
376
408
|
return;
|
|
377
409
|
}
|
|
378
410
|
|
|
@@ -412,11 +444,7 @@ async function main(): Promise<void> {
|
|
|
412
444
|
additionalContext,
|
|
413
445
|
};
|
|
414
446
|
try {
|
|
415
|
-
const binary = findAideBinary(
|
|
416
|
-
cwd,
|
|
417
|
-
pluginRoot:
|
|
418
|
-
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
419
|
-
});
|
|
447
|
+
const binary = findAideBinary(cwd);
|
|
420
448
|
if (binary) {
|
|
421
449
|
const startData = data as SubagentStartInput;
|
|
422
450
|
emitInjectionEvent(binary, cwd, {
|
|
@@ -438,40 +466,17 @@ async function main(): Promise<void> {
|
|
|
438
466
|
}
|
|
439
467
|
}
|
|
440
468
|
|
|
441
|
-
|
|
469
|
+
emitHookResult(output);
|
|
442
470
|
} catch (error) {
|
|
443
471
|
// Log error but don't block
|
|
444
472
|
if (log) {
|
|
445
473
|
log.error("Subagent tracker failed", error);
|
|
446
474
|
log.flush();
|
|
447
475
|
}
|
|
448
|
-
|
|
476
|
+
emitHookResult({ continue: true });
|
|
449
477
|
}
|
|
450
478
|
}
|
|
451
479
|
|
|
452
|
-
|
|
453
|
-
if (log) {
|
|
454
|
-
log.error(`UNCAUGHT EXCEPTION: ${err}`);
|
|
455
|
-
log.flush();
|
|
456
|
-
}
|
|
457
|
-
try {
|
|
458
|
-
console.log(JSON.stringify({ continue: true }));
|
|
459
|
-
} catch {
|
|
460
|
-
console.log('{"continue":true}');
|
|
461
|
-
}
|
|
462
|
-
process.exit(0);
|
|
463
|
-
});
|
|
464
|
-
process.on("unhandledRejection", (reason) => {
|
|
465
|
-
if (log) {
|
|
466
|
-
log.error(`UNHANDLED REJECTION: ${reason}`);
|
|
467
|
-
log.flush();
|
|
468
|
-
}
|
|
469
|
-
try {
|
|
470
|
-
console.log(JSON.stringify({ continue: true }));
|
|
471
|
-
} catch {
|
|
472
|
-
console.log('{"continue":true}');
|
|
473
|
-
}
|
|
474
|
-
process.exit(0);
|
|
475
|
-
});
|
|
480
|
+
installHookSafetyNet("subagent-tracker");
|
|
476
481
|
|
|
477
482
|
main();
|
|
@@ -26,7 +26,11 @@ import { existsSync, readFileSync } from "fs";
|
|
|
26
26
|
import { join } from "path";
|
|
27
27
|
import which from "which";
|
|
28
28
|
import { debug, setDebugCwd } from "../lib/logger.js";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
readStdin,
|
|
31
|
+
emitHookResult,
|
|
32
|
+
installHookSafetyNet,
|
|
33
|
+
} from "../lib/hook-utils.js";
|
|
30
34
|
|
|
31
35
|
const SOURCE = "task-completed";
|
|
32
36
|
|
|
@@ -416,13 +420,6 @@ async function main(): Promise<void> {
|
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
|
|
419
|
-
|
|
420
|
-
debug(SOURCE, `UNCAUGHT EXCEPTION: ${err}`);
|
|
421
|
-
process.exit(0);
|
|
422
|
-
});
|
|
423
|
-
process.on("unhandledRejection", (reason) => {
|
|
424
|
-
debug(SOURCE, `UNHANDLED REJECTION: ${reason}`);
|
|
425
|
-
process.exit(0);
|
|
426
|
-
});
|
|
423
|
+
installHookSafetyNet(SOURCE);
|
|
427
424
|
|
|
428
425
|
main();
|