wogiflow 2.6.0 → 2.6.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.
package/lib/workspace.js CHANGED
@@ -990,22 +990,32 @@ description: "Workspace worker rules — reply to manager after every channel ta
990
990
 
991
991
  You are **${name}** in a Wogi Workspace managed by an orchestrator.
992
992
 
993
- ## CRITICAL: Always Reply to the Manager
993
+ ## CRITICAL: Always Reply to the Manager (port ${config.channels.managerPort || (config.channels.basePort - 1)})
994
994
 
995
- When you receive a message via the workspace channel (appears as a channel notification), you MUST send results back to the manager after completing the work. **The user only sees the manager terminal — if you don't reply, they never see your results.**
995
+ When you receive a message via the workspace channel, you MUST send results back to the manager after completing the work. **The user only sees the manager terminal — if you don't reply, they never see your results.**
996
996
 
997
- Use the \`workspace_send_message\` MCP tool:
997
+ **PRIMARY method curl to manager port (always works, even if MCP tools haven't loaded yet):**
998
+
999
+ \`\`\`bash
1000
+ curl -s -X POST http://localhost:${config.channels.managerPort || (config.channels.basePort - 1)} -H "Content-Type: text/plain" -H "X-Wogi-From: ${name}" -d "## Results
1001
+
1002
+ [your findings/summary here]"
1003
+ \`\`\`
1004
+
1005
+ **ALTERNATIVE — workspace_send_message MCP tool (if available):**
998
1006
 
999
1007
  \`\`\`
1000
1008
  workspace_send_message(to: "manager", message: "## Results\\n\\n[your findings/summary here]")
1001
1009
  \`\`\`
1002
1010
 
1003
- This is NON-OPTIONAL. Every channel-dispatched task MUST end with a workspace_send_message to "manager".
1011
+ If the MCP tool is not available (shows "MCP servers still connecting"), use the curl command. **Do NOT write JSON files to .workspace/messages/ directly — that doesn't notify the manager.**
1012
+
1013
+ This is NON-OPTIONAL. Every channel-dispatched task MUST end with a reply to the manager.
1004
1014
 
1005
1015
  ## Peer Communication
1006
1016
 
1007
- You can talk to peer repos using the same tool:
1008
- ${peerNames.map(p => `- \`workspace_send_message(to: "${p}", message: "...")\``).join('\n')}
1017
+ You can talk to peer repos via curl or the MCP tool:
1018
+ ${peerNames.map(p => `- \`curl -s -X POST http://localhost:${channelMembers[p]?.port || '???'} -H "X-Wogi-From: ${name}" -d "your message"\``).join('\n')}
1009
1019
 
1010
1020
  ## Autonomous Mode — Auto-Approve Everything
1011
1021
 
@@ -1024,7 +1034,7 @@ Only send a question to the manager (instead of results) when:
1024
1034
  - The task requires **deleting user data** or making irreversible changes
1025
1035
  - You are genuinely **stuck** and cannot proceed
1026
1036
 
1027
- To escalate: \`workspace_send_message(to: "manager", message: "## Need Decision\\n\\n[describe the choice and options]")\`
1037
+ To escalate: \`curl -s -X POST http://localhost:${config.channels.managerPort || (config.channels.basePort - 1)} -H "X-Wogi-From: ${name}" -d "## Need Decision: [describe the choice and options]"\`
1028
1038
 
1029
1039
  For everything else — just do the work and report results.
1030
1040
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "2.6.0",
3
+ "version": "2.6.1",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -61,22 +61,19 @@ runHook('Stop', async ({ parsedInput }) => {
61
61
  };
62
62
  }
63
63
 
64
- // Workspace worker: auto-write results back to manager when stopping
65
- // This runs in the hook (not the AI), so it's guaranteed to execute.
66
- // The AI can't be relied on to call workspace_send_message the stop
67
- // hook fires before the AI gets a chance, or the AI forgets.
68
- if (process.env.WOGI_WORKSPACE_ROOT && process.env.WOGI_REPO_NAME) {
64
+ // Workspace worker: send results to manager via HTTP when stopping.
65
+ // Uses synchronous curl to guarantee delivery before the hook process exits.
66
+ // The async http.request approach was unreliable process exited before request completed.
67
+ if (process.env.WOGI_MANAGER_PORT && process.env.WOGI_REPO_NAME && process.env.WOGI_REPO_NAME !== 'manager') {
69
68
  try {
69
+ const { execSync } = require('node:child_process');
70
70
  const fs = require('node:fs');
71
71
  const path = require('node:path');
72
- const crypto = require('node:crypto');
73
- const workspaceRoot = process.env.WOGI_WORKSPACE_ROOT;
74
72
  const repoName = process.env.WOGI_REPO_NAME;
75
- const messagesDir = path.join(workspaceRoot, '.workspace', 'messages');
76
- fs.mkdirSync(messagesDir, { recursive: true });
73
+ const managerPort = process.env.WOGI_MANAGER_PORT;
77
74
 
78
75
  // Build summary from available state
79
- const summary = [];
76
+ const summaryParts = [];
80
77
  const { PATHS, safeJsonParse } = require('../../flow-utils');
81
78
 
82
79
  // Get current/recently completed task info
@@ -86,88 +83,40 @@ runHook('Stop', async ({ parsedInput }) => {
86
83
  const task = recentTask || inProgressTask;
87
84
 
88
85
  if (task) {
89
- summary.push(`**Task**: ${task.title || task.id}`);
90
- if (task.type) summary.push(`**Type**: ${task.type}`);
86
+ summaryParts.push(`**Task**: ${task.title || task.id}`);
87
+ if (task.type) summaryParts.push(`**Type**: ${task.type}`);
91
88
  }
92
89
 
93
- // Get last request-log entry
94
- try {
95
- const logPath = path.join(PATHS.root, 'request-log.md');
96
- if (fs.existsSync(logPath)) {
97
- const stat = fs.statSync(logPath);
98
- if (stat.size < 200 * 1024) {
99
- const logContent = fs.readFileSync(logPath, 'utf-8');
100
- const parts = logContent.split(/^### R-/m);
101
- if (parts.length > 1) {
102
- const lastEntry = parts[parts.length - 1];
103
- if (lastEntry.length < 2000) {
104
- summary.push(`**Log entry**:\n### R-${lastEntry.trim()}`);
105
- }
106
- }
107
- }
108
- }
109
- } catch (_err) { /* non-critical */ }
110
-
111
- // Get git status for changed files
90
+ // Get changed files
112
91
  try {
113
- const { execSync } = require('node:child_process');
114
92
  const diff = execSync('git diff --name-only HEAD 2>/dev/null || true', { cwd: PATHS.root, encoding: 'utf-8' }).trim();
115
93
  const staged = execSync('git diff --name-only --staged 2>/dev/null || true', { cwd: PATHS.root, encoding: 'utf-8' }).trim();
116
94
  const allChanged = [...new Set([...diff.split('\n'), ...staged.split('\n')].filter(Boolean))];
117
95
  if (allChanged.length > 0) {
118
- summary.push(`**Files changed**: ${allChanged.join(', ')}`);
96
+ summaryParts.push(`**Files changed**: ${allChanged.join(', ')}`);
119
97
  }
120
98
  } catch (_err) { /* non-critical */ }
