diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4b99a125d..2c38727e6 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "plugins": [ { "name": "genie", - "version": "3.260317.2", + "version": "3.260317.3", "source": "./plugins/genie", "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, wish them into plans, make with parallel agents, ship as one team. A coding genie that grows with your project." } diff --git a/openclaw.plugin.json b/openclaw.plugin.json index 5b348322e..3fe11d00f 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -2,7 +2,7 @@ "id": "genie", "name": "Genie", "description": "Skills, agents, and hooks for the Genie CLI terminal orchestration toolkit", - "version": "3.260317.2", + "version": "3.260317.3", "configSchema": { "type": "object", "additionalProperties": false, diff --git a/package.json b/package.json index 9d16347c0..88a28f2a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@automagik/genie", - "version": "3.260317.2", + "version": "3.260317.3", "description": "Collaborative terminal toolkit for human + AI workflows", "type": "module", "bin": { diff --git a/plugins/genie/.claude-plugin/plugin.json b/plugins/genie/.claude-plugin/plugin.json index 404d5d47b..a7c231f10 100644 --- a/plugins/genie/.claude-plugin/plugin.json +++ b/plugins/genie/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "genie", - "version": "3.260317.2", + "version": "3.260317.3", "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.", "author": { "name": "Namastex Labs" diff --git a/plugins/genie/agents/team-lead.md b/plugins/genie/agents/team-lead.md index 3e69dc0f5..4b23b620e 100644 --- a/plugins/genie/agents/team-lead.md +++ b/plugins/genie/agents/team-lead.md @@ -44,18 +44,18 @@ For cross-session agents, use `genie send '' --to ` via Bash. ## Phase 1 — Read Wish -Read the WISH.md at the path provided in your initial prompt. Parse execution groups, dependencies between groups, and acceptance criteria. Understand the full scope before dispatching anything. +Read the WISH.md at the path provided in your initial prompt. Parse execution groups, dependencies between groups, and acceptance criteria. -**Gate:** All groups parsed, dependency DAG verified (no circular deps), acceptance criteria extracted. If wish is unparseable or missing groups, report to PM and stop. +**Gate:** All groups parsed, dependency DAG understood. If wish is unparseable or missing groups, report to PM and stop. ## Phase 2 — Execute Groups -Dispatch groups whose dependencies are satisfied. Run independent groups in parallel. Never start a group before its dependencies complete. +Dispatch groups whose dependencies are satisfied. `genie work` auto-initializes state on first call — do NOT run `genie status` before your first dispatch. Just dispatch immediately. ```bash -genie work engineer # # Dispatches and spawns engineer +genie work engineer # # Auto-inits state, sets in_progress, spawns engineer genie read -engineer # Monitor progress genie done # # Mark group complete -genie status # Check overall progress +genie status # Check progress (only AFTER first dispatch) ``` One group per engineer dispatch. Wait for completion before marking done. diff --git a/plugins/genie/agents/team-lead/AGENTS.md b/plugins/genie/agents/team-lead/AGENTS.md index 3e69dc0f5..4b23b620e 100644 --- a/plugins/genie/agents/team-lead/AGENTS.md +++ b/plugins/genie/agents/team-lead/AGENTS.md @@ -44,18 +44,18 @@ For cross-session agents, use `genie send '' --to ` via Bash. ## Phase 1 — Read Wish -Read the WISH.md at the path provided in your initial prompt. Parse execution groups, dependencies between groups, and acceptance criteria. Understand the full scope before dispatching anything. +Read the WISH.md at the path provided in your initial prompt. Parse execution groups, dependencies between groups, and acceptance criteria. -**Gate:** All groups parsed, dependency DAG verified (no circular deps), acceptance criteria extracted. If wish is unparseable or missing groups, report to PM and stop. +**Gate:** All groups parsed, dependency DAG understood. If wish is unparseable or missing groups, report to PM and stop. ## Phase 2 — Execute Groups -Dispatch groups whose dependencies are satisfied. Run independent groups in parallel. Never start a group before its dependencies complete. +Dispatch groups whose dependencies are satisfied. `genie work` auto-initializes state on first call — do NOT run `genie status` before your first dispatch. Just dispatch immediately. ```bash -genie work engineer # # Dispatches and spawns engineer +genie work engineer # # Auto-inits state, sets in_progress, spawns engineer genie read -engineer # Monitor progress genie done # # Mark group complete -genie status # Check overall progress +genie status # Check progress (only AFTER first dispatch) ``` One group per engineer dispatch. Wait for completion before marking done. diff --git a/plugins/genie/package.json b/plugins/genie/package.json index 667c84060..9bf87c5c6 100644 --- a/plugins/genie/package.json +++ b/plugins/genie/package.json @@ -1,6 +1,6 @@ { "name": "genie-plugin", - "version": "3.260317.2", + "version": "3.260317.3", "private": true, "description": "Runtime dependencies for genie bundled CLIs", "type": "module", diff --git a/src/lib/team-manager.ts b/src/lib/team-manager.ts index 5b96415c9..a212a4678 100644 --- a/src/lib/team-manager.ts +++ b/src/lib/team-manager.ts @@ -120,10 +120,12 @@ export function validateBranchName(name: string): void { // Agent Kill Helper // ============================================================================ -/** Best-effort kill all running workers matching a given agent name (by role or id). */ -async function killWorkersByName(agentName: string): Promise { +/** Best-effort kill all running workers matching a given agent name, scoped to a team. */ +async function killWorkersByName(agentName: string, teamName?: string): Promise { const workers = await registry.list(); - const matches = workers.filter((w) => w.role === agentName || w.id === agentName); + const matches = workers.filter( + (w) => (w.role === agentName || w.id === agentName) && (!teamName || w.team === teamName), + ); for (const w of matches) { try { if (w.paneId && w.paneId !== 'inline') { @@ -312,10 +314,10 @@ export async function disbandTeam(teamName: string): Promise { } } - // Kill all running team members + // Kill all running team members (scoped to this team only) for (const member of config.members) { try { - await killWorkersByName(member); + await killWorkersByName(member, teamName); } catch { // Best-effort — continue with other members } @@ -426,14 +428,14 @@ export async function listMembers(teamName: string): Promise { return config.members; } -/** Kill all running workers for a team's members. Best-effort — continues on failure. */ +/** Kill all running workers for a team's members. Scoped to the team — won't kill other teams' workers. */ export async function killTeamMembers(teamName: string): Promise { const config = await getTeam(teamName); if (!config) return; for (const member of config.members) { try { - await killWorkersByName(member); + await killWorkersByName(member, teamName); } catch { // Best-effort — continue with other members } diff --git a/src/term-commands/dispatch.ts b/src/term-commands/dispatch.ts index 4c55e94fb..abb03311c 100644 --- a/src/term-commands/dispatch.ts +++ b/src/term-commands/dispatch.ts @@ -140,11 +140,12 @@ function getGitDiff(): string { /** * Parse WISH.md content to extract group definitions for state initialization. - * Looks for `### Group : ` headings and `**depends-on:**` lines. + * Looks for `### Group <id>: <title>` headings and `**depends-on:**` lines. + * Accepts both numbered (Group 1) and lettered (Group A) identifiers. */ export function parseWishGroups(content: string): GroupDefinition[] { const groups: GroupDefinition[] = []; - const groupPattern = /^### Group (\d+):/gim; + const groupPattern = /^### Group ([A-Za-z0-9]+):/gim; let match: RegExpExecArray | null = groupPattern.exec(content); while (match !== null) { @@ -153,7 +154,7 @@ export function parseWishGroups(content: string): GroupDefinition[] { // Find the next group heading or end of content const rest = content.slice(start + match[0].length); - const nextGroupIdx = rest.search(/^### Group \d+:/m); + const nextGroupIdx = rest.search(/^### Group [A-Za-z0-9]+:/m); const section = nextGroupIdx !== -1 ? rest.slice(0, nextGroupIdx) : rest; // Look for **depends-on:** line within this group section @@ -274,7 +275,7 @@ export async function workDispatchCommand(agentName: string, ref: string): Promi if (!groupSection) { console.error(`❌ Group "${group}" not found in ${wishPath}`); console.error(' Available groups:'); - const groups = content.match(/^### Group \d+:.*$/gm); + const groups = content.match(/^### Group [A-Za-z0-9]+:.*$/gm); if (groups) { for (const g of groups) console.error(` ${g}`); }