@jmylchreest/aide-plugin 0.1.1 → 0.1.3
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
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { execFileSync } from "child_process";
|
|
9
9
|
import { readFileSync, existsSync } from "fs";
|
|
10
|
+
import { renderBulletSection } from "./session-text.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Get git commits made during this session.
|
|
@@ -84,8 +85,7 @@ function parseTranscript(lines: string[]): TranscriptData {
|
|
|
84
85
|
const userMessages: string[] = [];
|
|
85
86
|
|
|
86
87
|
for (const entry of entries) {
|
|
87
|
-
const contentObj =
|
|
88
|
-
typeof entry.content === "object" ? entry.content : null;
|
|
88
|
+
const contentObj = typeof entry.content === "object" ? entry.content : null;
|
|
89
89
|
if (
|
|
90
90
|
entry.type === "tool_use" ||
|
|
91
91
|
(entry.type === "assistant" && contentObj?.tool_use)
|
|
@@ -145,33 +145,15 @@ export function buildSessionSummary(
|
|
|
145
145
|
return null;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
const summaryParts
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (filesModified.size > 0) {
|
|
160
|
-
const files = Array.from(filesModified).slice(0, 10);
|
|
161
|
-
summaryParts.push(
|
|
162
|
-
`## Files Modified\n${files.map((f) => `- ${f}`).join("\n")}`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (commits.length > 0) {
|
|
167
|
-
summaryParts.push(
|
|
168
|
-
`## Commits\n${commits.map((c) => `- ${c}`).join("\n")}`,
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (toolsUsed.size > 0) {
|
|
173
|
-
summaryParts.push(`## Tools Used\n${Array.from(toolsUsed).join(", ")}`);
|
|
174
|
-
}
|
|
148
|
+
const summaryParts = [
|
|
149
|
+
renderBulletSection("Tasks", userMessages, 3),
|
|
150
|
+
renderBulletSection("Files Modified", Array.from(filesModified), 10),
|
|
151
|
+
renderBulletSection("Commits", commits),
|
|
152
|
+
// Tools Used is a comma-joined line, not a bullet list.
|
|
153
|
+
toolsUsed.size > 0
|
|
154
|
+
? `## Tools Used\n${Array.from(toolsUsed).join(", ")}`
|
|
155
|
+
: null,
|
|
156
|
+
].filter((s): s is string => s !== null);
|
|
175
157
|
|
|
176
158
|
const summary = summaryParts.join("\n\n");
|
|
177
159
|
return summary.length >= 50 ? summary : null;
|
|
@@ -193,9 +175,8 @@ export function buildSessionSummaryFromState(
|
|
|
193
175
|
|
|
194
176
|
const summaryParts: string[] = [];
|
|
195
177
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
178
|
+
const commitsSection = renderBulletSection("Commits", commits);
|
|
179
|
+
if (commitsSection) summaryParts.push(commitsSection);
|
|
199
180
|
|
|
200
181
|
// Check for modified files via git
|
|
201
182
|
try {
|
|
@@ -211,15 +192,9 @@ export function buildSessionSummaryFromState(
|
|
|
211
192
|
).trim();
|
|
212
193
|
|
|
213
194
|
if (diff) {
|
|
214
|
-
const files = diff
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
.slice(0, 10);
|
|
218
|
-
if (files.length > 0) {
|
|
219
|
-
summaryParts.push(
|
|
220
|
-
`## Files Modified\n${files.map((f) => `- ${f}`).join("\n")}`,
|
|
221
|
-
);
|
|
222
|
-
}
|
|
195
|
+
const files = diff.split("\n").filter((f) => f.trim());
|
|
196
|
+
const filesSection = renderBulletSection("Files Modified", files, 10);
|
|
197
|
+
if (filesSection) summaryParts.push(filesSection);
|
|
223
198
|
}
|
|
224
199
|
} catch {
|
|
225
200
|
// Ignore
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session text helpers — platform-agnostic.
|
|
3
|
+
*
|
|
4
|
+
* Shared building blocks for the session summary / checkpoint builders. These
|
|
5
|
+
* were duplicated across partial-memory.ts, session-summary-logic.ts, and
|
|
6
|
+
* session-checkpoint-logic.ts; centralising them keeps the section formatting
|
|
7
|
+
* and the partial-classification rules in one place.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Partials grouped by the kind of action they record. */
|
|
11
|
+
export interface CategorizedPartials {
|
|
12
|
+
/** Files created or edited, de-duplicated, first-seen order preserved. */
|
|
13
|
+
files: string[];
|
|
14
|
+
/** Shell commands run. */
|
|
15
|
+
commands: string[];
|
|
16
|
+
/** Completed (sub)task descriptions. */
|
|
17
|
+
tasks: string[];
|
|
18
|
+
/** Anything that didn't match a known prefix. */
|
|
19
|
+
other: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Classify raw partial-memory content strings (as produced by
|
|
24
|
+
* buildPartialContent) into files / commands / tasks / other.
|
|
25
|
+
*/
|
|
26
|
+
export function categorizePartials(partials: string[]): CategorizedPartials {
|
|
27
|
+
const files = new Set<string>();
|
|
28
|
+
const commands: string[] = [];
|
|
29
|
+
const tasks: string[] = [];
|
|
30
|
+
const other: string[] = [];
|
|
31
|
+
|
|
32
|
+
for (const p of partials) {
|
|
33
|
+
if (p.startsWith("Created file: ") || p.startsWith("Edited file: ")) {
|
|
34
|
+
files.add(p.replace(/^(Created|Edited) file: /, ""));
|
|
35
|
+
} else if (p.startsWith("Ran command: ")) {
|
|
36
|
+
commands.push(p.replace("Ran command: ", ""));
|
|
37
|
+
} else if (p.startsWith("Completed task: ")) {
|
|
38
|
+
tasks.push(p.replace("Completed task: ", ""));
|
|
39
|
+
} else {
|
|
40
|
+
other.push(p);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { files: Array.from(files), commands, tasks, other };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Render a markdown bullet section: `## <heading>` followed by `- <item>`
|
|
49
|
+
* lines. Returns null when there are no items (so callers can omit the
|
|
50
|
+
* section). When `cap` is given, only the first `cap` items are rendered.
|
|
51
|
+
*/
|
|
52
|
+
export function renderBulletSection(
|
|
53
|
+
heading: string,
|
|
54
|
+
items: string[],
|
|
55
|
+
cap?: number,
|
|
56
|
+
): string | null {
|
|
57
|
+
if (items.length === 0) return null;
|
|
58
|
+
const list = cap !== undefined ? items.slice(0, cap) : items;
|
|
59
|
+
return `## ${heading}\n${list.map((i) => `- ${i}`).join("\n")}`;
|
|
60
|
+
}
|
package/src/core/tool-observe.ts
CHANGED
|
@@ -118,6 +118,41 @@ function extractOutputText(payload: unknown): string {
|
|
|
118
118
|
return "";
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Decide whether a tool call failed and, if so, return the error text to
|
|
123
|
+
* attach to the observe event (drives the friction detector). Returns "" when
|
|
124
|
+
* the call succeeded.
|
|
125
|
+
*
|
|
126
|
+
* The meaningful friction signal is a *tool-level* failure — an Edit whose
|
|
127
|
+
* target string wasn't found, a Read of a missing file, a command the shell
|
|
128
|
+
* couldn't run — not every non-zero shell exit (a failing test mid-TDD is
|
|
129
|
+
* expected, not friction). We treat as failed: an explicit success===false,
|
|
130
|
+
* or a tool_response carrying an is_error / error / failed marker (how Claude
|
|
131
|
+
* Code flags tool-level errors). When failed but no text is recoverable, a
|
|
132
|
+
* generic marker still lets the detector count the recurrence.
|
|
133
|
+
*/
|
|
134
|
+
export function toolFailureText(
|
|
135
|
+
success: boolean | undefined,
|
|
136
|
+
toolResponse: unknown,
|
|
137
|
+
): string {
|
|
138
|
+
let failed = success === false;
|
|
139
|
+
let errorField = "";
|
|
140
|
+
if (toolResponse && typeof toolResponse === "object") {
|
|
141
|
+
const r = toolResponse as Record<string, unknown>;
|
|
142
|
+
if (r.is_error === true || r.isError === true) failed = true;
|
|
143
|
+
if (r.status === "error" || r.status === "failed") failed = true;
|
|
144
|
+
if (typeof r.error === "string" && r.error.length > 0) {
|
|
145
|
+
failed = true;
|
|
146
|
+
errorField = r.error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!failed) return "";
|
|
150
|
+
// Prefer an explicit error field, then any rendered output, then a marker so
|
|
151
|
+
// the detector can still count the recurrence even with no text.
|
|
152
|
+
const text = errorField || extractOutputText(toolResponse);
|
|
153
|
+
return (text || "tool reported failure").slice(0, 500);
|
|
154
|
+
}
|
|
155
|
+
|
|
121
156
|
export interface ToolObserveInput {
|
|
122
157
|
toolName: string;
|
|
123
158
|
toolInput?: {
|
|
@@ -138,6 +173,14 @@ export interface ToolObserveInput {
|
|
|
138
173
|
*/
|
|
139
174
|
toolResponse?: unknown;
|
|
140
175
|
success?: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Explicit error text from a harness failure event (Claude Code's
|
|
178
|
+
* PostToolUseFailure, OpenCode tool errors). When set, it's used verbatim as
|
|
179
|
+
* the event's error rather than inferring failure from toolResponse — the
|
|
180
|
+
* harness already told us it failed. Empty/undefined means "use the
|
|
181
|
+
* heuristic in toolFailureText".
|
|
182
|
+
*/
|
|
183
|
+
errorText?: string;
|
|
141
184
|
sessionId?: string;
|
|
142
185
|
}
|
|
143
186
|
|
|
@@ -161,20 +204,6 @@ function lookupTool(name: string): ToolTax | null {
|
|
|
161
204
|
return null;
|
|
162
205
|
}
|
|
163
206
|
|
|
164
|
-
/**
|
|
165
|
-
* Resolve to the canonical tool name (Edit/Read/Write/...) so observe
|
|
166
|
-
* events from different harnesses aggregate into the same bucket on the
|
|
167
|
-
* dashboard. Falls back to the original name when no alias matches.
|
|
168
|
-
*/
|
|
169
|
-
function canonicalToolName(name: string): string {
|
|
170
|
-
if (NATIVE_TOOL_TAXONOMY[name]) return name;
|
|
171
|
-
const lower = name.toLowerCase();
|
|
172
|
-
for (const k of Object.keys(NATIVE_TOOL_TAXONOMY)) {
|
|
173
|
-
if (k.toLowerCase() === lower) return k;
|
|
174
|
-
}
|
|
175
|
-
return TOOL_ALIASES[lower] ?? name;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
207
|
/**
|
|
179
208
|
* Estimate tokens for the Read tool. If offset/limit are present, scale by
|
|
180
209
|
* the portion actually read. Returns 0 on stat failure (caller still records
|
|
@@ -281,6 +310,16 @@ export function recordToolEvent(
|
|
|
281
310
|
if (typeof pattern === "string" && pattern.length > 0) {
|
|
282
311
|
args.push(`--attr=pattern=${pattern.slice(0, 200)}`);
|
|
283
312
|
}
|
|
313
|
+
// Mark tool-level failures so the friction detector can spot a recurring
|
|
314
|
+
// obstacle (the same tool failing on the same target). An explicit
|
|
315
|
+
// errorText from a harness failure event wins; otherwise we infer from the
|
|
316
|
+
// response. Empty when the call succeeded, so successes record as before.
|
|
317
|
+
const errText =
|
|
318
|
+
(input.errorText && input.errorText.slice(0, 500)) ||
|
|
319
|
+
toolFailureText(input.success, input.toolResponse);
|
|
320
|
+
if (errText) {
|
|
321
|
+
args.push(`--error=${errText}`);
|
|
322
|
+
}
|
|
284
323
|
execFileSync(binary, args, {
|
|
285
324
|
cwd,
|
|
286
325
|
timeout: 3000,
|
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
import { existsSync, appendFileSync } from "fs";
|
|
12
12
|
import { join } from "path";
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import {
|
|
14
|
+
readStdin,
|
|
15
|
+
emitHookResult,
|
|
16
|
+
installHookSafetyNet,
|
|
17
|
+
findAideBinary,
|
|
18
|
+
} from "../lib/hook-utils.js";
|
|
15
19
|
import { cleanupAgent } from "../core/cleanup.js";
|
|
16
20
|
import { debug } from "../lib/logger.js";
|
|
17
21
|
import { findProjectRoot } from "../lib/project-root.js";
|
|
@@ -31,7 +35,7 @@ async function main(): Promise<void> {
|
|
|
31
35
|
try {
|
|
32
36
|
const input = await readStdin();
|
|
33
37
|
if (!input.trim()) {
|
|
34
|
-
|
|
38
|
+
emitHookResult({ continue: true });
|
|
35
39
|
return;
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -41,11 +45,7 @@ async function main(): Promise<void> {
|
|
|
41
45
|
|
|
42
46
|
// Clean up agent-specific state — delegates to core
|
|
43
47
|
if (agentId) {
|
|
44
|
-
const binary = findAideBinary(
|
|
45
|
-
cwd,
|
|
46
|
-
pluginRoot:
|
|
47
|
-
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
48
|
-
});
|
|
48
|
+
const binary = findAideBinary(cwd);
|
|
49
49
|
if (binary) {
|
|
50
50
|
const cleared = cleanupAgent(binary, cwd, agentId);
|
|
51
51
|
if (cleared) {
|
|
@@ -64,30 +64,13 @@ async function main(): Promise<void> {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// Always continue - cleanup is best-effort
|
|
67
|
-
|
|
67
|
+
emitHookResult({ continue: true });
|
|
68
68
|
} catch (error) {
|
|
69
69
|
debug(SOURCE, `Hook error: ${error}`);
|
|
70
|
-
|
|
70
|
+
emitHookResult({ continue: true });
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
debug(SOURCE, `UNCAUGHT EXCEPTION: ${err}`);
|
|
76
|
-
try {
|
|
77
|
-
console.log(JSON.stringify({ continue: true }));
|
|
78
|
-
} catch {
|
|
79
|
-
console.log('{"continue":true}');
|
|
80
|
-
}
|
|
81
|
-
process.exit(0);
|
|
82
|
-
});
|
|
83
|
-
process.on("unhandledRejection", (reason) => {
|
|
84
|
-
debug(SOURCE, `UNHANDLED REJECTION: ${reason}`);
|
|
85
|
-
try {
|
|
86
|
-
console.log(JSON.stringify({ continue: true }));
|
|
87
|
-
} catch {
|
|
88
|
-
console.log('{"continue":true}');
|
|
89
|
-
}
|
|
90
|
-
process.exit(0);
|
|
91
|
-
});
|
|
74
|
+
installHookSafetyNet(SOURCE);
|
|
92
75
|
|
|
93
76
|
main();
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
import { execFileSync } from "child_process";
|
|
15
15
|
import { Logger } from "../lib/logger.js";
|
|
16
|
-
import {
|
|
17
|
-
|
|
16
|
+
import {
|
|
17
|
+
readStdin,
|
|
18
|
+
emitHookResult,
|
|
19
|
+
installHookSafetyNet,
|
|
20
|
+
findAideBinary,
|
|
21
|
+
} from "../lib/hook-utils.js";
|
|
18
22
|
import { emitInjectionEvent } from "../core/read-tracking.js";
|
|
19
23
|
|
|
20
24
|
const SOURCE = "agent-signals";
|
|
@@ -53,12 +57,12 @@ interface SignalsResponse {
|
|
|
53
57
|
let log: Logger | null = null;
|
|
54
58
|
|
|
55
59
|
function passThrough(): void {
|
|
56
|
-
|
|
60
|
+
emitHookResult({ continue: true });
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
function block(message: string): void {
|
|
60
64
|
const out: HookOutput = { continue: false, message };
|
|
61
|
-
|
|
65
|
+
emitHookResult(out);
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
function injectContext(context: string): void {
|
|
@@ -69,7 +73,7 @@ function injectContext(context: string): void {
|
|
|
69
73
|
additionalContext: context,
|
|
70
74
|
},
|
|
71
75
|
};
|
|
72
|
-
|
|
76
|
+
emitHookResult(out);
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
function runAide(binary: string, cwd: string, args: string[]): string | null {
|
|
@@ -105,11 +109,7 @@ async function main(): Promise<void> {
|
|
|
105
109
|
const cwd = data.cwd || process.cwd();
|
|
106
110
|
log = new Logger("agent-signals", cwd);
|
|
107
111
|
|
|
108
|
-
const binary = findAideBinary(
|
|
109
|
-
cwd,
|
|
110
|
-
pluginRoot:
|
|
111
|
-
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
112
|
-
});
|
|
112
|
+
const binary = findAideBinary(cwd);
|
|
113
113
|
if (!binary) {
|
|
114
114
|
passThrough();
|
|
115
115
|
return;
|
|
@@ -237,13 +237,6 @@ async function main(): Promise<void> {
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
passThrough();
|
|
242
|
-
process.exit(0);
|
|
243
|
-
});
|
|
244
|
-
process.on("unhandledRejection", () => {
|
|
245
|
-
passThrough();
|
|
246
|
-
process.exit(0);
|
|
247
|
-
});
|
|
240
|
+
installHookSafetyNet(SOURCE);
|
|
248
241
|
|
|
249
242
|
main();
|
|
@@ -9,14 +9,18 @@
|
|
|
9
9
|
* Core logic is in src/core/comment-checker.ts for cross-platform reuse.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
readStdin,
|
|
14
|
+
emitHookResult,
|
|
15
|
+
installHookSafetyNet,
|
|
16
|
+
findAideBinary,
|
|
17
|
+
} from "../lib/hook-utils.js";
|
|
13
18
|
import { debug } from "../lib/logger.js";
|
|
14
19
|
import {
|
|
15
20
|
checkComments,
|
|
16
21
|
getCheckableFilePath,
|
|
17
22
|
getContentToCheck,
|
|
18
23
|
} from "../core/comment-checker.js";
|
|
19
|
-
import { findAideBinary } from "../core/aide-client.js";
|
|
20
24
|
import { emitInjectionEvent } from "../core/read-tracking.js";
|
|
21
25
|
|
|
22
26
|
const SOURCE = "comment-checker";
|
|
@@ -48,7 +52,7 @@ async function main(): Promise<void> {
|
|
|
48
52
|
try {
|
|
49
53
|
const input = await readStdin();
|
|
50
54
|
if (!input.trim()) {
|
|
51
|
-
|
|
55
|
+
emitHookResult({ continue: true });
|
|
52
56
|
return;
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -61,14 +65,14 @@ async function main(): Promise<void> {
|
|
|
61
65
|
// Only check Write/Edit/MultiEdit tool calls
|
|
62
66
|
const filePath = getCheckableFilePath(toolName, toolInput);
|
|
63
67
|
if (!filePath) {
|
|
64
|
-
|
|
68
|
+
emitHookResult({ continue: true });
|
|
65
69
|
return;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
// Get the content to analyze
|
|
69
73
|
const contentResult = getContentToCheck(toolName, toolInput);
|
|
70
74
|
if (!contentResult) {
|
|
71
|
-
|
|
75
|
+
emitHookResult({ continue: true });
|
|
72
76
|
return;
|
|
73
77
|
}
|
|
74
78
|
|
|
@@ -81,11 +85,7 @@ async function main(): Promise<void> {
|
|
|
81
85
|
`Detected ${result.suspiciousCount} suspicious comments in ${filePath}`,
|
|
82
86
|
);
|
|
83
87
|
try {
|
|
84
|
-
const binary = findAideBinary(
|
|
85
|
-
cwd,
|
|
86
|
-
pluginRoot:
|
|
87
|
-
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
88
|
-
});
|
|
88
|
+
const binary = findAideBinary(cwd);
|
|
89
89
|
if (binary && result.warning) {
|
|
90
90
|
emitInjectionEvent(binary, cwd, {
|
|
91
91
|
source: SOURCE,
|
|
@@ -109,33 +109,16 @@ async function main(): Promise<void> {
|
|
|
109
109
|
additionalContext: result.warning,
|
|
110
110
|
},
|
|
111
111
|
};
|
|
112
|
-
|
|
112
|
+
emitHookResult(output);
|
|
113
113
|
} else {
|
|
114
|
-
|
|
114
|
+
emitHookResult({ continue: true });
|
|
115
115
|
}
|
|
116
116
|
} catch (error) {
|
|
117
117
|
debug(SOURCE, `Hook error: ${error}`);
|
|
118
|
-
|
|
118
|
+
emitHookResult({ continue: true });
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
debug(SOURCE, `UNCAUGHT EXCEPTION: ${err}`);
|
|
124
|
-
try {
|
|
125
|
-
console.log(JSON.stringify({ continue: true }));
|
|
126
|
-
} catch {
|
|
127
|
-
console.log('{"continue":true}');
|
|
128
|
-
}
|
|
129
|
-
process.exit(0);
|
|
130
|
-
});
|
|
131
|
-
process.on("unhandledRejection", (reason) => {
|
|
132
|
-
debug(SOURCE, `UNHANDLED REJECTION: ${reason}`);
|
|
133
|
-
try {
|
|
134
|
-
console.log(JSON.stringify({ continue: true }));
|
|
135
|
-
} catch {
|
|
136
|
-
console.log('{"continue":true}');
|
|
137
|
-
}
|
|
138
|
-
process.exit(0);
|
|
139
|
-
});
|
|
122
|
+
installHookSafetyNet(SOURCE);
|
|
140
123
|
|
|
141
124
|
main();
|
|
@@ -11,10 +11,17 @@
|
|
|
11
11
|
* Core logic is in src/core/context-guard.ts for cross-platform reuse.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
readStdin,
|
|
16
|
+
emitHookResult,
|
|
17
|
+
installHookSafetyNet,
|
|
18
|
+
findAideBinary,
|
|
19
|
+
} from "../lib/hook-utils.js";
|
|
15
20
|
import { debug } from "../lib/logger.js";
|
|
16
|
-
import {
|
|
17
|
-
|
|
21
|
+
import {
|
|
22
|
+
checkContextGuard,
|
|
23
|
+
checkSmartReadHint,
|
|
24
|
+
} from "../core/context-guard.js";
|
|
18
25
|
import { emitInjectionEvent } from "../core/read-tracking.js";
|
|
19
26
|
|
|
20
27
|
const SOURCE = "context-guard";
|
|
@@ -44,7 +51,7 @@ async function main(): Promise<void> {
|
|
|
44
51
|
try {
|
|
45
52
|
const input = await readStdin();
|
|
46
53
|
if (!input.trim()) {
|
|
47
|
-
|
|
54
|
+
emitHookResult({ continue: true });
|
|
48
55
|
return;
|
|
49
56
|
}
|
|
50
57
|
|
|
@@ -55,11 +62,7 @@ async function main(): Promise<void> {
|
|
|
55
62
|
const sessionId = data.session_id || "unknown";
|
|
56
63
|
|
|
57
64
|
const result = checkContextGuard(toolName, toolInput, cwd, sessionId);
|
|
58
|
-
const binary = findAideBinary(
|
|
59
|
-
cwd,
|
|
60
|
-
pluginRoot:
|
|
61
|
-
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
62
|
-
});
|
|
65
|
+
const binary = findAideBinary(cwd);
|
|
63
66
|
|
|
64
67
|
if (result.shouldAdvise && result.advisory) {
|
|
65
68
|
debug(SOURCE, `Advising on large file read`);
|
|
@@ -84,7 +87,7 @@ async function main(): Promise<void> {
|
|
|
84
87
|
additionalContext: result.advisory,
|
|
85
88
|
},
|
|
86
89
|
};
|
|
87
|
-
|
|
90
|
+
emitHookResult(output);
|
|
88
91
|
} else {
|
|
89
92
|
// Smart read hint: suggest code index for re-reads of unchanged files
|
|
90
93
|
const hintResult = checkSmartReadHint(toolName, toolInput, cwd, binary);
|
|
@@ -111,34 +114,17 @@ async function main(): Promise<void> {
|
|
|
111
114
|
additionalContext: hintResult.hint,
|
|
112
115
|
},
|
|
113
116
|
};
|
|
114
|
-
|
|
117
|
+
emitHookResult(output);
|
|
115
118
|
} else {
|
|
116
|
-
|
|
119
|
+
emitHookResult({ continue: true });
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
} catch (error) {
|
|
120
123
|
debug(SOURCE, `Hook error: ${error}`);
|
|
121
|
-
|
|
124
|
+
emitHookResult({ continue: true });
|
|
122
125
|
}
|
|
123
126
|
}
|
|
124
127
|
|
|
125
|
-
|
|
126
|
-
debug(SOURCE, `UNCAUGHT EXCEPTION: ${err}`);
|
|
127
|
-
try {
|
|
128
|
-
console.log(JSON.stringify({ continue: true }));
|
|
129
|
-
} catch {
|
|
130
|
-
console.log('{"continue":true}');
|
|
131
|
-
}
|
|
132
|
-
process.exit(0);
|
|
133
|
-
});
|
|
134
|
-
process.on("unhandledRejection", (reason) => {
|
|
135
|
-
debug(SOURCE, `UNHANDLED REJECTION: ${reason}`);
|
|
136
|
-
try {
|
|
137
|
-
console.log(JSON.stringify({ continue: true }));
|
|
138
|
-
} catch {
|
|
139
|
-
console.log('{"continue":true}');
|
|
140
|
-
}
|
|
141
|
-
process.exit(0);
|
|
142
|
-
});
|
|
128
|
+
installHookSafetyNet(SOURCE);
|
|
143
129
|
|
|
144
130
|
main();
|
|
@@ -14,14 +14,18 @@
|
|
|
14
14
|
* Core logic is in src/core/context-pruning/ for cross-platform reuse.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
readStdin,
|
|
19
|
+
emitHookResult,
|
|
20
|
+
installHookSafetyNet,
|
|
21
|
+
findAideBinary,
|
|
22
|
+
} from "../lib/hook-utils.js";
|
|
18
23
|
import { debug } from "../lib/logger.js";
|
|
19
24
|
import { ContextPruningTracker } from "../core/context-pruning/index.js";
|
|
20
25
|
import type { ToolRecord } from "../core/context-pruning/types.js";
|
|
21
26
|
import { tmpdir } from "os";
|
|
22
27
|
import { join } from "path";
|
|
23
28
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
24
|
-
import { findAideBinary } from "../core/aide-client.js";
|
|
25
29
|
import { emitInjectionEvent } from "../core/read-tracking.js";
|
|
26
30
|
|
|
27
31
|
const SOURCE = "context-pruning";
|
|
@@ -103,7 +107,7 @@ async function main(): Promise<void> {
|
|
|
103
107
|
try {
|
|
104
108
|
const input = await readStdin();
|
|
105
109
|
if (!input.trim()) {
|
|
106
|
-
|
|
110
|
+
emitHookResult({ continue: true });
|
|
107
111
|
return;
|
|
108
112
|
}
|
|
109
113
|
|
|
@@ -116,7 +120,7 @@ async function main(): Promise<void> {
|
|
|
116
120
|
|
|
117
121
|
// Skip if no tool output to prune
|
|
118
122
|
if (!toolOutput || toolOutput.length < 50) {
|
|
119
|
-
|
|
123
|
+
emitHookResult({ continue: true });
|
|
120
124
|
return;
|
|
121
125
|
}
|
|
122
126
|
|
|
@@ -187,11 +191,7 @@ async function main(): Promise<void> {
|
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
try {
|
|
190
|
-
const binary = findAideBinary(
|
|
191
|
-
cwd,
|
|
192
|
-
pluginRoot:
|
|
193
|
-
process.env.AIDE_PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT,
|
|
194
|
-
});
|
|
194
|
+
const binary = findAideBinary(cwd);
|
|
195
195
|
const injected = output.hookSpecificOutput?.additionalContext;
|
|
196
196
|
if (binary && injected) {
|
|
197
197
|
emitInjectionEvent(binary, cwd, {
|
|
@@ -211,33 +211,16 @@ async function main(): Promise<void> {
|
|
|
211
211
|
// Non-fatal
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
emitHookResult(output);
|
|
215
215
|
} else {
|
|
216
|
-
|
|
216
|
+
emitHookResult({ continue: true });
|
|
217
217
|
}
|
|
218
218
|
} catch (error) {
|
|
219
219
|
debug(SOURCE, `Hook error: ${error}`);
|
|
220
|
-
|
|
220
|
+
emitHookResult({ continue: true });
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
debug(SOURCE, `UNCAUGHT EXCEPTION: ${err}`);
|
|
226
|
-
try {
|
|
227
|
-
console.log(JSON.stringify({ continue: true }));
|
|
228
|
-
} catch {
|
|
229
|
-
console.log('{"continue":true}');
|
|
230
|
-
}
|
|
231
|
-
process.exit(0);
|
|
232
|
-
});
|
|
233
|
-
process.on("unhandledRejection", (reason) => {
|
|
234
|
-
debug(SOURCE, `UNHANDLED REJECTION: ${reason}`);
|
|
235
|
-
try {
|
|
236
|
-
console.log(JSON.stringify({ continue: true }));
|
|
237
|
-
} catch {
|
|
238
|
-
console.log('{"continue":true}');
|
|
239
|
-
}
|
|
240
|
-
process.exit(0);
|
|
241
|
-
});
|
|
224
|
+
installHookSafetyNet(SOURCE);
|
|
242
225
|
|
|
243
226
|
main();
|