@automagik/genie 4.260402.6 → 4.260402.8

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.260402.6",
13
+ "version": "4.260402.8",
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
@@ -2292,7 +2292,7 @@ Board: ${board.name} (${board.id})`),board.description)console.log(`Description:
2292
2292
  Columns:`);for(let i2=0;i2<sorted.length;i2++){let c=sorted[i2],count=countByColumn.get(c.name)??countByColumn.get(c.id)??0,gate=` [gate: ${c.gate}]`,action=c.action?` (action: ${c.action})`:"";console.log(` ${i2+1}. ${c.label??c.name}${gate}${action} \u2014 ${count} task${count===1?"":"s"}`)}console.log("")}function buildColumnUpdates(options){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;return updates}async function handleBoardEdit(name,options){let bs=await getBoardService(),board=await resolveBoard(name,options.project);if(options.column){let col=board.columns.find((c)=>c.name===options.column||c.label===options.column);if(!col)throw Error(`Column not found: ${options.column}`);let updates=buildColumnUpdates(options);if(!await bs.updateColumn(board.id,col.id,updates))throw Error(`Failed to update column: ${options.column}`);console.log(`Updated column "${options.column}" on board "${board.name}".`);return}let boardUpdates={};if(options.name)boardUpdates.name=options.name;if(options.description)boardUpdates.description=options.description;if(Object.keys(boardUpdates).length===0)console.error("Error: No updates specified. Use --column, --name, or --description."),process.exit(1);let updated=await bs.updateBoard(board.id,boardUpdates);if(!updated)throw Error(`Failed to update board: ${name}`);console.log(`Updated board "${updated.name}" (${updated.id}).`)}async function handleBoardDelete(name,options){let bs=await getBoardService(),board=await resolveBoard(name,options.project);if(!options.force)console.log(`Deleting board "${board.name}" (${board.id})...`);if(!await bs.deleteBoard(board.id))throw Error(`Failed to delete board: ${name}`);console.log(`Deleted board "${board.name}" (${board.id}).`)}async function handleBoardColumns(name,options){let board=await resolveBoard(name,options.project);if(options.json){console.log(JSON.stringify(board.columns,null,2));return}printColumnPipeline(board.columns,`Board: ${board.name} (${board.columns.length} columns)`)}async function handleBoardUse(name,options){let board=await resolveBoard(name,options.project),repoRoot=execSync8("git rev-parse --show-toplevel",{encoding:"utf-8"}).trim(),genieDir=join36(repoRoot,".genie"),configPath2=join36(genieDir,"config.json");if(!existsSync27(genieDir))mkdirSync12(genieDir,{recursive:!0});let config={};if(existsSync27(configPath2))try{config=JSON.parse(readFileSync14(configPath2,"utf-8"))}catch{}config.activeBoard=board.id,writeFileSync11(configPath2,`${JSON.stringify(config,null,2)}
2293
2293
  `),console.log(`Active board set to "${board.name}" (${board.id})`)}async function handleBoardExport(name,options){let bs=await getBoardService(),board=await resolveBoard(name,options.project),exported=await bs.exportBoard(board.id),json2=JSON.stringify(exported,null,2);if(options.output){let dir=dirname6(options.output);if(!existsSync27(dir))mkdirSync12(dir,{recursive:!0});writeFileSync11(options.output,`${json2}
2294
2294
  `),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),result=await reconcileBoard2(board.id);if(options.json){console.log(JSON.stringify(result,null,2));return}if(result.fixed===0&&result.orphaned===0){console.log(`Board "${board.name}": all tasks have valid column_ids.`);return}if(console.log(`Board "${board.name}" reconciliation:`),console.log(` Fixed: ${result.fixed} task${result.fixed===1?"":"s"}`),result.orphaned>0){let count=result.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=readFileSync14(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(`
