opencodekit 0.18.4 → 0.18.5
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 +396 -6
- package/dist/template/.opencode/AGENTS.md +13 -1
- package/dist/template/.opencode/agent/build.md +4 -1
- package/dist/template/.opencode/agent/explore.md +5 -35
- package/dist/template/.opencode/command/verify.md +63 -12
- package/dist/template/.opencode/memory/research/benchmark-framework.md +162 -0
- package/dist/template/.opencode/memory/research/effectiveness-audit.md +213 -0
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +1429 -1678
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugin/lib/memory-helpers.ts +3 -129
- package/dist/template/.opencode/plugin/lib/memory-hooks.ts +4 -60
- package/dist/template/.opencode/plugin/memory.ts +0 -3
- package/dist/template/.opencode/skill/agent-teams/SKILL.md +16 -1
- package/dist/template/.opencode/skill/beads/SKILL.md +22 -0
- package/dist/template/.opencode/skill/brainstorming/SKILL.md +28 -0
- package/dist/template/.opencode/skill/code-navigation/SKILL.md +130 -0
- package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +12 -0
- package/dist/template/.opencode/skill/context-management/SKILL.md +122 -113
- package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +20 -0
- package/dist/template/.opencode/skill/design-system-audit/SKILL.md +113 -112
- package/dist/template/.opencode/skill/dispatching-parallel-agents/SKILL.md +8 -0
- package/dist/template/.opencode/skill/executing-plans/SKILL.md +7 -0
- package/dist/template/.opencode/skill/memory-system/SKILL.md +50 -266
- package/dist/template/.opencode/skill/mockup-to-code/SKILL.md +21 -6
- package/dist/template/.opencode/skill/receiving-code-review/SKILL.md +8 -0
- package/dist/template/.opencode/skill/root-cause-tracing/SKILL.md +15 -0
- package/dist/template/.opencode/skill/session-management/SKILL.md +4 -103
- package/dist/template/.opencode/skill/subagent-driven-development/SKILL.md +23 -2
- package/dist/template/.opencode/skill/swarm-coordination/SKILL.md +17 -1
- package/dist/template/.opencode/skill/systematic-debugging/SKILL.md +21 -0
- package/dist/template/.opencode/skill/tool-priority/SKILL.md +34 -16
- package/dist/template/.opencode/skill/ui-ux-research/SKILL.md +5 -127
- package/dist/template/.opencode/skill/verification-before-completion/SKILL.md +36 -0
- package/dist/template/.opencode/skill/verification-before-completion/references/VERIFICATION_PROTOCOL.md +133 -29
- package/dist/template/.opencode/skill/visual-analysis/SKILL.md +20 -7
- package/dist/template/.opencode/skill/writing-plans/SKILL.md +7 -0
- package/dist/template/.opencode/tool/context7.ts +9 -1
- package/dist/template/.opencode/tool/grepsearch.ts +9 -1
- package/package.json +1 -1
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Memory Plugin — Helpers
|
|
3
3
|
*
|
|
4
|
-
* Constants,
|
|
4
|
+
* Constants, tool formatting helpers, and file utilities.
|
|
5
5
|
* Pure functions — no plugin/closure dependencies.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import { readdir, readFile, stat } from "node:fs/promises";
|
|
10
|
-
import path from "node:path";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
11
9
|
import type { ObservationType } from "./memory-db.js";
|
|
12
10
|
|
|
13
11
|
// ============================================================================
|
|
@@ -41,25 +39,10 @@ export const FILE_REF_PATTERNS = [
|
|
|
41
39
|
/(?:^|\s)(\.opencode\/\S+)/gm,
|
|
42
40
|
];
|
|
43
41
|
|
|
44
|
-
// Compaction constants
|
|
45
|
-
export const MAX_SESSION_CONTEXT_CHARS = 3000;
|
|
46
|
-
export const MAX_PROJECT_FILES = 3;
|
|
47
|
-
export const MAX_PROJECT_FILE_CHARS = 900;
|
|
48
|
-
export const MAX_HANDOFF_CHARS = 2500;
|
|
49
|
-
export const MAX_BEADS = 8;
|
|
50
|
-
export const MAX_COMBINED_CONTEXT_CHARS = 10000;
|
|
51
|
-
const BEADS_DB_READ_ATTEMPTS = 3;
|
|
52
|
-
const BEADS_DB_RETRY_DELAY_MS = 500;
|
|
53
|
-
|
|
54
42
|
// ============================================================================
|
|
55
|
-
//
|
|
43
|
+
// File Helpers
|
|
56
44
|
// ============================================================================
|
|
57
45
|
|
|
58
|
-
export function truncate(text: string, maxChars: number): string {
|
|
59
|
-
if (text.length <= maxChars) return text;
|
|
60
|
-
return `${text.slice(0, maxChars)}\n...[truncated]`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
46
|
export async function safeReadFile(filePath: string): Promise<string> {
|
|
64
47
|
try {
|
|
65
48
|
return await readFile(filePath, "utf-8");
|
|
@@ -68,115 +51,6 @@ export async function safeReadFile(filePath: string): Promise<string> {
|
|
|
68
51
|
}
|
|
69
52
|
}
|
|
70
53
|
|
|
71
|
-
export function renderSection(title: string, body: string): string {
|
|
72
|
-
if (!body.trim()) return "";
|
|
73
|
-
return `## ${title}\n${body.trim()}`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export async function readProjectMemoryContext(
|
|
77
|
-
memoryDir: string,
|
|
78
|
-
): Promise<string> {
|
|
79
|
-
const projectDir = path.join(memoryDir, "project");
|
|
80
|
-
let names: string[] = [];
|
|
81
|
-
try {
|
|
82
|
-
names = (await readdir(projectDir))
|
|
83
|
-
.filter((n) => n.endsWith(".md"))
|
|
84
|
-
.sort()
|
|
85
|
-
.slice(0, MAX_PROJECT_FILES);
|
|
86
|
-
} catch {
|
|
87
|
-
return "";
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const chunks: string[] = [];
|
|
91
|
-
for (const name of names) {
|
|
92
|
-
const content = (await safeReadFile(path.join(projectDir, name))).trim();
|
|
93
|
-
if (!content) continue;
|
|
94
|
-
chunks.push(
|
|
95
|
-
`### ${name.replace(/\.md$/, "")}\n${truncate(content, MAX_PROJECT_FILE_CHARS)}`,
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
return chunks.join("\n\n");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export async function readLatestHandoff(handoffDir: string): Promise<string> {
|
|
102
|
-
let names: string[] = [];
|
|
103
|
-
try {
|
|
104
|
-
names = (await readdir(handoffDir)).filter((n) => n.endsWith(".md"));
|
|
105
|
-
} catch {
|
|
106
|
-
return "";
|
|
107
|
-
}
|
|
108
|
-
if (names.length === 0) return "";
|
|
109
|
-
|
|
110
|
-
const withMtime = await Promise.all(
|
|
111
|
-
names.map(async (name) => {
|
|
112
|
-
const fullPath = path.join(handoffDir, name);
|
|
113
|
-
try {
|
|
114
|
-
return { name, fullPath, mtimeMs: (await stat(fullPath)).mtimeMs };
|
|
115
|
-
} catch {
|
|
116
|
-
return { name, fullPath, mtimeMs: 0 };
|
|
117
|
-
}
|
|
118
|
-
}),
|
|
119
|
-
);
|
|
120
|
-
withMtime.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
121
|
-
const latest = withMtime[0];
|
|
122
|
-
const content = (await safeReadFile(latest.fullPath)).trim();
|
|
123
|
-
if (!content) return "";
|
|
124
|
-
return `Source: ${latest.name}\n${truncate(content, MAX_HANDOFF_CHARS)}`;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function delay(ms: number): Promise<void> {
|
|
128
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export async function readInProgressBeads(directory: string): Promise<string> {
|
|
132
|
-
const dbPath = path.join(directory, ".beads", "beads.db");
|
|
133
|
-
|
|
134
|
-
for (let attempt = 0; attempt < BEADS_DB_READ_ATTEMPTS; attempt++) {
|
|
135
|
-
let db: Database | undefined;
|
|
136
|
-
try {
|
|
137
|
-
db = new Database(dbPath, { readonly: true });
|
|
138
|
-
const rows = db
|
|
139
|
-
.query<{ id: string; title: string }, [number]>(
|
|
140
|
-
"SELECT id, title FROM issues WHERE status = 'in_progress' ORDER BY updated_at DESC LIMIT ?",
|
|
141
|
-
)
|
|
142
|
-
.all(MAX_BEADS);
|
|
143
|
-
return rows.length > 0
|
|
144
|
-
? rows.map((r) => `- ${r.id}: ${r.title}`).join("\n")
|
|
145
|
-
: "";
|
|
146
|
-
} catch (error) {
|
|
147
|
-
const code = String((error as { code?: string } | undefined)?.code ?? "");
|
|
148
|
-
const message = String(
|
|
149
|
-
(error as { message?: string } | undefined)?.message ?? error,
|
|
150
|
-
);
|
|
151
|
-
const isRetryable =
|
|
152
|
-
code.includes("SQLITE_BUSY") ||
|
|
153
|
-
code.includes("SQLITE_LOCKED") ||
|
|
154
|
-
message.includes("SQLITE_BUSY") ||
|
|
155
|
-
message.includes("SQLITE_LOCKED");
|
|
156
|
-
|
|
157
|
-
if (isRetryable && attempt < BEADS_DB_READ_ATTEMPTS - 1) {
|
|
158
|
-
await delay(BEADS_DB_RETRY_DELAY_MS);
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (isRetryable) {
|
|
163
|
-
console.warn(
|
|
164
|
-
`[memory] Failed to read beads.db after ${BEADS_DB_READ_ATTEMPTS} attempts: ${message}`,
|
|
165
|
-
);
|
|
166
|
-
} else {
|
|
167
|
-
console.warn(
|
|
168
|
-
`[memory] Failed to read beads.db, continuing without bead context: ${message}`,
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
return "";
|
|
172
|
-
} finally {
|
|
173
|
-
db?.close();
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return "";
|
|
178
|
-
}
|
|
179
|
-
|
|
180
54
|
// ============================================================================
|
|
181
55
|
// Tool Helpers
|
|
182
56
|
// ============================================================================
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
* - message.updated, message.removed, message.part.updated, message.part.removed
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import path from "node:path";
|
|
18
17
|
import { captureMessageMeta, captureMessagePart } from "./capture.js";
|
|
19
18
|
import { manageContext } from "./context.js";
|
|
20
19
|
import { curateFromDistillations } from "./curator.js";
|
|
@@ -25,23 +24,9 @@ import {
|
|
|
25
24
|
checkpointWAL,
|
|
26
25
|
getDatabaseSizes,
|
|
27
26
|
optimizeFTS5,
|
|
28
|
-
searchObservationsFTS,
|
|
29
27
|
} from "./memory-db.js";
|
|
30
|
-
import {
|
|
31
|
-
MAX_COMBINED_CONTEXT_CHARS,
|
|
32
|
-
MAX_SESSION_CONTEXT_CHARS,
|
|
33
|
-
readInProgressBeads,
|
|
34
|
-
readLatestHandoff,
|
|
35
|
-
readProjectMemoryContext,
|
|
36
|
-
renderSection,
|
|
37
|
-
safeReadFile,
|
|
38
|
-
truncate,
|
|
39
|
-
} from "./memory-helpers.js";
|
|
40
28
|
|
|
41
29
|
interface HookDeps {
|
|
42
|
-
memoryDir: string;
|
|
43
|
-
handoffDir: string;
|
|
44
|
-
directory: string;
|
|
45
30
|
showToast: (
|
|
46
31
|
title: string,
|
|
47
32
|
message: string,
|
|
@@ -51,7 +36,7 @@ interface HookDeps {
|
|
|
51
36
|
}
|
|
52
37
|
|
|
53
38
|
export function createHooks(deps: HookDeps) {
|
|
54
|
-
const {
|
|
39
|
+
const { showToast, log } = deps;
|
|
55
40
|
|
|
56
41
|
return {
|
|
57
42
|
// ================================================================
|
|
@@ -167,52 +152,11 @@ export function createHooks(deps: HookDeps) {
|
|
|
167
152
|
// Receives: (input: { sessionID }, output: { context, prompt? })
|
|
168
153
|
// ================================================================
|
|
169
154
|
"experimental.session.compacting": async (
|
|
170
|
-
|
|
155
|
+
_input: { sessionID?: string },
|
|
171
156
|
output: { context: string[]; prompt?: string },
|
|
172
157
|
) => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
MAX_SESSION_CONTEXT_CHARS,
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
const [projectContext, handoffContext] = await Promise.all([
|
|
179
|
-
readProjectMemoryContext(memoryDir),
|
|
180
|
-
readLatestHandoff(handoffDir),
|
|
181
|
-
]);
|
|
182
|
-
|
|
183
|
-
const beadsContext = await readInProgressBeads(directory);
|
|
184
|
-
|
|
185
|
-
// Add relevant observations for session
|
|
186
|
-
let knowledgeContext = "";
|
|
187
|
-
if (input.sessionID) {
|
|
188
|
-
try {
|
|
189
|
-
const recentObs = searchObservationsFTS("", { limit: 5 });
|
|
190
|
-
if (recentObs.length > 0) {
|
|
191
|
-
knowledgeContext = recentObs
|
|
192
|
-
.map((o) => `- [${o.type}] #${o.id}: ${o.title}`)
|
|
193
|
-
.join("\n");
|
|
194
|
-
}
|
|
195
|
-
} catch {
|
|
196
|
-
/* Non-fatal */
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const combined = [
|
|
201
|
-
renderSection("Session Continuity", sessionContext),
|
|
202
|
-
renderSection("Active Beads", beadsContext),
|
|
203
|
-
renderSection("Previous Handoff", handoffContext),
|
|
204
|
-
renderSection("Recent Knowledge", knowledgeContext),
|
|
205
|
-
renderSection("Project Memory", projectContext),
|
|
206
|
-
]
|
|
207
|
-
.filter(Boolean)
|
|
208
|
-
.join("\n\n");
|
|
209
|
-
|
|
210
|
-
if (combined) {
|
|
211
|
-
output.context.push(
|
|
212
|
-
`## Session Context\n${truncate(combined, MAX_COMBINED_CONTEXT_CHARS)}\n`,
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
158
|
+
// No context injection here — the session is already at the
|
|
159
|
+
// model limit when compaction fires. Only append prompt guidance.
|
|
216
160
|
output.prompt = `${output.prompt ?? ""}
|
|
217
161
|
|
|
218
162
|
<compaction_task>
|
|
@@ -12,6 +12,8 @@ dependencies: []
|
|
|
12
12
|
|
|
13
13
|
# Agent Teams - Multi-Agent Team Coordination
|
|
14
14
|
|
|
15
|
+
> **Replaces** single-agent sequential work when tasks benefit from parallel research, review, or competing hypotheses
|
|
16
|
+
|
|
15
17
|
## When to Use
|
|
16
18
|
|
|
17
19
|
- Parallel research, review, or competing approaches that need coordination
|
|
@@ -22,7 +24,6 @@ dependencies: []
|
|
|
22
24
|
- Single-agent tasks or tightly coupled edits where coordination overhead is wasteful
|
|
23
25
|
- Simple parallel work that can use fire-and-forget subagents instead
|
|
24
26
|
|
|
25
|
-
|
|
26
27
|
## Overview
|
|
27
28
|
|
|
28
29
|
**Agent Teams = Lead + Teammates + Shared Task List + Messaging**
|
|
@@ -53,6 +54,15 @@ Need shared findings? → Agent Teams
|
|
|
53
54
|
Simple parallel execution? → Subagents (Task tool)
|
|
54
55
|
```
|
|
55
56
|
|
|
57
|
+
### Parallel Skill Selection
|
|
58
|
+
|
|
59
|
+
| Scenario | Use This Skill |
|
|
60
|
+
| ------------------------------------------- | --------------------------- |
|
|
61
|
+
| 3+ independent bug investigations | dispatching-parallel-agents |
|
|
62
|
+
| Coordinated team (research + review + impl) | agent-teams |
|
|
63
|
+
| Large plan with dependency graph | swarm-coordination |
|
|
64
|
+
| 2 independent tasks | Just use 2 Task() calls |
|
|
65
|
+
|
|
56
66
|
## When to Use
|
|
57
67
|
|
|
58
68
|
- **Parallel research**: Multiple agents researching different aspects of a problem
|
|
@@ -251,3 +261,8 @@ Before dispatching a team:
|
|
|
251
261
|
- [ ] Each task has acceptance criteria (verification commands)
|
|
252
262
|
- [ ] Lead has a synthesis plan (how to integrate results)
|
|
253
263
|
- [ ] Tasks are sized appropriately (5-6 per teammate max)
|
|
264
|
+
|
|
265
|
+
## See Also
|
|
266
|
+
|
|
267
|
+
- `dispatching-parallel-agents` — for independent debugging-focused parallel investigations
|
|
268
|
+
- `swarm-coordination` — for dependency-aware large-plan execution
|
|
@@ -12,6 +12,7 @@ dependencies: []
|
|
|
12
12
|
|
|
13
13
|
# Beads Workflow - Multi-Agent Task Coordination
|
|
14
14
|
|
|
15
|
+
> **Replaces** ad-hoc task tracking with sticky notes, TODO comments, or mental checklists that lose state between sessions
|
|
15
16
|
## When to Use
|
|
16
17
|
|
|
17
18
|
- Coordinating multi-session work with dependencies, blockers, or file locking needs
|
|
@@ -110,6 +111,22 @@ See: `references/MULTI_AGENT.md` for swarm tool usage and examples.
|
|
|
110
111
|
5. **Write notes for future agents** - Assume zero conversation context
|
|
111
112
|
6. **Claim file paths before editing** - Use reserve to declare ownership (multi-agent only)
|
|
112
113
|
|
|
114
|
+
## Anti-Patterns
|
|
115
|
+
|
|
116
|
+
| Anti-Pattern | Why It Fails | Instead |
|
|
117
|
+
| --- | --- | --- |
|
|
118
|
+
| Claiming a bead without reading its current state first (`br show`) | Misses dependencies, blockers, and prior context | Run `br show <id>` before `br update <id> --status in_progress` |
|
|
119
|
+
| Closing a bead without verification evidence | Marks incomplete or broken work as done | Run verification commands and capture output before `br close` |
|
|
120
|
+
| Working on blocked beads (dependencies not met) | Wastes time and causes out-of-order delivery | Use `br ready` and confirm dependencies in `br show <id>` |
|
|
121
|
+
| Modifying bead state without user confirmation | Violates workflow expectations and can surprise collaborators | Ask before changing bead status, especially close/sync actions |
|
|
122
|
+
| Using `br sync` without `--flush-only` (can cause conflicts) | May write unexpected state and increase sync conflict risk | Always use `br sync --flush-only` then commit `.beads/` manually |
|
|
123
|
+
|
|
124
|
+
## Verification
|
|
125
|
+
|
|
126
|
+
- **Before closing:** run verification commands, paste output as evidence
|
|
127
|
+
- **After close:** `br show <id>` confirms `status=closed`
|
|
128
|
+
- **After sync:** `git status` shows clean working tree
|
|
129
|
+
|
|
113
130
|
## File Path Claiming (Summary)
|
|
114
131
|
|
|
115
132
|
Claim files before editing in multi-agent work using `br reserve <id> --files "..."`.
|
|
@@ -157,3 +174,8 @@ MAINTENANCE:
|
|
|
157
174
|
- `references/MULTI_AGENT.md`
|
|
158
175
|
- `references/FILE_CLAIMING.md`
|
|
159
176
|
- `references/BEST_PRACTICES.md`
|
|
177
|
+
|
|
178
|
+
## See Also
|
|
179
|
+
|
|
180
|
+
- `verification-before-completion`
|
|
181
|
+
- `beads-bridge`
|
|
@@ -8,6 +8,7 @@ dependencies: []
|
|
|
8
8
|
|
|
9
9
|
# Brainstorming Ideas Into Designs
|
|
10
10
|
|
|
11
|
+
> **Replaces** jumping straight to implementation without exploring alternatives, constraints, or edge cases
|
|
11
12
|
## When to Use
|
|
12
13
|
|
|
13
14
|
- You have a rough idea that needs clarification into a design or spec
|
|
@@ -84,3 +85,30 @@ Start by understanding the current project context, then ask questions one at a
|
|
|
84
85
|
- **Explore alternatives** - Always propose 2-3 approaches before settling
|
|
85
86
|
- **Incremental validation** - Present design in sections, validate each
|
|
86
87
|
- **Be flexible** - Go back and clarify when something doesn't make sense
|
|
88
|
+
|
|
89
|
+
## Example Flow
|
|
90
|
+
|
|
91
|
+
**User request**: "Add dark mode to the app"
|
|
92
|
+
|
|
93
|
+
**Good brainstorming questions**:
|
|
94
|
+
1. "Should dark mode be system-preference-aware, manual toggle, or both?"
|
|
95
|
+
2. "Where does theme state live — CSS variables, React context, localStorage?"
|
|
96
|
+
3. "Are there existing color tokens, or do we need to create a design token system?"
|
|
97
|
+
4. "What about images/icons — do they need dark variants?"
|
|
98
|
+
|
|
99
|
+
**Bad brainstorming** (jumping to solution):
|
|
100
|
+
1. "I'll add a ThemeContext with useState and toggle button" ← skipped alternatives
|
|
101
|
+
|
|
102
|
+
## Anti-Patterns
|
|
103
|
+
|
|
104
|
+
| Anti-Pattern | Why It Fails | Instead |
|
|
105
|
+
| --- | --- | --- |
|
|
106
|
+
| Asking questions the codebase can answer (search first) | Wastes turns and slows decisions; signals weak preparation | Do quick repo/docs lookup first, then ask only unresolved questions |
|
|
107
|
+
| Brainstorming during mechanical/routine tasks | Adds overhead when execution is already clear | Skip to execution using the relevant implementation skill |
|
|
108
|
+
| Generating 10+ alternatives without narrowing criteria | Creates analysis paralysis and no decision pressure | Present 2-3 viable options with explicit decision criteria |
|
|
109
|
+
| Continuing to brainstorm after a clear direction emerges | Burns time and erodes momentum | Confirm direction, summarize decisions, transition to PRD/plan |
|
|
110
|
+
|
|
111
|
+
## See Also
|
|
112
|
+
|
|
113
|
+
- `writing-plans` - Turn validated direction into zero-ambiguity implementation tasks
|
|
114
|
+
- `prd` - Capture behavioral requirements before implementation
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-navigation
|
|
3
|
+
description: Use when navigating unfamiliar code, tracing cross-file dependencies, or before editing — efficient code reading patterns that minimize tool calls and token waste
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags: [workflow, code-quality, context]
|
|
6
|
+
dependencies: []
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Code Navigation Skill
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- Exploring an unfamiliar codebase or module
|
|
14
|
+
- Tracing a function call across multiple files
|
|
15
|
+
- Understanding blast radius before a breaking change
|
|
16
|
+
- Planning edits that touch multiple files
|
|
17
|
+
|
|
18
|
+
## When NOT to Use
|
|
19
|
+
|
|
20
|
+
- Simple single-file edits where you already know the location
|
|
21
|
+
- Reading config or documentation files
|
|
22
|
+
|
|
23
|
+
## Core Principle
|
|
24
|
+
|
|
25
|
+
> Collapse multiple tool calls into fewer, smarter ones. Every unnecessary read or search wastes tokens and turns.
|
|
26
|
+
|
|
27
|
+
## Navigation Patterns
|
|
28
|
+
|
|
29
|
+
### Pattern 1: Search First, Read Second
|
|
30
|
+
|
|
31
|
+
**Wrong** (3-6 tool calls):
|
|
32
|
+
```
|
|
33
|
+
glob("*.ts") → read(file1) → "too big" → grep("functionName") → read(file2) → read(file3, section)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Right** (1-2 tool calls):
|
|
37
|
+
```
|
|
38
|
+
grep("functionName", path: "src/") → read(exact_file, offset: line-10, limit: 30)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Start with search (grep, LSP findReferences) to locate, then read only what you need.
|
|
42
|
+
|
|
43
|
+
### Pattern 2: Multi-Symbol Search
|
|
44
|
+
|
|
45
|
+
When tracing a call chain (A calls B calls C), search for all symbols together:
|
|
46
|
+
```
|
|
47
|
+
grep({ pattern: "functionA|functionB|functionC", path: "src/" })
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or use LSP outgoingCalls to get the full call tree from a single point.
|
|
51
|
+
|
|
52
|
+
### Pattern 3: Don't Re-Read What You've Already Seen
|
|
53
|
+
|
|
54
|
+
**Anti-pattern**: Search returns full function body, then agent reads the same file again.
|
|
55
|
+
|
|
56
|
+
If search results already show the code you need, work from that output. Only re-read when:
|
|
57
|
+
- You need surrounding context (lines above/below the match)
|
|
58
|
+
- You need the exact content for editing (verify before edit)
|
|
59
|
+
- The search result was truncated
|
|
60
|
+
|
|
61
|
+
### Pattern 4: Blast Radius Check (Before Breaking Changes)
|
|
62
|
+
|
|
63
|
+
**WHEN**: Before renaming, removing, or changing the signature of an export.
|
|
64
|
+
**SKIP**: When adding new code, fixing internal bugs, or reading.
|
|
65
|
+
|
|
66
|
+
Steps:
|
|
67
|
+
1. `lsp({ operation: "findReferences" })` — find all callers
|
|
68
|
+
2. `lsp({ operation: "incomingCalls" })` — get the call hierarchy
|
|
69
|
+
3. Review each caller to assess impact
|
|
70
|
+
4. Plan edits from leaf callers inward (furthest dependencies first)
|
|
71
|
+
|
|
72
|
+
### Pattern 5: Context Locality
|
|
73
|
+
|
|
74
|
+
When editing a file, search results from the same directory/package are more likely relevant. Pass context when available:
|
|
75
|
+
- In grep: use `path: "src/same-module/"` to scope
|
|
76
|
+
- In LSP: operations are already file-scoped
|
|
77
|
+
- In tilth: pass `context` param to boost nearby results
|
|
78
|
+
|
|
79
|
+
### Pattern 6: Outline Before Deep Read
|
|
80
|
+
|
|
81
|
+
For large files (>200 lines), get the structure first:
|
|
82
|
+
```
|
|
83
|
+
lsp({ operation: "documentSymbol", filePath: "src/large-file.ts", line: 1, character: 1 })
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This gives you function names + line ranges. Then read only the section you need with `offset` and `limit`.
|
|
87
|
+
|
|
88
|
+
### Pattern 7: Follow the Call Chain (Not the File Tree)
|
|
89
|
+
|
|
90
|
+
**Wrong**: Read files top-to-bottom hoping to understand the flow.
|
|
91
|
+
**Right**: Start from the entry point, follow function calls:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
1. lsp({ operation: "goToDefinition" }) → find where it's defined
|
|
95
|
+
2. lsp({ operation: "outgoingCalls" }) → what does it call?
|
|
96
|
+
3. lsp({ operation: "goToDefinition" }) → follow the interesting callee
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## With tilth MCP
|
|
100
|
+
|
|
101
|
+
When tilth is available, it provides superior navigation:
|
|
102
|
+
|
|
103
|
+
| Built-in Tool | tilth Equivalent | Advantage |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `grep` + `read` | `tilth_search` (expand: 2) | Returns definitions with inline source — no second read needed |
|
|
106
|
+
| `glob` | `tilth_files` | Adds token estimates per file |
|
|
107
|
+
| `read` (large file) | `tilth_read` | Auto-outlines large files, shows structure |
|
|
108
|
+
| `lsp(incomingCalls)` | `tilth_search(kind: "callers")` | Cross-language structural caller detection |
|
|
109
|
+
| Manual tracing | `tilth_deps` | Shows imports + downstream callers before breaking changes |
|
|
110
|
+
|
|
111
|
+
**IMPORTANT**: If tilth is available, prefer it over built-in grep/glob/read for code navigation. Tilth's expanded search results include full source — do NOT re-read files already shown in search output.
|
|
112
|
+
|
|
113
|
+
## Cost Awareness
|
|
114
|
+
|
|
115
|
+
Every tool call has a token cost. Efficient navigation means:
|
|
116
|
+
- Fewer tool calls per task
|
|
117
|
+
- Less context consumed by redundant reads
|
|
118
|
+
- More budget available for actual implementation
|
|
119
|
+
|
|
120
|
+
**Target**: Find and understand any symbol in ≤3 tool calls, not 6+.
|
|
121
|
+
|
|
122
|
+
## Common Mistakes
|
|
123
|
+
|
|
124
|
+
| Mistake | Fix |
|
|
125
|
+
|---|---|
|
|
126
|
+
| Read entire large file | Use outline first, then section read |
|
|
127
|
+
| Search → read same code again | Work from search results directly |
|
|
128
|
+
| Trace calls one-by-one | Multi-symbol search or outgoingCalls |
|
|
129
|
+
| Explore randomly | Start from entry point, follow calls |
|
|
130
|
+
| Forget to check blast radius | Always check before signature changes |
|
|
@@ -8,6 +8,7 @@ dependencies: []
|
|
|
8
8
|
|
|
9
9
|
# Condition-Based Waiting
|
|
10
10
|
|
|
11
|
+
> **Replaces** arbitrary `sleep()` / `setTimeout()` calls and hardcoded delays that cause flaky tests and slow CI
|
|
11
12
|
## When to Use
|
|
12
13
|
|
|
13
14
|
- Tests are flaky due to arbitrary delays or timing guesses
|
|
@@ -101,6 +102,12 @@ await new Promise((r) => setTimeout(r, 200)); // Then: wait for timed behavior
|
|
|
101
102
|
2. Based on known timing (not guessing)
|
|
102
103
|
3. Comment explaining WHY
|
|
103
104
|
|
|
105
|
+
## Verification
|
|
106
|
+
|
|
107
|
+
- **After applying:** run the previously flaky test 5+ times — should pass consistently
|
|
108
|
+
- **Check:** no hardcoded sleep/delay values remain in the test file
|
|
109
|
+
- **Measure:** test execution time should decrease (no wasted wait time)
|
|
110
|
+
|
|
104
111
|
## Real-World Impact
|
|
105
112
|
|
|
106
113
|
From debugging session (2025-10-03):
|
|
@@ -109,3 +116,8 @@ From debugging session (2025-10-03):
|
|
|
109
116
|
- Pass rate: 60% → 100%
|
|
110
117
|
- Execution time: 40% faster
|
|
111
118
|
- No more race conditions
|
|
119
|
+
|
|
120
|
+
## See Also
|
|
121
|
+
|
|
122
|
+
- `systematic-debugging`
|
|
123
|
+
- `test-driven-development`
|