@automagik/genie 4.260329.21 → 4.260329.22

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.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "genie",
13
- "version": "4.260329.21",
13
+ "version": "4.260329.22",
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
@@ -1994,8 +1994,9 @@ Next steps:`),console.log(" 1. Reload tmux: tmux source ~/.tmux.conf"),console.
1994
1994
  `+` genie task status <slug> \u2014 check progress
1995
1995
  `+" genie agent send --to <a> \u2014 communicate directly"},{test:/sleep\s+\d+\s*&&\s*.*(?:capture-pane|tmux\s+list)/,message:`Consider using genie primitives instead of terminal polling:
1996
1996
  genie task status <slug>
1997
- genie events list --since 5m`}];async function orchestrationGuard(payload){let command=payload.tool_input?.command;if(typeof command!=="string")return;for(let{test,message}of NUDGE_PATTERNS)if(test.test(command)){console.error(`[orchestration-guard] ${message}`);return}return}var getAgent2=()=>process.env.GENIE_AGENT_NAME??"unknown",getTeam=()=>process.env.GENIE_TEAM;async function emit(subject,event){try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(process.cwd(),subject,event)}catch{}}async function emitToolCallEvent(payload){let{tool_name:toolName,tool_input:input}=payload;if(!toolName||!input)return;await emit(`genie.tool.${getAgent2()}.call`,{timestamp:new Date().toISOString(),kind:"tool_call",agent:getAgent2(),team:getTeam(),text:summarizeToolCall(toolName,input),data:{toolCall:{name:toolName,input}},source:"hook"});return}async function emitMessageEvent(payload){let input=payload.tool_input;if(!input)return;let msgType=input.type;if(msgType&&msgType!=="message"&&msgType!=="broadcast")return;let to=input.to,content=input.content??input.message;if(!to||!content)return;let subject=msgType==="broadcast"?"genie.msg.broadcast":`genie.msg.${to}`;await emit(subject,{timestamp:new Date().toISOString(),kind:"message",agent:getAgent2(),team:getTeam(),peer:to,direction:"out",text:content,source:"hook"});return}async function emitUserPromptEvent(payload){let prompt2=payload.prompt;if(!prompt2)return;await emit(`genie.user.${getAgent2()}.prompt`,{timestamp:new Date().toISOString(),kind:"user",agent:getAgent2(),team:getTeam(),text:prompt2,source:"hook"});return}async function emitAssistantResponseEvent(payload){let lastMessage=payload.last_assistant_message;if(!lastMessage)return;await emit(`genie.agent.${getAgent2()}.response`,{timestamp:new Date().toISOString(),kind:"assistant",agent:getAgent2(),team:getTeam(),text:lastMessage,source:"hook"});return}function summarizeToolCall(name,input){switch(name){case"Read":case"Edit":case"Write":return`${name} ${input.file_path??""}`;case"Bash":return`$ ${String(input.command??"").split(`
1998
- `)[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)}`;default:return name}}init_types3();var handlers=[{name:"branch-guard",event:"PreToolUse",matcher:/^Bash$/,priority:1,fn:branchGuard},{name:"orchestration-guard",event:"PreToolUse",matcher:/^Bash$/,priority:2,fn:orchestrationGuard},{name:"identity-inject",event:"PreToolUse",matcher:/^SendMessage$/,priority:10,fn:identityInject},{name:"auto-spawn",event:"PreToolUse",matcher:/^SendMessage$/,priority:20,fn:autoSpawn},{name:"runtime-emit-tool",event:"PreToolUse",matcher:/.*/,priority:30,fn:emitToolCallEvent},{name:"runtime-emit-msg",event:"PostToolUse",matcher:/^SendMessage$/,priority:30,fn:emitMessageEvent},{name:"runtime-emit-user-prompt",event:"UserPromptSubmit",priority:30,fn:emitUserPromptEvent},{name:"runtime-emit-assistant-response",event:"Stop",priority:30,fn:emitAssistantResponseEvent}];function resolveHandlers(event,toolName){return handlers.filter((h)=>{if(h.event!==event)return!1;if(h.matcher&&toolName&&!h.matcher.test(toolName))return!1;if(h.matcher&&!toolName)return!1;return!0}).sort((a,b2)=>a.priority-b2.priority)}async function runHandler(handler,payload,currentInput){let handlerPayload={...payload};if(currentInput)handlerPayload.tool_input=currentInput;try{return await handler.fn(handlerPayload)}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(`[genie-hook] Handler "${handler.name}" threw: ${msg}`);return}}async function executeBlockingChain(matched,payload){let currentInput=payload.tool_input?{...payload.tool_input}:void 0;for(let handler of matched){let result=await runHandler(handler,payload,currentInput);if(!result)continue;if(result.decision==="deny")return{decision:"deny",reason:result.reason??`Denied by handler: ${handler.name}`};if(result.updatedInput)currentInput={...currentInput,...result.updatedInput}}if(currentInput&&payload.tool_input&&JSON.stringify(currentInput)!==JSON.stringify(payload.tool_input))return{updatedInput:currentInput};return{}}async function executeNonBlockingHandlers(matched,payload){await Promise.allSettled(matched.map((h)=>h.fn(payload).catch((err)=>{let msg=err instanceof Error?err.message:String(err);console.error(`[genie-hook] Handler "${h.name}" threw: ${msg}`)})))}async function dispatch(stdin){let payload;try{payload=JSON.parse(stdin)}catch{return console.error("[genie-hook] Invalid JSON on stdin"),""}let event=payload.hook_event_name;if(!event)return console.error("[genie-hook] Missing hook_event_name in payload"),"";let toolName=payload.tool_name,matched=resolveHandlers(event,toolName);if(matched.length===0)return"";if(isBlockingEvent(event)){let result=await executeBlockingChain(matched,payload);if(result.decision||result.updatedInput)return JSON.stringify(result);return""}return await executeNonBlockingHandlers(matched,payload),""}async function readStdin(){let chunks=[];for await(let chunk of Bun.stdin.stream())chunks.push(Buffer.from(chunk));return Buffer.concat(chunks).toString("utf-8")}async function dispatchAction(){let stdin=await readStdin();if(!stdin.trim())process.exit(0);let result=await dispatch(stdin);if(result)process.stdout.write(result)}function registerHookNamespace(program2){program2.command("hook").description("Hook middleware for Claude Code integration").command("dispatch").description("Dispatch a CC hook event (reads JSON from stdin, writes decision to stdout)").action(dispatchAction)}init_audit();init_db();init_otel_receiver();init_target_resolver();init_tmux();init_orchestrator();async function resolveOrcTarget(target){let resolved=await resolveTarget(target);return{paneId:resolved.paneId,session:resolved.session||target,label:formatResolvedLabel(resolved,target)}}async function sendTextChoice(paneId,text){await executeTmux2(`send-keys -t '${paneId}' End`),await sleep(100),await executeTmux2(`send-keys -t '${paneId}' Enter`),await sleep(100),await executeTmux2(`send-keys -t '${paneId}' ${shellEscape(text)}`),await sleep(100),await executeTmux2(`send-keys -t '${paneId}' Enter`)}function findCurrentOption(output){let lines=stripAnsi(output).split(`
1997
+ genie events list --since 5m`}];async function orchestrationGuard(payload){let command=payload.tool_input?.command;if(typeof command!=="string")return;for(let{test,message}of NUDGE_PATTERNS)if(test.test(command))return{systemMessage:`[orchestration-guard] ${message}`};return}var getAgent2=()=>process.env.GENIE_AGENT_NAME??"unknown",getTeam=()=>process.env.GENIE_TEAM;async function emit(subject,event){try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(process.cwd(),subject,event)}catch{}}async function emitToolCallEvent(payload){let{tool_name:toolName,tool_input:input}=payload;if(!toolName||!input)return;await emit(`genie.tool.${getAgent2()}.call`,{timestamp:new Date().toISOString(),kind:"tool_call",agent:getAgent2(),team:getTeam(),text:summarizeToolCall(toolName,input),data:{toolCall:{name:toolName,input}},source:"hook"});return}async function emitMessageEvent(payload){let input=payload.tool_input;if(!input)return;let msgType=input.type;if(msgType&&msgType!=="message"&&msgType!=="broadcast")return;let to=input.to,content=input.content??input.message;if(!to||!content)return;let subject=msgType==="broadcast"?"genie.msg.broadcast":`genie.msg.${to}`;await emit(subject,{timestamp:new Date().toISOString(),kind:"message",agent:getAgent2(),team:getTeam(),peer:to,direction:"out",text:content,source:"hook"});return}async function emitUserPromptEvent(payload){let prompt2=payload.prompt;if(!prompt2)return;await emit(`genie.user.${getAgent2()}.prompt`,{timestamp:new Date().toISOString(),kind:"user",agent:getAgent2(),team:getTeam(),text:prompt2,source:"hook"});return}async function emitAssistantResponseEvent(payload){let lastMessage=payload.last_assistant_message;if(!lastMessage)return;await emit(`genie.agent.${getAgent2()}.response`,{timestamp:new Date().toISOString(),kind:"assistant",agent:getAgent2(),team:getTeam(),text:lastMessage,source:"hook"});return}function summarizeToolCall(name,input){switch(name){case"Read":case"Edit":case"Write":return`${name} ${input.file_path??""}`;case"Bash":return`$ ${String(input.command??"").split(`
1998
+ `)[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)}`;default:return name}}init_types3();var handlers=[{name:"branch-guard",event:"PreToolUse",matcher:/^Bash$/,priority:1,fn:branchGuard},{name:"orchestration-guard",event:"PreToolUse",matcher:/^Bash$/,priority:2,fn:orchestrationGuard},{name:"identity-inject",event:"PreToolUse",matcher:/^SendMessage$/,priority:10,fn:identityInject},{name:"auto-spawn",event:"PreToolUse",matcher:/^SendMessage$/,priority:20,fn:autoSpawn},{name:"runtime-emit-tool",event:"PreToolUse",matcher:/.*/,priority:30,fn:emitToolCallEvent},{name:"runtime-emit-msg",event:"PostToolUse",matcher:/^SendMessage$/,priority:30,fn:emitMessageEvent},{name:"runtime-emit-user-prompt",event:"UserPromptSubmit",priority:30,fn:emitUserPromptEvent},{name:"runtime-emit-assistant-response",event:"Stop",priority:30,fn:emitAssistantResponseEvent}];function resolveHandlers(event,toolName){return handlers.filter((h)=>{if(h.event!==event)return!1;if(h.matcher&&toolName&&!h.matcher.test(toolName))return!1;if(h.matcher&&!toolName)return!1;return!0}).sort((a,b2)=>a.priority-b2.priority)}async function runHandler(handler,payload,currentInput){let handlerPayload={...payload};if(currentInput)handlerPayload.tool_input=currentInput;try{return await handler.fn(handlerPayload)}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(`[genie-hook] Handler "${handler.name}" threw: ${msg}`);return}}async function executeBlockingChain(matched,payload){let currentInput=payload.tool_input?{...payload.tool_input}:void 0,messages2=[];for(let handler of matched){let result=await runHandler(handler,payload,currentInput);if(!result)continue;if(result.decision==="deny")return{decision:"deny",reason:result.reason??`Denied by handler: ${handler.name}`};if(result.systemMessage)messages2.push(result.systemMessage);if(result.updatedInput)currentInput={...currentInput,...result.updatedInput}}let response={};if(currentInput&&payload.tool_input&&JSON.stringify(currentInput)!==JSON.stringify(payload.tool_input))response.updatedInput=currentInput;if(messages2.length>0)response.systemMessage=messages2.join(`
1999
+ `);return response}async function executeNonBlockingHandlers(matched,payload){await Promise.allSettled(matched.map((h)=>h.fn(payload).catch((err)=>{let msg=err instanceof Error?err.message:String(err);console.error(`[genie-hook] Handler "${h.name}" threw: ${msg}`)})))}async function dispatch(stdin){let payload;try{payload=JSON.parse(stdin)}catch{return console.error("[genie-hook] Invalid JSON on stdin"),""}let event=payload.hook_event_name;if(!event)return console.error("[genie-hook] Missing hook_event_name in payload"),"";let toolName=payload.tool_name,matched=resolveHandlers(event,toolName);if(matched.length===0)return"";if(isBlockingEvent(event)){let result=await executeBlockingChain(matched,payload);if(result.decision||result.updatedInput||result.systemMessage)return JSON.stringify(result);return""}return await executeNonBlockingHandlers(matched,payload),""}async function readStdin(){let chunks=[];for await(let chunk of Bun.stdin.stream())chunks.push(Buffer.from(chunk));return Buffer.concat(chunks).toString("utf-8")}async function dispatchAction(){let stdin=await readStdin();if(!stdin.trim())process.exit(0);let result=await dispatch(stdin);if(result)process.stdout.write(result)}function registerHookNamespace(program2){program2.command("hook").description("Hook middleware for Claude Code integration").command("dispatch").description("Dispatch a CC hook event (reads JSON from stdin, writes decision to stdout)").action(dispatchAction)}init_audit();init_db();init_otel_receiver();init_target_resolver();init_tmux();init_orchestrator();async function resolveOrcTarget(target){let resolved=await resolveTarget(target);return{paneId:resolved.paneId,session:resolved.session||target,label:formatResolvedLabel(resolved,target)}}async function sendTextChoice(paneId,text){await executeTmux2(`send-keys -t '${paneId}' End`),await sleep(100),await executeTmux2(`send-keys -t '${paneId}' Enter`),await sleep(100),await executeTmux2(`send-keys -t '${paneId}' ${shellEscape(text)}`),await sleep(100),await executeTmux2(`send-keys -t '${paneId}' Enter`)}function findCurrentOption(output){let lines=stripAnsi(output).split(`
1999
2000
  `);for(let line of lines){let match=line.match(/^\s*\u276F\s*(\d+)\./);if(match)return Number.parseInt(match[1],10)}return 1}async function navigateToOption(paneId,targetOption,currentOption){let diff=targetOption-currentOption,key=diff>0?"Down":"Up";for(let i2=0;i2<Math.abs(diff);i2++)await executeTmux2(`send-keys -t '${paneId}' ${key}`),await sleep(50);await sleep(100),await executeTmux2(`send-keys -t '${paneId}' Enter`)}async function answerQuestion(target,choice){try{let{paneId,label}=await resolveOrcTarget(target),output=await capturePaneContent(paneId,50),state=detectState(output);if(state.type!=="question"){console.log(`No question pending (state: ${state.type})`);return}if(choice.startsWith("text:")){let text=choice.slice(5);await sendTextChoice(paneId,text),console.log(`Sent feedback: "${text.substring(0,50)}${text.length>50?"...":""}"`)}else if(/^\d+$/.test(choice)){let targetOption=Number.parseInt(choice,10);await navigateToOption(paneId,targetOption,findCurrentOption(output)),console.log(`Selected option ${targetOption} for ${label}`)}else await executeTmux2(`send-keys -t '${paneId}' '${choice}'`),console.log(`Sent '${choice}' to ${label}`)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}function shellEscape(str2){return`"${str2.replace(/"/g,"\\\"").replace(/\$/g,"\\$")}"`}function sleep(ms){return new Promise((resolve4)=>setTimeout(resolve4,ms))}function registerAgentAnswer(parent){parent.command("answer <name> <choice>").description('Answer a question for an agent (use "text:..." for text input)').action(async(name,choice)=>{try{await answerQuestion(name,choice)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}var _brief;async function getBrief(){if(!_brief)_brief=await Promise.resolve().then(() => (init_brief(),exports_brief));return _brief}async function handleBrief(options){let team=options.team??process.env.GENIE_TEAM;if(!team)console.error("Error: --team is required (or set GENIE_TEAM)"),process.exit(1);let agent=options.agent??process.env.GENIE_AGENT_NAME,briefService=await getBrief(),brief=await briefService.generateBrief({team,agent,since:options.since,repoPath:process.cwd()});console.log(briefService.formatBrief(brief))}function registerAgentBrief(parent){parent.command("brief").description("Show startup brief \u2014 aggregated context since last session").option("--team <name>","Team name (default: GENIE_TEAM)").option("--agent <name>","Agent name (default: GENIE_AGENT_NAME)").option("--since <iso>","Start timestamp (default: last executor end)").action(async(options)=>{try{await handleBrief(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_audit();init_db();import{existsSync as existsSync18,readFileSync as readFileSync9,renameSync as renameSync2,writeFileSync as writeFileSync6}from"fs";import{homedir as homedir15}from"os";import{join as join19}from"path";var GENIE_HOME3=process.env.GENIE_HOME??join19(homedir15(),".genie"),CACHE_FILE="agent-directory.json",CACHE_BACKUP="agent-directory.json.bak";async function regenerateAgentCache(){try{if(!await isAvailable())return;let entries=(await(await getConnection())`
2000
2001
  SELECT name, install_path, manifest, installed_at
2001
2002
  FROM app_store
@@ -2,7 +2,7 @@
2
2
  "id": "genie",
3
3
  "name": "Genie",
4
4
  "description": "Skills, agents, and hooks for the Genie CLI terminal orchestration toolkit",
5
- "version": "4.260329.21",
5
+ "version": "4.260329.22",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260329.21",
3
+ "version": "4.260329.22",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260329.21",
3
+ "version": "4.260329.22",
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.260329.21",
3
+ "version": "4.260329.22",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -47,10 +47,9 @@ export async function orchestrationGuard(payload: HookPayload): Promise<HandlerR
47
47
 
48
48
  for (const { test, message } of NUDGE_PATTERNS) {
49
49
  if (test.test(command)) {
50
- // Informational onlylog to stderr so the agent sees the suggestion,
51
- // but return undefined to allow the command to proceed.
52
- console.error(`[orchestration-guard] ${message}`);
53
- return undefined;
50
+ // Informational nudgesystemMessage is shown to the agent
51
+ // but does not block the command from executing.
52
+ return { systemMessage: `[orchestration-guard] ${message}` };
54
53
  }
55
54
  }
56
55
 
@@ -123,6 +123,7 @@ async function runHandler(
123
123
 
124
124
  async function executeBlockingChain(matched: Handler[], payload: HookPayload): Promise<HookDecision> {
125
125
  let currentInput = payload.tool_input ? { ...payload.tool_input } : undefined;
126
+ const messages: string[] = [];
126
127
 
127
128
  for (const handler of matched) {
128
129
  const result = await runHandler(handler, payload, currentInput);
@@ -131,16 +132,23 @@ async function executeBlockingChain(matched: Handler[], payload: HookPayload): P
131
132
  if (result.decision === 'deny') {
132
133
  return { decision: 'deny', reason: result.reason ?? `Denied by handler: ${handler.name}` };
133
134
  }
135
+ if (result.systemMessage) {
136
+ messages.push(result.systemMessage);
137
+ }
134
138
  if (result.updatedInput) {
135
139
  currentInput = { ...currentInput, ...result.updatedInput };
136
140
  }
137
141
  }
138
142
 
143
+ const response: HookDecision = {};
139
144
  if (currentInput && payload.tool_input && JSON.stringify(currentInput) !== JSON.stringify(payload.tool_input)) {
140
- return { updatedInput: currentInput };
145
+ response.updatedInput = currentInput;
146
+ }
147
+ if (messages.length > 0) {
148
+ response.systemMessage = messages.join('\n');
141
149
  }
142
150
 
143
- return {};
151
+ return response;
144
152
  }
145
153
 
146
154
  async function executeNonBlockingHandlers(matched: Handler[], payload: HookPayload): Promise<void> {
@@ -184,8 +192,8 @@ export async function dispatch(stdin: string): Promise<string> {
184
192
 
185
193
  if (isBlockingEvent(event)) {
186
194
  const result = await executeBlockingChain(matched, payload);
187
- // Only output JSON if there's an actual decision/update to communicate
188
- if (result.decision || result.updatedInput) {
195
+ // Output JSON if there's a decision, update, or informational message
196
+ if (result.decision || result.updatedInput || result.systemMessage) {
189
197
  return JSON.stringify(result);
190
198
  }
191
199
  return '';
@@ -46,6 +46,8 @@ export interface HookDecision {
46
46
  decision?: 'allow' | 'deny' | 'ask';
47
47
  reason?: string;
48
48
  updatedInput?: Record<string, unknown>;
49
+ /** Informational message shown to the agent without blocking. */
50
+ systemMessage?: string;
49
51
  }
50
52
 
51
53
  /** Result from a handler — either a decision or void (implicit allow). */