121
99
 
122
- const msgId = 'msg-' + crypto.randomBytes(4).toString('hex');
123
- const message = {
124
- id: msgId,
125
- from: repoName,
126
- to: 'manager',
127
- type: 'task-complete',
128
- priority: 'medium',
129
- timestamp: new Date().toISOString(),
130
- subject: task ? `Completed: ${task.title || task.id}` : `Work completed by ${repoName}`,
131
- body: summary.join('\n') || `${repoName} finished processing.`,
132
- taskId: task?.id || null,
133
- actionRequired: false,
134
- status: 'pending'
135
- };
136
-
137
- // Write to file (fallback / persistent record)
138
- fs.writeFileSync(path.join(messagesDir, `${msgId}.json`), JSON.stringify(message, null, 2));
100
+ const body = summaryParts.join('\n') || `${repoName} finished processing.`;
139
101
 
140
- // Also HTTP POST to manager's channel port for real-time notification
141
- // This makes the message appear as a prompt in the manager's session immediately
142
- const managerPort = process.env.WOGI_MANAGER_PORT;
143
- if (managerPort && repoName !== 'manager') {
144
- try {
145
- const http = require('node:http');
146
- const body = Buffer.from(message.body, 'utf-8');
147
- const req = http.request({
148
- hostname: '127.0.0.1',
149
- port: parseInt(managerPort, 10),
150
- path: '/',
151
- method: 'POST',
152
- headers: {
153
- 'Content-Type': 'text/plain',
154
- 'Content-Length': body.byteLength,
155
- 'X-Wogi-From': repoName
156
- }
157
- });
158
- req.on('error', () => { /* best effort — file is the fallback */ });
159
- req.write(body);
160
- req.end();
161
- } catch (_err) { /* non-critical */ }
162
- }
163
-
164
- if (process.env.DEBUG) {
165
- console.error(`[Stop] Workspace message written: ${msgId}${managerPort ? ` + HTTP to manager:${managerPort}` : ''}`);
102
+ // PRIMARY: Synchronous curl to manager port guaranteed to complete before exit
103
+ try {
104
+ execSync(
105
+ `curl -s -X POST http://127.0.0.1:${managerPort} -H "Content-Type: text/plain" -H "X-Wogi-From: ${repoName}" --data-binary @-`,
106
+ { input: body, timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }
107
+ );
108
+ if (process.env.DEBUG) {
109
+ console.error(`[Stop] Sent results to manager via curl :${managerPort}`);
110
+ }
111
+ } catch (_err) {
112
+ // Manager might be offline — that's OK
113
+ if (process.env.DEBUG) {
114
+ console.error(`[Stop] curl to manager:${managerPort} failed: ${_err.message}`);
115
+ }
166
116
  }
167
117
  } catch (err) {
168
- // Non-critical — best effort
169
118
  if (process.env.DEBUG) {
170
- console.error(`[Stop] Workspace message failed: ${err.message}`);
119
+ console.error(`[Stop] Workspace notification failed: ${err.message}`);
171
120
  }
172
121
  }
173
122
  }