opencodekit 0.12.4 → 0.12.5
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/dist/index.js +2 -2
- package/dist/template/.opencode/command/accessibility-check.md +7 -10
- package/dist/template/.opencode/command/analyze-mockup.md +3 -16
- package/dist/template/.opencode/command/analyze-project.md +57 -69
- package/dist/template/.opencode/command/brainstorm.md +3 -11
- package/dist/template/.opencode/command/commit.md +10 -18
- package/dist/template/.opencode/command/create.md +4 -8
- package/dist/template/.opencode/command/design-audit.md +24 -51
- package/dist/template/.opencode/command/design.md +10 -17
- package/dist/template/.opencode/command/finish.md +9 -9
- package/dist/template/.opencode/command/fix-ci.md +7 -28
- package/dist/template/.opencode/command/fix-types.md +3 -7
- package/dist/template/.opencode/command/fix-ui.md +5 -11
- package/dist/template/.opencode/command/fix.md +4 -10
- package/dist/template/.opencode/command/handoff.md +8 -14
- package/dist/template/.opencode/command/implement.md +13 -16
- package/dist/template/.opencode/command/import-plan.md +20 -38
- package/dist/template/.opencode/command/init.md +9 -13
- package/dist/template/.opencode/command/integration-test.md +11 -13
- package/dist/template/.opencode/command/issue.md +4 -8
- package/dist/template/.opencode/command/new-feature.md +20 -40
- package/dist/template/.opencode/command/plan.md +8 -12
- package/dist/template/.opencode/command/pr.md +29 -38
- package/dist/template/.opencode/command/quick-build.md +3 -7
- package/dist/template/.opencode/command/research-and-implement.md +4 -6
- package/dist/template/.opencode/command/research.md +10 -7
- package/dist/template/.opencode/command/resume.md +12 -24
- package/dist/template/.opencode/command/revert-feature.md +21 -56
- package/dist/template/.opencode/command/review-codebase.md +21 -23
- package/dist/template/.opencode/command/skill-create.md +1 -5
- package/dist/template/.opencode/command/skill-optimize.md +3 -10
- package/dist/template/.opencode/command/status.md +28 -25
- package/dist/template/.opencode/command/triage.md +19 -31
- package/dist/template/.opencode/command/ui-review.md +6 -13
- package/dist/template/.opencode/command.backup/analyze-project.md +465 -0
- package/dist/template/.opencode/command.backup/finish.md +167 -0
- package/dist/template/.opencode/command.backup/implement.md +143 -0
- package/dist/template/.opencode/command.backup/pr.md +252 -0
- package/dist/template/.opencode/command.backup/status.md +376 -0
- package/dist/template/.opencode/memory/project/SHELL_OUTPUT_MIGRATION_PLAN.md +551 -0
- package/dist/template/.opencode/memory/project/gotchas.md +33 -28
- package/dist/template/.opencode/opencode.json +14 -28
- package/dist/template/.opencode/package.json +1 -3
- package/dist/template/.opencode/plugin/compaction.ts +51 -129
- package/dist/template/.opencode/plugin/handoff.ts +18 -163
- package/dist/template/.opencode/plugin/notification.ts +1 -1
- package/dist/template/.opencode/plugin/package.json +7 -0
- package/dist/template/.opencode/plugin/sessions.ts +185 -651
- package/dist/template/.opencode/plugin/skill-mcp.ts +2 -1
- package/dist/template/.opencode/plugin/truncator.ts +19 -41
- package/dist/template/.opencode/plugin/tsconfig.json +14 -13
- package/dist/template/.opencode/tool/bd-inbox.ts +109 -0
- package/dist/template/.opencode/tool/bd-msg.ts +62 -0
- package/dist/template/.opencode/tool/bd-release.ts +71 -0
- package/dist/template/.opencode/tool/bd-reserve.ts +120 -0
- package/package.json +2 -2
- package/dist/template/.opencode/plugin/beads.ts +0 -1419
- package/dist/template/.opencode/plugin/compactor.ts +0 -107
- package/dist/template/.opencode/plugin/enforcer.ts +0 -190
- package/dist/template/.opencode/plugin/injector.ts +0 -150
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode Compactor Plugin
|
|
3
|
-
* Warns at token thresholds before hitting limits
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
7
|
-
import {
|
|
8
|
-
THRESHOLDS,
|
|
9
|
-
type TokenStats,
|
|
10
|
-
getContextPercentage,
|
|
11
|
-
notify,
|
|
12
|
-
} from "./lib/notify";
|
|
13
|
-
|
|
14
|
-
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
15
|
-
|
|
16
|
-
interface WarningLevel {
|
|
17
|
-
level: LogLevel;
|
|
18
|
-
emoji: string;
|
|
19
|
-
action: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getWarningLevel(percentage: number): WarningLevel | null {
|
|
23
|
-
if (percentage >= THRESHOLDS.CRITICAL) {
|
|
24
|
-
return {
|
|
25
|
-
level: "error",
|
|
26
|
-
emoji: "🚨",
|
|
27
|
-
action: "CRITICAL: Prune immediately or start new session.",
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
if (percentage >= THRESHOLDS.URGENT) {
|
|
31
|
-
return {
|
|
32
|
-
level: "warn",
|
|
33
|
-
emoji: "⚠️",
|
|
34
|
-
action: "Consider pruning completed tool outputs.",
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
if (percentage >= THRESHOLDS.MODERATE) {
|
|
38
|
-
return {
|
|
39
|
-
level: "info",
|
|
40
|
-
emoji: "📈",
|
|
41
|
-
action: "Context at 70%. Consider consolidating.",
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const CompactorPlugin: Plugin = async ({ client, $ }) => {
|
|
48
|
-
const warnedSessions = new Map<string, number>();
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
event: async ({ event }) => {
|
|
52
|
-
const props = event.properties as Record<string, unknown>;
|
|
53
|
-
|
|
54
|
-
if (event.type === "session.updated") {
|
|
55
|
-
const info = props?.info as Record<string, unknown> | undefined;
|
|
56
|
-
const tokenStats = (info?.tokens || props?.tokens) as
|
|
57
|
-
| TokenStats
|
|
58
|
-
| undefined;
|
|
59
|
-
const sessionId = (info?.id || props?.sessionID) as string | undefined;
|
|
60
|
-
|
|
61
|
-
if (!sessionId || !tokenStats?.used || !tokenStats?.limit) return;
|
|
62
|
-
|
|
63
|
-
const pct = getContextPercentage(tokenStats);
|
|
64
|
-
const lastWarned = warnedSessions.get(sessionId) || 0;
|
|
65
|
-
|
|
66
|
-
const warning = getWarningLevel(pct);
|
|
67
|
-
if (!warning) return;
|
|
68
|
-
|
|
69
|
-
let currentThreshold = 0;
|
|
70
|
-
if (pct >= THRESHOLDS.CRITICAL) currentThreshold = THRESHOLDS.CRITICAL;
|
|
71
|
-
else if (pct >= THRESHOLDS.URGENT) currentThreshold = THRESHOLDS.URGENT;
|
|
72
|
-
else if (pct >= THRESHOLDS.MODERATE)
|
|
73
|
-
currentThreshold = THRESHOLDS.MODERATE;
|
|
74
|
-
|
|
75
|
-
if (lastWarned >= currentThreshold) return;
|
|
76
|
-
|
|
77
|
-
warnedSessions.set(sessionId, currentThreshold);
|
|
78
|
-
|
|
79
|
-
const message = `${warning.emoji} Context: ${pct}% (${tokenStats.used.toLocaleString()}/${tokenStats.limit.toLocaleString()} tokens). ${warning.action}`;
|
|
80
|
-
|
|
81
|
-
client.app
|
|
82
|
-
.log({
|
|
83
|
-
body: { service: "compactor", level: warning.level, message },
|
|
84
|
-
})
|
|
85
|
-
.catch(() => {});
|
|
86
|
-
|
|
87
|
-
if (pct >= THRESHOLDS.URGENT) {
|
|
88
|
-
await notify($, `Context ${pct}%`, warning.action);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (event.type === "session.compacted") {
|
|
93
|
-
const sessionId = props?.sessionID as string | undefined;
|
|
94
|
-
if (sessionId) {
|
|
95
|
-
warnedSessions.set(sessionId, 0);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (event.type === "session.deleted") {
|
|
100
|
-
const sessionId = props?.sessionID as string | undefined;
|
|
101
|
-
if (sessionId) {
|
|
102
|
-
warnedSessions.delete(sessionId);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
};
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode Enforcer Plugin
|
|
3
|
-
* ENFORCES continuation when session idles with incomplete TODOs
|
|
4
|
-
*
|
|
5
|
-
* Upgrade from notification-only to actual enforcement:
|
|
6
|
-
* Uses client.session.promptAsync() to inject continuation prompt
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
10
|
-
import { notify } from "./lib/notify";
|
|
11
|
-
|
|
12
|
-
interface TodoItem {
|
|
13
|
-
id: string;
|
|
14
|
-
content: string;
|
|
15
|
-
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
16
|
-
priority: "high" | "medium" | "low";
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Cooldown to prevent rapid-fire enforcement (5 minutes)
|
|
20
|
-
const ENFORCEMENT_COOLDOWN_MS = 5 * 60 * 1000;
|
|
21
|
-
|
|
22
|
-
// Track last enforcement time per session
|
|
23
|
-
const lastEnforcement = new Map<string, number>();
|
|
24
|
-
|
|
25
|
-
// Track todos per session
|
|
26
|
-
const sessionTodos = new Map<string, TodoItem[]>();
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Build continuation prompt based on incomplete TODOs
|
|
30
|
-
*/
|
|
31
|
-
function buildContinuationPrompt(incomplete: TodoItem[]): string {
|
|
32
|
-
const highPriority = incomplete.filter((t) => t.priority === "high");
|
|
33
|
-
const inProgress = incomplete.filter((t) => t.status === "in_progress");
|
|
34
|
-
|
|
35
|
-
let prompt = "You stopped with incomplete work. Continue immediately.\n\n";
|
|
36
|
-
|
|
37
|
-
if (inProgress.length > 0) {
|
|
38
|
-
prompt += "**In Progress (finish these first):**\n";
|
|
39
|
-
inProgress.forEach((t) => {
|
|
40
|
-
prompt += `- ${t.content}\n`;
|
|
41
|
-
});
|
|
42
|
-
prompt += "\n";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (highPriority.length > 0) {
|
|
46
|
-
const remaining = highPriority.filter((t) => t.status !== "in_progress");
|
|
47
|
-
if (remaining.length > 0) {
|
|
48
|
-
prompt += "**High Priority:**\n";
|
|
49
|
-
remaining.forEach((t) => {
|
|
50
|
-
prompt += `- ${t.content}\n`;
|
|
51
|
-
});
|
|
52
|
-
prompt += "\n";
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const others = incomplete.filter(
|
|
57
|
-
(t) => t.priority !== "high" && t.status !== "in_progress",
|
|
58
|
-
);
|
|
59
|
-
if (others.length > 0) {
|
|
60
|
-
prompt += "**Remaining:**\n";
|
|
61
|
-
others.slice(0, 5).forEach((t) => {
|
|
62
|
-
prompt += `- ${t.content}\n`;
|
|
63
|
-
});
|
|
64
|
-
if (others.length > 5) {
|
|
65
|
-
prompt += `- ... and ${others.length - 5} more\n`;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
prompt += "\nPick up where you left off. Do not ask for confirmation.";
|
|
70
|
-
|
|
71
|
-
return prompt;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export const EnforcerPlugin: Plugin = async ({ client, $ }) => {
|
|
75
|
-
return {
|
|
76
|
-
event: async ({ event }) => {
|
|
77
|
-
const props = event.properties as Record<string, unknown>;
|
|
78
|
-
|
|
79
|
-
// Track TODO updates
|
|
80
|
-
if (event.type === "todo.updated") {
|
|
81
|
-
const sessionId = props?.sessionID as string | undefined;
|
|
82
|
-
const todos = props?.todos as TodoItem[] | undefined;
|
|
83
|
-
|
|
84
|
-
if (sessionId && todos) {
|
|
85
|
-
sessionTodos.set(sessionId, todos);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Enforce on session idle
|
|
90
|
-
if (event.type === "session.idle") {
|
|
91
|
-
const sessionId = props?.sessionID as string | undefined;
|
|
92
|
-
if (!sessionId) return;
|
|
93
|
-
|
|
94
|
-
const todos = sessionTodos.get(sessionId) || [];
|
|
95
|
-
const incomplete = todos.filter(
|
|
96
|
-
(t) => t.status === "pending" || t.status === "in_progress",
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
// No incomplete TODOs - nothing to enforce
|
|
100
|
-
if (incomplete.length === 0) return;
|
|
101
|
-
|
|
102
|
-
// Check cooldown to prevent spam
|
|
103
|
-
const now = Date.now();
|
|
104
|
-
const lastTime = lastEnforcement.get(sessionId) || 0;
|
|
105
|
-
if (now - lastTime < ENFORCEMENT_COOLDOWN_MS) {
|
|
106
|
-
return; // Still in cooldown
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Count high priority and in-progress
|
|
110
|
-
const highPriority = incomplete.filter((t) => t.priority === "high");
|
|
111
|
-
const inProgress = incomplete.filter((t) => t.status === "in_progress");
|
|
112
|
-
|
|
113
|
-
// Only enforce for high-priority or in-progress items
|
|
114
|
-
// Low priority pending items don't warrant forced continuation
|
|
115
|
-
if (highPriority.length === 0 && inProgress.length === 0) {
|
|
116
|
-
// Just notify for low-priority items
|
|
117
|
-
await notify(
|
|
118
|
-
$,
|
|
119
|
-
"Session Idle",
|
|
120
|
-
`📋 ${incomplete.length} TODO${incomplete.length > 1 ? "s" : ""} remaining`,
|
|
121
|
-
);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Update cooldown
|
|
126
|
-
lastEnforcement.set(sessionId, now);
|
|
127
|
-
|
|
128
|
-
// Build and send continuation prompt
|
|
129
|
-
const continuationPrompt = buildContinuationPrompt(incomplete);
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
await client.session.promptAsync({
|
|
133
|
-
path: { id: sessionId },
|
|
134
|
-
body: {
|
|
135
|
-
parts: [
|
|
136
|
-
{
|
|
137
|
-
type: "text",
|
|
138
|
-
text: continuationPrompt,
|
|
139
|
-
},
|
|
140
|
-
],
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
client.app
|
|
145
|
-
.log({
|
|
146
|
-
body: {
|
|
147
|
-
service: "enforcer",
|
|
148
|
-
level: "info",
|
|
149
|
-
message: `Enforced continuation: ${inProgress.length} in-progress, ${highPriority.length} high-priority TODOs`,
|
|
150
|
-
},
|
|
151
|
-
})
|
|
152
|
-
.catch(() => {});
|
|
153
|
-
|
|
154
|
-
await notify(
|
|
155
|
-
$,
|
|
156
|
-
"Enforcer",
|
|
157
|
-
`Continuing ${inProgress.length + highPriority.length} incomplete TODOs`,
|
|
158
|
-
);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
// Fallback to notification if prompt fails
|
|
161
|
-
client.app
|
|
162
|
-
.log({
|
|
163
|
-
body: {
|
|
164
|
-
service: "enforcer",
|
|
165
|
-
level: "warn",
|
|
166
|
-
message: `Enforcement failed: ${(error as Error).message}`,
|
|
167
|
-
},
|
|
168
|
-
})
|
|
169
|
-
.catch(() => {});
|
|
170
|
-
|
|
171
|
-
const message =
|
|
172
|
-
highPriority.length > 0
|
|
173
|
-
? `🔴 ${highPriority.length} high-priority TODO${highPriority.length > 1 ? "s" : ""} incomplete`
|
|
174
|
-
: `🟡 ${inProgress.length} TODO${inProgress.length > 1 ? "s" : ""} still in progress`;
|
|
175
|
-
|
|
176
|
-
await notify($, "Session Idle", message);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Cleanup on session delete
|
|
181
|
-
if (event.type === "session.deleted") {
|
|
182
|
-
const sessionId = props?.sessionID as string | undefined;
|
|
183
|
-
if (sessionId) {
|
|
184
|
-
sessionTodos.delete(sessionId);
|
|
185
|
-
lastEnforcement.delete(sessionId);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
};
|
|
190
|
-
};
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode AGENTS.md Hierarchy Injector Plugin
|
|
3
|
-
*
|
|
4
|
-
* Walks up from read file's directory to project root, collecting ALL AGENTS.md
|
|
5
|
-
* files and injecting them into context. Solves the limitation where OpenCode's
|
|
6
|
-
* findUp only finds the first match.
|
|
7
|
-
*
|
|
8
|
-
* Injection order: root → specific (T-shaped context loading)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { existsSync, readFileSync } from "fs";
|
|
12
|
-
import { dirname, join, resolve } from "path";
|
|
13
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
14
|
-
|
|
15
|
-
// Cache injected directories per session to avoid duplicates
|
|
16
|
-
const sessionInjectedDirs = new Map<string, Set<string>>();
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Walk up from a directory to root, collecting all AGENTS.md files
|
|
20
|
-
*/
|
|
21
|
-
function collectAgentsMdFiles(
|
|
22
|
-
startDir: string,
|
|
23
|
-
projectRoot: string,
|
|
24
|
-
): { path: string; content: string }[] {
|
|
25
|
-
const files: { path: string; content: string }[] = [];
|
|
26
|
-
let currentDir = resolve(startDir);
|
|
27
|
-
const root = resolve(projectRoot);
|
|
28
|
-
|
|
29
|
-
// Walk up until we hit project root or filesystem root
|
|
30
|
-
while (currentDir.startsWith(root) || currentDir === root) {
|
|
31
|
-
const agentsMdPath = join(currentDir, "AGENTS.md");
|
|
32
|
-
|
|
33
|
-
if (existsSync(agentsMdPath)) {
|
|
34
|
-
try {
|
|
35
|
-
const content = readFileSync(agentsMdPath, "utf-8");
|
|
36
|
-
files.push({ path: agentsMdPath, content });
|
|
37
|
-
} catch {
|
|
38
|
-
// Skip unreadable files
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Move up one directory
|
|
43
|
-
const parentDir = dirname(currentDir);
|
|
44
|
-
if (parentDir === currentDir) break; // Hit filesystem root
|
|
45
|
-
currentDir = parentDir;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Reverse so root comes first (T-shaped: general → specific)
|
|
49
|
-
return files.reverse();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Check if a file path is a code file (not AGENTS.md itself)
|
|
54
|
-
*/
|
|
55
|
-
function isCodeFile(filePath: string): boolean {
|
|
56
|
-
const lowerPath = filePath.toLowerCase();
|
|
57
|
-
|
|
58
|
-
// Skip AGENTS.md files themselves
|
|
59
|
-
if (lowerPath.endsWith("agents.md")) return false;
|
|
60
|
-
|
|
61
|
-
// Skip common non-code files
|
|
62
|
-
if (lowerPath.includes("node_modules/")) return false;
|
|
63
|
-
if (lowerPath.includes("/.git/")) return false;
|
|
64
|
-
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Format injected AGENTS.md content
|
|
70
|
-
*/
|
|
71
|
-
function formatInjection(
|
|
72
|
-
files: { path: string; content: string }[],
|
|
73
|
-
projectRoot: string,
|
|
74
|
-
): string {
|
|
75
|
-
if (files.length === 0) return "";
|
|
76
|
-
|
|
77
|
-
const sections = files.map((f) => {
|
|
78
|
-
const relativePath = f.path.replace(projectRoot, "").replace(/^\//, "");
|
|
79
|
-
return `<!-- AGENTS.md from: ${relativePath} -->\n${f.content}`;
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
"\n\n---\n## Injected AGENTS.md Hierarchy\n\n" +
|
|
84
|
-
sections.join("\n\n---\n\n") +
|
|
85
|
-
"\n---\n\n"
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const InjectorPlugin: Plugin = async ({ directory, worktree }) => {
|
|
90
|
-
// Use worktree if available (git worktree), otherwise use directory
|
|
91
|
-
const projectRoot = worktree || directory;
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
"tool.execute.after": async (input, output) => {
|
|
95
|
-
// Only process read tool
|
|
96
|
-
if (input.tool !== "read") return;
|
|
97
|
-
|
|
98
|
-
// Get the file path from metadata
|
|
99
|
-
const filePath = output.metadata?.filePath as string | undefined;
|
|
100
|
-
if (!filePath) return;
|
|
101
|
-
|
|
102
|
-
// Skip non-code files
|
|
103
|
-
if (!isCodeFile(filePath)) return;
|
|
104
|
-
|
|
105
|
-
// Get or create session cache
|
|
106
|
-
let injectedDirs = sessionInjectedDirs.get(input.sessionID);
|
|
107
|
-
if (!injectedDirs) {
|
|
108
|
-
injectedDirs = new Set();
|
|
109
|
-
sessionInjectedDirs.set(input.sessionID, injectedDirs);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Get the directory of the read file
|
|
113
|
-
const fileDir = dirname(resolve(filePath));
|
|
114
|
-
|
|
115
|
-
// Check if we've already injected for this directory chain
|
|
116
|
-
if (injectedDirs.has(fileDir)) return;
|
|
117
|
-
|
|
118
|
-
// Collect AGENTS.md files
|
|
119
|
-
const agentsFiles = collectAgentsMdFiles(fileDir, projectRoot);
|
|
120
|
-
|
|
121
|
-
// Skip if no AGENTS.md files found
|
|
122
|
-
if (agentsFiles.length === 0) return;
|
|
123
|
-
|
|
124
|
-
// Mark this directory as injected
|
|
125
|
-
injectedDirs.add(fileDir);
|
|
126
|
-
|
|
127
|
-
// Also mark parent directories to avoid redundant injections
|
|
128
|
-
let dir = fileDir;
|
|
129
|
-
while (dir.startsWith(projectRoot) && dir !== projectRoot) {
|
|
130
|
-
injectedDirs.add(dir);
|
|
131
|
-
dir = dirname(dir);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Inject at the beginning of output
|
|
135
|
-
const injection = formatInjection(agentsFiles, projectRoot);
|
|
136
|
-
output.output = injection + output.output;
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
event: async ({ event }) => {
|
|
140
|
-
// Clean up cache when session is deleted
|
|
141
|
-
if (event.type === "session.deleted") {
|
|
142
|
-
const props = event.properties as Record<string, unknown>;
|
|
143
|
-
const sessionId = props?.sessionID as string | undefined;
|
|
144
|
-
if (sessionId) {
|
|
145
|
-
sessionInjectedDirs.delete(sessionId);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
};
|