@automagik/genie 4.260410.1 → 4.260410.2

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.260410.1",
13
+ "version": "4.260410.2",
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
@@ -3182,7 +3182,25 @@ Columns:`);for(let i2=0;i2<sorted.length;i2++){let c=sorted[i2],count=countByCol
3182
3182
  `),console.log(`Exported board "${board.name}" to ${options.output}`)}else console.log(json2)}async function handleBoardReconcile(name,options){let{reconcileBoard:reconcileBoard2}=await Promise.resolve().then(() => (init_board_service(),exports_board_service)),board=await resolveBoard(name,options.project),result2=await reconcileBoard2(board.id);if(options.json){console.log(JSON.stringify(result2,null,2));return}if(result2.fixed===0&&result2.orphaned===0){console.log(`Board "${board.name}": all tasks have valid column_ids.`);return}if(console.log(`Board "${board.name}" reconciliation:`),console.log(` Fixed: ${result2.fixed} task${result2.fixed===1?"":"s"}`),result2.orphaned>0){let count=result2.orphaned;console.log(` Still orphaned: ${count} task${count===1?"":"s"} (stage doesn't match any column)`)}}async function handleBoardImport(options){let bs=await getBoardService(),projectId=await resolveProjectId(options.project),raw=readFileSync20(options.json,"utf-8"),data=JSON.parse(raw),board=await bs.importBoard(data,projectId);console.log(`Imported board "${board.name}" (${board.id}) with ${board.columns.length} columns`)}async function handleTemplateList(options){let templates=await(await getTemplateService()).listTemplates();if(options.json){console.log(JSON.stringify(templates,null,2));return}printTemplateTable(templates)}async function handleTemplateShow(name,options){let template=await(await getTemplateService()).getTemplate(name);if(!template)throw Error(`Template not found: ${name}`);if(options.json){console.log(JSON.stringify(template,null,2));return}if(console.log(`
3183
3183
  Template: ${template.name} (${template.id})`),template.description)console.log(`Description: ${template.description}`);if(template.icon)console.log(`Icon: ${template.icon}`);console.log(`Built-in: ${template.isBuiltin?"yes":"no"}`),printColumnPipeline(template.columns,`Pipeline (${template.columns.length} columns)`)}async function handleTemplateCreate(name,options){let tmpl=await getTemplateService();if(options.fromBoard){let board=await(await getBoardService()).getBoard(options.fromBoard);if(!board)throw Error(`Board not found: ${options.fromBoard}`);let template2=await tmpl.snapshotFromBoard(board.id,name);console.log(`Created template "${template2.name}" (${template2.id}) from board "${board.name}" with ${template2.columns.length} columns`);return}let columns;if(options.columns)columns=options.columns.split(",").map((colName,i2)=>({id:crypto.randomUUID(),name:colName.trim(),label:colName.trim(),gate:"human",action:null,auto_advance:!1,transitions:[],roles:["*"],color:"#94a3b8",parallel:!1,on_fail:null,position:i2}));let template=await tmpl.createTemplate({name,description:options.description,columns});console.log(`Created template "${template.name}" (${template.id}) with ${template.columns.length} columns`)}async function handleTemplateEdit(name,options){let tmpl=await getTemplateService(),template=await tmpl.getTemplate(name);if(!template)throw Error(`Template not found: ${name}`);if(!options.column)console.error("Error: --column is required for template edit."),process.exit(1);let updates={};if(options.gate)updates.gate=options.gate;if(options.action)updates.action=options.action;if(options.color)updates.color=options.color;if(options.rename)updates.name=options.rename,updates.label=options.rename;if(!await tmpl.updateTemplateColumn(template.id,options.column,updates))throw Error(`Failed to update template: ${name}`);console.log(`Updated column "${options.column}" on template "${template.name}".`)}async function handleTemplateRename(oldName,newName){let tmpl=await getTemplateService(),template=await tmpl.getTemplate(oldName);if(!template)throw Error(`Template not found: ${oldName}`);let updated=await tmpl.renameTemplate(template.id,newName);if(!updated)throw Error(`Failed to rename template: ${oldName}`);console.log(`Renamed template "${oldName}" to "${updated.name}".`)}async function handleTemplateDelete(name){let tmpl=await getTemplateService(),template=await tmpl.getTemplate(name);if(!template)throw Error(`Template not found: ${name}`);if(!await tmpl.deleteTemplate(template.id))throw Error(`Failed to delete template: ${name}`);console.log(`Deleted template "${template.name}" (${template.id}).`)}function registerBoardCommands(program2){let board=program2.command("board").description("Board and pipeline management");board.command("create <name>").description("Create a new board").option("--project <project>","Project name").option("--from <template>","Create from template name").option("--columns <columns>","Comma-separated column names").option("--description <text>","Board description").action(async(name,options)=>{try{await handleBoardCreate(name,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("list").description("List all boards").option("--project <project>","Filter by project").option("--all","Include archived boards").option("--json","Output as JSON").action(async(options)=>{try{await handleBoardList(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("show <name...>").description("Show board detail").option("--project <project>","Disambiguate by project").option("--json","Output as JSON").action(async(nameParts,options)=>{try{await handleBoardShow(nameParts.join(" "),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("edit <name...>").description("Edit board or column properties").option("--project <project>","Disambiguate by project").option("--column <col>","Column name to edit").option("--gate <gate>","New gate value (human|agent|human+agent)").option("--action <action>","New action skill").option("--color <color>","New color hex").option("--rename <new>","Rename the column").option("--name <new>","Rename the board itself").option("--description <text>","Update description").action(async(nameParts,options)=>{try{await handleBoardEdit(nameParts.join(" "),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("delete <name...>").description("Delete a board").option("--project <project>","Disambiguate by project").option("--force","Skip confirmation").action(async(nameParts,options)=>{try{await handleBoardDelete(nameParts.join(" "),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("columns <name...>").description("Show board column pipeline").option("--project <project>","Disambiguate by project").option("--json","Output as JSON").action(async(nameParts,options)=>{try{await handleBoardColumns(nameParts.join(" "),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("use <name...>").description("Set active board for current repo").option("--project <project>","Disambiguate by project").action(async(nameParts,options)=>{try{await handleBoardUse(nameParts.join(" "),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("export <name...>").description("Export board as JSON").option("--project <project>","Disambiguate by project").option("--output <file>","Write to file instead of stdout").option("--json","Output as JSON (default, accepted for consistency)").action(async(nameParts,options)=>{try{await handleBoardExport(nameParts.join(" "),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("reconcile <name...>").description("Fix orphaned column_ids by matching task stage to board columns").option("--project <project>","Disambiguate by project").option("--json","Output as JSON").action(async(nameParts,options)=>{try{await handleBoardReconcile(nameParts.join(" "),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("archive <name...>").description("Archive a board and its unfinished tasks").option("--project <project>","Disambiguate by project").action(async(nameParts,options)=>{try{let ts3=await getTaskService3(),board2=await resolveBoard(nameParts.join(" "),options.project);await ts3.archiveBoard(board2.id),console.log(`Archived board "${board2.name}" and its unfinished tasks.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),board.command("import").description("Import board from JSON file").requiredOption("--json <file>","JSON file to import").requiredOption("--project <project>","Target project").action(async(options)=>{try{await handleBoardImport(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}});let template=board.command("template").description("Board template management");template.command("list").description("List all board templates").option("--json","Output as JSON").action(async(options)=>{try{await handleTemplateList(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),template.command("show <name>").description("Show template detail with pipeline view").option("--json","Output as JSON").action(async(name,options)=>{try{await handleTemplateShow(name,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),template.command("create <name>").description("Create a board template").option("--from-board <board>","Create from existing board").option("--columns <columns>","Comma-separated column names").option("--description <text>","Template description").action(async(name,options)=>{try{await handleTemplateCreate(name,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),template.command("edit <name>").description("Edit a template column").option("--column <col>","Column name to edit").option("--gate <gate>","New gate value (human|agent|human+agent)").option("--action <action>","New action skill").option("--rename <new>","Rename the column").option("--color <color>","New color hex").action(async(name,options)=>{try{await handleTemplateEdit(name,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),template.command("rename <old> <new>").description("Rename a template").action(async(oldName,newName)=>{try{await handleTemplateRename(oldName,newName)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),template.command("delete <name>").description("Delete a template").action(async(name)=>{try{await handleTemplateDelete(name)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}import{execSync as execSync11}from"child_process";import{existsSync as existsSync31,mkdirSync as mkdirSync14,readFileSync as readFileSync21,realpathSync as realpathSync4,writeFileSync as writeFileSync16}from"fs";import{homedir as homedir27}from"os";import{dirname as dirname8,join as join40,resolve as resolve6}from"path";var BRAIN_PKG2="@khal-os/brain",BRAIN_REPO="khal-os/brain";function resolveGenieRoot(){try{let scriptDir=dirname8(realpathSync4(process.argv[1])),candidates=[resolve6(scriptDir,".."),resolve6(scriptDir,"..","..")];for(let c of candidates)if(existsSync31(join40(c,"package.json")))return c}catch{}return resolve6(import.meta.dir,"..","..")}var BRAIN_DIR2=join40(resolveGenieRoot(),"node_modules","@khal-os","brain"),CACHE_PATH=join40(homedir27(),".genie","brain-version-check.json");function compareVersions(a,b2){let partsA=a.split(".").map(Number),partsB=b2.split(".").map(Number);for(let i2=0;i2<Math.max(partsA.length,partsB.length);i2++){let diff=(partsA[i2]??0)-(partsB[i2]??0);if(diff!==0)return diff}return 0}function readLocalBrainVersion(){try{return JSON.parse(readFileSync21(join40(BRAIN_DIR2,"package.json"),"utf-8")).version??"unknown"}catch{return}}function resolveBrainBin(){let candidates=[join40(resolveGenieRoot(),"node_modules",".bin","brain"),join40(BRAIN_DIR2,"dist","cli.js")];for(let c of candidates)if(existsSync31(c))return c;return}function findBrainVault(){let candidates=[process.cwd(),join40(process.cwd(),"brain"),join40(homedir27(),"brain")];for(let dir of candidates)if(existsSync31(join40(dir,"brain.json")))return dir;return null}function readActiveBrainConfig(){try{let configPath2=join40(homedir27(),".brain","config.json");if(!existsSync31(configPath2))return null;let data=JSON.parse(readFileSync21(configPath2,"utf-8"));if(!data.pid)return null;return{pid:data.pid,pgPort:data.pgPort,brainPath:data.brainPath}}catch{return null}}function isProcessAlive2(pid){try{return process.kill(pid,0),!0}catch{return!1}}async function stopBrainDaemon(){let config=readActiveBrainConfig();if(!config?.pid||!isProcessAlive2(config.pid))return!1;let brainBin=resolveBrainBin(),brainPath=config.brainPath;if(brainBin)try{let pathArg=brainPath?` --brain-path "${brainPath}"`:"";execSync11(`"${brainBin}" serve stop${pathArg}`,{stdio:"pipe",timeout:1e4})}catch{}for(let i2=0;i2<25;i2++){if(!isProcessAlive2(config.pid))return!0;await new Promise((r)=>setTimeout(r,200))}try{process.kill(config.pid,"SIGKILL")}catch{}return!0}function startBrainDaemon(vaultPath,extraArgs){let bin=resolveBrainBin();if(!bin)return;try{let argsStr=extraArgs?.length?` ${extraArgs.join(" ")}`:"";execSync11(`"${bin}" serve --daemon --brain-path "${vaultPath}"${argsStr}`,{stdio:"inherit",timeout:15000}),console.log(" Brain daemon started.")}catch{console.log(" Daemon failed to start. Run: brain serve --daemon")}}function readSavedDaemonArgs(brainPath){try{let serverJsonPath=join40(brainPath,".brain-server.json");return JSON.parse(readFileSync21(serverJsonPath,"utf-8")).args}catch{return}}function checkForUpdates(cachePath){try{let p=cachePath??CACHE_PATH;if(!existsSync31(p))return{updateAvailable:!1};let cache=JSON.parse(readFileSync21(p,"utf-8"));if(cache.updateAvailable&&cache.latestVersion)return{updateAvailable:!0,latestVersion:cache.latestVersion};return{updateAvailable:!1}}catch{return{updateAvailable:!1}}}function refreshVersionCache(localVersion){try{let version=localVersion??readLocalBrainVersion();if(!version)return;let latestTag=execSync11(`gh release view --repo ${BRAIN_REPO} --json tagName -q .tagName`,{stdio:"pipe",encoding:"utf-8"}).trim(),latestVersion=latestTag.replace(/^v/,""),localCore=version.replace(/^\d+\./,""),latestCore=latestVersion.replace(/^\d+\./,""),updateAvailable=compareVersions(latestCore,localCore)>0,cacheDir=join40(homedir27(),".genie");mkdirSync14(cacheDir,{recursive:!0}),writeFileSync16(CACHE_PATH,JSON.stringify({checkedAt:new Date().toISOString(),localVersion:version,latestTag,latestVersion,updateAvailable},null,2))}catch{}}async function updateBrain(){if(!existsSync31(join40(BRAIN_DIR2,"package.json")))return console.log(" Brain is not installed. Run: genie brain install"),!1;let oldVersion=readLocalBrainVersion()??"unknown";console.log(" Checking for updates...");let tag;try{tag=execSync11(`gh release view --repo ${BRAIN_REPO} --json tagName -q .tagName`,{stdio:"pipe",encoding:"utf-8"}).trim()}catch{return console.error(" Failed to check latest release. Ensure: gh auth login"),!1}let newVersion=tag.replace(/^v/,"");if(compareVersions(newVersion,oldVersion)<=0)return console.log(` Already at latest version (${oldVersion}).`),!0;console.log(` Upgrading: ${oldVersion} \u2192 ${newVersion}`),console.log("");let activeConfig=readActiveBrainConfig(),savedArgs=activeConfig?.brainPath?readSavedDaemonArgs(activeConfig.brainPath):void 0,wasRunning=activeConfig?await stopBrainDaemon():!1,tmpDir=join40(homedir27(),".cache","genie-brain");mkdirSync14(tmpDir,{recursive:!0}),execSync11(`gh release download ${tag} --repo ${BRAIN_REPO} --pattern '*.tgz' --dir "${tmpDir}" --clobber`,{stdio:"inherit"}),execSync11(`rm -rf "${BRAIN_DIR2}"`,{stdio:"pipe"}),mkdirSync14(BRAIN_DIR2,{recursive:!0}),execSync11(`tar xzf "${tmpDir}/khal-os-brain-${newVersion}.tgz" -C "${BRAIN_DIR2}" --strip-components=1`,{stdio:"inherit"}),execSync11("bun install",{cwd:BRAIN_DIR2,stdio:"inherit"}),console.log(`
3184
3184
  Updated: ${oldVersion} \u2192 ${newVersion}`);try{let migrateScript=`const b = require('${BRAIN_PKG2}'); if (b.runAllMigrations) b.runAllMigrations().then(() => process.exit(0)).catch((e) => { console.error(e); process.exit(1); }); else process.exit(0);`;execSync11(`bun -e "${migrateScript}"`,{cwd:BRAIN_DIR2,stdio:"inherit"}),console.log(" Migrations applied.")}catch{console.log(" Migration skipped. Run: genie brain migrate")}if(refreshVersionCache(newVersion),wasRunning&&activeConfig?.brainPath)startBrainDaemon(activeConfig.brainPath,savedArgs);return!0}async function showVersion(){let localVersion="not installed";try{let brain=await import(BRAIN_PKG2);localVersion=brain.getVersion?.()??brain.VERSION??"unknown"}catch(err){let msg=err instanceof Error?err.message:String(err);if(isModuleNotFound(msg)){console.log(" Brain is not installed. Run: genie brain install");return}}console.log(` Local: ${localVersion}`),refreshVersionCache(localVersion);let check2=checkForUpdates();if(check2.updateAvailable&&check2.latestVersion)console.log(` Latest: ${check2.latestVersion}`),console.log(""),console.log(" Update available. Run: genie brain upgrade");else console.log(" Status: up to date")}async function installBrain(){console.log(""),console.log(" Installing brain from GitHub release (enterprise)..."),console.log(""),console.log(" Source: https://github.com/khal-os/brain"),console.log(" Requires: GitHub org membership (khal-os)"),console.log("");try{try{execSync11("gh auth token",{stdio:"pipe"})}catch{return console.error(" GitHub CLI not authenticated. Run: gh auth login"),!1}let tag=execSync11(`gh release view --repo ${BRAIN_REPO} --json tagName -q .tagName`,{stdio:"pipe",encoding:"utf-8"}).trim(),version=tag.replace(/^v/,"");console.log(` Latest release: ${tag}`),console.log("");let root=resolveGenieRoot(),brainDir=join40(root,"node_modules","@khal-os","brain"),tmpDir=join40(homedir27(),".cache","genie-brain");mkdirSync14(tmpDir,{recursive:!0}),execSync11(`gh release download ${tag} --repo ${BRAIN_REPO} --pattern '*.tgz' --dir "${tmpDir}" --clobber`,{stdio:"inherit"}),execSync11(`rm -rf "${brainDir}"`,{stdio:"pipe"}),mkdirSync14(brainDir,{recursive:!0}),execSync11(`tar xzf "${tmpDir}/khal-os-brain-${version}.tgz" -C "${brainDir}" --strip-components=1`,{stdio:"inherit"}),execSync11("bun install",{cwd:brainDir,stdio:"inherit"}),console.log(""),console.log(` Brain ${version} installed from GitHub release.`),console.log("");try{let brain=await import(BRAIN_PKG2);if(brain.runAllMigrations)console.log(" Running brain migrations..."),await brain.runAllMigrations(),console.log(" Brain tables created in Postgres.")}catch{console.log(" Auto-migration skipped. Run: genie brain migrate")}let vaultPath=findBrainVault();if(vaultPath)startBrainDaemon(vaultPath);else console.log(" No brain vault found. Create one with: brain init --name <name> --path <path>");return console.log(""),console.log(" Get started:"),console.log(" genie brain init --name my-brain --path ./brain"),console.log(""),!0}catch(err){let msg=err instanceof Error?err.message:String(err);if(msg.includes("Authentication")||msg.includes("permission")||msg.includes("404"))console.error(" Access denied. Brain is enterprise-only."),console.log(""),console.log(" You need:"),console.log(" 1. Membership in the khal-os GitHub org"),console.log(" 2. GitHub CLI authenticated: gh auth login"),console.log(""),console.log(" Manual install:"),console.log(` gh release download --repo ${BRAIN_REPO} --pattern '*.tgz'`),console.log(" tar xzf khal-os-brain-*.tgz -C node_modules/@khal-os/brain --strip-components=1"),console.log("");else console.error(` Install failed: ${msg}`),console.log(""),console.log(" Manual install:"),console.log(` gh release download --repo ${BRAIN_REPO} --pattern '*.tgz'`),console.log(" tar xzf khal-os-brain-*.tgz -C node_modules/@khal-os/brain --strip-components=1"),console.log("");return!1}}async function uninstallBrain(){try{if(!existsSync31(BRAIN_DIR2)){console.log(" Brain is not installed.");return}await stopBrainDaemon(),execSync11(`rm -rf "${BRAIN_DIR2}"`,{stdio:"pipe"}),console.log(" Brain uninstalled.")}catch{console.error(` Uninstall failed. Manual: rm -rf ${BRAIN_DIR2}`)}}function isModuleNotFound(msg){return msg.includes("Cannot find")||msg.includes("not found")||msg.includes("MODULE_NOT_FOUND")}function printNotInstalledMessage(){console.log(""),console.log(" Brain is an enterprise knowledge graph engine."),console.log(" It is not installed."),console.log(""),console.log(" Quick install:"),console.log(""),console.log(" genie brain install"),console.log(""),console.log(" Requires GitHub org membership (khal-os)."),console.log("")}async function executeBrainCommand(args){try{let brain=await import(BRAIN_PKG2);if(brain.execute){await brain.execute(args);let check2=checkForUpdates();if(check2.updateAvailable&&check2.latestVersion)console.log(`
3185
- Update available (${check2.latestVersion}). Run: genie brain upgrade`)}else console.error("Brain module loaded but execute() not found."),console.error("Update: genie brain install")}catch(err){let msg=err instanceof Error?err.message:String(err);if(isModuleNotFound(msg))printNotInstalledMessage();else console.error(`Brain error: ${msg}`)}}function registerBrainCommands(program2){let brain=program2.command("brain").description("Knowledge graph engine (enterprise)").allowUnknownOption().allowExcessArguments().action(async(_options,cmd)=>{let args=cmd.args;if(args.length===0){brain.help();return}await executeBrainCommand(args)});brain.command("install").description("Install genie-brain from GitHub").action(async()=>{await installBrain()}),brain.command("uninstall").description("Remove genie-brain installation").action(async()=>{await uninstallBrain()}),brain.command("upgrade").description("Upgrade genie-brain to latest version").action(async()=>{await updateBrain()}),brain.command("version").description("Show installed brain version").action(async()=>{await showVersion()})}var _brief2;async function getBrief2(){if(!_brief2)_brief2=await Promise.resolve().then(() => (init_brief(),exports_brief));return _brief2}async function handleBrief2(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 getBrief2(),brief=await briefService.generateBrief({team,agent,since:options.since,repoPath:process.cwd()});console.log(briefService.formatBrief(brief))}function registerBriefCommands(program2){program2.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 handleBrief2(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}import{existsSync as existsSync32,mkdirSync as mkdirSync15,readFileSync as readFileSync22,unlinkSync as unlinkSync10,writeFileSync as writeFileSync17}from"fs";import{homedir as homedir28}from"os";import{join as join41}from"path";function genieHome4(){return process.env.GENIE_HOME??join41(homedir28(),".genie")}function pidFilePath(){return join41(genieHome4(),"scheduler.pid")}function logFilePath(){return join41(genieHome4(),"logs","scheduler.log")}function systemdDir(){return join41(homedir28(),".config","systemd","user")}function systemdUnitPath(){return join41(systemdDir(),"genie-scheduler.service")}function readPid(){let path3=pidFilePath();if(!existsSync32(path3))return null;let raw=readFileSync22(path3,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function removePid(){let path3=pidFilePath();if(existsSync32(path3))try{unlinkSync10(path3)}catch{}}function isProcessAlive3(pid){try{return process.kill(pid,0),!0}catch{return!1}}function servePidPath2(){return join41(genieHome4(),"serve.pid")}function readServePid2(){let path3=servePidPath2();if(!existsSync32(path3))return null;let raw=readFileSync22(path3,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function removeServePid2(){let path3=servePidPath2();if(existsSync32(path3))try{unlinkSync10(path3)}catch{}}function generateSystemdUnit(){let genieBin=process.argv[1]??"genie";return`[Unit]
3185
+ Update available (${check2.latestVersion}). Run: genie brain upgrade`)}else console.error("Brain module loaded but execute() not found."),console.error("Update: genie brain install")}catch(err){let msg=err instanceof Error?err.message:String(err);if(isModuleNotFound(msg))printNotInstalledMessage();else console.error(`Brain error: ${msg}`)}}function registerBrainCommands(program2){let brain=program2.command("brain").description("Knowledge graph engine (enterprise) \u2014 forwards unknown subcommands to @khal-os/brain").allowUnknownOption().allowExcessArguments().action(async(_options,cmd)=>{let args=cmd.args;if(args.length===0){brain.help();return}await executeBrainCommand(args)}).addHelpText("after",`
3186
+ Forwarded commands (require @khal-os/brain installed):
3187
+ status Show running brain server status
3188
+ health Show brain health score
3189
+ init Initialize a new brain vault
3190
+ search <query> Search the brain knowledge graph
3191
+ ingest <path> Ingest files into the brain
3192
+ analyze <path> Analyze a file against the brain
3193
+ config Manage brain configuration
3194
+ mount/unmount Mount brains
3195
+ graph Explore the knowledge graph
3196
+ traces View reasoning traces
3197
+
3198
+ Examples:
3199
+ $ genie brain status
3200
+ $ genie brain search "how does login work"
3201
+ $ genie brain init --name my-brain --path ./brain
3202
+
3203
+ Install brain: genie brain install`);brain.command("install").description("Install genie-brain from GitHub").action(async()=>{await installBrain()}),brain.command("uninstall").description("Remove genie-brain installation").action(async()=>{await uninstallBrain()}),brain.command("upgrade").description("Upgrade genie-brain to latest version").action(async()=>{await updateBrain()}),brain.command("version").description("Show installed brain version").action(async()=>{await showVersion()})}var _brief2;async function getBrief2(){if(!_brief2)_brief2=await Promise.resolve().then(() => (init_brief(),exports_brief));return _brief2}async function handleBrief2(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 getBrief2(),brief=await briefService.generateBrief({team,agent,since:options.since,repoPath:process.cwd()});console.log(briefService.formatBrief(brief))}function registerBriefCommands(program2){program2.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 handleBrief2(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}import{existsSync as existsSync32,mkdirSync as mkdirSync15,readFileSync as readFileSync22,unlinkSync as unlinkSync10,writeFileSync as writeFileSync17}from"fs";import{homedir as homedir28}from"os";import{join as join41}from"path";function genieHome4(){return process.env.GENIE_HOME??join41(homedir28(),".genie")}function pidFilePath(){return join41(genieHome4(),"scheduler.pid")}function logFilePath(){return join41(genieHome4(),"logs","scheduler.log")}function systemdDir(){return join41(homedir28(),".config","systemd","user")}function systemdUnitPath(){return join41(systemdDir(),"genie-scheduler.service")}function readPid(){let path3=pidFilePath();if(!existsSync32(path3))return null;let raw=readFileSync22(path3,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function removePid(){let path3=pidFilePath();if(existsSync32(path3))try{unlinkSync10(path3)}catch{}}function isProcessAlive3(pid){try{return process.kill(pid,0),!0}catch{return!1}}function servePidPath2(){return join41(genieHome4(),"serve.pid")}function readServePid2(){let path3=servePidPath2();if(!existsSync32(path3))return null;let raw=readFileSync22(path3,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function removeServePid2(){let path3=servePidPath2();if(existsSync32(path3))try{unlinkSync10(path3)}catch{}}function generateSystemdUnit(){let genieBin=process.argv[1]??"genie";return`[Unit]
3186
3204
  Description=Genie Serve (headless) \u2014 pgserve + scheduler + services
3187
3205
  Documentation=https://github.com/automagik/genie
3188
3206
  After=network.target
@@ -3277,8 +3295,11 @@ coverage
3277
3295
  `)}var MODEL_CHOICES=[{name:"opus (most capable)",value:"opus"},{name:"sonnet (balanced)",value:"sonnet"},{name:"haiku (fastest)",value:"haiku"}];async function runMiniWizard(ctx){let{confirm:confirm2}=await Promise.resolve().then(() => (init_esm14(),exports_esm));console.log(formatWelcome(ctx));let wantCustomize=await confirm2({message:"Customize workspace defaults?",default:!1}),result2={customized:!1,importedAgents:[],completed:!0};if(wantCustomize){let newDefaults=await customizeDefaults(ctx.config.agents?.defaults);if(newDefaults)result2.customized=!0,result2.defaults=newDefaults,persistDefaults(ctx.workspaceRoot,newDefaults)}if(ctx.pending.length>0){console.log(`
3278
3296
  Found ${ctx.pending.length} agent(s) in your project tree:`);for(let agent of ctx.pending)console.log(` ${agent.name} (${agent.relativePath})`);if(await confirm2({message:"Import discovered agents into workspace?",default:!0}))result2.importedAgents=ctx.pending.map((a)=>a.name)}return console.log(formatNextSteps(ctx)),result2}async function customizeDefaults(currentDefaults){let{select:select2}=await Promise.resolve().then(() => (init_esm14(),exports_esm)),effective=computeEffectiveDefaults(currentDefaults),updates={},changed=!1,model=await select2({message:"Default model:",choices:MODEL_CHOICES,default:effective.model});if(model!==BUILTIN_DEFAULTS.model)updates.model=model,changed=!0;let permissionMode=await select2({message:"Default permission mode:",choices:[{name:"default (ask for risky tools)",value:"default"},{name:"plan (require plan approval)",value:"plan"},{name:"bypassPermissions (auto-approve all)",value:"bypassPermissions"}],default:effective.permissionMode});if(permissionMode!==BUILTIN_DEFAULTS.permissionMode)updates.permissionMode=permissionMode,changed=!0;return changed?updates:null}function persistDefaults(workspaceRoot,newDefaults){let configPath2=join51(workspaceRoot,".genie","workspace.json");try{let raw=readFileSync27(configPath2,"utf-8"),config=JSON.parse(raw);if(!config.agents)config.agents={};if(!config.agents.defaults)config.agents.defaults={};Object.assign(config.agents.defaults,newDefaults),writeFileSync20(configPath2,`${JSON.stringify(config,null,2)}
3279
3297
  `,"utf-8"),console.log(" Workspace defaults updated.")}catch(err){console.error(` Failed to update workspace.json: ${err instanceof Error?err.message:String(err)}`)}}import{existsSync as existsSync43,mkdirSync as mkdirSync19,readFileSync as readFileSync28,writeFileSync as writeFileSync21}from"fs";import{join as join52}from"path";function pendingPath(workspaceRoot){return join52(workspaceRoot,".genie","pending-agents.json")}function loadPending(workspaceRoot){let filePath=pendingPath(workspaceRoot);if(!existsSync43(filePath))return{agents:[]};try{let raw=readFileSync28(filePath,"utf-8"),parsed=JSON.parse(raw);return{agents:Array.isArray(parsed.agents)?parsed.agents:[]}}catch{return{agents:[]}}}function savePending(workspaceRoot,store){let filePath=pendingPath(workspaceRoot);mkdirSync19(join52(workspaceRoot,".genie"),{recursive:!0}),writeFileSync21(filePath,`${JSON.stringify(store,null,2)}
3280
- `,"utf-8")}function refreshPending(workspaceRoot,discovered){let existing=loadPending(workspaceRoot),dismissedSet=new Set(existing.agents.filter((a)=>a.dismissed).map((a)=>a.path)),now=new Date().toISOString(),store={agents:discovered.map((d)=>({name:d.name,path:d.path,relativePath:d.relativePath,isSubAgent:d.isSubAgent,parentName:d.parentName,discoveredAt:existing.agents.find((a)=>a.path===d.path)?.discoveredAt??now,dismissed:dismissedSet.has(d.path)}))};return savePending(workspaceRoot,store),store}function listPending(workspaceRoot){return loadPending(workspaceRoot).agents.filter((a)=>!a.dismissed)}function removePending(workspaceRoot,agentPath){let store=loadPending(workspaceRoot),idx=store.agents.findIndex((a)=>a.path===agentPath);if(idx===-1)return!1;return store.agents.splice(idx,1),savePending(workspaceRoot,store),!0}init_workspace();init_templates();function detectPgUrl(){if(process.env.DATABASE_URL)return process.env.DATABASE_URL;if(process.env.PG_URL)return process.env.PG_URL;try{let{execSync:execSync15}=__require("child_process"),match=execSync15("pgrep -af pgserve 2>/dev/null",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).match(/postgres(?:ql)?:\/\/[^\s]+/);if(match)return match[0]}catch{}return}async function ensureSetupCompleteForInit(){let{isSetupComplete:isSetupComplete2}=await Promise.resolve().then(() => (init_genie_config2(),exports_genie_config));if(isSetupComplete2())return;console.log("Genie setup not complete. Running `genie setup` before continuing...");let{setupCommand:setupCommand2}=await Promise.resolve().then(() => (init_setup(),exports_setup));await setupCommand2()}function scaffoldAgentInWorkspace(workspaceRoot,name,agentsDir){let baseDir=agentsDir??join53(workspaceRoot,"agents"),agentDir=join53(baseDir,name);if(existsSync44(agentDir))throw Error(`Agent directory already exists: ${agentDir}`);mkdirSync20(agentDir,{recursive:!0}),mkdirSync20(join53(agentDir,"brain","memory"),{recursive:!0}),mkdirSync20(join53(agentDir,".claude"),{recursive:!0});let workspaceDefaults;try{workspaceDefaults=getWorkspaceConfig(workspaceRoot).agents?.defaults}catch{}scaffoldAgentFiles(agentDir,name,workspaceDefaults),writeFileSync22(join53(agentDir,".claude","settings.local.json"),`${JSON.stringify({agentName:name},null,2)}
3281
- `);let reposTarget=join53(workspaceRoot,"repos");if(existsSync44(reposTarget))try{symlinkSync(reposTarget,join53(agentDir,"repos"))}catch{}if(console.log(`Agent scaffolded: agents/${name}/`),console.log(" AGENTS.md, SOUL.md, HEARTBEAT.md"),console.log(" brain/memory/"),console.log(" .claude/settings.local.json"),existsSync44(join53(agentDir,"repos")))console.log(" repos -> ../repos (symlink)")}async function maybeBootstrapDefaultAgent(workspaceRoot){if(scanAgents2(workspaceRoot).length>0)return!1;if(!await esm_default4({message:"No agent found in this workspace. Scaffold the default `genie` agent now?",default:!0}))return console.log(" Skipped default agent bootstrap. Run `genie init agent genie` later."),!1;return scaffoldAgentInWorkspace(workspaceRoot,"genie"),!0}async function syncWorkspaceAgents(workspaceRoot){let agents=scanAgents2(workspaceRoot);if(agents.length===0)return;console.log(` Found ${agents.length} agent(s): ${agents.join(", ")}`);try{let{syncAgentDirectory:syncAgentDirectory2}=await Promise.resolve().then(() => (init_agent_sync(),exports_agent_sync)),result2=await syncAgentDirectory2(workspaceRoot);if(result2.registered.length>0)console.log(` Registered: ${result2.registered.join(", ")}`);if(result2.updated.length>0)console.log(` Updated: ${result2.updated.join(", ")}`)}catch{}}async function initWorkspace(){let cwd=process.cwd(),existing=findWorkspace(cwd);if(existing){if(console.log(`Already inside workspace: ${existing.root}`),await maybeBootstrapDefaultAgent(existing.root))await syncWorkspaceAgents(existing.root);return}let genieDir=join53(cwd,".genie");mkdirSync20(genieDir,{recursive:!0});let pgUrl=detectPgUrl(),config={name:basename9(cwd),pgUrl,agents:{defaults:{}},tmux:{socket:"genie"},sdk:{}};writeFileSync22(join53(genieDir,"workspace.json"),`${JSON.stringify(config,null,2)}
3298
+ `,"utf-8")}function refreshPending(workspaceRoot,discovered){let existing=loadPending(workspaceRoot),dismissedSet=new Set(existing.agents.filter((a)=>a.dismissed).map((a)=>a.path)),now=new Date().toISOString(),store={agents:discovered.map((d)=>({name:d.name,path:d.path,relativePath:d.relativePath,isSubAgent:d.isSubAgent,parentName:d.parentName,discoveredAt:existing.agents.find((a)=>a.path===d.path)?.discoveredAt??now,dismissed:dismissedSet.has(d.path)}))};return savePending(workspaceRoot,store),store}function listPending(workspaceRoot){return loadPending(workspaceRoot).agents.filter((a)=>!a.dismissed)}function removePending(workspaceRoot,agentPath){let store=loadPending(workspaceRoot),idx=store.agents.findIndex((a)=>a.path===agentPath);if(idx===-1)return!1;return store.agents.splice(idx,1),savePending(workspaceRoot,store),!0}init_workspace();init_templates();function detectPgUrl(){if(process.env.DATABASE_URL)return process.env.DATABASE_URL;if(process.env.PG_URL)return process.env.PG_URL;try{let{execSync:execSync15}=__require("child_process"),match=execSync15("pgrep -af pgserve 2>/dev/null",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).match(/postgres(?:ql)?:\/\/[^\s]+/);if(match)return match[0]}catch{}return}async function ensureSetupCompleteForInit(){let{isSetupComplete:isSetupComplete2}=await Promise.resolve().then(() => (init_genie_config2(),exports_genie_config));if(isSetupComplete2())return;console.log("Genie setup not complete. Running `genie setup` before continuing...");let{setupCommand:setupCommand2}=await Promise.resolve().then(() => (init_setup(),exports_setup));await setupCommand2()}function scaffoldAgentInWorkspace(workspaceRoot,name,agentsDir){let baseDir=agentsDir??join53(workspaceRoot,"agents"),agentDir=join53(baseDir,name);if(existsSync44(agentDir))throw Error(`Agent directory already exists: ${agentDir}`);mkdirSync20(agentDir,{recursive:!0}),mkdirSync20(join53(agentDir,"brain","memory"),{recursive:!0}),mkdirSync20(join53(agentDir,".claude"),{recursive:!0});let workspaceDefaults;try{workspaceDefaults=getWorkspaceConfig(workspaceRoot).agents?.defaults}catch{}scaffoldAgentFiles(agentDir,name,workspaceDefaults);let settings={agentName:name,autoMemoryEnabled:!0,autoMemoryDirectory:"./brain/memory"};writeFileSync22(join53(agentDir,".claude","settings.local.json"),`${JSON.stringify(settings,null,2)}
3299
+ `),writeFileSync22(join53(agentDir,"brain","memory","MEMORY.md"),`# Memory Index
3300
+
3301
+ _This file is maintained by the auto-memory system. New memories are added automatically._
3302
+ `);let reposTarget=join53(workspaceRoot,"repos");if(existsSync44(reposTarget))try{symlinkSync(reposTarget,join53(agentDir,"repos"))}catch{}if(console.log(`Agent scaffolded: agents/${name}/`),console.log(" AGENTS.md, SOUL.md, HEARTBEAT.md"),console.log(" brain/memory/MEMORY.md (seeded)"),console.log(" .claude/settings.local.json (auto-memory enabled)"),existsSync44(join53(agentDir,"repos")))console.log(" repos -> ../repos (symlink)")}async function maybeBootstrapDefaultAgent(workspaceRoot){if(scanAgents2(workspaceRoot).length>0)return!1;if(!await esm_default4({message:"No agent found in this workspace. Scaffold the default `genie` agent now?",default:!0}))return console.log(" Skipped default agent bootstrap. Run `genie init agent genie` later."),!1;return scaffoldAgentInWorkspace(workspaceRoot,"genie"),!0}async function syncWorkspaceAgents(workspaceRoot){let agents=scanAgents2(workspaceRoot);if(agents.length===0)return;console.log(` Found ${agents.length} agent(s): ${agents.join(", ")}`);try{let{syncAgentDirectory:syncAgentDirectory2}=await Promise.resolve().then(() => (init_agent_sync(),exports_agent_sync)),result2=await syncAgentDirectory2(workspaceRoot);if(result2.registered.length>0)console.log(` Registered: ${result2.registered.join(", ")}`);if(result2.updated.length>0)console.log(` Updated: ${result2.updated.join(", ")}`)}catch{}}async function initWorkspace(){let cwd=process.cwd(),existing=findWorkspace(cwd);if(existing){if(console.log(`Already inside workspace: ${existing.root}`),await maybeBootstrapDefaultAgent(existing.root))await syncWorkspaceAgents(existing.root);return}let genieDir=join53(cwd,".genie");mkdirSync20(genieDir,{recursive:!0});let pgUrl=detectPgUrl(),config={name:basename9(cwd),pgUrl,agents:{defaults:{}},tmux:{socket:"genie"},sdk:{}};writeFileSync22(join53(genieDir,"workspace.json"),`${JSON.stringify(config,null,2)}
3282
3303
  `);let genieignorePath=join53(cwd,".genieignore");if(!existsSync44(genieignorePath))writeFileSync22(genieignorePath,GENIEIGNORE_DEFAULTS,"utf-8"),console.log(" Created .genieignore");if(console.log(`Workspace created: ${cwd}`),pgUrl)console.log(` pgUrl: ${pgUrl}`);await maybeBootstrapDefaultAgent(cwd),await syncWorkspaceAgents(cwd),await runPostInitFlow(cwd,config)}async function runPostInitFlow(workspaceRoot,config){if(!isInteractive())return;let discovered=await discoverExternalAgents(workspaceRoot);refreshPending(workspaceRoot,discovered);let pending=listPending(workspaceRoot),ctx={workspaceRoot,workspaceName:basename9(workspaceRoot),config,discovered,pending,canonicalAgentCount:scanAgents2(workspaceRoot).length},result2=await runMiniWizard(ctx);if(result2.importedAgents.length>0){let toImport=pending.filter((p)=>result2.importedAgents.includes(p.name)),discoveredToImport=toImport.map((p)=>({name:p.name,path:p.path,relativePath:p.relativePath,isSubAgent:p.isSubAgent,parentName:p.parentName})),importResult=importAgents(workspaceRoot,discoveredToImport);for(let name of importResult.imported){let agent=toImport.find((a)=>a.name===name);if(agent)removePending(workspaceRoot,agent.path);console.log(` Imported: ${name}`)}for(let err of importResult.errors)console.error(` Import failed (${err.name}): ${err.error}`);await syncWorkspaceAgents(workspaceRoot)}}function resolveAgentsDir(wsRoot,dirOption){if(dirOption)return resolve8(dirOption);let cwd=process.cwd(),rel=relative3(wsRoot,cwd);if(rel.startsWith(".."))return join53(wsRoot,"agents");let segments=rel===""?[]:rel.split(sep2),idx=segments.indexOf("agents");if(idx===-1)return join53(wsRoot,"agents");return join53(wsRoot,...segments.slice(0,idx+1))}async function initAgent(name,options){if(!name||/[\/\\]/.test(name)||name==="."||name===".."||name.includes(".."))console.error("Error: Agent name must not contain path separators or traversal sequences."),process.exit(1);let cwd=process.cwd(),ws=findWorkspace(cwd);if(!ws)console.error("Error: Not in a genie workspace. Run `genie init` first."),process.exit(1);let agentsDir=resolveAgentsDir(ws.root,options.dir);try{scaffoldAgentInWorkspace(ws.root,name,agentsDir)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}function registerInitCommands(program2){program2.command("init").description("Initialize a genie workspace").action(async()=>{await ensureSetupCompleteForInit(),await initWorkspace()}).command("agent <name>").description("Scaffold a new agent in the workspace").option("--dir <path>","Target directory for agent (default: CWD if inside agents/, else workspace agents/)").action(async(name,options)=>{await ensureSetupCompleteForInit(),await initAgent(name,options)})}init_db();init_runtime_events();init_term_format();function parseSince2(since){let match=since.match(/^(\d+)([smhd])$/);if(!match)return since;let amount=Number.parseInt(match[1],10),unit=match[2],ms={s:1000,m:60000,h:3600000,d:86400000}[unit]??3600000;return new Date(Date.now()-amount*ms).toISOString()}async function metricsNowCommand(options){if(!await isAvailable())console.error("Database not available."),process.exit(1);let sql=await getConnection(),snapshots=await sql`SELECT * FROM machine_snapshots ORDER BY created_at DESC LIMIT 1`,agentCount=await sql`SELECT count(DISTINCT agent_id)::int as cnt FROM executors WHERE state NOT IN ('done', 'error', 'terminated')`,teamCount=await sql`SELECT count(*)::int as cnt FROM teams WHERE status = 'in_progress'`,snapshot=snapshots[0]??{},eventMetrics=getEventMetrics(),data={active_workers:snapshot.active_workers??agentCount[0]?.cnt??0,active_teams:snapshot.active_teams??teamCount[0]?.cnt??0,tmux_sessions:snapshot.tmux_sessions??0,cpu_percent:snapshot.cpu_percent??null,memory_mb:snapshot.memory_mb??null,snapshot_at:snapshot.created_at?new Date(snapshot.created_at).toISOString():null,events_emitted:eventMetrics.eventsEmitted,events_failed:eventMetrics.eventsFailed,last_emit_duration_ms:eventMetrics.lastEmitDuration,circuit_state:eventMetrics.circuitState};if(options.json){console.log(JSON.stringify(data,null,2));return}if(console.log("Machine State:"),console.log(` Workers: ${data.active_workers}`),console.log(` Teams: ${data.active_teams}`),console.log(` Tmux: ${data.tmux_sessions} sessions`),data.cpu_percent!==null)console.log(` CPU: ${data.cpu_percent}%`);if(data.memory_mb!==null)console.log(` Memory: ${data.memory_mb} MB`);if(data.snapshot_at)console.log(` As of: ${formatRelativeTimestamp(data.snapshot_at)}`);console.log(`
3283
3304
  Event Throughput:`),console.log(` Emitted: ${data.events_emitted}`),console.log(` Failed: ${data.events_failed}`),console.log(` Last ms: ${data.last_emit_duration_ms}`),console.log(` Circuit: ${data.circuit_state}`)}async function metricsHistoryCommand(options){if(!await isAvailable())console.error("Database not available."),process.exit(1);let sql=await getConnection(),sinceTs=parseSince2(options.since??"1h"),rows=await sql`
3284
3305
  SELECT active_workers, active_teams, tmux_sessions, cpu_percent, memory_mb, created_at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260410.1",
3
+ "version": "4.260410.2",
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.260410.1",
3
+ "version": "4.260410.2",
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.260410.1",
3
+ "version": "4.260410.2",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -521,7 +521,7 @@ async function executeBrainCommand(args: string[]): Promise<void> {
521
521
  export function registerBrainCommands(program: Command): void {
522
522
  const brain = program
523
523
  .command('brain')
524
- .description('Knowledge graph engine (enterprise)')
524
+ .description('Knowledge graph engine (enterprise) — forwards unknown subcommands to @khal-os/brain')
525
525
  .allowUnknownOption()
526
526
  .allowExcessArguments()
527
527
  .action(async (_options: Record<string, unknown>, cmd: Command) => {
@@ -533,7 +533,29 @@ export function registerBrainCommands(program: Command): void {
533
533
  return;
534
534
  }
535
535
  await executeBrainCommand(args);
536
- });
536
+ })
537
+ .addHelpText(
538
+ 'after',
539
+ `
540
+ Forwarded commands (require @khal-os/brain installed):
541
+ status Show running brain server status
542
+ health Show brain health score
543
+ init Initialize a new brain vault
544
+ search <query> Search the brain knowledge graph
545
+ ingest <path> Ingest files into the brain
546
+ analyze <path> Analyze a file against the brain
547
+ config Manage brain configuration
548
+ mount/unmount Mount brains
549
+ graph Explore the knowledge graph
550
+ traces View reasoning traces
551
+
552
+ Examples:
553
+ $ genie brain status
554
+ $ genie brain search "how does login work"
555
+ $ genie brain init --name my-brain --path ./brain
556
+
557
+ Install brain: genie brain install`,
558
+ );
537
559
 
538
560
  brain
539
561
  .command('install')
@@ -1,8 +1,9 @@
1
1
  import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
2
- import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
2
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  import { scaffoldAgentFiles } from '../templates/index.js';
6
+ import { scaffoldAgentInWorkspace } from './init.js';
6
7
 
7
8
  let testDir: string;
8
9
 
@@ -37,3 +38,28 @@ describe('scaffoldAgentFiles', () => {
37
38
  expect(content).not.toMatch(/^name:/m);
38
39
  });
39
40
  });
41
+
42
+ describe('scaffoldAgentInWorkspace', () => {
43
+ test('writes settings.local.json with auto-memory enabled and seeds MEMORY.md', () => {
44
+ // Minimal workspace.json so getWorkspaceConfig doesn't crash
45
+ mkdirSync(join(testDir, '.genie'), { recursive: true });
46
+ writeFileSync(join(testDir, '.genie', 'workspace.json'), `${JSON.stringify({ name: 'test-ws' }, null, 2)}\n`);
47
+
48
+ scaffoldAgentInWorkspace(testDir, 'atlas');
49
+
50
+ const settingsPath = join(testDir, 'agents', 'atlas', '.claude', 'settings.local.json');
51
+ expect(existsSync(settingsPath)).toBe(true);
52
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
53
+ expect(settings).toEqual({
54
+ agentName: 'atlas',
55
+ autoMemoryEnabled: true,
56
+ autoMemoryDirectory: './brain/memory',
57
+ });
58
+
59
+ const memoryPath = join(testDir, 'agents', 'atlas', 'brain', 'memory', 'MEMORY.md');
60
+ expect(existsSync(memoryPath)).toBe(true);
61
+ const memory = readFileSync(memoryPath, 'utf-8');
62
+ expect(memory).toContain('# Memory Index');
63
+ expect(memory).toContain('auto-memory system');
64
+ });
65
+ });
@@ -43,7 +43,7 @@ async function ensureSetupCompleteForInit(): Promise<void> {
43
43
  await setupCommand();
44
44
  }
45
45
 
46
- function scaffoldAgentInWorkspace(workspaceRoot: string, name: string, agentsDir?: string): void {
46
+ export function scaffoldAgentInWorkspace(workspaceRoot: string, name: string, agentsDir?: string): void {
47
47
  const baseDir = agentsDir ?? join(workspaceRoot, 'agents');
48
48
  const agentDir = join(baseDir, name);
49
49
  if (existsSync(agentDir)) {
@@ -64,7 +64,18 @@ function scaffoldAgentInWorkspace(workspaceRoot: string, name: string, agentsDir
64
64
  }
65
65
 
66
66
  scaffoldAgentFiles(agentDir, name, workspaceDefaults);
67
- writeFileSync(join(agentDir, '.claude', 'settings.local.json'), `${JSON.stringify({ agentName: name }, null, 2)}\n`);
67
+
68
+ const settings = {
69
+ agentName: name,
70
+ autoMemoryEnabled: true,
71
+ autoMemoryDirectory: './brain/memory',
72
+ };
73
+ writeFileSync(join(agentDir, '.claude', 'settings.local.json'), `${JSON.stringify(settings, null, 2)}\n`);
74
+
75
+ writeFileSync(
76
+ join(agentDir, 'brain', 'memory', 'MEMORY.md'),
77
+ '# Memory Index\n\n_This file is maintained by the auto-memory system. New memories are added automatically._\n',
78
+ );
68
79
 
69
80
  const reposTarget = join(workspaceRoot, 'repos');
70
81
  if (existsSync(reposTarget)) {
@@ -77,8 +88,8 @@ function scaffoldAgentInWorkspace(workspaceRoot: string, name: string, agentsDir
77
88
 
78
89
  console.log(`Agent scaffolded: agents/${name}/`);
79
90
  console.log(' AGENTS.md, SOUL.md, HEARTBEAT.md');
80
- console.log(' brain/memory/');
81
- console.log(' .claude/settings.local.json');
91
+ console.log(' brain/memory/MEMORY.md (seeded)');
92
+ console.log(' .claude/settings.local.json (auto-memory enabled)');
82
93
  if (existsSync(join(agentDir, 'repos'))) {
83
94
  console.log(' repos -> ../repos (symlink)');
84
95
  }