opencodekit 0.12.2 → 0.12.4
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 +1 -1
- package/dist/template/.opencode/AGENTS.md +40 -417
- package/dist/template/.opencode/agent/build.md +119 -9
- package/dist/template/.opencode/agent/planner.md +0 -1
- package/dist/template/.opencode/agent/rush.md +81 -19
- package/dist/template/.opencode/command/accessibility-check.md +1 -1
- package/dist/template/.opencode/command/commit.md +1 -1
- package/dist/template/.opencode/command/create.md +68 -441
- package/dist/template/.opencode/command/finish.md +82 -252
- package/dist/template/.opencode/command/fix-ci.md +52 -247
- package/dist/template/.opencode/command/fix-types.md +32 -292
- package/dist/template/.opencode/command/fix-ui.md +49 -234
- package/dist/template/.opencode/command/fix.md +57 -194
- package/dist/template/.opencode/command/handoff.md +66 -243
- package/dist/template/.opencode/command/implement.md +67 -231
- package/dist/template/.opencode/command/issue.md +42 -190
- package/dist/template/.opencode/command/plan.md +86 -442
- package/dist/template/.opencode/command/pr.md +3 -1
- package/dist/template/.opencode/command/research-and-implement.md +69 -370
- package/dist/template/.opencode/command/research.md +72 -197
- package/dist/template/.opencode/command/resume.md +70 -438
- package/dist/template/.opencode/command/status.md +11 -11
- package/dist/template/.opencode/command/triage.md +23 -18
- package/dist/template/.opencode/memory/project/commands.md +139 -7
- package/dist/template/.opencode/memory/project/gotchas.md +85 -0
- package/dist/template/.opencode/opencode.json +556 -510
- package/dist/template/.opencode/plugin/beads.ts +181 -16
- package/dist/template/.opencode/skill/beads/SKILL.md +15 -0
- package/dist/template/.opencode/skill/context-engineering/SKILL.md +94 -0
- package/dist/template/.opencode/skill/memory-system/SKILL.md +107 -0
- package/dist/template/.opencode/skill/session-management/SKILL.md +111 -0
- package/dist/template/.opencode/skill/tool-priority/SKILL.md +115 -0
- package/package.json +1 -1
|
@@ -366,10 +366,12 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
366
366
|
filePath: string,
|
|
367
367
|
reason?: string,
|
|
368
368
|
ttlSeconds = 600,
|
|
369
|
+
overrideAgent?: string,
|
|
369
370
|
): Promise<{ acquired: boolean; holder?: string }> {
|
|
370
371
|
const lockPath = lockDir(filePath);
|
|
371
372
|
const now = Date.now();
|
|
372
373
|
const expires = now + ttlSeconds * 1000;
|
|
374
|
+
const agentToUse = overrideAgent || state.agentId;
|
|
373
375
|
|
|
374
376
|
// Atomic: mkdir fails if dir exists
|
|
375
377
|
try {
|
|
@@ -384,10 +386,10 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
384
386
|
if (lock.expires < now) {
|
|
385
387
|
// Expired - remove and retry
|
|
386
388
|
await $`rm -rf ${lockPath}`.cwd(directory).quiet();
|
|
387
|
-
return acquireLock(filePath, reason, ttlSeconds);
|
|
389
|
+
return acquireLock(filePath, reason, ttlSeconds, overrideAgent);
|
|
388
390
|
}
|
|
389
391
|
|
|
390
|
-
if (lock.agent ===
|
|
392
|
+
if (lock.agent === agentToUse) {
|
|
391
393
|
// We already hold this lock - refresh it
|
|
392
394
|
lock.expires = expires;
|
|
393
395
|
await $`echo ${JSON.stringify(lock)} > ${metaPath}`
|
|
@@ -400,14 +402,14 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
400
402
|
} catch {
|
|
401
403
|
// Corrupted lock - remove and retry
|
|
402
404
|
await $`rm -rf ${lockPath}`.cwd(directory).quiet();
|
|
403
|
-
return acquireLock(filePath, reason, ttlSeconds);
|
|
405
|
+
return acquireLock(filePath, reason, ttlSeconds, overrideAgent);
|
|
404
406
|
}
|
|
405
407
|
}
|
|
406
408
|
|
|
407
409
|
// Lock acquired - write metadata
|
|
408
410
|
const lockData: LockData = {
|
|
409
411
|
path: filePath,
|
|
410
|
-
agent:
|
|
412
|
+
agent: agentToUse,
|
|
411
413
|
reason,
|
|
412
414
|
created: now,
|
|
413
415
|
expires,
|
|
@@ -597,7 +599,7 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
597
599
|
role: tool.schema
|
|
598
600
|
.string()
|
|
599
601
|
.optional()
|
|
600
|
-
.describe("Role:
|
|
602
|
+
.describe("Role: build|rush|explore|planner|review|scout|vision"),
|
|
601
603
|
},
|
|
602
604
|
async execute(args) {
|
|
603
605
|
await $`bd init`
|
|
@@ -670,11 +672,17 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
670
672
|
.default("completed")
|
|
671
673
|
.describe("Completion message"),
|
|
672
674
|
},
|
|
673
|
-
async execute(args) {
|
|
675
|
+
async execute(args, context) {
|
|
674
676
|
const taskId = args.id || state.currentTask;
|
|
675
677
|
if (!taskId) return json({ error: "No task ID" });
|
|
676
678
|
|
|
677
|
-
|
|
679
|
+
// Audit trail
|
|
680
|
+
const auditAgent = context?.agent || state.agentId;
|
|
681
|
+
|
|
682
|
+
const closeResult = await bdClose(
|
|
683
|
+
taskId,
|
|
684
|
+
`${args.msg} [by ${auditAgent}]`,
|
|
685
|
+
);
|
|
678
686
|
if (closeResult.error) return json({ error: closeResult.error });
|
|
679
687
|
|
|
680
688
|
// Release all locks
|
|
@@ -708,7 +716,9 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
708
716
|
tags: tool.schema
|
|
709
717
|
.array(tool.schema.string())
|
|
710
718
|
.optional()
|
|
711
|
-
.describe(
|
|
719
|
+
.describe(
|
|
720
|
+
"Agent tags: build,rush,explore,planner,review,scout,vision",
|
|
721
|
+
),
|
|
712
722
|
parent: tool.schema.string().optional().describe("Parent issue ID"),
|
|
713
723
|
deps: tool.schema
|
|
714
724
|
.array(tool.schema.string())
|
|
@@ -754,7 +764,9 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
754
764
|
"Assign task to role (leader only). Adds tag and notifies.",
|
|
755
765
|
args: {
|
|
756
766
|
id: tool.schema.string().describe("Issue ID"),
|
|
757
|
-
role: tool.schema
|
|
767
|
+
role: tool.schema
|
|
768
|
+
.string()
|
|
769
|
+
.describe("Role: build|rush|explore|planner|review|scout|vision"),
|
|
758
770
|
notify: tool.schema
|
|
759
771
|
.boolean()
|
|
760
772
|
.default(true)
|
|
@@ -882,16 +894,24 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
882
894
|
.default(600)
|
|
883
895
|
.describe("Seconds until expiry"),
|
|
884
896
|
},
|
|
885
|
-
async execute(args) {
|
|
897
|
+
async execute(args, context) {
|
|
886
898
|
if (!args.paths?.length) return json({ error: "paths required" });
|
|
887
899
|
|
|
900
|
+
// Log who is reserving for audit
|
|
901
|
+
const reservingAgent = context?.agent || state.agentId;
|
|
902
|
+
|
|
888
903
|
await ensureReservationsDir();
|
|
889
904
|
|
|
890
905
|
const granted: string[] = [];
|
|
891
906
|
const conflicts: { path: string; holder?: string }[] = [];
|
|
892
907
|
|
|
893
908
|
for (const path of args.paths) {
|
|
894
|
-
const result = await acquireLock(
|
|
909
|
+
const result = await acquireLock(
|
|
910
|
+
path,
|
|
911
|
+
args.reason,
|
|
912
|
+
args.ttl,
|
|
913
|
+
reservingAgent,
|
|
914
|
+
);
|
|
895
915
|
if (result.acquired) {
|
|
896
916
|
granted.push(path);
|
|
897
917
|
} else {
|
|
@@ -963,12 +983,15 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
963
983
|
.default(false)
|
|
964
984
|
.describe("Send to all workspaces"),
|
|
965
985
|
},
|
|
966
|
-
async execute(args) {
|
|
986
|
+
async execute(args, context) {
|
|
967
987
|
if (!args.subj) return json({ error: "subj required" });
|
|
968
988
|
|
|
989
|
+
// Use context agent if available for audit trail
|
|
990
|
+
const senderAgent = context?.agent || state.agentId;
|
|
991
|
+
|
|
969
992
|
const msg: Message = {
|
|
970
993
|
id: `msg-${Date.now().toString(36)}`,
|
|
971
|
-
from:
|
|
994
|
+
from: senderAgent,
|
|
972
995
|
to: args.to || "all",
|
|
973
996
|
subj: args.subj,
|
|
974
997
|
body: args.body,
|
|
@@ -1000,6 +1023,117 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
1000
1023
|
},
|
|
1001
1024
|
}),
|
|
1002
1025
|
|
|
1026
|
+
bd_ack: tool({
|
|
1027
|
+
description: "Acknowledge message(s). Marks as read.",
|
|
1028
|
+
args: {
|
|
1029
|
+
ids: tool.schema
|
|
1030
|
+
.array(tool.schema.string())
|
|
1031
|
+
.describe("Message IDs to acknowledge"),
|
|
1032
|
+
},
|
|
1033
|
+
async execute(args) {
|
|
1034
|
+
if (!args.ids?.length) return json({ error: "ids required" });
|
|
1035
|
+
|
|
1036
|
+
try {
|
|
1037
|
+
const content =
|
|
1038
|
+
await $`cat ${RESERVATIONS_DIR}/messages.jsonl 2>/dev/null`
|
|
1039
|
+
.cwd(directory)
|
|
1040
|
+
.text();
|
|
1041
|
+
|
|
1042
|
+
if (!content.trim()) return json({ acked: 0 });
|
|
1043
|
+
|
|
1044
|
+
const idsToAck = new Set(args.ids);
|
|
1045
|
+
let acked = 0;
|
|
1046
|
+
|
|
1047
|
+
const msgs = content
|
|
1048
|
+
.trim()
|
|
1049
|
+
.split("\n")
|
|
1050
|
+
.map((line) => {
|
|
1051
|
+
try {
|
|
1052
|
+
const msg = JSON.parse(line) as Message;
|
|
1053
|
+
if (idsToAck.has(msg.id) && !msg.read) {
|
|
1054
|
+
msg.read = true;
|
|
1055
|
+
acked++;
|
|
1056
|
+
}
|
|
1057
|
+
return msg;
|
|
1058
|
+
} catch {
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
})
|
|
1062
|
+
.filter((m): m is Message => m !== null);
|
|
1063
|
+
|
|
1064
|
+
const newContent = msgs.map((m) => JSON.stringify(m)).join("\n");
|
|
1065
|
+
await $`echo ${newContent} > ${RESERVATIONS_DIR}/messages.jsonl`
|
|
1066
|
+
.cwd(directory)
|
|
1067
|
+
.quiet();
|
|
1068
|
+
|
|
1069
|
+
return json({ ok: 1, acked });
|
|
1070
|
+
} catch {
|
|
1071
|
+
return json({ acked: 0 });
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
}),
|
|
1075
|
+
|
|
1076
|
+
bd_whois: tool({
|
|
1077
|
+
description: "Agent directory lookup. See who's working on what.",
|
|
1078
|
+
args: {
|
|
1079
|
+
agent: tool.schema
|
|
1080
|
+
.string()
|
|
1081
|
+
.optional()
|
|
1082
|
+
.describe("Agent ID to lookup (empty=all)"),
|
|
1083
|
+
},
|
|
1084
|
+
async execute(args) {
|
|
1085
|
+
const locks = await getAllLocks();
|
|
1086
|
+
const { tasks: inProgressTasks } = await bdList({
|
|
1087
|
+
status: "in_progress",
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
// Build agent activity map
|
|
1091
|
+
const agentMap: Record<
|
|
1092
|
+
string,
|
|
1093
|
+
{
|
|
1094
|
+
files: string[];
|
|
1095
|
+
task?: string;
|
|
1096
|
+
role?: string;
|
|
1097
|
+
}
|
|
1098
|
+
> = {};
|
|
1099
|
+
|
|
1100
|
+
// Add locks info
|
|
1101
|
+
for (const lock of locks) {
|
|
1102
|
+
if (!agentMap[lock.agent]) {
|
|
1103
|
+
agentMap[lock.agent] = { files: [] };
|
|
1104
|
+
}
|
|
1105
|
+
agentMap[lock.agent].files.push(lock.path);
|
|
1106
|
+
if (lock.task) {
|
|
1107
|
+
agentMap[lock.agent].task = lock.task;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// Add current agent info
|
|
1112
|
+
if (!agentMap[state.agentId]) {
|
|
1113
|
+
agentMap[state.agentId] = { files: [] };
|
|
1114
|
+
}
|
|
1115
|
+
agentMap[state.agentId].role = state.role || undefined;
|
|
1116
|
+
if (state.currentTask) {
|
|
1117
|
+
agentMap[state.agentId].task = state.currentTask;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// Filter if specific agent requested
|
|
1121
|
+
if (args.agent) {
|
|
1122
|
+
const agent = agentMap[args.agent];
|
|
1123
|
+
if (!agent) return json({ error: "agent not found" });
|
|
1124
|
+
return json({ agent: args.agent, ...agent });
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
return json({
|
|
1128
|
+
agents: Object.entries(agentMap).map(([id, info]) => ({
|
|
1129
|
+
id,
|
|
1130
|
+
...info,
|
|
1131
|
+
})),
|
|
1132
|
+
in_progress_tasks: inProgressTasks.length,
|
|
1133
|
+
});
|
|
1134
|
+
},
|
|
1135
|
+
}),
|
|
1136
|
+
|
|
1003
1137
|
bd_status: tool({
|
|
1004
1138
|
description: "Workspace overview. Shows agents, tasks, locks.",
|
|
1005
1139
|
args: {
|
|
@@ -1038,10 +1172,22 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
1038
1172
|
|
|
1039
1173
|
bd_sync: tool({
|
|
1040
1174
|
description: "Sync with git. Pull/push changes.",
|
|
1041
|
-
args: {
|
|
1042
|
-
|
|
1175
|
+
args: {
|
|
1176
|
+
reason: tool.schema
|
|
1177
|
+
.string()
|
|
1178
|
+
.optional()
|
|
1179
|
+
.describe("Audit trail reason for sync"),
|
|
1180
|
+
},
|
|
1181
|
+
async execute(args, context) {
|
|
1043
1182
|
const result = await bdSync();
|
|
1044
|
-
|
|
1183
|
+
// Log sync with context for audit trail
|
|
1184
|
+
const syncAgent = context?.agent || state.agentId;
|
|
1185
|
+
return json({
|
|
1186
|
+
ok: 1,
|
|
1187
|
+
output: result.output,
|
|
1188
|
+
by: syncAgent,
|
|
1189
|
+
reason: args.reason,
|
|
1190
|
+
});
|
|
1045
1191
|
},
|
|
1046
1192
|
}),
|
|
1047
1193
|
|
|
@@ -1249,6 +1395,25 @@ export const BeadsCorePlugin: Plugin = async ({ $, directory }) => {
|
|
|
1249
1395
|
if (event.type === "session.idle" && state.currentTask) {
|
|
1250
1396
|
await bdSync();
|
|
1251
1397
|
}
|
|
1398
|
+
|
|
1399
|
+
// Cleanup expired locks on compaction
|
|
1400
|
+
if (event.type === "session.compacted") {
|
|
1401
|
+
await cleanupExpiredLocks();
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// Log errors to audit trail
|
|
1405
|
+
if (event.type === "session.error" && state.currentTask) {
|
|
1406
|
+
await appendMessage({
|
|
1407
|
+
id: `err-${Date.now().toString(36)}`,
|
|
1408
|
+
from: state.agentId,
|
|
1409
|
+
to: "all",
|
|
1410
|
+
subj: `Session error on task ${state.currentTask}`,
|
|
1411
|
+
body: `Error occurred during task execution`,
|
|
1412
|
+
importance: "high",
|
|
1413
|
+
at: Date.now(),
|
|
1414
|
+
read: false,
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1252
1417
|
},
|
|
1253
1418
|
};
|
|
1254
1419
|
};
|
|
@@ -340,6 +340,21 @@ bd_msg({
|
|
|
340
340
|
bd_inbox({ unread: true, n: 10 });
|
|
341
341
|
```
|
|
342
342
|
|
|
343
|
+
### Acknowledge Messages
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
bd_ack({ ids: ["msg-abc", "msg-def"] }); // Mark as read
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Agent Coordination
|
|
350
|
+
|
|
351
|
+
### Who's Working on What
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
bd_whois(); // All agents with their files and tasks
|
|
355
|
+
bd_whois({ agent: "build-abc" }); // Specific agent lookup
|
|
356
|
+
```
|
|
357
|
+
|
|
343
358
|
## Status and Analysis
|
|
344
359
|
|
|
345
360
|
### Workspace Overview
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use when managing context window, deciding what to load/prune, or understanding AI adoption stages - covers constraint awareness and intent layer principles
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Context Engineering Skill
|
|
6
|
+
|
|
7
|
+
## AI Adoption Stages
|
|
8
|
+
|
|
9
|
+
OpenCode operates at **Stage 5-6**:
|
|
10
|
+
|
|
11
|
+
- **Stage 5** (Agentic Verification): Agents run tests and iterate autonomously
|
|
12
|
+
- **Stage 6** (Multi-Agent Orchestration): Parallel workstreams with coordination
|
|
13
|
+
|
|
14
|
+
**Current constraint**: Planning and specification quality. Implementation capacity is not the bottleneck—how well you specify requirements is.
|
|
15
|
+
|
|
16
|
+
## Autonomous Duration
|
|
17
|
+
|
|
18
|
+
The key metric: **How long can an agent work before losing the plot?**
|
|
19
|
+
|
|
20
|
+
Extend autonomous duration by:
|
|
21
|
+
|
|
22
|
+
- Binding tighter to intent (clear specs, constraints, invariants)
|
|
23
|
+
- Providing systematic context (AGENTS.md hierarchy, memory files)
|
|
24
|
+
- Verification loops (test → iterate → verify)
|
|
25
|
+
|
|
26
|
+
## Greenfield vs Legacy
|
|
27
|
+
|
|
28
|
+
| Type | Context | Agent Performance |
|
|
29
|
+
| -------------- | -------------------------- | ----------------------------- |
|
|
30
|
+
| **Greenfield** | Simple, fast prototypes | Works well immediately |
|
|
31
|
+
| **Legacy** | Complex, hidden invariants | Needs careful context loading |
|
|
32
|
+
|
|
33
|
+
Codebase complexity is a primary difficulty knob. Context is how you pay it down.
|
|
34
|
+
|
|
35
|
+
## Three Context Constraints
|
|
36
|
+
|
|
37
|
+
1. **Blind spots cause hallucinations** - If agent doesn't see specific context, it fills gaps with generic training priors. You only get the behavior you load.
|
|
38
|
+
|
|
39
|
+
2. **Everything influences everything** - Noise-to-signal ratio matters. Irrelevant files degrade ALL output quality.
|
|
40
|
+
|
|
41
|
+
3. **Window is finite** - Performance degrades BEFORE hitting hard token limits. Curate the smallest, highest-signal slice.
|
|
42
|
+
|
|
43
|
+
## Practical Implications
|
|
44
|
+
|
|
45
|
+
| Instead of | Do This |
|
|
46
|
+
| ----------------------- | ----------------------------------------------------- |
|
|
47
|
+
| Reading entire files | Use `lsp_lsp_document_symbols` for outline |
|
|
48
|
+
| Loading whole documents | Read specific line ranges |
|
|
49
|
+
| Flat file loading | Navigate AGENTS.md hierarchy (progressive disclosure) |
|
|
50
|
+
| Keeping completed work | Prune context aggressively |
|
|
51
|
+
|
|
52
|
+
## Intent Layer Principles
|
|
53
|
+
|
|
54
|
+
### What Belongs in Each AGENTS.md
|
|
55
|
+
|
|
56
|
+
- **Purpose & Scope** - What this area does. What it explicitly DOESN'T do.
|
|
57
|
+
- **Entry Points & Contracts** - Main APIs, invariants, "all X goes through Y"
|
|
58
|
+
- **Usage Patterns** - Canonical examples: "To add a rule, follow this pattern..."
|
|
59
|
+
- **Anti-patterns** - Negative examples: "Never call X directly; go through Y"
|
|
60
|
+
- **Dependencies & Downlinks** - What it connects to, pointers to child AGENTS.md
|
|
61
|
+
- **Pitfalls** - Things that repeatedly confused agents/humans
|
|
62
|
+
|
|
63
|
+
### Key Mechanics
|
|
64
|
+
|
|
65
|
+
| Principle | Meaning |
|
|
66
|
+
| ------------------------------- | ------------------------------------------------------------- |
|
|
67
|
+
| **Hierarchical loading** | When a node loads, all ancestors load too (T-shaped view) |
|
|
68
|
+
| **Compression, not bloat** | Good nodes compress code; 10k tokens for 20k code adds weight |
|
|
69
|
+
| **Least Common Ancestor (LCA)** | Place shared knowledge at shallowest node covering all paths |
|
|
70
|
+
| **Downlinks for discovery** | Point to related context without loading everything upfront |
|
|
71
|
+
|
|
72
|
+
## Context Budget Guidelines
|
|
73
|
+
|
|
74
|
+
| Phase | Target Context | Action |
|
|
75
|
+
| ----------------- | -------------- | ----------------------------------------- |
|
|
76
|
+
| Starting work | <50k tokens | Load only essential AGENTS.md + task spec |
|
|
77
|
+
| Mid-task | 50-100k tokens | Prune completed reads, keep active files |
|
|
78
|
+
| Approaching limit | >100k tokens | Aggressive pruning, extract key findings |
|
|
79
|
+
| Near capacity | >150k tokens | Consider session restart with handoff |
|
|
80
|
+
|
|
81
|
+
## Anti-Patterns
|
|
82
|
+
|
|
83
|
+
❌ Loading "everything that might be relevant"
|
|
84
|
+
❌ Keeping old file reads after editing complete
|
|
85
|
+
❌ Reading entire files when you only need a function
|
|
86
|
+
❌ Ignoring AGENTS.md hierarchy (loading leaf without ancestors)
|
|
87
|
+
|
|
88
|
+
## Best Practices
|
|
89
|
+
|
|
90
|
+
✅ Start with minimum viable context
|
|
91
|
+
✅ Use LSP tools for targeted information
|
|
92
|
+
✅ Prune after each completed sub-task
|
|
93
|
+
✅ Trust AGENTS.md hierarchy for discovery
|
|
94
|
+
✅ Extract findings before pruning valuable reads
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memory-system
|
|
3
|
+
description: Use when persisting learnings, loading previous context, or searching past decisions - covers memory file structure, tools, and when to update each file
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory System
|
|
7
|
+
|
|
8
|
+
Persistent context that survives across sessions.
|
|
9
|
+
|
|
10
|
+
## Directory Structure
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
.opencode/memory/
|
|
14
|
+
_templates/ # Task templates (prd, observation, session-summary)
|
|
15
|
+
handoffs/ # Phase transitions
|
|
16
|
+
research/ # Research findings
|
|
17
|
+
observations/ # Structured observations
|
|
18
|
+
project/ # Persistent project knowledge
|
|
19
|
+
commands.md # Build, test, lint, deploy commands
|
|
20
|
+
conventions.md # Code patterns, commit style, PR process
|
|
21
|
+
gotchas.md # Footguns, edge cases, "don't forget this"
|
|
22
|
+
architecture.md # Key modules, directory structure
|
|
23
|
+
user.md # Identity, preferences, communication style
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Standard Memory Blocks
|
|
27
|
+
|
|
28
|
+
| File | Purpose | Update When |
|
|
29
|
+
| ------------------------- | ------------------------ | --------------------------- |
|
|
30
|
+
| `project/commands.md` | Build/test/lint commands | Discovering new command |
|
|
31
|
+
| `project/conventions.md` | Code patterns, style | Learning team pattern |
|
|
32
|
+
| `project/gotchas.md` | Footguns, warnings | Hitting unexpected behavior |
|
|
33
|
+
| `project/architecture.md` | Key modules, structure | Mapping new area |
|
|
34
|
+
| `user.md` | Preferences, workflow | Learning user preference |
|
|
35
|
+
|
|
36
|
+
## Explicit Memory Updates
|
|
37
|
+
|
|
38
|
+
Don't rely on implicit learning. Explicitly persist:
|
|
39
|
+
|
|
40
|
+
- Non-obvious project behavior → `project/gotchas.md`
|
|
41
|
+
- User preferences discovered → `user.md`
|
|
42
|
+
- New build/test commands → `project/commands.md`
|
|
43
|
+
- Code patterns to follow → `project/conventions.md`
|
|
44
|
+
|
|
45
|
+
## Memory Tools
|
|
46
|
+
|
|
47
|
+
### memory-read
|
|
48
|
+
|
|
49
|
+
Load previous context or templates:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
memory - read({ file: "project/commands" }); // Load commands
|
|
53
|
+
memory - read({ file: "_templates/prd" }); // Load PRD template
|
|
54
|
+
memory - read({ file: "handoffs/bd-abc123" }); // Load specific handoff
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### memory-update
|
|
58
|
+
|
|
59
|
+
Save learnings or handoffs:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
memory -
|
|
63
|
+
update({
|
|
64
|
+
file: "project/gotchas",
|
|
65
|
+
content: "### New Gotcha\n\nDescription...",
|
|
66
|
+
mode: "append", // or "replace"
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### memory-search
|
|
71
|
+
|
|
72
|
+
Find past decisions, research, or handoffs:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
memory - search({ query: "authentication" });
|
|
76
|
+
memory - search({ query: "bugfix", type: "observations" });
|
|
77
|
+
memory - search({ query: "session", type: "handoffs" });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Observations
|
|
81
|
+
|
|
82
|
+
Record important findings with structured metadata:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
observation({
|
|
86
|
+
type: "decision", // decision, bugfix, feature, pattern, discovery, learning, warning
|
|
87
|
+
title: "Use JWT auth",
|
|
88
|
+
content: "Decided to use JWT because...",
|
|
89
|
+
concepts: "auth, security",
|
|
90
|
+
files: "src/auth.ts",
|
|
91
|
+
bead_id: "bd-abc123",
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**When to create observations:**
|
|
96
|
+
|
|
97
|
+
- Major architectural decisions
|
|
98
|
+
- Bug root causes discovered
|
|
99
|
+
- Patterns worth reusing
|
|
100
|
+
- Gotchas and warnings for future
|
|
101
|
+
|
|
102
|
+
## Best Practices
|
|
103
|
+
|
|
104
|
+
1. **Read before work** - Check relevant memory files at session start
|
|
105
|
+
2. **Update during work** - Don't wait until end; persist incrementally
|
|
106
|
+
3. **Be specific** - Include file paths, function names, concrete examples
|
|
107
|
+
4. **Keep it actionable** - Future agents should know what to do with the info
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: session-management
|
|
3
|
+
description: Use when context is growing large, switching tasks, or needing previous session context - covers thresholds, session tools, and workflow patterns
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Session Management
|
|
7
|
+
|
|
8
|
+
**Philosophy**: Short sessions (<150k tokens) beat long bloated ones. Agents get worse with too much context. Cost is exponential.
|
|
9
|
+
|
|
10
|
+
## Context Thresholds
|
|
11
|
+
|
|
12
|
+
The environment monitors context usage and warns at these thresholds:
|
|
13
|
+
|
|
14
|
+
| Threshold | Action |
|
|
15
|
+
| --------- | ---------------------------------------------------------- |
|
|
16
|
+
| **70%** | Consolidate work; consider pruning irrelevant tool outputs |
|
|
17
|
+
| **85%** | Summarize findings and consider starting a new session |
|
|
18
|
+
| **95%** | Critical: prune context immediately or restart session |
|
|
19
|
+
|
|
20
|
+
## Session Tools
|
|
21
|
+
|
|
22
|
+
### list_sessions
|
|
23
|
+
|
|
24
|
+
Discover available sessions before reading:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
list_sessions({ limit: 10, project: "current" }); // Current project
|
|
28
|
+
list_sessions({ since: "today" }); // Today's sessions
|
|
29
|
+
list_sessions({ project: "all", since: "yesterday" }); // Cross-project
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### read_session
|
|
33
|
+
|
|
34
|
+
Pull context from previous session:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
read_session("last"); // Most recent
|
|
38
|
+
read_session("2 ago", { project: "current" }); // 2nd most recent
|
|
39
|
+
read_session("today"); // Today's first session
|
|
40
|
+
read_session("ses_abc123", { focus: "file changes" }); // Specific aspect
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### search_session
|
|
44
|
+
|
|
45
|
+
Full-text search across sessions:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
search_session({ query: "auth bug" }); // Search all sessions
|
|
49
|
+
search_session({ query: "OAuth", session_id: "ses_abc" }); // Specific session
|
|
50
|
+
search_session({ query: "error", limit: 10 }); // Limit results
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Use to find past discussions, decisions, or work on a topic before starting new work.
|
|
54
|
+
|
|
55
|
+
### summarize_session
|
|
56
|
+
|
|
57
|
+
Generate AI summary of a session:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
summarize_session("ses_abc123"); // Trigger AI summarization
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use before `read_session` to get a quick overview of what happened in a past session without loading full context.
|
|
64
|
+
|
|
65
|
+
## When to Start New Session
|
|
66
|
+
|
|
67
|
+
- Completing distinct task from `bd ready`
|
|
68
|
+
- Token usage approaching 150k
|
|
69
|
+
- Switching phases (implementation → review → testing)
|
|
70
|
+
- After handoff (`/handoff <bead-id>`)
|
|
71
|
+
|
|
72
|
+
## Session Workflow Pattern
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
Session 1: Implement feature X (80k tokens)
|
|
76
|
+
↓ close, update memory
|
|
77
|
+
Session 2: list_sessions() → read_session("last") → Refactor (60k tokens)
|
|
78
|
+
↓
|
|
79
|
+
Session 3: read_session("previous") → Add tests (90k tokens)
|
|
80
|
+
↓
|
|
81
|
+
Session 4: read_session refs → Final review (100k tokens)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Result**: 4 fresh contexts vs 1 degraded 330k context. Better performance, lower cost.
|
|
85
|
+
|
|
86
|
+
## Context Transfer
|
|
87
|
+
|
|
88
|
+
Use all available sources:
|
|
89
|
+
|
|
90
|
+
1. `read_session("last")` - Previous session work
|
|
91
|
+
2. Git state - `git diff`, `git log` - Code changes
|
|
92
|
+
3. Memory files - `.opencode/memory/*` - Persistent context
|
|
93
|
+
4. Beads - `bd show <id>` - Task specs
|
|
94
|
+
|
|
95
|
+
**Don't**: Carry everything forward. Extract what's needed, discard the rest.
|
|
96
|
+
|
|
97
|
+
## Pruning Strategy
|
|
98
|
+
|
|
99
|
+
When context grows large:
|
|
100
|
+
|
|
101
|
+
1. **Discard** completed task outputs (read files you won't edit again)
|
|
102
|
+
2. **Extract** key findings before discarding research
|
|
103
|
+
3. **Summarize** complex investigations into memory files
|
|
104
|
+
4. **Restart** session if above 85% and work is at a natural break
|
|
105
|
+
|
|
106
|
+
## Anti-Patterns
|
|
107
|
+
|
|
108
|
+
- ❌ Running until context limit forces restart
|
|
109
|
+
- ❌ Carrying all previous reads forward "just in case"
|
|
110
|
+
- ❌ Not using memory files for cross-session persistence
|
|
111
|
+
- ❌ Re-reading the same files every session instead of extracting key info
|