Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
Expand Down
2 changes: 1 addition & 1 deletion openclaw.plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion plugins/genie/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
10 changes: 5 additions & 5 deletions plugins/genie/agents/team-lead.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ For cross-session agents, use `genie send '<text>' --to <agent>` via Bash.
<lifecycle>

## 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Mirror the same status-timing fix here to avoid first-loop failures.

This file has the same contradiction: “do not run status before first dispatch” vs heartbeat’s unconditional status check. Update heartbeat wording here as well.

Also applies to: 58-58

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/team-lead.md` at line 52, Update the contradictory
guidance in team-lead.md so the heartbeat wording matches the status-timing fix:
change the unconditional instruction about running `genie status` in the
heartbeat section to state that `genie work` auto-initializes state on first
call and you should NOT run `genie status` before the first dispatch; ensure
both occurrences referencing heartbeat/status timing (the paragraph that starts
"Dispatch groups whose dependencies are satisfied..." and the other similar
line) are edited to mirror this behavior and remove any implication of an
unconditional status check before the first dispatch.


```bash
genie work engineer <slug>#<group> # Dispatches and spawns engineer
genie work engineer <slug>#<group> # Auto-inits state, sets in_progress, spawns engineer
genie read <team>-engineer # Monitor progress
genie done <slug>#<group> # Mark group complete
genie status <slug> # Check overall progress
genie status <slug> # Check progress (only AFTER first dispatch)
```

One group per engineer dispatch. Wait for completion before marking done.
Expand Down
10 changes: 5 additions & 5 deletions plugins/genie/agents/team-lead/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ For cross-session agents, use `genie send '<text>' --to <agent>` via Bash.
<lifecycle>

## 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Status timing guidance conflicts with the heartbeat checklist.

These lines correctly say not to run genie status before first dispatch, but the heartbeat still instructs status every loop. That contradiction can cause immediate pre-dispatch failure.

Suggested fix
-2. **Wish status** — `genie status <slug>` — which groups are done, in-progress, or blocked?
+2. **Wish status** — after first dispatch, run `genie status <slug>` — which groups are done, in-progress, or blocked?

Also applies to: 58-58

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/team-lead/AGENTS.md` at line 52, Update the heartbeat
checklist so it no longer instructs running `genie status` on every loop before
`genie work` has auto-initialized state; instead, make the heartbeat description
conditional or explicit: skip the `genie status` step until after the first
dispatch/initialization, or change wording to run `genie status` only after
`genie work` has been invoked at least once. Locate the “heartbeat checklist”
section and the lines referencing `genie work` / `genie status` (the dispatch
guidance) and edit them to remove the contradictory pre-dispatch `genie status`
instruction so it matches the “do NOT run `genie status` before your first
dispatch” guidance.


```bash
genie work engineer <slug>#<group> # Dispatches and spawns engineer
genie work engineer <slug>#<group> # Auto-inits state, sets in_progress, spawns engineer
genie read <team>-engineer # Monitor progress
genie done <slug>#<group> # Mark group complete
genie status <slug> # Check overall progress
genie status <slug> # Check progress (only AFTER first dispatch)
```

One group per engineer dispatch. Wait for completion before marking done.
Expand Down
2 changes: 1 addition & 1 deletion plugins/genie/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
16 changes: 9 additions & 7 deletions src/lib/team-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
/** Best-effort kill all running workers matching a given agent name, scoped to a team. */
async function killWorkersByName(agentName: string, teamName?: string): Promise<void> {
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') {
Expand Down Expand Up @@ -312,10 +314,10 @@ export async function disbandTeam(teamName: string): Promise<boolean> {
}
}

// 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
}
Expand Down Expand Up @@ -426,14 +428,14 @@ export async function listMembers(teamName: string): Promise<string[] | null> {
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<void> {
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
}
Expand Down
9 changes: 5 additions & 4 deletions src/term-commands/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,12 @@ function getGitDiff(): string {

/**
* Parse WISH.md content to extract group definitions for state initialization.
* Looks for `### Group <N>: <title>` 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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep group extraction compatible with lettered IDs

By broadening parseWishGroups to accept alphanumeric group IDs, this commit makes lettered groups (Group A, Group B, etc.) look supported, but extractGroup still searches for the next heading with ^### Group \d and therefore cannot stop at the next lettered heading. In WISH files that omit --- separators between groups, dispatching #A will include Group B and later sections in the assigned context, which can misroute implementation/review work for that group.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Alphanumeric group support is incomplete without updating extraction boundaries.

parseWishGroups now accepts alphanumeric IDs, but extractGroup still finds the next heading with numeric-only matching (^### Group \d). For lettered groups, this can cause section extraction to bleed into later groups and dispatch incorrect scope.

Suggested fix
-  const nextBoundary = afterHeading.slice(1).search(/^### Group \d|^---$/m);
+  const nextBoundary = afterHeading.slice(1).search(/^### Group [A-Za-z0-9]+:|^---$/m);

Also applies to: 157-157

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/term-commands/dispatch.ts` at line 148, parseWishGroups was updated to
accept alphanumeric IDs but extractGroup still looks for the next numeric-only
heading, causing sections to bleed; update the heading-matching regex used in
extractGroup (and any other place using /^### Group \d/) to the same
alphanumeric pattern used above (e.g., /^### Group ([A-Za-z0-9]+):/i) and ensure
the extraction boundary logic stops at that next alphanumeric group heading or
EOF so group slices align with parseWishGroups; reference functions:
parseWishGroups and extractGroup and the group heading regex
variable/groupPattern.


let match: RegExpExecArray | null = groupPattern.exec(content);
while (match !== null) {
Expand All @@ -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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The regex used to find the next group heading is missing the case-insensitive (i) flag. The main groupPattern at line 148 is case-insensitive, so this can lead to incorrect parsing of group sections if group headings in WISH.md have mixed casing (e.g., ### Group 1 followed by ### group 2). This will cause the parser to treat the content of all subsequent groups as part of the first group's section.

Suggested change
const nextGroupIdx = rest.search(/^### Group [A-Za-z0-9]+:/m);
const nextGroupIdx = rest.search(/^### Group [A-Za-z0-9]+:/im);

const section = nextGroupIdx !== -1 ? rest.slice(0, nextGroupIdx) : rest;

// Look for **depends-on:** line within this group section
Expand Down Expand Up @@ -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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The regex used to find available groups for the error message is missing the case-insensitive (i) flag. This means if a WISH.md file uses lowercase group headings (e.g., ### group 1: ...), they won't be listed as available groups in the error message when a group is not found, which could be confusing for the user.

Suggested change
const groups = content.match(/^### Group [A-Za-z0-9]+:.*$/gm);
const groups = content.match(/^### Group [A-Za-z0-9]+:.*$/gim);

if (groups) {
for (const g of groups) console.error(` ${g}`);
}
Expand Down
Loading