2295
- 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 execSync9}from"child_process";var BRAIN_PKG="@automagik/genie-brain",BRAIN_REPO="github:automagik-dev/genie-brain";async function installBrain(){console.log(""),console.log(" Installing genie-brain from GitHub (enterprise)..."),console.log(""),console.log(" Source: https://github.com/automagik-dev/genie-brain"),console.log(" Requires: GitHub org membership (automagik-dev)"),console.log("");try{execSync9(`bun add ${BRAIN_REPO}`,{stdio:"inherit"}),console.log(""),console.log(" \u2713 Brain installed from GitHub."),console.log("");try{let brain=await import(BRAIN_PKG);if(brain.runAllMigrations)console.log(" Running brain migrations..."),await brain.runAllMigrations(),console.log(" \u2713 Brain tables created in Postgres.")}catch{console.log(" \u26A0 Auto-migration skipped. Run: genie brain migrate")}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(" \u2717 Access denied. Brain is enterprise-only."),console.log(""),console.log(" You need:"),console.log(" 1. Membership in the automagik-dev GitHub org"),console.log(" 2. SSH key or GH token configured for git"),console.log(""),console.log(" Manual install:"),console.log(` bun add ${BRAIN_REPO}`),console.log("");else console.error(` \u2717 Install failed: ${msg}`),console.log(""),console.log(" Manual install:"),console.log(` bun add ${BRAIN_REPO}`),console.log("");return!1}}function uninstallBrain(){try{execSync9(`bun remove ${BRAIN_PKG}`,{stdio:"inherit"}),console.log(" \u2713 Brain uninstalled.")}catch{console.error(" Uninstall failed. Manual: bun remove @automagik/genie-brain")}}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 (automagik-dev)."),console.log("")}async function executeBrainCommand(args){try{let brain=await import(BRAIN_PKG);if(brain.execute)await brain.execute(args);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){program2.command("brain").description("Knowledge graph engine (enterprise)").allowUnknownOption().allowExcessArguments().action(async(_options,cmd)=>{let args=cmd.args;if(args[0]==="install"){await installBrain();return}if(args[0]==="uninstall"){uninstallBrain();return}await executeBrainCommand(args)})}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 existsSync28,mkdirSync as mkdirSync13,readFileSync as readFileSync15,unlinkSync as unlinkSync7,writeFileSync as writeFileSync12}from"fs";import{homedir as homedir25}from"os";import{join as join37}from"path";function genieHome3(){return process.env.GENIE_HOME??join37(homedir25(),".genie")}function pidFilePath(){return join37(genieHome3(),"scheduler.pid")}function logFilePath(){return join37(genieHome3(),"logs","scheduler.log")}function systemdDir(){return join37(homedir25(),".config","systemd","user")}function systemdUnitPath(){return join37(systemdDir(),"genie-scheduler.service")}function readPid(){let path2=pidFilePath();if(!existsSync28(path2))return null;let raw=readFileSync15(path2,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function writePid(pid){let dir=genieHome3();mkdirSync13(dir,{recursive:!0}),writeFileSync12(pidFilePath(),String(pid),"utf-8")}function removePid(){let path2=pidFilePath();if(existsSync28(path2))try{unlinkSync7(path2)}catch{}}function isProcessAlive2(pid){try{return process.kill(pid,0),!0}catch{return!1}}function generateSystemdUnit(){let genieBin=process.argv[1]??"genie";return`[Unit]
2295
+ 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 execSync9}from"child_process";var BRAIN_PKG="@automagik/genie-brain",BRAIN_REPO="github:automagik-dev/genie-brain";async function installBrain(){console.log(""),console.log(" Installing genie-brain from GitHub (enterprise)..."),console.log(""),console.log(" Source: https://github.com/automagik-dev/genie-brain"),console.log(" Requires: GitHub org membership (automagik-dev)"),console.log("");try{try{execSync9("gh auth token",{stdio:"pipe"})}catch{return console.error(" \u2717 GitHub CLI not authenticated. Run: gh auth login"),!1}let brainDir="node_modules/@automagik/genie-brain";execSync9(`rm -rf "${brainDir}"`,{stdio:"pipe"}),execSync9("mkdir -p node_modules/@automagik",{stdio:"pipe"}),execSync9(`gh repo clone automagik-dev/genie-brain "${brainDir}" -- --depth 1`,{stdio:"inherit"}),execSync9("bun install",{cwd:brainDir,stdio:"inherit"}),execSync9("bun run build",{cwd:brainDir,stdio:"inherit"}),console.log(""),console.log(" \u2713 Brain installed from GitHub."),console.log("");try{let brain=await import(BRAIN_PKG);if(brain.runAllMigrations)console.log(" Running brain migrations..."),await brain.runAllMigrations(),console.log(" \u2713 Brain tables created in Postgres.")}catch{console.log(" \u26A0 Auto-migration skipped. Run: genie brain migrate")}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(" \u2717 Access denied. Brain is enterprise-only."),console.log(""),console.log(" You need:"),console.log(" 1. Membership in the automagik-dev GitHub org"),console.log(" 2. SSH key or GH token configured for git"),console.log(""),console.log(" Manual install:"),console.log(` bun add ${BRAIN_REPO}`),console.log("");else console.error(` \u2717 Install failed: ${msg}`),console.log(""),console.log(" Manual install:"),console.log(` bun add ${BRAIN_REPO}`),console.log("");return!1}}function uninstallBrain(){try{execSync9('rm -rf "node_modules/@automagik/genie-brain"',{stdio:"pipe"}),console.log(" \u2713 Brain uninstalled.")}catch{console.error(" Uninstall failed. Manual: rm -rf node_modules/@automagik/genie-brain")}}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 (automagik-dev)."),console.log("")}async function executeBrainCommand(args){try{let brain=await import(BRAIN_PKG);if(brain.execute)await brain.execute(args);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){program2.command("brain").description("Knowledge graph engine (enterprise)").allowUnknownOption().allowExcessArguments().action(async(_options,cmd)=>{let args=cmd.args;if(args[0]==="install"){await installBrain();return}if(args[0]==="uninstall"){uninstallBrain();return}await executeBrainCommand(args)})}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 existsSync28,mkdirSync as mkdirSync13,readFileSync as readFileSync15,unlinkSync as unlinkSync7,writeFileSync as writeFileSync12}from"fs";import{homedir as homedir25}from"os";import{join as join37}from"path";function genieHome3(){return process.env.GENIE_HOME??join37(homedir25(),".genie")}function pidFilePath(){return join37(genieHome3(),"scheduler.pid")}function logFilePath(){return join37(genieHome3(),"logs","scheduler.log")}function systemdDir(){return join37(homedir25(),".config","systemd","user")}function systemdUnitPath(){return join37(systemdDir(),"genie-scheduler.service")}function readPid(){let path2=pidFilePath();if(!existsSync28(path2))return null;let raw=readFileSync15(path2,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function writePid(pid){let dir=genieHome3();mkdirSync13(dir,{recursive:!0}),writeFileSync12(pidFilePath(),String(pid),"utf-8")}function removePid(){let path2=pidFilePath();if(existsSync28(path2))try{unlinkSync7(path2)}catch{}}function isProcessAlive2(pid){try{return process.kill(pid,0),!0}catch{return!1}}function generateSystemdUnit(){let genieBin=process.argv[1]??"genie";return`[Unit]
2296
2296
  Description=Genie Scheduler Daemon
