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.
Files changed (41) hide show
  1. package/README.md +7 -14
  2. package/dist/index.js +1 -1
  3. package/dist/template/.opencode/AGENTS.md +89 -17
  4. package/dist/template/.opencode/README.md +43 -6
  5. package/dist/template/.opencode/artifacts/harness-workflows/plan.md +317 -0
  6. package/dist/template/.opencode/command/audit.md +65 -0
  7. package/dist/template/.opencode/command/init.md +19 -2
  8. package/dist/template/.opencode/command/research.md +67 -16
  9. package/dist/template/.opencode/command/ship.md +55 -5
  10. package/dist/template/.opencode/command/verify.md +5 -5
  11. package/dist/template/.opencode/opencode.json +12 -0
  12. package/dist/template/.opencode/plugin/README.md +0 -6
  13. package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +0 -2
  14. package/dist/template/.opencode/skill/development-lifecycle/SKILL.md +11 -9
  15. package/dist/template/.opencode/skill/manifest.json +77 -0
  16. package/dist/template/.opencode/workflows/audit-pattern.md +51 -0
  17. package/dist/template/.opencode/workflows/batch-implement.md +82 -0
  18. package/dist/template/.opencode/workflows/deep-research.md +58 -0
  19. package/dist/template/.opencode/workflows/development-lifecycle-workflow.md +129 -0
  20. package/package.json +1 -1
  21. package/dist/template/.opencode/command/clarify.md +0 -46
  22. package/dist/template/.opencode/command/commit.md +0 -53
  23. package/dist/template/.opencode/command/design.md +0 -129
  24. package/dist/template/.opencode/command/explore.md +0 -169
  25. package/dist/template/.opencode/command/improve-architecture.md +0 -55
  26. package/dist/template/.opencode/command/pr.md +0 -148
  27. package/dist/template/.opencode/command/refactor.md +0 -65
  28. package/dist/template/.opencode/command/review-codebase.md +0 -128
  29. package/dist/template/.opencode/command/test.md +0 -66
  30. package/dist/template/.opencode/command/ui-review.md +0 -109
  31. package/dist/template/.opencode/opencodex-fast.jsonc +0 -3
  32. package/dist/template/.opencode/plugin/rtk.ts +0 -43
  33. package/dist/template/.opencode/skill/agent-teams/SKILL.md +0 -268
  34. package/dist/template/.opencode/skill/code-navigation/SKILL.md +0 -142
  35. package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +0 -135
  36. package/dist/template/.opencode/skill/condition-based-waiting/example.ts +0 -171
  37. package/dist/template/.opencode/skill/context-engineering/SKILL.md +0 -176
  38. package/dist/template/.opencode/skill/memory-system/SKILL.md +0 -147
  39. package/dist/template/.opencode/skill/structured-edit/SKILL.md +0 -191
  40. package/dist/template/.opencode/skill/ubiquitous-language/SKILL.md +0 -184
  41. 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.