opencodekit 0.23.3 → 0.23.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/README.md +7 -14
- package/dist/index.js +1 -1
- package/dist/template/.opencode/AGENTS.md +89 -17
- package/dist/template/.opencode/README.md +43 -6
- package/dist/template/.opencode/artifacts/harness-workflows/plan.md +317 -0
- package/dist/template/.opencode/command/audit.md +65 -0
- package/dist/template/.opencode/command/init.md +19 -2
- package/dist/template/.opencode/command/research.md +67 -16
- package/dist/template/.opencode/command/ship.md +55 -5
- package/dist/template/.opencode/command/verify.md +5 -5
- package/dist/template/.opencode/opencode.json +12 -0
- package/dist/template/.opencode/plugin/README.md +0 -6
- package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +0 -2
- package/dist/template/.opencode/skill/development-lifecycle/SKILL.md +11 -9
- package/dist/template/.opencode/skill/manifest.json +77 -0
- package/dist/template/.opencode/workflows/audit-pattern.md +51 -0
- package/dist/template/.opencode/workflows/batch-implement.md +82 -0
- package/dist/template/.opencode/workflows/deep-research.md +58 -0
- package/dist/template/.opencode/workflows/development-lifecycle-workflow.md +129 -0
- package/package.json +1 -1
- package/dist/template/.opencode/command/clarify.md +0 -46
- package/dist/template/.opencode/command/commit.md +0 -53
- package/dist/template/.opencode/command/design.md +0 -129
- package/dist/template/.opencode/command/explore.md +0 -169
- package/dist/template/.opencode/command/improve-architecture.md +0 -55
- package/dist/template/.opencode/command/pr.md +0 -148
- package/dist/template/.opencode/command/refactor.md +0 -65
- package/dist/template/.opencode/command/review-codebase.md +0 -128
- package/dist/template/.opencode/command/test.md +0 -66
- package/dist/template/.opencode/command/ui-review.md +0 -109
- package/dist/template/.opencode/opencodex-fast.jsonc +0 -3
- package/dist/template/.opencode/plugin/rtk.ts +0 -43
- package/dist/template/.opencode/skill/agent-teams/SKILL.md +0 -268
- package/dist/template/.opencode/skill/code-navigation/SKILL.md +0 -142
- package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +0 -135
- package/dist/template/.opencode/skill/condition-based-waiting/example.ts +0 -171
- package/dist/template/.opencode/skill/context-engineering/SKILL.md +0 -176
- package/dist/template/.opencode/skill/memory-system/SKILL.md +0 -147
- package/dist/template/.opencode/skill/structured-edit/SKILL.md +0 -191
- package/dist/template/.opencode/skill/ubiquitous-language/SKILL.md +0 -184
- package/dist/template/.opencode/skill/v0/SKILL.md +0 -158
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
// Complete implementation of condition-based waiting utilities
|
|
2
|
-
// From: Lace test infrastructure improvements (2025-10-03)
|
|
3
|
-
// Context: Fixed 15 flaky tests by replacing arbitrary timeouts
|
|
4
|
-
//
|
|
5
|
-
// Self-contained — types are defined inline to avoid external dependencies.
|
|
6
|
-
|
|
7
|
-
/** Minimal thread manager interface for condition polling */
|
|
8
|
-
interface ThreadManager {
|
|
9
|
-
getEvents(threadId: string): LaceEvent[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** Generic event type identifier */
|
|
13
|
-
type LaceEventType = string;
|
|
14
|
-
|
|
15
|
-
/** Generic event with type discriminator and optional data payload */
|
|
16
|
-
interface LaceEvent {
|
|
17
|
-
type: string;
|
|
18
|
-
data?: Record<string, unknown>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Wait for a specific event type to appear in thread
|
|
23
|
-
*
|
|
24
|
-
* @param threadManager - The thread manager to query
|
|
25
|
-
* @param threadId - Thread to check for events
|
|
26
|
-
* @param eventType - Type of event to wait for
|
|
27
|
-
* @param timeoutMs - Maximum time to wait (default 5000ms)
|
|
28
|
-
* @returns Promise resolving to the first matching event
|
|
29
|
-
*
|
|
30
|
-
* Example:
|
|
31
|
-
* await waitForEvent(threadManager, agentThreadId, 'TOOL_RESULT');
|
|
32
|
-
*/
|
|
33
|
-
export function waitForEvent(
|
|
34
|
-
threadManager: ThreadManager,
|
|
35
|
-
threadId: string,
|
|
36
|
-
eventType: LaceEventType,
|
|
37
|
-
timeoutMs = 5000,
|
|
38
|
-
): Promise<LaceEvent> {
|
|
39
|
-
return new Promise((resolve, reject) => {
|
|
40
|
-
const startTime = Date.now();
|
|
41
|
-
|
|
42
|
-
const check = () => {
|
|
43
|
-
const events = threadManager.getEvents(threadId);
|
|
44
|
-
const event = events.find((e) => e.type === eventType);
|
|
45
|
-
|
|
46
|
-
if (event) {
|
|
47
|
-
resolve(event);
|
|
48
|
-
} else if (Date.now() - startTime > timeoutMs) {
|
|
49
|
-
reject(new Error(`Timeout waiting for ${eventType} event after ${timeoutMs}ms`));
|
|
50
|
-
} else {
|
|
51
|
-
setTimeout(check, 10); // Poll every 10ms for efficiency
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
check();
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Wait for a specific number of events of a given type
|
|
61
|
-
*
|
|
62
|
-
* @param threadManager - The thread manager to query
|
|
63
|
-
* @param threadId - Thread to check for events
|
|
64
|
-
* @param eventType - Type of event to wait for
|
|
65
|
-
* @param count - Number of events to wait for
|
|
66
|
-
* @param timeoutMs - Maximum time to wait (default 5000ms)
|
|
67
|
-
* @returns Promise resolving to all matching events once count is reached
|
|
68
|
-
*
|
|
69
|
-
* Example:
|
|
70
|
-
* // Wait for 2 AGENT_MESSAGE events (initial response + continuation)
|
|
71
|
-
* await waitForEventCount(threadManager, agentThreadId, 'AGENT_MESSAGE', 2);
|
|
72
|
-
*/
|
|
73
|
-
export function waitForEventCount(
|
|
74
|
-
threadManager: ThreadManager,
|
|
75
|
-
threadId: string,
|
|
76
|
-
eventType: LaceEventType,
|
|
77
|
-
count: number,
|
|
78
|
-
timeoutMs = 5000,
|
|
79
|
-
): Promise<LaceEvent[]> {
|
|
80
|
-
return new Promise((resolve, reject) => {
|
|
81
|
-
const startTime = Date.now();
|
|
82
|
-
|
|
83
|
-
const check = () => {
|
|
84
|
-
const events = threadManager.getEvents(threadId);
|
|
85
|
-
const matchingEvents = events.filter((e) => e.type === eventType);
|
|
86
|
-
|
|
87
|
-
if (matchingEvents.length >= count) {
|
|
88
|
-
resolve(matchingEvents);
|
|
89
|
-
} else if (Date.now() - startTime > timeoutMs) {
|
|
90
|
-
reject(
|
|
91
|
-
new Error(
|
|
92
|
-
`Timeout waiting for ${count} ${eventType} events after ${timeoutMs}ms (got ${matchingEvents.length})`,
|
|
93
|
-
),
|
|
94
|
-
);
|
|
95
|
-
} else {
|
|
96
|
-
setTimeout(check, 10);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
check();
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Wait for an event matching a custom predicate
|
|
106
|
-
* Useful when you need to check event data, not just type
|
|
107
|
-
*
|
|
108
|
-
* @param threadManager - The thread manager to query
|
|
109
|
-
* @param threadId - Thread to check for events
|
|
110
|
-
* @param predicate - Function that returns true when event matches
|
|
111
|
-
* @param description - Human-readable description for error messages
|
|
112
|
-
* @param timeoutMs - Maximum time to wait (default 5000ms)
|
|
113
|
-
* @returns Promise resolving to the first matching event
|
|
114
|
-
*
|
|
115
|
-
* Example:
|
|
116
|
-
* // Wait for TOOL_RESULT with specific ID
|
|
117
|
-
* await waitForEventMatch(
|
|
118
|
-
* threadManager,
|
|
119
|
-
* agentThreadId,
|
|
120
|
-
* (e) => e.type === 'TOOL_RESULT' && e.data.id === 'call_123',
|
|
121
|
-
* 'TOOL_RESULT with id=call_123'
|
|
122
|
-
* );
|
|
123
|
-
*/
|
|
124
|
-
export function waitForEventMatch(
|
|
125
|
-
threadManager: ThreadManager,
|
|
126
|
-
threadId: string,
|
|
127
|
-
predicate: (event: LaceEvent) => boolean,
|
|
128
|
-
description: string,
|
|
129
|
-
timeoutMs = 5000,
|
|
130
|
-
): Promise<LaceEvent> {
|
|
131
|
-
return new Promise((resolve, reject) => {
|
|
132
|
-
const startTime = Date.now();
|
|
133
|
-
|
|
134
|
-
const check = () => {
|
|
135
|
-
const events = threadManager.getEvents(threadId);
|
|
136
|
-
const event = events.find(predicate);
|
|
137
|
-
|
|
138
|
-
if (event) {
|
|
139
|
-
resolve(event);
|
|
140
|
-
} else if (Date.now() - startTime > timeoutMs) {
|
|
141
|
-
reject(new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`));
|
|
142
|
-
} else {
|
|
143
|
-
setTimeout(check, 10);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
check();
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Usage example from actual debugging session:
|
|
152
|
-
//
|
|
153
|
-
// BEFORE (flaky):
|
|
154
|
-
// ---------------
|
|
155
|
-
// const messagePromise = agent.sendMessage('Execute tools');
|
|
156
|
-
// await new Promise(r => setTimeout(r, 300)); // Hope tools start in 300ms
|
|
157
|
-
// agent.abort();
|
|
158
|
-
// await messagePromise;
|
|
159
|
-
// await new Promise(r => setTimeout(r, 50)); // Hope results arrive in 50ms
|
|
160
|
-
// expect(toolResults.length).toBe(2); // Fails randomly
|
|
161
|
-
//
|
|
162
|
-
// AFTER (reliable):
|
|
163
|
-
// ----------------
|
|
164
|
-
// const messagePromise = agent.sendMessage('Execute tools');
|
|
165
|
-
// await waitForEventCount(threadManager, threadId, 'TOOL_CALL', 2); // Wait for tools to start
|
|
166
|
-
// agent.abort();
|
|
167
|
-
// await messagePromise;
|
|
168
|
-
// await waitForEventCount(threadManager, threadId, 'TOOL_RESULT', 2); // Wait for results
|
|
169
|
-
// expect(toolResults.length).toBe(2); // Always succeeds
|
|
170
|
-
//
|
|
171
|
-
// Result: 60% pass rate → 100%, 40% faster execution
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: context-engineering
|
|
3
|
-
description: Use when designing AGENTS.md hierarchies, understanding autonomous duration, or writing intent layers - covers principles for extending agent work capacity
|
|
4
|
-
version: 1.0.0
|
|
5
|
-
tags: [context, documentation]
|
|
6
|
-
dependencies: []
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Context Engineering
|
|
10
|
-
|
|
11
|
-
## When to Use
|
|
12
|
-
|
|
13
|
-
- Designing or refactoring AGENTS.md hierarchies and intent layers
|
|
14
|
-
- You need to extend autonomous work duration via better context structure
|
|
15
|
-
|
|
16
|
-
## When NOT to Use
|
|
17
|
-
|
|
18
|
-
- You only need pruning/distillation mechanics (use context-management)
|
|
19
|
-
- Simple tasks where context design is not relevant
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Core Principle
|
|
24
|
-
|
|
25
|
-
**Autonomous Duration**: How long can an agent work before losing the plot?
|
|
26
|
-
|
|
27
|
-
Extend it by:
|
|
28
|
-
|
|
29
|
-
- Binding tighter to intent (clear specs, constraints, invariants)
|
|
30
|
-
- Providing systematic context (AGENTS.md hierarchy, memory files)
|
|
31
|
-
- Verification loops (test → iterate → verify)
|
|
32
|
-
|
|
33
|
-
## Three Context Constraints
|
|
34
|
-
|
|
35
|
-
1. **Blind spots cause hallucinations** - Agent fills gaps with generic priors
|
|
36
|
-
2. **Everything influences everything** - Noise degrades ALL output quality
|
|
37
|
-
3. **Window is finite** - Performance degrades BEFORE hard token limits
|
|
38
|
-
|
|
39
|
-
## Intent Layer Principles
|
|
40
|
-
|
|
41
|
-
### What Belongs in Each AGENTS.md
|
|
42
|
-
|
|
43
|
-
- **Purpose & Scope** - What this area does. What it DOESN'T do.
|
|
44
|
-
- **Entry Points & Contracts** - Main APIs, invariants
|
|
45
|
-
- **Usage Patterns** - Canonical examples
|
|
46
|
-
- **Anti-patterns** - What NOT to do
|
|
47
|
-
- **Dependencies & Downlinks** - Pointers to related context
|
|
48
|
-
|
|
49
|
-
### Key Mechanics
|
|
50
|
-
|
|
51
|
-
| Principle | Meaning |
|
|
52
|
-
| ------------------------ | -------------------------------------------------------- |
|
|
53
|
-
| **Hierarchical loading** | When node loads, all ancestors load too (T-shaped view) |
|
|
54
|
-
| **Compression** | Good nodes compress code; don't add bloat |
|
|
55
|
-
| **LCA placement** | Place shared knowledge at shallowest node covering paths |
|
|
56
|
-
| **Downlinks** | Point to related context without loading everything |
|
|
57
|
-
|
|
58
|
-
## Practical Implications
|
|
59
|
-
|
|
60
|
-
| Instead of | Do This |
|
|
61
|
-
| ----------------------- | --------------------------------------- |
|
|
62
|
-
| Reading entire files | Use `lsp documentSymbol` for outline |
|
|
63
|
-
| Loading whole documents | Read specific line ranges |
|
|
64
|
-
| Flat file loading | Navigate AGENTS.md hierarchy |
|
|
65
|
-
| Keeping completed work | Compress closed phases, sweep stale noise (context-management) |
|
|
66
|
-
|
|
67
|
-
## Anti-Patterns
|
|
68
|
-
|
|
69
|
-
❌ Loading "everything that might be relevant"
|
|
70
|
-
❌ Keeping old file reads after editing complete
|
|
71
|
-
❌ Reading entire files when you only need a function
|
|
72
|
-
❌ Ignoring AGENTS.md hierarchy
|
|
73
|
-
|
|
74
|
-
## Static vs Runtime Context (Longshot Pattern)
|
|
75
|
-
|
|
76
|
-
At scale (10+ agents), the difference between **static context** and **runtime context** is the difference between a coherent swarm and chaos.
|
|
77
|
-
|
|
78
|
-
### Definitions
|
|
79
|
-
|
|
80
|
-
| Type | What It Is | When Loaded | Example |
|
|
81
|
-
| ------------------- | ------------------------------------------------------------ | ---------------------- | --------------------------------------- |
|
|
82
|
-
| **Static Context** | Always-on knowledge — invariants, constraints, project shape | Always (auto-injected) | AGENTS.md, tech-stack.md, user.md |
|
|
83
|
-
| **Runtime Context** | Per-task injections — what THIS task needs right now | Per-task | Delegation packet, task spec, file list |
|
|
84
|
-
|
|
85
|
-
### Why the Split Matters
|
|
86
|
-
|
|
87
|
-
Without separation, context becomes soup:
|
|
88
|
-
|
|
89
|
-
- Agent loads everything → hits token limit → degrades
|
|
90
|
-
- Agents share stale context → conflicting decisions
|
|
91
|
-
- No clear source of truth for "what is the objective"
|
|
92
|
-
|
|
93
|
-
With separation:
|
|
94
|
-
|
|
95
|
-
- Static = immune to session pollution (always fresh)
|
|
96
|
-
- Runtime = scoped to task (cleaned up when done)
|
|
97
|
-
- Result: agents stay coherent at 200-agent scale
|
|
98
|
-
|
|
99
|
-
### Task Packet Format
|
|
100
|
-
|
|
101
|
-
Every task dispatched to a worker agent MUST include an explicit context block:
|
|
102
|
-
|
|
103
|
-
```markdown
|
|
104
|
-
## Task Packet
|
|
105
|
-
|
|
106
|
-
### Static Context (always available)
|
|
107
|
-
|
|
108
|
-
- Project rules: AGENTS.md
|
|
109
|
-
- Tech stack: .opencode/memory/project/tech-stack.md
|
|
110
|
-
- Gotchas: .opencode/memory/project/gotchas.md
|
|
111
|
-
|
|
112
|
-
### Runtime Context (this task only)
|
|
113
|
-
|
|
114
|
-
- Objective: [one sentence]
|
|
115
|
-
- Scope: [files this task may touch]
|
|
116
|
-
- Constraints: [must_do / must_not_do]
|
|
117
|
-
- Dependencies: [what was produced by prior tasks]
|
|
118
|
-
- Verification: [acceptance commands]
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Injection Pattern
|
|
122
|
-
|
|
123
|
-
When spawning workers, always inject runtime context explicitly:
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
// WRONG: Vague prompt — agent guesses context
|
|
127
|
-
Task({ prompt: "Implement auth service" });
|
|
128
|
-
|
|
129
|
-
// RIGHT: Explicit static + runtime context split
|
|
130
|
-
Task({
|
|
131
|
-
prompt: `## Static Context
|
|
132
|
-
AGENTS.md governs all decisions. Tech stack: Bun, TypeScript strict mode.
|
|
133
|
-
|
|
134
|
-
## Runtime Context
|
|
135
|
-
Objective: Implement JWT auth service in src/auth/service.ts.
|
|
136
|
-
Scope: Only modify src/auth/ directory.
|
|
137
|
-
Dependencies: Schema defined in src/db/schema.ts (from task-1).
|
|
138
|
-
Constraints:
|
|
139
|
-
MUST DO: Use zod for input validation
|
|
140
|
-
MUST NOT DO: Add new dependencies without approval
|
|
141
|
-
Verification:
|
|
142
|
-
npm run typecheck && npm run lint && vitest src/auth/`,
|
|
143
|
-
});
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### Context Pollution Anti-Patterns
|
|
147
|
-
|
|
148
|
-
| Anti-Pattern | Problem | Fix |
|
|
149
|
-
| ------------------------------------------- | --------------------------------- | ------------------------------------- |
|
|
150
|
-
| Passing entire AGENTS.md as runtime context | Bloats token budget on every task | Load via static injection only |
|
|
151
|
-
| Runtime state persisting across waves | Stale context poisons next wave | Clear runtime state between waves |
|
|
152
|
-
| No objective in task packet | Agent drifts from goal | Always include one-sentence objective |
|
|
153
|
-
| Injection without scope | Agent modifies wrong files | Always declare file scope |
|
|
154
|
-
|
|
155
|
-
### Static Context Files (Always Inject)
|
|
156
|
-
|
|
157
|
-
These files are the project's invariant layer. Always available, never stale:
|
|
158
|
-
|
|
159
|
-
```
|
|
160
|
-
.opencode/memory/project/
|
|
161
|
-
├── user.md # User preferences, workflow rules
|
|
162
|
-
├── tech-stack.md # Frameworks, constraints
|
|
163
|
-
├── gotchas.md # Footguns, warnings
|
|
164
|
-
└── project.md # Vision, success criteria
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### Runtime Context Files (Per-Task)
|
|
168
|
-
|
|
169
|
-
These are created fresh per task and cleaned up after:
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
.opencode/artifacts/<slug>/
|
|
173
|
-
├── delegation.md # Task-specific instructions
|
|
174
|
-
├── spec.md # Technical requirements
|
|
175
|
-
└── progress.txt # Task state (append-only)
|
|
176
|
-
```
|
|
@@ -1,147 +0,0 @@
|
|
|
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
|
-
version: 1.2.0
|
|
5
|
-
tags: [context, workflow]
|
|
6
|
-
dependencies: []
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Memory System Best Practices
|
|
10
|
-
|
|
11
|
-
> **Replaces** losing context between sessions — persistent knowledge that survives session boundaries
|
|
12
|
-
|
|
13
|
-
## When to Use
|
|
14
|
-
|
|
15
|
-
- Starting work and needing prior decisions, bugfixes, or patterns
|
|
16
|
-
- Recording non-obvious decisions/learnings for future sessions
|
|
17
|
-
- Creating handoffs so the next session can continue quickly
|
|
18
|
-
|
|
19
|
-
## When NOT to Use
|
|
20
|
-
|
|
21
|
-
- Ephemeral debugging notes that won't matter after the current task
|
|
22
|
-
- Storing generated artifacts/log dumps as long-term memory
|
|
23
|
-
|
|
24
|
-
## Core Principle
|
|
25
|
-
|
|
26
|
-
**Progressive disclosure**: search compactly, fetch fully only when relevant, then record high-signal observations.
|
|
27
|
-
|
|
28
|
-
## Session Workflow
|
|
29
|
-
|
|
30
|
-
1. **Ground (search first)**
|
|
31
|
-
- Run `memory-search` with task keywords before implementation.
|
|
32
|
-
- Check recent handoffs when resuming interrupted work.
|
|
33
|
-
2. **Calibrate (progressive disclosure)**
|
|
34
|
-
- Use search results as index; `memory-search({ file: "..." })` for file access.
|
|
35
|
-
- Retrieve full observation details from search output.
|
|
36
|
-
3. **Record (high-signal only)**
|
|
37
|
-
- Create `observation` for decisions, bugfixes, patterns, warnings, or durable learnings.
|
|
38
|
-
- Include searchable concepts and concrete file references.
|
|
39
|
-
4. **Handoff (if session boundary)**
|
|
40
|
-
- Write a concise status note with completed work, blockers, and next steps using `observation`.
|
|
41
|
-
|
|
42
|
-
## What Goes Where
|
|
43
|
-
|
|
44
|
-
| Store | Put Here | Avoid Here |
|
|
45
|
-
| --- | --- | --- |
|
|
46
|
-
| `observation` (SQLite) | Events: decisions, bugfixes, reusable patterns, warnings, handoffs | Temporary notes, speculative ideas without evidence |
|
|
47
|
-
| `memory-search` by file | Durable docs: handoffs, research, project notes | Every minor runtime detail from a single debug run |
|
|
48
|
-
| Auto pipeline | Captured messages + distillations (automatic) | Manual copying of full transcripts |
|
|
49
|
-
|
|
50
|
-
## Observation Quality Bar
|
|
51
|
-
|
|
52
|
-
Use this checklist before creating an observation:
|
|
53
|
-
|
|
54
|
-
- Is it likely useful in a future session?
|
|
55
|
-
- Is it non-obvious (not already in code/comments)?
|
|
56
|
-
- Can I summarize it in one clear title + short narrative?
|
|
57
|
-
- Did I include strong search terms in `concepts` and relevant files?
|
|
58
|
-
|
|
59
|
-
If most answers are "no", skip creating the observation.
|
|
60
|
-
|
|
61
|
-
## Anti-Patterns
|
|
62
|
-
|
|
63
|
-
| Anti-Pattern | Why It Fails | Instead |
|
|
64
|
-
| --- | --- | --- |
|
|
65
|
-
| Storing transient debugging info as permanent observations | Pollutes search results with low-value noise | Keep transient info in session context; record only durable findings |
|
|
66
|
-
| Creating observations for every small finding (signal-to-noise) | Important items get buried and retrieval quality drops | Batch minor notes; publish one distilled observation per meaningful outcome |
|
|
67
|
-
| Not searching memory before creating duplicate observations | Produces conflicting/duplicated records | Run `memory-search` first; update/supersede existing records when appropriate |
|
|
68
|
-
| Using `observation` with `file` param for document-style content | Document-style content should use `memory-search({ file: "..." })` for file access | Use `observation` for events; write to memory files when structured document storage is needed |
|
|
69
|
-
|
|
70
|
-
## Verification
|
|
71
|
-
|
|
72
|
-
After creating an observation: `memory-search` with relevant keywords should find it.
|
|
73
|
-
|
|
74
|
-
## Practical Defaults
|
|
75
|
-
|
|
76
|
-
- Prefer specific queries over broad ones (`"auth race condition init"` > `"auth"`).
|
|
77
|
-
- For ongoing work, append to one handoff file per task/day instead of many tiny files.
|
|
78
|
-
- Keep observation titles concrete and action-oriented.
|
|
79
|
-
|
|
80
|
-
## Admin Operations
|
|
81
|
-
|
|
82
|
-
The `memory-admin` tool supports these operations:
|
|
83
|
-
|
|
84
|
-
### Core (existing)
|
|
85
|
-
| Operation | Purpose |
|
|
86
|
-
|---|---|
|
|
87
|
-
| `status` | Storage stats, FTS5 health, pipeline counts |
|
|
88
|
-
| `full` | Full maintenance cycle (archive + checkpoint + vacuum) |
|
|
89
|
-
| `archive` | Archive observations older than N days |
|
|
90
|
-
| `checkpoint` | Checkpoint WAL file |
|
|
91
|
-
| `vacuum` | Vacuum database |
|
|
92
|
-
| `migrate` | Import .opencode/memory/observations/*.md into SQLite |
|
|
93
|
-
| `capture-stats` | Temporal message capture statistics |
|
|
94
|
-
| `distill-now` | Force distillation for current session |
|
|
95
|
-
| `curate-now` | Force curator run |
|
|
96
|
-
|
|
97
|
-
### Knowledge Intelligence (new in v2.1)
|
|
98
|
-
| Operation | Purpose |
|
|
99
|
-
|---|---|
|
|
100
|
-
| `lint` | Find duplicates, contradictions, stale/orphan observations |
|
|
101
|
-
| `index` | Generate a structured catalog of all observations |
|
|
102
|
-
| `compile` | Build concept-grouped articles from observation clusters |
|
|
103
|
-
| `log` | View the append-only operation audit trail |
|
|
104
|
-
|
|
105
|
-
Examples:
|
|
106
|
-
```
|
|
107
|
-
memory-admin({ operation: "lint" })
|
|
108
|
-
memory-admin({ operation: "lint", older_than_days: 60 })
|
|
109
|
-
memory-admin({ operation: "index" })
|
|
110
|
-
memory-admin({ operation: "compile" })
|
|
111
|
-
memory-admin({ operation: "log" })
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Reading Compiled Artifacts
|
|
115
|
-
```
|
|
116
|
-
memory-search({ file: "index" }) // Full observation catalog
|
|
117
|
-
memory-search({ file: "compiled/auth" }) // Compiled article for "auth" concept
|
|
118
|
-
memory-search({ file: "log" }) // Operation audit trail
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## Validation Gate
|
|
122
|
-
|
|
123
|
-
The `observation` tool now validates before storing:
|
|
124
|
-
- **Exact duplicate** → rejected (returns duplicate ID + supersede hint)
|
|
125
|
-
- **Near-duplicate** → stored with warning
|
|
126
|
-
- **Contradiction** → stored with warning (for decisions sharing concepts)
|
|
127
|
-
- **Low quality** → stored with warning (no narrative + no concepts)
|
|
128
|
-
|
|
129
|
-
To update an existing observation, use `supersedes`:
|
|
130
|
-
```
|
|
131
|
-
observation({ type: "decision", title: "Use JWT", supersedes: "42", ... })
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Idle Pipeline
|
|
135
|
-
|
|
136
|
-
During `session.idle`, the memory system automatically runs:
|
|
137
|
-
1. Distill undistilled messages
|
|
138
|
-
2. Curate observations from distillations
|
|
139
|
-
3. Optimize FTS5 index
|
|
140
|
-
4. Checkpoint WAL if large
|
|
141
|
-
5. Compile concept articles (max 10)
|
|
142
|
-
6. Regenerate memory index
|
|
143
|
-
|
|
144
|
-
## See Also
|
|
145
|
-
|
|
146
|
-
- `context-management`
|
|
147
|
-
- `session-management`
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: structured-edit
|
|
3
|
-
description: Use when editing files to reduce str_replace failures - combines LSP location with read-verify-edit pattern for reliable edits
|
|
4
|
-
version: 1.0.0
|
|
5
|
-
tags: [code-quality, workflow]
|
|
6
|
-
dependencies: []
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Structured Edit Protocol
|
|
10
|
-
|
|
11
|
-
## When to Use
|
|
12
|
-
|
|
13
|
-
- Editing files where exact string matches are error-prone (e.g., str_replace failures)
|
|
14
|
-
- Making targeted changes that require precise location and verification
|
|
15
|
-
|
|
16
|
-
## When NOT to Use
|
|
17
|
-
|
|
18
|
-
- One-line edits where a direct edit is safe and unambiguous
|
|
19
|
-
- Large refactors better served by full rewrites or file splits
|
|
20
|
-
|
|
21
|
-
## Common Rationalizations
|
|
22
|
-
|
|
23
|
-
| Rationalization | Rebuttal |
|
|
24
|
-
| ------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
25
|
-
| "I remember what the file looks like" | You remember what it looked like 5 edits ago. Read it fresh |
|
|
26
|
-
| "The edit is simple, I don't need to verify" | Simple edits fail most often — whitespace, encoding, duplicate matches |
|
|
27
|
-
| "LSP lookup is an extra step I can skip" | LSP takes 1 call. Retrying a failed edit takes 3-5 calls |
|
|
28
|
-
| "I'll just use a larger context block to be safe" | Larger blocks = more chances for invisible character mismatches. Use minimal unique context |
|
|
29
|
-
| "The file hasn't changed since I last read it" | Other edits, formatters, and git operations can modify files between your reads |
|
|
30
|
-
|
|
31
|
-
## Overview
|
|
32
|
-
|
|
33
|
-
The `str_replace` edit tool is the #1 source of failures in LLM coding. Models reproduce content with subtle differences (whitespace, encoding, line endings) causing "string not found" errors.
|
|
34
|
-
|
|
35
|
-
**Core principle:** Don't guess content — locate, read, verify, then edit.
|
|
36
|
-
|
|
37
|
-
## Why This Exists
|
|
38
|
-
|
|
39
|
-
`str_replace` failures happen when:
|
|
40
|
-
|
|
41
|
-
| Cause | Example |
|
|
42
|
-
| -------------------- | ------------------------------------------ |
|
|
43
|
-
| Whitespace mismatch | Tabs vs spaces, trailing spaces |
|
|
44
|
-
| Content changed | File modified since last read |
|
|
45
|
-
| Multiple matches | Same string appears twice in file |
|
|
46
|
-
| Encoding differences | Line endings (CRLF vs LF), invisible chars |
|
|
47
|
-
| Stale context | Editing from memory instead of fresh read |
|
|
48
|
-
|
|
49
|
-
**Result:** Wasted tokens on retries, frustrated developers, broken workflows.
|
|
50
|
-
|
|
51
|
-
## The Protocol
|
|
52
|
-
|
|
53
|
-
### Step 1: LOCATE
|
|
54
|
-
|
|
55
|
-
Use LSP to find exact positions instead of guessing:
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
// Find where a function is defined
|
|
59
|
-
lsp({ operation: "goToDefinition", filePath, line, character });
|
|
60
|
-
|
|
61
|
-
// Find all references to a symbol
|
|
62
|
-
lsp({ operation: "findReferences", filePath, line, character });
|
|
63
|
-
|
|
64
|
-
// Get all symbols in a file
|
|
65
|
-
lsp({ operation: "documentSymbol", filePath, line: 1, character: 1 });
|
|
66
|
-
|
|
67
|
-
// Search for symbol across workspace
|
|
68
|
-
lsp({ operation: "workspaceSymbol", filePath, line: 1, character: 1 });
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Step 2: READ
|
|
72
|
-
|
|
73
|
-
Get fresh file content at the located position:
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
// Read context around target line
|
|
77
|
-
read({ filePath, offset: line - 10, limit: 30 });
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**Always include context** — don't just read the target line.
|
|
81
|
-
|
|
82
|
-
### Step 3: VERIFY
|
|
83
|
-
|
|
84
|
-
Confirm expected content exists at the location:
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
// Check that what you expect is actually there
|
|
88
|
-
if (!content.includes(expectedSubstring)) {
|
|
89
|
-
// STOP — investigate before proceeding
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Step 4: EDIT
|
|
94
|
-
|
|
95
|
-
Apply minimal, scoped change with unique context:
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
// Include enough surrounding lines for uniqueness
|
|
99
|
-
edit({
|
|
100
|
-
filePath,
|
|
101
|
-
oldString: " // unique context before\n targetLine\n // unique context after",
|
|
102
|
-
newString: " // unique context before\n modifiedLine\n // unique context after",
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
**Guidelines:**
|
|
107
|
-
|
|
108
|
-
- Include 2-3 lines before/after for uniqueness
|
|
109
|
-
- Never use just the target line
|
|
110
|
-
- Keep oldString minimal but unique
|
|
111
|
-
|
|
112
|
-
### Step 5: CONFIRM
|
|
113
|
-
|
|
114
|
-
Verify the edit succeeded:
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
// Read back the modified section
|
|
118
|
-
read({ filePath, offset: line - 5, limit: 15 });
|
|
119
|
-
|
|
120
|
-
// Confirm content matches intent
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Red Flags — STOP
|
|
124
|
-
|
|
125
|
-
If you catch yourself:
|
|
126
|
-
|
|
127
|
-
- Editing without reading first
|
|
128
|
-
- Assuming content hasn't changed
|
|
129
|
-
- Using large multi-line oldString values
|
|
130
|
-
- Skipping the verify step
|
|
131
|
-
- Guessing line numbers without LSP
|
|
132
|
-
|
|
133
|
-
**STOP.** Return to Step 1.
|
|
134
|
-
|
|
135
|
-
## Best Practices
|
|
136
|
-
|
|
137
|
-
### File Size Matters
|
|
138
|
-
|
|
139
|
-
From research on the "harness problem":
|
|
140
|
-
|
|
141
|
-
| File Size | Strategy |
|
|
142
|
-
| ------------- | ------------------------------------------ |
|
|
143
|
-
| < 100 lines | Full rewrite often easier than str_replace |
|
|
144
|
-
| 100-400 lines | Structured edit with good context |
|
|
145
|
-
| > 400 lines | Strongly prefer structured edits |
|
|
146
|
-
|
|
147
|
-
**Prefer smaller files** — composition over monoliths.
|
|
148
|
-
|
|
149
|
-
### Unique Context Selection
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// BAD: Non-unique, whitespace-sensitive
|
|
153
|
-
oldString: "return result;";
|
|
154
|
-
|
|
155
|
-
// GOOD: Unique with surrounding context
|
|
156
|
-
oldString: " // Calculate final value\n const result = compute(input);\n return result;";
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### When to Use LSP vs Direct
|
|
160
|
-
|
|
161
|
-
| Scenario | Approach |
|
|
162
|
-
| ------------------------- | --------------------- |
|
|
163
|
-
| Finding function/class | LSP goToDefinition |
|
|
164
|
-
| Finding all usages | LSP findReferences |
|
|
165
|
-
| Modifying specific symbol | LSP + structured edit |
|
|
166
|
-
| Large refactoring | Consider full rewrite |
|
|
167
|
-
| Simple one-line change | Direct edit OK |
|
|
168
|
-
|
|
169
|
-
## Integration with Other Skills
|
|
170
|
-
|
|
171
|
-
**Use with:**
|
|
172
|
-
|
|
173
|
-
- `verification-before-completion` — Always verify edits succeeded
|
|
174
|
-
- `systematic-debugging` — When edit failures indicate deeper issues
|
|
175
|
-
- `defense-in-depth` — Add validation after structural changes
|
|
176
|
-
|
|
177
|
-
## Quick Reference
|
|
178
|
-
|
|
179
|
-
```
|
|
180
|
-
LOCATE → lsp({ operation: "goToDefinition" | "findReferences", ... })
|
|
181
|
-
READ → read({ filePath, offset: line-10, limit: 30 })
|
|
182
|
-
VERIFY → Check expected content exists
|
|
183
|
-
EDIT → edit({ oldString: "...unique context...", newString: "..." })
|
|
184
|
-
CONFIRM → read() again to verify success
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## The Bottom Line
|
|
188
|
-
|
|
189
|
-
**Don't trust your memory. Don't guess content. Locate, read, verify, edit, confirm.**
|
|
190
|
-
|
|
191
|
-
This protocol eliminates the #1 source of LLM coding failures.
|