2297
2297
  Documentation=https://github.com/automagik/genie
2298
2298
  After=network.target
@@ -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.260402.6",
5
+ "version": "4.260402.8",
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.260402.6",
3
+ "version": "4.260402.8",
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.260402.6",
3
+ "version": "4.260402.8",
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.260402.6",
3
+ "version": "4.260402.8",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -1,190 +1,131 @@
1
1
  ---
2
2
  name: brain
3
- description: "Obsidian-style knowledge vaultstore, search, and retrieve agent knowledge across sessions via notesmd-cli."
3
+ description: "Knowledge graph enginesearch, analyze, and manage AI agent brains with confidence scoring, autoschema, and multimodal support."
4
4
  ---
5
5
 
6
- # /brain — Agent Knowledge Vault
6
+ # /brain — Knowledge Graph Engine
7
7
 
8
- Persistent long-term memory for agents. Knowledge is stored in `brain/`, searched before answering, and written back every session.
9
-
10
- ## Brain vs Memory
11
-
12
- These are **different tools for different purposes**:
13
-
14
- | | **Brain** (this skill) | **Memory** (Claude native) |
15
- |---|---|---|
16
- | **What** | Context graph — entities, relationships, domain knowledge | Behavioral learnings — feedback, decisions, user preferences |
17
- | **Tool** | `notesmd-cli` (Obsidian-style vault) | `.claude/memory/` files with YAML frontmatter |
18
- | **When** | Domain intel, playbooks, company/person context, session logs | Corrections, conventions, project rules, user profile |
19
- | **Updated by** | `/brain` (this skill) | `/learn` skill, auto memory system |
20
- | **Format** | Markdown notes in `brain/` directory | Typed memory files (user, feedback, project, reference) |
21
-
22
- **Rule of thumb:** If it's *knowledge about the world* → brain. If it's *how the agent should behave* → memory.
8
+ Search, analyze, and manage knowledge brains powered by genie-brain. Brains are Postgres-backed, Obsidian-compatible knowledge vaults with BM25 + vector search, confidence scoring, and agentic autoschema.
23
9
 
