@xqli02/mneme 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.
- package/README.md +175 -0
- package/bin/mneme.mjs +275 -0
- package/package.json +31 -0
- package/src/commands/auto.mjs +654 -0
- package/src/commands/compact.mjs +137 -0
- package/src/commands/doctor.mjs +91 -0
- package/src/commands/facts.mjs +148 -0
- package/src/commands/init.mjs +344 -0
- package/src/commands/propose.mjs +150 -0
- package/src/commands/review.mjs +210 -0
- package/src/commands/status.mjs +164 -0
- package/src/opencode-client.mjs +126 -0
- package/src/templates/AGENTS.md +259 -0
- package/src/templates/facts-architecture.md +6 -0
- package/src/templates/facts-invariants.md +6 -0
- package/src/templates/facts-performance_rules.md +5 -0
- package/src/templates/facts-pitfalls.md +5 -0
- package/src/templates/gitignore +23 -0
- package/src/templates/opencode-prompt.md +48 -0
- package/src/utils.mjs +90 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mneme status — Unified dashboard for all three memory layers.
|
|
3
|
+
*
|
|
4
|
+
* Shows:
|
|
5
|
+
* Layer 1 (OpenClaw): Facts files summary (count, total lines)
|
|
6
|
+
* Layer 2 (Beads): Task counts by status, ready tasks
|
|
7
|
+
* Layer 3 (OpenCode): Git working tree state, unpushed commits
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { run, log, color } from "../utils.mjs";
|
|
13
|
+
|
|
14
|
+
// ── Layer 1: OpenClaw ───────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
function showOpenClaw() {
|
|
17
|
+
console.log(color.bold("\n Layer 1 — OpenClaw (Long-term Facts)"));
|
|
18
|
+
console.log(color.dim(" ─────────────────────────────────────"));
|
|
19
|
+
|
|
20
|
+
const factsDir = ".openclaw/facts";
|
|
21
|
+
if (!existsSync(factsDir)) {
|
|
22
|
+
log.warn(" .openclaw/facts/ not found — run `mneme init`");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const files = readdirSync(factsDir).filter((f) => f.endsWith(".md"));
|
|
27
|
+
if (files.length === 0) {
|
|
28
|
+
log.warn(" No facts files found");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let totalLines = 0;
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
const filePath = join(factsDir, file);
|
|
35
|
+
const content = readFileSync(filePath, "utf-8");
|
|
36
|
+
const lines = content.split("\n").length;
|
|
37
|
+
totalLines += lines;
|
|
38
|
+
const name = file.replace(/\.md$/, "");
|
|
39
|
+
console.log(` ${color.green("●")} ${name} ${color.dim(`(${lines} lines)`)}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log(
|
|
43
|
+
color.dim(`\n ${files.length} file(s), ${totalLines} total lines`),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Warn if approaching the 800-line budget
|
|
47
|
+
if (totalLines > 600) {
|
|
48
|
+
log.warn(
|
|
49
|
+
` Facts approaching size budget (${totalLines}/800 lines) — consider pruning`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Layer 2: Beads ──────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
function showBeads() {
|
|
57
|
+
console.log(color.bold("\n Layer 2 — Beads (Task State)"));
|
|
58
|
+
console.log(color.dim(" ────────────────────────────"));
|
|
59
|
+
|
|
60
|
+
if (!existsSync(".beads/config.yaml")) {
|
|
61
|
+
log.warn(" .beads/ not initialized — run `mneme init`");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Get counts by status
|
|
66
|
+
const openOut = run("bd list --status=open 2>&1");
|
|
67
|
+
const ipOut = run("bd list --status=in_progress 2>&1");
|
|
68
|
+
const blockedOut = run("bd blocked 2>&1");
|
|
69
|
+
const readyOut = run("bd ready 2>&1");
|
|
70
|
+
|
|
71
|
+
const countLines = (output) => {
|
|
72
|
+
if (!output || output.includes("No issues") || output.includes("Error")) return 0;
|
|
73
|
+
// Count lines that look like bead entries (start with ○, ◐, ●, ✓, or ❄, or contain mneme-/bd-)
|
|
74
|
+
return output
|
|
75
|
+
.split("\n")
|
|
76
|
+
.filter((l) => /^[○◐●✓❄]/.test(l.trim()) || /\b(mneme|bd)-[a-z0-9]+\b/.test(l))
|
|
77
|
+
.length;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const openCount = countLines(openOut);
|
|
81
|
+
const ipCount = countLines(ipOut);
|
|
82
|
+
const blockedCount = countLines(blockedOut);
|
|
83
|
+
const readyCount = countLines(readyOut);
|
|
84
|
+
|
|
85
|
+
console.log(` ${color.green("○")} Open: ${openCount}`);
|
|
86
|
+
console.log(` ${color.yellow("◐")} In progress: ${ipCount}`);
|
|
87
|
+
console.log(` ${color.red("●")} Blocked: ${blockedCount}`);
|
|
88
|
+
console.log(` ${color.blue("▶")} Ready: ${readyCount}`);
|
|
89
|
+
|
|
90
|
+
// Show ready tasks
|
|
91
|
+
if (readyOut && !readyOut.includes("No issues") && readyCount > 0) {
|
|
92
|
+
console.log(color.dim("\n Ready to work on:"));
|
|
93
|
+
const lines = readyOut.split("\n").filter((l) => /^[○◐]/.test(l.trim()));
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
console.log(` ${line.trim()}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Layer 3: OpenCode (Session Context) ─────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
function showOpenCode() {
|
|
103
|
+
console.log(color.bold("\n Layer 3 — OpenCode (Session Context)"));
|
|
104
|
+
console.log(color.dim(" ─────────────────────────────────────"));
|
|
105
|
+
|
|
106
|
+
if (!existsSync(".git")) {
|
|
107
|
+
log.warn(" Not a git repository");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Working tree status
|
|
112
|
+
const status = run("git status --porcelain 2>&1");
|
|
113
|
+
if (status === null) {
|
|
114
|
+
log.warn(" Could not read git status");
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (status === "") {
|
|
119
|
+
console.log(` ${color.green("●")} Working tree clean`);
|
|
120
|
+
} else {
|
|
121
|
+
const lines = status.split("\n").filter(Boolean);
|
|
122
|
+
const modified = lines.filter((l) => l.startsWith(" M") || l.startsWith("M ")).length;
|
|
123
|
+
const added = lines.filter((l) => l.startsWith("A ") || l.startsWith("??")).length;
|
|
124
|
+
const deleted = lines.filter((l) => l.startsWith(" D") || l.startsWith("D ")).length;
|
|
125
|
+
const staged = lines.filter((l) => /^[MADRC]/.test(l)).length;
|
|
126
|
+
|
|
127
|
+
const parts = [];
|
|
128
|
+
if (modified > 0) parts.push(`${modified} modified`);
|
|
129
|
+
if (added > 0) parts.push(`${added} new`);
|
|
130
|
+
if (deleted > 0) parts.push(`${deleted} deleted`);
|
|
131
|
+
if (staged > 0) parts.push(`${staged} staged`);
|
|
132
|
+
|
|
133
|
+
console.log(` ${color.yellow("●")} ${parts.join(", ")}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Unpushed commits
|
|
137
|
+
const unpushed = run("git log @{u}..HEAD --oneline 2>&1");
|
|
138
|
+
if (unpushed === null || unpushed.includes("no upstream")) {
|
|
139
|
+
console.log(` ${color.dim("●")} No remote tracking branch`);
|
|
140
|
+
} else if (unpushed === "") {
|
|
141
|
+
console.log(` ${color.green("●")} Up to date with remote`);
|
|
142
|
+
} else {
|
|
143
|
+
const count = unpushed.split("\n").filter(Boolean).length;
|
|
144
|
+
console.log(` ${color.yellow("●")} ${count} unpushed commit(s)`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Current branch
|
|
148
|
+
const branch = run("git branch --show-current 2>&1");
|
|
149
|
+
if (branch) {
|
|
150
|
+
console.log(` ${color.dim("●")} Branch: ${branch}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ── Main ────────────────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
export async function status() {
|
|
157
|
+
console.log(`\n${color.bold("mneme status")} — three-layer memory dashboard`);
|
|
158
|
+
|
|
159
|
+
showOpenClaw();
|
|
160
|
+
showBeads();
|
|
161
|
+
showOpenCode();
|
|
162
|
+
|
|
163
|
+
console.log();
|
|
164
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode HTTP client — zero-dependency wrapper around opencode serve API.
|
|
3
|
+
*
|
|
4
|
+
* Uses native fetch (Node 18+) and SSE parsing for event streams.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create an OpenCode HTTP client connected to a running server.
|
|
9
|
+
* @param {{ baseUrl: string }} opts
|
|
10
|
+
*/
|
|
11
|
+
export function createClient(baseUrl) {
|
|
12
|
+
const base = baseUrl.replace(/\/$/, "");
|
|
13
|
+
|
|
14
|
+
async function request(method, path, body) {
|
|
15
|
+
const url = `${base}${path}`;
|
|
16
|
+
const opts = {
|
|
17
|
+
method,
|
|
18
|
+
headers: { "Content-Type": "application/json" },
|
|
19
|
+
};
|
|
20
|
+
if (body !== undefined) {
|
|
21
|
+
opts.body = JSON.stringify(body);
|
|
22
|
+
}
|
|
23
|
+
const res = await fetch(url, opts);
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const text = await res.text().catch(() => "");
|
|
26
|
+
throw new Error(`OpenCode API ${method} ${path}: ${res.status} ${text}`);
|
|
27
|
+
}
|
|
28
|
+
// 204 No Content
|
|
29
|
+
if (res.status === 204) return null;
|
|
30
|
+
const contentType = res.headers.get("content-type") || "";
|
|
31
|
+
if (contentType.includes("application/json")) {
|
|
32
|
+
return res.json();
|
|
33
|
+
}
|
|
34
|
+
return res.text();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Subscribe to SSE event stream.
|
|
39
|
+
* Returns an async iterator of parsed events: { type, properties }.
|
|
40
|
+
*/
|
|
41
|
+
async function* subscribeEvents(path = "/event") {
|
|
42
|
+
const url = `${base}${path}`;
|
|
43
|
+
const res = await fetch(url, {
|
|
44
|
+
headers: { Accept: "text/event-stream" },
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
throw new Error(`SSE connect failed: ${res.status}`);
|
|
48
|
+
}
|
|
49
|
+
const reader = res.body.getReader();
|
|
50
|
+
const decoder = new TextDecoder();
|
|
51
|
+
let buffer = "";
|
|
52
|
+
let currentEvent = {};
|
|
53
|
+
|
|
54
|
+
while (true) {
|
|
55
|
+
const { done, value } = await reader.read();
|
|
56
|
+
if (done) break;
|
|
57
|
+
buffer += decoder.decode(value, { stream: true });
|
|
58
|
+
|
|
59
|
+
const lines = buffer.split("\n");
|
|
60
|
+
buffer = lines.pop(); // keep incomplete line
|
|
61
|
+
|
|
62
|
+
for (const line of lines) {
|
|
63
|
+
if (line.startsWith("event:")) {
|
|
64
|
+
currentEvent.type = line.slice(6).trim();
|
|
65
|
+
} else if (line.startsWith("data:")) {
|
|
66
|
+
const data = line.slice(5).trim();
|
|
67
|
+
try {
|
|
68
|
+
currentEvent.properties = JSON.parse(data);
|
|
69
|
+
} catch {
|
|
70
|
+
currentEvent.properties = data;
|
|
71
|
+
}
|
|
72
|
+
} else if (line === "") {
|
|
73
|
+
// Empty line = end of event
|
|
74
|
+
if (currentEvent.type || currentEvent.properties) {
|
|
75
|
+
yield currentEvent;
|
|
76
|
+
currentEvent = {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
/** Health check */
|
|
85
|
+
health: () => request("GET", "/global/health"),
|
|
86
|
+
|
|
87
|
+
/** Session management */
|
|
88
|
+
session: {
|
|
89
|
+
list: () => request("GET", "/session"),
|
|
90
|
+
create: (body) => request("POST", "/session", body),
|
|
91
|
+
get: (id) => request("GET", `/session/${id}`),
|
|
92
|
+
delete: (id) => request("DELETE", `/session/${id}`),
|
|
93
|
+
abort: (id) => request("POST", `/session/${id}/abort`),
|
|
94
|
+
messages: (id) => request("GET", `/session/${id}/message`),
|
|
95
|
+
status: () => request("GET", "/session/status"),
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Send a prompt (synchronous — waits for full response).
|
|
99
|
+
* @param {string} id - session ID
|
|
100
|
+
* @param {object} body - { parts: [{ type: "text", text: "..." }], model?, agent? }
|
|
101
|
+
*/
|
|
102
|
+
prompt: (id, body) => request("POST", `/session/${id}/message`, body),
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Send a prompt asynchronously (returns immediately, 204).
|
|
106
|
+
* Listen to events for progress.
|
|
107
|
+
*/
|
|
108
|
+
promptAsync: (id, body) =>
|
|
109
|
+
request("POST", `/session/${id}/prompt_async`, body),
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/** Config */
|
|
113
|
+
config: {
|
|
114
|
+
get: () => request("GET", "/config"),
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/** Events */
|
|
118
|
+
events: {
|
|
119
|
+
subscribe: () => subscribeEvents("/event"),
|
|
120
|
+
subscribeGlobal: () => subscribeEvents("/global/event"),
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
/** Raw request for anything else */
|
|
124
|
+
request,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# AGENTS.md — OpenCode Agent 行为规则
|
|
2
|
+
|
|
3
|
+
## 身份与定位
|
|
4
|
+
|
|
5
|
+
你是一个长期工程项目中的执行 agent。你**不承担长期记忆**,也**不承担任务管理**。你的记忆和任务状态分别由 OpenClaw 和 Beads 管理。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Session 启动流程(必须严格遵守)
|
|
10
|
+
|
|
11
|
+
每个新 session 开始时,按以下顺序执行:
|
|
12
|
+
|
|
13
|
+
1. **读取 OpenClaw facts(长期事实)**
|
|
14
|
+
- 阅读 `.openclaw/facts/` 下的所有文件
|
|
15
|
+
- 这些内容优先级**高于**你的推理和对话历史
|
|
16
|
+
- 若发现 facts 与当前情况矛盾,**提出质疑**而非直接修改
|
|
17
|
+
|
|
18
|
+
2. **读取 Beads 当前任务列表**
|
|
19
|
+
- 执行 `mneme ready` 查看当前可执行的任务
|
|
20
|
+
- 执行 `mneme list --status=open` 查看所有未完成任务
|
|
21
|
+
- 了解当前所有未完成任务及其状态和依赖关系
|
|
22
|
+
|
|
23
|
+
3. **选择一个 bead 作为本 session 的 focus**
|
|
24
|
+
- 只选一个,不要同时推进多个不相关任务
|
|
25
|
+
- 优先从 `mneme ready` 的结果中选择(无阻塞依赖的 open 状态 bead)
|
|
26
|
+
|
|
27
|
+
4. **开始执行**
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 执行过程中的规则
|
|
32
|
+
|
|
33
|
+
### Beads 管理
|
|
34
|
+
|
|
35
|
+
- 开始工作前先 claim 任务:`mneme update <id> --status=in_progress`
|
|
36
|
+
- 完成一个阶段性目标后,更新对应 bead 的 `notes`:`mneme update <id> --notes="进度说明"`
|
|
37
|
+
- **不要依赖对话历史记住进度**,所有进度必须写入 Beads
|
|
38
|
+
- 新发现的子任务应创建为新的 bead:`mneme create --title="..." --description="..." --type=task -p 2`
|
|
39
|
+
- 关联发现的工作:`mneme dep add <new-id> <parent-id>`
|
|
40
|
+
- **WARNING**: 禁止使用 `bd edit`,它会打开交互式编辑器。始终使用 `mneme update` 加参数
|
|
41
|
+
|
|
42
|
+
### OpenClaw 管理
|
|
43
|
+
|
|
44
|
+
- 在执行过程中若发现新的长期事实(架构决策、红线、陷阱等),**提议**写入 OpenClaw
|
|
45
|
+
- 提议需明确说明:
|
|
46
|
+
- 写入哪个 facts 文件
|
|
47
|
+
- 具体内容
|
|
48
|
+
- 为什么这是一个"长期事实"而非临时结论
|
|
49
|
+
- **等待人工确认后才能写入**
|
|
50
|
+
|
|
51
|
+
### 代码操作
|
|
52
|
+
|
|
53
|
+
- 只负责代码分析、文件修改、命令执行等即时操作
|
|
54
|
+
- 所有操作应服务于当前 focus bead 的目标
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 三层路由决策(自动分类)
|
|
59
|
+
|
|
60
|
+
在工作过程中,你会不断产生新的信息。**你必须主动判断每条信息属于哪一层**,而不是等待用户指示。以下是决策逻辑:
|
|
61
|
+
|
|
62
|
+
### 决策树
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
新信息产生
|
|
66
|
+
│
|
|
67
|
+
├─ 问:"6 个月后这条信息还有用吗?"
|
|
68
|
+
│ │
|
|
69
|
+
│ ├─ YES → 问:"这是一个事实/约束/教训,还是一个待办/进度?"
|
|
70
|
+
│ │ │
|
|
71
|
+
│ │ ├─ 事实/约束/教训 → **OpenClaw**(提议写入,等待确认)
|
|
72
|
+
│ │ │
|
|
73
|
+
│ │ └─ 待办/进度 → **Beads**(直接写入)
|
|
74
|
+
│ │
|
|
75
|
+
│ └─ NO → 问:"下个 session 需要这条信息吗?"
|
|
76
|
+
│ │
|
|
77
|
+
│ ├─ YES → **Beads**(写入 notes 或创建新 bead)
|
|
78
|
+
│ │
|
|
79
|
+
│ └─ NO → **OpenCode**(留在当前上下文,不持久化)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 分类示例
|
|
83
|
+
|
|
84
|
+
| 信息 | 分类 | 动作 |
|
|
85
|
+
|----------------------------------------------|-----------|------------------------------------------------|
|
|
86
|
+
| "这个项目用 Dolt 作为数据库后端" | OpenClaw | 提议写入 `facts/architecture.md` |
|
|
87
|
+
| "禁止在生产环境直接修改 Dolt 数据" | OpenClaw | 提议写入 `facts/invariants.md` |
|
|
88
|
+
| "`bd edit` 会打开交互式编辑器导致 agent 卡住"| OpenClaw | 提议写入 `facts/pitfalls.md` |
|
|
89
|
+
| "需要实现用户认证模块" | Beads | `mneme create --title="实现用户认证" ...` |
|
|
90
|
+
| "认证模块完成了 JWT 签发部分,还差验证" | Beads | `mneme update <id> --notes="JWT 签发完成..."` |
|
|
91
|
+
| "这个函数的第 3 个参数是 timeout" | OpenCode | 不持久化,仅在当前操作中使用 |
|
|
92
|
+
| "正在对比两种实现方案的 trade-off" | OpenCode | 不持久化,除非最终选择成为架构决策 |
|
|
93
|
+
|
|
94
|
+
### 自动路由规则
|
|
95
|
+
|
|
96
|
+
1. **架构决策** → 一旦做出并确认,提议写入 `facts/architecture.md`
|
|
97
|
+
2. **红线/约束** → 发现不可违反的规则时,提议写入 `facts/invariants.md`
|
|
98
|
+
3. **踩坑经验** → 遇到非显而易见的陷阱时,提议写入 `facts/pitfalls.md`
|
|
99
|
+
4. **性能基准** → 发现关键性能约束时,提议写入 `facts/performance_rules.md`
|
|
100
|
+
5. **新任务/子任务** → 发现需要做的工作时,`mneme create` 创建 bead
|
|
101
|
+
6. **进度更新** → 完成阶段性目标时,`mneme update --notes` 记录
|
|
102
|
+
7. **临时分析** → 代码分析、调试过程、方案对比 → 留在 OpenCode,不持久化
|
|
103
|
+
|
|
104
|
+
### 写入 OpenClaw 的门槛检查
|
|
105
|
+
|
|
106
|
+
在提议写入 OpenClaw 前,必须通过以下所有检查:
|
|
107
|
+
|
|
108
|
+
- [ ] 这条信息已被验证(不是假设或推测)
|
|
109
|
+
- [ ] 这条信息在未来 session 中会被反复需要(不是一次性的)
|
|
110
|
+
- [ ] 这条信息不会快速过时(不是某个临时状态)
|
|
111
|
+
- [ ] 现有 facts 文件中没有等价信息(避免重复)
|
|
112
|
+
|
|
113
|
+
只有全部通过才提议写入。提议后仍需等待人工确认。
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Compaction 前的固定动作
|
|
118
|
+
|
|
119
|
+
当感知到上下文即将过长,或在完成阶段性目标后,执行以下步骤:
|
|
120
|
+
|
|
121
|
+
1. **持久化已确认的结论**
|
|
122
|
+
- 将本 session 中已确认的结论更新到对应 bead:`mneme update <id> --notes="结论"`
|
|
123
|
+
|
|
124
|
+
2. **提议新的长期事实**
|
|
125
|
+
- 若发现应加入 OpenClaw 的内容,提出提议
|
|
126
|
+
|
|
127
|
+
3. **更新 bead 状态**
|
|
128
|
+
- 若当前 bead 已完成,关闭它:`mneme close <id> --reason="完成说明"`
|
|
129
|
+
- 若未完成,确保 `notes` 中记录了当前进度和卡点
|
|
130
|
+
|
|
131
|
+
4. **允许 compaction**
|
|
132
|
+
|
|
133
|
+
> 原则:**可以丢失推理过程,但不能丢失状态与事实**
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 禁止行为
|
|
138
|
+
|
|
139
|
+
- 禁止跳过 session 启动流程直接开始工作
|
|
140
|
+
- 禁止从对话历史恢复任务进度
|
|
141
|
+
- 禁止单方面修改或删除 OpenClaw facts
|
|
142
|
+
- 禁止在一个 session 中同时推进多个不相关的 bead
|
|
143
|
+
- 禁止将未验证的假设写入 OpenClaw
|
|
144
|
+
- 禁止创建模糊的、无法验证完成与否的 bead
|
|
145
|
+
- 禁止使用 `bd edit`(会打开交互式编辑器)
|
|
146
|
+
|
|
147
|
+
<!-- BEGIN BEADS INTEGRATION -->
|
|
148
|
+
## Issue Tracking with mneme (beads)
|
|
149
|
+
|
|
150
|
+
**IMPORTANT**: This project uses **mneme** (backed by bd/beads) for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods.
|
|
151
|
+
|
|
152
|
+
### Why beads?
|
|
153
|
+
|
|
154
|
+
- Dolt-Powered: Version-controlled SQL database with cell-level merge and native branching
|
|
155
|
+
- Dependency-aware: Track blockers and relationships between issues
|
|
156
|
+
- Agent-optimized: JSON output (`--json`), ready work detection, hash-based IDs
|
|
157
|
+
- Zero conflict: Hash-based IDs (`bd-a1b2`) prevent merge collisions
|
|
158
|
+
- Compaction: Semantic "memory decay" summarizes old closed tasks
|
|
159
|
+
|
|
160
|
+
### Essential Commands
|
|
161
|
+
|
|
162
|
+
**Finding work:**
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
mneme ready # Show issues with no open blockers
|
|
166
|
+
mneme list --status=open # All open issues
|
|
167
|
+
mneme list --status=in_progress # Your active work
|
|
168
|
+
mneme show <id> # Detailed issue view with dependencies
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Creating issues:**
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
mneme create --title="Issue title" --description="Context" --type=task -p 2
|
|
175
|
+
mneme create --title="Bug found" --description="Details" --type=bug -p 1
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Claiming and updating:**
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
mneme update <id> --status=in_progress # Claim work
|
|
182
|
+
mneme update <id> --notes="Progress notes" # Add notes
|
|
183
|
+
mneme update <id> --title="New title" # Update title
|
|
184
|
+
mneme update <id> --description="Updated" # Update description
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Dependencies:**
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
mneme dep add <child> <parent> # child depends on parent
|
|
191
|
+
mneme blocked # Show all blocked issues
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Completing work:**
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
mneme close <id> --reason "Done" # Close single issue
|
|
198
|
+
mneme close <id1> <id2> ... # Close multiple at once
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Issue Types
|
|
202
|
+
|
|
203
|
+
- `bug` - Something broken
|
|
204
|
+
- `feature` - New functionality
|
|
205
|
+
- `task` - Work item (tests, docs, refactoring)
|
|
206
|
+
- `epic` - Large feature with subtasks
|
|
207
|
+
- `chore` - Maintenance (dependencies, tooling)
|
|
208
|
+
|
|
209
|
+
### Priorities
|
|
210
|
+
|
|
211
|
+
- `0` / `P0` - Critical (security, data loss, broken builds)
|
|
212
|
+
- `1` / `P1` - High (major features, important bugs)
|
|
213
|
+
- `2` / `P2` - Medium (default)
|
|
214
|
+
- `3` / `P3` - Low (polish, optimization)
|
|
215
|
+
- `4` / `P4` - Backlog (future ideas)
|
|
216
|
+
|
|
217
|
+
### Workflow for AI Agents
|
|
218
|
+
|
|
219
|
+
1. **Check ready work**: `mneme ready` shows unblocked issues
|
|
220
|
+
2. **Claim your task**: `mneme update <id> --status=in_progress`
|
|
221
|
+
3. **Work on it**: Implement, test, document
|
|
222
|
+
4. **Discover new work?** Create linked issue with `mneme create` + `mneme dep add`
|
|
223
|
+
5. **Complete**: `mneme close <id> --reason "Done"`
|
|
224
|
+
|
|
225
|
+
### Important Rules
|
|
226
|
+
|
|
227
|
+
- Use mneme for ALL task tracking
|
|
228
|
+
- Always use `--json` flag for programmatic use
|
|
229
|
+
- Check `mneme ready` before asking "what should I work on?"
|
|
230
|
+
- Do NOT use `bd edit` (opens interactive editor)
|
|
231
|
+
- Do NOT create markdown TODO lists for task tracking
|
|
232
|
+
- Do NOT use external issue trackers
|
|
233
|
+
|
|
234
|
+
<!-- END BEADS INTEGRATION -->
|
|
235
|
+
|
|
236
|
+
## Landing the Plane (Session Completion)
|
|
237
|
+
|
|
238
|
+
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
|
239
|
+
|
|
240
|
+
**MANDATORY WORKFLOW:**
|
|
241
|
+
|
|
242
|
+
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
|
243
|
+
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
|
244
|
+
3. **Update issue status** - Close finished work, update in-progress items
|
|
245
|
+
4. **PUSH TO REMOTE** - This is MANDATORY:
|
|
246
|
+
```bash
|
|
247
|
+
git pull --rebase
|
|
248
|
+
git push
|
|
249
|
+
git status # MUST show "up to date with origin"
|
|
250
|
+
```
|
|
251
|
+
5. **Clean up** - Clear stashes, prune remote branches
|
|
252
|
+
6. **Verify** - All changes committed AND pushed
|
|
253
|
+
7. **Hand off** - Provide context for next session
|
|
254
|
+
|
|
255
|
+
**CRITICAL RULES:**
|
|
256
|
+
- Work is NOT complete until `git push` succeeds
|
|
257
|
+
- NEVER stop before pushing - that leaves work stranded locally
|
|
258
|
+
- NEVER say "ready to push when you are" - YOU must push
|
|
259
|
+
- If push fails, resolve and retry until it succeeds
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# OS
|
|
2
|
+
.DS_Store
|
|
3
|
+
Thumbs.db
|
|
4
|
+
|
|
5
|
+
# Dolt server
|
|
6
|
+
.dolt/
|
|
7
|
+
|
|
8
|
+
# Beads internal state (managed by bd, not hand-edited)
|
|
9
|
+
.beads/dolt/
|
|
10
|
+
.beads/last-touched
|
|
11
|
+
.beads/interactions.jsonl
|
|
12
|
+
.beads/.local_version
|
|
13
|
+
|
|
14
|
+
# Editor
|
|
15
|
+
*.swp
|
|
16
|
+
*.swo
|
|
17
|
+
*~
|
|
18
|
+
.idea/
|
|
19
|
+
.vscode/
|
|
20
|
+
|
|
21
|
+
# Environment
|
|
22
|
+
.env
|
|
23
|
+
.env.*
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
这是一个长期工程项目,请严格按以下顺序建立上下文:
|
|
2
|
+
|
|
3
|
+
## 第一步:读取 OpenClaw facts(长期事实)
|
|
4
|
+
|
|
5
|
+
请完整阅读以下文件:
|
|
6
|
+
- .openclaw/facts/architecture.md
|
|
7
|
+
- .openclaw/facts/invariants.md
|
|
8
|
+
- .openclaw/facts/performance_rules.md
|
|
9
|
+
- .openclaw/facts/pitfalls.md
|
|
10
|
+
|
|
11
|
+
这些内容是长期事实:
|
|
12
|
+
- 优先级高于对话历史和你的推理
|
|
13
|
+
- 不要随意推翻
|
|
14
|
+
- 若发现矛盾,请提出而不是修改
|
|
15
|
+
|
|
16
|
+
## 第二步:读取 Beads 当前任务列表
|
|
17
|
+
|
|
18
|
+
使用 `mneme` 命令查看当前任务状态:
|
|
19
|
+
- `mneme ready` — 查看可执行任务(无阻塞依赖)
|
|
20
|
+
- `mneme list --status=open` — 查看所有未完成任务
|
|
21
|
+
- `mneme show <id>` — 查看具体任务详情
|
|
22
|
+
|
|
23
|
+
## 第三步:选择 focus
|
|
24
|
+
|
|
25
|
+
- 只选择一个 bead 作为本 session 的目标
|
|
26
|
+
- 优先从 `mneme ready` 结果中选择
|
|
27
|
+
- 选定后 claim 它:`mneme update <id> --status=in_progress`
|
|
28
|
+
- 不要试图从对话历史中恢复状态
|
|
29
|
+
|
|
30
|
+
## 信息路由(自动执行,不需要用户指示)
|
|
31
|
+
|
|
32
|
+
在工作过程中你会产生新信息。**你必须自动判断它属于哪一层**:
|
|
33
|
+
|
|
34
|
+
- **6 个月后还有用?** + 是事实/约束/教训 → **提议写入 OpenClaw**(需人工确认)
|
|
35
|
+
- **6 个月后还有用?** + 是待办/进度 → **写入 Beads**(`mneme create` 或 `mneme update --notes`)
|
|
36
|
+
- **下个 session 需要?** → **写入 Beads**
|
|
37
|
+
- **仅当前操作需要?** → 留在 OpenCode,不持久化
|
|
38
|
+
|
|
39
|
+
写入 OpenClaw 前的检查:已验证 + 反复需要 + 不会快速过时 + 无重复。详见 AGENTS.md "三层路由决策" 一节。
|
|
40
|
+
|
|
41
|
+
## 重要原则
|
|
42
|
+
|
|
43
|
+
- 不要跳过上述步骤直接开始工作
|
|
44
|
+
- 完成阶段性目标后更新 Beads:`mneme update <id> --notes="进度"`
|
|
45
|
+
- 完成任务后关闭:`mneme close <id> --reason="完成说明"`
|
|
46
|
+
- 发现新的长期事实时提议写入 OpenClaw(需人工确认)
|
|
47
|
+
- Compaction 前必须持久化状态与结论
|
|
48
|
+
- 禁止使用 `bd edit`(会打开交互式编辑器),使用 `mneme update` 代替
|