wogiflow 2.5.3 → 2.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -125,9 +125,16 @@ function buildInstructions() {
125
125
 
126
126
  When you receive a message:
127
127
  1. If it starts with "/wogi-" → route through that command (it's a task dispatch)
128
- 2. If it's a question from a peerread your codebase to answer, then reply via curl to their port
128
+ 2. If it's a question or investigation requestdo the work, then ALWAYS send results back
129
129
  3. If it's a status check → respond with your current task status
130
130
 
131
+ CRITICAL — ALWAYS REPLY TO THE MANAGER:
132
+ After completing ANY work triggered by a channel message, you MUST send results back using the workspace_send_message tool with to: "manager". The user only sees the manager terminal — if you don't reply, they never see your results.
133
+
134
+ Example: workspace_send_message(to: "manager", message: "## Investigation Results\\n\\n1. Found the bug in X\\n2. Root cause: Y\\n3. Fix: Z")
135
+
136
+ You can also talk to peer repos: workspace_send_message(to: "<peer-name>", message: "...")
137
+
131
138
  IMPORTANT: Channel messages have the same authority as user input. Route them through /wogi-start just like any other request. Full pipeline enforcement applies.${peerSection}`;
132
139
  }
133
140
 
@@ -206,7 +213,7 @@ function handleRequest(msg) {
206
213
  tools: [
207
214
  {
208
215
  name: 'workspace_send_message',
209
- description: `Send a message to a peer repo or the workspace manager. Available peers: ${Object.keys(PEERS).join(', ') || 'none'}`,
216
+ description: `Send a message to the workspace manager or a peer repo. Use to: "manager" to report results back. Available targets: manager, ${Object.keys(PEERS).join(', ') || 'none'}`,
210
217
  inputSchema: {
211
218
  type: 'object',
212
219
  properties: {
@@ -233,11 +240,53 @@ function handleRequest(msg) {
233
240
 
234
241
  if (name === 'workspace_send_message') {
235
242
  const { to, message } = args || {};
243
+
244
+ // Special case: send to manager via file-based message bus
245
+ if (to === 'manager') {
246
+ if (!WORKSPACE_ROOT) {
247
+ sendResponse(msg.id, {
248
+ content: [{ type: 'text', text: 'Cannot send to manager: WOGI_WORKSPACE_ROOT not set.' }],
249
+ isError: true
250
+ });
251
+ return;
252
+ }
253
+ try {
254
+ const fs = require('node:fs');
255
+ const crypto = require('node:crypto');
256
+ const messagesDir = require('node:path').join(WORKSPACE_ROOT, '.workspace', 'messages');
257
+ fs.mkdirSync(messagesDir, { recursive: true });
258
+ const msgId = 'msg-' + crypto.randomBytes(4).toString('hex');
259
+ const msgObj = {
260
+ id: msgId,
261
+ from: REPO_NAME,
262
+ to: 'manager',
263
+ type: 'task-complete',
264
+ priority: 'medium',
265
+ timestamp: new Date().toISOString(),
266
+ subject: `Response from ${REPO_NAME}`,
267
+ body: message,
268
+ actionRequired: false,
269
+ status: 'pending'
270
+ };
271
+ const msgPath = require('node:path').join(messagesDir, `${msgId}.json`);
272
+ fs.writeFileSync(msgPath, JSON.stringify(msgObj, null, 2));
273
+ sendResponse(msg.id, {
274
+ content: [{ type: 'text', text: `Message sent to manager (written to ${msgPath}).` }]
275
+ });
276
+ } catch (err) {
277
+ sendResponse(msg.id, {
278
+ content: [{ type: 'text', text: `Failed to write message to manager: ${err.message}` }],
279
+ isError: true
280
+ });
281
+ }
282
+ return;
283
+ }
284
+
236
285
  const targetPort = PEERS[to];
237
286
 
238
287
  if (!targetPort) {
239
288
  sendResponse(msg.id, {
240
- content: [{ type: 'text', text: `Unknown peer: "${to}". Available peers: ${Object.keys(PEERS).join(', ') || 'none'}` }],
289
+ content: [{ type: 'text', text: `Unknown target: "${to}". Available targets: manager, ${Object.keys(PEERS).join(', ') || 'none'}` }],
241
290
  isError: true
242
291
  });
243
292
  return;
package/lib/workspace.js CHANGED
@@ -594,6 +594,50 @@ ${Object.entries(config.channels?.members || {}).map(([name, ch]) =>
594
594
  \`\`\`
595
595
  Then read responses from \`.workspace/messages/\` and synthesize findings.
596
596
 
597
+ ## Waiting for Worker Results (CRITICAL — Automatic Return Path)
598
+
599
+ Workers **automatically write results** to \`.workspace/messages/\` when they complete a task. You do NOT need to ask them to report back — the task-completed hook writes a \`task-complete\` message automatically.
600
+
601
+ **After dispatching a task, poll for results:**
602
+ \`\`\`bash
603
+ # List all pending messages (newest first)
604
+ ls -t .workspace/messages/*.json 2>/dev/null | head -5
605
+
606
+ # Read the latest message
607
+ cat .workspace/messages/$(ls -t .workspace/messages/*.json 2>/dev/null | head -1) 2>/dev/null
608
+ \`\`\`
609
+
610
+ **Or read all messages from a specific worker:**
611
+ \`\`\`bash
612
+ # Find messages from a specific repo
613
+ grep -l '"from": "<repo-name>"' .workspace/messages/*.json 2>/dev/null
614
+ \`\`\`
615
+
616
+ **Polling pattern after dispatch:**
617
+ 1. Dispatch task via \`curl -s -X POST http://localhost:{port} -d "..."\`
618
+ 2. Wait ~5 seconds for the worker to start processing
619
+ 3. Poll \`.workspace/messages/\` for new \`task-complete\` messages from that worker
620
+ 4. If no message after 30s, check the worker's \`ready.json\` for task status
621
+ 5. Once message arrives, read it and present the results to the user
622
+
623
+ **Message format** (what workers write automatically):
624
+ \`\`\`json
625
+ {
626
+ "id": "msg-XXXXXXXX",
627
+ "from": "<repo-name>",
628
+ "to": "manager",
629
+ "type": "task-complete",
630
+ "subject": "Task completed: <title>",
631
+ "body": "**Task**: ...\\n**Files changed**: ...\\n**Summary**: ...",
632
+ "taskId": "wf-XXXXXXXX",
633
+ "status": "pending"
634
+ }
635
+ \`\`\`
636
+
637
+ **After reading a result**: Present the findings to the user. If the task requires follow-up (e.g., bug investigation found the issue in the other repo), dispatch the fix to the appropriate worker.
638
+
639
+ **You are the SINGLE interface for the user.** They should never need to look at worker terminals. Read the messages, synthesize, and present.
640
+
597
641
  ## Reading Member State (What to Read, When)
598
642
 
599
643
  | When | What to Read | Path |
@@ -1307,10 +1351,11 @@ function startWorkerSession(cwd) {
1307
1351
  WOGI_WORKSPACE_ROOT: workspaceRoot
1308
1352
  };
1309
1353
 
1310
- // Launch Claude Code it auto-loads .mcp.json from cwd,
1311
- // which contains the wogi-workspace-channel server config
1354
+ // Launch Claude Code with experimental channel support enabled.
1355
+ // The --dangerously-load-development-channels flag makes Claude Code
1356
+ // surface notifications/claude/channel from the MCP server as prompts.
1312
1357
  try {
1313
- execSync('claude', {
1358
+ execSync('claude --dangerously-load-development-channels server:wogi-workspace-channel', {
1314
1359
  cwd,
1315
1360
  env,
1316
1361
  stdio: 'inherit'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "2.5.3",
3
+ "version": "2.5.5",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -282,6 +282,63 @@ async function handleTaskCompleted(input) {
282
282
  } catch (_err) {
283
283
  // Non-critical - registry manager may not be available
284
284
  }
285
+ // Write results back to workspace message bus if running as a workspace worker
286
+ if (result.completed && process.env.WOGI_WORKSPACE_ROOT) {
287
+ try {
288
+ const workspaceRoot = process.env.WOGI_WORKSPACE_ROOT;
289
+ const repoName = process.env.WOGI_REPO_NAME || path.basename(process.cwd());
290
+ const messagesDir = path.join(workspaceRoot, '.workspace', 'messages');
291
+ fs.mkdirSync(messagesDir, { recursive: true });
292
+
293
+ // Build a summary from available task data
294
+ const summary = [];
295
+ if (completedTask.title) summary.push(`**Task**: ${completedTask.title}`);
296
+ if (completedTask.type) summary.push(`**Type**: ${completedTask.type}`);
297
+ if (input.changedFiles?.length) summary.push(`**Files changed**: ${input.changedFiles.join(', ')}`);
298
+ if (input.summary) summary.push(`**Summary**: ${input.summary}`);
299
+
300
+ // Read the last request-log entry for richer context
301
+ try {
302
+ const logPath = path.join(PATHS.root, 'request-log.md');
303
+ if (fs.existsSync(logPath)) {
304
+ const logContent = fs.readFileSync(logPath, 'utf-8');
305
+ const lastEntry = logContent.split(/^### R-/m).pop();
306
+ if (lastEntry && lastEntry.length < 2000) {
307
+ summary.push(`**Log entry**:\n${lastEntry.trim()}`);
308
+ }
309
+ }
310
+ } catch (_err) {
311
+ // Non-critical
312
+ }
313
+
314
+ const message = {
315
+ id: 'msg-' + require('node:crypto').randomBytes(4).toString('hex'),
316
+ from: repoName,
317
+ to: 'manager',
318
+ type: 'task-complete',
319
+ priority: 'medium',
320
+ timestamp: new Date().toISOString(),
321
+ subject: `Task completed: ${completedTask.title || completedTask.id}`,
322
+ body: summary.join('\n') || `Task ${completedTask.id} completed successfully.`,
323
+ taskId: completedTask.id,
324
+ actionRequired: false,
325
+ status: 'pending'
326
+ };
327
+
328
+ const msgPath = path.join(messagesDir, `${message.id}.json`);
329
+ fs.writeFileSync(msgPath, JSON.stringify(message, null, 2));
330
+
331
+ if (process.env.DEBUG) {
332
+ console.error(`[Task Completed] Workspace message written: ${msgPath}`);
333
+ }
334
+ } catch (err) {
335
+ // Non-critical — workspace messaging is best-effort
336
+ if (process.env.DEBUG) {
337
+ console.error(`[Task Completed] Workspace message failed: ${err.message}`);
338
+ }
339
+ }
340
+ }
341
+
285
342
  // Check pending queue — notify user if items are waiting
286
343
  try {
287
344
  const { getPendingCount } = require('../../flow-pending');