opencodekit 0.18.4 → 0.18.6

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 (43) hide show
  1. package/dist/index.js +491 -47
  2. package/dist/template/.opencode/AGENTS.md +13 -1
  3. package/dist/template/.opencode/agent/build.md +4 -1
  4. package/dist/template/.opencode/agent/explore.md +25 -58
  5. package/dist/template/.opencode/command/ship.md +7 -5
  6. package/dist/template/.opencode/command/verify.md +63 -12
  7. package/dist/template/.opencode/memory/research/benchmark-framework.md +162 -0
  8. package/dist/template/.opencode/memory/research/effectiveness-audit.md +213 -0
  9. package/dist/template/.opencode/memory.db +0 -0
  10. package/dist/template/.opencode/memory.db-shm +0 -0
  11. package/dist/template/.opencode/memory.db-wal +0 -0
  12. package/dist/template/.opencode/opencode.json +1429 -1678
  13. package/dist/template/.opencode/package.json +1 -1
  14. package/dist/template/.opencode/plugin/lib/memory-helpers.ts +3 -129
  15. package/dist/template/.opencode/plugin/lib/memory-hooks.ts +4 -60
  16. package/dist/template/.opencode/plugin/memory.ts +0 -3
  17. package/dist/template/.opencode/skill/agent-teams/SKILL.md +16 -1
  18. package/dist/template/.opencode/skill/beads/SKILL.md +22 -0
  19. package/dist/template/.opencode/skill/brainstorming/SKILL.md +28 -0
  20. package/dist/template/.opencode/skill/code-navigation/SKILL.md +130 -0
  21. package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +12 -0
  22. package/dist/template/.opencode/skill/context-management/SKILL.md +122 -113
  23. package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +20 -0
  24. package/dist/template/.opencode/skill/design-system-audit/SKILL.md +113 -112
  25. package/dist/template/.opencode/skill/dispatching-parallel-agents/SKILL.md +8 -0
  26. package/dist/template/.opencode/skill/executing-plans/SKILL.md +156 -132
  27. package/dist/template/.opencode/skill/memory-system/SKILL.md +50 -266
  28. package/dist/template/.opencode/skill/mockup-to-code/SKILL.md +21 -6
  29. package/dist/template/.opencode/skill/receiving-code-review/SKILL.md +8 -0
  30. package/dist/template/.opencode/skill/root-cause-tracing/SKILL.md +15 -0
  31. package/dist/template/.opencode/skill/session-management/SKILL.md +4 -103
  32. package/dist/template/.opencode/skill/subagent-driven-development/SKILL.md +23 -2
  33. package/dist/template/.opencode/skill/swarm-coordination/SKILL.md +17 -1
  34. package/dist/template/.opencode/skill/systematic-debugging/SKILL.md +21 -0
  35. package/dist/template/.opencode/skill/tool-priority/SKILL.md +34 -16
  36. package/dist/template/.opencode/skill/ui-ux-research/SKILL.md +5 -127
  37. package/dist/template/.opencode/skill/verification-before-completion/SKILL.md +36 -0
  38. package/dist/template/.opencode/skill/verification-before-completion/references/VERIFICATION_PROTOCOL.md +133 -29
  39. package/dist/template/.opencode/skill/visual-analysis/SKILL.md +20 -7
  40. package/dist/template/.opencode/skill/writing-plans/SKILL.md +7 -0
  41. package/dist/template/.opencode/tool/context7.ts +9 -1
  42. package/dist/template/.opencode/tool/grepsearch.ts +9 -1
  43. package/package.json +1 -1
@@ -11,7 +11,7 @@
11
11
  "type-check": "tsc --noEmit"
12
12
  },
13
13
  "dependencies": {
14
- "@opencode-ai/plugin": "1.2.20"
14
+ "@opencode-ai/plugin": "1.2.21"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/node": "^25.3.0",
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * Memory Plugin — Helpers
3
3
  *
4
- * Constants, compaction utilities, and tool formatting helpers.
4
+ * Constants, tool formatting helpers, and file utilities.
5
5
  * Pure functions — no plugin/closure dependencies.
6
6
  */
7
7
 
8
- import { Database } from "bun:sqlite";
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
- // Compaction Helpers
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 { memoryDir, handoffDir, directory, showToast, log } = deps;
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
- input: { sessionID?: string },
155
+ _input: { sessionID?: string },
171
156
  output: { context: string[]; prompt?: string },
172
157
  ) => {
173
- const sessionContext = truncate(
174
- (await safeReadFile(path.join(memoryDir, "session-context.md"))).trim(),
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>
@@ -73,9 +73,6 @@ export const MemoryPlugin: Plugin = async ({ client, directory }) => {
73
73
 
74
74
  // Assemble hooks
75
75
  const hooks = createHooks({
76
- memoryDir,
77
- handoffDir,
78
- directory,
79
76
  showToast,
80
77
  log,
81
78
  });
@@ -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`