oc-tweaks 0.1.0

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.
@@ -0,0 +1,318 @@
1
+ // @ts-nocheck
2
+
3
+ import { afterEach, describe, expect, test } from "bun:test"
4
+
5
+ import { notifyPlugin } from "../plugins/notify"
6
+
7
+ const originalBunFile = Bun.file
8
+ const originalHome = Bun.env?.HOME
9
+
10
+ function mockBunFile(mockData: Record<string, any>) {
11
+ ;(globalThis as any).Bun.file = (path: string) => ({
12
+ exists: async () => path in mockData,
13
+ json: async () => {
14
+ if (!(path in mockData)) throw new Error("ENOENT")
15
+ const data = mockData[path]
16
+ if (data instanceof Error) throw data
17
+ return data
18
+ },
19
+ text: async () => JSON.stringify(mockData[path] ?? ""),
20
+ })
21
+ }
22
+
23
+ function createShellMock(options?: {
24
+ availableCommands?: string[]
25
+ throwOnExecution?: boolean
26
+ }) {
27
+ const available = new Set(options?.availableCommands ?? [])
28
+ const calls: Array<{ command: string; values: any[] }> = []
29
+
30
+ const $ = async (strings: TemplateStringsArray, ...values: any[]) => {
31
+ const segments = Array.from(strings)
32
+ const command = segments.reduce(
33
+ (acc, segment, index) =>
34
+ acc + segment + (index < values.length ? String(values[index]) : ""),
35
+ "",
36
+ )
37
+
38
+ calls.push({ command, values })
39
+
40
+ if (command.startsWith("which ")) {
41
+ const bin = String(values[0] ?? command.slice("which ".length).trim())
42
+ if (available.has(bin)) return { stdout: `${bin}\n` }
43
+ throw new Error(`missing ${bin}`)
44
+ }
45
+
46
+ if (options?.throwOnExecution) {
47
+ throw new Error("execution failed")
48
+ }
49
+
50
+ return { stdout: "" }
51
+ }
52
+
53
+ return { $, calls }
54
+ }
55
+
56
+
57
+ afterEach(() => {
58
+ ;(globalThis as any).Bun.file = originalBunFile
59
+ if (originalHome === undefined) {
60
+ delete (Bun.env as any).HOME
61
+ } else {
62
+ ;(Bun.env as any).HOME = originalHome
63
+ }
64
+ })
65
+
66
+ describe("notifyPlugin", () => {
67
+ test("returns empty hooks when notify.enabled is false", async () => {
68
+ const home = "/tmp/oc-notify-disabled"
69
+ ;(Bun.env as any).HOME = home
70
+ const path = `${home}/.config/opencode/oc-tweaks.json`
71
+ mockBunFile({
72
+ [path]: {
73
+ notify: { enabled: false },
74
+ },
75
+ })
76
+
77
+ const { $ } = createShellMock({ availableCommands: ["notify-send"] })
78
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client: {} })
79
+
80
+ expect(hooks).toEqual({})
81
+ })
82
+
83
+ test("auto-detects notifier once and caches detection result", async () => {
84
+ const home = "/tmp/oc-notify-detect"
85
+ ;(Bun.env as any).HOME = home
86
+ const path = `${home}/.config/opencode/oc-tweaks.json`
87
+ mockBunFile({ [path]: { notify: { enabled: true } } })
88
+
89
+ const { $, calls } = createShellMock({ availableCommands: ["notify-send"] })
90
+ const client = {
91
+ session: {
92
+ messages: async () => ({
93
+ data: [
94
+ { info: { role: "user" }, parts: [{ type: "text", text: "irrelevant" }] },
95
+ { info: { role: "assistant" }, parts: [{ type: "text", text: "**Done** now" }] },
96
+ ],
97
+ }),
98
+ },
99
+ }
100
+
101
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client })
102
+ await hooks.event({ event: { type: "session.idle", properties: { sessionID: "s1" } } })
103
+ await hooks.event({ event: { type: "session.error", properties: {} } })
104
+
105
+ const whichCalls = calls.filter((entry) => entry.command.startsWith("which "))
106
+ const notifyCalls = calls.filter((entry) => entry.command.startsWith("notify-send "))
107
+
108
+ expect(whichCalls.length).toBe(4)
109
+ expect(notifyCalls.length).toBe(2)
110
+ expect(notifyCalls[0].command).toContain("notify-send oc: demo")
111
+ expect(notifyCalls[0].command).toContain("✓ Done now")
112
+ })
113
+
114
+ test("uses custom command and replaces $TITLE/$MESSAGE placeholders", async () => {
115
+ const home = "/tmp/oc-notify-custom"
116
+ ;(Bun.env as any).HOME = home
117
+ const path = `${home}/.config/opencode/oc-tweaks.json`
118
+ mockBunFile({
119
+ [path]: {
120
+ notify: {
121
+ enabled: true,
122
+ command: "custom-bin --title $TITLE --message $MESSAGE",
123
+ },
124
+ },
125
+ })
126
+
127
+ const { $, calls } = createShellMock()
128
+ const client = {
129
+ session: {
130
+ messages: async () => ({
131
+ data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Hello *world*" }] }],
132
+ }),
133
+ },
134
+ }
135
+
136
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client })
137
+ await hooks.event({ event: { type: "session.idle", properties: { sessionID: "s2" } } })
138
+
139
+ const whichCalls = calls.filter((entry) => entry.command.startsWith("which "))
140
+ const executeCalls = calls.filter((entry) => !entry.command.startsWith("which "))
141
+
142
+ expect(whichCalls.length).toBe(0)
143
+ expect(executeCalls.length).toBe(1)
144
+ expect(executeCalls[0].command).toContain("custom-bin --title oc: demo --message ✓ Hello world")
145
+ expect(executeCalls[0].command).not.toContain("$TITLE")
146
+ expect(executeCalls[0].command).not.toContain("$MESSAGE")
147
+ })
148
+
149
+ test("does not notify when notifyOnIdle/notifyOnError are false", async () => {
150
+ const home = "/tmp/oc-notify-switches"
151
+ ;(Bun.env as any).HOME = home
152
+ const path = `${home}/.config/opencode/oc-tweaks.json`
153
+ mockBunFile({
154
+ [path]: {
155
+ notify: {
156
+ enabled: true,
157
+ notifyOnIdle: false,
158
+ notifyOnError: false,
159
+ },
160
+ },
161
+ })
162
+
163
+ const { $, calls } = createShellMock({ availableCommands: ["notify-send"] })
164
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client: {} })
165
+
166
+ await hooks.event({ event: { type: "session.idle", properties: { sessionID: "s3" } } })
167
+ await hooks.event({ event: { type: "session.error", properties: {} } })
168
+
169
+ const notifyCalls = calls.filter((entry) => !entry.command.startsWith("which "))
170
+ expect(notifyCalls.length).toBe(0)
171
+ })
172
+
173
+ test("falls back to client.tui.showToast when no shell notifier exists", async () => {
174
+ const home = "/tmp/oc-notify-tui"
175
+ ;(Bun.env as any).HOME = home
176
+ const path = `${home}/.config/opencode/oc-tweaks.json`
177
+ mockBunFile({ [path]: { notify: { enabled: true } } })
178
+
179
+ const { $, calls } = createShellMock()
180
+ const toastCalls: any[] = []
181
+ const client = {
182
+ tui: {
183
+ showToast: (...args: any[]) => {
184
+ toastCalls.push(args)
185
+ },
186
+ },
187
+ }
188
+
189
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client })
190
+ await hooks.event({ event: { type: "session.error", properties: {} } })
191
+
192
+ const whichCalls = calls.filter((entry) => entry.command.startsWith("which "))
193
+ expect(whichCalls.length).toBe(4)
194
+ expect(toastCalls.length).toBe(1)
195
+ })
196
+
197
+ test("degrades silently when execution fails and does not throw", async () => {
198
+ const home = "/tmp/oc-notify-non-blocking"
199
+ ;(Bun.env as any).HOME = home
200
+ const path = `${home}/.config/opencode/oc-tweaks.json`
201
+ mockBunFile({ [path]: { notify: { enabled: true } } })
202
+
203
+ const { $ } = createShellMock({
204
+ availableCommands: ["notify-send"],
205
+ throwOnExecution: true,
206
+ })
207
+
208
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client: {} })
209
+
210
+ await expect(
211
+ hooks.event({ event: { type: "session.error", properties: {} } }),
212
+ ).resolves.toBeUndefined()
213
+ })
214
+
215
+ test("keeps silent when no notifier is available", async () => {
216
+ const home = "/tmp/oc-notify-none"
217
+ ;(Bun.env as any).HOME = home
218
+ const path = `${home}/.config/opencode/oc-tweaks.json`
219
+ mockBunFile({ [path]: { notify: { enabled: true } } })
220
+
221
+ const { $ } = createShellMock()
222
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client: {} })
223
+
224
+ await expect(
225
+ hooks.event({ event: { type: "session.error", properties: {} } }),
226
+ ).resolves.toBeUndefined()
227
+ })
228
+ })
229
+
230
+ test("detects pwsh and uses wpf sender", async () => {
231
+ const home = "/tmp/oc-notify-wpf-detect"
232
+ ;(Bun.env as any).HOME = home
233
+ const path = `${home}/.config/opencode/oc-tweaks.json`
234
+ mockBunFile({ [path]: { notify: { enabled: true } } })
235
+
236
+ const { $, calls } = createShellMock({ availableCommands: ["pwsh"] })
237
+ const client = {
238
+ session: {
239
+ messages: async () => ({
240
+ data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Task done" }] }],
241
+ }),
242
+ },
243
+ }
244
+
245
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client })
246
+ await hooks.event({ event: { type: "session.idle", properties: { sessionID: "s4" } } })
247
+
248
+ const pwshCalls = calls.filter((entry) => entry.command.includes("pwsh"))
249
+ expect(pwshCalls.length).toBeGreaterThan(0)
250
+
251
+ const bun_e_calls = calls.filter((entry) => entry.command.includes("bun -e"))
252
+ expect(bun_e_calls.length).toBeGreaterThan(0)
253
+
254
+ const jsCode = String(bun_e_calls[0].values[0] ?? "")
255
+ expect(jsCode).toContain("PresentationFramework")
256
+ expect(jsCode).toContain("ShowActivated")
257
+ })
258
+
259
+ test("wpf notification script contains WS_EX_NOACTIVATE for non-focus behavior", async () => {
260
+ const home = "/tmp/oc-notify-wpf-noactivate"
261
+ ;(Bun.env as any).HOME = home
262
+ const path = `${home}/.config/opencode/oc-tweaks.json`
263
+ mockBunFile({ [path]: { notify: { enabled: true } } })
264
+
265
+ const { $, calls } = createShellMock({ availableCommands: ["pwsh"] })
266
+ const client = {
267
+ session: {
268
+ messages: async () => ({
269
+ data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Finished" }] }],
270
+ }),
271
+ },
272
+ }
273
+
274
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client })
275
+ await hooks.event({ event: { type: "session.idle", properties: { sessionID: "s5" } } })
276
+
277
+ const bun_e_calls = calls.filter((entry) => entry.command.includes("bun -e"))
278
+ expect(bun_e_calls.length).toBeGreaterThan(0)
279
+
280
+ const jsCode = String(bun_e_calls[0].values[0] ?? "")
281
+ expect(jsCode).toMatch(/WS_EX_NOACTIVATE|0x08000000|MakeGlobalWindow/)
282
+ })
283
+
284
+ test("wpf notification uses custom style from config", async () => {
285
+ const home = "/tmp/oc-notify-wpf-style"
286
+ ;(Bun.env as any).HOME = home
287
+ const path = `${home}/.config/opencode/oc-tweaks.json`
288
+ mockBunFile({
289
+ [path]: {
290
+ notify: {
291
+ enabled: true,
292
+ style: {
293
+ backgroundColor: "#FF0000",
294
+ duration: 3000,
295
+ },
296
+ },
297
+ },
298
+ })
299
+
300
+ const { $, calls } = createShellMock({ availableCommands: ["pwsh"] })
301
+ const client = {
302
+ session: {
303
+ messages: async () => ({
304
+ data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Complete" }] }],
305
+ }),
306
+ },
307
+ }
308
+
309
+ const hooks = await notifyPlugin({ $, directory: "/tmp/demo", client })
310
+ await hooks.event({ event: { type: "session.idle", properties: { sessionID: "s6" } } })
311
+
312
+ const bun_e_calls = calls.filter((entry) => entry.command.includes("bun -e"))
313
+ expect(bun_e_calls.length).toBeGreaterThan(0)
314
+
315
+ const jsCode = String(bun_e_calls[0].values[0] ?? "")
316
+ expect(jsCode).toContain("#FF0000")
317
+ expect(jsCode).toContain("3000")
318
+ })
@@ -0,0 +1,164 @@
1
+ // @ts-nocheck
2
+
3
+ import { describe, test, expect, beforeEach, afterEach } from "bun:test"
4
+
5
+ import { safeHook } from "../utils/safe-hook"
6
+ import { loadJsonConfig, loadOcTweaksConfig } from "../utils/config"
7
+
8
+ const originalBunFile = Bun.file
9
+ const originalHome = Bun.env?.HOME
10
+
11
+ function mockBunFile(mockData: Record<string, any>) {
12
+ ;(globalThis as any).Bun.file = (path: string) => ({
13
+ exists: async () => path in mockData,
14
+ json: async () => {
15
+ if (!(path in mockData)) throw new Error("ENOENT")
16
+ const data = mockData[path]
17
+ if (data instanceof Error) throw data
18
+ return data
19
+ },
20
+ text: async () => JSON.stringify(mockData[path] ?? ""),
21
+ })
22
+ }
23
+
24
+ afterEach(() => {
25
+ ;(globalThis as any).Bun.file = originalBunFile
26
+ if (originalHome === undefined) {
27
+ delete (Bun.env as any).HOME
28
+ } else {
29
+ ;(Bun.env as any).HOME = originalHome
30
+ }
31
+ })
32
+
33
+ describe("safeHook", () => {
34
+ function createWarnSpy() {
35
+ const calls: any[] = []
36
+ const originalWarn = console.warn
37
+ console.warn = (...args: any[]) => {
38
+ calls.push(args)
39
+ }
40
+ return {
41
+ calls,
42
+ restore: () => {
43
+ console.warn = originalWarn
44
+ },
45
+ }
46
+ }
47
+
48
+ let warnSpy: ReturnType<typeof createWarnSpy>
49
+
50
+ beforeEach(() => {
51
+ warnSpy = createWarnSpy()
52
+ })
53
+
54
+ afterEach(() => {
55
+ warnSpy.restore()
56
+ })
57
+
58
+ test("returns original function result", async () => {
59
+ const wrapped = safeHook("add", (value: number) => value + 1)
60
+ const result = await Promise.resolve(wrapped(1))
61
+
62
+ expect(result).toBe(2)
63
+ expect(warnSpy.calls.length).toBe(0)
64
+ })
65
+
66
+ test("swallows errors silently (now uses logger, not console.warn)", async () => {
67
+ const error = new Error("boom")
68
+ const wrapped = safeHook("fail", () => {
69
+ throw error
70
+ })
71
+
72
+ // Should not throw, and should not call console.warn (logger is used instead)
73
+ await expect(Promise.resolve(wrapped())).resolves.toBeUndefined()
74
+ expect(warnSpy.calls.length).toBe(0)
75
+ })
76
+
77
+ test("works with async functions", async () => {
78
+ const wrapped = safeHook("async", async (value: number) => value * 2)
79
+ const result = await wrapped(3)
80
+
81
+ expect(result).toBe(6)
82
+ expect(warnSpy.calls.length).toBe(0)
83
+ })
84
+ })
85
+
86
+ describe("loadJsonConfig", () => {
87
+ test("returns defaults when file is missing", async () => {
88
+ mockBunFile({})
89
+ const defaults = { a: 1, b: true }
90
+
91
+ const result = await loadJsonConfig("/tmp/missing.json", defaults)
92
+
93
+ expect(result).toEqual(defaults)
94
+ })
95
+
96
+ test("merges defaults with parsed config", async () => {
97
+ const path = "/tmp/config.json"
98
+ mockBunFile({
99
+ [path]: {
100
+ b: false,
101
+ c: "extra",
102
+ },
103
+ })
104
+ const defaults = { a: 1, b: true }
105
+
106
+ const result = await loadJsonConfig(path, defaults)
107
+
108
+ expect(result).toEqual({ a: 1, b: false, c: "extra" })
109
+ })
110
+
111
+ test("returns defaults when json parsing fails", async () => {
112
+ const path = "/tmp/bad.json"
113
+ mockBunFile({
114
+ [path]: new Error("invalid json"),
115
+ })
116
+ const defaults = { enabled: true }
117
+
118
+ const result = await loadJsonConfig(path, defaults)
119
+
120
+ expect(result).toEqual(defaults)
121
+ })
122
+ })
123
+
124
+ describe("loadOcTweaksConfig", () => {
125
+ test("loads config from default path", async () => {
126
+ const home = "/tmp/oc-home"
127
+ ;(Bun.env as any).HOME = home
128
+ const path = `${home}/.config/opencode/oc-tweaks.json`
129
+
130
+ mockBunFile({
131
+ [path]: {
132
+ notify: {
133
+ enabled: false,
134
+ notifyOnIdle: true,
135
+ },
136
+ },
137
+ })
138
+
139
+ const result = await loadOcTweaksConfig()
140
+
141
+ expect(result.notify.enabled).toBe(false)
142
+ expect(result.notify.notifyOnIdle).toBe(true)
143
+ expect(result.compaction).toEqual({})
144
+ })
145
+
146
+ test("string \"false\" is not treated as disabled", async () => {
147
+ const home = "/tmp/oc-home-2"
148
+ ;(Bun.env as any).HOME = home
149
+ const path = `${home}/.config/opencode/oc-tweaks.json`
150
+
151
+ mockBunFile({
152
+ [path]: {
153
+ notify: {
154
+ enabled: "false",
155
+ },
156
+ },
157
+ })
158
+
159
+ const result = await loadOcTweaksConfig()
160
+
161
+ expect(result.notify.enabled).toBe("false")
162
+ expect(result.notify.enabled === false).toBe(false)
163
+ })
164
+ })
@@ -0,0 +1,12 @@
1
+ /// <reference types="bun-types" />
2
+
3
+ declare const Bun: any
4
+
5
+ declare module "bun:test" {
6
+ export const describe: (...args: any[]) => any
7
+ export const test: (...args: any[]) => any
8
+ export const expect: any
9
+ export const mock: any
10
+ export const beforeEach: any
11
+ export const afterEach: any
12
+ }
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { mkdir } from "node:fs/promises"
4
+ import { dirname } from "node:path"
5
+ declare const Bun: any
6
+
7
+ export const DEFAULT_CONFIG = {
8
+ notify: { enabled: true },
9
+ compaction: { enabled: true },
10
+ backgroundSubagent: { enabled: true },
11
+ leaderboard: { enabled: false },
12
+ logging: { enabled: false, maxLines: 200 }
13
+ }
14
+
15
+ export async function initConfig(): Promise<{ created: boolean; path: string }> {
16
+ const home = Bun.env?.HOME ?? ((globalThis as any)?.process?.env?.HOME ?? "") ?? ""
17
+ const configPath = `${home}/.config/opencode/oc-tweaks.json`
18
+
19
+ const file = Bun.file(configPath)
20
+ if (await file.exists()) {
21
+ return { created: false, path: configPath }
22
+ }
23
+
24
+ const json = JSON.stringify(DEFAULT_CONFIG, null, 2)
25
+ await mkdir(dirname(configPath), { recursive: true })
26
+ await Bun.write(configPath, json + "\n")
27
+
28
+ return { created: true, path: configPath }
29
+ }
30
+
31
+ // Only run when executed directly
32
+ const isMain = typeof Bun !== "undefined" &&
33
+ Bun.main === import.meta.path
34
+
35
+ if (isMain) {
36
+ const result = await initConfig()
37
+ if (result.created) {
38
+ console.log(`Created: ${result.path}`)
39
+ console.log("All plugins configured. Edit the file to customize.")
40
+ } else {
41
+ console.log(`Config already exists: ${result.path}`)
42
+ console.log("Nothing changed. Edit the file manually to update your configuration.")
43
+ }
44
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { backgroundSubagentPlugin } from "./plugins/background-subagent"
2
+ export { compactionPlugin } from "./plugins/compaction"
3
+ export { leaderboardPlugin } from "./plugins/leaderboard"
4
+ export { notifyPlugin } from "./plugins/notify"
File without changes
@@ -0,0 +1,59 @@
1
+ import type { Plugin } from "@opencode-ai/plugin"
2
+
3
+ import { loadOcTweaksConfig, safeHook } from "../utils"
4
+
5
+ const SUB_AGENT_DISPATCH_PROMPT = `
6
+ ## Sub-Agent Dispatch Policy
7
+
8
+ When calling \`task()\` to dispatch sub-agents, you should default to \`run_in_background=true\`.
9
+ This keeps the main conversation responsive while sub-agents work in the background.
10
+
11
+ Only use \`run_in_background=false\` when ALL of these conditions are met:
12
+ 1. The next step cannot easily proceed without the sub-agent's result
13
+ 2. There is NO other useful work to do while waiting
14
+ 3. The user is explicitly waiting for that specific result
15
+
16
+ When in doubt → background. Use \`background_output()\` to collect results later.
17
+ `
18
+
19
+ const VIOLATION_WARNING = `💡 [Reminder] Consider using background mode for better responsiveness.
20
+ You used foreground mode (run_in_background=false). Check the three conditions in the system prompt.
21
+ If not all three are met, consider run_in_background=true + background_output() for next time.`
22
+
23
+ export const backgroundSubagentPlugin: Plugin = async () => {
24
+ const config = await loadOcTweaksConfig()
25
+ if (!config || config.backgroundSubagent?.enabled !== true) return {}
26
+
27
+ const foregroundCalls = new Set<string>()
28
+
29
+ return {
30
+ "experimental.chat.system.transform": safeHook(
31
+ "background-subagent:system.transform",
32
+ async (_input: unknown, output: { system: string[] }) => {
33
+ output.system.push(SUB_AGENT_DISPATCH_PROMPT)
34
+ },
35
+ ),
36
+
37
+ "tool.execute.before": safeHook(
38
+ "background-subagent:tool.execute.before",
39
+ async (
40
+ input: { tool: string; callID: string },
41
+ output: { args?: { run_in_background?: boolean } },
42
+ ) => {
43
+ if (input.tool !== "task") return
44
+ if (!output.args?.run_in_background) {
45
+ foregroundCalls.add(input.callID)
46
+ }
47
+ },
48
+ ),
49
+
50
+ "tool.execute.after": safeHook(
51
+ "background-subagent:tool.execute.after",
52
+ async (input: { callID: string }, output: { output: string }) => {
53
+ if (!foregroundCalls.has(input.callID)) return
54
+ foregroundCalls.delete(input.callID)
55
+ output.output += `\n\n${VIOLATION_WARNING}`
56
+ },
57
+ ),
58
+ }
59
+ }
@@ -0,0 +1,28 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+
3
+ import { loadOcTweaksConfig, safeHook } from "../utils";
4
+
5
+ const LANGUAGE_PREFERENCE_PROMPT = `
6
+ ## Language Preference
7
+
8
+ Important: Write the compaction summary in the user's preferred language(for example, if user prefers Chinese, then the compaction should be in Chinese as well).
9
+ All section titles, descriptions, analysis, and next-step suggestions should use the user's language.
10
+ Keep technical terms (filenames, variable names, commands, code snippets) in their original form.
11
+ `;
12
+
13
+ export const compactionPlugin: Plugin = async () => {
14
+ const config = await loadOcTweaksConfig();
15
+ if (!config || config.compaction?.enabled !== true) return {};
16
+
17
+ return {
18
+ "experimental.session.compacting": safeHook(
19
+ "compaction",
20
+ async (
21
+ _input: { sessionID: string },
22
+ output: { context: string[]; prompt?: string },
23
+ ) => {
24
+ output.context.push(LANGUAGE_PREFERENCE_PROMPT);
25
+ },
26
+ ),
27
+ };
28
+ };