mumucc 0.4.10 → 0.4.12

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.10",
3
+ "version": "0.4.12",
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
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  ;(globalThis as any).MACRO = {
7
- VERSION: '0.4.10-mumucc',
7
+ VERSION: '0.4.12-mumucc',
8
8
  VERSION_CHANGELOG: '{}',
9
9
  BUILD_TIME: new Date().toISOString(),
10
10
  PACKAGE_URL: 'https://github.com/mumuxsy/mumucc',
@@ -294,9 +294,8 @@ export const AgentTool = buildTool({
294
294
  // Teammates (in-process or tmux) passing `name` would trigger spawnTeammate()
295
295
  // below, but TeamFile.members is a flat array with one leadAgentId — nested
296
296
  // teammates land in the roster with no provenance and confuse the lead.
297
- const teamName = resolveTeamName({
298
- team_name
299
- }, appState);
297
+ const explicitTeamName = team_name;
298
+ const teamName = explicitTeamName;
300
299
  if (isTeammate() && teamName && name) {
301
300
  throw new Error('Teammates cannot spawn other teammates — the team roster is flat. To spawn a subagent instead, omit the `name` parameter.');
302
301
  }
@@ -309,7 +308,7 @@ export const AgentTool = buildTool({
309
308
 
310
309
  // Check if this is a multi-agent spawn request
311
310
  // Spawn is triggered when team_name is set (from param or context) and name is provided
312
- if (teamName && name) {
311
+ if (explicitTeamName && name) {
313
312
  // Set agent definition color for grouped UI display before spawning
314
313
  const agentDef = subagent_type ? toolUseContext.options.agentDefinitions.activeAgents.find(a => a.agentType === subagent_type) : undefined;
315
314
  if (agentDef?.color) {
@@ -340,7 +339,7 @@ export const AgentTool = buildTool({
340
339
  name,
341
340
  prompt,
342
341
  description,
343
- team_name: teamName,
342
+ team_name: explicitTeamName,
344
343
  use_splitpane: true,
345
344
  plan_mode_required: spawnMode === 'plan',
346
345
  model: resolvedTeammateModel,
@@ -407,7 +406,7 @@ export const AgentTool = buildTool({
407
406
  // Same lifecycle constraint as the run_in_background guard above, but for
408
407
  // agent definitions that force background via `background: true`. Checked
409
408
  // here because selectedAgent is only now resolved.
410
- if (isInProcessTeammate() && teamName && selectedAgent.background === true) {
409
+ if (isInProcessTeammate() && explicitTeamName && selectedAgent.background === true) {
411
410
  throw new Error(`In-process teammates cannot spawn background agents. Agent '${selectedAgent.agentType}' has background: true in its definition.`);
412
411
  }
413
412
 
@@ -45,6 +45,34 @@ const inputSchema = lazySchema(() =>
45
45
  'Type/role of the team lead (e.g., "researcher", "test-runner"). ' +
46
46
  'Used for team file and inter-agent coordination.',
47
47
  ),
48
+ members: z
49
+ .array(
50
+ z.strictObject({
51
+ name: z.string().describe('Unique teammate display name.'),
52
+ agent_type: z
53
+ .string()
54
+ .optional()
55
+ .describe('Agent type/frontmatter role for this member.'),
56
+ model: z
57
+ .string()
58
+ .optional()
59
+ .describe(
60
+ 'Explicit model for this member (for example openai:gpt-5.4, kimi:kimi-for-coding, minimax:MiniMax-M2.7-highspeed, or opus).',
61
+ ),
62
+ mode: z
63
+ .string()
64
+ .optional()
65
+ .describe('Optional permission mode hint for this member.'),
66
+ prompt: z
67
+ .string()
68
+ .optional()
69
+ .describe('Optional initial task/purpose note for this member.'),
70
+ }),
71
+ )
72
+ .optional()
73
+ .describe(
74
+ 'Optional teammate definitions to seed into the team config at creation time.',
75
+ ),
48
76
  }),
49
77
  )
50
78
  type InputSchema = ReturnType<typeof inputSchema>
@@ -53,6 +81,7 @@ export type Output = {
53
81
  team_name: string
54
82
  team_file_path: string
55
83
  lead_agent_id: string
84
+ member_count?: number
56
85
  }
57
86
 
58
87
  export type Input = z.infer<InputSchema>
@@ -127,7 +156,7 @@ export const TeamCreateTool: Tool<InputSchema, Output> = buildTool({
127
156
 
128
157
  async call(input, context) {
129
158
  const { setAppState, getAppState } = context
130
- const { team_name, description: _description, agent_type } = input
159
+ const { team_name, description: _description, agent_type, members } = input
131
160
 
132
161
  // Check if already in a team - restrict to one team per leader
133
162
  const appState = getAppState()
@@ -174,6 +203,28 @@ export const TeamCreateTool: Tool<InputSchema, Output> = buildTool({
174
203
  ],
175
204
  }
176
205
 
206
+ const seenNames = new Set([TEAM_LEAD_NAME])
207
+ for (const member of members ?? []) {
208
+ const sanitizedMemberName = sanitizeName(member.name)
209
+ if (!member.name.trim()) continue
210
+ if (seenNames.has(member.name)) continue
211
+ seenNames.add(member.name)
212
+
213
+ teamFile.members.push({
214
+ agentId: formatAgentId(member.name, finalTeamName),
215
+ name: member.name,
216
+ agentType: member.agent_type,
217
+ model: member.model,
218
+ prompt: member.prompt,
219
+ joinedAt: Date.now(),
220
+ tmuxPaneId: '',
221
+ cwd: getCwd(),
222
+ subscriptions: [],
223
+ isActive: false,
224
+ ...(member.mode ? { mode: member.mode as any } : {}),
225
+ })
226
+ }
227
+
177
228
  await writeTeamFileAsync(finalTeamName, teamFile)
178
229
  // Track for session-end cleanup — teams were left on disk forever
179
230
  // unless explicitly TeamDelete'd (gh-32730).
@@ -232,6 +283,7 @@ export const TeamCreateTool: Tool<InputSchema, Output> = buildTool({
232
283
  team_name: finalTeamName,
233
284
  team_file_path: teamFilePath,
234
285
  lead_agent_id: leadAgentId,
286
+ member_count: teamFile.members.length,
235
287
  },
236
288
  }
237
289
  },
@@ -26,13 +26,31 @@ Create a new team to coordinate multiple agents working on a project. Teams have
26
26
  \`\`\`
27
27
  {
28
28
  "team_name": "my-project",
29
- "description": "Working on feature X"
29
+ "description": "Working on feature X",
30
+ "members": [
31
+ {
32
+ "name": "backend",
33
+ "agent_type": "backend-architect",
34
+ "model": "openai:gpt-5.4"
35
+ },
36
+ {
37
+ "name": "frontend",
38
+ "agent_type": "ui-designer",
39
+ "model": "kimi:kimi-for-coding"
40
+ },
41
+ {
42
+ "name": "deploy",
43
+ "agent_type": "deployment-engineer",
44
+ "model": "minimax:MiniMax-M2.7-highspeed"
45
+ }
46
+ ]
30
47
  }
31
48
  \`\`\`
32
49
 
33
50
  This creates:
34
51
  - A team file at \`~/.mumucc/teams/{team-name}/config.json\`
35
52
  - A corresponding task list directory at \`~/.mumucc/tasks/{team-name}/\`
53
+ - Optional teammate entries pre-seeded into the team config, including their preferred models
36
54
 
37
55
  ## Team Workflow
38
56
 
@@ -19,7 +19,14 @@ import { TEAM_DELETE_TOOL_NAME } from './constants.js'
19
19
  import { getPrompt } from './prompt.js'
20
20
  import { renderToolResultMessage, renderToolUseMessage } from './UI.js'
21
21
 
22
- const inputSchema = lazySchema(() => z.strictObject({}))
22
+ const inputSchema = lazySchema(() =>
23
+ z.strictObject({
24
+ team_name: z
25
+ .string()
26
+ .optional()
27
+ .describe('Optional explicit team name to delete. Falls back to current team context.'),
28
+ }),
29
+ )
23
30
  type InputSchema = ReturnType<typeof inputSchema>
24
31
 
25
32
  export type Output = {
@@ -69,10 +76,10 @@ export const TeamDeleteTool: Tool<InputSchema, Output> = buildTool({
69
76
  }
70
77
  },
71
78
 
72
- async call(_input, context) {
79
+ async call(input, context) {
73
80
  const { setAppState, getAppState } = context
74
81
  const appState = getAppState()
75
- const teamName = appState.teamContext?.teamName
82
+ const teamName = input.team_name || appState.teamContext?.teamName
76
83
 
77
84
  if (teamName) {
78
85
  // Read team config to check for active members