24
10
  ## When to Use
25
- - Agent needs to recall prior session context, decisions, or intel
26
- - New intel (person, company, deal) is discovered mid-session
27
- - A playbook pattern is confirmed or updated
28
- - Provisioning a new agent with a knowledge vault
29
-
30
- ## Flow
31
-
32
- ### Session Start (mandatory)
11
+ - Search for knowledge before answering a question
12
+ - Check what the brain knows (and doesn't know) about a topic
13
+ - Analyze content with deep reasoning
14
+ - Ingest new content into the brain
15
+ - Check brain health and coverage gaps
33
16
 
34
- 1. Read the conversation opener. Derive 2-3 search terms from the topic.
35
- 2. `notesmd-cli search-content "<term>"` for each term.
36
- 3. `notesmd-cli print "<note-name>"` for relevant hits.
37
- 4. Only then begin forming a response.
38
- 5. Fall back to external research (web search, browser) only if brain is insufficient.
17
+ ## Prerequisites
39
18
 
40
- On topic shift mid-conversation: re-run `notesmd-cli search-content "<new-topic>"` before answering.
19
+ Brain must be installed: `genie brain install`
20
+ If not installed, guide the user to run the install command.
41
21
 
42
- ### Write-Back (3 mandatory triggers)
43
-
44
- ### Trigger 1: Session End (always)
22
+ ## Commands
45
23
 
24
+ ### Search — find knowledge with confidence
46
25
  ```bash
47
- notesmd-cli daily
48
- # Write: discussion summary, decisions, intel discovered, actions taken
26
+ genie brain search "<query>" --brain <id>
49
27
  ```
28
+ Returns ranked results with confidence level (FULL/HIGH/PARTIAL/LOW/NONE).
29
+ - FULL/HIGH → brain knows this well, use the results
30
+ - PARTIAL → brain has some info, may need to supplement
31
+ - LOW/NONE → gap detected, go external or research
50
32
 
51
- ### Trigger 2: New Intel Discovered (immediately)
33
+ **Always search before answering domain questions.** If confidence is LOW/NONE, say so — don't hallucinate.
52
34
 
35
+ ### Health — check brain quality
53
36
  ```bash
54
- notesmd-cli create "Intelligence/<person-or-company-name>"
55
- # Write now — do not wait until session end
37
+ genie brain health --path <brain-path> [--fix]
56
38
  ```
39
+ 7-dimension score: Schema, Freshness, Coverage, Connections, Content, Conflicts, Acceptance.
40
+ `--fix` auto-repairs: adds missing dates, converts tags, generates MOCs.
57
41
 
58
- ### Trigger 3: Playbook Pattern Updated (immediately)
59
-
42
+ ### Status brain dashboard
60
43
  ```bash
61
- notesmd-cli print "Playbooks/<playbook-name>"
62
- # Edit: add confirmed pattern, new rule, exception, or example
44
+ genie brain status
63
45
  ```
46
+ Lists all registered brains with file counts, health, and query stats.
64
47
 
65
- ## Commands
66
-
67
- | Command | Purpose |
68
- |---------|---------|
69
- | `notesmd-cli search-content "<keyword>"` | Search vault content (use BEFORE answering domain questions) |
70
- | `notesmd-cli print "<note-name>"` | Read a specific note |
71
- | `notesmd-cli daily` | Open/create today's session log in `Daily/` |
72
- | `notesmd-cli create "<name>"` | Create a note (use folder prefix: `"Intelligence/Name"`) |
73
- | `notesmd-cli list` | Browse full vault structure |
74
- | `notesmd-cli set-default --vault <path>` | Configure vault path (one-time setup) |
75
-
76
- ## Installation (Auto-Detect)
77
-
78
- On first use, check if `notesmd-cli` is available:
79
-
48
+ ### Init — create a new brain
80
49
  ```bash
81
- command -v notesmd-cli >/dev/null 2>&1 && echo "installed" || echo "missing"
50
+ genie brain init --name <name> --path <path> [--type gtm|pm|engineering|research|personal]
82
51
  ```
52
+ Creates an Obsidian-compatible vault with autoschema. Brain types provide base scaffolding.
83
53
 
84
- **If missing**, offer to install from https://github.com/Yakitrak/notesmd-cli:
85
-
54
+ ### Process ingest new content
86
55
  ```bash
87
- # macOS (Homebrew)
88
- brew install yakitrak/yakitrak/notesmd-cli
89
-
90
- # Linux / manual
91
- # Download the latest release binary from:
92
- # https://github.com/Yakitrak/notesmd-cli/releases
93
- # Place in /usr/local/bin/notesmd-cli and chmod +x
94
-
95
- # Or use the bundled install script (if available)
96
- bash skills/brain/scripts/install-notesmd.sh --vault ./brain
56
+ genie brain process --brain <id> --path <path>
97
57
  ```
98
-
99
- After install, configure the vault:
100
-
58
+ Processes files in `to_process/`:
59
+ - Markdown classified and moved to decided folder
60
+ - Images → described via Gemini Vision → .desc.md
61
+ - Audio → transcribed → .transcript.md
62
+ - PDF → extracted → .extracted.md
63
+ - Code → symbols extracted → .symbols.md
64
+
65
+ ### Analyze — deep reasoning via rlmx
101
66
  ```bash
102
- notesmd-cli set-default --vault ./brain/
67
+ genie brain analyze "<query>" --brain <id> --path <path>
103
68
  ```
69
+ Uses rlmx reasoning engine for deep analysis with file references.
104
70
 
105
- If the user declines installation, skip brain operations gracefully and note that `/brain` requires `notesmd-cli`.
106
-
107
- ## Provisioning a New Agent Brain
108
-
71
+ ### Link discover connections
109
72
  ```bash
110
- mkdir -p brain/{_Templates,Company,Daily,Domains,Intelligence,Playbooks}
111
- notesmd-cli set-default --vault ./brain/
112
- cp skills/brain/templates/*.md brain/_Templates/
113
- notesmd-cli list
114
- ```
115
-
116
- Then add the protocol snippets below to the agent's config files.
117
-
118
- ### CLAUDE.md Template Block
119
-
120
- ```markdown
121
- ## FIRST THING YOU DO (every session)
122
-
123
- 1. Read the conversation opener to understand the topic
124
- 2. Derive 2-3 search terms
125
- 3. Run: `notesmd-cli search-content "<term>"` for each
126
- 4. If results found: `notesmd-cli print "<note-name>"`
127
- 5. Only THEN begin forming your response
128
- 6. If brain is insufficient: use web search as fallback
129
-
130
- ## WHEN TOPIC SHIFTS
131
-
132
- Re-run `notesmd-cli search-content "<new-topic>"` before answering.
133
-
134
- ## AT SESSION END (mandatory)
135
-
136
- Run `notesmd-cli daily`. Log: discussion, decisions, intel, actions.
137
-
138
- ## WRITE IMMEDIATELY WHEN
139
-
140
- - New intel discovered -> `notesmd-cli create "Intelligence/<name>"`
141
- - Playbook updated -> edit relevant `Playbooks/` note
142
- - Domain insight validated -> update relevant `Domains/` note
143
- ```
144
-
145
- ### AGENTS.md Protocol Snippet
146
-
147
- ```markdown
148
- ## Brain Protocol
149
-
150
- ### Session Start
151
- - Derive 2-3 keywords from topic
152
- - `notesmd-cli search-content "<keyword>"` for each
153
- - `notesmd-cli print "<note-name>"` for relevant results
154
- - External research only when brain is insufficient
155
-
156
- ### Mid-Conversation
157
- - Re-scan on topic shift: `notesmd-cli search-content "<new-topic>"`
158
-
159
- ### Session End (mandatory)
160
- - `notesmd-cli daily` — log: discussion, decisions, intel, actions
161
-
162
- ### Write Immediately When
163
- - New intel -> `notesmd-cli create "Intelligence/<name>"`
164
- - Playbook updated -> edit `Playbooks/` note now
165
- ```
166
-
167
- ## Auto-Sync (optional)
168
-
169
- Push brain changes to GitHub via inotifywait + cron:
170
-
171
- ```bash
172
- # Watcher (scripts/brain-sync.sh)
173
- while inotifywait -r -e modify,create,delete ./brain/ 2>/dev/null; do
174
- cd brain && git add -A && \
175
- git commit -m "brain: auto-sync $(date +%Y-%m-%d_%H:%M)" && \
176
- git push && cd ..
177
- done
178
-
179
- # Cron fallback (every 30 min)
180
- # */30 * * * * cd /path/to/workspace && bash scripts/brain-sync.sh >> logs/brain-sync.log 2>&1
73
+ genie brain link --brain <id>
181
74
  ```
75
+ Generates wikilinks from tag overlap and wikilink references.
76
+
77
+ ## How Agents Should Use This
78
+
79
+ ### Before answering domain questions:
80
+ 1. Search the brain: `genie brain search "<topic>" --brain <my-brain-id>`
81
+ 2. Check confidence level
82
+ 3. If FULL/HIGH → cite the results
83
+ 4. If PARTIAL → use results + note limitations
84
+ 5. If NONE → say "brain doesn't cover this" and research externally
85
+
86
+ ### After learning something new:
87
+ 1. Write a .md file with frontmatter to `brain/to_process/`
88
+ 2. Run `genie brain process` to classify and index it
89
+ 3. The brain grows over time
90
+
91
+ ### Session hygiene:
92
+ - Start: check `genie brain status` for brain health
93
+ - During: search brain before making claims
94
+ - End: write session learnings to brain
95
+
96
+ ## Brain Types
97
+
98
+ | Type | Use Case | Base Folders |
99
+ |------|----------|-------------|
100
+ | `gtm` | Marketing, competitive intel | Intelligence/, DevRel/, Company/ |
101
+ | `pm` | Product management | Backlog/, Roadmap/, Specs/ |
102
+ | `engineering` | Architecture, code | Architecture/, Decisions/, Runbooks/ |
103
+ | `research` | R&D, papers | Papers/, Notes/, Experiments/ |
104
+ | `personal` | Personal knowledge (PARA) | Projects/, Areas/, Resources/ |
105
+ | `generic` | Auto-decided by content | (autoschema decides) |
106
+
107
+ ## Confidence Levels
108
+
109
+ | Level | Meaning | Agent Action |
110
+ |-------|---------|-------------|
111
+ | **FULL** | Brain knows this well (3+ strong results) | Use directly, cite sources |
112
+ | **HIGH** | Good coverage (2+ results) | Use with confidence |
113
+ | **PARTIAL** | Some info available | Use + supplement if needed |
114
+ | **LOW** | Weak match | Go external, mention brain gap |
115
+ | **NONE** | Brain doesn't know this | Research externally, don't guess |
116
+
117
+ ## Available Brains on This Server
118
+
119
+ Run `genie brain status` to see all. Current brains include:
120
+ - **genie-gtm** — Marketing intelligence, competitors, DevRel
121
+ - **vegapunk** — R&D, architecture, code analysis
122
+ - **totvs** — Client project management
123
+ - **sofia** — Personal assistant knowledge
124
+ - **namastex-global** — Company-wide shared knowledge
182
125
 
183
126
  ## Rules
184
-
185
- - Local knowledge first. External research is fallback, never default.
186
- - Run startup search every session, no exceptions.
187
- - Write back on all 3 triggers. The brain goes stale if agents only read.
188
- - Never skip the daily log at session end.
189
- - Write intel immediately when discovered — do not batch until session end.
190
- - Templates live in `skills/brain/templates/`. Copy to `brain/_Templates/` during provisioning.
127
+ - **Search before claiming.** If the brain has an answer, use it.
128
+ - **Respect confidence.** NONE means NONE don't fabricate.
129
+ - **Write back.** If you learn something the brain should know, add it.
130
+ - **Use frontmatter.** All brain files need YAML frontmatter (type, tags, dates).
131
+ - **Keep it Obsidian-compatible.** Wikilinks, not regular links.
@@ -25,12 +25,26 @@ async function installBrain(): Promise<boolean> {
25
25
  console.log('');
26
26
 
27
27
  try {
28
- // Install directly from GitHubbun resolves git repos natively
29
- // Only people with repo access (SSH key or GH token) can install
30
- execSync(`bun add ${BRAIN_REPO}`, {
28
+ // Verify GitHub CLI is authenticated (no token extraction gh handles auth securely)
29
+ try {
30
+ execSync('gh auth token', { stdio: 'pipe' });
31
+ } catch {
32
+ console.error(' ✗ GitHub CLI not authenticated. Run: gh auth login');
33
+ return false;
34
+ }
35
+
36
+ // Clone brain repo using gh CLI (handles private repos without exposing tokens in process list)
37
+ const brainDir = 'node_modules/@automagik/genie-brain';
38
+ execSync(`rm -rf "${brainDir}"`, { stdio: 'pipe' });
39
+ execSync('mkdir -p node_modules/@automagik', { stdio: 'pipe' });
40
+ execSync(`gh repo clone automagik-dev/genie-brain "${brainDir}" -- --depth 1`, {
31
41
  stdio: 'inherit',
32
42
  });
33
43
 
44
+ // Install brain's deps + build
45
+ execSync('bun install', { cwd: brainDir, stdio: 'inherit' });
46
+ execSync('bun run build', { cwd: brainDir, stdio: 'inherit' });
47
+
34
48
  console.log('');
35
49
  console.log(' ✓ Brain installed from GitHub.');
36
50
  console.log('');
@@ -78,10 +92,11 @@ async function installBrain(): Promise<boolean> {
78
92
 
79
93
  function uninstallBrain(): void {
80
94
  try {
81
- execSync(`bun remove ${BRAIN_PKG}`, { stdio: 'inherit' });
95
+ const brainDir = 'node_modules/@automagik/genie-brain';
96
+ execSync(`rm -rf "${brainDir}"`, { stdio: 'pipe' });
82
97
  console.log(' ✓ Brain uninstalled.');
83
98
  } catch {
84
- console.error(' Uninstall failed. Manual: bun remove @automagik/genie-brain');
99
+ console.error(' Uninstall failed. Manual: rm -rf node_modules/@automagik/genie-brain');
85
100
  }
86
101
  }
87
102