@horizon-ai-dev/shokunin 0.1.0-alpha.1 → 0.1.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -157,6 +157,63 @@ Use when:
157
157
  - `repeat_interval_seconds`: Interval for repeat alerts
158
158
  - `max_repeats`: Maximum number of repeat alerts
159
159
 
160
+ ## Agent Loop (Autonomous Mode)
161
+
162
+ The plugin includes `agent-loop`, a bash script for running autonomous coding sessions with beads task tracking.
163
+
164
+ ### Installation
165
+
166
+ After installing the plugin, the `agent-loop` command becomes available:
167
+
168
+ ```bash
169
+ # Install plugin globally to get the agent-loop command
170
+ npm install -g @horizon-ai-dev/shokunin
171
+
172
+ # Or run directly via npx
173
+ npx @horizon-ai-dev/shokunin agent-loop
174
+ ```
175
+
176
+ ### Usage
177
+
178
+ ```bash
179
+ # Run with default 10 iterations
180
+ agent-loop
181
+
182
+ # Run with custom max iterations
183
+ agent-loop 20
184
+
185
+ # Initialize beads if not present
186
+ agent-loop --init-beads
187
+
188
+ # Combine options
189
+ agent-loop 50 --init-beads
190
+ ```
191
+
192
+ ### What It Does
193
+
194
+ Each iteration:
195
+ 1. **Syncs with remote** - Pulls latest git changes and beads tasks
196
+ 2. **Finds ready work** - Uses `bd ready` to find unblocked tasks
197
+ 3. **Claims and implements** - Picks highest priority task, marks in_progress, implements
198
+ 4. **Runs E2E tests** - If `pnpm e2e` exists, runs full test suite
199
+ 5. **Handles failures** - Creates P0 blocking tasks for E2E regressions
200
+ 6. **Syncs and pushes** - Commits changes, syncs beads, pushes to remote
201
+
202
+ ### Prerequisites
203
+
204
+ - OpenCode CLI installed (`npm install -g opencode`)
205
+ - Beads CLI installed (`bd` command available)
206
+ - Git repository with remote configured
207
+ - Beads initialized in project (`.beads/` directory)
208
+
209
+ ### Files Included
210
+
211
+ | File | Purpose |
212
+ |------|---------|
213
+ | `scripts/agent-loop.sh` | Main orchestration script |
214
+ | `scripts/agent/prompt.txt` | Agent instructions for beads-driven development |
215
+ | `scripts/agent/test-failure-prompt.txt` | Prompt for creating P0 tasks on E2E failures |
216
+
160
217
  ## Bundled Skills
161
218
 
162
219
  The plugin includes 6 bundled skills:
@@ -12,8 +12,9 @@
12
12
  * 5. Install beads git hooks
13
13
  * 6. Configure playwright-mcp in opencode.json
14
14
  * 7. Copy 6 skills to .opencode/skill/
15
- * 8. Smart-merge model config into opencode.json
16
- * 9. Add note to AGENTS.md about runtime context
15
+ * 8. Copy agent-loop.sh and agent prompt files
16
+ * 9. Smart-merge model config into opencode.json
17
+ * 10. Add note to AGENTS.md about runtime context
17
18
  */
18
19
  import type { Config } from "@opencode-ai/sdk";
