@automagik/genie 4.260507.5 → 4.260507.6

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/dist/genie.js CHANGED
@@ -2954,7 +2954,14 @@ Genie Serve`),console.log("\u2500".repeat(50)),console.log(` Status: ${runn
2954
2954
  WHERE team = ${slug}
2955
2955
  AND state IS DISTINCT FROM 'archived'
2956
2956
  RETURNING id
2957
- `;if(rows.length>0)console.log(` \uD83D\uDCE6 Archived ${rows.length} wish-named agent row${rows.length===1?"":"s"} (team="${slug}")`);return rows.length}catch(err){let detail=err instanceof Error?err.message:String(err);return console.warn(` \u26A0\uFE0F Could not archive wish-named agent rows: ${detail}`),0}}function autoKillPane(){let paneId=process.env.TMUX_PANE;if(paneId)setTimeout(()=>{try{let{genieTmuxCmd:genieTmuxCmd2}=(init_tmux_wrapper(),__toCommonJS(exports_tmux_wrapper));execSync13(genieTmuxCmd2(`kill-pane -t '${paneId}'`),{encoding:"utf-8"})}catch{process.exit(0)}},1000);else process.exit(0)}async function resolveNotificationTargets(){let teamName=process.env.GENIE_TEAM;if(!teamName)return{leader:"team-lead"};try{let teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),leader=await teamManager.resolveLeaderName(teamName),config=await teamManager.getTeam(teamName);return{leader,spawner:config?.spawner}}catch{return{leader:teamName}}}async function notifyWaveCompletion(waveResult,wishComplete){console.log(` \uD83C\uDF0A ${waveResult.waveName} complete! All groups done: ${waveResult.waveGroups.join(", ")}`);try{let protocolRouter=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router)),repoPath=process.cwd(),{leader,spawner}=await resolveNotificationTargets(),message=wishComplete?`WISH COMPLETE \u2014 all groups done: [${waveResult.waveGroups.join(", ")}]. Run \`genie team done\` to clean up.`:`${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(", ")}]. Run /review or advance to next wave.`,result2=await protocolRouter.sendMessage(repoPath,"cli",leader,message);if(result2&&typeof result2==="object"&&"delivered"in result2&&!result2.delivered)console.warn(` \u26A0\uFE0F Wave-complete notification to ${leader} may not have been delivered.`);else console.log(` Notified ${leader} of wave completion.`);if(spawner&&spawner!==leader&&spawner!=="cli")await protocolRouter.sendMessage(repoPath,"cli",spawner,message).catch(()=>{}),console.log(` Notified spawner (${spawner}) of wave completion.`)}catch{console.warn(" \u26A0\uFE0F Could not notify leader (messaging unavailable).")}}async function doneCommand(ref){try{let{slug,group}=parseRef(ref),result2=await completeGroup(slug,group);if(console.log(`\u2705 Group "${group}" marked as done in wish "${slug}"`),result2.completedAt)console.log(` Completed at: ${formatTimestamp(result2.completedAt)}`);let state=await getState(slug);if(state){let nowReady=Object.entries(state.groups).filter(([,g])=>g.status==="ready"&&g.dependsOn.includes(group)).map(([name])=>name);if(nowReady.length>0)console.log(` Unblocked: ${nowReady.join(", ")}`)}await ensureWorkPushed(slug,group);let wishComplete=await isWishComplete(slug),waveResult=await detectWaveCompletion(slug,group);if(waveResult)await notifyWaveCompletion(waveResult,wishComplete);if(wishComplete)console.log(" \uD83C\uDF89 Wish fully complete \u2014 all groups done."),await autoCleanupTeam(),await archiveWishNamedAgents(slug);autoKillPane()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function autoInitWishState(slug){let wishPath=resolveWishPath(slug);if(!wishPath)console.error(`\u274C No state found for wish "${slug}" and no WISH.md found in cwd or repo root`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);let content=await readFile12(wishPath,"utf-8"),groups=parseWishGroups(content);if(groups.length===0)console.error(`\u274C No execution groups found in ${wishPath}`),process.exit(1);let state=await createState(slug,groups);return console.log(`\uD83D\uDCDD Auto-initialized state for wish "${slug}" (${groups.length} groups)`),state}function parseWishLifecycleStatus(content){let patterns2=[/^\|\s*\*\*Status\*\*\s*\|\s*([^|]+?)\s*\|/im,/^\*\*Status:\*\*\s*([A-Za-z][A-Za-z_-]*)/im,/^Status:\s*([A-Za-z][A-Za-z_-]*)/im];for(let pattern of patterns2){let token=content.match(pattern)?.[1]?.trim().match(/^[A-Za-z][A-Za-z_-]*/)?.[0];if(token)return token.toUpperCase()}return null}async function getTerminalWishLifecycleStatus(slug){let wishPath=resolveWishPath(slug);if(!wishPath)return null;let content=await readFile12(wishPath,"utf-8"),status2=parseWishLifecycleStatus(content);if(!status2||!TERMINAL_WISH_STATUSES.has(status2.toLowerCase()))return null;return{status:status2,wishPath}}function printTerminalWishLifecycleStatus(slug,terminal){console.log(`
2957
+ `;if(rows.length>0)console.log(` \uD83D\uDCE6 Archived ${rows.length} wish-named agent row${rows.length===1?"":"s"} (team="${slug}")`);return rows.length}catch(err){let detail=err instanceof Error?err.message:String(err);return console.warn(` \u26A0\uFE0F Could not archive wish-named agent rows: ${detail}`),0}}function autoKillPane(){let paneId=process.env.TMUX_PANE;if(paneId)setTimeout(()=>{try{let{genieTmuxCmd:genieTmuxCmd2}=(init_tmux_wrapper(),__toCommonJS(exports_tmux_wrapper));execSync13(genieTmuxCmd2(`kill-pane -t '${paneId}'`),{encoding:"utf-8"})}catch{process.exit(0)}},1000);else process.exit(0)}async function resolveNotificationTargets(){let teamName=process.env.GENIE_TEAM;if(!teamName)return{leader:"team-lead"};try{let teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),leader=await teamManager.resolveLeaderName(teamName),config=await teamManager.getTeam(teamName);return{leader,spawner:config?.spawner}}catch{return{leader:teamName}}}async function notifyWaveCompletion(waveResult,wishComplete,report2){console.log(` \uD83C\uDF0A ${waveResult.waveName} complete! All groups done: ${waveResult.waveGroups.join(", ")}`);try{let protocolRouter=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router)),repoPath=process.cwd(),{leader,spawner}=await resolveNotificationTargets(),reportBlock=`
2958
+
2959
+ --- Handoff ---
2960
+ ${report2.trimEnd()}
2961
+ --- End handoff ---
2962
+ `,message=wishComplete?`WISH COMPLETE \u2014 all groups done: [${waveResult.waveGroups.join(", ")}].${reportBlock}
2963
+ Team will be auto-cleaned. Run \`genie team done\` to confirm or override.`:`${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(", ")}].${reportBlock}
2964
+ Run /review or advance to next wave.`,result2=await protocolRouter.sendMessage(repoPath,"cli",leader,message);if(result2&&typeof result2==="object"&&"delivered"in result2&&!result2.delivered)console.warn(` \u26A0\uFE0F Wave-complete notification to ${leader} may not have been delivered.`);else console.log(` Notified ${leader} of wave completion.`);if(spawner&&spawner!==leader&&spawner!=="cli")await protocolRouter.sendMessage(repoPath,"cli",spawner,message).catch(()=>{}),console.log(` Notified spawner (${spawner}) of wave completion.`)}catch{console.warn(" \u26A0\uFE0F Could not notify leader (messaging unavailable).")}}async function doneCommand(ref,report2){if(!report2?.trim())throw Error("doneCommand: report is required \u2014 full group handoff (what shipped, verified, left, surprises). Not a one-liner.");try{let{slug,group}=parseRef(ref),result2=await completeGroup(slug,group);if(console.log(`\u2705 Group "${group}" marked as done in wish "${slug}"`),console.log("--- Handoff ---"),console.log(report2.trimEnd()),console.log("--- End handoff ---"),result2.completedAt)console.log(` Completed at: ${formatTimestamp(result2.completedAt)}`);let state=await getState(slug);if(state){let nowReady=Object.entries(state.groups).filter(([,g])=>g.status==="ready"&&g.dependsOn.includes(group)).map(([name])=>name);if(nowReady.length>0)console.log(` Unblocked: ${nowReady.join(", ")}`)}await ensureWorkPushed(slug,group);let wishComplete=await isWishComplete(slug),waveResult=await detectWaveCompletion(slug,group);if(waveResult)await notifyWaveCompletion(waveResult,wishComplete,report2);if(wishComplete)console.log(" \uD83C\uDF89 Wish fully complete \u2014 all groups done."),await autoCleanupTeam(),await archiveWishNamedAgents(slug);autoKillPane()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function autoInitWishState(slug){let wishPath=resolveWishPath(slug);if(!wishPath)console.error(`\u274C No state found for wish "${slug}" and no WISH.md found in cwd or repo root`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);let content=await readFile12(wishPath,"utf-8"),groups=parseWishGroups(content);if(groups.length===0)console.error(`\u274C No execution groups found in ${wishPath}`),process.exit(1);let state=await createState(slug,groups);return console.log(`\uD83D\uDCDD Auto-initialized state for wish "${slug}" (${groups.length} groups)`),state}function parseWishLifecycleStatus(content){let patterns2=[/^\|\s*\*\*Status\*\*\s*\|\s*([^|]+?)\s*\|/im,/^\*\*Status:\*\*\s*([A-Za-z][A-Za-z_-]*)/im,/^Status:\s*([A-Za-z][A-Za-z_-]*)/im];for(let pattern of patterns2){let token=content.match(pattern)?.[1]?.trim().match(/^[A-Za-z][A-Za-z_-]*/)?.[0];if(token)return token.toUpperCase()}return null}async function getTerminalWishLifecycleStatus(slug){let wishPath=resolveWishPath(slug);if(!wishPath)return null;let content=await readFile12(wishPath,"utf-8"),status2=parseWishLifecycleStatus(content);if(!status2||!TERMINAL_WISH_STATUSES.has(status2.toLowerCase()))return null;return{status:status2,wishPath}}function printTerminalWishLifecycleStatus(slug,terminal){console.log(`
2958
2965
  Wish: ${slug}`),console.log("\u2500".repeat(60)),console.log(` Status: ${terminal.status}`),console.log(` Source: ${terminal.wishPath}`),console.log(""),console.log(" No active execution state initialized for terminal wish status."),console.log("")}async function printWishExecutors(slug){try{let{registry:registry3,executorRegistry,assignmentRegistry}=await loadExecutorInfo(),agents=await registry3.listAgents({team:process.env.GENIE_TEAM}),executorInfoLines=[];for(let agent of agents){if(!agent.currentExecutorId)continue;let executor=await executorRegistry.getExecutor(agent.currentExecutorId);if(!executor||executor.state==="terminated"||executor.state==="done")continue;let assignment=await assignmentRegistry.getActiveAssignment(executor.id);if(assignment?.wishSlug!==slug)continue;let taskLabel=`Group ${assignment.groupNumber??"?"}`,name=agent.customName??agent.role??agent.id.slice(0,12);executorInfoLines.push(` Agent: ${padRight(name,16)} | Executor: ${executor.id.slice(0,12)} (${executor.provider}) | State: ${padRight(executor.state,10)} | Task: ${taskLabel}`)}if(executorInfoLines.length>0){console.log(`
2959
2966
  Active Executors:`),console.log("\u2500".repeat(60));for(let line of executorInfoLines)console.log(line)}}catch{}}async function statusCommand(slug){try{let terminal=await getTerminalWishLifecycleStatus(slug);if(terminal){printTerminalWishLifecycleStatus(slug,terminal);return}let state=await getState(slug)??await autoInitWishState(slug);console.log(`
2960
2967
  Wish: ${state.wish}`),console.log("\u2500".repeat(60));let entries=Object.entries(state.groups),maxNameLen=Math.max(...entries.map(([name])=>name.length),5);console.log(` ${padRight("GROUP",maxNameLen)} STATUS ASSIGNEE STARTED COMPLETED`),console.log(` ${"\u2500".repeat(maxNameLen+62)}`);for(let[name,group]of entries){let icon=STATUS_ICONS[group.status]??"\u2753",status2=padRight(`${icon} ${group.status}`,13),assignee=padRight(group.assignee??"-",13),started=padRight(formatTimestamp(group.startedAt)||"-",14),completed=formatTimestamp(group.completedAt)||"-";console.log(` ${padRight(name,maxNameLen)} ${status2} ${assignee} ${started} ${completed}`)}let total=entries.length,done=entries.filter(([,g])=>g.status==="done").length,inProgress=entries.filter(([,g])=>g.status==="in_progress").length,ready=entries.filter(([,g])=>g.status==="ready").length,blocked=entries.filter(([,g])=>g.status==="blocked").length;console.log(""),console.log(` Progress: ${done}/${total} done | ${inProgress} in progress | ${ready} ready | ${blocked} blocked`),await printWishExecutors(slug),console.log("")}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function resetAction(ref,options){try{if(ref.includes("#")){let{slug,group}=parseRef(ref),result2=await resetGroup(slug,group);if(console.log(`\uD83D\uDD04 Group "${group}" reset to ready in wish "${slug}"`),result2.status==="ready")console.log(" Status: ready (assignee cleared)");return}await resetWishCommand(ref,options?.yes??!1)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}function registerStateCommands(_program){}async function confirmWipe(slug,existing,confirmed){let groupCount=Object.keys(existing.groups).length,inProgress=Object.values(existing.groups).filter((g)=>g.status==="in_progress").length,summary=`Wipe all state for "${slug}" (${groupCount} groups, ${inProgress} in-progress)?`;if(!isInteractive()){if(confirmed)return!0;console.error(`\u274C ${summary}`),console.error(" Refusing to wipe in non-interactive mode. Pass --yes to confirm."),process.exit(2)}let{confirm:confirm3}=await Promise.resolve().then(() => (init_esm14(),exports_esm));return confirm3({message:summary,default:!1})}function printResetState(state){console.log(""),console.log(`Wish: ${state.wish}`),console.log("\u2500".repeat(60));for(let[name,group]of Object.entries(state.groups)){let icon=STATUS_ICONS[group.status]??"\u2753";console.log(` ${name} ${icon} ${group.status}`)}}async function resetWishCommand(slug,confirmed){let wishPath=resolveWishPath(slug);if(!wishPath)throw Error(`No WISH.md found for "${slug}" \u2014 searched cwd and repo root`);let content=await readFile12(wishPath,"utf-8"),groups=parseWishGroups(content);if(groups.length===0)throw Error(`No execution groups found in ${wishPath}`);let existing=await getState(slug);if(existing){if(!await confirmWipe(slug,existing,confirmed)){console.log("Aborted.");return}console.log(`\uD83D\uDDD1\uFE0F Replacing existing state for wish "${slug}"`)}else console.log(`\u2139\uFE0F No existing state for wish "${slug}" \u2014 creating fresh`);let state=await createState(slug,groups);console.log(`\uD83D\uDCDD Recreated state from ${wishPath} (${groups.length} groups)`),printResetState(state)}var STATUS_ICONS,TERMINAL_WISH_STATUSES;var init_state=__esm(()=>{init_interactivity();init_term_format();init_wish_state();init_dispatch();STATUS_ICONS={blocked:"\uD83D\uDD12",ready:"\uD83D\uDFE2",in_progress:"\uD83D\uDD04",done:"\u2705"};TERMINAL_WISH_STATUSES=new Set(["shipped","done","complete","completed","archived"])});var exports_team={};__export(exports_team,{registerTeamNamespace:()=>registerTeamNamespace,handleTeamRepair:()=>handleTeamRepair,handleTeamCreate:()=>handleTeamCreate});import{existsSync as existsSync61,mkdirSync as mkdirSync29,renameSync as renameSync11,statSync as statSync14}from"fs";import{copyFile as copyFile3,cp,mkdir as mkdir8}from"fs/promises";import{homedir as homedir47}from"os";import{join as join73,resolve as resolve15}from"path";function registerTeamNamespace(program2){let team=program2.command("team").description("Team lifecycle management");team.command("create <name>").description("Create a new team with a git worktree").requiredOption("--repo <path>","Path to the git repository").option("--branch <branch>","Base branch to create from","dev").option("--wish <slug>","Wish slug \u2014 auto-spawns a task leader with wish context").option("--tmux-session <name>","Tmux session to place team window in (default: derived from repo path)").option("--session <name>","Alias for --tmux-session (deprecated)").option("--no-spawn","Create team and copy wish without spawning the leader (useful for testing)").addHelpText("after",`
@@ -3100,7 +3107,8 @@ ${label} <TODO>
3100
3107
  JOIN executors e ON e.agent_id = a.id
3101
3108
  WHERE e.id = ${executorId}
3102
3109
  LIMIT 1
3103
- `)[0]??null}async function rejectIfPermanent(deps){let result2=await(deps.lookupCallingAgent??defaultLookupCallingAgent)();if(result2?.kind==="permanent")throw new PermanentAgentDoneRejected({agentId:result2.id})}async function runAgentSessionPath(deps){await rejectIfPermanent(deps);let result2=await(deps.turnCloseFn??turnClose)({outcome:"done"});if(result2.noop)console.log(`\u2139\uFE0F Executor ${result2.executorId} already closed \u2014 no-op.`);else console.log(`\u2705 Turn closed: outcome=done, executor=${result2.executorId}`)}async function doneAction(ref,deps={}){let agentName=process.env.GENIE_AGENT_NAME;try{if(!ref&&agentName){await runAgentSessionPath(deps);return}if(ref){await(deps.wishDone??(async(r)=>{let{doneCommand:doneCommand2}=await Promise.resolve().then(() => (init_state(),exports_state));await doneCommand2(r)}))(ref);return}console.error("\u274C genie done requires either a <slug>#<group> ref (team-lead) or GENIE_AGENT_NAME (inside agent session)."),process.exit(2)}catch(err){if(err instanceof PermanentAgentDoneRejected)console.error(`\u274C ${err.message}`),process.exit(4);throw err}}var PermanentAgentDoneRejected;var init_done=__esm(()=>{init_db();init_turn_close();PermanentAgentDoneRejected=class PermanentAgentDoneRejected extends Error{agentId;reason;constructor(opts){super(`Permanent agent "${opts.agentId}" cannot call \`genie done\`. Permanent identities (team-leads, dir-row placeholders, root agents) do not have task lifecycles. Use \`genie agent stop ${opts.agentId}\` to halt the executor without marking the identity as done.`);this.name="PermanentAgentDoneRejected",this.agentId=opts.agentId,this.reason=opts.reason??"permanent_agents_never_call_done"}}});var exports_blocked={};__export(exports_blocked,{blockedAction:()=>blockedAction});async function blockedAction(options,deps={}){let reason=options.reason?.trim();if(!reason)console.error('\u274C genie blocked requires --reason "<message>"'),process.exit(2);let result2=await(deps.turnCloseFn??turnClose)({outcome:"blocked",reason});if(result2.noop)console.log(`\u2139\uFE0F Executor ${result2.executorId} already closed \u2014 no-op.`);else console.log(`\uD83D\uDD12 Turn closed: outcome=blocked, executor=${result2.executorId}`),console.log(` Reason: ${reason}`)}var init_blocked=__esm(()=>{init_turn_close()});var exports_failed={};__export(exports_failed,{failedAction:()=>failedAction});async function failedAction(options,deps={}){let reason=options.reason?.trim();if(!reason)console.error('\u274C genie failed requires --reason "<message>"'),process.exit(2);let result2=await(deps.turnCloseFn??turnClose)({outcome:"failed",reason});if(result2.noop)console.log(`\u2139\uFE0F Executor ${result2.executorId} already closed \u2014 no-op.`);else console.log(`\uD83D\uDED1 Turn closed: outcome=failed, executor=${result2.executorId}`),console.log(` Reason: ${reason}`)}var init_failed=__esm(()=>{init_turn_close()});async function resolveExecutorId2(sql,opts){if(opts.executorId)return{id:opts.executorId,source:"executorId"};if(opts.paneId){let rows=await sql`
3110
+ `)[0]??null}async function rejectIfPermanent(deps){let result2=await(deps.lookupCallingAgent??defaultLookupCallingAgent)();if(result2?.kind==="permanent")throw new PermanentAgentDoneRejected({agentId:result2.id})}async function runAgentSessionPath(deps,report2){await rejectIfPermanent(deps);let result2=await(deps.turnCloseFn??turnClose)({outcome:"done",reason:report2});if(result2.noop)console.log(`\u2139\uFE0F Executor ${result2.executorId} already closed \u2014 no-op.`);else console.log(`\u2705 Turn closed: outcome=done, executor=${result2.executorId}`),console.log("--- Handoff ---"),console.log(report2.trimEnd()),console.log("--- End handoff ---")}async function doneAction(ref,options,deps={}){let agentName=process.env.GENIE_AGENT_NAME,report2=options.report?.trim();if(!report2)console.error(REPORT_MISSING_HINT),process.exit(2);try{if(!ref&&agentName){await runAgentSessionPath(deps,report2);return}if(ref){await(deps.wishDone??(async(r,rpt)=>{let{doneCommand:doneCommand2}=await Promise.resolve().then(() => (init_state(),exports_state));await doneCommand2(r,rpt)}))(ref,report2);return}console.error("\u274C genie done requires either a <slug>#<group> ref (team-lead) or GENIE_AGENT_NAME (inside agent session)."),process.exit(2)}catch(err){if(err instanceof PermanentAgentDoneRejected)console.error(`\u274C ${err.message}`),process.exit(4);throw err}}var PermanentAgentDoneRejected,REPORT_MISSING_HINT;var init_done=__esm(()=>{init_db();init_turn_close();PermanentAgentDoneRejected=class PermanentAgentDoneRejected extends Error{agentId;reason;constructor(opts){super(`Permanent agent "${opts.agentId}" cannot call \`genie done\`. Permanent identities (team-leads, dir-row placeholders, root agents) do not have task lifecycles. Use \`genie agent stop ${opts.agentId}\` to halt the executor without marking the identity as done.`);this.name="PermanentAgentDoneRejected",this.agentId=opts.agentId,this.reason=opts.reason??"permanent_agents_never_call_done"}};REPORT_MISSING_HINT=['\u274C genie done requires --report "<session handoff>".',""," This is your handoff to the orchestrator. It lands in the audit trail and the"," wave/wish-complete notification, and is the ONLY summary anyone reading later"," will see without replaying your transcript. Write it like you are briefing the"," next person on call.",""," Cover:"," \u2022 What you attempted (the actual goal of this turn / group)"," \u2022 What shipped \u2014 files changed, PRs opened, migrations run, services touched"," \u2022 What is verified vs unverified (tests passed? smoke run? CI green?)"," \u2022 What is left, blocked, or deferred \u2014 and why"," \u2022 Surprises or decisions a future agent needs to know (data losses, infra"," quirks, hooks fired, anything non-obvious)",""," Length: as long as it needs to be. A one-liner is almost never enough."," Multi-line is fine \u2014 pass via heredoc or a file:",` genie done --report "$(cat <<'EOF'`," Goal: wire dev-local auth bridge for hv tenant."," Shipped: PR #143 (fixtures), PR #144 (smoke). Both green on CI."," Verified: 'make smoke' passed locally; tenant-A login round-trip OK."," Left: CSRF rotation deferred to followup (issue #1245)."," Notes: had to bump core@1.260507.5 \u2014 desktop shell rebuild required."," EOF",' )"'].join(`
3111
+ `)});var exports_blocked={};__export(exports_blocked,{blockedAction:()=>blockedAction});async function blockedAction(options,deps={}){let reason=options.reason?.trim();if(!reason)console.error('\u274C genie blocked requires --reason "<message>"'),process.exit(2);let result2=await(deps.turnCloseFn??turnClose)({outcome:"blocked",reason});if(result2.noop)console.log(`\u2139\uFE0F Executor ${result2.executorId} already closed \u2014 no-op.`);else console.log(`\uD83D\uDD12 Turn closed: outcome=blocked, executor=${result2.executorId}`),console.log(` Reason: ${reason}`)}var init_blocked=__esm(()=>{init_turn_close()});var exports_failed={};__export(exports_failed,{failedAction:()=>failedAction});async function failedAction(options,deps={}){let reason=options.reason?.trim();if(!reason)console.error('\u274C genie failed requires --reason "<message>"'),process.exit(2);let result2=await(deps.turnCloseFn??turnClose)({outcome:"failed",reason});if(result2.noop)console.log(`\u2139\uFE0F Executor ${result2.executorId} already closed \u2014 no-op.`);else console.log(`\uD83D\uDED1 Turn closed: outcome=failed, executor=${result2.executorId}`),console.log(` Reason: ${reason}`)}var init_failed=__esm(()=>{init_turn_close()});async function resolveExecutorId2(sql,opts){if(opts.executorId)return{id:opts.executorId,source:"executorId"};if(opts.paneId){let rows=await sql`
3104
3112
  SELECT id FROM executors
3105
3113
  WHERE tmux_pane_id = ${opts.paneId}
3106
3114
  ORDER BY started_at DESC
@@ -4904,8 +4912,26 @@ Stage Pipeline:`);let stages=t.stages;for(let i2=0;i2<stages.length;i2++){let s2
4904
4912
  `)}function parseWishOrError(markdown){try{return parseWish(markdown)}catch(err){if(err instanceof WishParseError)return err;throw err}}function reportWishFileMissing(slug,wishPath,jsonMode){if(jsonMode)console.log(JSON.stringify({error:`Wish file not found: ${wishPath}`,rule:"missing-title",wish:slug,file:wishPath}));else console.error(`\u274C Wish file not found: ${wishPath}`);process.exit(1)}function emitLintReport(report2,wishPath,jsonMode){if(jsonMode)console.log(JSON.stringify(report2));else console.log(formatLintReport(report2,{color:process.stdout.isTTY??!1,path:wishPath}))}function exitWithErrorCount(report2){let errors3=report2.violations.filter((v)=>v.severity==="error").length;process.exit(errors3>0?1:0)}function handleNoFixableViolations(report2,wishPath,jsonMode){if(jsonMode)console.log(JSON.stringify({...report2,fixedViolations:0}));else console.log(formatLintReport(report2,{color:process.stdout.isTTY??!1,path:wishPath})),console.log(`
4905
4913
  No fixable violations to apply.`);process.exit(report2.summary.total>0?1:0)}function handleDryRunFix(report2,markdown,fixed,wishPath,jsonMode){if(jsonMode)console.log(JSON.stringify({...report2,dryRun:!0,diff:renderDiff(markdown,fixed)}));else console.log(formatLintReport(report2,{color:process.stdout.isTTY??!1,path:wishPath})),console.log(`
4906
4914
  --- Dry-run diff (${wishPath}) ---`),console.log(renderDiff(markdown,fixed)),console.log(`
4907
- File not modified (--dry-run).`);process.exit(report2.summary.total>0?1:0)}async function applyAndReportFix(report2,fixed,wishPath,slug,lintOpts,jsonMode){await writeFile11(wishPath,fixed,"utf-8");let docOrError2=parseWishOrError(fixed),report22={...lintWish(docOrError2,fixed,lintOpts),wish:slug,file:wishPath},fixedCount=report2.summary.fixable;if(jsonMode)console.log(JSON.stringify({...report22,fixedViolations:fixedCount}));else console.log(`\u2705 Applied ${fixedCount} fix(es) to ${wishPath}`),console.log(""),console.log(formatLintReport(report22,{color:process.stdout.isTTY??!1,path:wishPath}));let remainingErrors=report22.violations.filter((v)=>v.severity==="error").length;process.exit(remainingErrors>0?1:0)}async function runLintFix(report2,markdown,wishPath,slug,lintOpts,options){let{applyFixes:applyFixes2}=await Promise.resolve().then(() => (init_wish_lint(),exports_wish_lint)),fixed=applyFixes2(markdown,report2),jsonMode=options.json??!1;if(fixed===markdown)handleNoFixableViolations(report2,wishPath,jsonMode);if(options.dryRun)handleDryRunFix(report2,markdown,fixed,wishPath,jsonMode);return applyAndReportFix(report2,fixed,wishPath,slug,lintOpts,jsonMode)}async function wishLintCommand(slug,options){let wishPath=join88(process.cwd(),".genie","wishes",slug,"WISH.md"),jsonMode=options.json??!1;if(!existsSync73(wishPath))reportWishFileMissing(slug,wishPath,jsonMode);let markdown=await readFile17(wishPath,"utf-8"),lintOpts={allowTodoPlaceholders:options.allowTodoPlaceholders},docOrError=parseWishOrError(markdown),report2={...lintWish(docOrError,markdown,lintOpts),wish:slug,file:wishPath};if(options.fix){await runLintFix(report2,markdown,wishPath,slug,lintOpts,options);return}emitLintReport(report2,wishPath,jsonMode),exitWithErrorCount(report2)}function reportWishParseError(error2,jsonMode){let payload={error:error2.message,rule:error2.rule,line:error2.line,column:error2.column??null,file:error2.file??null};if(jsonMode){console.log(JSON.stringify(payload));return}if(console.error(`\u274C Parse failed (${payload.rule}): ${payload.error}`),payload.file)console.error(` File: ${payload.file}`);if(payload.line)console.error(` Line: ${payload.line}`)}async function wishParseCommand(slug,options){try{let doc=parseWishFile(slug);console.log(options.json?JSON.stringify(doc):JSON.stringify(doc,null,2));return}catch(error2){if(error2 instanceof WishParseError)reportWishParseError(error2,options.json??!1),process.exit(1);let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function isWishFile(wishPath){try{return(await stat11(wishPath)).isFile()}catch{return!1}}function parseWishSummary(slug){try{let doc=parseWishFile(slug);return{status:doc.metadata.status??"-",groupCount:String(doc.executionGroups.length)}}catch(error2){return{status:error2 instanceof WishParseError?"malformed":"error",groupCount:"-"}}}async function loadStateCounts(slug){try{let state=await(await Promise.resolve().then(() => (init_wish_state(),exports_wish_state))).getState(slug);if(!state)return{ready:0,inProgress:0,done:0};let ready=0,inProgress=0,done=0;for(let g of Object.values(state.groups))if(g.status==="ready")ready++;else if(g.status==="in_progress")inProgress++;else if(g.status==="done")done++;return{ready,inProgress,done}}catch{return{ready:0,inProgress:0,done:0}}}async function wishListCommand(){let wishesRoot=join88(process.cwd(),".genie","wishes");if(!existsSync73(wishesRoot))console.error(`\u274C Wishes directory not found: ${wishesRoot}`),process.exit(1);let entries=await readdir11(wishesRoot),rows=[];for(let entry2 of entries.sort()){if(entry2.startsWith("_")||entry2.startsWith("."))continue;if(!await isWishFile(join88(wishesRoot,entry2,"WISH.md")))continue;let{status:status2,groupCount}=parseWishSummary(entry2),{ready,inProgress,done}=await loadStateCounts(entry2);rows.push({slug:entry2,status:status2,groupCount,ready,inProgress,done})}if(rows.length===0){console.log("No wishes found under .genie/wishes/");return}let slugW=Math.max(4,...rows.map((r)=>r.slug.length)),statusW=Math.max(6,...rows.map((r)=>r.status.length)),pad=(s2,n)=>s2+" ".repeat(Math.max(0,n-s2.length));console.log(` ${pad("SLUG",slugW)} ${pad("STATUS",statusW)} GROUPS READY IN-PROG DONE`),console.log(` ${"\u2500".repeat(slugW+statusW+32)}`);for(let r of rows)console.log(` ${pad(r.slug,slugW)} ${pad(r.status,statusW)} ${pad(r.groupCount,6)} ${pad(String(r.ready),5)} ${pad(String(r.inProgress),7)} ${r.done}`);console.log(""),console.log(` Total: ${rows.length} wishes`)}function registerWishCommands(program2){let wish=program2.command("wish").description("Wish lifecycle management");wish.command("new <slug>").description("Scaffold a new WISH.md from templates/wish-template.md").option("--force","Overwrite an existing wish directory").action(async(slug,options)=>{await wishNewCommand(slug,options)}),wish.command("lint <slug>").description("Structural health check (stub \u2014 full implementation in Group 3)").option("--json","Emit machine-readable JSON output").option("--fix","Auto-repair deterministic violations").option("--dry-run","With --fix: print diff without writing").option("--allow-todo-placeholders","Pass <TODO> placeholders without emitting todo-placeholder-remaining").action(async(slug,options)=>{await wishLintCommand(slug,options)}),wish.command("parse <slug>").description("Parse WISH.md and emit the WishDocument as JSON").option("--json","One-line JSON output (default: pretty-printed)").action(async(slug,options)=>{await wishParseCommand(slug,options)}),wish.command("status <slug>").description("Show wish state overview for all groups").action(async(slug)=>{await statusCommand(slug)}),wish.command("done <ref>").description("Mark a wish group as done (format: <slug>#<group>)").action(async(ref)=>{await doneCommand(ref)}),wish.command("reset <ref>").option("-y, --yes","Skip confirmation prompt (required in non-interactive mode)").description("Reset wish state. <slug>#<group> resets one in-progress group; bare <slug> wipes the wish and recreates from current WISH.md").action(async(ref,options)=>{await resetAction(ref,options)}),wish.command("list").description("Enumerate all wishes with status, group counts, and progress").action(async()=>{await wishListCommand()})}var _T_BOOT=Date.now();try{let{execSync:execSyncStartup}=__require("child_process");if(execSyncStartup("git config core.bare",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()==="true")execSyncStartup("git config core.bare false",{stdio:["pipe","pipe","pipe"]})}catch{}function parseNumericFlag2(flagName){return(value)=>{let n=Number(value);if(Number.isNaN(n))throw Error(`${flagName} must be a number, got: ${value}`);return n}}if(process.env.GENIE_PROFILE_DB)console.error(`[profile] imports=${Date.now()-_T_BOOT}ms`);var program2=new Command;program2.name("genie").description("Genie CLI - AI-assisted development").version(VERSION);program2.option("--no-interactive","Disable interactive prompts (exit 2 instead of prompting)");program2.option("--no-tui","Skip TUI bootstrap (or set GENIE_TUI_DISABLE=1)");program2.configureHelp({sortSubcommands:!0,showGlobalOptions:!0});program2.configureOutput({outputError:(str5,write)=>{let cmd=program2.commands.find((c)=>process.argv.slice(2,6).includes(c.name())),prefix=cmd?`genie ${cmd.name()}`:"genie";write(`\x1B[31mError (${prefix}): ${str5}\x1B[0m
4908
- `)}});async function startNamedSession(name){let{randomUUID:randomUUID15}=await import("crypto"),{buildTeamLeadCommand:buildTeamLeadCommand2}=await Promise.resolve().then(() => (init_team_lead_command(),exports_team_lead_command)),{getAgentsFilePath:getAgentsFilePath2}=await Promise.resolve().then(() => (init_session(),exports_session)),systemPromptFile=getAgentsFilePath2(),sessionId=randomUUID15(),cmd=buildTeamLeadCommand2(name,{systemPromptFile:systemPromptFile??void 0,sessionId});console.log(`Starting new session: ${name}`);let{spawnSync:spawnSync11}=await import("child_process"),result2=spawnSync11("sh",["-c",cmd],{stdio:"inherit"});if(result2.status)process.exit(result2.status)}program2.command("setup").description("Configure genie settings").option("--quick","Accept all defaults").option("--shortcuts","Only configure keyboard shortcuts").option("--codex","Only configure Codex integration").option("--terminal","Only configure terminal defaults").option("--session","Only configure session settings").option("--reset","Reset configuration to defaults").option("--show","Show current configuration").action(async(options)=>{await setupCommand(options)});program2.command("install").description("Register genie-serve under pm2 with hardened defaults (canonical-pgserve-pm2-supervision wave 2)").option("--skip-pgserve","Don't run `pgserve install` first (operators who manage pgserve themselves)").action(async(options)=>{await installCommand(options)});program2.command("doctor").description("Run diagnostic checks on genie installation").option("--fix","Auto-fix: kill zombie postgres, clean shared memory, restart daemon").option("--observability","Report partition health + GENIE_WIDE_EMIT flag state").option("--perf","Report rolling per-handler P50/P99 from hook_perf_baseline + flag P99 regressions and recent fallback-log entries").option("--fix-team-orphans","Archive stale Claude-team config dirs missing config.json (paired with invincible-genie wish migration 050)").option("--dry-run","Pair with --fix-team-orphans to preview archive moves without mutating").option("--json","Emit JSON instead of human output (pairs with --observability)").action(doctorCommand);program2.command("update").description("Update Genie CLI to the latest version").option("--next","Switch to dev builds (npm @next tag)").option("--stable","Switch to stable releases (npm @latest tag)").option("-y, --yes","Skip the TTY confirmation prompt (or set GENIE_UPDATE_YES=1)").option("--no-restart","Skip post-update maintenance AND the verify probe").option("--no-verify","Run maintenance but skip the post-restart verify probe").option("--skip-maintenance","Skip post-update maintenance (or set GENIE_UPDATE_SKIP_MAINTENANCE=1)").option("--skip-cleanup <names>","Comma-separated legacy-artifact cleanup names to skip").option("--no-sidecar-cleanup","Accepted for cross-CLI portability with omni (no-op for genie)").action(updateCommand);program2.command("migrate").description("Apply pending genie host-migrations (auto-runs on install)").option("--dry-run","List pending migrations without executing").option("--quiet","Suppress per-step OK lines (used by postinstall)").option("--status","Show applied / pending / failed table").action(async(opts)=>{await migrateCommand({dryRun:opts.dryRun,quiet:opts.quiet,status:opts.status})});program2.command("uninstall").description("Remove Genie CLI and clean up hooks").action(uninstallCommand);var shortcuts=program2.command("shortcuts").description("Manage tmux keyboard shortcuts");shortcuts.action(shortcutsShowCommand);shortcuts.command("show").description("Show available shortcuts and installation status").action(shortcutsShowCommand);shortcuts.command("install").description("Install shortcuts to config files (~/.tmux.conf, shell rc)").action(shortcutsInstallCommand);shortcuts.command("uninstall").description("Remove shortcuts from config files").action(shortcutsUninstallCommand);registerServeCommands(program2);registerAppCommand(program2);registerInitCommands(program2);registerTeamNamespace(program2);registerDirNamespace(program2);registerAgentCommands(program2);registerObserveCommands(program2);registerOmniNamespace(program2);registerSendInboxCommands(program2);registerStateCommands(program2);registerDispatchCommands(program2);registerDispatchGroupCommands(program2);registerWishCommands(program2);registerHookNamespace(program2);registerDbCommands(program2);registerScheduleCommands(program2);registerDaemonCommands(program2);registerTaskCommands(program2);registerTypeCommands(program2);registerBoardCommands(program2);registerTagCommands(program2);registerReleaseCommands(program2);registerSecCommands(program2);registerProjectCommands(program2);registerPruneCommands(program2);registerNotifyCommands(program2);registerEventsCommands(program2);registerSessionsCommands(program2);registerMetricsCommands(program2);registerExportCommands(program2);registerImportCommands(program2);registerTemplateCommands(program2);registerBrainCommands(program2);registerBriefCommands(program2);registerApprovalCommands(program2);program2.command("done [ref]").description("Close the current turn (inside an agent session) or mark a wish group done (team-lead, <slug>#<group>)").action(async(ref)=>{let{doneAction:doneAction2}=await Promise.resolve().then(() => (init_done(),exports_done));await doneAction2(ref)});program2.command("blocked").description("Close the current turn with outcome=blocked").requiredOption("--reason <message>","Why the turn is blocked").action(async(options)=>{let{blockedAction:blockedAction2}=await Promise.resolve().then(() => (init_blocked(),exports_blocked));await blockedAction2(options)});program2.command("failed").description("Close the current turn with outcome=failed").requiredOption("--reason <message>","Why the turn failed").action(async(options)=>{let{failedAction:failedAction2}=await Promise.resolve().then(() => (init_failed(),exports_failed));await failedAction2(options)});program2.command("pane-trap").description("Internal: write clean_exit_unverified outcome for a dying pane/shell. Invoked by the tmux pane-died hook and the inline shell EXIT trap.").option("--pane-id <id>","tmux pane id (%N) \u2014 resolved to executor via executors.tmux_pane_id").option("--executor-id <id>","explicit executor UUID (preferred when available)").option("--reason <reason>","trap source: pane_died or shell_exit","pane_died").action(async(options)=>{let{paneTrapAction:paneTrapAction2}=await Promise.resolve().then(() => (init_pane_trap2(),exports_pane_trap));await paneTrapAction2(options)});installWorkspaceCheck(program2);var auditTimers=new Map,auditSpans=new Map;program2.hook("preAction",(_thisCommand,actionCommand)=>{let name=actionCommand.name();auditTimers.set(name,Date.now()),Promise.resolve().then(() => (init_db(),exports_db)).then(({isConnected:isConnected2})=>{if(!isConnected2())return;recordAuditEvent("command",name,"command_start",getActor(),{args:actionCommand.args}).catch(()=>{})}).catch(()=>{}),(async()=>{try{let{isWideEmitEnabled:isWideEmitEnabled2}=await Promise.resolve().then(() => exports_observability_flag);if(!isWideEmitEnabled2())return;let{startSpan:startSpan2}=await Promise.resolve().then(() => (init_emit(),exports_emit)),{getAmbient:getAmbient2}=await Promise.resolve().then(() => (init_trace_context(),exports_trace_context)),handle=startSpan2("cli.command",{command:name,args:actionCommand.args??[],cwd:process.cwd()},{severity:"debug",source_subsystem:"cli",ctx:getAmbient2()??void 0,agent:getActor()});auditSpans.set(name,handle)}catch{}})()});program2.hook("postAction",async(_thisCommand,actionCommand)=>{let name=actionCommand.name(),startMs=auditTimers.get(name),durationMs=startMs?Date.now()-startMs:void 0;auditTimers.delete(name);try{let{isConnected:isConnected2}=await Promise.resolve().then(() => (init_db(),exports_db));if(!isConnected2())return;await recordAuditEvent("command",name,"command_success",getActor(),{args:actionCommand.args,duration_ms:durationMs})}catch{}let handle=auditSpans.get(name);auditSpans.delete(name);try{if(handle){let{isWideEmitEnabled:isWideEmitEnabled2}=await Promise.resolve().then(() => exports_observability_flag);if(isWideEmitEnabled2()){let{endSpan:endSpan2}=await Promise.resolve().then(() => (init_emit(),exports_emit));endSpan2(handle,{exit_code:0,duration_ms:durationMs??0},{severity:"debug",source_subsystem:"cli",agent:getActor()})}}}catch{}try{let{flushNow:flushNow2}=await Promise.resolve().then(() => (init_emit(),exports_emit));await flushNow2()}catch{}});program2.command("spawn <name>").description("Spawn a new agent by name (resolves from directory or built-ins)").option("--provider <provider>","Provider: claude or codex","claude").option("--team <team>","Team name").option("--model <model>","Model override (e.g., sonnet, opus)").option("--skill <skill>","Skill to load (optional)").option("--layout <layout>","Layout mode: mosaic (default) or vertical").option("--color <color>","Teammate pane border color").option("--plan-mode","Start teammate in plan mode").option("--permission-mode <mode>","Permission mode (e.g., acceptEdits)").option("--extra-args <args...>","Extra CLI args forwarded to provider").option("--cwd <path>","Working directory for the agent (overrides directory entry)").option("--session <session>","Tmux session name to spawn into").option("--role <role>","Override role name for registration (avoids duplicate guard)").option("--new-window","Create a new tmux window instead of splitting").option("--window <target>","Tmux window to split into (e.g., genie:3)").option("--no-auto-resume","Disable auto-resume on pane death").option("--no-auto-sync","Disable auto-registration from workspace agents directory").option("--stream","Stream SDK messages to stdout in real-time (claude-sdk provider)").option("--stream-format <format>","Streaming output format: text, json, ndjson (default: text)","text").option("--sdk-max-turns <n>","SDK: max conversation turns",parseNumericFlag2("--sdk-max-turns")).option("--sdk-max-budget <usd>","SDK: max budget in USD",parseNumericFlag2("--sdk-max-budget")).option("--sdk-stream","SDK: enable streaming output (shortcut for --stream)").option("--sdk-effort <level>","SDK: reasoning effort level (low, medium, high, max)").option("--sdk-resume <session-id>","SDK: resume a previous session by ID").option("--prompt <text>","Initial prompt to send as the first user message").addHelpText("after",`
4915
+ File not modified (--dry-run).`);process.exit(report2.summary.total>0?1:0)}async function applyAndReportFix(report2,fixed,wishPath,slug,lintOpts,jsonMode){await writeFile11(wishPath,fixed,"utf-8");let docOrError2=parseWishOrError(fixed),report22={...lintWish(docOrError2,fixed,lintOpts),wish:slug,file:wishPath},fixedCount=report2.summary.fixable;if(jsonMode)console.log(JSON.stringify({...report22,fixedViolations:fixedCount}));else console.log(`\u2705 Applied ${fixedCount} fix(es) to ${wishPath}`),console.log(""),console.log(formatLintReport(report22,{color:process.stdout.isTTY??!1,path:wishPath}));let remainingErrors=report22.violations.filter((v)=>v.severity==="error").length;process.exit(remainingErrors>0?1:0)}async function runLintFix(report2,markdown,wishPath,slug,lintOpts,options){let{applyFixes:applyFixes2}=await Promise.resolve().then(() => (init_wish_lint(),exports_wish_lint)),fixed=applyFixes2(markdown,report2),jsonMode=options.json??!1;if(fixed===markdown)handleNoFixableViolations(report2,wishPath,jsonMode);if(options.dryRun)handleDryRunFix(report2,markdown,fixed,wishPath,jsonMode);return applyAndReportFix(report2,fixed,wishPath,slug,lintOpts,jsonMode)}async function wishLintCommand(slug,options){let wishPath=join88(process.cwd(),".genie","wishes",slug,"WISH.md"),jsonMode=options.json??!1;if(!existsSync73(wishPath))reportWishFileMissing(slug,wishPath,jsonMode);let markdown=await readFile17(wishPath,"utf-8"),lintOpts={allowTodoPlaceholders:options.allowTodoPlaceholders},docOrError=parseWishOrError(markdown),report2={...lintWish(docOrError,markdown,lintOpts),wish:slug,file:wishPath};if(options.fix){await runLintFix(report2,markdown,wishPath,slug,lintOpts,options);return}emitLintReport(report2,wishPath,jsonMode),exitWithErrorCount(report2)}function reportWishParseError(error2,jsonMode){let payload={error:error2.message,rule:error2.rule,line:error2.line,column:error2.column??null,file:error2.file??null};if(jsonMode){console.log(JSON.stringify(payload));return}if(console.error(`\u274C Parse failed (${payload.rule}): ${payload.error}`),payload.file)console.error(` File: ${payload.file}`);if(payload.line)console.error(` Line: ${payload.line}`)}async function wishParseCommand(slug,options){try{let doc=parseWishFile(slug);console.log(options.json?JSON.stringify(doc):JSON.stringify(doc,null,2));return}catch(error2){if(error2 instanceof WishParseError)reportWishParseError(error2,options.json??!1),process.exit(1);let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function isWishFile(wishPath){try{return(await stat11(wishPath)).isFile()}catch{return!1}}function parseWishSummary(slug){try{let doc=parseWishFile(slug);return{status:doc.metadata.status??"-",groupCount:String(doc.executionGroups.length)}}catch(error2){return{status:error2 instanceof WishParseError?"malformed":"error",groupCount:"-"}}}async function loadStateCounts(slug){try{let state=await(await Promise.resolve().then(() => (init_wish_state(),exports_wish_state))).getState(slug);if(!state)return{ready:0,inProgress:0,done:0};let ready=0,inProgress=0,done=0;for(let g of Object.values(state.groups))if(g.status==="ready")ready++;else if(g.status==="in_progress")inProgress++;else if(g.status==="done")done++;return{ready,inProgress,done}}catch{return{ready:0,inProgress:0,done:0}}}async function wishListCommand(){let wishesRoot=join88(process.cwd(),".genie","wishes");if(!existsSync73(wishesRoot))console.error(`\u274C Wishes directory not found: ${wishesRoot}`),process.exit(1);let entries=await readdir11(wishesRoot),rows=[];for(let entry2 of entries.sort()){if(entry2.startsWith("_")||entry2.startsWith("."))continue;if(!await isWishFile(join88(wishesRoot,entry2,"WISH.md")))continue;let{status:status2,groupCount}=parseWishSummary(entry2),{ready,inProgress,done}=await loadStateCounts(entry2);rows.push({slug:entry2,status:status2,groupCount,ready,inProgress,done})}if(rows.length===0){console.log("No wishes found under .genie/wishes/");return}let slugW=Math.max(4,...rows.map((r)=>r.slug.length)),statusW=Math.max(6,...rows.map((r)=>r.status.length)),pad=(s2,n)=>s2+" ".repeat(Math.max(0,n-s2.length));console.log(` ${pad("SLUG",slugW)} ${pad("STATUS",statusW)} GROUPS READY IN-PROG DONE`),console.log(` ${"\u2500".repeat(slugW+statusW+32)}`);for(let r of rows)console.log(` ${pad(r.slug,slugW)} ${pad(r.status,statusW)} ${pad(r.groupCount,6)} ${pad(String(r.ready),5)} ${pad(String(r.inProgress),7)} ${r.done}`);console.log(""),console.log(` Total: ${rows.length} wishes`)}function registerWishCommands(program2){let wish=program2.command("wish").description("Wish lifecycle management");wish.command("new <slug>").description("Scaffold a new WISH.md from templates/wish-template.md").option("--force","Overwrite an existing wish directory").action(async(slug,options)=>{await wishNewCommand(slug,options)}),wish.command("lint <slug>").description("Structural health check (stub \u2014 full implementation in Group 3)").option("--json","Emit machine-readable JSON output").option("--fix","Auto-repair deterministic violations").option("--dry-run","With --fix: print diff without writing").option("--allow-todo-placeholders","Pass <TODO> placeholders without emitting todo-placeholder-remaining").action(async(slug,options)=>{await wishLintCommand(slug,options)}),wish.command("parse <slug>").description("Parse WISH.md and emit the WishDocument as JSON").option("--json","One-line JSON output (default: pretty-printed)").action(async(slug,options)=>{await wishParseCommand(slug,options)}),wish.command("status <slug>").description("Show wish state overview for all groups").action(async(slug)=>{await statusCommand(slug)}),wish.command("done <ref>").description("Mark a wish group as done (format: <slug>#<group>)").option("-r, --report <message>","Full group handoff \u2014 what shipped, what is verified, what is left, surprises, decisions. REQUIRED. Multi-line OK (pass via heredoc).").action(async(ref,options)=>{let report2=options.report?.trim();if(!report2)console.error(`\u274C genie wish done requires --report "<group handoff>".
4916
+ `+`
4917
+ This is the orchestrator's handoff. It lands in the audit trail and the
4918
+ `+` wave/wish-complete notification \u2014 the ONLY summary anyone reading later
4919
+ `+` will see without replaying your transcript.
4920
+
4921
+ Cover: what was attempted, what shipped, what is verified vs unverified,
4922
+ what is left or deferred, and any surprises or decisions a future agent
4923
+ needs to know. Length: as long as it needs to be. Multi-line is fine.
4924
+
4925
+ Example (heredoc):
4926
+ genie wish done my-wish#3 --report "$(cat <<'EOF'
4927
+ Goal: wire dev-local fixtures + smoke for group 3.
4928
+ Shipped: PR #143 + PR #144, both merged to dev.
4929
+ Verified: make smoke passed; tenant-A login round-trip OK.
4930
+ Left: CSRF rotation deferred to issue #1245.
4931
+ `+` Notes: bumped core@1.260507.5 \u2014 desktop rebuild required.
4932
+ `+` EOF
4933
+ )"`),process.exit(2);await doneCommand(ref,report2)}),wish.command("reset <ref>").option("-y, --yes","Skip confirmation prompt (required in non-interactive mode)").description("Reset wish state. <slug>#<group> resets one in-progress group; bare <slug> wipes the wish and recreates from current WISH.md").action(async(ref,options)=>{await resetAction(ref,options)}),wish.command("list").description("Enumerate all wishes with status, group counts, and progress").action(async()=>{await wishListCommand()})}var _T_BOOT=Date.now();try{let{execSync:execSyncStartup}=__require("child_process");if(execSyncStartup("git config core.bare",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()==="true")execSyncStartup("git config core.bare false",{stdio:["pipe","pipe","pipe"]})}catch{}function parseNumericFlag2(flagName){return(value)=>{let n=Number(value);if(Number.isNaN(n))throw Error(`${flagName} must be a number, got: ${value}`);return n}}if(process.env.GENIE_PROFILE_DB)console.error(`[profile] imports=${Date.now()-_T_BOOT}ms`);var program2=new Command;program2.name("genie").description("Genie CLI - AI-assisted development").version(VERSION);program2.option("--no-interactive","Disable interactive prompts (exit 2 instead of prompting)");program2.option("--no-tui","Skip TUI bootstrap (or set GENIE_TUI_DISABLE=1)");program2.configureHelp({sortSubcommands:!0,showGlobalOptions:!0});program2.configureOutput({outputError:(str5,write)=>{let cmd=program2.commands.find((c)=>process.argv.slice(2,6).includes(c.name())),prefix=cmd?`genie ${cmd.name()}`:"genie";write(`\x1B[31mError (${prefix}): ${str5}\x1B[0m
4934
+ `)}});async function startNamedSession(name){let{randomUUID:randomUUID15}=await import("crypto"),{buildTeamLeadCommand:buildTeamLeadCommand2}=await Promise.resolve().then(() => (init_team_lead_command(),exports_team_lead_command)),{getAgentsFilePath:getAgentsFilePath2}=await Promise.resolve().then(() => (init_session(),exports_session)),systemPromptFile=getAgentsFilePath2(),sessionId=randomUUID15(),cmd=buildTeamLeadCommand2(name,{systemPromptFile:systemPromptFile??void 0,sessionId});console.log(`Starting new session: ${name}`);let{spawnSync:spawnSync11}=await import("child_process"),result2=spawnSync11("sh",["-c",cmd],{stdio:"inherit"});if(result2.status)process.exit(result2.status)}program2.command("setup").description("Configure genie settings").option("--quick","Accept all defaults").option("--shortcuts","Only configure keyboard shortcuts").option("--codex","Only configure Codex integration").option("--terminal","Only configure terminal defaults").option("--session","Only configure session settings").option("--reset","Reset configuration to defaults").option("--show","Show current configuration").action(async(options)=>{await setupCommand(options)});program2.command("install").description("Register genie-serve under pm2 with hardened defaults (canonical-pgserve-pm2-supervision wave 2)").option("--skip-pgserve","Don't run `pgserve install` first (operators who manage pgserve themselves)").action(async(options)=>{await installCommand(options)});program2.command("doctor").description("Run diagnostic checks on genie installation").option("--fix","Auto-fix: kill zombie postgres, clean shared memory, restart daemon").option("--observability","Report partition health + GENIE_WIDE_EMIT flag state").option("--perf","Report rolling per-handler P50/P99 from hook_perf_baseline + flag P99 regressions and recent fallback-log entries").option("--fix-team-orphans","Archive stale Claude-team config dirs missing config.json (paired with invincible-genie wish migration 050)").option("--dry-run","Pair with --fix-team-orphans to preview archive moves without mutating").option("--json","Emit JSON instead of human output (pairs with --observability)").action(doctorCommand);program2.command("update").description("Update Genie CLI to the latest version").option("--next","Switch to dev builds (npm @next tag)").option("--stable","Switch to stable releases (npm @latest tag)").option("-y, --yes","Skip the TTY confirmation prompt (or set GENIE_UPDATE_YES=1)").option("--no-restart","Skip post-update maintenance AND the verify probe").option("--no-verify","Run maintenance but skip the post-restart verify probe").option("--skip-maintenance","Skip post-update maintenance (or set GENIE_UPDATE_SKIP_MAINTENANCE=1)").option("--skip-cleanup <names>","Comma-separated legacy-artifact cleanup names to skip").option("--no-sidecar-cleanup","Accepted for cross-CLI portability with omni (no-op for genie)").action(updateCommand);program2.command("migrate").description("Apply pending genie host-migrations (auto-runs on install)").option("--dry-run","List pending migrations without executing").option("--quiet","Suppress per-step OK lines (used by postinstall)").option("--status","Show applied / pending / failed table").action(async(opts)=>{await migrateCommand({dryRun:opts.dryRun,quiet:opts.quiet,status:opts.status})});program2.command("uninstall").description("Remove Genie CLI and clean up hooks").action(uninstallCommand);var shortcuts=program2.command("shortcuts").description("Manage tmux keyboard shortcuts");shortcuts.action(shortcutsShowCommand);shortcuts.command("show").description("Show available shortcuts and installation status").action(shortcutsShowCommand);shortcuts.command("install").description("Install shortcuts to config files (~/.tmux.conf, shell rc)").action(shortcutsInstallCommand);shortcuts.command("uninstall").description("Remove shortcuts from config files").action(shortcutsUninstallCommand);registerServeCommands(program2);registerAppCommand(program2);registerInitCommands(program2);registerTeamNamespace(program2);registerDirNamespace(program2);registerAgentCommands(program2);registerObserveCommands(program2);registerOmniNamespace(program2);registerSendInboxCommands(program2);registerStateCommands(program2);registerDispatchCommands(program2);registerDispatchGroupCommands(program2);registerWishCommands(program2);registerHookNamespace(program2);registerDbCommands(program2);registerScheduleCommands(program2);registerDaemonCommands(program2);registerTaskCommands(program2);registerTypeCommands(program2);registerBoardCommands(program2);registerTagCommands(program2);registerReleaseCommands(program2);registerSecCommands(program2);registerProjectCommands(program2);registerPruneCommands(program2);registerNotifyCommands(program2);registerEventsCommands(program2);registerSessionsCommands(program2);registerMetricsCommands(program2);registerExportCommands(program2);registerImportCommands(program2);registerTemplateCommands(program2);registerBrainCommands(program2);registerBriefCommands(program2);registerApprovalCommands(program2);program2.command("done [ref]").description("Close the current turn (inside an agent session) or mark a wish group done (team-lead, <slug>#<group>)").option("-r, --report <message>","Full session handoff \u2014 what shipped, what is verified, what is left, surprises, decisions. REQUIRED. As long as it needs to be (multi-line OK; pass via heredoc).").action(async(ref,options)=>{let{doneAction:doneAction2}=await Promise.resolve().then(() => (init_done(),exports_done));await doneAction2(ref,options)});program2.command("blocked").description("Close the current turn with outcome=blocked").requiredOption("--reason <message>","Why the turn is blocked").action(async(options)=>{let{blockedAction:blockedAction2}=await Promise.resolve().then(() => (init_blocked(),exports_blocked));await blockedAction2(options)});program2.command("failed").description("Close the current turn with outcome=failed").requiredOption("--reason <message>","Why the turn failed").action(async(options)=>{let{failedAction:failedAction2}=await Promise.resolve().then(() => (init_failed(),exports_failed));await failedAction2(options)});program2.command("pane-trap").description("Internal: write clean_exit_unverified outcome for a dying pane/shell. Invoked by the tmux pane-died hook and the inline shell EXIT trap.").option("--pane-id <id>","tmux pane id (%N) \u2014 resolved to executor via executors.tmux_pane_id").option("--executor-id <id>","explicit executor UUID (preferred when available)").option("--reason <reason>","trap source: pane_died or shell_exit","pane_died").action(async(options)=>{let{paneTrapAction:paneTrapAction2}=await Promise.resolve().then(() => (init_pane_trap2(),exports_pane_trap));await paneTrapAction2(options)});installWorkspaceCheck(program2);var auditTimers=new Map,auditSpans=new Map;program2.hook("preAction",(_thisCommand,actionCommand)=>{let name=actionCommand.name();auditTimers.set(name,Date.now()),Promise.resolve().then(() => (init_db(),exports_db)).then(({isConnected:isConnected2})=>{if(!isConnected2())return;recordAuditEvent("command",name,"command_start",getActor(),{args:actionCommand.args}).catch(()=>{})}).catch(()=>{}),(async()=>{try{let{isWideEmitEnabled:isWideEmitEnabled2}=await Promise.resolve().then(() => exports_observability_flag);if(!isWideEmitEnabled2())return;let{startSpan:startSpan2}=await Promise.resolve().then(() => (init_emit(),exports_emit)),{getAmbient:getAmbient2}=await Promise.resolve().then(() => (init_trace_context(),exports_trace_context)),handle=startSpan2("cli.command",{command:name,args:actionCommand.args??[],cwd:process.cwd()},{severity:"debug",source_subsystem:"cli",ctx:getAmbient2()??void 0,agent:getActor()});auditSpans.set(name,handle)}catch{}})()});program2.hook("postAction",async(_thisCommand,actionCommand)=>{let name=actionCommand.name(),startMs=auditTimers.get(name),durationMs=startMs?Date.now()-startMs:void 0;auditTimers.delete(name);try{let{isConnected:isConnected2}=await Promise.resolve().then(() => (init_db(),exports_db));if(!isConnected2())return;await recordAuditEvent("command",name,"command_success",getActor(),{args:actionCommand.args,duration_ms:durationMs})}catch{}let handle=auditSpans.get(name);auditSpans.delete(name);try{if(handle){let{isWideEmitEnabled:isWideEmitEnabled2}=await Promise.resolve().then(() => exports_observability_flag);if(isWideEmitEnabled2()){let{endSpan:endSpan2}=await Promise.resolve().then(() => (init_emit(),exports_emit));endSpan2(handle,{exit_code:0,duration_ms:durationMs??0},{severity:"debug",source_subsystem:"cli",agent:getActor()})}}}catch{}try{let{flushNow:flushNow2}=await Promise.resolve().then(() => (init_emit(),exports_emit));await flushNow2()}catch{}});program2.command("spawn <name>").description("Spawn a new agent by name (resolves from directory or built-ins)").option("--provider <provider>","Provider: claude or codex","claude").option("--team <team>","Team name").option("--model <model>","Model override (e.g., sonnet, opus)").option("--skill <skill>","Skill to load (optional)").option("--layout <layout>","Layout mode: mosaic (default) or vertical").option("--color <color>","Teammate pane border color").option("--plan-mode","Start teammate in plan mode").option("--permission-mode <mode>","Permission mode (e.g., acceptEdits)").option("--extra-args <args...>","Extra CLI args forwarded to provider").option("--cwd <path>","Working directory for the agent (overrides directory entry)").option("--session <session>","Tmux session name to spawn into").option("--role <role>","Override role name for registration (avoids duplicate guard)").option("--new-window","Create a new tmux window instead of splitting").option("--window <target>","Tmux window to split into (e.g., genie:3)").option("--no-auto-resume","Disable auto-resume on pane death").option("--no-auto-sync","Disable auto-registration from workspace agents directory").option("--stream","Stream SDK messages to stdout in real-time (claude-sdk provider)").option("--stream-format <format>","Streaming output format: text, json, ndjson (default: text)","text").option("--sdk-max-turns <n>","SDK: max conversation turns",parseNumericFlag2("--sdk-max-turns")).option("--sdk-max-budget <usd>","SDK: max budget in USD",parseNumericFlag2("--sdk-max-budget")).option("--sdk-stream","SDK: enable streaming output (shortcut for --stream)").option("--sdk-effort <level>","SDK: reasoning effort level (low, medium, high, max)").option("--sdk-resume <session-id>","SDK: resume a previous session by ID").option("--prompt <text>","Initial prompt to send as the first user message").addHelpText("after",`
4909
4935
  Examples:
4910
4936
  genie spawn engineer # Spawn built-in engineer role
4911
4937
  genie spawn researcher --model sonnet # Spawn with model override
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260507.5",
3
+ "version": "4.260507.6",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows. NOTE: the npm distribution is being soft-deprecated — the canonical install is `curl -fsSL https://get.automagik.dev/genie | bash` (cosign + SLSA verified). See https://automagik.dev/genie/security/distribution-sovereignty",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260507.5",
3
+ "version": "4.260507.6",
4
4
  "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.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260507.5",
3
+ "version": "4.260507.6",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",