@automagik/genie 0.260203.503 → 0.260203.629
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +11 -2
- package/dist/claudio.js +1 -1
- package/dist/genie.js +50 -45
- package/dist/term.js +1 -1
- package/install.sh +11 -0
- package/package.json +1 -1
- package/src/genie-commands/tui.ts +63 -0
- package/src/genie.ts +10 -0
- package/src/lib/version.ts +1 -1
package/dist/term.js
CHANGED
|
@@ -27,7 +27,7 @@ Expecting one of '${E.join("', '")}'`);let B=`${D}Help`;return this.on(B,($)=>{l
|
|
|
27
27
|
`)$+=EF(X)}}return $};BF.exports=(D,F,E)=>{return String(D).normalize().replace(/\r\n/g,`
|
|
28
28
|
`).split(`
|
|
29
29
|
`).map((B)=>qE(B,F,E)).join(`
|
|
30
|
-
`)}});var qF=c((ZQ,YF)=>{var _E=FD("stream");class JF extends _E{#E=null;constructor(D={}){super(D);this.writable=this.readable=!0,this.muted=!1,this.on("pipe",this._onpipe),this.replace=D.replace,this._prompt=D.prompt||null,this._hadControl=!1}#F(D,F){if(this._dest)return this._dest[D];if(this._src)return this._src[D];return F}#D(D,...F){if(typeof this._dest?.[D]==="function")this._dest[D](...F);if(typeof this._src?.[D]==="function")this._src[D](...F)}get isTTY(){if(this.#E!==null)return this.#E;return this.#F("isTTY",!1)}set isTTY(D){this.#E=D}get rows(){return this.#F("rows")}get columns(){return this.#F("columns")}mute(){this.muted=!0}unmute(){this.muted=!1}_onpipe(D){this._src=D}pipe(D,F){return this._dest=D,super.pipe(D,F)}pause(){if(this._src)return this._src.pause()}resume(){if(this._src)return this._src.resume()}write(D){if(this.muted){if(!this.replace)return!0;if(D.match(/^\u001b/)){if(D.indexOf(this._prompt)===0)D=D.slice(this._prompt.length),D=D.replace(/./g,this.replace),D=this._prompt+D;return this._hadControl=!0,this.emit("data",D)}else{if(this._prompt&&this._hadControl&&D.indexOf(this._prompt)===0)this._hadControl=!1,this.emit("data",this._prompt),D=D.slice(this._prompt.length);D=D.toString().replace(/./g,this.replace)}}this.emit("data",D)}end(D){if(this.muted)if(D&&this.replace)D=D.toString().replace(/./g,this.replace);else D=null;if(D)this.emit("data",D);this.emit("end")}destroy(...D){return this.#D("destroy",...D)}destroySoon(...D){return this.#D("destroySoon",...D)}close(...D){return this.#D("close",...D)}}YF.exports=JF});var g8=iD(h8(),1),{program:HB,createCommand:_B,createArgument:GB,createOption:WB,CommanderError:UB,InvalidArgumentError:AB,InvalidOptionArgumentError:CB,Command:l8,Argument:zB,Option:VB,Help:KB}=g8.default;var m8="0.260203.
|
|
30
|
+
`)}});var qF=c((ZQ,YF)=>{var _E=FD("stream");class JF extends _E{#E=null;constructor(D={}){super(D);this.writable=this.readable=!0,this.muted=!1,this.on("pipe",this._onpipe),this.replace=D.replace,this._prompt=D.prompt||null,this._hadControl=!1}#F(D,F){if(this._dest)return this._dest[D];if(this._src)return this._src[D];return F}#D(D,...F){if(typeof this._dest?.[D]==="function")this._dest[D](...F);if(typeof this._src?.[D]==="function")this._src[D](...F)}get isTTY(){if(this.#E!==null)return this.#E;return this.#F("isTTY",!1)}set isTTY(D){this.#E=D}get rows(){return this.#F("rows")}get columns(){return this.#F("columns")}mute(){this.muted=!0}unmute(){this.muted=!1}_onpipe(D){this._src=D}pipe(D,F){return this._dest=D,super.pipe(D,F)}pause(){if(this._src)return this._src.pause()}resume(){if(this._src)return this._src.resume()}write(D){if(this.muted){if(!this.replace)return!0;if(D.match(/^\u001b/)){if(D.indexOf(this._prompt)===0)D=D.slice(this._prompt.length),D=D.replace(/./g,this.replace),D=this._prompt+D;return this._hadControl=!0,this.emit("data",D)}else{if(this._prompt&&this._hadControl&&D.indexOf(this._prompt)===0)this._hadControl=!1,this.emit("data",this._prompt),D=D.slice(this._prompt.length);D=D.toString().replace(/./g,this.replace)}}this.emit("data",D)}end(D){if(this.muted)if(D&&this.replace)D=D.toString().replace(/./g,this.replace);else D=null;if(D)this.emit("data",D);this.emit("end")}destroy(...D){return this.#D("destroy",...D)}destroySoon(...D){return this.#D("destroySoon",...D)}close(...D){return this.#D("close",...D)}}YF.exports=JF});var g8=iD(h8(),1),{program:HB,createCommand:_B,createArgument:GB,createOption:WB,CommanderError:UB,InvalidArgumentError:AB,InvalidOptionArgumentError:CB,Command:l8,Argument:zB,Option:VB,Help:KB}=g8.default;var m8="0.260203.0629";import{join as X7}from"path";var r=[];for(let D=0;D<256;++D)r.push((D+256).toString(16).slice(1));function c8(D,F=0){return(r[D[F+0]]+r[D[F+1]]+r[D[F+2]]+r[D[F+3]]+"-"+r[D[F+4]]+r[D[F+5]]+"-"+r[D[F+6]]+r[D[F+7]]+"-"+r[D[F+8]]+r[D[F+9]]+"-"+r[D[F+10]]+r[D[F+11]]+r[D[F+12]]+r[D[F+13]]+r[D[F+14]]+r[D[F+15]]).toLowerCase()}import{randomFillSync as w5}from"crypto";var K0=new Uint8Array(256),V0=K0.length;function q3(){if(V0>K0.length-16)w5(K0),V0=0;return K0.slice(V0,V0+=16)}import{randomUUID as v5}from"crypto";var H3={randomUUID:v5};function u5(D,F,E){if(H3.randomUUID&&!F&&!D)return H3.randomUUID();D=D||{};let B=D.random??D.rng?.()??q3();if(B.length<16)throw Error("Random bytes length must be >= 16");if(B[6]=B[6]&15|64,B[8]=B[8]&63|128,F){if(E=E||0,E<0||E+16>F.length)throw RangeError(`UUID byte range ${E}:${E+15} is out of buffer bounds`);for(let $=0;$<16;++$)F[E+$]=B[$];return F}return c8(B)}var R0=u5;import{exec as x5}from"child_process";import{promisify as k5}from"util";import{join as y5}from"path";import{mkdirSync as h5,existsSync as g5}from"fs";import{homedir as l5}from"os";var m5=k5(x5);function c5(){let D=y5(l5(),".genie","logs","tmux");if(!g5(D))h5(D,{recursive:!0});return D}function p5(D){return D.filter((F)=>!/^-v+$/.test(F))}function d5(){return process.env.GENIE_TMUX_DEBUG==="1"}async function p8(D){let F=typeof D==="string"?D.split(/\s+/).filter(Boolean):D,E=p5(F),B=d5(),$={};if(B)E=["-v",...E],$.cwd=c5();let X=`tmux ${E.join(" ")}`,{stdout:Q}=await m5(X,$);return Q.trim()}var i5={type:"bash"};async function O(D){try{return await p8(D)}catch(F){throw Error(`Failed to execute tmux command: ${F.message}`)}}async function W3(){try{let F=await O("list-sessions -F '#{session_id}:#{session_name}:#{?session_attached,1,0}:#{session_windows}'");if(!F)return[];return F.split(`
|
|
31
31
|
`).map((E)=>{let[B,$,X,Q]=E.split(":");return{id:B,name:$,attached:X==="1",windows:parseInt(Q,10)}})}catch(D){if(D.message.includes("no server running"))return[];throw D}}async function v(D){try{return(await W3()).find((E)=>E.name===D)||null}catch(F){return null}}async function x(D){try{let E=await O(`list-windows -t '${D}' -F '#{window_id}:#{window_name}:#{?window_active,1,0}'`);if(!E)return[];return E.split(`
|
|
32
32
|
`).map((B)=>{let[$,X,Q]=B.split(":");return{id:$,name:X,active:Q==="1",sessionId:D}})}catch(F){if(F.message.includes("no server running")||F.message.includes("session not found"))return[];throw F}}async function h(D){try{let E=await O(`list-panes -t '${D}' -F '#{pane_id}:#{pane_title}:#{?pane_active,1,0}'`);if(!E)return[];return E.split(`
|
|
33
33
|
`).map((B)=>{let[$,X,Q]=B.split(":");return{id:$,windowId:D,title:X,active:Q==="1"}})}catch(F){if(F.message.includes("no server running")||F.message.includes("window not found"))return[];throw F}}async function p(D,F=200,E=!1){try{return await O(`capture-pane -p ${E?"-e":""} -t '${D}' -S -${F} -E -`)}catch(B){if(B.message.includes("no server running")||B.message.includes("pane not found"))return"";throw B}}async function nD(D){return await O(`new-session -d -s "${D}" -e LC_ALL=C.UTF-8 -e LANG=C.UTF-8`),v(D)}async function j0(D,F){let E=await O(`new-window -t '${D}' -n '${F}'`);return(await x(D)).find(($)=>$.name===F)||null}async function M0(D){await O(`kill-session -t '${D}'`)}async function d8(D){await O(`kill-window -t '${D}'`)}async function W2(D){await O(`kill-pane -t '${D}'`)}function n5(D){return D.replace(/'/g,"'\\''")}async function U2(D,F="vertical",E,B){let $="split-window";if(F==="horizontal")$+=" -h";else $+=" -v";if(B)$+=` -c '${n5(B)}'`;if(E!==void 0&&E>0&&E<100)$+=` -p ${E}`;$+=` -t '${D}'`,$+=" -P -F '#{pane_id}'";let X=(await O($)).trim(),Q=await O(`display-message -p -t '${X}' '#{window_id}'`);return{id:X,windowId:Q.trim(),active:!1,title:""}}var _3=new Map,G3="TMUX_MCP_START",L0="TMUX_MCP_DONE_";async function QD(D,F,E,B){let $=R0(),X;if(E||B)X=F;else{let Q=r5();X=`echo "${G3}"; ${F}; echo "${Q}"`}if(_3.set($,{id:$,paneId:D,command:F,status:"pending",startTime:new Date,rawMode:E||B}),B)if(["Up","Down","Left","Right","Escape","Tab","Enter","Space","BSpace","Delete","Home","End","PageUp","PageDown","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12"].includes(X))await O(`send-keys -t '${D}' ${X}`);else for(let J of X)await O(`send-keys -t '${D}' '${J.replace(/'/g,"'\\''")}'`);else await O(`send-keys -t '${D}' '${X.replace(/'/g,"'\\''")}' Enter`);return $}async function i8(D){let F=_3.get(D);if(!F)return null;if(F.status!=="pending")return F;let E=await p(F.paneId,1000);if(F.rawMode)return F.result="Status tracking unavailable for rawMode commands. Use capture-pane to monitor interactive apps instead.",F;let B=E.lastIndexOf(G3),$=E.lastIndexOf(L0);if(B===-1||$===-1||$<=B)return F.result="Command output could not be captured properly",F;let X=E.substring($).split(`
|
package/install.sh
CHANGED
|
@@ -334,6 +334,15 @@ install_bun_if_needed() {
|
|
|
334
334
|
fi
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
# Ensure bun's global bin directory is in PATH
|
|
338
|
+
# This is needed after installing global packages via bun
|
|
339
|
+
ensure_bun_in_path() {
|
|
340
|
+
export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
|
|
341
|
+
if [[ ":$PATH:" != *":$BUN_INSTALL/bin:"* ]]; then
|
|
342
|
+
export PATH="$BUN_INSTALL/bin:$PATH"
|
|
343
|
+
fi
|
|
344
|
+
}
|
|
345
|
+
|
|
337
346
|
install_tmux_if_needed() {
|
|
338
347
|
if check_command tmux; then
|
|
339
348
|
success "tmux installed"
|
|
@@ -426,6 +435,7 @@ install_genie_cli() {
|
|
|
426
435
|
|
|
427
436
|
if check_command bun; then
|
|
428
437
|
bun install -g "$PACKAGE_NAME"
|
|
438
|
+
ensure_bun_in_path
|
|
429
439
|
elif check_command npm; then
|
|
430
440
|
npm install -g "$PACKAGE_NAME"
|
|
431
441
|
else
|
|
@@ -441,6 +451,7 @@ install_genie_cli() {
|
|
|
441
451
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
442
452
|
|
|
443
453
|
run_claudio_setup() {
|
|
454
|
+
ensure_bun_in_path
|
|
444
455
|
log "Running claudio setup..."
|
|
445
456
|
if check_command claudio; then
|
|
446
457
|
claudio setup
|
package/package.json
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Genie TUI Command
|
|
3
|
+
*
|
|
4
|
+
* Persistent "master genie" session that:
|
|
5
|
+
* - Always lives in ~/workspace
|
|
6
|
+
* - Uses fixed session name "genie"
|
|
7
|
+
* - Persists until manually reset via --reset flag
|
|
8
|
+
* - Starts Claude Code on first creation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawnSync } from 'child_process';
|
|
12
|
+
import { homedir } from 'os';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import * as tmux from '../lib/tmux.js';
|
|
15
|
+
|
|
16
|
+
const SESSION_NAME = 'genie';
|
|
17
|
+
const WORKSPACE_DIR = join(homedir(), 'workspace');
|
|
18
|
+
|
|
19
|
+
export interface TuiOptions {
|
|
20
|
+
reset?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function tuiCommand(options: TuiOptions = {}): Promise<void> {
|
|
24
|
+
try {
|
|
25
|
+
// Handle reset flag - kill existing session
|
|
26
|
+
if (options.reset) {
|
|
27
|
+
const existing = await tmux.findSessionByName(SESSION_NAME);
|
|
28
|
+
if (existing) {
|
|
29
|
+
console.log(`🗑️ Resetting session "${SESSION_NAME}"...`);
|
|
30
|
+
await tmux.killSession(SESSION_NAME);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if session exists
|
|
35
|
+
let session = await tmux.findSessionByName(SESSION_NAME);
|
|
36
|
+
|
|
37
|
+
if (!session) {
|
|
38
|
+
// Create session
|
|
39
|
+
console.log(`✨ Creating session "${SESSION_NAME}"...`);
|
|
40
|
+
session = await tmux.createSession(SESSION_NAME);
|
|
41
|
+
if (!session) {
|
|
42
|
+
console.error(`❌ Failed to create session "${SESSION_NAME}"`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Change to workspace directory
|
|
47
|
+
await tmux.executeTmux(`send-keys -t '${SESSION_NAME}' 'cd ${WORKSPACE_DIR}' Enter`);
|
|
48
|
+
|
|
49
|
+
// Start Claude Code
|
|
50
|
+
await tmux.executeTmux(`send-keys -t '${SESSION_NAME}' 'claude' Enter`);
|
|
51
|
+
console.log(`✅ Started Claude Code in ${WORKSPACE_DIR}`);
|
|
52
|
+
} else {
|
|
53
|
+
console.log(`📎 Session "${SESSION_NAME}" already exists`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Attach to session (replaces current process)
|
|
57
|
+
console.log(`📎 Attaching...`);
|
|
58
|
+
spawnSync('tmux', ['attach', '-t', SESSION_NAME], { stdio: 'inherit' });
|
|
59
|
+
} catch (error: any) {
|
|
60
|
+
console.error(`❌ Error: ${error.message}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/genie.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { setupCommand, type SetupOptions } from './genie-commands/setup.js';
|
|
|
7
7
|
import { updateCommand } from './genie-commands/update.js';
|
|
8
8
|
import { uninstallCommand } from './genie-commands/uninstall.js';
|
|
9
9
|
import { doctorCommand } from './genie-commands/doctor.js';
|
|
10
|
+
import { tuiCommand, type TuiOptions } from './genie-commands/tui.js';
|
|
10
11
|
import {
|
|
11
12
|
shortcutsShowCommand,
|
|
12
13
|
shortcutsInstallCommand,
|
|
@@ -61,6 +62,15 @@ program
|
|
|
61
62
|
.description('Remove Genie CLI and clean up hooks')
|
|
62
63
|
.action(uninstallCommand);
|
|
63
64
|
|
|
65
|
+
// TUI command - attach to master genie session
|
|
66
|
+
program
|
|
67
|
+
.command('tui')
|
|
68
|
+
.description('Attach to master genie session in ~/workspace')
|
|
69
|
+
.option('-r, --reset', 'Kill existing session and start fresh')
|
|
70
|
+
.action(async (options: TuiOptions) => {
|
|
71
|
+
await tuiCommand(options);
|
|
72
|
+
});
|
|
73
|
+
|
|
64
74
|
// Shortcuts command group - manage tmux keyboard shortcuts
|
|
65
75
|
const shortcuts = program
|
|
66
76
|
.command('shortcuts')
|
package/src/lib/version.ts
CHANGED