oc-tweaks 0.1.3 → 0.3.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.
- package/README.md +36 -0
- package/dist/cli/init.js +1 -0
- package/dist/index.js +188 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A collection of runtime enhancement plugins for [OpenCode](https://opencode.ai/)
|
|
|
11
11
|
The currently available plugins are:
|
|
12
12
|
- **`notify`**: Sends desktop notifications when a task is completed or an error occurs.
|
|
13
13
|
- **`compaction`**: Injects a language preference prompt during session compaction to ensure summaries are in your preferred language.
|
|
14
|
+
- **`autoMemory`**: Smart memory assistant that injects memory context, detects memory trigger phrases, and supports active memory writes via `remember` tool and `/remember` command.
|
|
14
15
|
- **`backgroundSubagent`**: Adds a system prompt to encourage using background sub-agents for better responsiveness.
|
|
15
16
|
- **`leaderboard`**: Reports token usage to [claudecount.com](https://claudecount.com) for community leaderboards.
|
|
16
17
|
|
|
@@ -96,6 +97,20 @@ This plugin ensures that when OpenCode compacts a session's context, the resulti
|
|
|
96
97
|
|---|---|---|---|
|
|
97
98
|
| `enabled` | boolean | `true` | Enable or disable the plugin. |
|
|
98
99
|
|
|
100
|
+
### `autoMemory`
|
|
101
|
+
|
|
102
|
+
This plugin provides an intelligent memory workflow:
|
|
103
|
+
- Injects global/project memory context into system prompt.
|
|
104
|
+
- Adds trigger phrases (Chinese + English) so the assistant remembers when to persist key information.
|
|
105
|
+
- Registers a custom `remember` tool for active memory writes.
|
|
106
|
+
- Creates `~/.config/opencode/commands/remember.md` automatically (if missing) so users can run `/remember` explicitly.
|
|
107
|
+
|
|
108
|
+
**Configuration Options:**
|
|
109
|
+
|
|
110
|
+
| Property | Type | Default | Description |
|
|
111
|
+
|---|---|---|---|
|
|
112
|
+
| `enabled` | boolean | `true` | Enable or disable the plugin. |
|
|
113
|
+
|
|
99
114
|
### `backgroundSubagent`
|
|
100
115
|
|
|
101
116
|
This plugin injects a policy into the system prompt, reminding the AI agent to use `run_in_background=true` by default when dispatching sub-agents. It helps maintain a responsive main conversation. If a foreground task is dispatched, a friendly reminder is shown.
|
|
@@ -150,6 +165,9 @@ Here is an example of a `~/.config/opencode/oc-tweaks.json` file with all option
|
|
|
150
165
|
"compaction": {
|
|
151
166
|
"enabled": true
|
|
152
167
|
},
|
|
168
|
+
"autoMemory": {
|
|
169
|
+
"enabled": true
|
|
170
|
+
},
|
|
153
171
|
"backgroundSubagent": {
|
|
154
172
|
"enabled": true
|
|
155
173
|
},
|
|
@@ -179,6 +197,7 @@ Here is an example of a `~/.config/opencode/oc-tweaks.json` file with all option
|
|
|
179
197
|
目前可用的插件包括:
|
|
180
198
|
- **`notify`**: 在任务完成或发生错误时发送桌面通知。
|
|
181
199
|
- **`compaction`**: 在会话上下文压缩期间注入语言偏好提示,以确保摘要使用你的首选语言。
|
|
200
|
+
- **`autoMemory`**: 智能记忆助手——自动注入 memory 上下文、识别触发词,并支持 `remember` tool 与 `/remember` 命令主动写入。
|
|
182
201
|
- **`backgroundSubagent`**: 添加系统提示,鼓励使用后台子代理以获得更好的响应性。
|
|
183
202
|
- **`leaderboard`**: 向 [claudecount.com](https://claudecount.com) 报告 token 用量,用于社区排行榜。
|
|
184
203
|
|
|
@@ -264,6 +283,20 @@ bunx oc-tweaks init
|
|
|
264
283
|
|---|---|---|---|
|
|
265
284
|
| `enabled` | boolean | `true` | 启用或禁用此插件。 |
|
|
266
285
|
|
|
286
|
+
### `autoMemory`
|
|
287
|
+
|
|
288
|
+
该插件提供智能记忆工作流:
|
|
289
|
+
- 自动把全局/项目 memory 上下文注入 system prompt。
|
|
290
|
+
- 内置中英文触发词,命中后优先执行记忆写入。
|
|
291
|
+
- 注册自定义 `remember` tool,支持 AI 主动写入 memory 文件。
|
|
292
|
+
- 自动创建 `~/.config/opencode/commands/remember.md`(若不存在),支持用户显式输入 `/remember`。
|
|
293
|
+
|
|
294
|
+
**配置选项:**
|
|
295
|
+
|
|
296
|
+
| 属性 | 类型 | 默认值 | 描述 |
|
|
297
|
+
|---|---|---|---|
|
|
298
|
+
| `enabled` | boolean | `true` | 启用或禁用此插件。 |
|
|
299
|
+
|
|
267
300
|
### `backgroundSubagent`
|
|
268
301
|
|
|
269
302
|
此插件向系统提示中注入一项策略,提醒 AI 代理在派发子代理时默认使用 `run_in_background=true`。这有助于保持主对话的响应性。如果派发了前台任务,则会显示一个友好的提醒。
|
|
@@ -318,6 +351,9 @@ bunx oc-tweaks init
|
|
|
318
351
|
"compaction": {
|
|
319
352
|
"enabled": true
|
|
320
353
|
},
|
|
354
|
+
"autoMemory": {
|
|
355
|
+
"enabled": true
|
|
356
|
+
},
|
|
321
357
|
"backgroundSubagent": {
|
|
322
358
|
"enabled": true
|
|
323
359
|
},
|
package/dist/cli/init.js
CHANGED
|
@@ -7,6 +7,7 @@ import { dirname } from "path";
|
|
|
7
7
|
var DEFAULT_CONFIG = {
|
|
8
8
|
notify: { enabled: true },
|
|
9
9
|
compaction: { enabled: true },
|
|
10
|
+
autoMemory: { enabled: true },
|
|
10
11
|
backgroundSubagent: { enabled: true },
|
|
11
12
|
leaderboard: { enabled: false },
|
|
12
13
|
logging: { enabled: false, maxLines: 200 }
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/plugins/auto-memory.ts
|
|
2
|
+
import { mkdir as mkdir2, readdir } from "node:fs/promises";
|
|
3
|
+
|
|
1
4
|
// src/utils/logger.ts
|
|
2
5
|
import { mkdir } from "node:fs/promises";
|
|
3
6
|
import { dirname } from "node:path";
|
|
@@ -59,6 +62,7 @@ async function loadJsonConfig(path, defaults) {
|
|
|
59
62
|
}
|
|
60
63
|
var DEFAULT_CONFIG = {
|
|
61
64
|
compaction: {},
|
|
65
|
+
autoMemory: {},
|
|
62
66
|
backgroundSubagent: {},
|
|
63
67
|
leaderboard: {},
|
|
64
68
|
notify: {}
|
|
@@ -76,6 +80,186 @@ async function loadOcTweaksConfig() {
|
|
|
76
80
|
return null;
|
|
77
81
|
}
|
|
78
82
|
}
|
|
83
|
+
// src/plugins/auto-memory.ts
|
|
84
|
+
var TRIGGER_WORDS_CN = ["记住", "保存偏好", "记录一下", "记到memory", "别忘了"];
|
|
85
|
+
var TRIGGER_WORDS_EN = ["remember", "save to memory", "note this down", "don't forget", "record"];
|
|
86
|
+
var REMEMBER_COMMAND_CONTENT = `---
|
|
87
|
+
description: 记忆助手 - 将关键信息写入 memory 文件
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
当用户希望你记住偏好、决策或长期有价值的信息时,
|
|
91
|
+
直接使用 Write 或 Edit 工具操作 memory 文件。
|
|
92
|
+
|
|
93
|
+
## 保存位置
|
|
94
|
+
- 全局 memory:\`~/.config/opencode/memory/\`
|
|
95
|
+
- 项目 memory:\`{project}/.opencode/memory/\`
|
|
96
|
+
|
|
97
|
+
## 保存步骤
|
|
98
|
+
1. 提取要保存的信息(保持原意,不扩写)
|
|
99
|
+
2. 确定文件分类(如 preferences.md、decisions.md、setup.md、notes.md)
|
|
100
|
+
3. 确定 scope(全局 vs 项目级)
|
|
101
|
+
4. 使用 Read 工具检查目标文件是否已存在,读取现有内容
|
|
102
|
+
5. 使用 Edit 工具追加新内容(若文件存在),或用 Write 创建新文件
|
|
103
|
+
|
|
104
|
+
## 格式规范
|
|
105
|
+
- 使用 markdown bullet points
|
|
106
|
+
- 保持简洁,不扩写
|
|
107
|
+
- 不存临时信息(只存跨会话有价值的内容)
|
|
108
|
+
- 不重复 AGENTS.md / CLAUDE.md 中已有的内容
|
|
109
|
+
|
|
110
|
+
如有参数,则优先围绕参数提取重点:$ARGUMENTS
|
|
111
|
+
`;
|
|
112
|
+
function getHome2() {
|
|
113
|
+
return Bun.env?.HOME ?? process.env.HOME ?? "";
|
|
114
|
+
}
|
|
115
|
+
async function listMarkdownFiles(path) {
|
|
116
|
+
try {
|
|
117
|
+
const entries = await readdir(path);
|
|
118
|
+
return entries.filter((item) => item.endsWith(".md")).sort();
|
|
119
|
+
} catch {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function ensureRememberCommand(home) {
|
|
124
|
+
const commandDir = `${home}/.config/opencode/commands`;
|
|
125
|
+
const commandPath = `${commandDir}/remember.md`;
|
|
126
|
+
const commandFile = Bun.file(commandPath);
|
|
127
|
+
if (await commandFile.exists()) {
|
|
128
|
+
try {
|
|
129
|
+
const existing = await commandFile.text();
|
|
130
|
+
if (existing.trim() === REMEMBER_COMMAND_CONTENT.trim())
|
|
131
|
+
return;
|
|
132
|
+
} catch {}
|
|
133
|
+
}
|
|
134
|
+
await mkdir2(commandDir, { recursive: true });
|
|
135
|
+
await Bun.write(commandPath, REMEMBER_COMMAND_CONTENT);
|
|
136
|
+
}
|
|
137
|
+
async function ensureAutoMemoryInfra(home, projectMemoryDir) {
|
|
138
|
+
await mkdir2(`${home}/.config/opencode/memory`, { recursive: true });
|
|
139
|
+
await mkdir2(projectMemoryDir, { recursive: true });
|
|
140
|
+
await ensureRememberCommand(home);
|
|
141
|
+
}
|
|
142
|
+
function buildMemoryGuide(params) {
|
|
143
|
+
const globalList = params.globalFiles.length > 0 ? params.globalFiles.map((name) => `- \`${name}\``).join(`
|
|
144
|
+
`) : "- (暂无全局 memory 文件)";
|
|
145
|
+
const projectList = params.projectFiles.length > 0 ? params.projectFiles.map((name) => `- \`${name}\``).join(`
|
|
146
|
+
`) : "- (暂无项目级 memory 文件)";
|
|
147
|
+
const injectedContents = params.fileContents.size > 0 ? Array.from(params.fileContents.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([path, content]) => `Contents of ${path}:
|
|
148
|
+
${content}`).join(`
|
|
149
|
+
|
|
150
|
+
`) : "(暂无可注入的 memory 内容)";
|
|
151
|
+
return `## \uD83E\uDDE0 Memory 系统指引
|
|
152
|
+
|
|
153
|
+
Memory 是 AGENTS.md / CLAUDE.md 的**补充**,用于存储跨会话有价值的信息。
|
|
154
|
+
不要将 AGENTS.md / CLAUDE.md 中已有的内容重复写入 memory。
|
|
155
|
+
|
|
156
|
+
可用记忆层:
|
|
157
|
+
1. 全局 memory:\`${params.globalMemoryDir}\`
|
|
158
|
+
2. 项目 memory:\`${params.projectMemoryDir}\`
|
|
159
|
+
|
|
160
|
+
### 何时保存 memory
|
|
161
|
+
|
|
162
|
+
**你必须(MUST)保存 memory 当:**
|
|
163
|
+
- 用户明确要求记住(触发词:${TRIGGER_WORDS_CN.join("、")} / ${TRIGGER_WORDS_EN.join(", ")})
|
|
164
|
+
- 用户纠正了你的行为或表达了明确偏好
|
|
165
|
+
|
|
166
|
+
**建议保存 memory 当:**
|
|
167
|
+
- 发现了跨会话有用的模式或约定(想想:如果明天从头开始,这个信息有帮助吗?)
|
|
168
|
+
- 用户描述了目标或背景("我在做..."、"我们在迁移到...")
|
|
169
|
+
- 找到了可能再次出现的问题的解决方案
|
|
170
|
+
- 用户的工作流、工具、沟通风格偏好
|
|
171
|
+
|
|
172
|
+
### 不要保存
|
|
173
|
+
|
|
174
|
+
- 临时的当前任务细节(只在本次对话有用的信息)
|
|
175
|
+
- AGENTS.md 或 CLAUDE.md 中已有的内容(不得重复或矛盾)
|
|
176
|
+
- 可能不完整或未验证的结论(先查证再记录)
|
|
177
|
+
- 机密信息(密码、API key 等)
|
|
178
|
+
|
|
179
|
+
### 如何保存
|
|
180
|
+
|
|
181
|
+
直接使用你的内置 Write 或 Edit 工具操作 memory 文件:
|
|
182
|
+
- 全局 memory:\`${params.globalMemoryDir}/\`
|
|
183
|
+
- 项目 memory:\`${params.projectMemoryDir}/\`
|
|
184
|
+
|
|
185
|
+
文件按主题分类(如 preferences.md、decisions.md、setup.md、notes.md)。
|
|
186
|
+
写入时保持简洁,用 markdown bullet points,保持原意不扩写。
|
|
187
|
+
|
|
188
|
+
### 如何更新已有 memory
|
|
189
|
+
|
|
190
|
+
- 更新已有文件时,使用 Edit 工具追加或修改特定段落,不要用 Write 整体覆盖
|
|
191
|
+
- 内容要具体、信息密集(包含文件路径、函数名、具体命令等)
|
|
192
|
+
- 当某个 memory 文件内容过长时,精简旧条目而不是无限追加
|
|
193
|
+
- 更新时保持已有内容的结构完整,不要破坏其他条目
|
|
194
|
+
|
|
195
|
+
### 当前 Memory 文件
|
|
196
|
+
**全局**
|
|
197
|
+
${globalList}
|
|
198
|
+
|
|
199
|
+
**项目级**
|
|
200
|
+
${projectList}
|
|
201
|
+
|
|
202
|
+
### 用户核心 Preferences
|
|
203
|
+
${injectedContents}`;
|
|
204
|
+
}
|
|
205
|
+
var autoMemoryPlugin = async ({ directory }) => {
|
|
206
|
+
const home = getHome2();
|
|
207
|
+
const globalMemoryDir = `${home}/.config/opencode/memory`;
|
|
208
|
+
const projectMemoryDir = `${directory}/.opencode/memory`;
|
|
209
|
+
try {
|
|
210
|
+
const config = await loadOcTweaksConfig();
|
|
211
|
+
if (config?.autoMemory?.enabled === true) {
|
|
212
|
+
await ensureAutoMemoryInfra(home, projectMemoryDir);
|
|
213
|
+
}
|
|
214
|
+
} catch {}
|
|
215
|
+
return {
|
|
216
|
+
"experimental.chat.system.transform": safeHook("auto-memory:system.transform", async (_input, output) => {
|
|
217
|
+
const config = await loadOcTweaksConfig();
|
|
218
|
+
if (!config || config.autoMemory?.enabled !== true)
|
|
219
|
+
return;
|
|
220
|
+
await ensureAutoMemoryInfra(home, projectMemoryDir);
|
|
221
|
+
const [globalFiles, projectFiles] = await Promise.all([
|
|
222
|
+
listMarkdownFiles(globalMemoryDir),
|
|
223
|
+
listMarkdownFiles(projectMemoryDir)
|
|
224
|
+
]);
|
|
225
|
+
const fileContents = new Map;
|
|
226
|
+
const allPaths = [
|
|
227
|
+
...globalFiles.map((name) => ({ dir: globalMemoryDir, name })),
|
|
228
|
+
...projectFiles.map((name) => ({ dir: projectMemoryDir, name }))
|
|
229
|
+
];
|
|
230
|
+
await Promise.all(allPaths.map(async ({ dir, name }) => {
|
|
231
|
+
try {
|
|
232
|
+
const content = await Bun.file(`${dir}/${name}`).text();
|
|
233
|
+
if (content.trim())
|
|
234
|
+
fileContents.set(`${dir}/${name}`, content.trim());
|
|
235
|
+
} catch {}
|
|
236
|
+
}));
|
|
237
|
+
output.system.push(buildMemoryGuide({
|
|
238
|
+
globalMemoryDir,
|
|
239
|
+
projectMemoryDir,
|
|
240
|
+
globalFiles,
|
|
241
|
+
projectFiles,
|
|
242
|
+
fileContents
|
|
243
|
+
}));
|
|
244
|
+
}),
|
|
245
|
+
"experimental.session.compacting": safeHook("auto-memory:compacting", async (_input, output) => {
|
|
246
|
+
const config = await loadOcTweaksConfig();
|
|
247
|
+
if (!config || config.autoMemory?.enabled !== true)
|
|
248
|
+
return;
|
|
249
|
+
await ensureAutoMemoryInfra(home, projectMemoryDir);
|
|
250
|
+
output.context.push(`## \uD83D\uDCBE Memory 保存提示 (Compaction Phase)
|
|
251
|
+
|
|
252
|
+
如果本轮对话有值得长期保存的信息,请在摘要中标记:
|
|
253
|
+
|
|
254
|
+
\`\`\`
|
|
255
|
+
[MEMORY: 文件名.md]
|
|
256
|
+
这里写要保存的内容
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
后续对话中应根据该标记调用内置 Read/Edit/Write 工具写入对应 memory 文件。`);
|
|
260
|
+
})
|
|
261
|
+
};
|
|
262
|
+
};
|
|
79
263
|
// src/plugins/background-subagent.ts
|
|
80
264
|
var SUB_AGENT_DISPATCH_PROMPT = `
|
|
81
265
|
## Sub-Agent Dispatch Policy
|
|
@@ -141,7 +325,7 @@ var compactionPlugin = async () => {
|
|
|
141
325
|
};
|
|
142
326
|
};
|
|
143
327
|
// src/plugins/leaderboard.ts
|
|
144
|
-
function
|
|
328
|
+
function getHome3() {
|
|
145
329
|
return Bun.env?.HOME ?? (globalThis?.process?.env?.HOME ?? "") ?? "";
|
|
146
330
|
}
|
|
147
331
|
var API_ENDPOINT = "https://api.claudecount.com/api/usage/hook";
|
|
@@ -185,7 +369,7 @@ async function loadLeaderboardConfig(configPath) {
|
|
|
185
369
|
if (typeof configPath === "string") {
|
|
186
370
|
return readLeaderboardConfig(configPath);
|
|
187
371
|
}
|
|
188
|
-
const paths = [`${
|
|
372
|
+
const paths = [`${getHome3()}/.claude/leaderboard.json`, `${getHome3()}/.config/claude/leaderboard.json`];
|
|
189
373
|
for (const path of paths) {
|
|
190
374
|
const config = await readLeaderboardConfig(path);
|
|
191
375
|
if (config)
|
|
@@ -550,5 +734,6 @@ export {
|
|
|
550
734
|
notifyPlugin,
|
|
551
735
|
leaderboardPlugin,
|
|
552
736
|
compactionPlugin,
|
|
553
|
-
backgroundSubagentPlugin
|
|
737
|
+
backgroundSubagentPlugin,
|
|
738
|
+
autoMemoryPlugin
|
|
554
739
|
};
|