mumucc 0.4.16 → 0.4.17
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mumucc",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.17",
|
|
4
4
|
"description": "Open-source AI coding assistant CLI with multi-model support (Anthropic, OpenAI/GPT, DeepSeek, GLM, Ollama, etc.), MCP integration, agent swarms, and out-of-the-box developer experience.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/shims/globals.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import { assignTeammateColor } from '../../utils/swarm/teammateLayoutManager.js'
|
|
27
27
|
import {
|
|
28
28
|
ensureTasksDir,
|
|
29
|
+
clearLeaderTeamName,
|
|
29
30
|
resetTaskList,
|
|
30
31
|
setLeaderTeamName,
|
|
31
32
|
} from '../../utils/tasks.js'
|
|
@@ -163,9 +164,19 @@ export const TeamCreateTool: Tool<InputSchema, Output> = buildTool({
|
|
|
163
164
|
const existingTeam = appState.teamContext?.teamName
|
|
164
165
|
|
|
165
166
|
if (existingTeam) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
if (readTeamFile(existingTeam)) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`Already leading team "${existingTeam}". A leader can only manage one team at a time. Use TeamDelete to end the current team before creating a new one.`,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Recover from a previously failed implicit spawn that populated
|
|
174
|
+
// AppState.teamContext before the team file was written.
|
|
175
|
+
clearLeaderTeamName()
|
|
176
|
+
setAppState(prev => ({
|
|
177
|
+
...prev,
|
|
178
|
+
teamContext: undefined,
|
|
179
|
+
}))
|
|
169
180
|
}
|
|
170
181
|
|
|
171
182
|
// If team already exists, generate a unique name instead of failing
|
|
@@ -24,7 +24,10 @@ import { getCwd } from '../../utils/cwd.js'
|
|
|
24
24
|
import { logForDebugging } from '../../utils/debug.js'
|
|
25
25
|
import { errorMessage } from '../../utils/errors.js'
|
|
26
26
|
import { execFileNoThrow } from '../../utils/execFileNoThrow.js'
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
getDefaultMainLoopModel,
|
|
29
|
+
parseUserSpecifiedModel,
|
|
30
|
+
} from '../../utils/model/model.js'
|
|
28
31
|
import type { PermissionMode } from '../../utils/permissions/PermissionMode.js'
|
|
29
32
|
import { isTmuxAvailable } from '../../utils/swarm/backends/detection.js'
|
|
30
33
|
import {
|
|
@@ -52,8 +55,11 @@ import {
|
|
|
52
55
|
import { buildInheritedEnvVars } from '../../utils/swarm/spawnUtils.js'
|
|
53
56
|
import {
|
|
54
57
|
readTeamFileAsync,
|
|
58
|
+
registerTeamForSessionCleanup,
|
|
55
59
|
sanitizeAgentName,
|
|
56
60
|
sanitizeName,
|
|
61
|
+
type TeamFile,
|
|
62
|
+
getTeamFilePath,
|
|
57
63
|
writeTeamFileAsync,
|
|
58
64
|
} from '../../utils/swarm/teamHelpers.js'
|
|
59
65
|
import {
|
|
@@ -66,6 +72,7 @@ import {
|
|
|
66
72
|
import { getHardcodedTeammateModelFallback } from '../../utils/swarm/teammateModel.js'
|
|
67
73
|
import { registerTask } from '../../utils/task/framework.js'
|
|
68
74
|
import { writeToMailbox } from '../../utils/teammateMailbox.js'
|
|
75
|
+
import { ensureTasksDir, resetTaskList, setLeaderTeamName } from '../../utils/tasks.js'
|
|
69
76
|
import type { CustomAgentDefinition } from '../AgentTool/loadAgentsDir.js'
|
|
70
77
|
import { isCustomAgent } from '../AgentTool/loadAgentsDir.js'
|
|
71
78
|
|
|
@@ -102,6 +109,64 @@ export function resolveTeammateModel(
|
|
|
102
109
|
return inputModel ?? getDefaultTeammateModel(leaderModel)
|
|
103
110
|
}
|
|
104
111
|
|
|
112
|
+
type EnsuredTeamContext = {
|
|
113
|
+
teamFile: TeamFile
|
|
114
|
+
teamFilePath: string
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function ensureTeamExistsForSpawn(
|
|
118
|
+
teamName: string,
|
|
119
|
+
context: ToolUseContext,
|
|
120
|
+
): Promise<EnsuredTeamContext> {
|
|
121
|
+
const existing = await readTeamFileAsync(teamName)
|
|
122
|
+
if (existing) {
|
|
123
|
+
return {
|
|
124
|
+
teamFile: existing,
|
|
125
|
+
teamFilePath: getTeamFilePath(teamName),
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const appState = context.getAppState()
|
|
130
|
+
const now = Date.now()
|
|
131
|
+
const leadAgentId = formatAgentId(TEAM_LEAD_NAME, teamName)
|
|
132
|
+
const leadModel = parseUserSpecifiedModel(
|
|
133
|
+
appState.mainLoopModelForSession ??
|
|
134
|
+
appState.mainLoopModel ??
|
|
135
|
+
getDefaultMainLoopModel(),
|
|
136
|
+
)
|
|
137
|
+
const taskListId = sanitizeName(teamName)
|
|
138
|
+
|
|
139
|
+
const teamFile: TeamFile = {
|
|
140
|
+
name: teamName,
|
|
141
|
+
createdAt: now,
|
|
142
|
+
leadAgentId,
|
|
143
|
+
leadSessionId: getSessionId(),
|
|
144
|
+
members: [
|
|
145
|
+
{
|
|
146
|
+
agentId: leadAgentId,
|
|
147
|
+
name: TEAM_LEAD_NAME,
|
|
148
|
+
agentType: TEAM_LEAD_NAME,
|
|
149
|
+
model: leadModel,
|
|
150
|
+
joinedAt: now,
|
|
151
|
+
tmuxPaneId: '',
|
|
152
|
+
cwd: getCwd(),
|
|
153
|
+
subscriptions: [],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await writeTeamFileAsync(teamName, teamFile)
|
|
159
|
+
registerTeamForSessionCleanup(teamName)
|
|
160
|
+
await resetTaskList(taskListId)
|
|
161
|
+
await ensureTasksDir(taskListId)
|
|
162
|
+
setLeaderTeamName(taskListId)
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
teamFile,
|
|
166
|
+
teamFilePath: getTeamFilePath(teamName),
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
105
170
|
// ============================================================================
|
|
106
171
|
// Types
|
|
107
172
|
// ============================================================================
|
|
@@ -328,6 +393,8 @@ async function handleSpawnSplitPane(
|
|
|
328
393
|
)
|
|
329
394
|
}
|
|
330
395
|
|
|
396
|
+
const ensuredTeam = await ensureTeamExistsForSpawn(teamName, context)
|
|
397
|
+
|
|
331
398
|
// Generate unique name if duplicate exists in team
|
|
332
399
|
const uniqueName = await generateUniqueTeammateName(name, teamName)
|
|
333
400
|
|
|
@@ -450,14 +517,15 @@ async function handleSpawnSplitPane(
|
|
|
450
517
|
const windowName = insideTmux ? 'current' : 'swarm-view'
|
|
451
518
|
|
|
452
519
|
// Track the teammate in AppState's teamContext with color
|
|
453
|
-
// If spawning without spawnTeam, set up the leader as team lead
|
|
454
520
|
setAppState(prev => ({
|
|
455
521
|
...prev,
|
|
456
522
|
teamContext: {
|
|
457
523
|
...prev.teamContext,
|
|
458
|
-
teamName
|
|
459
|
-
teamFilePath:
|
|
460
|
-
|
|
524
|
+
teamName,
|
|
525
|
+
teamFilePath:
|
|
526
|
+
prev.teamContext?.teamFilePath || ensuredTeam.teamFilePath,
|
|
527
|
+
leadAgentId:
|
|
528
|
+
prev.teamContext?.leadAgentId || ensuredTeam.teamFile.leadAgentId,
|
|
461
529
|
teammates: {
|
|
462
530
|
...(prev.teamContext?.teammates || {}),
|
|
463
531
|
[teammateId]: {
|
|
@@ -488,12 +556,7 @@ async function handleSpawnSplitPane(
|
|
|
488
556
|
})
|
|
489
557
|
|
|
490
558
|
// Register agent in the team file
|
|
491
|
-
const teamFile =
|
|
492
|
-
if (!teamFile) {
|
|
493
|
-
throw new Error(
|
|
494
|
-
`Team "${teamName}" does not exist. Call spawnTeam first to create the team.`,
|
|
495
|
-
)
|
|
496
|
-
}
|
|
559
|
+
const teamFile = ensuredTeam.teamFile
|
|
497
560
|
teamFile.members.push({
|
|
498
561
|
agentId: teammateId,
|
|
499
562
|
name: sanitizedName,
|
|
@@ -568,6 +631,8 @@ async function handleSpawnSeparateWindow(
|
|
|
568
631
|
)
|
|
569
632
|
}
|
|
570
633
|
|
|
634
|
+
const ensuredTeam = await ensureTeamExistsForSpawn(teamName, context)
|
|
635
|
+
|
|
571
636
|
// Generate unique name if duplicate exists in team
|
|
572
637
|
const uniqueName = await generateUniqueTeammateName(name, teamName)
|
|
573
638
|
|
|
@@ -668,9 +733,11 @@ async function handleSpawnSeparateWindow(
|
|
|
668
733
|
...prev,
|
|
669
734
|
teamContext: {
|
|
670
735
|
...prev.teamContext,
|
|
671
|
-
teamName
|
|
672
|
-
teamFilePath:
|
|
673
|
-
|
|
736
|
+
teamName,
|
|
737
|
+
teamFilePath:
|
|
738
|
+
prev.teamContext?.teamFilePath || ensuredTeam.teamFilePath,
|
|
739
|
+
leadAgentId:
|
|
740
|
+
prev.teamContext?.leadAgentId || ensuredTeam.teamFile.leadAgentId,
|
|
674
741
|
teammates: {
|
|
675
742
|
...(prev.teamContext?.teammates || {}),
|
|
676
743
|
[teammateId]: {
|
|
@@ -702,12 +769,7 @@ async function handleSpawnSeparateWindow(
|
|
|
702
769
|
})
|
|
703
770
|
|
|
704
771
|
// Register agent in the team file
|
|
705
|
-
const teamFile =
|
|
706
|
-
if (!teamFile) {
|
|
707
|
-
throw new Error(
|
|
708
|
-
`Team "${teamName}" does not exist. Call spawnTeam first to create the team.`,
|
|
709
|
-
)
|
|
710
|
-
}
|
|
772
|
+
const teamFile = ensuredTeam.teamFile
|
|
711
773
|
teamFile.members.push({
|
|
712
774
|
agentId: teammateId,
|
|
713
775
|
name: sanitizedName,
|
|
@@ -863,6 +925,8 @@ async function handleSpawnInProcess(
|
|
|
863
925
|
)
|
|
864
926
|
}
|
|
865
927
|
|
|
928
|
+
const ensuredTeam = await ensureTeamExistsForSpawn(teamName, context)
|
|
929
|
+
|
|
866
930
|
// Generate unique name if duplicate exists in team
|
|
867
931
|
const uniqueName = await generateUniqueTeammateName(name, teamName)
|
|
868
932
|
|
|
@@ -940,39 +1004,22 @@ async function handleSpawnInProcess(
|
|
|
940
1004
|
}
|
|
941
1005
|
|
|
942
1006
|
// Track the teammate in AppState's teamContext
|
|
943
|
-
// Auto-register leader if spawning without prior spawnTeam call
|
|
944
1007
|
setAppState(prev => {
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
? formatAgentId(TEAM_LEAD_NAME, teamName)
|
|
948
|
-
: prev.teamContext!.leadAgentId
|
|
1008
|
+
const leadAgentId =
|
|
1009
|
+
prev.teamContext?.leadAgentId || ensuredTeam.teamFile.leadAgentId
|
|
949
1010
|
|
|
950
|
-
// Build teammates map, including leader if needed for inbox polling
|
|
951
1011
|
const existingTeammates = prev.teamContext?.teammates || {}
|
|
952
|
-
const leadEntry = needsLeaderSetup
|
|
953
|
-
? {
|
|
954
|
-
[leadAgentId]: {
|
|
955
|
-
name: TEAM_LEAD_NAME,
|
|
956
|
-
agentType: TEAM_LEAD_NAME,
|
|
957
|
-
color: assignTeammateColor(leadAgentId),
|
|
958
|
-
tmuxSessionName: 'in-process',
|
|
959
|
-
tmuxPaneId: 'leader',
|
|
960
|
-
cwd: getCwd(),
|
|
961
|
-
spawnedAt: Date.now(),
|
|
962
|
-
},
|
|
963
|
-
}
|
|
964
|
-
: {}
|
|
965
1012
|
|
|
966
1013
|
return {
|
|
967
1014
|
...prev,
|
|
968
1015
|
teamContext: {
|
|
969
1016
|
...prev.teamContext,
|
|
970
|
-
teamName
|
|
971
|
-
teamFilePath:
|
|
1017
|
+
teamName,
|
|
1018
|
+
teamFilePath:
|
|
1019
|
+
prev.teamContext?.teamFilePath || ensuredTeam.teamFilePath,
|
|
972
1020
|
leadAgentId,
|
|
973
1021
|
teammates: {
|
|
974
1022
|
...existingTeammates,
|
|
975
|
-
...leadEntry,
|
|
976
1023
|
[teammateId]: {
|
|
977
1024
|
name: sanitizedName,
|
|
978
1025
|
agentType: agent_type,
|
|
@@ -988,12 +1035,7 @@ async function handleSpawnInProcess(
|
|
|
988
1035
|
})
|
|
989
1036
|
|
|
990
1037
|
// Register agent in the team file
|
|
991
|
-
const teamFile =
|
|
992
|
-
if (!teamFile) {
|
|
993
|
-
throw new Error(
|
|
994
|
-
`Team "${teamName}" does not exist. Call spawnTeam first to create the team.`,
|
|
995
|
-
)
|
|
996
|
-
}
|
|
1038
|
+
const teamFile = ensuredTeam.teamFile
|
|
997
1039
|
teamFile.members.push({
|
|
998
1040
|
agentId: teammateId,
|
|
999
1041
|
name: sanitizedName,
|