19
20
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sn-configure.d.ts","sourceRoot":"","sources":["../../src/commands/sn-configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAqP3C;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,SAAS,CAOhD,CAAC"}
1
+ {"version":3,"file":"sn-configure.d.ts","sourceRoot":"","sources":["../../src/commands/sn-configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAuT3C;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,SAAS,CAOhD,CAAC"}
@@ -12,8 +12,9 @@
12
12
  * 5. Install beads git hooks
13
13
  * 6. Configure playwright-mcp in opencode.json
14
14
  * 7. Copy 6 skills to .opencode/skill/
15
- * 8. Smart-merge model config into opencode.json
16
- * 9. Add note to AGENTS.md about runtime context
15
+ * 8. Copy agent-loop.sh and agent prompt files
16
+ * 9. Smart-merge model config into opencode.json
17
+ * 10. Add note to AGENTS.md about runtime context
17
18
  */
18
19
  /**
19
20
  * Command name (without leading slash).
@@ -167,21 +168,79 @@ Note: The skill files are bundled with the Shokunin plugin. To copy them, you'll
167
168
 
168
169
  Since we're running as a command template, use bash to copy:
169
170
  \`\`\`bash
170
- # Find the plugin location (it's in node_modules/@horizon-ai-dev/shokunin)
171
- PLUGIN_DIR="$(dirname "$(realpath "$(npm root)")/@horizon-ai-dev/shokunin")/vendor/skills"
171
+ # Find the plugin location
172
+ find_plugin_dir() {
173
+ # Try npm global location first
174
+ local npm_global_plugins="$(npm root -g)/@horizon-ai-dev/shokunin"
175
+ if [ -d "$npm_global_plugins/skills" ]; then
176
+ echo "$npm_global_plugins"
177
+ return
178
+ fi
179
+
180
+ # Try local node_modules
181
+ local local_plugins="$(npm root)/@horizon-ai-dev/shokunin"
182
+ if [ -d "$local_plugins/skills" ]; then
183
+ echo "$local_plugins"
184
+ return
185
+ fi
186
+
187
+ # Try relative path for development
188
+ if [ -d "./packages/shokunin-opencode-plugin/skills" ]; then
189
+ echo "./packages/shokunin-opencode-plugin"
190
+ return
191
+ fi
192
+
193
+ echo ""
194
+ }
172
195
 
173
- # Or if running locally during development
174
- PLUGIN_DIR="./package/vendor/skills"
196
+ PLUGIN_DIR="$(find_plugin_dir)"
197
+
198
+ if [ -z "$PLUGIN_DIR" ]; then
199
+ echo "Error: Could not find Shokunin plugin directory"
200
+ exit 1
201
+ fi
175
202
 
176
203
  # Copy each skill
177
204
  for skill in prd-generator prd-decomposition agent-development command-development skill-development playwright-skill; do
178
- if [ -d "$PLUGIN_DIR/$skill" ]; then
179
- cp -r "$PLUGIN_DIR/$skill" ".opencode/skill/"
205
+ if [ -d "$PLUGIN_DIR/skills/$skill" ]; then
206
+ cp -r "$PLUGIN_DIR/skills/$skill" ".opencode/skill/"
180
207
  fi
181
208
  done
182
209
  \`\`\`
183
210
 
184
- ## Step 8: Smart-Merge Model Config
211
+ ## Step 8: Copy Agent Loop Script and Prompts
212
+
213
+ Copy the agent-loop.sh script and its associated prompt files for autonomous agent execution:
214
+
215
+ \`\`\`bash
216
+ # Create the scripts directory if it doesn't exist
217
+ mkdir -p scripts/agent
218
+
219
+ # Copy agent-loop.sh to project root scripts directory
220
+ if [ -f "$PLUGIN_DIR/scripts/agent-loop.sh" ]; then
221
+ cp "$PLUGIN_DIR/scripts/agent-loop.sh" "./scripts/agent-loop.sh"
222
+ chmod +x "./scripts/agent-loop.sh"
223
+ echo "Copied agent-loop.sh"
224
+ fi
225
+
226
+ # Copy agent prompt files
227
+ if [ -d "$PLUGIN_DIR/scripts/agent" ]; then
228
+ cp -r "$PLUGIN_DIR/scripts/agent/"* "./scripts/agent/"
229
+ echo "Copied agent prompt files"
230
+ fi
231
+ \`\`\`
232
+
233
+ The agent-loop files include:
234
+ - \`scripts/agent-loop.sh\` - Main agent loop script for autonomous task execution
235
+ - \`scripts/agent/prompt.txt\` - Primary agent prompt for task work
236
+ - \`scripts/agent/test-failure-prompt.txt\` - Prompt for handling test failures
237
+
238
+ After copying, you can run the agent loop with:
239
+ \`\`\`bash
240
+ ./scripts/agent-loop.sh [max_iterations] [--init-beads]
241
+ \`\`\`
242
+
243
+ ## Step 9: Smart-Merge Model Config
185
244
 
186
245
  Based on the user's selection in Step 1, merge the model configuration into opencode.json.
187
246
 
@@ -202,7 +261,7 @@ Smart-merge rules:
202
261
  - For conflicts, keep existing value and report the conflict
203
262
  - Show user what changes were made
204
263
 
205
- ## Step 9: Update AGENTS.md
264
+ ## Step 10: Update AGENTS.md
206
265
 
207
266
  Check if AGENTS.md exists:
208
267
  \`\`\`bash
@@ -241,15 +300,23 @@ MCP Servers:
241
300
  Skills Installed:
242
301
  - [list of skills copied]
243
302
 
303
+ Agent Loop:
304
+ - scripts/agent-loop.sh: [copied]
305
+ - scripts/agent/prompt.txt: [copied]
306
+ - scripts/agent/test-failure-prompt.txt: [copied]
307
+
244
308
  Files Modified:
245
309
  - opencode.json
246
310
  - .opencode/skill/
311
+ - scripts/agent-loop.sh
312
+ - scripts/agent/
247
313
  - AGENTS.md
248
314
 
249
315
  Next Steps:
250
316
  1. Run /sn-check-config to verify configuration
251
317
  2. Try /sn-review-pr to review code changes
252
318
  3. Use Tab to switch to sn-code-reviewer agent
319
+ 4. Run ./scripts/agent-loop.sh to start autonomous agent
253
320
  \`\`\`
254
321
 
255
322
  ## Error Handling
@@ -1 +1 @@
1
- {"version":3,"file":"sn-configure.js","sourceRoot":"","sources":["../../src/commands/sn-configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAE3C;;GAEG;AACH,MAAM,aAAa,GAAG;;;;;;;;;;;CAWrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;EAqBvB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAsMqB,CAAC;AAErC;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,CAAC,YAAY,CAAC,EAAE;QACd,WAAW,EACT,qHAAqH;QACvH,QAAQ,EAAE,gBAAgB;QAC1B,OAAO,EAAE,IAAI,EAAE,6CAA6C;KAC7D;CACF,CAAC"}
1
+ {"version":3,"file":"sn-configure.js","sourceRoot":"","sources":["../../src/commands/sn-configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAE3C;;GAEG;AACH,MAAM,aAAa,GAAG;;;;;;;;;;;CAWrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;EAqBvB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAwQqB,CAAC;AAErC;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,CAAC,YAAY,CAAC,EAAE;QACd,WAAW,EACT,qHAAqH;QACvH,QAAQ,EAAE,gBAAgB;QAC1B,OAAO,EAAE,IAAI,EAAE,6CAA6C;KAC7D;CACF,CAAC"}
package/dist/plugin.d.ts CHANGED
@@ -17,14 +17,21 @@
17
17
  * - sn-silent-failure-hunter (subagent) - Error handling analysis
18
18
  * - sn-type-design-analyzer (subagent) - TypeScript type design
19
19
  * - sn-skill-reviewer (subagent) - OpenCode skill review
20
+ *
21
+ * IMPORTANT: This plugin handles context injection differently from raw beads:
22
+ * - Combines SHOKUNIN_RULES + beads-context into a SINGLE session.prompt() call
23
+ * - This prevents the freeze bug caused by multiple session.prompt() calls
24
+ * within the chat.message hook (see issue investigation 2026-01-18)
20
25
  */
21
26
  import type { Plugin } from "@opencode-ai/plugin";
22
27
  /**
23
28
  * The main Shokunin plugin export.
24
29
  *
25
- * Wraps BeadsPlugin and merges any additional Shokunin-specific hooks.
26
- * This allows extending the plugin functionality while maintaining
27
- * full compatibility with opencode-beads.
30
+ * Unlike a simple BeadsPlugin wrapper, this plugin:
31
+ * 1. Loads beads commands and agents directly (not via BeadsPlugin)
32
+ * 2. Handles context injection with a SINGLE session.prompt() call
33
+ * that combines both SHOKUNIN_RULES and beads-context
34
+ * 3. This prevents the freeze bug caused by nested session.prompt() calls
28
35
  */
29
36
  export declare const ShokuninPlugin: Plugin;
30
37
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAS,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAkDzD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,MA4E5B,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAS,MAAM,EAAe,MAAM,qBAAqB,CAAC;AA6ItE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,EAAE,MAoF5B,CAAC"}
package/dist/plugin.js CHANGED
@@ -17,8 +17,15 @@
17
17
  * - sn-silent-failure-hunter (subagent) - Error handling analysis
18
18
  * - sn-type-design-analyzer (subagent) - TypeScript type design
19
19
  * - sn-skill-reviewer (subagent) - OpenCode skill review
20
+ *
21
+ * IMPORTANT: This plugin handles context injection differently from raw beads:
22
+ * - Combines SHOKUNIN_RULES + beads-context into a SINGLE session.prompt() call
23
+ * - This prevents the freeze bug caused by multiple session.prompt() calls
24
+ * within the chat.message hook (see issue investigation 2026-01-18)
20
25
  */
21
- import { BeadsPlugin } from "opencode-beads";
26
+ // Import beads utilities directly - we handle context injection ourselves
27
+ // to avoid the freeze bug caused by multiple session.prompt() calls
28
+ import { BEADS_GUIDANCE, loadAgent, loadCommands, } from "opencode-beads/src/vendor";
22
29
  // Shokunin agents
23
30
  import { snCodeReviewerAgent } from "./agents/sn-code-reviewer.js";
24
31
  import { snCodeSimplifierAgent } from "./agents/sn-code-simplifier.js";
@@ -33,7 +40,8 @@ import { snConfigureCommand } from "./commands/sn-configure.js";
33
40
  import { snReviewPrCommand } from "./commands/sn-review-pr.js";
34
41
  import { snUpdateCommand } from "./commands/sn-update.js";
35
42
  // Shokunin context
36
- import { looksLikeCompaction, SHOKUNIN_RULES, } from "./context/shokunin-rules.js";
43
+ import { SHOKUNIN_RULES } from "./context/shokunin-rules.js";
44
+ // Note: looksLikeCompaction is no longer needed - we use session.compacted event instead
37
45
  // Shokunin tools
38
46
  import { shokuninTools } from "./tools/emergency-stop.js";
39
47
  /**
@@ -57,77 +65,155 @@ const shokuninAgents = {
57
65
  ...snTypeDesignAnalyzerAgent,
58
66
  ...snSkillReviewerAgent,
59
67
  };
68
+ /**
69
+ * Get the current model/agent context for a session by querying messages.
70
+ *
71
+ * Mirrors OpenCode's internal lastModel() logic to find the most recent
72
+ * user message. Used during event handling when we don't have direct access
73
+ * to the current user message's context.
74
+ */
75
+ async function getSessionContext(client, sessionID) {
76
+ try {
77
+ const response = await client.session.messages({
78
+ path: { id: sessionID },
79
+ query: { limit: 50 },
80
+ });
81
+ if (response.data) {
82
+ for (const msg of response.data) {
83
+ if (msg.info.role === "user" && "model" in msg.info && msg.info.model) {
84
+ return { model: msg.info.model, agent: msg.info.agent };
85
+ }
86
+ }
87
+ }
88
+ }
89
+ catch {
90
+ // On error, return undefined (let opencode use its default)
91
+ }
92
+ return;
93
+ }
94
+ /**
95
+ * Inject combined Shokunin + Beads context into a session.
96
+ *
97
+ * IMPORTANT: This combines both contexts into a SINGLE session.prompt() call
98
+ * to avoid the freeze bug caused by multiple session.prompt() calls within
99
+ * the chat.message hook.
100
+ *
101
+ * Runs `bd prime` and combines the output with SHOKUNIN_RULES.
102
+ * Silently skips beads context if bd is not installed or not initialized.
103
+ */
104
+ async function injectCombinedContext(client, $, sessionID, context) {
105
+ // Start with shokunin rules
106
+ let combinedContext = SHOKUNIN_RULES;
107
+ // Try to add beads context
108
+ try {
109
+ const primeOutput = await $ `bd prime`.text();
110
+ if (primeOutput && primeOutput.trim() !== "") {
111
+ combinedContext += `\n\n<beads-context>
112
+ ${primeOutput.trim()}
113
+ </beads-context>
114
+
115
+ ${BEADS_GUIDANCE}`;
116
+ }
117
+ }
118
+ catch {
119
+ // Silent skip if bd prime fails (not installed or not initialized)
120
+ // Shokunin rules will still be injected
121
+ }
122
+ // Single injection call with combined context
123
+ try {
124
+ await client.session.prompt({
125
+ path: { id: sessionID },
126
+ body: {
127
+ noReply: true,
128
+ model: context?.model,
129
+ agent: context?.agent,
130
+ parts: [{ type: "text", text: combinedContext, synthetic: true }],
131
+ },
132
+ });
133
+ }
134
+ catch {
135
+ // Silent skip if injection fails
136
+ }
137
+ }
60
138
  /**
61
139
  * The main Shokunin plugin export.
62
140
  *
63
- * Wraps BeadsPlugin and merges any additional Shokunin-specific hooks.
64
- * This allows extending the plugin functionality while maintaining
65
- * full compatibility with opencode-beads.
141
+ * Unlike a simple BeadsPlugin wrapper, this plugin:
142
+ * 1. Loads beads commands and agents directly (not via BeadsPlugin)
143
+ * 2. Handles context injection with a SINGLE session.prompt() call
144
+ * that combines both SHOKUNIN_RULES and beads-context
145
+ * 3. This prevents the freeze bug caused by nested session.prompt() calls
66
146
  */
67
- export const ShokuninPlugin = async (input) => {
68
- const { client } = input;
69
- // Get hooks from BeadsPlugin
70
- const beadsHooks = await BeadsPlugin(input);
71
- // Track sessions that have received shokunin-rules context
147
+ export const ShokuninPlugin = async ({ client, $ }) => {
148
+ // Load beads commands and agents directly
149
+ const [beadsCommands, beadsAgents] = await Promise.all([
150
+ loadCommands(),
151
+ loadAgent(),
152
+ ]);
153
+ // Track sessions that have received combined context
72
154
  const injectedSessions = new Set();
73
- // Shokunin-specific hooks
74
155
  const shokuninHooks = {
75
156
  config: async (config) => {
76
157
  // Inject Shokunin agents
77
158
  config.agent = {
78
159
  ...config.agent,
79
160
  ...shokuninAgents,
161
+ ...beadsAgents,
80
162
  };
81
163
  // Inject Shokunin commands
82
164
  config.command = {
83
165
  ...config.command,
84
166
  ...shokuninCommands,
167
+ ...beadsCommands,
85
168
  };
86
- // Call beads config hook if it exists
87
- if (beadsHooks.config) {
88
- await beadsHooks.config(config);
89
- }
90
169
  },
91
170
  // Register Shokunin tools
92
171
  tool: shokuninTools,
93
- // Inject shokunin-rules context on first message and after compaction
94
- "chat.message": async (hookInput, output) => {
172
+ // Inject combined shokunin-rules + beads context on first message
173
+ "chat.message": async (hookInput, _output) => {
95
174
  const sessionID = hookInput.sessionID;
96
- // Get the first text part from the message
97
- const textPart = output.parts.find((p) => p.type === "text");
98
- const messageText = textPart?.type === "text" ? textPart.text : "";
99
- // Check if this is a new session or post-compaction
100
- const isNewSession = !injectedSessions.has(sessionID);
101
- const isPostCompaction = looksLikeCompaction(messageText);
102
- // Inject context if needed
103
- if (isNewSession || isPostCompaction) {
104
- try {
105
- await client.session.prompt({
106
- path: { id: sessionID },
107
- body: {
108
- noReply: true,
109
- model: hookInput.model,
110
- agent: hookInput.agent,
111
- parts: [{ type: "text", text: SHOKUNIN_RULES, synthetic: true }],
112
- },
175
+ // Skip if already injected this session
176
+ if (injectedSessions.has(sessionID))
177
+ return;
178
+ // Check if combined context was already injected (handles plugin reload/reconnection)
179
+ try {
180
+ const existing = await client.session.messages({
181
+ path: { id: sessionID },
182
+ });
183
+ if (existing.data) {
184
+ const hasContext = existing.data.some((msg) => {
185
+ const parts = msg.parts || msg.info.parts;
186
+ if (!parts)
187
+ return false;
188
+ return parts.some((part) => part.type === "text" &&
189
+ (part.text?.includes("<shokunin-rules>") ||
190
+ part.text?.includes("<beads-context>")));
113
191
  });
114
- injectedSessions.add(sessionID);
115
- }
116
- catch {
117
- // Silent skip if injection fails
192
+ if (hasContext) {
193
+ injectedSessions.add(sessionID);
194
+ return;
195
+ }
118
196
  }
119
197
  }
120
- // Call beads chat.message hook if it exists
121
- if (beadsHooks["chat.message"]) {
122
- await beadsHooks["chat.message"](hookInput, output);
198
+ catch {
199
+ // On error, proceed with injection
200
+ }
201
+ injectedSessions.add(sessionID);
202
+ // Inject combined context with a SINGLE session.prompt() call
203
+ await injectCombinedContext(client, $, sessionID, {
204
+ model: hookInput.model,
205
+ agent: hookInput.agent,
206
+ });
207
+ },
208
+ // Re-inject context after session compaction
209
+ event: async ({ event }) => {
210
+ if (event.type === "session.compacted") {
211
+ const sessionID = event.properties.sessionID;
212
+ const context = await getSessionContext(client, sessionID);
213
+ await injectCombinedContext(client, $, sessionID, context);
123
214
  }
124
215
  },
125
216
  };
126
- // Merge hooks - shokunin hooks override beads hooks when both exist
127
- // This ensures our config hook runs and then calls beads' config hook
128
- return {
129
- ...beadsHooks,
130
- ...shokuninHooks,
131
- };
217
+ return shokuninHooks;
132
218
  };
133
219
  //# sourceMappingURL=plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@horizon-ai-dev/shokunin",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.7",
4
4
  "type": "module",
5
5
  "description": "Complete AI-assisted development workflow plugin for OpenCode. Re-exports opencode-beads with specialized agents, commands, and bundled skills.",
6
6
  "author": "Horizon AI Dev",
@@ -15,20 +15,20 @@
15
15
  "beads",
16
16
  "shokunin",
17
17
  "ai",
18
- "agent"
18
+ "agent",
19
+ "autonomous"
19
20
  ],
20
21
  "main": "dist/index.js",
21
22
  "types": "dist/index.d.ts",
23
+ "bin": {
24
+ "agent-loop": "./scripts/agent-loop.sh"
25
+ },
22
26
  "files": [
23
27
  "dist",
24
28
  "vendor",
29
+ "scripts",
25
30
  "README.md"
26
31
  ],
27
- "scripts": {
28
- "build": "tsc",
29
- "typecheck": "tsc --noEmit",
30
- "prepublishOnly": "pnpm build"
31
- },
32
32
  "dependencies": {
33
33
  "@opencode-ai/plugin": "^1.0.143",
34
34
  "@opencode-ai/sdk": "^1.0.143",
@@ -42,5 +42,14 @@
42
42
  },
43
43
  "engines": {
44
44
  "bun": ">=1.0.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsc",
48
+ "typecheck": "tsc --noEmit",
49
+ "publish:alpha": "pnpm version prerelease --preid=alpha --no-git-tag-version && git add package.json && git commit -m \"chore: bump to $(node -p \"require('./package.json').version\")\" && pnpm publish --access public --tag alpha --no-git-checks",
50
+ "publish:beta": "pnpm version prerelease --preid=beta --no-git-tag-version && git add package.json && git commit -m \"chore: bump to $(node -p \"require('./package.json').version\")\" && pnpm publish --access public --tag beta --no-git-checks",
51
+ "publish:release": "pnpm version patch && pnpm publish --access public",
52
+ "publish:minor": "pnpm version minor && pnpm publish --access public",
53
+ "publish:major": "pnpm version major && pnpm publish --access public"
45
54
  }
46
- }
55
+ }
@@ -0,0 +1,270 @@
1
+ # Autonomous Agent Instructions (Beads Edition)
2
+
3
+ You are an autonomous coding agent working on a software project using Beads for task tracking.
4
+
5
+ ## Beads Overview
6
+
7
+ Beads is a distributed, git-backed graph issue tracker designed for AI agents. Issues are stored as JSONL in `.beads/` and synchronized via git.
8
+
9
+ Key concepts:
10
+ - `bd ready` - Find tasks with no blockers (ready to work)
11
+ - `bd show <id>` - View task details
12
+ - `bd update <id> --status in_progress` - Claim work
13
+ - `bd close <id>` - Complete work
14
+ - `bd sync` - Synchronize with remote (CRITICAL at session end)
15
+
16
+ ## Your Task
17
+
18
+ 1. **Synchronize with remote first** - Run `bd sync` to pull latest tasks/changes
19
+ 2. **Read the current state** - Run `bd ready --json` to find available work
20
+ 3. **Check for in-progress work** - Run `bd list --status=in_progress --json` to resume any interrupted work
21
+ 4. **Check branch alignment** - Verify you're on the correct working branch
22
+ 5. **Pick the highest priority ready task** - Select from `bd ready` output (P0 highest, P4 lowest)
23
+ 6. **Claim the task** - Run `bd update <id> --status in_progress --json`
24
+ 7. **Implement the task** - Do the actual work
25
+ 8. **Run quality checks** - typecheck, lint, test (use whatever your project requires)
26
+ 9. **Update AGENTS.md files** if you discover reusable patterns (see below)
27
+ 10. **If checks pass**, commit ALL code changes with message: `feat: [Task ID] - [Task Title]`
28
+ 11. **Close the task** - Run `bd close <id> --reason "Implemented and tested" --json`
29
+ 12. **Add learnings** - If significant discoveries, create a note task or update the closed task's notes
30
+ 13. **Synchronize** - Run `bd sync` to push all changes
31
+
32
+ ## Recovery from Interruption
33
+
34
+ On startup, ALWAYS check for interrupted work:
35
+
36
+ ```bash
37
+ # 1. Sync with remote to get latest state
38
+ bd sync
39
+
40
+ # 2. Check for any in-progress work (might be from crashed session)
41
+ bd list --status=in_progress --json
42
+
43
+ # 3. If found, either:
44
+ # - Resume the work if it's still valid
45
+ # - Reset status if work was completed but not closed:
46
+ # bd close <id> --reason "Completed in previous session"
47
+ # - Or reopen if it needs rework:
48
+ # bd update <id> --status open --json
49
+ ```
50
+
51
+ ## Progress Tracking with Beads
52
+
53
+ Use Beads' built-in tracking:
54
+
55
+ ### Recording Progress
56
+ ```bash
57
+ # Update task with implementation notes
58
+ bd update <id> --notes "Implemented auth validation. Changed files: src/auth.ts, tests/auth.test.ts"
59
+
60
+ # Add design notes if relevant
61
+ bd update <id> --design "Used JWT validation pattern from @repo/auth"
62
+ ```
63
+
64
+ ### Recording Learnings
65
+ ```bash
66
+ # For significant patterns discovered, create a documentation task
67
+ bd create "Document: [Pattern Name]" -t chore -p 3 --description "Pattern discovered while working on <parent-id>: [description]" --json
68
+
69
+ # Or if it's a quick note, add to the task's notes field
70
+ bd update <id> --notes "Learning: Always use IF NOT EXISTS for migrations"
71
+ ```
72
+
73
+ ### Discovering Related Work
74
+ ```bash
75
+ # If you find a bug or new requirement while working, track it:
76
+ bd create "Found: [issue description]" -t bug -p 2 --deps discovered-from:<current-task-id> --json
77
+ ```
78
+
79
+ ## Consolidate Patterns in AGENTS.md
80
+
81
+ If you discover a **reusable pattern** that future iterations should know, add it to the relevant AGENTS.md file (project root or directory-specific):
82
+
83
+ ```markdown
84
+ ## Codebase Patterns
85
+ - Example: Use `sql<number>` template for aggregations
86
+ - Example: Always use `IF NOT EXISTS` for migrations
87
+ - Example: Export types from actions.ts for UI components
88
+ ```
89
+
90
+ Only add patterns that are **general and reusable**, not task-specific details.
91
+
92
+ ## Update AGENTS.md Files
93
+
94
+ Before committing, check if any edited files have learnings worth preserving:
95
+
96
+ 1. **Identify directories with edited files**
97
+ 2. **Check for existing AGENTS.md** in those directories or parent directories
98
+ 3. **Add valuable learnings**:
99
+ - API patterns or conventions specific to that module
100
+ - Gotchas or non-obvious requirements
101
+ - Dependencies between files
102
+ - Testing approaches for that area
103
+
104
+ **Do NOT add:**
105
+ - Task-specific implementation details (those go in the task notes)
106
+ - Temporary debugging notes
107
+ - Information already tracked in Beads
108
+
109
+ ## Quality Requirements
110
+
111
+ - ALL commits must pass your project's quality checks (typecheck, lint, test)
112
+ - Do NOT commit broken code
113
+ - Keep changes focused and minimal
114
+ - Follow existing code patterns
115
+
116
+ Run these commands and ensure they pass:
117
+ ```bash
118
+ pnpm check # Linting
119
+ pnpm test # Unit tests
120
+ pnpm build # Full build (includes typecheck) - allow up to 10 min
121
+ ```
122
+
123
+ ## E2E Testing (MANDATORY)
124
+
125
+ ### Distinction: Skill vs Persistent Tests
126
+
127
+ | Tool | Purpose | When to Use |
128
+ |------|---------|-------------|
129
+ | `playwright-skill` | Interactive browser exploration | During development, debugging, one-off verification |
130
+ | E2E test files | Permanent regression tests | Encoding acceptance criteria, run every iteration |
131
+
132
+ **Key principle:** The playwright-skill is for exploratory testing during development. Acceptance criteria MUST be encoded as persistent E2E tests that run every iteration to prevent regressions.
133
+
134
+ ### Before Closing ANY UI Task
135
+
136
+ 1. **Encode acceptance criteria as persistent E2E test**
137
+ - Location: See app-specific AGENTS.md for test paths (e.g., `apps/app/e2e/`)
138
+ - Each acceptance criterion = one or more test assertions
139
+ - Tests must be self-contained and repeatable
140
+
141
+ 2. **Run ALL E2E tests (not just new ones)**
142
+ ```bash
143
+ pnpm e2e
144
+ ```
145
+
146
+ 3. **All tests must pass**
147
+ - If ANY test fails (including tests for other features), you MUST NOT close the task
148
+ - Fix the regression first
149
+ - The agent-loop will automatically create a P0 blocking task if E2E tests fail
150
+
151
+ ### E2E Test Template
152
+
153
+ For task `bd-abc123` with acceptance criteria:
154
+ - Filter dropdown has options: All, Active, Completed
155
+ - Selecting "Active" shows only active tasks
156
+
157
+ Create `apps/app/e2e/tasks/bd-abc123-filter.spec.ts`:
158
+
159
+ ```typescript
160
+ import { expect, test } from '@playwright/test'
161
+
162
+ const TITLE_PATTERN = /.+/
163
+
164
+ test.describe('Task Filter (bd-abc123)', () => {
165
+ test('filter dropdown has correct options', async ({ page }) => {
166
+ await page.goto('/tasks')
167
+ await page.getByRole('combobox', { name: /filter/i }).click()
168
+ await expect(page.getByRole('option', { name: 'All' })).toBeVisible()
169
+ await expect(page.getByRole('option', { name: 'Active' })).toBeVisible()
170
+ await expect(page.getByRole('option', { name: 'Completed' })).toBeVisible()
171
+ })
172
+
173
+ test('selecting Active shows only active tasks', async ({ page }) => {
174
+ await page.goto('/tasks')
175
+ await page.getByRole('combobox', { name: /filter/i }).selectOption('Active')
176
+ // Assert filtering worked...
177
+ })
178
+ })
179
+ ```
180
+
181
+ ## Browser Testing During Development
182
+
183
+ For interactive browser exploration during development, use the `playwright-skill`:
184
+
185
+ 1. Load the `playwright-skill` skill (use the skill tool)
186
+ 2. Navigate to the relevant page
187
+ 3. Verify the UI changes work as expected
188
+ 4. Take screenshots if helpful
189
+
190
+ **IMPORTANT:** This is for exploratory testing only. You MUST ALSO create persistent E2E tests from acceptance criteria (see above).
191
+
192
+ ## Stop Condition
193
+
194
+ After completing a task, check if ALL work is done:
195
+
196
+ ```bash
197
+ bd ready --json
198
+ ```
199
+
200
+ If `bd ready` returns an empty array (no ready tasks), check overall status:
201
+
202
+ ```bash
203
+ bd stats --json
204
+ ```
205
+
206
+ If all tasks are complete (open=0, in_progress=0), reply with:
207
+ <promise>COMPLETE</promise>
208
+
209
+ If there are still tasks with blockers, check:
210
+ ```bash
211
+ bd blocked --json
212
+ ```
213
+
214
+ And report what's blocking progress.
215
+
216
+ If there are still ready tasks, end your response normally (another iteration will pick up the next task).
217
+
218
+ ## Important
219
+
220
+ - Work on ONE task per iteration
221
+ - Always run `bd sync` at the START and END of each iteration
222
+ - Commit frequently
223
+ - Keep CI green
224
+ - Use `--json` flag for all bd commands (easier to parse)
225
+
226
+ ## Essential bd Commands Reference
227
+
228
+ ```bash
229
+ # Finding work
230
+ bd ready --json # Tasks ready to work on
231
+ bd list --status=open --json # All open tasks
232
+ bd list --status=in_progress --json # In-progress work
233
+ bd blocked --json # What's blocked and why
234
+ bd show <id> --json # Detailed task view
235
+
236
+ # Working on tasks
237
+ bd update <id> --status in_progress --json # Claim work
238
+ bd update <id> --notes "..." --json # Add notes
239
+ bd close <id> --reason "..." --json # Complete work
240
+
241
+ # Creating new tasks
242
+ bd create "Title" -t task|bug|feature -p 0-4 --json
243
+ bd create "Title" --deps discovered-from:<id> --json # Link to parent
244
+
245
+ # Dependencies
246
+ bd dep add <child> <parent> --type blocks|related|discovered-from
247
+
248
+ # Synchronization (CRITICAL)
249
+ bd sync # Full sync with remote
250
+ bd sync --status # Check sync status
251
+
252
+ # Project health
253
+ bd stats --json # Overall statistics
254
+ ```
255
+
256
+ ## Priority Levels
257
+
258
+ - `0` (P0) - Critical: security, data loss, broken builds
259
+ - `1` (P1) - High: major features, important bugs
260
+ - `2` (P2) - Medium: nice-to-have, minor bugs
261
+ - `3` (P3) - Low: polish, optimization
262
+ - `4` (P4) - Backlog: future ideas
263
+
264
+ ## Issue Types
265
+
266
+ - `bug` - Something broken
267
+ - `feature` - New functionality
268
+ - `task` - General work item
269
+ - `epic` - Large feature with subtasks
270
+ - `chore` - Maintenance work
@@ -0,0 +1,40 @@
1
+ # E2E Test Failure - Create Blocking Fix Task
2
+
3
+ E2E tests failed during the agent loop. You MUST create a beads task to fix this regression.
4
+
5
+ ## Test Failure Output
6
+
7
+ ```
8
+ {{E2E_OUTPUT}}
9
+ ```
10
+
11
+ ## Your Task
12
+
13
+ 1. **Analyze the failure** - Identify which test(s) failed and why
14
+ 2. **Create a blocking beads task** with:
15
+ - Title: "Fix E2E regression: [brief description]"
16
+ - Type: bug
17
+ - Priority: 0 (P0 - Critical, blocks all work)
18
+ - Description: Include the failing test name, error message, and likely cause
19
+ - Acceptance criteria: "E2E test [name] passes", "Full E2E suite passes"
20
+
21
+ 3. **Add dependency** - If you can identify which recent task likely caused this, link them:
22
+ ```bash
23
+ bd dep add <fix-task-id> <causing-task-id> --type discovered-from
24
+ ```
25
+
26
+ 4. **Sync** - Run `bd sync` to save the task
27
+
28
+ ## Example
29
+
30
+ ```bash
31
+ bd create "Fix E2E regression: Task filter dropdown not visible" \
32
+ --type bug \
33
+ --priority 0 \
34
+ --description "E2E test 'filter dropdown has correct options' fails with: Timeout waiting for selector '[role=combobox]'. Likely caused by recent filter component refactor." \
35
+ --acceptance "E2E test 'filter dropdown has correct options' passes
36
+ Full E2E suite passes (pnpm e2e)" \
37
+ --labels "area:frontend,regression"
38
+ ```
39
+
40
+ Do NOT attempt to fix the code in this session. Just create the task with sufficient context for the next iteration to fix it.
@@ -0,0 +1,439 @@
1
+ #!/bin/bash
2
+ # Agent Loop - Long-running AI agent loop using OpenCode with Beads task tracking
3
+ # Usage: agent-loop [max_iterations] [--init-beads]
4
+ #
5
+ # Features:
6
+ # - Uses Beads for distributed, git-backed task tracking
7
+ # - Syncs with remote before each iteration to get latest tasks
8
+ # - Recovers gracefully from unexpected termination
9
+ # - Archives completed runs for historical reference
10
+ #
11
+ # This script is part of the @horizon-ai-dev/shokunin OpenCode plugin.
12
+ # It can be run from any directory with a beads-initialized project.
13
+
14
+ set -e
15
+
16
+ MAX_ITERATIONS=${1:-10}
17
+ INIT_BEADS=false
18
+
19
+ # Parse arguments
20
+ for arg in "$@"; do
21
+ case $arg in
22
+ --init-beads)
23
+ INIT_BEADS=true
24
+ shift
25
+ ;;
26
+ [0-9]*)
27
+ MAX_ITERATIONS=$arg
28
+ shift
29
+ ;;
30
+ esac
31
+ done
32
+
33
+ # Resolve script location (handles symlinks from npm bin)
34
+ resolve_script_dir() {
35
+ local source="${BASH_SOURCE[0]}"
36
+ local dir
37
+ # Resolve symlinks
38
+ while [ -h "$source" ]; do
39
+ dir="$(cd -P "$(dirname "$source")" && pwd)"
40
+ source="$(readlink "$source")"
41
+ # Handle relative symlinks
42
+ [[ $source != /* ]] && source="$dir/$source"
43
+ done
44
+ dir="$(cd -P "$(dirname "$source")" && pwd)"
45
+ echo "$dir"
46
+ }
47
+
48
+ SCRIPT_DIR="$(resolve_script_dir)"
49
+ PROMPT_FILE="$SCRIPT_DIR/agent/prompt.txt"
50
+ TEST_FAILURE_PROMPT_FILE="$SCRIPT_DIR/agent/test-failure-prompt.txt"
51
+
52
+ # Working directory where the user runs the command
53
+ WORK_DIR="$(pwd)"
54
+ BEADS_DIR="$WORK_DIR/.beads"
55
+ AGENT_DIR="$WORK_DIR/.agent-loop"
56
+ CONFIG_FILE="$AGENT_DIR/opencode.json"
57
+ STATE_FILE="$AGENT_DIR/.agent-state"
58
+
59
+ # Extended timeout for turbo builds (10 minutes in milliseconds)
60
+ export OPENCODE_TIMEOUT=600000
61
+
62
+ # Color codes for output
63
+ RED='\033[0;31m'
64
+ GREEN='\033[0;32m'
65
+ YELLOW='\033[1;33m'
66
+ BLUE='\033[0;34m'
67
+ NC='\033[0m' # No Color
68
+
69
+ log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
70
+ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
71
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
72
+ log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
73
+
74
+ # Create OpenCode config for autonomous mode (all permissions allowed)
75
+ create_opencode_config() {
76
+ cat > "$CONFIG_FILE" << 'EOF'
77
+ {
78
+ "$schema": "https://opencode.ai/config.json",
79
+ "share": "auto",
80
+ "autoupdate": false,
81
+ "compaction": {
82
+ "auto": true,
83
+ "prune": true
84
+ },
85
+ "permission": {
86
+ "edit": "allow",
87
+ "bash": {
88
+ "*": "allow",
89
+ "*terraform apply*": "allow",
90
+ "*terraform destroy*": "allow",
91
+ "*terraform import*": "allow",
92
+ "*terraform state rm*": "allow",
93
+ "*terraform state mv*": "allow",
94
+ "*terraform taint*": "allow",
95
+ "*terraform apply -replace*": "allow",
96
+ "*terraform state replace-provider*": "allow",
97
+ "*gcloud *": "allow",
98
+ "*rm -rf*": "allow",
99
+ "*rm -r*": "allow",
100
+ "chmod 777": "allow",
101
+ "chmod -R": "allow",
102
+ "chown -R": "allow",
103
+ "*curl http*": "allow",
104
+ "*wget http*": "allow",
105
+ "*sudo*": "allow",
106
+ "*su*": "allow",
107
+ "bd*": "allow",
108
+ "*apt install*": "allow",
109
+ "*apt-get install*": "allow",
110
+ "*pip install*": "allow",
111
+ "*pnpm install -g*": "allow",
112
+ "*yarn global add*": "allow",
113
+ "*systemctl*": "allow",
114
+ "*service*": "allow",
115
+ "*kill -9*": "allow",
116
+ "*killall*": "allow",
117
+ "*mount*": "allow",
118
+ "*umount*": "allow",
119
+ "*fdisk*": "allow",
120
+ "*mkfs*": "allow",
121
+ "*dd*": "allow",
122
+ "*ssh*": "allow",
123
+ "*scp*": "allow",
124
+ "*rsync*": "allow",
125
+ "*supabase*": "allow",
126
+ "*psql*": "allow"
127
+ }
128
+ }
129
+ }
130
+ EOF
131
+ }
132
+
133
+ # Check if bd (beads CLI) is available
134
+ check_beads_installed() {
135
+ if ! command -v bd &> /dev/null; then
136
+ log_error "Beads CLI (bd) not found. Please install it first:"
137
+ echo " curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash"
138
+ echo " Or: npm install -g @beads/bd"
139
+ exit 1
140
+ fi
141
+ log_info "Beads CLI found: $(which bd)"
142
+ }
143
+
144
+ # Check if opencode is available
145
+ check_opencode_installed() {
146
+ if ! command -v opencode &> /dev/null; then
147
+ log_error "OpenCode CLI not found. Please install it first:"
148
+ echo " npm install -g opencode"
149
+ exit 1
150
+ fi
151
+ log_info "OpenCode CLI found: $(which opencode)"
152
+ }
153
+
154
+ # Initialize Beads if not already initialized
155
+ init_beads_if_needed() {
156
+ if [ ! -d "$BEADS_DIR" ]; then
157
+ if [ "$INIT_BEADS" = true ]; then
158
+ log_info "Initializing Beads in $WORK_DIR..."
159
+ bd init --quiet
160
+ log_success "Beads initialized"
161
+ else
162
+ log_error "Beads not initialized. Run with --init-beads or manually run 'bd init'"
163
+ exit 1
164
+ fi
165
+ else
166
+ log_info "Beads directory found at $BEADS_DIR"
167
+ fi
168
+ }
169
+
170
+ # Sync with remote to get latest tasks and push any local changes
171
+ sync_with_remote() {
172
+ log_info "Syncing with remote..."
173
+
174
+ # First, pull latest git changes
175
+ if git remote -v | grep -q origin; then
176
+ git pull --rebase origin "$(git branch --show-current)" 2>/dev/null || {
177
+ log_warn "Git pull failed, continuing with local state"
178
+ }
179
+ fi
180
+
181
+ # Then sync beads (imports from JSONL if newer, exports if DB is newer)
182
+ bd sync 2>/dev/null || {
183
+ log_warn "Beads sync had issues, attempting import..."
184
+ bd import -i .beads/issues.jsonl 2>/dev/null || true
185
+ }
186
+
187
+ log_success "Sync complete"
188
+ }
189
+
190
+ # Check for and recover from interrupted work
191
+ check_interrupted_work() {
192
+ log_info "Checking for interrupted work..."
193
+
194
+ local in_progress=$(bd list --status=in_progress --json 2>/dev/null | jq -r 'length // 0')
195
+
196
+ if [ "$in_progress" -gt 0 ]; then
197
+ log_warn "Found $in_progress task(s) in progress from previous session"
198
+ echo "The agent will attempt to resume or reconcile these tasks."
199
+
200
+ # Show the in-progress tasks
201
+ bd list --status=in_progress --json 2>/dev/null | jq -r '.[] | " - \(.id): \(.title)"'
202
+ else
203
+ log_info "No interrupted work found"
204
+ fi
205
+ }
206
+
207
+ # Get current work status for logging
208
+ get_work_status() {
209
+ local ready=$(bd ready --json 2>/dev/null | jq -r 'length // 0')
210
+ local in_progress=$(bd list --status=in_progress --json 2>/dev/null | jq -r 'length // 0')
211
+ local blocked=$(bd blocked --json 2>/dev/null | jq -r 'length // 0')
212
+ local stats=$(bd stats --json 2>/dev/null)
213
+ local total_open=$(echo "$stats" | jq -r '.open // 0')
214
+ local total_closed=$(echo "$stats" | jq -r '.closed // 0')
215
+
216
+ echo "Tasks: Ready=$ready, In-Progress=$in_progress, Blocked=$blocked, Open=$total_open, Closed=$total_closed"
217
+ }
218
+
219
+ # Save agent state for recovery
220
+ save_state() {
221
+ local iteration=$1
222
+ local session_id=$2
223
+
224
+ cat > "$STATE_FILE" << EOF
225
+ {
226
+ "iteration": $iteration,
227
+ "session_id": "$session_id",
228
+ "timestamp": "$(date -Iseconds)",
229
+ "branch": "$(git branch --show-current 2>/dev/null || echo 'unknown')"
230
+ }
231
+ EOF
232
+ }
233
+
234
+ # Load previous state if it exists
235
+ load_state() {
236
+ if [ -f "$STATE_FILE" ]; then
237
+ cat "$STATE_FILE"
238
+ else
239
+ echo "{}"
240
+ fi
241
+ }
242
+
243
+ # Archive completed run
244
+ archive_run() {
245
+ local run_name=$1
246
+ local archive_dir="$AGENT_DIR/archive"
247
+ local date_prefix=$(date +%Y-%m-%d-%H%M%S)
248
+ local archive_path="$archive_dir/$date_prefix-$run_name"
249
+
250
+ mkdir -p "$archive_path"
251
+
252
+ # Export current beads state for the archive
253
+ bd list --json > "$archive_path/tasks-final.json" 2>/dev/null || true
254
+ bd stats --json > "$archive_path/stats-final.json" 2>/dev/null || true
255
+
256
+ # Copy agent state
257
+ [ -f "$STATE_FILE" ] && cp "$STATE_FILE" "$archive_path/"
258
+
259
+ log_info "Run archived to: $archive_path"
260
+ }
261
+
262
+ # Main execution starts here
263
+ echo ""
264
+ echo "================================================================"
265
+ echo " Beads-Powered Agent Loop"
266
+ echo " Max Iterations: $MAX_ITERATIONS"
267
+ echo " Working Directory: $WORK_DIR"
268
+ echo "================================================================"
269
+ echo ""
270
+
271
+ # Pre-flight checks
272
+ check_beads_installed
273
+ check_opencode_installed
274
+ init_beads_if_needed
275
+ mkdir -p "$AGENT_DIR"
276
+
277
+ # Sync with remote to get latest state
278
+ sync_with_remote
279
+
280
+ # Check for interrupted work from previous runs
281
+ check_interrupted_work
282
+
283
+ # Show current status
284
+ log_info "$(get_work_status)"
285
+
286
+ # Create OpenCode config for autonomous mode
287
+ create_opencode_config
288
+
289
+ # Read the prompt file content
290
+ if [ ! -f "$PROMPT_FILE" ]; then
291
+ log_error "Prompt file not found at $PROMPT_FILE"
292
+ log_error "This may indicate a broken installation. Try reinstalling the plugin."
293
+ exit 1
294
+ fi
295
+
296
+ PROMPT_CONTENT=$(cat "$PROMPT_FILE")
297
+
298
+ # Main iteration loop
299
+ for i in $(seq 1 $MAX_ITERATIONS); do
300
+ echo ""
301
+ echo "================================================================"
302
+ echo " Iteration $i of $MAX_ITERATIONS - $(date '+%Y-%m-%d %H:%M:%S')"
303
+ echo "================================================================"
304
+
305
+ # Sync before each iteration to catch any external changes
306
+ log_info "Pre-iteration sync..."
307
+ sync_with_remote
308
+
309
+ # Show current work status
310
+ log_info "$(get_work_status)"
311
+
312
+ # Check if there's actually work to do
313
+ READY_COUNT=$(bd ready --json 2>/dev/null | jq -r 'length // 0')
314
+ IN_PROGRESS_COUNT=$(bd list --status=in_progress --json 2>/dev/null | jq -r 'length // 0')
315
+
316
+ if [ "$READY_COUNT" -eq 0 ] && [ "$IN_PROGRESS_COUNT" -eq 0 ]; then
317
+ log_info "No ready or in-progress tasks found"
318
+
319
+ # Check if everything is complete
320
+ STATS=$(bd stats --json 2>/dev/null)
321
+ OPEN=$(echo "$STATS" | jq -r '.open // 0')
322
+
323
+ if [ "$OPEN" -eq 0 ]; then
324
+ log_success "All tasks complete!"
325
+ archive_run "completed"
326
+ rm -f "$CONFIG_FILE"
327
+ exit 0
328
+ else
329
+ log_warn "Open tasks exist but none are ready (possibly blocked)"
330
+ bd blocked --json 2>/dev/null | jq -r '.[] | " Blocked: \(.id) - \(.title)"' || true
331
+ fi
332
+ fi
333
+
334
+ # Generate a unique session title for this iteration
335
+ SESSION_TITLE="agent-loop-iteration-$i-$(date +%Y%m%d-%H%M%S)"
336
+
337
+ # Save state before running
338
+ save_state $i "$SESSION_TITLE"
339
+
340
+ # Run opencode in non-interactive mode
341
+ log_info "Starting OpenCode session: $SESSION_TITLE"
342
+
343
+ OUTPUT=$(OPENCODE_CONFIG="$CONFIG_FILE" opencode run \
344
+ --title "$SESSION_TITLE" \
345
+ "$PROMPT_CONTENT" 2>&1 | tee /dev/stderr) || true
346
+
347
+ # Extract session ID from output if available
348
+ SESSION_ID=$(echo "$OUTPUT" | grep -oP 'session[:\s]+\K[a-zA-Z0-9-]+' | head -1 || echo "iteration-$i")
349
+ export OPENCODE_SESSION_ID="$SESSION_ID"
350
+
351
+ # Run ALL E2E tests to verify no regressions (if pnpm e2e exists)
352
+ if [ -f "package.json" ] && grep -q '"e2e"' package.json 2>/dev/null; then
353
+ log_info "Running full E2E test suite..."
354
+ E2E_FAILED=false
355
+ E2E_OUTPUT=$(pnpm e2e --reporter=line 2>&1) || E2E_FAILED=true
356
+
357
+ if [ "$E2E_FAILED" = true ]; then
358
+ log_error "E2E tests failed! Creating blocking task for remediation..."
359
+
360
+ # Save the failure output for context
361
+ E2E_FAILURE_FILE="$AGENT_DIR/.e2e-failure-$i"
362
+ echo "$E2E_OUTPUT" > "$E2E_FAILURE_FILE"
363
+
364
+ # Run opencode with the test-failure prompt to create a blocking beads task
365
+ if [ -f "$TEST_FAILURE_PROMPT_FILE" ]; then
366
+ TEST_FAILURE_PROMPT=$(cat "$TEST_FAILURE_PROMPT_FILE")
367
+ # Replace placeholder with actual test output (first 100 lines to avoid token overflow)
368
+ TEST_OUTPUT_EXCERPT=$(head -100 "$E2E_FAILURE_FILE")
369
+ TEST_FAILURE_PROMPT="${TEST_FAILURE_PROMPT//\{\{E2E_OUTPUT\}\}/$TEST_OUTPUT_EXCERPT}"
370
+
371
+ OPENCODE_CONFIG="$CONFIG_FILE" opencode run \
372
+ --title "e2e-failure-iteration-$i" \
373
+ "$TEST_FAILURE_PROMPT" 2>&1 | tee /dev/stderr || true
374
+ else
375
+ log_warn "Test failure prompt file not found, skipping task creation"
376
+ fi
377
+
378
+ # Clean up failure file
379
+ rm -f "$E2E_FAILURE_FILE"
380
+
381
+ # Continue to next iteration (which should pick up the blocking fix task)
382
+ log_warn "E2E failure recorded as P0 blocking task. Next iteration will prioritize the fix."
383
+ else
384
+ log_success "E2E tests passed"
385
+ fi
386
+ else
387
+ log_info "No E2E tests configured (no 'e2e' script in package.json)"
388
+ fi
389
+
390
+ # Post-iteration sync to ensure all changes are pushed
391
+ log_info "Post-iteration sync..."
392
+ bd sync 2>/dev/null || log_warn "Post-iteration sync had issues"
393
+
394
+ # Push git changes
395
+ if git remote -v | grep -q origin; then
396
+ git push origin "$(git branch --show-current)" 2>/dev/null || {
397
+ log_warn "Git push failed, changes are local only"
398
+ }
399
+ fi
400
+
401
+ # Check for completion signal
402
+ if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
403
+ echo ""
404
+ log_success "Agent completed all tasks!"
405
+ log_info "Completed at iteration $i of $MAX_ITERATIONS"
406
+
407
+ # Final sync and archive
408
+ bd sync 2>/dev/null || true
409
+ archive_run "completed"
410
+
411
+ # Clean up config file
412
+ rm -f "$CONFIG_FILE"
413
+ exit 0
414
+ fi
415
+
416
+ # Show post-iteration status
417
+ log_info "Post-iteration: $(get_work_status)"
418
+
419
+ echo ""
420
+ log_info "Iteration $i complete. Continuing in 2 seconds..."
421
+ sleep 2
422
+ done
423
+
424
+ # Final sync before exit
425
+ log_info "Final sync..."
426
+ bd sync 2>/dev/null || true
427
+
428
+ # Archive the incomplete run
429
+ archive_run "max-iterations-reached"
430
+
431
+ # Clean up config file
432
+ rm -f "$CONFIG_FILE"
433
+
434
+ echo ""
435
+ log_warn "Agent reached max iterations ($MAX_ITERATIONS) without completing all tasks."
436
+ log_info "Final status: $(get_work_status)"
437
+ log_info "Run 'bd ready --json' to see remaining tasks"
438
+ log_info "Run 'bd blocked --json' to see blocked tasks"
439
+ exit 1