opencode-debug-agent 0.1.0 → 0.1.1

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 (3) hide show
  1. package/dist/index.js +119 -64
  2. package/package.json +1 -1
  3. package/src/index.ts +125 -96
package/dist/index.js CHANGED
@@ -10,9 +10,6 @@ var __export = (target, all) => {
10
10
  });
11
11
  };
12
12
 
13
- // src/index.ts
14
- import path from "path";
15
-
16
13
  // node_modules/zod/v4/classic/external.js
17
14
  var exports_external = {};
18
15
  __export(exports_external, {
@@ -14176,60 +14173,124 @@ var debugStatus = tool({
14176
14173
  });
14177
14174
 
14178
14175
  // src/index.ts
14179
- function parseSkillFrontmatter(content) {
14180
- const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
14181
- const match2 = content.match(frontmatterRegex);
14182
- if (!match2) {
14183
- return { frontmatter: { name: "", description: "" }, body: content.trim() };
14184
- }
14185
- const [, yamlContent, body] = match2;
14186
- const frontmatter = { name: "", description: "" };
14187
- for (const line of yamlContent.split(`
14188
- `)) {
14189
- const colonIndex = line.indexOf(":");
14190
- if (colonIndex === -1)
14191
- continue;
14192
- const key = line.slice(0, colonIndex).trim();
14193
- const value = line.slice(colonIndex + 1).trim();
14194
- if (key === "name")
14195
- frontmatter.name = value;
14196
- if (key === "description")
14197
- frontmatter.description = value;
14198
- }
14199
- return { frontmatter, body: body.trim() };
14200
- }
14201
- async function loadSkills() {
14202
- const skills = [];
14203
- const skillDir = path.join(import.meta.dir, "skill");
14204
- const glob = new Bun.Glob("**/SKILL.md");
14205
- try {
14206
- for await (const file2 of glob.scan({ cwd: skillDir, absolute: true })) {
14207
- const content = await Bun.file(file2).text();
14208
- const { frontmatter, body } = parseSkillFrontmatter(content);
14209
- if (frontmatter.name) {
14210
- skills.push({
14211
- name: frontmatter.name,
14212
- description: frontmatter.description,
14213
- content: body
14214
- });
14215
- }
14216
- }
14217
- } catch {}
14218
- return skills;
14219
- }
14220
- async function loadAgentPrompt() {
14221
- try {
14222
- const agentFile = path.join(import.meta.dir, "agent", "debug.md");
14223
- const content = await Bun.file(agentFile).text();
14224
- const match2 = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
14225
- return match2 ? match2[1].trim() : content;
14226
- } catch {
14227
- return "You are a debugging specialist.";
14228
- }
14229
- }
14176
+ var AGENT_PROMPT = `You are a debugging specialist. Your purpose is to help users debug runtime issues by capturing and analyzing execution data.
14177
+
14178
+ ## IMPORTANT: Port Handling
14179
+ - \`debug_start\` returns a ready-to-use \`snippet\` with the correct port baked in
14180
+ - ALWAYS use the snippet from the tool response - never hardcode ports
14181
+ - If you need the current port later, call \`debug_status\`
14182
+ - The server remembers its port across sessions, so existing instrumentations keep working
14183
+
14184
+ ## Workflow
14185
+
14186
+ 1. Call \`debug_start\` to start the debug server
14187
+ 2. Use the returned \`snippet\` to insert fetch() calls at strategic locations in the user's code
14188
+ - Replace \`LABEL_HERE\` with a descriptive label (e.g., "before-api-call", "user-input", "loop-iteration")
14189
+ - Replace \`YOUR_DATA\` with the variables you want to capture
14190
+ 3. Ask user to reproduce the issue
14191
+ 4. Call \`debug_read\` to analyze captured logs
14192
+ 5. Identify the problem from the runtime data
14193
+ 6. Call \`debug_stop\` when done
14194
+ 7. Remove all fetch() instrumentation calls from the code
14195
+
14196
+ ## If Instrumentations Already Exist
14197
+ - Call \`debug_status\` first to check for existing port
14198
+ - Use \`grep\` to find existing \`localhost:\\d+/log\` patterns in codebase
14199
+ - Start server on the same port to avoid breaking existing instrumentations
14200
+
14201
+ ## Example Instrumentation
14202
+
14203
+ After calling \`debug_start\`, you'll get a snippet like:
14204
+ \`\`\`javascript
14205
+ fetch("http://localhost:54321/log", {
14206
+ method: "POST",
14207
+ headers: {"Content-Type": "application/json"},
14208
+ body: JSON.stringify({label: "LABEL_HERE", data: {YOUR_DATA}})
14209
+ })
14210
+ \`\`\`
14211
+
14212
+ Insert this at strategic points:
14213
+ \`\`\`javascript
14214
+ // Before an API call
14215
+ fetch("http://localhost:54321/log", {
14216
+ method: "POST",
14217
+ headers: {"Content-Type": "application/json"},
14218
+ body: JSON.stringify({label: "pre-api", data: {userId, requestBody}})
14219
+ })
14220
+
14221
+ // After receiving response
14222
+ fetch("http://localhost:54321/log", {
14223
+ method: "POST",
14224
+ headers: {"Content-Type": "application/json"},
14225
+ body: JSON.stringify({label: "post-api", data: {response, status}})
14226
+ })
14227
+ \`\`\`
14228
+
14229
+ ## Tips
14230
+ - Use descriptive labels to make logs easy to understand
14231
+ - Capture relevant variables at each point
14232
+ - Add instrumentation around suspected problem areas
14233
+ - Compare expected vs actual values in the logs`;
14234
+ var DEBUG_SKILL = {
14235
+ name: "debug",
14236
+ description: "Runtime debugging - start a debug server, instrument code with fetch() calls, capture and analyze execution data",
14237
+ content: `## CRITICAL: Port Handling
14238
+ - \`debug_start\` returns a \`snippet\` with the correct port - ALWAYS use it
14239
+ - Never hardcode ports in fetch() calls
14240
+ - Call \`debug_status\` to get current port if needed later
14241
+ - Server persists port to \`.opencode/debug.port\` so existing instrumentations keep working
14242
+
14243
+ ## Workflow
14244
+
14245
+ 1. \`debug_start\` - Returns {port, url, snippet}
14246
+ 2. Insert the returned \`snippet\` at strategic code locations:
14247
+ - Replace \`LABEL_HERE\` with descriptive label (e.g., "before-api", "after-parse")
14248
+ - Replace \`YOUR_DATA\` with variables to capture (e.g., \`{userId, response}\`)
14249
+ 3. Ask user to reproduce the issue
14250
+ 4. \`debug_read\` - Analyze captured logs (returns parsed JSON array)
14251
+ 5. \`debug_stop\` - Stop server when done
14252
+ 6. Remove all fetch() instrumentation calls from the code
14253
+
14254
+ ## Before Starting - Check for Existing Instrumentations
14255
+ 1. Call \`debug_status\` - check if server already running or port persisted
14256
+ 2. Use grep to search for \`localhost:\\d+/log\` patterns in codebase
14257
+ 3. If found, ensure server starts on same port to avoid breaking existing code
14258
+
14259
+ ## Tools Reference
14260
+
14261
+ | Tool | Args | Returns |
14262
+ |------|------|---------|
14263
+ | \`debug_start\` | \`port?\` | \`{port, url, snippet, message}\` |
14264
+ | \`debug_stop\` | - | confirmation message |
14265
+ | \`debug_read\` | \`tail?\` | \`{entries: [{timestamp, label, data}, ...], count}\` |
14266
+ | \`debug_clear\` | - | confirmation message |
14267
+ | \`debug_status\` | - | \`{active, port?, url?, persistedPort?, hint?}\` |
14268
+
14269
+ ## Example Session
14270
+
14271
+ \`\`\`
14272
+ > debug_start
14273
+ {port: 54321, url: "http://localhost:54321", snippet: "fetch(...)"}
14274
+
14275
+ > [Insert snippet at line 42 and 67 in user's code]
14276
+
14277
+ > [User reproduces the issue]
14278
+
14279
+ > debug_read
14280
+ {entries: [
14281
+ {timestamp: "...", label: "before-api", data: {userId: 123}},
14282
+ {timestamp: "...", label: "after-api", data: {error: "timeout"}}
14283
+ ], count: 2}
14284
+
14285
+ > [Analyze: API call is timing out]
14286
+
14287
+ > debug_stop
14288
+ {message: "Debug server stopped."}
14289
+
14290
+ > [Remove instrumentation from lines 42 and 67]
14291
+ \`\`\``
14292
+ };
14230
14293
  var DebugAgentPlugin = async () => {
14231
- const skills = await loadSkills();
14232
- const agentPrompt = await loadAgentPrompt();
14233
14294
  return {
14234
14295
  tool: {
14235
14296
  debug_start: debugStart,
@@ -14243,17 +14304,11 @@ var DebugAgentPlugin = async () => {
14243
14304
  config2.agent["debug"] = {
14244
14305
  description: "Runtime debugging - capture and analyze execution data",
14245
14306
  mode: "primary",
14246
- prompt: agentPrompt
14307
+ prompt: AGENT_PROMPT
14247
14308
  };
14248
14309
  const configWithSkill = config2;
14249
14310
  configWithSkill.skill = configWithSkill.skill ?? {};
14250
- for (const skill of skills) {
14251
- configWithSkill.skill[skill.name] = {
14252
- name: skill.name,
14253
- description: skill.description,
14254
- content: skill.content
14255
- };
14256
- }
14311
+ configWithSkill.skill[DEBUG_SKILL.name] = DEBUG_SKILL;
14257
14312
  }
14258
14313
  };
14259
14314
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-debug-agent",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "OpenCode plugin for runtime debugging - capture and analyze execution data",
5
5
  "author": {
6
6
  "name": "anis00723",
package/src/index.ts CHANGED
@@ -9,102 +9,137 @@
9
9
  */
10
10
 
11
11
  import type { Plugin } from '@opencode-ai/plugin';
12
- import path from 'path';
13
12
  import { debugStart, debugStop, debugRead, debugClear, debugStatus } from './tools';
14
13
 
15
14
  // ============================================================
16
- // SKILL LOADER
17
- // Loads SKILL.md files from src/skill/ directory
15
+ // EMBEDDED CONTENT (to avoid file loading issues in npm packages)
18
16
  // ============================================================
19
17
 
20
- interface SkillFrontmatter {
21
- name: string;
22
- description: string;
23
- }
24
-
25
- interface ParsedSkill {
26
- name: string;
27
- description: string;
28
- content: string;
29
- }
30
-
31
- /**
32
- * Parse YAML frontmatter from a skill file
33
- */
34
- function parseSkillFrontmatter(content: string): { frontmatter: SkillFrontmatter; body: string } {
35
- const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
36
- const match = content.match(frontmatterRegex);
37
-
38
- if (!match) {
39
- return { frontmatter: { name: '', description: '' }, body: content.trim() };
40
- }
41
-
42
- const [, yamlContent, body] = match;
43
- const frontmatter: SkillFrontmatter = { name: '', description: '' };
44
-
45
- for (const line of yamlContent.split('\n')) {
46
- const colonIndex = line.indexOf(':');
47
- if (colonIndex === -1) continue;
48
-
49
- const key = line.slice(0, colonIndex).trim();
50
- const value = line.slice(colonIndex + 1).trim();
51
-
52
- if (key === 'name') frontmatter.name = value;
53
- if (key === 'description') frontmatter.description = value;
54
- }
55
-
56
- return { frontmatter, body: body.trim() };
57
- }
58
-
59
- /**
60
- * Load all skill files from the skill directory
61
- */
62
- async function loadSkills(): Promise<ParsedSkill[]> {
63
- const skills: ParsedSkill[] = [];
64
- const skillDir = path.join(import.meta.dir, 'skill');
65
- const glob = new Bun.Glob('**/SKILL.md');
66
-
67
- try {
68
- for await (const file of glob.scan({ cwd: skillDir, absolute: true })) {
69
- const content = await Bun.file(file).text();
70
- const { frontmatter, body } = parseSkillFrontmatter(content);
71
-
72
- if (frontmatter.name) {
73
- skills.push({
74
- name: frontmatter.name,
75
- description: frontmatter.description,
76
- content: body,
77
- });
78
- }
79
- }
80
- } catch {
81
- // Skill directory may not exist yet
82
- }
83
-
84
- return skills;
85
- }
18
+ const AGENT_PROMPT = `You are a debugging specialist. Your purpose is to help users debug runtime issues by capturing and analyzing execution data.
19
+
20
+ ## IMPORTANT: Port Handling
21
+ - \`debug_start\` returns a ready-to-use \`snippet\` with the correct port baked in
22
+ - ALWAYS use the snippet from the tool response - never hardcode ports
23
+ - If you need the current port later, call \`debug_status\`
24
+ - The server remembers its port across sessions, so existing instrumentations keep working
25
+
26
+ ## Workflow
27
+
28
+ 1. Call \`debug_start\` to start the debug server
29
+ 2. Use the returned \`snippet\` to insert fetch() calls at strategic locations in the user's code
30
+ - Replace \`LABEL_HERE\` with a descriptive label (e.g., "before-api-call", "user-input", "loop-iteration")
31
+ - Replace \`YOUR_DATA\` with the variables you want to capture
32
+ 3. Ask user to reproduce the issue
33
+ 4. Call \`debug_read\` to analyze captured logs
34
+ 5. Identify the problem from the runtime data
35
+ 6. Call \`debug_stop\` when done
36
+ 7. Remove all fetch() instrumentation calls from the code
37
+
38
+ ## If Instrumentations Already Exist
39
+ - Call \`debug_status\` first to check for existing port
40
+ - Use \`grep\` to find existing \`localhost:\\d+/log\` patterns in codebase
41
+ - Start server on the same port to avoid breaking existing instrumentations
42
+
43
+ ## Example Instrumentation
44
+
45
+ After calling \`debug_start\`, you'll get a snippet like:
46
+ \`\`\`javascript
47
+ fetch("http://localhost:54321/log", {
48
+ method: "POST",
49
+ headers: {"Content-Type": "application/json"},
50
+ body: JSON.stringify({label: "LABEL_HERE", data: {YOUR_DATA}})
51
+ })
52
+ \`\`\`
53
+
54
+ Insert this at strategic points:
55
+ \`\`\`javascript
56
+ // Before an API call
57
+ fetch("http://localhost:54321/log", {
58
+ method: "POST",
59
+ headers: {"Content-Type": "application/json"},
60
+ body: JSON.stringify({label: "pre-api", data: {userId, requestBody}})
61
+ })
62
+
63
+ // After receiving response
64
+ fetch("http://localhost:54321/log", {
65
+ method: "POST",
66
+ headers: {"Content-Type": "application/json"},
67
+ body: JSON.stringify({label: "post-api", data: {response, status}})
68
+ })
69
+ \`\`\`
70
+
71
+ ## Tips
72
+ - Use descriptive labels to make logs easy to understand
73
+ - Capture relevant variables at each point
74
+ - Add instrumentation around suspected problem areas
75
+ - Compare expected vs actual values in the logs`;
76
+
77
+ const DEBUG_SKILL = {
78
+ name: 'debug',
79
+ description:
80
+ 'Runtime debugging - start a debug server, instrument code with fetch() calls, capture and analyze execution data',
81
+ content: `## CRITICAL: Port Handling
82
+ - \`debug_start\` returns a \`snippet\` with the correct port - ALWAYS use it
83
+ - Never hardcode ports in fetch() calls
84
+ - Call \`debug_status\` to get current port if needed later
85
+ - Server persists port to \`.opencode/debug.port\` so existing instrumentations keep working
86
+
87
+ ## Workflow
88
+
89
+ 1. \`debug_start\` - Returns {port, url, snippet}
90
+ 2. Insert the returned \`snippet\` at strategic code locations:
91
+ - Replace \`LABEL_HERE\` with descriptive label (e.g., "before-api", "after-parse")
92
+ - Replace \`YOUR_DATA\` with variables to capture (e.g., \`{userId, response}\`)
93
+ 3. Ask user to reproduce the issue
94
+ 4. \`debug_read\` - Analyze captured logs (returns parsed JSON array)
95
+ 5. \`debug_stop\` - Stop server when done
96
+ 6. Remove all fetch() instrumentation calls from the code
97
+
98
+ ## Before Starting - Check for Existing Instrumentations
99
+ 1. Call \`debug_status\` - check if server already running or port persisted
100
+ 2. Use grep to search for \`localhost:\\d+/log\` patterns in codebase
101
+ 3. If found, ensure server starts on same port to avoid breaking existing code
102
+
103
+ ## Tools Reference
104
+
105
+ | Tool | Args | Returns |
106
+ |------|------|---------|
107
+ | \`debug_start\` | \`port?\` | \`{port, url, snippet, message}\` |
108
+ | \`debug_stop\` | - | confirmation message |
109
+ | \`debug_read\` | \`tail?\` | \`{entries: [{timestamp, label, data}, ...], count}\` |
110
+ | \`debug_clear\` | - | confirmation message |
111
+ | \`debug_status\` | - | \`{active, port?, url?, persistedPort?, hint?}\` |
112
+
113
+ ## Example Session
114
+
115
+ \`\`\`
116
+ > debug_start
117
+ {port: 54321, url: "http://localhost:54321", snippet: "fetch(...)"}
118
+
119
+ > [Insert snippet at line 42 and 67 in user's code]
120
+
121
+ > [User reproduces the issue]
122
+
123
+ > debug_read
124
+ {entries: [
125
+ {timestamp: "...", label: "before-api", data: {userId: 123}},
126
+ {timestamp: "...", label: "after-api", data: {error: "timeout"}}
127
+ ], count: 2}
128
+
129
+ > [Analyze: API call is timing out]
130
+
131
+ > debug_stop
132
+ {message: "Debug server stopped."}
133
+
134
+ > [Remove instrumentation from lines 42 and 67]
135
+ \`\`\``,
136
+ };
86
137
 
87
- /**
88
- * Load agent definition from markdown file
89
- */
90
- async function loadAgentPrompt(): Promise<string> {
91
- try {
92
- const agentFile = path.join(import.meta.dir, 'agent', 'debug.md');
93
- const content = await Bun.file(agentFile).text();
94
-
95
- // Extract body after frontmatter
96
- const match = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
97
- return match ? match[1].trim() : content;
98
- } catch {
99
- return 'You are a debugging specialist.';
100
- }
101
- }
138
+ // ============================================================
139
+ // PLUGIN EXPORT
140
+ // ============================================================
102
141
 
103
142
  export const DebugAgentPlugin: Plugin = async () => {
104
- // Load skills at initialization
105
- const skills = await loadSkills();
106
- const agentPrompt = await loadAgentPrompt();
107
-
108
143
  return {
109
144
  // Register debug tools
110
145
  tool: {
@@ -122,7 +157,7 @@ export const DebugAgentPlugin: Plugin = async () => {
122
157
  config.agent['debug'] = {
123
158
  description: 'Runtime debugging - capture and analyze execution data',
124
159
  mode: 'primary',
125
- prompt: agentPrompt,
160
+ prompt: AGENT_PROMPT,
126
161
  };
127
162
 
128
163
  // Inject skills (using type assertion as skill may not be in Config type yet)
@@ -130,13 +165,7 @@ export const DebugAgentPlugin: Plugin = async () => {
130
165
  skill?: Record<string, { name: string; description: string; content: string }>;
131
166
  };
132
167
  configWithSkill.skill = configWithSkill.skill ?? {};
133
- for (const skill of skills) {
134
- configWithSkill.skill[skill.name] = {
135
- name: skill.name,
136
- description: skill.description,
137
- content: skill.content,
138
- };
139
- }
168
+ configWithSkill.skill[DEBUG_SKILL.name] = DEBUG_SKILL;
140
169
  },
141
170
  };
142
171
  };