@automagik/genie 4.260323.3 → 4.260323.5
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/.claude-plugin/marketplace.json +1 -1
- package/dist/genie.js +7 -7
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/genie/.claude-plugin/plugin.json +1 -1
- package/plugins/genie/agents/team-lead/AGENTS.md +1 -0
- package/plugins/genie/agents/team-lead/HEARTBEAT.md +3 -0
- package/plugins/genie/package.json +1 -1
- package/src/term-commands/history.ts +23 -30
- package/src/term-commands/state.ts +2 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "genie",
|
|
13
|
-
"version": "4.260323.
|
|
13
|
+
"version": "4.260323.5",
|
|
14
14
|
"source": "./plugins/genie",
|
|
15
15
|
"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."
|
|
16
16
|
}
|
package/dist/genie.js
CHANGED
|
@@ -969,7 +969,7 @@ Done. ${results.length} migration${results.length===1?"":"s"} applied.`),await s
|
|
|
969
969
|
(built-in ${resolved.entry.registeredAt==="(built-in)"?"agent":"agent"})`);console.log(""),printEntry(resolved.entry),console.log("")}async function listEntries(json2,includeBuiltins){let entries=await ls();if(json2){listEntriesJson(entries,includeBuiltins);return}if(entries.length===0&&!includeBuiltins){console.log(`
|
|
970
970
|
No agents registered. Add one with: genie dir add <name> --dir <path>`),console.log(`Use --builtins to also see built-in roles and council members.
|
|
971
971
|
`);return}if(entries.length>0)printRegisteredTable(entries);if(includeBuiltins)printBuiltinsTable()}function listEntriesJson(entries,includeBuiltins){let result=entries.map((e)=>({...e,builtin:!1}));if(includeBuiltins)for(let b2 of ALL_BUILTINS)result.push({name:b2.name,description:b2.description,model:b2.model,category:b2.category,scope:"built-in",builtin:!0});console.log(JSON.stringify(result,null,2))}function printRegisteredTable(entries){console.log(""),console.log("REGISTERED AGENTS"),console.log("-".repeat(85)),console.log(` ${"NAME".padEnd(22)}${"SCOPE".padEnd(10)}${"DIR".padEnd(30)}${"MODE".padEnd(8)}${"MODEL".padEnd(8)}ROLES`),console.log(` ${"-".repeat(20)} ${"-".repeat(8)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(6)} ${"-".repeat(15)}`);for(let entry of entries){let dir=contractPath(entry.dir),truncDir=dir.length>28?`${dir.slice(0,25)}...`:dir,roles=entry.roles?.join(", ")||"-";console.log(` ${entry.name.padEnd(22)}${entry.scope.padEnd(10)}${truncDir.padEnd(30)}${entry.promptMode.padEnd(8)}${(entry.model||"-").padEnd(8)}${roles}`)}console.log("")}function printBuiltinsTable(){console.log("BUILT-IN AGENTS"),console.log("-".repeat(80)),console.log(` ${"NAME".padEnd(22)}${"TYPE".padEnd(10)}${"MODEL".padEnd(8)}DESCRIPTION`),console.log(` ${"-".repeat(20)} ${"-".repeat(8)} ${"-".repeat(6)} ${"-".repeat(30)}`);for(let agent of ALL_BUILTINS)console.log(` ${agent.name.padEnd(22)}${agent.category.padEnd(10)}${(agent.model||"-").padEnd(8)}${agent.description}`);console.log("")}async function handleOmniRegistration(name,options){let omniUrl=await resolveOmniApiUrl();if(!omniUrl)return;console.log(`
|
|
972
|
-
Registering in Omni (${omniUrl})...`);let existingId=await findOmniAgent(name);if(existingId){console.log(` Agent already exists in Omni: ${existingId}`),await edit(name,{omniAgentId:existingId},{global:options.global}),console.log(" Linked existing Omni agent to directory entry.");return}let omniAgentId=await registerAgentInOmni(name,{model:options.model,roles:options.roles});if(omniAgentId)await edit(name,{omniAgentId},{global:options.global}),console.log(` Omni agent created: ${omniAgentId}`),console.log(" Session isolation: per-person + per-channel")}async function handleAgentRegister(name,options){let promptMode=validatePromptMode(options.promptMode),entry=await add({name,dir:resolvePath(options.dir),repo:options.repo?resolvePath(options.repo):void 0,promptMode,model:options.model,roles:options.roles},{global:options.global}),scope=options.global?"global":"project";if(console.log(`Agent "${entry.name}" registered (${scope}).`),printEntry(entry),!options.skipOmni)await handleOmniRegistration(name,options)}function registerAgentNamespace(program2){program2.command("agent").description("Agent lifecycle management").command("register <name>").description("Register an agent locally and auto-register in Omni when configured").requiredOption("--dir <path>","Agent folder (CWD + AGENTS.md)").option("--repo <path>","Default git repo (overridden by team)").option("--prompt-mode <mode>","Prompt mode: append or system","append").option("--model <model>","Default model (sonnet, opus, codex)").option("--roles <roles...>","Built-in roles this agent can orchestrate").option("--global","Write to global directory instead of project").option("--skip-omni","Skip Omni auto-registration").action(async(name,options)=>{try{await handleAgentRegister(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_protocol_router();init_wish_state();init_agents();import{execSync as execSync6}from"child_process";import{existsSync as existsSync19}from"fs";import{mkdir as mkdir8,readFile as readFile9,writeFile as writeFile7}from"fs/promises";import{tmpdir}from"os";import{join as join24}from"path";init_wish_state();import{execSync as execSync5}from"child_process";import{existsSync as existsSync18}from"fs";import{readFile as readFile8}from"fs/promises";import{join as join23}from"path";function parseRef(ref){let hashIdx=ref.indexOf("#");if(hashIdx===-1)throw Error(`Invalid reference "${ref}". Expected format: <slug>#<group>`);let slug=ref.slice(0,hashIdx),group=ref.slice(hashIdx+1);if(!slug||!group)throw Error(`Invalid reference "${ref}". Both slug and group are required.`);return{slug,group}}var STATUS_ICONS={blocked:"\uD83D\uDD12",ready:"\uD83D\uDFE2",in_progress:"\uD83D\uDD04",done:"\u2705"};function formatTimestamp(iso){if(!iso)return"";return new Date(iso).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1})}function padRight2(str2,len){return str2.length>=len?str2:str2+" ".repeat(len-str2.length)}async function detectWaveCompletion(slug,groupName,cwd){let base=cwd??process.cwd(),wishPath=join23(base,".genie","wishes",slug,"WISH.md");if(!existsSync18(wishPath))return null;let content=await readFile8(wishPath,"utf-8"),targetWave=parseExecutionStrategy(content).find((w)=>w.groups.some((g)=>g.group===groupName));if(!targetWave)return null;let state2=await getState(slug,cwd);if(!state2)return null;let waveGroupNames=targetWave.groups.map((g)=>g.group);if(!waveGroupNames.every((g)=>state2.groups[g]?.status==="done"))return null;return{waveName:targetWave.name,waveGroups:waveGroupNames}}async function ensureWorkPushed(slug,group){try{if(execSync5("git status --porcelain",{encoding:"utf-8"}).trim())console.log(" Committing dirty working tree..."),execSync5("git add -A",{encoding:"utf-8"}),execSync5(`git commit -m "wip: ${slug}#${group}"`,{encoding:"utf-8"}),console.log(` Committed as "wip: ${slug}#${group}"`)}catch{}try{if(execSync5("git log @{u}..HEAD --oneline",{encoding:"utf-8"}).trim())console.log(" Pushing unpushed commits..."),execSync5("git push",{encoding:"utf-8",timeout:30000}),console.log(" Push complete.")}catch{try{let branch=execSync5("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim();if(branch&&branch!=="HEAD")execSync5(`git push -u origin ${branch}`,{encoding:"utf-8",timeout:30000}),console.log(" Push complete (set upstream).")}catch{console.log(" \u26A0\uFE0F Push failed \u2014 manual push may be needed.")}}}function autoKillPane(){let paneId=process.env.TMUX_PANE;if(paneId)setTimeout(()=>{try{execSync5(`tmux kill-pane -t '${paneId}'`,{encoding:"utf-8"})}catch{process.exit(0)}},1000);else process.exit(0)}async function doneCommand(ref){try{let{slug,group}=parseRef(ref),result=await completeGroup(slug,group);if(console.log(`\u2705 Group "${group}" marked as done in wish "${slug}"`),result.completedAt)console.log(` Completed at: ${formatTimestamp(result.completedAt)}`);let state2=await getState(slug);if(state2){let nowReady=Object.entries(state2.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 waveResult=await detectWaveCompletion(slug,group);if(waveResult){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(),message=`${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(", ")}]. Run /review or advance to next wave.`,result2=await protocolRouter.sendMessage(repoPath,"cli","team-lead",message);if(result2&&typeof result2==="object"&&"delivered"in result2&&!result2.delivered)console.warn(" \u26A0\uFE0F Wave-complete notification may not have been delivered.");else console.log(" Notified team-lead of wave completion.")}catch{console.warn(" \u26A0\uFE0F Could not notify team-lead (messaging unavailable).")}}autoKillPane()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function statusCommand(slug){try{let state2=await getState(slug);if(!state2)console.error(`\u274C No state found for wish "${slug}"`),console.error("
|
|
972
|
+
Registering in Omni (${omniUrl})...`);let existingId=await findOmniAgent(name);if(existingId){console.log(` Agent already exists in Omni: ${existingId}`),await edit(name,{omniAgentId:existingId},{global:options.global}),console.log(" Linked existing Omni agent to directory entry.");return}let omniAgentId=await registerAgentInOmni(name,{model:options.model,roles:options.roles});if(omniAgentId)await edit(name,{omniAgentId},{global:options.global}),console.log(` Omni agent created: ${omniAgentId}`),console.log(" Session isolation: per-person + per-channel")}async function handleAgentRegister(name,options){let promptMode=validatePromptMode(options.promptMode),entry=await add({name,dir:resolvePath(options.dir),repo:options.repo?resolvePath(options.repo):void 0,promptMode,model:options.model,roles:options.roles},{global:options.global}),scope=options.global?"global":"project";if(console.log(`Agent "${entry.name}" registered (${scope}).`),printEntry(entry),!options.skipOmni)await handleOmniRegistration(name,options)}function registerAgentNamespace(program2){program2.command("agent").description("Agent lifecycle management").command("register <name>").description("Register an agent locally and auto-register in Omni when configured").requiredOption("--dir <path>","Agent folder (CWD + AGENTS.md)").option("--repo <path>","Default git repo (overridden by team)").option("--prompt-mode <mode>","Prompt mode: append or system","append").option("--model <model>","Default model (sonnet, opus, codex)").option("--roles <roles...>","Built-in roles this agent can orchestrate").option("--global","Write to global directory instead of project").option("--skip-omni","Skip Omni auto-registration").action(async(name,options)=>{try{await handleAgentRegister(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_protocol_router();init_wish_state();init_agents();import{execSync as execSync6}from"child_process";import{existsSync as existsSync19}from"fs";import{mkdir as mkdir8,readFile as readFile9,writeFile as writeFile7}from"fs/promises";import{tmpdir}from"os";import{join as join24}from"path";init_wish_state();import{execSync as execSync5}from"child_process";import{existsSync as existsSync18}from"fs";import{readFile as readFile8}from"fs/promises";import{join as join23}from"path";function parseRef(ref){let hashIdx=ref.indexOf("#");if(hashIdx===-1)throw Error(`Invalid reference "${ref}". Expected format: <slug>#<group>`);let slug=ref.slice(0,hashIdx),group=ref.slice(hashIdx+1);if(!slug||!group)throw Error(`Invalid reference "${ref}". Both slug and group are required.`);return{slug,group}}var STATUS_ICONS={blocked:"\uD83D\uDD12",ready:"\uD83D\uDFE2",in_progress:"\uD83D\uDD04",done:"\u2705"};function formatTimestamp(iso){if(!iso)return"";return new Date(iso).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1})}function padRight2(str2,len){return str2.length>=len?str2:str2+" ".repeat(len-str2.length)}async function detectWaveCompletion(slug,groupName,cwd){let base=cwd??process.cwd(),wishPath=join23(base,".genie","wishes",slug,"WISH.md");if(!existsSync18(wishPath))return null;let content=await readFile8(wishPath,"utf-8"),targetWave=parseExecutionStrategy(content).find((w)=>w.groups.some((g)=>g.group===groupName));if(!targetWave)return null;let state2=await getState(slug,cwd);if(!state2)return null;let waveGroupNames=targetWave.groups.map((g)=>g.group);if(!waveGroupNames.every((g)=>state2.groups[g]?.status==="done"))return null;return{waveName:targetWave.name,waveGroups:waveGroupNames}}async function ensureWorkPushed(slug,group){try{if(execSync5("git status --porcelain",{encoding:"utf-8"}).trim())console.log(" Committing dirty working tree..."),execSync5("git add -A",{encoding:"utf-8"}),execSync5(`git commit -m "wip: ${slug}#${group}"`,{encoding:"utf-8"}),console.log(` Committed as "wip: ${slug}#${group}"`)}catch{}try{if(execSync5("git log @{u}..HEAD --oneline",{encoding:"utf-8"}).trim())console.log(" Pushing unpushed commits..."),execSync5("git push",{encoding:"utf-8",timeout:30000}),console.log(" Push complete.")}catch{try{let branch=execSync5("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim();if(branch&&branch!=="HEAD")execSync5(`git push -u origin ${branch}`,{encoding:"utf-8",timeout:30000}),console.log(" Push complete (set upstream).")}catch{console.log(" \u26A0\uFE0F Push failed \u2014 manual push may be needed.")}}}function autoKillPane(){let paneId=process.env.TMUX_PANE;if(paneId)setTimeout(()=>{try{execSync5(`tmux kill-pane -t '${paneId}'`,{encoding:"utf-8"})}catch{process.exit(0)}},1000);else process.exit(0)}async function doneCommand(ref){try{let{slug,group}=parseRef(ref),result=await completeGroup(slug,group);if(console.log(`\u2705 Group "${group}" marked as done in wish "${slug}"`),result.completedAt)console.log(` Completed at: ${formatTimestamp(result.completedAt)}`);let state2=await getState(slug);if(state2){let nowReady=Object.entries(state2.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 waveResult=await detectWaveCompletion(slug,group);if(waveResult){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(),message=`${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(", ")}]. Run /review or advance to next wave.`,result2=await protocolRouter.sendMessage(repoPath,"cli","team-lead",message);if(result2&&typeof result2==="object"&&"delivered"in result2&&!result2.delivered)console.warn(" \u26A0\uFE0F Wave-complete notification may not have been delivered.");else console.log(" Notified team-lead of wave completion.")}catch{console.warn(" \u26A0\uFE0F Could not notify team-lead (messaging unavailable).")}}autoKillPane()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function statusCommand(slug){try{let state2=await getState(slug);if(!state2)console.error(`\u274C No state found for wish "${slug}"`),console.error(" This means work has not been dispatched yet."),console.error(` Run: genie work ${slug}`),process.exit(1);console.log(`
|
|
973
973
|
Wish: ${state2.wish}`),console.log("\u2500".repeat(60));let entries=Object.entries(state2.groups),maxNameLen=Math.max(...entries.map(([name])=>name.length),5);console.log(` ${padRight2("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",status=padRight2(`${icon} ${group.status}`,13),assignee=padRight2(group.assignee??"-",13),started=padRight2(formatTimestamp(group.startedAt)||"-",14),completed=formatTimestamp(group.completedAt)||"-";console.log(` ${padRight2(name,maxNameLen)} ${status} ${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`),console.log("")}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}function registerStateCommands(program2){program2.command("done <ref>").description("Mark a wish group as done (format: <slug>#<group>)").action(async(ref)=>{await doneCommand(ref)}),program2.command("status <slug>").description("Show wish state overview for all groups").action(async(slug)=>{await statusCommand(slug)}),program2.command("reset <ref>").description("Reset an in-progress group back to ready (format: <slug>#<group>)").action(async(ref)=>{try{let{slug,group}=parseRef(ref),result=await resetGroup(slug,group);if(console.log(`\uD83D\uDD04 Group "${group}" reset to ready in wish "${slug}"`),result.status==="ready")console.log(" Status: ready (assignee cleared)")}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}})}async function writeContextFile(content){let dir=join24(tmpdir(),"genie-dispatch");await mkdir8(dir,{recursive:!0});let ts=Date.now().toString(36),rand=Math.random().toString(36).slice(2,8),filePath=join24(dir,`ctx-${ts}-${rand}.md`);return await writeFile7(filePath,content),filePath}function extractGroup(content,groupName){let pattern=new RegExp(`^### Group ${escapeRegExp(groupName)}:`,"m"),match=content.match(pattern);if(!match||match.index===void 0)return null;let start=match.index,nextBoundary=content.slice(start).slice(1).search(/^### Group \d|^---$/m),end=nextBoundary!==-1?start+1+nextBoundary:content.length;return content.slice(start,end).trim()}function extractWishContext(content){let execGroupsIdx=content.indexOf("## Execution Groups");if(execGroupsIdx!==-1)return content.slice(0,execGroupsIdx).trim();return content.slice(0,2000).trim()}function escapeRegExp(str2){return str2.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function buildContextPrompt(opts){let parts=[`# Dispatch Context (${opts.command})`,"",`**Source file:** \`${opts.filePath}\``,"(Read the full document at the path above for complete context)",""];if(opts.wishContext)parts.push("## Wish Context","",opts.wishContext,"");if(parts.push("## Assigned Section","",opts.sectionContent,""),opts.skill)parts.push("## Initial Command","",`Run \`/${opts.skill}\` to begin.`,"");return parts.join(`
|
|
974
974
|
`)}function getGitDiff(){try{let diff=execSync6("git diff HEAD",{encoding:"utf-8",maxBuffer:1048576}),staged=execSync6("git diff --cached",{encoding:"utf-8",maxBuffer:1048576}),combined=[diff,staged].filter(Boolean).join(`
|
|
975
975
|
`);if(combined.length>50000)return`${combined.slice(0,50000)}
|
|
@@ -987,12 +987,12 @@ ${diff}
|
|
|
987
987
|
|
|
988
988
|
When done, report your verdict:
|
|
989
989
|
Run: genie send '<SHIP|FIX-FIRST|BLOCKED> \u2014 <summary>' --to team-lead`)}function registerDispatchCommands(program2){program2.command("brainstorm <agent> <slug>").description("Spawn agent with brainstorm DRAFT.md context").action(async(agent,slug)=>{await brainstormCommand(agent,slug)}),program2.command("wish <agent> <slug>").description("Spawn agent with wish DESIGN.md context").action(async(agent,slug)=>{await wishCommand(agent,slug)}),program2.command("work <ref> [agent]").description("Auto-orchestrate a wish, or dispatch work on a specific group").action(async(ref,agent)=>{try{let work=detectWorkMode(ref,agent);if(work.mode==="auto")await autoOrchestrateCommand(work.slug);else await workDispatchCommand(work.agent,work.ref)}catch(error2){console.error(`\u274C ${error2 instanceof Error?error2.message:error2}`),process.exit(1)}}),program2.command("review <agent> <ref>").description("Spawn agent with review scope for a wish group (format: <slug>#<group>)").action(async(agent,ref)=>{await reviewCommand(agent,ref)})}init_agent_registry();function formatTime(timestamp2){try{return new Date(timestamp2).toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!1})}catch{return"??:??"}}function truncate(str2,maxLen){if(str2.length<=maxLen)return str2;return`${str2.slice(0,maxLen-3)}...`}function formatPath(path3){let shortened=path3.replace(/^\/home\/\w+\/workspace\//,"~/").replace(/^\/home\/\w+\//,"~/");return truncate(shortened,50)}function flushPendingReads(ctx){if(ctx.pendingReads.length===0)return;ctx.events.push({timestamp:ctx.lastReadTime,type:"read",summary:ctx.pendingReads.length===1?`Read: ${formatPath(ctx.pendingReads[0])}`:`Read ${ctx.pendingReads.length} files`,details:ctx.pendingReads.length>1?ctx.pendingReads.map(formatPath):void 0}),ctx.pendingReads.length=0}function flushPendingEdits(ctx){if(ctx.pendingEdits.length===0)return;ctx.events.push({timestamp:ctx.lastEditTime,type:"edit",summary:ctx.pendingEdits.length===1?`Edit: ${formatPath(ctx.pendingEdits[0])}`:`Edit ${ctx.pendingEdits.length} files`,details:ctx.pendingEdits.length>1?ctx.pendingEdits.map(formatPath):void 0}),ctx.pendingEdits.length=0}function flushAll(ctx){flushPendingReads(ctx),flushPendingEdits(ctx)}function processToolCallEntry(entry,ctx){if(!entry.toolCall)return;let{name,input}=entry.toolCall,inputRecord=input,normalizedName=name==="shell"||name==="exec_command"?"Bash":name;switch(normalizedName){case"Read":if(ctx.lastReadTime=entry.timestamp,inputRecord.file_path)ctx.pendingReads.push(String(inputRecord.file_path));break;case"Edit":if(flushPendingReads(ctx),ctx.lastEditTime=entry.timestamp,inputRecord.file_path)ctx.pendingEdits.push(String(inputRecord.file_path));break;case"Write":flushAll(ctx),ctx.events.push({timestamp:entry.timestamp,type:"write",summary:`Write: ${formatPath(String(inputRecord.file_path||"unknown"))}`});break;case"Bash":{flushAll(ctx);let cmd=String(inputRecord.command||"").replace(/\n/g," ");ctx.events.push({timestamp:entry.timestamp,type:"bash",summary:`Bash: ${truncate(cmd,60)}`});break}case"AskUserQuestion":{flushAll(ctx);let questions=inputRecord.questions;ctx.events.push({timestamp:entry.timestamp,type:"question",summary:`Question: ${truncate(questions?.[0]?.question||"question",60)}`});break}default:flushAll(ctx),ctx.events.push({timestamp:entry.timestamp,type:"bash",summary:`${normalizedName}: ${truncate(entry.text.replace(/\n/g," "),60)}`})}}function processTranscriptEntry(entry,ctx){if(entry.role==="user"){flushAll(ctx),ctx.events.push({timestamp:entry.timestamp,type:"prompt",summary:truncate(entry.text.replace(/\n/g," "),80)});return}if(entry.role==="tool_call"){processToolCallEntry(entry,ctx);return}if(entry.role==="assistant"&&entry.text.length>100)flushAll(ctx),ctx.events.push({timestamp:entry.timestamp,type:"response",summary:truncate(entry.text.replace(/\n/g," "),80)})}function extractEvents(entries){let ctx={events:[],pendingReads:[],pendingEdits:[],lastReadTime:"",lastEditTime:""};for(let entry of entries)processTranscriptEntry(entry,ctx);return flushAll(ctx),ctx.events}function detectStatus(entries){if(entries.length===0)return"unknown";let lastEntries=entries.slice(-10);for(let entry of lastEntries.reverse())if(entry.role==="tool_call"&&entry.toolCall?.name==="AskUserQuestion")return"question";let last=entries[entries.length-1];if(last.role==="user")return"working";if(last.role==="assistant")return"idle";return"unknown"}function formatEventsForDisplay(events,stats){let lines=[];lines.push(""),lines.push(`Session: ${stats.workerId} [${stats.provider}]${stats.branch?` (${stats.branch})`:""} | ${stats.duration} | ${stats.totalEntries} entries \u2192 ${stats.compressedLines} lines`),lines.push("");for(let event of events){let time=formatTime(event.timestamp),icon=getEventIcon(event.type);if(lines.push(`[${time}] ${icon} ${event.summary}`),event.details&&event.details.length>0){for(let detail of event.details.slice(0,5))lines.push(` ${detail}`);if(event.details.length>5)lines.push(` ... and ${event.details.length-5} more`)}if(event.result)lines.push(` \u2192 ${event.result}`)}return lines.push(""),lines.push(`Status: ${stats.status.toUpperCase()} | ${stats.exchanges} exchanges | ${stats.toolCalls} tool calls | ${stats.compressionRatio.toFixed(0)}x compression`),lines.push(""),lines.join(`
|
|
990
|
-
`)}function getEventIcon(type2){switch(type2){case"prompt":return"\uD83D\uDCAC";case"read":return"\uD83D\uDCD6";case"edit":return"\u270F\uFE0F";case"write":return"\uD83D\uDCDD";case"bash":return"\u26A1";case"question":return"\u2753";case"answer":return"\u2705";case"permission":return"\uD83D\uDD10";case"thinking":return"\uD83E\uDD14";case"response":return"\uD83D\uDCAD";default:return"\u2022"}}function formatTranscriptEntryForDisplay(entry){let time=formatTime(entry.timestamp);
|
|
991
|
-
[${time}] USER:`,entry.text];
|
|
992
|
-
[${time}] TOOL:`,
|
|
993
|
-
[${time}] ASSISTANT:`,
|
|
994
|
-
[${time}] RESULT:`,` ${text}`]
|
|
995
|
-
[${time}] SYSTEM:`,entry.text];return[]}function formatFullConversation(entries){return entries.flatMap(formatTranscriptEntryForDisplay).join(`
|
|
990
|
+
`)}function getEventIcon(type2){switch(type2){case"prompt":return"\uD83D\uDCAC";case"read":return"\uD83D\uDCD6";case"edit":return"\u270F\uFE0F";case"write":return"\uD83D\uDCDD";case"bash":return"\u26A1";case"question":return"\u2753";case"answer":return"\u2705";case"permission":return"\uD83D\uDD10";case"thinking":return"\uD83E\uDD14";case"response":return"\uD83D\uDCAD";default:return"\u2022"}}function formatToolDetail(toolCall){let input=toolCall.input;if(toolCall.name==="Bash"||toolCall.name==="shell")return` ${toolCall.name}: ${input.command??""}`;if(["Read","Edit","Write"].includes(toolCall.name))return` ${toolCall.name}: ${input.file_path??""}`;return` ${toolCall.name}`}function formatTranscriptEntryForDisplay(entry){let time=formatTime(entry.timestamp);switch(entry.role){case"user":return[`
|
|
991
|
+
[${time}] USER:`,entry.text];case"tool_call":return entry.toolCall?[`
|
|
992
|
+
[${time}] TOOL:`,formatToolDetail(entry.toolCall)]:[];case"assistant":return[`
|
|
993
|
+
[${time}] ASSISTANT:`,truncate(entry.text,500)];case"tool_result":return[`
|
|
994
|
+
[${time}] RESULT:`,` ${truncate(entry.text,500)}`];case"system":return[`
|
|
995
|
+
[${time}] SYSTEM:`,entry.text];default:return[]}}function formatFullConversation(entries){return entries.flatMap(formatTranscriptEntryForDisplay).join(`
|
|
996
996
|
`)}async function findWorker(identifier){let worker=await get(identifier);if(worker)return worker;if(worker=await findByTask(identifier),worker)return worker;return(await list()).find((w)=>w.id.includes(identifier)||w.taskId?.includes(identifier)||w.taskTitle?.toLowerCase().includes(identifier.toLowerCase()))??null}async function resolveContext(workerIdOrName,options){if(options.logFile)return{worker:{id:"direct",paneId:"",session:"",worktree:null,startedAt:new Date().toISOString(),state:"idle",lastStateChange:new Date().toISOString(),repoPath:process.cwd(),provider:"claude"},workerId:"direct",provider:"claude",duration:"N/A"};let worker=await findWorker(workerIdOrName);if(!worker)console.error(`Agent "${workerIdOrName}" not found. Run \`genie ls\` to see agents.`),process.exit(1);let elapsed=getElapsedTime(worker);return{worker,workerId:worker.id,provider:worker.provider??"claude",branch:worker.worktree?`work/${worker.taskId}`:void 0,duration:elapsed.formatted}}function buildFilter(options){let filter={},hasFilter=!1;if(options.last&&options.last>0)filter.last=options.last,hasFilter=!0;if(options.after)filter.since=options.after,hasFilter=!0;if(options.type)filter.roles=[options.type],hasFilter=!0;return hasFilter?filter:void 0}function filterSinceExchanges(entries,since){let userCount=0;for(let i2=entries.length-1;i2>=0;i2--)if(entries[i2].role==="user"){if(userCount++,userCount>=since)return entries.slice(i2)}return entries}async function loadEntries(ctx,options){let{readTranscript:readTranscript2,getProvider:getProvider2}=await Promise.resolve().then(() => exports_transcript);if(options.logFile)return(await getProvider2(ctx.worker)).readEntries(options.logFile);return readTranscript2(ctx.worker)}function filterEntries(entries,options){let{applyFilter:applyFilter2}=__toCommonJS(exports_transcript),filtered=options.since&&options.since>0?filterSinceExchanges(entries,options.since):entries,transcriptFilter=buildFilter(options);if(transcriptFilter)filtered=applyFilter2(filtered,transcriptFilter);return filtered}function outputEntries(filtered,options){if(options.ndjson){for(let entry of filtered){let{raw:_raw,...rest}=entry;console.log(JSON.stringify(options.raw?entry:rest))}return!0}if(options.raw){for(let entry of filtered)console.log(JSON.stringify(entry.raw));return!0}if(options.full)return console.log(formatFullConversation(filtered)),!0;return!1}async function historyCommand(workerIdOrName,options){let ctx=await resolveContext(workerIdOrName,options),entries=await loadEntries(ctx,options);if(entries.length===0)console.error(`No transcript found for agent "${ctx.workerId}".`),process.exit(1);let filtered=filterEntries(entries,options);if(outputEntries(filtered,options))return;let events=extractEvents(filtered),toolCallCount=entries.filter((e)=>e.role==="tool_call").length,userMessageCount=entries.filter((e)=>e.role==="user").length,stats={workerId:ctx.workerId,taskId:ctx.workerId,branch:ctx.branch,provider:ctx.provider,duration:ctx.duration,totalEntries:entries.length,compressedLines:events.length,compressionRatio:entries.length/Math.max(events.length,1),exchanges:userMessageCount,toolCalls:toolCallCount,status:detectStatus(entries)};if(options.json){console.log(JSON.stringify({stats,events},null,2));return}console.log(formatEventsForDisplay(events,stats))}init_agent_registry();init_mailbox();import{appendFile as appendFile2,mkdir as mkdir9,readFile as readFile12}from"fs/promises";import{join as join27}from"path";init_file_lock();function chatDir(repoPath){return join27(repoPath,".genie","chat")}function chatFilePath(repoPath,teamName){let safeName=teamName.replace(/\//g,"--");return join27(chatDir(repoPath),`${safeName}.jsonl`)}async function readMessages(repoPath,teamName,since){let filePath=chatFilePath(repoPath,teamName),content;try{content=await readFile12(filePath,"utf-8")}catch{return[]}let lines=content.trim().split(`
|
|
997
997
|
`).filter(Boolean),messages2=[];for(let line of lines)try{messages2.push(JSON.parse(line))}catch{}if(since){let sinceTime=new Date(since).getTime();messages2=messages2.filter((m)=>new Date(m.timestamp).getTime()>=sinceTime)}return messages2}function isSystemNoise(text){let trimmed=text.trimStart();return trimmed.startsWith("<command-name>")||trimmed.startsWith("<command-message>")||trimmed.startsWith("Base directory for this skill:")||trimmed.startsWith("<system-reminder>")||trimmed.startsWith("<local-command")}function transcriptToLogEvent(entry,agent,team){let kindMap={user:"user",assistant:"assistant",system:"system",tool_call:"tool_call",tool_result:"tool_result"},text=entry.text.trim();if(isSystemNoise(text))return null;if(!text)return null;return{timestamp:entry.timestamp,kind:kindMap[entry.role]??"assistant",agent,team,text,data:{role:entry.role,...entry.toolCall?{toolCall:entry.toolCall}:{},...entry.model?{model:entry.model}:{},...entry.usage?{usage:entry.usage}:{}},source:"provider"}}function inboxMessageToLogEvent(msg,agent,team){return{timestamp:msg.createdAt,kind:"message",agent,team,direction:"in",peer:msg.from,text:msg.body,data:{messageId:msg.id,from:msg.from,to:msg.to,read:msg.read},source:"mailbox"}}function outboxMessageToLogEvent(msg,agent,team){return{timestamp:msg.createdAt,kind:"message",agent,team,direction:"out",peer:msg.to,text:msg.body,data:{messageId:msg.id,from:msg.from,to:msg.to},source:"mailbox"}}function chatMessageToLogEvent(msg,team){return{timestamp:msg.timestamp,kind:"message",agent:msg.sender,team,text:msg.body,data:{chatId:msg.id,sender:msg.sender},source:"chat"}}function applyLogFilter(events,filter){if(!filter)return events;let result=events;if(filter.since){let sinceMs=new Date(filter.since).getTime();result=result.filter((e)=>new Date(e.timestamp).getTime()>=sinceMs)}if(filter.kinds&&filter.kinds.length>0){let kinds=new Set(filter.kinds);result=result.filter((e)=>kinds.has(e.kind))}if(filter.last&&filter.last>0)result=result.slice(-filter.last);return result}function sortByTimestamp(events){return events.sort((a,b2)=>new Date(a.timestamp).getTime()-new Date(b2.timestamp).getTime())}async function readAgentLog(agent,repoPath,filter){let{id:agentName,team}=agent,[transcriptEntries,inboxMessages,outboxMessages,chatMessages]=await Promise.all([readTranscriptSafe(agent),inbox(repoPath,agentName),readOutbox(repoPath,agentName),team?readMessages(repoPath,team):Promise.resolve([])]),events=[];for(let entry of transcriptEntries){let event=transcriptToLogEvent(entry,agentName,team);if(event)events.push(event)}for(let msg of inboxMessages)events.push(inboxMessageToLogEvent(msg,agentName,team));for(let msg of outboxMessages)events.push(outboxMessageToLogEvent(msg,agentName,team));if(team)for(let msg of chatMessages)events.push(chatMessageToLogEvent(msg,team));let sorted=sortByTimestamp(events);return applyLogFilter(sorted,filter)}async function readTeamLog(agents,repoPath,teamName,filter){let chatEvents=(await readMessages(repoPath,teamName)).map((msg)=>chatMessageToLogEvent(msg,teamName)),perAgentEvents=await Promise.all(agents.map(async(agent)=>{let agentName=agent.id,[transcriptEntries,inboxMessages,outboxMessages]=await Promise.all([readTranscriptSafe(agent),inbox(repoPath,agentName),readOutbox(repoPath,agentName)]),events=[];for(let entry of transcriptEntries){let event=transcriptToLogEvent(entry,agentName,teamName);if(event)events.push(event)}for(let msg of inboxMessages)events.push(inboxMessageToLogEvent(msg,agentName,teamName));for(let msg of outboxMessages)events.push(outboxMessageToLogEvent(msg,agentName,teamName));return events})),allEvents=[...chatEvents,...perAgentEvents.flat()],sorted=sortByTimestamp(allEvents);return applyLogFilter(sorted,filter)}async function followAgentLog(agent,repoPath,filter,onEvent){return startNatsFollow([agent],repoPath,agent.team,filter,onEvent)}async function followTeamLog(agents,repoPath,teamName,filter,onEvent){return startNatsFollow(agents,repoPath,teamName,filter,onEvent)}async function startNatsFollow(_agents,_repoPath,_team,filter,onEvent){let nats=await Promise.resolve().then(() => (init_nats_client(),exports_nats_client));if(!await nats.isAvailable())throw Error("NATS is not available. Install nats package and ensure NATS server is running.");let kindsFilter=filter?.kinds?new Set(filter.kinds):null,subs=[],seenKeys=new Set,eventKey=(e)=>`${e.timestamp}|${e.kind}|${e.agent}|${e.text.slice(0,80)}`,handleNatsEvent=(_subject,data)=>{let event=data;if(!event?.timestamp||!event?.kind)return;if(kindsFilter&&!kindsFilter.has(event.kind))return;let key=eventKey(event);if(seenKeys.has(key))return;seenKeys.add(key),onEvent(event)},allSub=await nats.subscribe("genie.>",handleNatsEvent);return subs.push(allSub),{mode:"nats",stop:async()=>{for(let sub of subs)sub.unsubscribe()}}}async function readTranscriptSafe(agent){try{let{readTranscript:readTranscript2}=await Promise.resolve().then(() => exports_transcript);return await readTranscript2(agent)}catch{return[]}}function formatTime2(timestamp2){try{return new Date(timestamp2).toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1})}catch{return"??:??:??"}}function kindIcon(kind){switch(kind){case"user":return"U";case"assistant":return"A";case"message":return"M";case"state":return"S";case"tool_call":return"C";case"tool_result":return"R";case"system":return"*";default:return"?"}}function kindColor(kind){switch(kind){case"user":return"\x1B[33m";case"assistant":return"\x1B[36m";case"message":return"\x1B[35m";case"state":return"\x1B[35m";case"tool_call":return"\x1B[32m";case"tool_result":return"\x1B[90m";case"system":return"\x1B[34m";default:return"\x1B[0m"}}var RESET="\x1B[0m",DIM="\x1B[90m",BOLD="\x1B[1m";function summarizeToolCall2(event){let tc=event.data?.toolCall;if(!tc)return event.text;let input=tc.input;switch(tc.name){case"Read":case"Edit":case"Write":return`${tc.name} ${input.file_path??""}`;case"Bash":return`$ ${String(input.command??"").split(`
|
|
998
998
|
`)[0]}`;case"Grep":return`Grep "${input.pattern}" ${input.path??""}`;case"Glob":return`Glob ${input.pattern}`;case"Agent":return`Agent: ${input.description??""}`;case"SendMessage":return`SendMessage \u2192 ${input.to}: ${String(input.message??"").slice(0,80)}`;case"shell":case"exec_command":return`$ ${(Array.isArray(input.command)?input.command.join(" "):String(input.command??"")).split(`
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.260323.
|
|
3
|
+
"version": "4.260323.5",
|
|
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"
|
|
@@ -68,4 +68,5 @@ Never rely on messages alone to determine completion. Always check `genie status
|
|
|
68
68
|
- NEVER push to main or master.
|
|
69
69
|
- NEVER use the Agent tool.
|
|
70
70
|
- NEVER run `genie status`, `genie ls`, or `genie inbox` before Phase 2.
|
|
71
|
+
- If you ran `genie status` and got "No state found", this means work has NOT been dispatched. Go to Phase 2 immediately and run `genie work <slug>`. Do NOT poll or wait.
|
|
71
72
|
</constraints>
|
|
@@ -16,6 +16,9 @@ genie status <slug>
|
|
|
16
16
|
```
|
|
17
17
|
Which groups are done? Which are in progress? Which are blocked?
|
|
18
18
|
|
|
19
|
+
If `genie status` returns "No state found", work was never dispatched.
|
|
20
|
+
Run `genie work <slug>` to initialize and dispatch — do NOT poll.
|
|
21
|
+
|
|
19
22
|
### 3. Check Workers
|
|
20
23
|
```bash
|
|
21
24
|
genie ls
|
|
@@ -310,41 +310,34 @@ function getEventIcon(type: CompressedEvent['type']): string {
|
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
function
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return [`\n[${time}] USER:`, entry.text];
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (entry.role === 'tool_call' && entry.toolCall) {
|
|
321
|
-
const input = entry.toolCall.input as Record<string, unknown>;
|
|
322
|
-
let detail: string;
|
|
323
|
-
if (entry.toolCall.name === 'Bash' || entry.toolCall.name === 'shell') {
|
|
324
|
-
detail = ` ${entry.toolCall.name}: ${input.command ?? ''}`;
|
|
325
|
-
} else if (['Read', 'Edit', 'Write'].includes(entry.toolCall.name)) {
|
|
326
|
-
detail = ` ${entry.toolCall.name}: ${input.file_path ?? ''}`;
|
|
327
|
-
} else {
|
|
328
|
-
detail = ` ${entry.toolCall.name}`;
|
|
329
|
-
}
|
|
330
|
-
return [`\n[${time}] TOOL:`, detail];
|
|
313
|
+
function formatToolDetail(toolCall: NonNullable<TranscriptEntry['toolCall']>): string {
|
|
314
|
+
const input = toolCall.input as Record<string, unknown>;
|
|
315
|
+
if (toolCall.name === 'Bash' || toolCall.name === 'shell') {
|
|
316
|
+
return ` ${toolCall.name}: ${input.command ?? ''}`;
|
|
331
317
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const text = entry.text.slice(0, 500) + (entry.text.length > 500 ? '...' : '');
|
|
335
|
-
return [`\n[${time}] ASSISTANT:`, text];
|
|
318
|
+
if (['Read', 'Edit', 'Write'].includes(toolCall.name)) {
|
|
319
|
+
return ` ${toolCall.name}: ${input.file_path ?? ''}`;
|
|
336
320
|
}
|
|
321
|
+
return ` ${toolCall.name}`;
|
|
322
|
+
}
|
|
337
323
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
return [`\n[${time}] RESULT:`, ` ${text}`];
|
|
341
|
-
}
|
|
324
|
+
function formatTranscriptEntryForDisplay(entry: TranscriptEntry): string[] {
|
|
325
|
+
const time = formatTime(entry.timestamp);
|
|
342
326
|
|
|
343
|
-
|
|
344
|
-
|
|
327
|
+
switch (entry.role) {
|
|
328
|
+
case 'user':
|
|
329
|
+
return [`\n[${time}] USER:`, entry.text];
|
|
330
|
+
case 'tool_call':
|
|
331
|
+
return entry.toolCall ? [`\n[${time}] TOOL:`, formatToolDetail(entry.toolCall)] : [];
|
|
332
|
+
case 'assistant':
|
|
333
|
+
return [`\n[${time}] ASSISTANT:`, truncate(entry.text, 500)];
|
|
334
|
+
case 'tool_result':
|
|
335
|
+
return [`\n[${time}] RESULT:`, ` ${truncate(entry.text, 500)}`];
|
|
336
|
+
case 'system':
|
|
337
|
+
return [`\n[${time}] SYSTEM:`, entry.text];
|
|
338
|
+
default:
|
|
339
|
+
return [];
|
|
345
340
|
}
|
|
346
|
-
|
|
347
|
-
return [];
|
|
348
341
|
}
|
|
349
342
|
|
|
350
343
|
function formatFullConversation(entries: TranscriptEntry[]): string {
|
|
@@ -238,7 +238,8 @@ export async function statusCommand(slug: string): Promise<void> {
|
|
|
238
238
|
const state = await wishState.getState(slug);
|
|
239
239
|
if (!state) {
|
|
240
240
|
console.error(`❌ No state found for wish "${slug}"`);
|
|
241
|
-
console.error('
|
|
241
|
+
console.error(' This means work has not been dispatched yet.');
|
|
242
|
+
console.error(` Run: genie work ${slug}`);
|
|
242
243
|
process.exit(1);
|
|
243
244
|
}
|
|
244
245
|
|