@jmylchreest/aide-plugin 0.0.42 → 0.0.44
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/context-usage/SKILL.md +157 -0
- package/skills/survey/SKILL.md +174 -0
- package/src/core/context-pruning/dedup.ts +173 -0
- package/src/core/context-pruning/index.ts +19 -0
- package/src/core/context-pruning/purge.ts +80 -0
- package/src/core/context-pruning/supersede.ts +67 -0
- package/src/core/context-pruning/tracker.ts +179 -0
- package/src/core/context-pruning/types.ts +63 -0
- package/src/core/session-init.ts +12 -6
- package/src/core/session-summary-logic.ts +16 -13
- package/src/core/skill-matcher.ts +28 -8
- package/src/core/types.ts +4 -0
- package/src/opencode/hooks.ts +116 -12
package/src/opencode/hooks.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { execFileSync } from "child_process";
|
|
29
|
+
import { join } from "path";
|
|
29
30
|
import { findAideBinary } from "../core/aide-client.js";
|
|
30
31
|
import {
|
|
31
32
|
ensureDirectories,
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
buildSummaryFromPartials,
|
|
67
68
|
cleanupPartials,
|
|
68
69
|
} from "../core/partial-memory.js";
|
|
70
|
+
import { ContextPruningTracker } from "../core/context-pruning/index.js";
|
|
69
71
|
import type { MemoryInjection, SessionState } from "../core/types.js";
|
|
70
72
|
import type {
|
|
71
73
|
Hooks,
|
|
@@ -114,6 +116,8 @@ interface AideState {
|
|
|
114
116
|
/** Per-session metadata for agent-like tracking */
|
|
115
117
|
sessionInfoMap: Map<string, SessionInfo>;
|
|
116
118
|
client: OpenCodeClient;
|
|
119
|
+
/** Context pruning tracker for dedup/supersede/purge of tool outputs */
|
|
120
|
+
pruningTracker: ContextPruningTracker;
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
/**
|
|
@@ -142,6 +146,7 @@ export async function createHooks(
|
|
|
142
146
|
lastUserPrompt: null,
|
|
143
147
|
sessionInfoMap: new Map(),
|
|
144
148
|
client,
|
|
149
|
+
pruningTracker: new ContextPruningTracker(cwd),
|
|
145
150
|
};
|
|
146
151
|
|
|
147
152
|
// Run one-time initialization (directories, binary, config)
|
|
@@ -184,16 +189,49 @@ function createConfigHandler(
|
|
|
184
189
|
// Discover all skills and register them as OpenCode commands
|
|
185
190
|
const skills = discoverSkills(state.cwd, state.pluginRoot ?? undefined);
|
|
186
191
|
|
|
192
|
+
// Register our skill directories with OpenCode's native skill discovery
|
|
193
|
+
// as a fallback, so the native `skill` tool can also find them.
|
|
194
|
+
const skillsConfig = (input as Record<string, unknown>).skills as
|
|
195
|
+
| { paths?: string[]; urls?: string[] }
|
|
196
|
+
| undefined;
|
|
197
|
+
const existingPaths = skillsConfig?.paths ?? [];
|
|
198
|
+
const aidePaths = [
|
|
199
|
+
join(state.cwd, ".aide", "skills"),
|
|
200
|
+
join(state.cwd, "skills"),
|
|
201
|
+
];
|
|
202
|
+
if (state.pluginRoot) {
|
|
203
|
+
aidePaths.push(join(state.pluginRoot, "skills"));
|
|
204
|
+
}
|
|
205
|
+
// Only add paths that aren't already registered
|
|
206
|
+
const newPaths = aidePaths.filter((p) => !existingPaths.includes(p));
|
|
207
|
+
if (newPaths.length > 0) {
|
|
208
|
+
(input as Record<string, unknown>).skills = {
|
|
209
|
+
...skillsConfig,
|
|
210
|
+
paths: [...existingPaths, ...newPaths],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
187
214
|
if (!input.command) {
|
|
188
215
|
input.command = {};
|
|
189
216
|
}
|
|
190
217
|
|
|
191
218
|
for (const skill of skills) {
|
|
219
|
+
// Skip skills restricted to other platforms
|
|
220
|
+
if (
|
|
221
|
+
skill.platforms &&
|
|
222
|
+
skill.platforms.length > 0 &&
|
|
223
|
+
!skill.platforms.includes("opencode")
|
|
224
|
+
) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
192
227
|
const commandName = `aide:${skill.name}`;
|
|
193
228
|
// Only register if not already defined (user config takes priority)
|
|
194
229
|
if (!input.command[commandName]) {
|
|
195
230
|
input.command[commandName] = {
|
|
196
|
-
|
|
231
|
+
// IMPORTANT: Template wording must NOT trigger the native "skill" tool.
|
|
232
|
+
// The actual instructions are injected into the system prompt by
|
|
233
|
+
// createCommandHandler → pendingSkillsContext → createSystemTransformHandler.
|
|
234
|
+
template: `Follow the aide "${skill.name}" instructions that have been injected into your system prompt. Do NOT use the skill tool. {{arguments}}`,
|
|
197
235
|
description: skill.description || `aide ${skill.name} skill`,
|
|
198
236
|
};
|
|
199
237
|
}
|
|
@@ -234,9 +272,18 @@ function createCommandHandler(state: AideState): (
|
|
|
234
272
|
// Format the skill content for injection
|
|
235
273
|
const context = formatSkillsContext([skill]);
|
|
236
274
|
|
|
237
|
-
// Store for system transform injection
|
|
275
|
+
// Store for system transform injection (into system prompt)
|
|
238
276
|
state.pendingSkillsContext = context;
|
|
239
277
|
|
|
278
|
+
// Also inject into the command output parts so the model sees the
|
|
279
|
+
// instructions directly in the user message. This avoids reliance
|
|
280
|
+
// on the system transform alone and prevents the model from trying
|
|
281
|
+
// to call the native "skill" tool to find the instructions.
|
|
282
|
+
output.parts.push({
|
|
283
|
+
type: "text",
|
|
284
|
+
text: `<aide-instructions>\n${context}\n</aide-instructions>`,
|
|
285
|
+
});
|
|
286
|
+
|
|
240
287
|
// Also store the arguments as the user prompt for the transform
|
|
241
288
|
if (args) {
|
|
242
289
|
state.lastUserPrompt = args;
|
|
@@ -299,7 +346,7 @@ function initializeAide(state: AideState): void {
|
|
|
299
346
|
state.binary,
|
|
300
347
|
state.cwd,
|
|
301
348
|
projectName,
|
|
302
|
-
|
|
349
|
+
2,
|
|
303
350
|
config,
|
|
304
351
|
);
|
|
305
352
|
}
|
|
@@ -506,14 +553,20 @@ async function handleSessionIdle(
|
|
|
506
553
|
let summary: string | null = null;
|
|
507
554
|
|
|
508
555
|
if (partials.length > 0) {
|
|
509
|
-
const commits = getSessionCommits(
|
|
556
|
+
const commits = getSessionCommits(
|
|
557
|
+
state.cwd,
|
|
558
|
+
state.sessionState?.startedAt,
|
|
559
|
+
);
|
|
510
560
|
summary = buildSummaryFromPartials(partials, commits, []);
|
|
511
561
|
debug(SOURCE, `Built summary from ${partials.length} partials`);
|
|
512
562
|
}
|
|
513
563
|
|
|
514
564
|
// Fall back to state-only summary if no partials
|
|
515
565
|
if (!summary) {
|
|
516
|
-
summary = buildSessionSummaryFromState(
|
|
566
|
+
summary = buildSessionSummaryFromState(
|
|
567
|
+
state.cwd,
|
|
568
|
+
state.sessionState?.startedAt,
|
|
569
|
+
);
|
|
517
570
|
}
|
|
518
571
|
|
|
519
572
|
if (summary) {
|
|
@@ -584,7 +637,7 @@ async function handleMessagePartUpdated(
|
|
|
584
637
|
state.lastUserPrompt = prompt;
|
|
585
638
|
|
|
586
639
|
const skills = discoverSkills(state.cwd, state.pluginRoot ?? undefined);
|
|
587
|
-
const matched = matchSkills(prompt, skills, 3);
|
|
640
|
+
const matched = matchSkills(prompt, skills, 3, "opencode");
|
|
588
641
|
|
|
589
642
|
if (matched.length > 0) {
|
|
590
643
|
try {
|
|
@@ -704,6 +757,29 @@ function createToolAfterHandler(
|
|
|
704
757
|
debug(SOURCE, `Partial memory write failed (non-fatal): ${err}`);
|
|
705
758
|
}
|
|
706
759
|
|
|
760
|
+
// Context pruning: dedup/supersede/purge tool outputs
|
|
761
|
+
try {
|
|
762
|
+
const toolArgs = (_output.metadata?.args || {}) as Record<
|
|
763
|
+
string,
|
|
764
|
+
unknown
|
|
765
|
+
>;
|
|
766
|
+
const pruneResult = state.pruningTracker.process(
|
|
767
|
+
input.callID,
|
|
768
|
+
input.tool,
|
|
769
|
+
toolArgs,
|
|
770
|
+
_output.output,
|
|
771
|
+
);
|
|
772
|
+
if (pruneResult.modified) {
|
|
773
|
+
_output.output = pruneResult.output;
|
|
774
|
+
debug(
|
|
775
|
+
SOURCE,
|
|
776
|
+
`Context pruning [${pruneResult.strategy}]: saved ${pruneResult.bytesSaved} bytes for ${input.tool}`,
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
} catch (err) {
|
|
780
|
+
debug(SOURCE, `Context pruning failed (non-fatal): ${err}`);
|
|
781
|
+
}
|
|
782
|
+
|
|
707
783
|
// Comment checker: detect excessive comments in Write/Edit output
|
|
708
784
|
try {
|
|
709
785
|
const toolArgs = (_output.metadata?.args || {}) as Record<
|
|
@@ -742,6 +818,10 @@ function createCompactionHandler(
|
|
|
742
818
|
output: { context: string[]; prompt?: string },
|
|
743
819
|
) => Promise<void> {
|
|
744
820
|
return async (input, output) => {
|
|
821
|
+
// Reset context pruning tracker — compaction clears the conversation
|
|
822
|
+
// history, so dedup/supersede references would be stale.
|
|
823
|
+
state.pruningTracker.reset();
|
|
824
|
+
|
|
745
825
|
// Save state snapshot before compaction
|
|
746
826
|
if (state.binary) {
|
|
747
827
|
saveStateSnapshot(state.binary, state.cwd, input.sessionID);
|
|
@@ -758,7 +838,10 @@ function createCompactionHandler(
|
|
|
758
838
|
let summary: string | null = null;
|
|
759
839
|
|
|
760
840
|
if (partials.length > 0) {
|
|
761
|
-
const commits = getSessionCommits(
|
|
841
|
+
const commits = getSessionCommits(
|
|
842
|
+
state.cwd,
|
|
843
|
+
state.sessionState?.startedAt,
|
|
844
|
+
);
|
|
762
845
|
summary = buildSummaryFromPartials(partials, commits, []);
|
|
763
846
|
debug(
|
|
764
847
|
SOURCE,
|
|
@@ -768,7 +851,10 @@ function createCompactionHandler(
|
|
|
768
851
|
|
|
769
852
|
// Fall back to state-only summary if no partials
|
|
770
853
|
if (!summary) {
|
|
771
|
-
summary = buildSessionSummaryFromState(
|
|
854
|
+
summary = buildSessionSummaryFromState(
|
|
855
|
+
state.cwd,
|
|
856
|
+
state.sessionState?.startedAt,
|
|
857
|
+
);
|
|
772
858
|
}
|
|
773
859
|
|
|
774
860
|
if (summary) {
|
|
@@ -802,7 +888,7 @@ function createCompactionHandler(
|
|
|
802
888
|
state.binary,
|
|
803
889
|
state.cwd,
|
|
804
890
|
projectName,
|
|
805
|
-
|
|
891
|
+
2,
|
|
806
892
|
);
|
|
807
893
|
state.memories = freshMemories;
|
|
808
894
|
state.welcomeContext = buildWelcomeContext(
|
|
@@ -844,6 +930,17 @@ function createSystemTransformHandler(
|
|
|
844
930
|
output.system.push(state.welcomeContext);
|
|
845
931
|
}
|
|
846
932
|
|
|
933
|
+
// Inject context pruning notes only after the first prune fires.
|
|
934
|
+
// Avoids adding ~280 bytes to every session that never triggers pruning.
|
|
935
|
+
if (state.pruningTracker.getStats().prunedCalls > 0) {
|
|
936
|
+
output.system.push(`<aide-context-pruning>
|
|
937
|
+
Tool outputs may contain these tags from aide's context optimization:
|
|
938
|
+
- [aide:dedup] — This output is identical to a previous call. Refer to the earlier result.
|
|
939
|
+
- [aide:supersede] — A prior Read of this file is now stale after a Write/Edit.
|
|
940
|
+
- [aide:purge] — Large error output was trimmed. Re-run the command for full output.
|
|
941
|
+
</aide-context-pruning>`);
|
|
942
|
+
}
|
|
943
|
+
|
|
847
944
|
// Inject per-session context only when swarm mode is active and session
|
|
848
945
|
// has been assigned a worktree/role (e.g., by swarm orchestration).
|
|
849
946
|
// Without this guard, every normal session would incorrectly be told
|
|
@@ -894,7 +991,12 @@ function createSystemTransformHandler(
|
|
|
894
991
|
} else if (state.lastUserPrompt) {
|
|
895
992
|
try {
|
|
896
993
|
const skills = discoverSkills(state.cwd, state.pluginRoot ?? undefined);
|
|
897
|
-
const matched = matchSkills(
|
|
994
|
+
const matched = matchSkills(
|
|
995
|
+
state.lastUserPrompt,
|
|
996
|
+
skills,
|
|
997
|
+
3,
|
|
998
|
+
"opencode",
|
|
999
|
+
);
|
|
898
1000
|
if (matched.length > 0) {
|
|
899
1001
|
output.system.push(formatSkillsContext(matched));
|
|
900
1002
|
debug(
|
|
@@ -907,8 +1009,9 @@ function createSystemTransformHandler(
|
|
|
907
1009
|
}
|
|
908
1010
|
}
|
|
909
1011
|
|
|
910
|
-
// Inject messaging protocol
|
|
911
|
-
|
|
1012
|
+
// Inject messaging protocol only in swarm mode (saves ~450 bytes per turn otherwise)
|
|
1013
|
+
if (rawMode === "swarm") {
|
|
1014
|
+
output.system.push(`<aide-messaging>
|
|
912
1015
|
|
|
913
1016
|
## Agent Messaging
|
|
914
1017
|
|
|
@@ -927,6 +1030,7 @@ Use aide MCP tools to coordinate with other agents or sessions:
|
|
|
927
1030
|
- Check messages periodically for requests from other agents
|
|
928
1031
|
|
|
929
1032
|
</aide-messaging>`);
|
|
1033
|
+
}
|
|
930
1034
|
};
|
|
931
1035
|
}
|
|
932
1036
|
|