@automagik/genie 4.260421.32 → 4.260422.1
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.
Potentially problematic release.
This version of @automagik/genie might be problematic. Click here for more details.
package/dist/genie.js
CHANGED
|
@@ -155,6 +155,7 @@ ${bin} set-option -w pane-active-border-style "fg=$COLOR"
|
|
|
155
155
|
AND (pane_id IS NULL OR pane_id = '')
|
|
156
156
|
AND current_executor_id IS NULL
|
|
157
157
|
AND started_at < now() - interval '1 second' * ${thresholdSeconds}
|
|
158
|
+
AND id NOT LIKE 'dir:%'
|
|
158
159
|
RETURNING id
|
|
159
160
|
`;for(let row of rows)console.error(`[reconcile] Reset stuck agent ${row.id} from spawning \u2192 error`),recordAuditEvent("worker",row.id,"state_changed","reconciler",{state:"error",reason:"stale_spawn"}).catch(()=>{});let resetIds=rows.map((r)=>r.id),staleWithPane=await sql`
|
|
160
161
|
SELECT id, pane_id FROM agents
|
|
@@ -725,8 +726,8 @@ Next steps:`),console.log(" 1. Reload tmux: tmux source ~/.tmux.conf"),console.
|
|
|
725
726
|
`),await promptUninstallFrom(join21(home,".tmux.conf"),"generated by genie-cli","tmux.conf");for(let shellRc of[join21(home,".zshrc"),join21(home,".bashrc")])await promptUninstallFrom(shellRc,"generated by genie-cli",shellRc);let termuxDir=join21(home,".termux"),isTermux=existsSync19(termuxDir)||process.env.TERMUX_VERSION;if(isTermux){let termuxProps=join21(termuxDir,"termux.properties");if(await promptUninstallFrom(termuxProps,"generated by genie-cli","termux.properties"),!contentExists(termuxProps,"generated by genie-cli"))console.log(" Run: termux-reload-settings")}if(console.log(`
|
|
726
727
|
\u2705 Uninstallation complete!`),console.log(`
|
|
727
728
|
Next steps:`),console.log(" 1. Reload tmux: tmux source ~/.tmux.conf"),console.log(" 2. Restart your shell or run: source ~/.bashrc"),isTermux)console.log(" 3. Reload Termux: termux-reload-settings")}var GENIE_CONTENT_MARKERS;var init_shortcuts=__esm(()=>{GENIE_CONTENT_MARKERS=["genie","Ctrl+","split-window","new-window","stty -ixon","Warp-like","bind-key -n","extra-keys","F1=","bind -x"]});var exports_setup={};__export(exports_setup,{setupCommand:()=>setupCommand});import{homedir as homedir18}from"os";import{join as join22}from"path";function printHeader(){console.log(),console.log(`\x1B[1m\x1B[36m${"=".repeat(64)}\x1B[0m`),console.log("\x1B[1m\x1B[36m Genie Setup Wizard\x1B[0m"),console.log(`\x1B[1m\x1B[36m${"=".repeat(64)}\x1B[0m`),console.log()}function printSection(title,description){if(console.log(),console.log(`\x1B[1m${title}\x1B[0m`),description)console.log(`\x1B[2m${description}\x1B[0m`);console.log()}async function configureSession(config,quick){if(printSection("2. Session Configuration","Configure tmux session settings"),quick)return console.log(` Using defaults: session="${config.session.name}", window="${config.session.defaultWindow}"`),config;let sessionName=await esm_default5({message:"Session name:",default:config.session.name}),defaultWindow=await esm_default5({message:"Default window name:",default:config.session.defaultWindow}),autoCreate=await esm_default4({message:"Auto-create session on connect?",default:config.session.autoCreate});return config.session={name:sessionName,defaultWindow,autoCreate},config}async function configureTerminal(config,quick){if(printSection("3. Terminal Defaults","Configure default values for term commands"),quick)return console.log(` Using defaults: timeout=${config.terminal.execTimeout}ms, lines=${config.terminal.readLines}`),config;let timeoutStr=await esm_default5({message:"Exec timeout (milliseconds):",default:String(config.terminal.execTimeout),validate:(v)=>{let n=Number.parseInt(v,10);return!Number.isNaN(n)&&n>0?!0:"Must be a positive number"}}),linesStr=await esm_default5({message:"Read lines (default for genie agent read):",default:String(config.terminal.readLines),validate:(v)=>{let n=Number.parseInt(v,10);return!Number.isNaN(n)&&n>0?!0:"Must be a positive number"}}),worktreeBase=await esm_default5({message:"Worktree base directory (leave empty for ~/.genie/worktrees/<project>/):",default:config.terminal.worktreeBase??""});return config.terminal={execTimeout:Number.parseInt(timeoutStr,10),readLines:Number.parseInt(linesStr,10),...worktreeBase?{worktreeBase}:{}},config}async function configureShortcuts(config,quick){printSection("4. Keyboard Shortcuts","Warp-like tmux shortcuts for quick navigation");let home=homedir18(),tmuxConf=join22(home,".tmux.conf");if(isShortcutsInstalled(tmuxConf))return console.log(" \x1B[32m\u2713\x1B[0m Tmux shortcuts already installed"),config.shortcuts.tmuxInstalled=!0,config;if(console.log(" Available shortcuts:"),console.log(" \x1B[36mCtrl+T\x1B[0m \u2192 New tab (window)"),console.log(" \x1B[36mCtrl+S\x1B[0m \u2192 Vertical split"),console.log(" \x1B[36mCtrl+H\x1B[0m \u2192 Horizontal split"),console.log(),quick)return console.log(" Skipped in quick mode. Run \x1B[36mgenie setup --shortcuts\x1B[0m to install."),config;if(await esm_default4({message:"Install tmux keyboard shortcuts?",default:!1}))console.log(),await installShortcuts(),config.shortcuts.tmuxInstalled=!0,await updateShortcutsConfig({tmuxInstalled:!0});else console.log(" Skipped. Run \x1B[36mgenie shortcuts install\x1B[0m later.");return config}function printCodexResult(result2){if(result2==="changed")console.log(" \x1B[32m\u2713\x1B[0m Codex config updated");else if(result2==="unchanged")console.log(" \x1B[32m\u2713\x1B[0m Codex config already up to date");else console.log(" \x1B[31m\u2717\x1B[0m Failed to update codex config")}async function configureCodex(config,quick){printSection("5. Codex Integration","Configure OpenAI Codex for genie agents");let codexCheck=await checkCommand("codex");if(!codexCheck.exists)return console.log(" \x1B[33m!\x1B[0m Codex CLI not found. Skipping codex integration."),config;if(console.log(` \x1B[32m\u2713\x1B[0m Codex CLI found (${codexCheck.version??"unknown version"})`),isCodexConfigured())return console.log(" \x1B[32m\u2713\x1B[0m Codex config already configured"),config.codex={configured:!0},config;if(console.log(),console.log(" Genie needs to configure codex for agent communication:"),console.log(" \x1B[36mdisable_paste_burst\x1B[0m \u2192 Reliable tmux command injection"),console.log(" \x1B[36mOTel exporter\x1B[0m \u2192 Telemetry relay for state detection"),console.log(` Config: \x1B[2m${contractPath(getCodexConfigPath())}\x1B[0m`),console.log(),quick){let result2=ensureCodexOtelConfig();return printCodexResult(result2),config.codex={configured:result2!=="error"},config}if(await esm_default4({message:"Configure Codex for genie agent integration?",default:!0})){let result2=ensureCodexOtelConfig();printCodexResult(result2),config.codex={configured:result2!=="error"}}else console.log(" Skipped. Run \x1B[36mgenie setup --codex\x1B[0m later.");return config}async function configureDebug(config,quick){if(printSection("6. Debug Options","Logging and debugging settings"),quick)return console.log(" Using defaults: tmuxDebug=false, verbose=false"),config;let tmuxDebug=await esm_default4({message:"Enable tmux debug logging?",default:config.logging.tmuxDebug}),verbose=await esm_default4({message:"Enable verbose mode?",default:config.logging.verbose});return config.logging={tmuxDebug,verbose},config}async function configurePromptMode(config,quick){if(printSection("7. Prompt Mode","Controls how genie injects system prompts into Claude Code"),quick)return console.log(` Using default: promptMode="${config.promptMode}"`),config;console.log(" append \u2014 Uses --append-system-prompt-file (preserves Claude Code default system prompt)"),console.log(" system \u2014 Uses --system-prompt-file (replaces Claude Code default system prompt)"),console.log();let promptMode=await esm_default11({message:"Prompt mode:",choices:[{name:"append (recommended \u2014 preserves CC default)",value:"append"},{name:"system (replaces CC default)",value:"system"}],default:config.promptMode});return config.promptMode=promptMode,config}async function showSummaryAndSave(config){printSection("Summary",`Configuration will be saved to ${contractPath(getGenieConfigPath())}`),console.log(` Session: \x1B[36m${config.session.name}\x1B[0m (window: ${config.session.defaultWindow})`),console.log(` Terminal: timeout=${config.terminal.execTimeout}ms, lines=${config.terminal.readLines}`),console.log(` Shortcuts: ${config.shortcuts.tmuxInstalled?"\x1B[32minstalled\x1B[0m":"\x1B[2mnot installed\x1B[0m"}`),console.log(` Codex: ${config.codex?.configured?"\x1B[32mconfigured\x1B[0m":"\x1B[2mnot configured\x1B[0m"}`),console.log(` Debug: tmux=${config.logging.tmuxDebug}, verbose=${config.logging.verbose}`),console.log(` Prompt mode: \x1B[36m${config.promptMode}\x1B[0m`),console.log(),config.setupComplete=!0,config.lastSetupAt=new Date().toISOString(),await saveGenieConfig(config),console.log("\x1B[32m\u2713 Configuration saved!\x1B[0m")}async function showCurrentConfig(){let config=await loadGenieConfig();console.log(),console.log("\x1B[1mCurrent Genie Configuration\x1B[0m"),console.log(`\x1B[2m${contractPath(getGenieConfigPath())}\x1B[0m`),console.log(),console.log(JSON.stringify(config,null,2)),console.log()}function printNextSteps(){console.log(),console.log("\x1B[1mNext Steps:\x1B[0m"),console.log(),console.log(" Start a session: \x1B[36mgenie\x1B[0m"),console.log(" Watch AI work: \x1B[36mtmux attach -t genie\x1B[0m"),console.log(" Check health: \x1B[36mgenie doctor\x1B[0m"),console.log()}async function setupCommand(options={}){if(options.show){await showCurrentConfig();return}if(options.reset){await resetConfig(),console.log("\x1B[32m\u2713 Configuration reset to defaults.\x1B[0m"),console.log();return}let config=await loadGenieConfig();if(options.shortcuts){printHeader(),await configureShortcuts(config,!1),await markSetupComplete();return}if(options.terminal){printHeader(),config=await configureTerminal(config,!1),await saveGenieConfig(config),console.log("\x1B[32m\u2713 Terminal configuration saved.\x1B[0m");return}if(options.session){printHeader(),config=await configureSession(config,!1),await saveGenieConfig(config),console.log("\x1B[32m\u2713 Session configuration saved.\x1B[0m");return}if(options.codex){if(printHeader(),config=await configureCodex(config,!1),await saveGenieConfig(config),config.codex?.configured)console.log("\x1B[32m\u2713 Codex configuration saved.\x1B[0m");return}let quick=options.quick??!1;if(printHeader(),quick)console.log("\x1B[2mQuick mode: accepting all defaults\x1B[0m");config=await configureSession(config,quick),config=await configureTerminal(config,quick),config=await configureShortcuts(config,quick),config=await configureCodex(config,quick),config=await configureDebug(config,quick),config=await configurePromptMode(config,quick),await showSummaryAndSave(config),installGenieTmuxConf(),printNextSteps()}function installGenieTmuxConf(){let{existsSync:existsSync20,copyFileSync:copyFileSync2,mkdirSync:mkdirSync10,chmodSync:chmodSync2}=__require("fs"),{resolve:resolve3,dirname:dirname6}=__require("path"),genieHome3=process.env.GENIE_HOME??join22(homedir18(),".genie"),dest=join22(genieHome3,"tmux.conf");if(existsSync20(dest))return;let src=[resolve3(__dirname,"..","..","scripts","tmux","genie.tmux.conf"),resolve3(__dirname,"..","scripts","tmux","genie.tmux.conf")].find((p)=>existsSync20(p));if(!src)return;try{mkdirSync10(genieHome3,{recursive:!0}),copyFileSync2(src,dest),console.log(`\x1B[32m\u2713\x1B[0m Installed genie tmux config to ${dest}`)}catch{}let osc52Src=join22(dirname6(src),"osc52-copy.sh"),osc52Dest=join22(genieHome3,"osc52-copy.sh");if(existsSync20(osc52Src))try{copyFileSync2(osc52Src,osc52Dest),chmodSync2(osc52Dest,493)}catch{}}var __dirname="/home/runner/_work/genie/genie/src/genie-commands";var init_setup=__esm(()=>{init_esm14();init_codex_config();init_genie_config2();init_system_detect();init_shortcuts()});var exports_version={};__export(exports_version,{VERSION:()=>VERSION});import{existsSync as existsSync23,readFileSync as readFileSync17}from"fs";import{dirname as dirname6,resolve as resolve3}from"path";function readVersionFromPackageJson(){let candidates=[resolve3(dirname6(import.meta.dir??__dirname),"..","..","package.json"),resolve3(dirname6(import.meta.dir??__dirname),"..","package.json"),resolve3(dirname6(import.meta.dir??__dirname),"package.json")];for(let candidate of candidates)try{if(existsSync23(candidate)){let pkg=JSON.parse(readFileSync17(candidate,"utf-8"));if(pkg.version)return pkg.version}}catch{}return FALLBACK_VERSION}var __dirname="/home/runner/_work/genie/genie/src/lib",FALLBACK_VERSION="0.0.0-unknown",VERSION;var init_version=__esm(()=>{VERSION=readVersionFromPackageJson()});var exports_agent_directory={};__export(exports_agent_directory,{rm:()=>rm3,resolve:()=>resolve4,ls:()=>ls,loadIdentity:()=>loadIdentity,getProjectRoot:()=>getProjectRoot,get:()=>get2,findSessionByRepo:()=>findSessionByRepo,edit:()=>edit,add:()=>add});import{existsSync as existsSync24}from"fs";import{join as join26}from"path";function getProjectRoot(){if(process.env.GENIE_PROJECT_ROOT)return process.env.GENIE_PROJECT_ROOT;try{let{execSync:execSync6}=__require("child_process");return execSync6("git rev-parse --show-toplevel",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return process.cwd()}}async function add(entry2,_options){if(!entry2.name||entry2.name.trim()==="")throw Error("Agent name is required.");if(!entry2.dir||entry2.dir.trim()==="")throw Error("Agent directory (--dir) is required.");if(!existsSync24(entry2.dir))throw Error(`Directory does not exist: ${entry2.dir}`);let agentsPath=join26(entry2.dir,"AGENTS.md");if(!existsSync24(agentsPath))throw Error(`AGENTS.md not found in ${entry2.dir}. Each agent directory must contain an AGENTS.md file.`);let full={...entry2,promptMode:entry2.promptMode??"append",registeredAt:new Date().toISOString()},existing=await resolve4(entry2.name);if(existing&&!existing.builtin)throw Error(`Agent "${entry2.name}" already exists. Use "genie dir edit" to update or "genie dir rm" first.`);let metadata=buildMetadata(full),{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2();return await sql`
|
|
728
|
-
INSERT INTO agents (id, role, custom_name, started_at, metadata)
|
|
729
|
-
VALUES (${`dir:${entry2.name}`}, ${entry2.name}, ${entry2.name}, now(), ${sql.json(metadata)})
|
|
729
|
+
INSERT INTO agents (id, role, custom_name, started_at, state, metadata)
|
|
730
|
+
VALUES (${`dir:${entry2.name}`}, ${entry2.name}, ${entry2.name}, now(), ${null}, ${sql.json(metadata)})
|
|
730
731
|
ON CONFLICT (id) DO UPDATE SET metadata = ${sql.json(metadata)}
|
|
731
732
|
`,full}async function rm3(name,options){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2(),dirRemoved=(await sql`DELETE FROM agents WHERE id = ${`dir:${name}`}`).count>0;if(options?.force){let roleDelete=await sql`DELETE FROM agents WHERE role = ${name}`;return{removed:dirRemoved||roleDelete.count>0}}if(dirRemoved)return{removed:!0};let instances=await sql`SELECT id FROM agents WHERE role = ${name}`;if(instances.length===0)return{removed:!1};let idList=instances.map((r)=>r.id).join(", ");return{removed:!1,message:`No directory entry for "${name}". Active instances: ${idList}. Use 'genie kill <id>' to terminate, or re-run with --force to remove all.`}}async function resolve4(name){let templateTeam=await lookupTemplateTeam(name);try{let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),rows=await(await getConnection2())`
|
|
732
733
|
SELECT role, metadata, created_at FROM agents
|
|
@@ -2039,11 +2040,11 @@ ${body}`;writeFileSync12(filePath,output,"utf-8")}function serializeSdkConfig(sd
|
|
|
2039
2040
|
HAVING COUNT(*) > 1
|
|
2040
2041
|
ORDER BY COUNT(*) DESC, custom_name ASC
|
|
2041
2042
|
LIMIT 200
|
|
2042
|
-
`},queryFn=opts?.query??defaultQuery;return{id:"rot.duplicate-agents",version,riskClass:"low",async query(){return{duplicates:await queryFn()}},shouldFire(state){return state.duplicates.length>0},render(state){let row=state.duplicates[0],subject=`${row.team}/${row.custom_name}`;return{type:"rot.detected",subject,payload:{pattern_id:"pattern-4-duplicate-agents",entity_id:subject,observed_state_json:{team:row.team,custom_name:row.custom_name,dup_count:row.dup_count,agent_ids:row.agent_ids,total_offending_pairs:state.duplicates.length}}}}}}var init_pattern_4_duplicate_agents=__esm(()=>{init_db();init_detectors();registerDetector(createDuplicateAgentsDetector())});function createZombieTeamLeadDetector(opts){let thresholdMs=(opts?.idleMinutes??DEFAULT_IDLE_MINUTES)*60*1000,version=opts?.version??"0.1.0",defaultQuery=async()=>{let sql=await getConnection();return(await sql`
|
|
2043
|
+
`},queryFn=opts?.query??defaultQuery;return{id:"rot.duplicate-agents",version,riskClass:"low",async query(){return{duplicates:await queryFn()}},shouldFire(state){return state.duplicates.length>0},render(state){let row=state.duplicates[0],subject=`${row.team}/${row.custom_name}`;return{type:"rot.detected",subject,payload:{pattern_id:"pattern-4-duplicate-agents",entity_id:subject,observed_state_json:{team:row.team,custom_name:row.custom_name,dup_count:row.dup_count,agent_ids:row.agent_ids,total_offending_pairs:state.duplicates.length}}}}}}var init_pattern_4_duplicate_agents=__esm(()=>{init_db();init_detectors();registerDetector(createDuplicateAgentsDetector())});function teamLeadPredicate(sql){return sql`id IN (SELECT DISTINCT reports_to FROM agents WHERE reports_to IS NOT NULL)`}function createZombieTeamLeadDetector(opts){let thresholdMs=(opts?.idleMinutes??DEFAULT_IDLE_MINUTES)*60*1000,version=opts?.version??"0.1.0",defaultQuery=async()=>{let sql=await getConnection();return(await sql`
|
|
2043
2044
|
WITH active_leads AS (
|
|
2044
2045
|
SELECT id, team, state
|
|
2045
2046
|
FROM agents
|
|
2046
|
-
WHERE
|
|
2047
|
+
WHERE ${teamLeadPredicate(sql)}
|
|
2047
2048
|
AND team IS NOT NULL
|
|
2048
2049
|
AND state = ANY(${sql.array([...ALIVE_STATES])})
|
|
2049
2050
|
),
|
|
@@ -2119,7 +2120,7 @@ ${body}`;writeFileSync12(filePath,output,"utf-8")}function serializeSdkConfig(sd
|
|
|
2119
2120
|
team_leads AS (
|
|
2120
2121
|
SELECT DISTINCT ON (team) team, id AS lead_agent_id, state AS lead_state
|
|
2121
2122
|
FROM agents
|
|
2122
|
-
WHERE
|
|
2123
|
+
WHERE ${teamLeadPredicate(sql)}
|
|
2123
2124
|
AND team IS NOT NULL
|
|
2124
2125
|
ORDER BY team, created_at DESC
|
|
2125
2126
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.260422.1",
|
|
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"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- Backfill: clear 'spawning'/'error' from directory-agent identity rows.
|
|
2
|
+
--
|
|
3
|
+
-- Directory rows (id prefix `dir:`) are identity records, not runtime spawns.
|
|
4
|
+
-- They never have a pane or executor of their own — state is tracked via
|
|
5
|
+
-- their runtime/executor children. Prior to this fix, directory.add()
|
|
6
|
+
-- INSERTed without a `state` value and the column DEFAULT ('spawning')
|
|
7
|
+
-- applied, causing reconcileStaleSpawns() to flip every dir row to
|
|
8
|
+
-- 'error' ~60s after every `genie serve` boot.
|
|
9
|
+
--
|
|
10
|
+
-- The INSERT is now corrected in agent-directory.ts to pass NULL explicitly,
|
|
11
|
+
-- and reconcileStaleSpawns() skips `dir:%` ids as belt-and-suspenders.
|
|
12
|
+
-- This migration repairs rows already poisoned by the old behavior.
|
|
13
|
+
UPDATE agents
|
|
14
|
+
SET state = NULL,
|
|
15
|
+
last_state_change = now()
|
|
16
|
+
WHERE id LIKE 'dir:%'
|
|
17
|
+
AND state IN ('spawning', 'error')
|
|
18
|
+
AND (pane_id IS NULL OR pane_id = '')
|
|
19
|
+
AND current_executor_id IS NULL;
|