wogiflow 2.4.4 → 2.5.0
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/.claude/commands/wogi-audit.md +26 -0
- package/.claude/commands/wogi-review.md +29 -0
- package/.claude/docs/explore-agents.md +8 -2
- package/lib/workspace-channel-server.js +364 -0
- package/lib/workspace-routing.js +301 -4
- package/lib/workspace.js +313 -37
- package/package.json +1 -1
- package/scripts/flow-schema-drift.js +837 -0
package/lib/workspace.js
CHANGED
|
@@ -328,14 +328,24 @@ function generateWorkspaceConfig(workspaceName, members) {
|
|
|
328
328
|
sync: {
|
|
329
329
|
autoOnSessionStart: true,
|
|
330
330
|
autoAfterTaskComplete: true
|
|
331
|
+
},
|
|
332
|
+
channels: {
|
|
333
|
+
enabled: true,
|
|
334
|
+
basePort: 8801,
|
|
335
|
+
members: {}
|
|
331
336
|
}
|
|
332
337
|
};
|
|
333
338
|
|
|
339
|
+
let port = config.channels.basePort;
|
|
340
|
+
if (port + members.length - 1 > 65535) {
|
|
341
|
+
throw new Error(`Channel port range ${port}-${port + members.length - 1} exceeds maximum port 65535. Reduce basePort or number of members.`);
|
|
342
|
+
}
|
|
334
343
|
for (const member of members) {
|
|
335
344
|
config.members[member.name] = {
|
|
336
345
|
path: `./${member.name}`,
|
|
337
346
|
role: member.role
|
|
338
347
|
};
|
|
348
|
+
config.channels.members[member.name] = { port: port++ };
|
|
339
349
|
}
|
|
340
350
|
|
|
341
351
|
return config;
|
|
@@ -546,49 +556,43 @@ ${Object.entries(manifest.members).map(([name, m]) => {
|
|
|
546
556
|
}).join('\n')}
|
|
547
557
|
- **Both/all repos**: api contract, schema change, integration, full-stack, end-to-end
|
|
548
558
|
|
|
549
|
-
### Step 3:
|
|
550
|
-
|
|
551
|
-
**Single-repo task — spawn one sub-agent:**
|
|
552
|
-
\`\`\`
|
|
553
|
-
Use the Agent tool:
|
|
554
|
-
prompt: "You are working in the <REPO> repository.
|
|
555
|
-
Stack: <STACK>
|
|
556
|
-
Task: <TASK DESCRIPTION>
|
|
559
|
+
### Step 3: Dispatch via Channels
|
|
557
560
|
|
|
558
|
-
|
|
559
|
-
<PASTE DECISIONS CONTENT>
|
|
561
|
+
Each worker repo runs a full Claude Code session with its own CLAUDE.md, hooks, and WogiFlow pipeline. You dispatch tasks by sending HTTP requests to their channel ports. **Do NOT use the Agent tool for implementation** — use channels for full enforcement.
|
|
560
562
|
|
|
561
|
-
|
|
563
|
+
**Worker Channel Ports:**
|
|
564
|
+
${Object.entries(config.channels?.members || {}).map(([name, ch]) => `- **${name}**: \`http://localhost:${ch.port}\``).join('\n')}
|
|
562
565
|
|
|
563
|
-
|
|
564
|
-
|
|
566
|
+
**Check if workers are running:**
|
|
567
|
+
\`\`\`bash
|
|
568
|
+
${Object.entries(config.channels?.members || {}).map(([name, ch]) => `curl -s http://localhost:${ch.port}/health`).join('\n')}
|
|
565
569
|
\`\`\`
|
|
566
570
|
|
|
567
|
-
|
|
568
|
-
1. Read the contract from \`.workspace/contracts/\` (if exists)
|
|
569
|
-
2. If the API contract needs updating, update it first
|
|
570
|
-
3. Spawn provider sub-agent (${providers.join(' or ')}) — implement the API side
|
|
571
|
-
4. Read what the provider changed (from its git diff or commit message)
|
|
572
|
-
5. Write a message to \`.workspace/messages/\` describing the change
|
|
573
|
-
6. Spawn consumer sub-agent (${consumers.join(' or ')}) — implement the client side, include the message as context
|
|
574
|
-
7. Verify both sides work together
|
|
571
|
+
If a worker is down, tell the user: "Start the ${'{'}repo{'}'} worker: \`cd ${'{'}repo{'}'}/ && flow workspace start\`"
|
|
575
572
|
|
|
576
|
-
**
|
|
577
|
-
|
|
578
|
-
|
|
573
|
+
**Single-repo task:**
|
|
574
|
+
1. Create task in the repo's ready.json using \`decomposeToRepoTasks()\` or directly
|
|
575
|
+
2. Dispatch: \`curl -s -X POST http://localhost:${'{'}port{'}'} -d "/wogi-start ${'{'}taskId${'}'}"\`
|
|
576
|
+
3. The worker's full WogiFlow pipeline handles the rest (explore, spec, implement, verify)
|
|
577
|
+
4. Monitor: check \`.workspace/messages/\` for a \`task-complete\` message
|
|
579
578
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
579
|
+
**Cross-repo task:**
|
|
580
|
+
1. Read the contract from \`.workspace/contracts/\` (if exists)
|
|
581
|
+
2. If the API contract needs updating, update it first
|
|
582
|
+
3. Create tasks in each repo's ready.json (provider gets P0 priority)
|
|
583
|
+
4. Dispatch to provider first: \`curl -s -X POST http://localhost:${providers[0] ? config.channels?.members?.[providers[0]]?.port || '{port}' : '{port}'} -d "/wogi-start ${'{'}providerTaskId${'}'}"\`
|
|
584
|
+
5. Wait for provider completion message in \`.workspace/messages/\`
|
|
585
|
+
6. Then dispatch to consumer: \`curl -s -X POST http://localhost:${consumers[0] ? config.channels?.members?.[consumers[0]]?.port || '{port}' : '{port}'} -d "/wogi-start ${'{'}consumerTaskId${'}'}"\`
|
|
586
|
+
7. Wait for consumer completion, then verify integration
|
|
587
|
+
|
|
588
|
+
**Bug investigation — dispatch to all workers:**
|
|
589
|
+
\`\`\`bash
|
|
590
|
+
# Send investigation request to all workers in parallel
|
|
591
|
+
${Object.entries(config.channels?.members || {}).map(([name, ch]) =>
|
|
592
|
+
`curl -s -X POST http://localhost:${ch.port} -d "Investigate: <BUG_DESCRIPTION>. Check recent changes, error logs, relevant code. Report back via workspace message."`
|
|
593
|
+
).join('\n')}
|
|
591
594
|
\`\`\`
|
|
595
|
+
Then read responses from \`.workspace/messages/\` and synthesize findings.
|
|
592
596
|
|
|
593
597
|
## Reading Member State (What to Read, When)
|
|
594
598
|
|
|
@@ -637,6 +641,51 @@ When spawning a sub-agent, check for pending messages to that repo and include t
|
|
|
637
641
|
Shared API contracts: \`.workspace/contracts/\`
|
|
638
642
|
When a provider changes an endpoint, update the contract BEFORE the consumer implements.
|
|
639
643
|
|
|
644
|
+
## Peer Communication (Worker ↔ Worker)
|
|
645
|
+
|
|
646
|
+
Workers can talk directly to each other — no manager bottleneck. Each worker knows its peers' channel ports.
|
|
647
|
+
|
|
648
|
+
A worker asking its peer a question:
|
|
649
|
+
\`\`\`bash
|
|
650
|
+
curl -s -X POST http://localhost:{peer_port} -H "X-Wogi-From: {my_repo}" -d "Is the POST /users endpoint ready? What's the expected payload shape?"
|
|
651
|
+
\`\`\`
|
|
652
|
+
|
|
653
|
+
The peer receives this as a channel event, reads its codebase to answer, and can reply back the same way. This is useful when:
|
|
654
|
+
- Frontend needs to know the exact API shape before implementing
|
|
655
|
+
- Backend wants to know which fields the frontend actually uses
|
|
656
|
+
- Any repo needs real-time coordination without going through the manager
|
|
657
|
+
|
|
658
|
+
Workers also have a \`workspace_send_message\` MCP tool that handles this automatically.
|
|
659
|
+
|
|
660
|
+
## Command Routing (CRITICAL)
|
|
661
|
+
|
|
662
|
+
**You are an orchestrator — you do NOT execute code-level commands locally.** When the user invokes a WogiFlow command, route it based on this table:
|
|
663
|
+
|
|
664
|
+
| Command | Action | Why |
|
|
665
|
+
|---------|--------|-----|
|
|
666
|
+
| \`/wogi-review\` | **Dispatch to ALL workers** → aggregate findings + cross-repo analysis | Workers have the source code and full pipeline |
|
|
667
|
+
| \`/wogi-audit\` | **Dispatch to ALL workers** → aggregate audit results + cross-repo analysis | Same reason |
|
|
668
|
+
| \`/wogi-test\` | **Dispatch to ALL workers** → aggregate test results | Tests run in each repo's environment |
|
|
669
|
+
| \`/wogi-health\` | **Dispatch to ALL workers** → unified health report | Each repo has its own workflow state |
|
|
670
|
+
| \`/wogi-start "task"\` | **Analyze → decompose → dispatch** to correct worker(s) | Already designed for this |
|
|
671
|
+
| \`/wogi-status\` | Run **locally** — workspace-level overview | Reads workspace state files |
|
|
672
|
+
| \`/wogi-ready\` | Run **locally** — cross-repo task queue | Reads all repos' ready.json |
|
|
673
|
+
| \`/wogi-session-end\` | Run **locally** — end workspace session | Workspace-level action |
|
|
674
|
+
|
|
675
|
+
**How to dispatch a command to all workers:**
|
|
676
|
+
\`\`\`bash
|
|
677
|
+
${Object.entries(config.channels?.members || {}).map(([name, ch]) =>
|
|
678
|
+
`curl -s -X POST http://localhost:${ch.port} -d "/wogi-review" # → ${name}`
|
|
679
|
+
).join('\n')}
|
|
680
|
+
\`\`\`
|
|
681
|
+
|
|
682
|
+
**After all workers complete:** Run a cross-repo analysis that only you can do:
|
|
683
|
+
1. Collect findings from \`.workspace/messages/\` (workers write results there)
|
|
684
|
+
2. Check API contract alignment between provider and consumer
|
|
685
|
+
3. Check for integration gaps (orphaned consumers, drifted contracts)
|
|
686
|
+
4. Check shared decisions compliance (\`.workspace/state/decisions.md\`)
|
|
687
|
+
5. Present unified report: per-repo findings + cross-repo findings
|
|
688
|
+
|
|
640
689
|
## Workspace Commands
|
|
641
690
|
|
|
642
691
|
| Command | What it does |
|
|
@@ -645,6 +694,7 @@ When a provider changes an endpoint, update the contract BEFORE the consumer imp
|
|
|
645
694
|
| \`flow workspace status\` | Show all repos, tasks, messages, contracts |
|
|
646
695
|
| \`flow workspace add <path>\` | Add a new member repo |
|
|
647
696
|
| \`flow workspace remove <name>\` | Remove a member repo |
|
|
697
|
+
| \`flow workspace start\` | Start a worker session (run from within a member repo) |
|
|
648
698
|
|
|
649
699
|
## File Reference
|
|
650
700
|
|
|
@@ -690,7 +740,13 @@ function generateWorkspaceSettings(memberNames) {
|
|
|
690
740
|
'Bash(node --check *)',
|
|
691
741
|
'Bash(node scripts/*)',
|
|
692
742
|
'Bash(ls *)',
|
|
693
|
-
'Bash(cat *)'
|
|
743
|
+
'Bash(cat *)',
|
|
744
|
+
'Bash(curl http://localhost:*)',
|
|
745
|
+
'Bash(curl http://127.0.0.1:*)',
|
|
746
|
+
'Bash(curl -s http://localhost:*)',
|
|
747
|
+
'Bash(curl -s http://127.0.0.1:*)',
|
|
748
|
+
'Bash(curl -s -X POST http://localhost:*)',
|
|
749
|
+
'Bash(curl -s -X POST http://127.0.0.1:*)'
|
|
694
750
|
];
|
|
695
751
|
|
|
696
752
|
for (const name of (memberNames || [])) {
|
|
@@ -785,6 +841,95 @@ state/contract-versions.json
|
|
|
785
841
|
}
|
|
786
842
|
}
|
|
787
843
|
|
|
844
|
+
// ============================================================
|
|
845
|
+
// ============================================================
|
|
846
|
+
// Channel MCP Config Generation
|
|
847
|
+
// ============================================================
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Generate .mcp.json for each member repo with channel server config.
|
|
851
|
+
* This allows workers to receive task dispatches from the manager
|
|
852
|
+
* and communicate with peer repos via channels.
|
|
853
|
+
*
|
|
854
|
+
* @param {string} workspaceRoot
|
|
855
|
+
* @param {Object} config — workspace config (with channels section)
|
|
856
|
+
*/
|
|
857
|
+
function generateMemberMcpConfigs(workspaceRoot, config) {
|
|
858
|
+
const channelMembers = config.channels?.members || {};
|
|
859
|
+
const memberNames = Object.keys(channelMembers);
|
|
860
|
+
|
|
861
|
+
const VALID_NAME = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
862
|
+
|
|
863
|
+
for (const [name, channelConfig] of Object.entries(channelMembers)) {
|
|
864
|
+
if (!VALID_NAME.test(name)) {
|
|
865
|
+
console.error(` ✗ ${name}: invalid member name (must match [a-zA-Z0-9_-]) — skipping`);
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const memberPath = path.resolve(workspaceRoot, config.members[name]?.path || `./${name}`);
|
|
870
|
+
|
|
871
|
+
// Path traversal guard — ensure member path is inside workspace root
|
|
872
|
+
if (!memberPath.startsWith(workspaceRoot + path.sep) && memberPath !== workspaceRoot) {
|
|
873
|
+
console.error(` ✗ ${name}: path escapes workspace root (${config.members[name]?.path}) — skipping`);
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const mcpJsonPath = path.join(memberPath, '.mcp.json');
|
|
878
|
+
|
|
879
|
+
// Build peer list (all other members)
|
|
880
|
+
const peers = memberNames
|
|
881
|
+
.filter(n => n !== name)
|
|
882
|
+
.map(n => `${n}:${channelMembers[n].port}`)
|
|
883
|
+
.join(',');
|
|
884
|
+
|
|
885
|
+
// Resolve channel server script path
|
|
886
|
+
// Use require.resolve to find the installed wogiflow package
|
|
887
|
+
let channelServerPath;
|
|
888
|
+
try {
|
|
889
|
+
channelServerPath = require.resolve('wogiflow/lib/workspace-channel-server.js');
|
|
890
|
+
} catch (_err) {
|
|
891
|
+
// Fallback: relative path from workspace root
|
|
892
|
+
channelServerPath = path.join(__dirname, 'workspace-channel-server.js');
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
const mcpConfig = {
|
|
896
|
+
mcpServers: {
|
|
897
|
+
'wogi-workspace-channel': {
|
|
898
|
+
command: 'node',
|
|
899
|
+
args: [channelServerPath],
|
|
900
|
+
env: {
|
|
901
|
+
WOGI_CHANNEL_PORT: String(channelConfig.port),
|
|
902
|
+
WOGI_REPO_NAME: name,
|
|
903
|
+
WOGI_PEERS: peers,
|
|
904
|
+
WOGI_WORKSPACE_ROOT: workspaceRoot
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
// Merge with existing .mcp.json if present
|
|
911
|
+
let existingConfig = {};
|
|
912
|
+
try {
|
|
913
|
+
if (fs.existsSync(mcpJsonPath)) {
|
|
914
|
+
existingConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf-8'));
|
|
915
|
+
}
|
|
916
|
+
} catch (_err) {
|
|
917
|
+
// Ignore malformed existing config
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const merged = {
|
|
921
|
+
...existingConfig,
|
|
922
|
+
mcpServers: {
|
|
923
|
+
...(existingConfig.mcpServers || {}),
|
|
924
|
+
...mcpConfig.mcpServers
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
fs.writeFileSync(mcpJsonPath, JSON.stringify(merged, null, 2));
|
|
929
|
+
console.log(` ✓ ${name}/.mcp.json (channel port ${channelConfig.port}, peers: ${peers || 'none'})`);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
788
933
|
// ============================================================
|
|
789
934
|
// Main Init Function
|
|
790
935
|
// ============================================================
|
|
@@ -899,6 +1044,13 @@ async function initWorkspace(args) {
|
|
|
899
1044
|
JSON.stringify(settings, null, 2)
|
|
900
1045
|
);
|
|
901
1046
|
console.log(' ✓ .claude/settings.json');
|
|
1047
|
+
|
|
1048
|
+
// Generate .mcp.json for each member repo (channel server config)
|
|
1049
|
+
if (config.channels?.enabled) {
|
|
1050
|
+
console.log('');
|
|
1051
|
+
console.log('── Setting up workspace channels ────────────\n');
|
|
1052
|
+
generateMemberMcpConfigs(workspaceRoot, config);
|
|
1053
|
+
}
|
|
902
1054
|
console.log('');
|
|
903
1055
|
|
|
904
1056
|
// Summary
|
|
@@ -981,6 +1133,122 @@ function generateIntegrationMap(manifest) {
|
|
|
981
1133
|
return lines.join('\n');
|
|
982
1134
|
}
|
|
983
1135
|
|
|
1136
|
+
// ============================================================
|
|
1137
|
+
// Worker Session Launcher
|
|
1138
|
+
// ============================================================
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Start a Claude Code worker session with the workspace channel enabled.
|
|
1142
|
+
* Must be run from within a member repo directory.
|
|
1143
|
+
*
|
|
1144
|
+
* @param {string} cwd — current working directory (should be a member repo)
|
|
1145
|
+
*/
|
|
1146
|
+
function startWorkerSession(cwd) {
|
|
1147
|
+
const { execSync } = require('node:child_process');
|
|
1148
|
+
|
|
1149
|
+
// Find workspace root by walking up
|
|
1150
|
+
let workspaceRoot = null;
|
|
1151
|
+
let dir = cwd;
|
|
1152
|
+
while (dir !== path.dirname(dir)) {
|
|
1153
|
+
if (fs.existsSync(path.join(dir, WORKSPACE_CONFIG_FILE))) {
|
|
1154
|
+
workspaceRoot = dir;
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
dir = path.dirname(dir);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (!workspaceRoot) {
|
|
1161
|
+
console.error('Error: Not inside a workspace. Could not find wogi-workspace.json in any parent directory.');
|
|
1162
|
+
process.exit(1);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// Read workspace config
|
|
1166
|
+
let config;
|
|
1167
|
+
try {
|
|
1168
|
+
config = JSON.parse(fs.readFileSync(path.join(workspaceRoot, WORKSPACE_CONFIG_FILE), 'utf-8'));
|
|
1169
|
+
} catch (err) {
|
|
1170
|
+
console.error(`Error reading workspace config: ${err.message}`);
|
|
1171
|
+
process.exit(1);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
if (!config.channels?.enabled) {
|
|
1175
|
+
console.error('Error: Channels are not enabled in this workspace. Run "flow workspace init" to set up channels.');
|
|
1176
|
+
process.exit(1);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// Determine which member we are based on cwd
|
|
1180
|
+
const relativePath = path.relative(workspaceRoot, cwd);
|
|
1181
|
+
let memberName = null;
|
|
1182
|
+
let memberChannel = null;
|
|
1183
|
+
|
|
1184
|
+
for (const [name, memberConfig] of Object.entries(config.members)) {
|
|
1185
|
+
const memberRelPath = memberConfig.path.replace(/^\.\//, '');
|
|
1186
|
+
if (relativePath === memberRelPath || relativePath.startsWith(memberRelPath + path.sep)) {
|
|
1187
|
+
memberName = name;
|
|
1188
|
+
memberChannel = config.channels.members[name];
|
|
1189
|
+
break;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
if (!memberName || !memberChannel) {
|
|
1194
|
+
console.error(`Error: Current directory "${relativePath}" does not match any workspace member.`);
|
|
1195
|
+
console.error('Members:', Object.keys(config.members).join(', '));
|
|
1196
|
+
process.exit(1);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// Build peer list
|
|
1200
|
+
const peers = Object.entries(config.channels.members)
|
|
1201
|
+
.filter(([n]) => n !== memberName)
|
|
1202
|
+
.map(([n, ch]) => `${n}:${ch.port}`)
|
|
1203
|
+
.join(',');
|
|
1204
|
+
|
|
1205
|
+
// Verify .mcp.json exists with channel config
|
|
1206
|
+
const mcpJsonPath = path.join(cwd, '.mcp.json');
|
|
1207
|
+
let mcpConfigValid = false;
|
|
1208
|
+
try {
|
|
1209
|
+
if (fs.existsSync(mcpJsonPath)) {
|
|
1210
|
+
const mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf-8'));
|
|
1211
|
+
mcpConfigValid = !!mcpConfig?.mcpServers?.['wogi-workspace-channel'];
|
|
1212
|
+
}
|
|
1213
|
+
} catch (_err) {
|
|
1214
|
+
// Malformed .mcp.json
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
if (!mcpConfigValid) {
|
|
1218
|
+
console.error(`Error: .mcp.json missing or does not contain wogi-workspace-channel config.`);
|
|
1219
|
+
console.error('Run "flow workspace init" from the workspace root to generate channel configs.');
|
|
1220
|
+
process.exit(1);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
console.log(`Starting worker session for "${memberName}" (port ${memberChannel.port})`);
|
|
1224
|
+
if (peers) console.log(`Peers: ${peers}`);
|
|
1225
|
+
console.log('');
|
|
1226
|
+
|
|
1227
|
+
// Set up environment for the channel server
|
|
1228
|
+
const env = {
|
|
1229
|
+
...process.env,
|
|
1230
|
+
WOGI_CHANNEL_PORT: String(memberChannel.port),
|
|
1231
|
+
WOGI_REPO_NAME: memberName,
|
|
1232
|
+
WOGI_PEERS: peers,
|
|
1233
|
+
WOGI_WORKSPACE_ROOT: workspaceRoot
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
// Launch Claude Code with the channel
|
|
1237
|
+
try {
|
|
1238
|
+
execSync('claude --dangerously-load-development-channels server:wogi-workspace-channel', {
|
|
1239
|
+
cwd,
|
|
1240
|
+
env,
|
|
1241
|
+
stdio: 'inherit'
|
|
1242
|
+
});
|
|
1243
|
+
} catch (err) {
|
|
1244
|
+
// Exit code 0 or SIGINT from user quit — that's fine
|
|
1245
|
+
if (err.status && err.status !== 0 && err.signal !== 'SIGINT') {
|
|
1246
|
+
console.error(`Worker session exited with error (code ${err.status}): ${err.message}`);
|
|
1247
|
+
process.exit(err.status);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
984
1252
|
// ============================================================
|
|
985
1253
|
// CLI Router
|
|
986
1254
|
// ============================================================
|
|
@@ -1033,6 +1301,10 @@ async function workspace(args) {
|
|
|
1033
1301
|
console.log(`✓ Removed '${name}' from workspace`);
|
|
1034
1302
|
break;
|
|
1035
1303
|
}
|
|
1304
|
+
case 'start': {
|
|
1305
|
+
startWorkerSession(process.cwd());
|
|
1306
|
+
break;
|
|
1307
|
+
}
|
|
1036
1308
|
default:
|
|
1037
1309
|
console.log(`
|
|
1038
1310
|
Wogi Workspace — Multi-Repo Orchestration
|
|
@@ -1045,11 +1317,13 @@ Commands:
|
|
|
1045
1317
|
status Show unified workspace status
|
|
1046
1318
|
add Add a member repo to the workspace
|
|
1047
1319
|
remove Remove a member repo from the workspace
|
|
1320
|
+
start Start a worker session with channel (run from a member repo)
|
|
1048
1321
|
|
|
1049
1322
|
Examples:
|
|
1050
1323
|
flow workspace init # Create workspace from subdirectories
|
|
1051
1324
|
flow workspace sync # Refresh after external changes
|
|
1052
1325
|
flow workspace status # Show all repos, tasks, contracts
|
|
1326
|
+
cd frontend/ && flow workspace start # Start worker session
|
|
1053
1327
|
`);
|
|
1054
1328
|
}
|
|
1055
1329
|
}
|
|
@@ -1069,5 +1343,7 @@ module.exports = {
|
|
|
1069
1343
|
createWorkspaceStructure,
|
|
1070
1344
|
WORKSPACE_CONFIG_FILE,
|
|
1071
1345
|
WORKSPACE_DIR,
|
|
1072
|
-
MEMBER_ROLES
|
|
1346
|
+
MEMBER_ROLES,
|
|
1347
|
+
generateMemberMcpConfigs,
|
|
1348
|
+
startWorkerSession
|
|
1073
1349
|
};
|