@automagik/genie 4.260324.20 → 4.260325.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.260324.20",
13
+ "version": "4.260325.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
@@ -128,7 +128,7 @@ ${errCtx.stack}`;d.reject(err)}else d.resolve(msg)}});return sub.requestSubject=
128
128
  `}static header(){return`Test,Date,Lang,Version,Count,MsgPayload,Bytes,Millis,Async
129
129
  `}}exports.Metric=Metric;class Bench{constructor(nc,opts={msgs:1e5,size:128,subject:"",asyncRequests:!1,pub:!1,sub:!1,req:!1,rep:!1}){if(this.nc=nc,this.callbacks=opts.callbacks||!1,this.msgs=opts.msgs||0,this.size=opts.size||0,this.subject=opts.subject||nuid_1.nuid.next(),this.asyncRequests=opts.asyncRequests||!1,this.pub=opts.pub||!1,this.sub=opts.sub||!1,this.req=opts.req||!1,this.rep=opts.rep||!1,this.perf=new util_1.Perf,this.payload=this.size?new Uint8Array(this.size):types_1.Empty,!this.pub&&!this.sub&&!this.req&&!this.rep)throw Error("no bench option selected")}run(){return __awaiter(this,void 0,void 0,function*(){if(this.nc.closed().then((err)=>{if(err)throw new core_1.NatsError(`bench closed with an error: ${err.message}`,core_1.ErrorCode.Unknown,err)}),this.callbacks)yield this.runCallbacks();else yield this.runAsync();return this.processMetrics()})}processMetrics(){let nc=this.nc,{lang,version}=nc.protocol.transport;if(this.pub&&this.sub)this.perf.measure("pubsub","pubStart","subStop");if(this.req&&this.rep)this.perf.measure("reqrep","reqStart","reqStop");let measures=this.perf.getEntries(),pubsub=measures.find((m)=>m.name==="pubsub"),reqrep=measures.find((m)=>m.name==="reqrep"),req=measures.find((m)=>m.name==="req"),rep=measures.find((m)=>m.name==="rep"),pub=measures.find((m)=>m.name==="pub"),sub=measures.find((m)=>m.name==="sub"),stats=this.nc.stats(),metrics=[];if(pubsub){let{name,duration}=pubsub,m=new Metric(name,duration);m.msgs=this.msgs*2,m.bytes=stats.inBytes+stats.outBytes,m.lang=lang,m.version=version,m.payload=this.payload.length,metrics.push(m)}if(reqrep){let{name,duration}=reqrep,m=new Metric(name,duration);m.msgs=this.msgs*2,m.bytes=stats.inBytes+stats.outBytes,m.lang=lang,m.version=version,m.payload=this.payload.length,metrics.push(m)}if(pub){let{name,duration}=pub,m=new Metric(name,duration);m.msgs=this.msgs,m.bytes=stats.outBytes,m.lang=lang,m.version=version,m.payload=this.payload.length,metrics.push(m)}if(sub){let{name,duration}=sub,m=new Metric(name,duration);m.msgs=this.msgs,m.bytes=stats.inBytes,m.lang=lang,m.version=version,m.payload=this.payload.length,metrics.push(m)}if(rep){let{name,duration}=rep,m=new Metric(name,duration);m.msgs=this.msgs,m.bytes=stats.inBytes+stats.outBytes,m.lang=lang,m.version=version,m.payload=this.payload.length,metrics.push(m)}if(req){let{name,duration}=req,m=new Metric(name,duration);m.msgs=this.msgs,m.bytes=stats.inBytes+stats.outBytes,m.lang=lang,m.version=version,m.payload=this.payload.length,metrics.push(m)}return metrics}runCallbacks(){return __awaiter(this,void 0,void 0,function*(){let jobs=[];if(this.sub){let d=(0,util_1.deferred)();jobs.push(d);let i2=0;this.nc.subscribe(this.subject,{max:this.msgs,callback:()=>{if(i2++,i2===1)this.perf.mark("subStart");if(i2===this.msgs)this.perf.mark("subStop"),this.perf.measure("sub","subStart","subStop"),d.resolve()}})}if(this.rep){let d=(0,util_1.deferred)();jobs.push(d);let i2=0;this.nc.subscribe(this.subject,{max:this.msgs,callback:(_,m)=>{if(m.respond(this.payload),i2++,i2===1)this.perf.mark("repStart");if(i2===this.msgs)this.perf.mark("repStop"),this.perf.measure("rep","repStart","repStop"),d.resolve()}})}if(this.pub){let job=(()=>__awaiter(this,void 0,void 0,function*(){this.perf.mark("pubStart");for(let i2=0;i2<this.msgs;i2++)this.nc.publish(this.subject,this.payload);yield this.nc.flush(),this.perf.mark("pubStop"),this.perf.measure("pub","pubStart","pubStop")}))();jobs.push(job)}if(this.req){let job=(()=>__awaiter(this,void 0,void 0,function*(){if(this.asyncRequests){this.perf.mark("reqStart");let a=[];for(let i2=0;i2<this.msgs;i2++)a.push(this.nc.request(this.subject,this.payload,{timeout:20000}));yield Promise.all(a),this.perf.mark("reqStop"),this.perf.measure("req","reqStart","reqStop")}else{this.perf.mark("reqStart");for(let i2=0;i2<this.msgs;i2++)yield this.nc.request(this.subject);this.perf.mark("reqStop"),this.perf.measure("req","reqStart","reqStop")}}))();jobs.push(job)}yield Promise.all(jobs)})}runAsync(){return __awaiter(this,void 0,void 0,function*(){let jobs=[];if(this.rep){let first=!1,sub=this.nc.subscribe(this.subject,{max:this.msgs}),job=(()=>__awaiter(this,void 0,void 0,function*(){var _a,e_1,_b,_c;try{for(var _d=!0,sub_1=__asyncValues(sub),sub_1_1;sub_1_1=yield sub_1.next(),_a=sub_1_1.done,!_a;_d=!0){_c=sub_1_1.value,_d=!1;let m=_c;if(!first)this.perf.mark("repStart"),first=!0;m.respond(this.payload)}}catch(e_1_1){e_1={error:e_1_1}}finally{try{if(!_d&&!_a&&(_b=sub_1.return))yield _b.call(sub_1)}finally{if(e_1)throw e_1.error}}yield this.nc.flush(),this.perf.mark("repStop"),this.perf.measure("rep","repStart","repStop")}))();jobs.push(job)}if(this.sub){let first=!1,sub=this.nc.subscribe(this.subject,{max:this.msgs}),job=(()=>__awaiter(this,void 0,void 0,function*(){var _a,e_2,_b,_c;try{for(var _d=!0,sub_2=__asyncValues(sub),sub_2_1;sub_2_1=yield sub_2.next(),_a=sub_2_1.done,!_a;_d=!0){_c=sub_2_1.value,_d=!1;let _m=_c;if(!first)this.perf.mark("subStart"),first=!0}}catch(e_2_1){e_2={error:e_2_1}}finally{try{if(!_d&&!_a&&(_b=sub_2.return))yield _b.call(sub_2)}finally{if(e_2)throw e_2.error}}this.perf.mark("subStop"),this.perf.measure("sub","subStart","subStop")}))();jobs.push(job)}if(this.pub){let job=(()=>__awaiter(this,void 0,void 0,function*(){this.perf.mark("pubStart");for(let i2=0;i2<this.msgs;i2++)this.nc.publish(this.subject,this.payload);yield this.nc.flush(),this.perf.mark("pubStop"),this.perf.measure("pub","pubStart","pubStop")}))();jobs.push(job)}if(this.req){let job=(()=>__awaiter(this,void 0,void 0,function*(){if(this.asyncRequests){this.perf.mark("reqStart");let a=[];for(let i2=0;i2<this.msgs;i2++)a.push(this.nc.request(this.subject,this.payload,{timeout:20000}));yield Promise.all(a),this.perf.mark("reqStop"),this.perf.measure("req","reqStart","reqStop")}else{this.perf.mark("reqStart");for(let i2=0;i2<this.msgs;i2++)yield this.nc.request(this.subject);this.perf.mark("reqStop"),this.perf.measure("req","reqStart","reqStop")}}))();jobs.push(job)}yield Promise.all(jobs)})}}exports.Bench=Bench;function throughput(bytes,seconds){return`${humanizeBytes(bytes/seconds)}/sec`}function msgThroughput(msgs,seconds){return`${Math.floor(msgs/seconds)} msgs/sec`}function humanizeBytes(bytes,si=!1){let base=si?1000:1024,pre=si?["k","M","G","T","P","E"]:["K","M","G","T","P","E"],post=si?"iB":"B";if(bytes<base)return`${bytes.toFixed(2)} ${post}`;let exp=parseInt(Math.log(bytes)/Math.log(base)+""),index=parseInt(exp-1+"");return`${(bytes/Math.pow(base,exp)).toFixed(2)} ${pre[index]}${post}`}function humanizeNumber(n){return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")}});var require_internal_mod=__commonJS((exports)=>{var __createBinding=exports&&exports.__createBinding||(Object.create?function(o,m,k,k2){if(k2===void 0)k2=k;var desc=Object.getOwnPropertyDescriptor(m,k);if(!desc||("get"in desc?!m.__esModule:desc.writable||desc.configurable))desc={enumerable:!0,get:function(){return m[k]}};Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){if(k2===void 0)k2=k;o[k2]=m[k]}),__exportStar=exports&&exports.__exportStar||function(m,exports2){for(var p in m)if(p!=="default"&&!Object.prototype.hasOwnProperty.call(exports2,p))__createBinding(exports2,m,p)};Object.defineProperty(exports,"__esModule",{value:!0});exports.parseIP=exports.isIP=exports.TE=exports.TD=exports.Metric=exports.Bench=exports.writeAll=exports.readAll=exports.MAX_SIZE=exports.DenoBuffer=exports.State=exports.Parser=exports.Kind=exports.QueuedIteratorImpl=exports.StringCodec=exports.JSONCodec=exports.usernamePasswordAuthenticator=exports.tokenAuthenticator=exports.nkeyAuthenticator=exports.jwtAuthenticator=exports.credsAuthenticator=exports.RequestOne=exports.checkUnsupportedOption=exports.checkOptions=exports.buildAuthenticator=exports.DataBuffer=exports.MuxSubscription=exports.Heartbeat=exports.MsgHdrsImpl=exports.headers=exports.canonicalMIMEHeaderKey=exports.timeout=exports.render=exports.nanos=exports.millis=exports.extend=exports.delay=exports.deferred=exports.deadline=exports.collect=exports.backoff=exports.ProtocolHandler=exports.INFO=exports.Connect=exports.setTransportFactory=exports.getResolveFn=exports.MsgImpl=exports.nuid=exports.Nuid=exports.NatsConnectionImpl=void 0;exports.Subscriptions=exports.SubscriptionImpl=exports.syncIterator=exports.ServiceVerb=exports.ServiceResponseType=exports.ServiceErrorHeader=exports.ServiceErrorCodeHeader=exports.ServiceError=exports.RequestStrategy=exports.NatsError=exports.Match=exports.isNatsError=exports.Events=exports.ErrorCode=exports.DebugEvents=exports.createInbox=exports.extractProtocolMessage=exports.Empty=exports.parseSemVer=exports.compare=exports.NoopKvCodecs=exports.defaultBucketOpts=exports.Bucket=exports.Base64KeyCodec=exports.TypedSubscription=void 0;var nats_1=require_nats();Object.defineProperty(exports,"NatsConnectionImpl",{enumerable:!0,get:function(){return nats_1.NatsConnectionImpl}});var nuid_1=require_nuid();Object.defineProperty(exports,"Nuid",{enumerable:!0,get:function(){return nuid_1.Nuid}});Object.defineProperty(exports,"nuid",{enumerable:!0,get:function(){return nuid_1.nuid}});var msg_1=require_msg();Object.defineProperty(exports,"MsgImpl",{enumerable:!0,get:function(){return msg_1.MsgImpl}});var transport_1=require_transport();Object.defineProperty(exports,"getResolveFn",{enumerable:!0,get:function(){return transport_1.getResolveFn}});Object.defineProperty(exports,"setTransportFactory",{enumerable:!0,get:function(){return transport_1.setTransportFactory}});var protocol_1=require_protocol();Object.defineProperty(exports,"Connect",{enumerable:!0,get:function(){return protocol_1.Connect}});Object.defineProperty(exports,"INFO",{enumerable:!0,get:function(){return protocol_1.INFO}});Object.defineProperty(exports,"ProtocolHandler",{enumerable:!0,get:function(){return protocol_1.ProtocolHandler}});var util_1=require_util();Object.defineProperty(exports,"backoff",{enumerable:!0,get:function(){return util_1.backoff}});Object.defineProperty(exports,"collect",{enumerable:!0,get:function(){return util_1.collect}});Object.defineProperty(exports,"deadline",{enumerable:!0,get:function(){return util_1.deadline}});Object.defineProperty(exports,"deferred",{enumerable:!0,get:function(){return util_1.deferred}});Object.defineProperty(exports,"delay",{enumerable:!0,get:function(){return util_1.delay}});Object.defineProperty(exports,"extend",{enumerable:!0,get:function(){return util_1.extend}});Object.defineProperty(exports,"millis",{enumerable:!0,get:function(){return util_1.millis}});Object.defineProperty(exports,"nanos",{enumerable:!0,get:function(){return util_1.nanos}});Object.defineProperty(exports,"render",{enumerable:!0,get:function(){return util_1.render}});Object.defineProperty(exports,"timeout",{enumerable:!0,get:function(){return util_1.timeout}});var headers_1=require_headers();Object.defineProperty(exports,"canonicalMIMEHeaderKey",{enumerable:!0,get:function(){return headers_1.canonicalMIMEHeaderKey}});Object.defineProperty(exports,"headers",{enumerable:!0,get:function(){return headers_1.headers}});Object.defineProperty(exports,"MsgHdrsImpl",{enumerable:!0,get:function(){return headers_1.MsgHdrsImpl}});var heartbeats_1=require_heartbeats();Object.defineProperty(exports,"Heartbeat",{enumerable:!0,get:function(){return heartbeats_1.Heartbeat}});var muxsubscription_1=require_muxsubscription();Object.defineProperty(exports,"MuxSubscription",{enumerable:!0,get:function(){return muxsubscription_1.MuxSubscription}});var databuffer_1=require_databuffer();Object.defineProperty(exports,"DataBuffer",{enumerable:!0,get:function(){return databuffer_1.DataBuffer}});var options_1=require_options();Object.defineProperty(exports,"buildAuthenticator",{enumerable:!0,get:function(){return options_1.buildAuthenticator}});Object.defineProperty(exports,"checkOptions",{enumerable:!0,get:function(){return options_1.checkOptions}});Object.defineProperty(exports,"checkUnsupportedOption",{enumerable:!0,get:function(){return options_1.checkUnsupportedOption}});var request_1=require_request();Object.defineProperty(exports,"RequestOne",{enumerable:!0,get:function(){return request_1.RequestOne}});var authenticator_1=require_authenticator();Object.defineProperty(exports,"credsAuthenticator",{enumerable:!0,get:function(){return authenticator_1.credsAuthenticator}});Object.defineProperty(exports,"jwtAuthenticator",{enumerable:!0,get:function(){return authenticator_1.jwtAuthenticator}});Object.defineProperty(exports,"nkeyAuthenticator",{enumerable:!0,get:function(){return authenticator_1.nkeyAuthenticator}});Object.defineProperty(exports,"tokenAuthenticator",{enumerable:!0,get:function(){return authenticator_1.tokenAuthenticator}});Object.defineProperty(exports,"usernamePasswordAuthenticator",{enumerable:!0,get:function(){return authenticator_1.usernamePasswordAuthenticator}});var codec_1=require_codec();Object.defineProperty(exports,"JSONCodec",{enumerable:!0,get:function(){return codec_1.JSONCodec}});Object.defineProperty(exports,"StringCodec",{enumerable:!0,get:function(){return codec_1.StringCodec}});__exportStar(require_nkeys2(),exports);var queued_iterator_1=require_queued_iterator();Object.defineProperty(exports,"QueuedIteratorImpl",{enumerable:!0,get:function(){return queued_iterator_1.QueuedIteratorImpl}});var parser_1=require_parser();Object.defineProperty(exports,"Kind",{enumerable:!0,get:function(){return parser_1.Kind}});Object.defineProperty(exports,"Parser",{enumerable:!0,get:function(){return parser_1.Parser}});Object.defineProperty(exports,"State",{enumerable:!0,get:function(){return parser_1.State}});var denobuffer_1=require_denobuffer();Object.defineProperty(exports,"DenoBuffer",{enumerable:!0,get:function(){return denobuffer_1.DenoBuffer}});Object.defineProperty(exports,"MAX_SIZE",{enumerable:!0,get:function(){return denobuffer_1.MAX_SIZE}});Object.defineProperty(exports,"readAll",{enumerable:!0,get:function(){return denobuffer_1.readAll}});Object.defineProperty(exports,"writeAll",{enumerable:!0,get:function(){return denobuffer_1.writeAll}});var bench_1=require_bench();Object.defineProperty(exports,"Bench",{enumerable:!0,get:function(){return bench_1.Bench}});Object.defineProperty(exports,"Metric",{enumerable:!0,get:function(){return bench_1.Metric}});var encoders_1=require_encoders();Object.defineProperty(exports,"TD",{enumerable:!0,get:function(){return encoders_1.TD}});Object.defineProperty(exports,"TE",{enumerable:!0,get:function(){return encoders_1.TE}});var ipparser_1=require_ipparser();Object.defineProperty(exports,"isIP",{enumerable:!0,get:function(){return ipparser_1.isIP}});Object.defineProperty(exports,"parseIP",{enumerable:!0,get:function(){return ipparser_1.parseIP}});var typedsub_1=require_typedsub();Object.defineProperty(exports,"TypedSubscription",{enumerable:!0,get:function(){return typedsub_1.TypedSubscription}});var kv_1=require_kv();Object.defineProperty(exports,"Base64KeyCodec",{enumerable:!0,get:function(){return kv_1.Base64KeyCodec}});Object.defineProperty(exports,"Bucket",{enumerable:!0,get:function(){return kv_1.Bucket}});Object.defineProperty(exports,"defaultBucketOpts",{enumerable:!0,get:function(){return kv_1.defaultBucketOpts}});Object.defineProperty(exports,"NoopKvCodecs",{enumerable:!0,get:function(){return kv_1.NoopKvCodecs}});var semver_1=require_semver();Object.defineProperty(exports,"compare",{enumerable:!0,get:function(){return semver_1.compare}});Object.defineProperty(exports,"parseSemVer",{enumerable:!0,get:function(){return semver_1.parseSemVer}});var types_1=require_types();Object.defineProperty(exports,"Empty",{enumerable:!0,get:function(){return types_1.Empty}});var transport_2=require_transport();Object.defineProperty(exports,"extractProtocolMessage",{enumerable:!0,get:function(){return transport_2.extractProtocolMessage}});var core_1=require_core();Object.defineProperty(exports,"createInbox",{enumerable:!0,get:function(){return core_1.createInbox}});Object.defineProperty(exports,"DebugEvents",{enumerable:!0,get:function(){return core_1.DebugEvents}});Object.defineProperty(exports,"ErrorCode",{enumerable:!0,get:function(){return core_1.ErrorCode}});Object.defineProperty(exports,"Events",{enumerable:!0,get:function(){return core_1.Events}});Object.defineProperty(exports,"isNatsError",{enumerable:!0,get:function(){return core_1.isNatsError}});Object.defineProperty(exports,"Match",{enumerable:!0,get:function(){return core_1.Match}});Object.defineProperty(exports,"NatsError",{enumerable:!0,get:function(){return core_1.NatsError}});Object.defineProperty(exports,"RequestStrategy",{enumerable:!0,get:function(){return core_1.RequestStrategy}});Object.defineProperty(exports,"ServiceError",{enumerable:!0,get:function(){return core_1.ServiceError}});Object.defineProperty(exports,"ServiceErrorCodeHeader",{enumerable:!0,get:function(){return core_1.ServiceErrorCodeHeader}});Object.defineProperty(exports,"ServiceErrorHeader",{enumerable:!0,get:function(){return core_1.ServiceErrorHeader}});Object.defineProperty(exports,"ServiceResponseType",{enumerable:!0,get:function(){return core_1.ServiceResponseType}});Object.defineProperty(exports,"ServiceVerb",{enumerable:!0,get:function(){return core_1.ServiceVerb}});Object.defineProperty(exports,"syncIterator",{enumerable:!0,get:function(){return core_1.syncIterator}});var protocol_2=require_protocol();Object.defineProperty(exports,"SubscriptionImpl",{enumerable:!0,get:function(){return protocol_2.SubscriptionImpl}});Object.defineProperty(exports,"Subscriptions",{enumerable:!0,get:function(){return protocol_2.Subscriptions}})});var require_internal_mod2=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.ConsumerEvents=exports.ConsumerDebugEvents=exports.StoreCompression=exports.StorageType=exports.RetentionPolicy=exports.ReplayPolicy=exports.DiscardPolicy=exports.DeliverPolicy=exports.AckPolicy=exports.RepublishHeaders=exports.KvWatchInclude=exports.JsHeaders=exports.isConsumerOptsBuilder=exports.DirectMsgHeaders=exports.consumerOpts=exports.AdvisoryKind=exports.isHeartbeatMsg=exports.isFlowControlMsg=exports.checkJsError=void 0;var jsutil_1=require_jsutil();Object.defineProperty(exports,"checkJsError",{enumerable:!0,get:function(){return jsutil_1.checkJsError}});Object.defineProperty(exports,"isFlowControlMsg",{enumerable:!0,get:function(){return jsutil_1.isFlowControlMsg}});Object.defineProperty(exports,"isHeartbeatMsg",{enumerable:!0,get:function(){return jsutil_1.isHeartbeatMsg}});var types_1=require_types2();Object.defineProperty(exports,"AdvisoryKind",{enumerable:!0,get:function(){return types_1.AdvisoryKind}});Object.defineProperty(exports,"consumerOpts",{enumerable:!0,get:function(){return types_1.consumerOpts}});Object.defineProperty(exports,"DirectMsgHeaders",{enumerable:!0,get:function(){return types_1.DirectMsgHeaders}});Object.defineProperty(exports,"isConsumerOptsBuilder",{enumerable:!0,get:function(){return types_1.isConsumerOptsBuilder}});Object.defineProperty(exports,"JsHeaders",{enumerable:!0,get:function(){return types_1.JsHeaders}});Object.defineProperty(exports,"KvWatchInclude",{enumerable:!0,get:function(){return types_1.KvWatchInclude}});Object.defineProperty(exports,"RepublishHeaders",{enumerable:!0,get:function(){return types_1.RepublishHeaders}});var jsapi_types_1=require_jsapi_types();Object.defineProperty(exports,"AckPolicy",{enumerable:!0,get:function(){return jsapi_types_1.AckPolicy}});Object.defineProperty(exports,"DeliverPolicy",{enumerable:!0,get:function(){return jsapi_types_1.DeliverPolicy}});Object.defineProperty(exports,"DiscardPolicy",{enumerable:!0,get:function(){return jsapi_types_1.DiscardPolicy}});Object.defineProperty(exports,"ReplayPolicy",{enumerable:!0,get:function(){return jsapi_types_1.ReplayPolicy}});Object.defineProperty(exports,"RetentionPolicy",{enumerable:!0,get:function(){return jsapi_types_1.RetentionPolicy}});Object.defineProperty(exports,"StorageType",{enumerable:!0,get:function(){return jsapi_types_1.StorageType}});Object.defineProperty(exports,"StoreCompression",{enumerable:!0,get:function(){return jsapi_types_1.StoreCompression}});var consumer_1=require_consumer();Object.defineProperty(exports,"ConsumerDebugEvents",{enumerable:!0,get:function(){return consumer_1.ConsumerDebugEvents}});Object.defineProperty(exports,"ConsumerEvents",{enumerable:!0,get:function(){return consumer_1.ConsumerEvents}})});var require_nats_base_client=__commonJS((exports)=>{var __createBinding=exports&&exports.__createBinding||(Object.create?function(o,m,k,k2){if(k2===void 0)k2=k;var desc=Object.getOwnPropertyDescriptor(m,k);if(!desc||("get"in desc?!m.__esModule:desc.writable||desc.configurable))desc={enumerable:!0,get:function(){return m[k]}};Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){if(k2===void 0)k2=k;o[k2]=m[k]}),__exportStar=exports&&exports.__exportStar||function(m,exports2){for(var p in m)if(p!=="default"&&!Object.prototype.hasOwnProperty.call(exports2,p))__createBinding(exports2,m,p)};Object.defineProperty(exports,"__esModule",{value:!0});__exportStar(require_internal_mod(),exports);__exportStar(require_internal_mod2(),exports)});var require_node_transport=__commonJS((exports)=>{var __awaiter=exports&&exports.__awaiter||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P(function(resolve5){resolve5(value)})}return new(P||(P=Promise))(function(resolve5,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator.throw(value))}catch(e){reject(e)}}function step(result){result.done?resolve5(result.value):adopt(result.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})},__await=exports&&exports.__await||function(v){return this instanceof __await?(this.v=v,this):new __await(v)},__asyncGenerator=exports&&exports.__asyncGenerator||function(thisArg,_arguments,generator){if(!Symbol.asyncIterator)throw TypeError("Symbol.asyncIterator is not defined.");var g=generator.apply(thisArg,_arguments||[]),i2,q=[];return i2=Object.create((typeof AsyncIterator==="function"?AsyncIterator:Object).prototype),verb("next"),verb("throw"),verb("return",awaitReturn),i2[Symbol.asyncIterator]=function(){return this},i2;function awaitReturn(f){return function(v){return Promise.resolve(v).then(f,reject)}}function verb(n,f){if(g[n]){if(i2[n]=function(v){return new Promise(function(a,b){q.push([n,v,a,b])>1||resume(n,v)})},f)i2[n]=f(i2[n])}}function resume(n,v){try{step(g[n](v))}catch(e){settle(q[0][3],e)}}function step(r){r.value instanceof __await?Promise.resolve(r.value.v).then(fulfill,reject):settle(q[0][2],r)}function fulfill(value){resume("next",value)}function reject(value){resume("throw",value)}function settle(f,v){if(f(v),q.shift(),q.length)resume(q[0][0],q[0][1])}};Object.defineProperty(exports,"__esModule",{value:!0});exports.NodeTransport=void 0;exports.nodeResolveHost=nodeResolveHost;var nats_base_client_1=require_nats_base_client(),net_1=__require("net"),util_1=require_util(),tls_1=__require("tls"),{resolve:resolve4}=__require("path"),{readFile:readFile4,existsSync:existsSync14}=__require("fs"),dns=__require("dns"),VERSION2="2.29.3",LANG="nats.js";class NodeTransport{constructor(){this.yields=[],this.signal=(0,nats_base_client_1.deferred)(),this.closedNotification=(0,nats_base_client_1.deferred)(),this.connected=!1,this.tlsName="",this.done=!1,this.lang=LANG,this.version=VERSION2}connect(hp,options){return __awaiter(this,void 0,void 0,function*(){this.tlsName=hp.tlsName,this.options=options;let{tls}=this.options,{handshakeFirst}=tls||{};try{if(handshakeFirst===!0)this.socket=yield this.tlsFirst(hp);else this.socket=yield this.dial(hp);let info=yield this.peekInfo();(0,nats_base_client_1.checkOptions)(info,options);let{tls_required:tlsRequired,tls_available:tlsAvailable}=info,desired=tlsAvailable===!0&&options.tls!==null;if(!handshakeFirst&&(tlsRequired||desired))this.socket=yield this.startTLS();if(tlsRequired&&this.socket.encrypted!==!0)throw new nats_base_client_1.NatsError("tls",nats_base_client_1.ErrorCode.ServerOptionNotAvailable);return this.connected=!0,this.setupHandlers(),this.signal.resolve(),Promise.resolve()}catch(err){if(!err)err=nats_base_client_1.NatsError.errorForCode(nats_base_client_1.ErrorCode.ConnectionRefused,Error("node provided an undefined error!"));let{code}=err,perr=code==="ECONNREFUSED"?nats_base_client_1.NatsError.errorForCode(nats_base_client_1.ErrorCode.ConnectionRefused,err):err;if(this.socket)this.socket.destroy();throw perr}})}dial(hp){let d=(0,nats_base_client_1.deferred)(),dialError,socket=(0,net_1.createConnection)(hp.port,hp.hostname,()=>{d.resolve(socket),socket.removeAllListeners()});return socket.on("error",(err)=>{dialError=err}),socket.on("close",()=>{socket.removeAllListeners(),d.reject(dialError)}),socket.setNoDelay(!0),d}get isClosed(){return this.done}close(err){return this._closed(err,!1)}peekInfo(){let d=(0,nats_base_client_1.deferred)(),peekError;return this.socket.on("data",(frame)=>{this.yields.push(frame);let t=nats_base_client_1.DataBuffer.concat(...this.yields),pm=(0,nats_base_client_1.extractProtocolMessage)(t);if(pm!=="")try{let m=nats_base_client_1.INFO.exec(pm);if(!m)throw Error("unexpected response from server");let info=JSON.parse(m[1]);d.resolve(info)}catch(err){d.reject(err)}finally{this.socket.removeAllListeners()}}),this.socket.on("error",(err)=>{peekError=err}),this.socket.on("close",()=>{this.socket.removeAllListeners(),d.reject(peekError)}),d}loadFile(fn){if(!fn)return Promise.resolve();let d=(0,nats_base_client_1.deferred)();try{if(fn=resolve4(fn),!existsSync14(fn))d.reject(Error(`${fn} doesn't exist`));readFile4(fn,(err,data)=>{if(err)return d.reject(err);d.resolve(data)})}catch(err){d.reject(err)}return d}loadClientCerts(){return __awaiter(this,void 0,void 0,function*(){let tlsOpts={},{certFile,cert,caFile,ca,keyFile,key}=this.options.tls;try{if(certFile){let data=yield this.loadFile(certFile);if(data)tlsOpts.cert=data}else if(cert)tlsOpts.cert=cert;if(keyFile){let data=yield this.loadFile(keyFile);if(data)tlsOpts.key=data}else if(key)tlsOpts.key=key;if(caFile){let data=yield this.loadFile(caFile);if(data)tlsOpts.ca=[data]}else if(ca)tlsOpts.ca=ca;return Promise.resolve(tlsOpts)}catch(err){return Promise.reject(err)}})}tlsFirst(hp){return __awaiter(this,void 0,void 0,function*(){let tlsError,tlsOpts={servername:this.tlsName,rejectUnauthorized:!0};if(this.socket)tlsOpts.socket=this.socket;if(typeof this.options.tls==="object")try{let certOpts=(yield this.loadClientCerts())||{};tlsOpts=(0,util_1.extend)(tlsOpts,this.options.tls,certOpts)}catch(err){return Promise.reject(new nats_base_client_1.NatsError(err.message,nats_base_client_1.ErrorCode.Tls,err))}let d=(0,nats_base_client_1.deferred)();try{let tlsSocket=(0,tls_1.connect)(hp.port,hp.hostname,tlsOpts,()=>{tlsSocket.removeAllListeners(),d.resolve(tlsSocket)});tlsSocket.on("error",(err)=>{tlsError=err}),tlsSocket.on("secureConnect",()=>{if(tlsOpts.rejectUnauthorized===!1)return;if(!tlsSocket.authorized)throw tlsSocket.authorizationError}),tlsSocket.on("close",()=>{d.reject(tlsError),tlsSocket.removeAllListeners()}),tlsSocket.setNoDelay(!0)}catch(err){d.reject(nats_base_client_1.NatsError.errorForCode(nats_base_client_1.ErrorCode.Tls,err))}return d})}startTLS(){return __awaiter(this,void 0,void 0,function*(){let tlsError,tlsOpts={socket:this.socket,servername:this.tlsName,rejectUnauthorized:!0};if(typeof this.options.tls==="object")try{let certOpts=(yield this.loadClientCerts())||{};tlsOpts=(0,util_1.extend)(tlsOpts,this.options.tls,certOpts)}catch(err){return Promise.reject(new nats_base_client_1.NatsError(err.message,nats_base_client_1.ErrorCode.Tls,err))}let d=(0,nats_base_client_1.deferred)();try{let tlsSocket=(0,tls_1.connect)(tlsOpts,()=>{tlsSocket.removeAllListeners(),d.resolve(tlsSocket)});tlsSocket.on("error",(err)=>{tlsError=err}),tlsSocket.on("secureConnect",()=>{if(tlsOpts.rejectUnauthorized===!1)return;if(!tlsSocket.authorized)throw tlsSocket.authorizationError}),tlsSocket.on("close",()=>{d.reject(tlsError),tlsSocket.removeAllListeners()})}catch(err){d.reject(nats_base_client_1.NatsError.errorForCode(nats_base_client_1.ErrorCode.Tls,err))}return d})}setupHandlers(){let connError;this.socket.on("data",(frame)=>{return this.yields.push(frame),this.signal.resolve()}),this.socket.on("error",(err)=>{connError=err}),this.socket.on("end",()=>{var _a,_b;if((_a=this.socket)===null||_a===void 0?void 0:_a.destroyed)return;(_b=this.socket)===null||_b===void 0||_b.write(new Uint8Array(0),()=>{var _a2;(_a2=this.socket)===null||_a2===void 0||_a2.end()})}),this.socket.on("close",()=>{this._closed(connError,!1)})}[Symbol.asyncIterator](){return this.iterate()}iterate(){return __asyncGenerator(this,arguments,function*(){let debug=this.options.debug;while(!0){if(this.yields.length===0)yield __await(this.signal);let yields=this.yields;this.yields=[];for(let i2=0;i2<yields.length;i2++){if(debug)console.info(`> ${(0,nats_base_client_1.render)(yields[i2])}`);yield yield __await(yields[i2])}if(this.done)break;else if(this.yields.length===0)yields.length=0,this.yields=yields,this.signal=(0,nats_base_client_1.deferred)()}})}discard(){}disconnect(){this._closed(void 0,!0).then().catch()}isEncrypted(){return this.socket instanceof tls_1.TLSSocket}_send(frame){if(this.isClosed||this.socket===void 0)return Promise.resolve();if(this.options.debug)console.info(`< ${(0,nats_base_client_1.render)(frame)}`);let d=(0,nats_base_client_1.deferred)();try{this.socket.write(frame,(err)=>{if(err){if(this.options.debug)console.error(`!!! ${(0,nats_base_client_1.render)(frame)}: ${err}`);return d.reject(err)}return d.resolve()})}catch(err){if(this.options.debug)console.error(`!!! ${(0,nats_base_client_1.render)(frame)}: ${err}`);d.reject(err)}return d}send(frame){this._send(frame).catch((_err)=>{})}_closed(err_1){return __awaiter(this,arguments,void 0,function*(err,internal=!0){if(!this.connected)return;if(this.done)return;if(this.closeError=err,!err&&this.socket&&internal)try{yield this._send(new TextEncoder().encode(""))}catch(err2){if(this.options.debug)console.log("transport close terminated with an error",err2)}try{if(this.socket)this.socket.removeAllListeners(),this.socket.destroy(),this.socket=void 0}catch(err2){console.log(err2)}this.done=!0,this.closedNotification.resolve(this.closeError)})}closed(){return this.closedNotification}}exports.NodeTransport=NodeTransport;function nodeResolveHost(s){return __awaiter(this,void 0,void 0,function*(){let a=(0,nats_base_client_1.deferred)(),aaaa=(0,nats_base_client_1.deferred)();dns.resolve4(s,(err,records)=>{if(err)a.resolve(err);else a.resolve(records)}),dns.resolve6(s,(err,records)=>{if(err)aaaa.resolve(err);else aaaa.resolve(records)});let ips=[],da=yield a;if(Array.isArray(da))ips.push(...da);let daaaa=yield aaaa;if(Array.isArray(daaaa))ips.push(...daaaa);if(ips.length===0)ips.push(s);return ips})}});var require_connect=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.connect=connect;var node_transport_1=require_node_transport(),nats_base_client_1=require_nats_base_client();function connect(opts={}){return(0,nats_base_client_1.setTransportFactory)({factory:()=>{return new node_transport_1.NodeTransport},dnsResolveFn:node_transport_1.nodeResolveHost}),nats_base_client_1.NatsConnectionImpl.connect(opts)}});var require_mod3=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.consumerOpts=exports.StoreCompression=exports.StorageType=exports.RetentionPolicy=exports.RepublishHeaders=exports.ReplayPolicy=exports.KvWatchInclude=exports.JsHeaders=exports.DiscardPolicy=exports.DirectMsgHeaders=exports.DeliverPolicy=exports.ConsumerEvents=exports.ConsumerDebugEvents=exports.AdvisoryKind=exports.AckPolicy=exports.isHeartbeatMsg=exports.isFlowControlMsg=exports.checkJsError=void 0;var internal_mod_1=require_internal_mod2();Object.defineProperty(exports,"checkJsError",{enumerable:!0,get:function(){return internal_mod_1.checkJsError}});Object.defineProperty(exports,"isFlowControlMsg",{enumerable:!0,get:function(){return internal_mod_1.isFlowControlMsg}});Object.defineProperty(exports,"isHeartbeatMsg",{enumerable:!0,get:function(){return internal_mod_1.isHeartbeatMsg}});var internal_mod_2=require_internal_mod2();Object.defineProperty(exports,"AckPolicy",{enumerable:!0,get:function(){return internal_mod_2.AckPolicy}});Object.defineProperty(exports,"AdvisoryKind",{enumerable:!0,get:function(){return internal_mod_2.AdvisoryKind}});Object.defineProperty(exports,"ConsumerDebugEvents",{enumerable:!0,get:function(){return internal_mod_2.ConsumerDebugEvents}});Object.defineProperty(exports,"ConsumerEvents",{enumerable:!0,get:function(){return internal_mod_2.ConsumerEvents}});Object.defineProperty(exports,"DeliverPolicy",{enumerable:!0,get:function(){return internal_mod_2.DeliverPolicy}});Object.defineProperty(exports,"DirectMsgHeaders",{enumerable:!0,get:function(){return internal_mod_2.DirectMsgHeaders}});Object.defineProperty(exports,"DiscardPolicy",{enumerable:!0,get:function(){return internal_mod_2.DiscardPolicy}});Object.defineProperty(exports,"JsHeaders",{enumerable:!0,get:function(){return internal_mod_2.JsHeaders}});Object.defineProperty(exports,"KvWatchInclude",{enumerable:!0,get:function(){return internal_mod_2.KvWatchInclude}});Object.defineProperty(exports,"ReplayPolicy",{enumerable:!0,get:function(){return internal_mod_2.ReplayPolicy}});Object.defineProperty(exports,"RepublishHeaders",{enumerable:!0,get:function(){return internal_mod_2.RepublishHeaders}});Object.defineProperty(exports,"RetentionPolicy",{enumerable:!0,get:function(){return internal_mod_2.RetentionPolicy}});Object.defineProperty(exports,"StorageType",{enumerable:!0,get:function(){return internal_mod_2.StorageType}});Object.defineProperty(exports,"StoreCompression",{enumerable:!0,get:function(){return internal_mod_2.StoreCompression}});var types_1=require_types2();Object.defineProperty(exports,"consumerOpts",{enumerable:!0,get:function(){return types_1.consumerOpts}})});var require_mod4=__commonJS((exports)=>{var __createBinding=exports&&exports.__createBinding||(Object.create?function(o,m,k,k2){if(k2===void 0)k2=k;var desc=Object.getOwnPropertyDescriptor(m,k);if(!desc||("get"in desc?!m.__esModule:desc.writable||desc.configurable))desc={enumerable:!0,get:function(){return m[k]}};Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){if(k2===void 0)k2=k;o[k2]=m[k]}),__exportStar=exports&&exports.__exportStar||function(m,exports2){for(var p in m)if(p!=="default"&&!Object.prototype.hasOwnProperty.call(exports2,p))__createBinding(exports2,m,p)};Object.defineProperty(exports,"__esModule",{value:!0});exports.connect=void 0;if(typeof TextEncoder>"u"){let{TextEncoder:TextEncoder2,TextDecoder:TextDecoder2}=__require("util");global.TextEncoder=TextEncoder2,global.TextDecoder=TextDecoder2}if(typeof globalThis.crypto>"u"){let c=__require("crypto");global.crypto=c.webcrypto}if(typeof globalThis.ReadableStream>"u"){let chunks=process.versions.node.split(".");if(parseInt(chunks[0])>=16){let streams=__require("stream/web");global.ReadableStream=streams.ReadableStream}}var connect_1=require_connect();Object.defineProperty(exports,"connect",{enumerable:!0,get:function(){return connect_1.connect}});__exportStar(require_mod2(),exports);__exportStar(require_mod3(),exports)});var exports_nats_client={};__export(exports_nats_client,{subscribe:()=>subscribe,publish:()=>publish,isAvailable:()=>isAvailable,close:()=>close,_resetForTesting:()=>_resetForTesting});function getNatsUrl(){return process.env.GENIE_NATS_URL||"nats://localhost:4222"}function warnOnce(key,message){if(!state[key])state[key]=!0,console.warn(`[genie:nats] ${message}`)}async function importNats(){try{return await Promise.resolve().then(() => __toESM(require_mod4(),1))}catch{return warnOnce("warnedMissingPackage","nats package not installed \u2014 real-time features disabled"),null}}async function ensureConnection(){if(state.connection&&!state.connection.isClosed())return resetIdleTimer(),!0;if(state.connecting)return state.connecting;return state.connecting=(async()=>{try{let natsModule=await importNats();if(!natsModule)return!1;return state.connection=await natsModule.connect({servers:getNatsUrl(),maxReconnectAttempts:2,reconnectTimeWait:500,timeout:2000}),state.codec=natsModule.JSONCodec(),state.warnedUnavailable=!1,resetIdleTimer(),!0}catch{return state.connection=null,state.codec=null,warnOnce("warnedUnavailable",`NATS not available at ${getNatsUrl()}`),!1}finally{state.connecting=null}})(),state.connecting}function resetIdleTimer(){if(state.idleTimer)clearTimeout(state.idleTimer);if(state.activeSubscriptions>0)return;state.idleTimer=setTimeout(()=>{if(state.activeSubscriptions===0)close().catch(()=>{})},500)}async function publish(subject,data){if(!await ensureConnection()||!state.connection||!state.codec)return;try{state.connection.publish(subject,state.codec.encode(data))}catch{}resetIdleTimer()}async function subscribe(subject,callback){let noop={unsubscribe:()=>{}};if(!await ensureConnection()||!state.connection||!state.codec)return noop;try{let sub=state.connection.subscribe(subject),codec=state.codec;if(state.activeSubscriptions++,state.idleTimer)clearTimeout(state.idleTimer),state.idleTimer=null;return(async()=>{try{for await(let msg of sub)try{callback(msg.subject,codec.decode(msg.data))}catch{}}catch{}})(),{unsubscribe:()=>{try{sub.unsubscribe()}catch{}state.activeSubscriptions=Math.max(0,state.activeSubscriptions-1),resetIdleTimer()}}}catch{return noop}}async function isAvailable(){return ensureConnection()}async function close(){if(state.idleTimer)clearTimeout(state.idleTimer),state.idleTimer=null;if(state.connection){try{await state.connection.drain()}catch{try{await state.connection.close()}catch{}}state.connection=null,state.codec=null}}function _resetForTesting(){if(state.connection=null,state.codec=null,state.connecting=null,state.warnedUnavailable=!1,state.warnedMissingPackage=!1,state.activeSubscriptions=0,state.idleTimer)clearTimeout(state.idleTimer);state.idleTimer=null}var state;var init_nats_client=__esm(()=>{state={connection:null,codec:null,connecting:null,warnedUnavailable:!1,warnedMissingPackage:!1,activeSubscriptions:0,idleTimer:null}});function isBlockingEvent(event){return BLOCKING_EVENTS.has(event)}var DISPATCHED_EVENTS,BLOCKING_EVENTS;var init_types2=__esm(()=>{DISPATCHED_EVENTS=["PreToolUse","PostToolUse","SessionStart","SessionEnd","TeammateIdle","TaskCompleted"],BLOCKING_EVENTS=new Set(["PreToolUse","UserPromptSubmit","TeammateIdle","TaskCompleted","PermissionRequest"])});function buildLayoutCommand(windowTarget,mode="mosaic"){return`select-layout -t '${windowTarget}' ${mode==="vertical"?"even-horizontal":"tiled"}`}function resolveLayoutMode(layoutFlag){if(layoutFlag==="vertical")return"vertical";return"mosaic"}import{randomUUID}from"crypto";var native_default;var init_native=__esm(()=>{native_default={randomUUID}});import{randomFillSync}from"crypto";function rng(){if(poolPtr>rnds8Pool.length-16)randomFillSync(rnds8Pool),poolPtr=0;return rnds8Pool.slice(poolPtr,poolPtr+=16)}var rnds8Pool,poolPtr;var init_rng=__esm(()=>{rnds8Pool=new Uint8Array(256),poolPtr=rnds8Pool.length});function unsafeStringify(arr,offset=0){return(byteToHex[arr[offset+0]]+byteToHex[arr[offset+1]]+byteToHex[arr[offset+2]]+byteToHex[arr[offset+3]]+"-"+byteToHex[arr[offset+4]]+byteToHex[arr[offset+5]]+"-"+byteToHex[arr[offset+6]]+byteToHex[arr[offset+7]]+"-"+byteToHex[arr[offset+8]]+byteToHex[arr[offset+9]]+"-"+byteToHex[arr[offset+10]]+byteToHex[arr[offset+11]]+byteToHex[arr[offset+12]]+byteToHex[arr[offset+13]]+byteToHex[arr[offset+14]]+byteToHex[arr[offset+15]]).toLowerCase()}var byteToHex;var init_stringify=__esm(()=>{byteToHex=[];for(let i2=0;i2<256;++i2)byteToHex.push((i2+256).toString(16).slice(1))});function v4(options,buf,offset){if(native_default.randomUUID&&!buf&&!options)return native_default.randomUUID();options=options||{};let rnds=options.random??options.rng?.()??rng();if(rnds.length<16)throw Error("Random bytes length must be >= 16");if(rnds[6]=rnds[6]&15|64,rnds[8]=rnds[8]&63|128,buf){if(offset=offset||0,offset<0||offset+16>buf.length)throw RangeError(`UUID byte range ${offset}:${offset+15} is out of buffer bounds`);for(let i2=0;i2<16;++i2)buf[offset+i2]=rnds[i2];return buf}return unsafeStringify(rnds)}var v4_default;var init_v4=__esm(()=>{init_native();init_rng();init_stringify();v4_default=v4});var init_esm7=__esm(()=>{init_v4()});import{appendFile,mkdir as mkdir5,readFile as readFile4,writeFile as writeFile4}from"fs/promises";import path,{join as join15}from"path";function mailboxDir(repoPath){return join15(repoPath,".genie","mailbox")}function mailboxFilePath(repoPath,workerId){let safeId=path.basename(workerId);return join15(mailboxDir(repoPath),`${safeId}.json`)}function outboxFilePath(repoPath,workerId){let safeId=path.basename(workerId);return join15(mailboxDir(repoPath),`${safeId}-sent.jsonl`)}async function loadMailbox(repoPath,workerId){try{let content=await readFile4(mailboxFilePath(repoPath,workerId),"utf-8");return JSON.parse(content)}catch{return{workerId,messages:[],lastUpdated:new Date().toISOString()}}}async function saveMailbox(repoPath,mailbox){let dir=mailboxDir(repoPath);await mkdir5(dir,{recursive:!0}),mailbox.lastUpdated=new Date().toISOString(),await writeFile4(mailboxFilePath(repoPath,mailbox.workerId),JSON.stringify(mailbox,null,2))}function generateMessageId(){return`msg-${v4_default()}`}async function send(repoPath,from,to,body){await mkdir5(mailboxDir(repoPath),{recursive:!0});let release=await acquireLock(mailboxFilePath(repoPath,to));try{let mailbox=await loadMailbox(repoPath,to),message={id:generateMessageId(),from,to,body,createdAt:new Date().toISOString(),read:!1,deliveredAt:null};mailbox.messages.push(message),await saveMailbox(repoPath,mailbox),await appendFile(outboxFilePath(repoPath,from),`${JSON.stringify(message)}
130
130
  `);try{let{publish:publish2}=await Promise.resolve().then(() => (init_nats_client(),exports_nats_client));await publish2(`genie.msg.${to}`,{timestamp:message.createdAt,kind:"message",agent:from,direction:"out",peer:to,text:body,data:{messageId:message.id,from,to},source:"mailbox"})}catch{}return message}finally{await release()}}async function inbox(repoPath,workerId){return(await loadMailbox(repoPath,workerId)).messages}async function readOutbox(repoPath,workerId){try{let lines=(await readFile4(outboxFilePath(repoPath,workerId),"utf-8")).trim().split(`
131
- `).filter(Boolean),messages=[];for(let line of lines)try{messages.push(JSON.parse(line))}catch{}return messages}catch{return[]}}async function markDelivered(repoPath,workerId,messageId){await mkdir5(mailboxDir(repoPath),{recursive:!0});let release=await acquireLock(mailboxFilePath(repoPath,workerId));try{let mailbox=await loadMailbox(repoPath,workerId),msg=mailbox.messages.find((m)=>m.id===messageId);if(!msg)return!1;return msg.deliveredAt=new Date().toISOString(),await saveMailbox(repoPath,mailbox),!0}finally{await release()}}function toNativeInboxMessage(msg,color="blue"){let words=msg.body.split(/\s+/),summary=words.slice(0,8).join(" ")+(words.length>8?"...":"");return{from:msg.from,text:msg.body,summary,timestamp:msg.createdAt,color,read:!1}}var init_mailbox=__esm(()=>{init_esm7();init_file_lock()});var exports_team_manager={};__export(exports_team_manager,{validateBranchName:()=>validateBranchName,updateTeamConfig:()=>updateTeamConfig,setTeamStatus:()=>setTeamStatus,pruneStaleWorktrees:()=>pruneStaleWorktrees,listTeams:()=>listTeams2,listMembers:()=>listMembers,killTeamMembers:()=>killTeamMembers,hireAgent:()=>hireAgent,getTeam:()=>getTeam2,fireAgent:()=>fireAgent,disbandTeam:()=>disbandTeam,createTeam:()=>createTeam});import{existsSync as existsSync14}from"fs";import{mkdir as mkdir6,readFile as readFile5,readdir as readdir2,rm as rm3,unlink as unlink4,writeFile as writeFile5}from"fs/promises";import{homedir as homedir13}from"os";import path2,{join as join16}from"path";var{$:$3}=globalThis.Bun;function getGenieDir2(){return process.env.GENIE_HOME??join16(homedir13(),".genie")}function teamsDir(){return join16(getGenieDir2(),"teams")}function safeFileName(name){return name.replace(/\//g,"--")}function teamFilePath(name){let safeName=safeFileName(path2.basename(name)===name?name:name);return join16(teamsDir(),`${safeName}.json`)}function getWorktreeBase(repoPath){let base=loadGenieConfigSync().terminal?.worktreeBase;if(base&&base!==".worktrees"){if(path2.isAbsolute(base))return base;return join16(repoPath,base)}let projectName=path2.basename(repoPath);return join16(getGenieDir2(),"worktrees",projectName)}function validateBranchName(name){let errors3=[];if(/\s/.test(name))errors3.push("contains spaces");if(name.includes(".."))errors3.push('contains ".."');if(name.includes("~"))errors3.push('contains "~"');if(name.includes("^"))errors3.push('contains "^"');if(name.includes(":"))errors3.push('contains ":"');if(name.includes("?"))errors3.push('contains "?"');if(name.includes("*"))errors3.push('contains "*"');if(name.includes("["))errors3.push('contains "["');if(name.includes("\\"))errors3.push('contains "\\"');if(/[\x00-\x1f\x7f]/.test(name))errors3.push("contains control characters");if(name.endsWith(".lock"))errors3.push('ends with ".lock"');if(name.endsWith("/"))errors3.push('ends with "/"');if(name.endsWith("."))errors3.push('ends with "."');if(name.startsWith("-"))errors3.push('starts with "-"');if(errors3.length>0)throw Error(`Invalid team name '${name}': must be a valid git branch name (${errors3.join(", ")})`)}async function killWorkersByName(agentName,teamName){let matches=(await list()).filter((w)=>(w.role===agentName||w.id===agentName)&&(!teamName||w.team===teamName));for(let w of matches){try{if(w.paneId&&w.paneId!=="inline"){let{execSync:execSync3}=__require("child_process");execSync3(`tmux kill-pane -t ${w.paneId}`,{stdio:"ignore"})}}catch{}await unregister(w.id)}}async function ensureWorktree(repoPath,branchName,worktreePath,baseBranch){try{await $3`git -C ${repoPath} fetch origin ${baseBranch}`.quiet()}catch{}if(await mkdir6(path2.dirname(worktreePath),{recursive:!0}),existsSync14(worktreePath))return;let branchExists=!1;try{await $3`git -C ${repoPath} rev-parse --verify ${branchName}`.quiet(),branchExists=!0}catch{if(!branchExists)try{await $3`git -C ${repoPath} branch ${branchName} origin/${baseBranch}`.quiet()}catch{try{await $3`git -C ${repoPath} branch ${branchName} ${baseBranch}`.quiet()}catch{await $3`git -C ${repoPath} branch ${branchName}`.quiet()}}}await $3`git clone --shared --branch ${branchName} ${repoPath} ${worktreePath}`.quiet();try{let userName=(await $3`git -C ${repoPath} config user.name`.quiet()).text().trim(),userEmail=(await $3`git -C ${repoPath} config user.email`.quiet()).text().trim();if(userName)await $3`git -C ${worktreePath} config user.name ${userName}`.quiet();if(userEmail)await $3`git -C ${worktreePath} config user.email ${userEmail}`.quiet()}catch{}}async function createTeam(name,repo,baseBranch="dev"){validateBranchName(name);let repoPath=path2.resolve(repo),dir=teamsDir();await mkdir6(dir,{recursive:!0});let filePath=teamFilePath(name);if(existsSync14(filePath)){let content=await readFile5(filePath,"utf-8");return JSON.parse(content)}let worktreeBase=getWorktreeBase(repoPath),worktreePath=join16(worktreeBase,name);await ensureWorktree(repoPath,name,worktreePath,baseBranch);let now=new Date().toISOString(),config={name,repo:repoPath,baseBranch,worktreePath,members:[],status:"in_progress",createdAt:now};if(isInsideClaudeCode()){config.nativeTeamsEnabled=!0;try{let result=await registerAsTeamLead(name);config.nativeTeamParentSessionId=result.sessionId}catch{}}return await writeFile5(filePath,JSON.stringify(config,null,2)),config}async function hireAgent(teamName,agentName){let config=await getTeam2(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);let added;if(agentName==="council")added=BUILTIN_COUNCIL_MEMBERS.map((m)=>m.name).filter((n)=>!config.members.includes(n)),config.members.push(...added);else{if(config.members.includes(agentName))return[];config.members.push(agentName),added=[agentName]}let filePath=teamFilePath(teamName);return await writeFile5(filePath,JSON.stringify(config,null,2)),added}async function fireAgent(teamName,agentName){let config=await getTeam2(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);let idx=config.members.indexOf(agentName);if(idx===-1)return!1;config.members.splice(idx,1);let filePath=teamFilePath(teamName);await writeFile5(filePath,JSON.stringify(config,null,2));try{await killWorkersByName(agentName)}catch{}return!0}async function disbandTeam(teamName){let config=await getTeam2(teamName);if(!config)return!1;if(config.nativeTeamsEnabled)try{await deleteNativeTeam(teamName)}catch{}for(let member of config.members)try{await killWorkersByName(member,teamName)}catch{}if(config.worktreePath&&existsSync14(config.worktreePath))try{await rm3(config.worktreePath,{recursive:!0,force:!0})}catch{}let filePath=teamFilePath(teamName);try{await unlink4(filePath)}catch{return!1}return await pruneStaleWorktrees(config.repo),!0}async function pruneStaleWorktrees(_repoPath){let dir=teamsDir(),files;try{files=await readdir2(dir)}catch{return}for(let file of files){if(!file.endsWith(".json"))continue;try{let content=await readFile5(join16(dir,file),"utf-8"),config=JSON.parse(content);if(config.worktreePath&&!existsSync14(config.worktreePath))await unlink4(join16(dir,file))}catch{}}}async function updateTeamConfig(name,config){let filePath=teamFilePath(name);await writeFile5(filePath,JSON.stringify(config,null,2))}async function getTeam2(name){try{let content=await readFile5(teamFilePath(name),"utf-8");return JSON.parse(content)}catch{return null}}async function listTeams2(){let dir=teamsDir();try{let files=await readdir2(dir),teams=[];for(let file of files){if(!file.endsWith(".json"))continue;try{let content=await readFile5(join16(dir,file),"utf-8");teams.push(JSON.parse(content))}catch{}}return teams}catch{return[]}}async function listMembers(teamName){let config=await getTeam2(teamName);if(!config)return null;return config.members}async function killTeamMembers(teamName){let config=await getTeam2(teamName);if(!config)return;for(let member of config.members)try{await killWorkersByName(member,teamName)}catch{}}async function setTeamStatus(teamName,status){let filePath=teamFilePath(teamName),release=await acquireLock(filePath);try{let config=await getTeam2(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);config.status=status,await writeFile5(filePath,JSON.stringify(config,null,2))}finally{await release()}}var init_team_manager=__esm(()=>{init_agent_registry();init_builtin_agents();init_claude_native_teams();init_file_lock();init_genie_config2()});import{readdirSync as readdirSync4}from"fs";import{join as join17}from"path";function getMigrationsDir(){return join17(import.meta.dir,"..","db","migrations")}async function loadMigrationFiles(){let candidates=[getMigrationsDir(),join17(process.cwd(),"src","db","migrations")];for(let dir of candidates)try{let files=readdirSync4(dir).filter((f)=>f.endsWith(".sql")).sort();if(files.length===0)continue;let migrations=[];for(let file of files){let content=await Bun.file(join17(dir,file)).text();migrations.push({name:file.replace(/\.sql$/,""),sql:content})}return migrations}catch{}return[]}async function ensureMigrationsTable(sql){await sql`
131
+ `).filter(Boolean),messages=[];for(let line of lines)try{messages.push(JSON.parse(line))}catch{}return messages}catch{return[]}}async function markDelivered(repoPath,workerId,messageId){await mkdir5(mailboxDir(repoPath),{recursive:!0});let release=await acquireLock(mailboxFilePath(repoPath,workerId));try{let mailbox=await loadMailbox(repoPath,workerId),msg=mailbox.messages.find((m)=>m.id===messageId);if(!msg)return!1;return msg.deliveredAt=new Date().toISOString(),await saveMailbox(repoPath,mailbox),!0}finally{await release()}}function toNativeInboxMessage(msg,color="blue"){let words=msg.body.split(/\s+/),summary=words.slice(0,8).join(" ")+(words.length>8?"...":"");return{from:msg.from,text:msg.body,summary,timestamp:msg.createdAt,color,read:!1}}var init_mailbox=__esm(()=>{init_esm7();init_file_lock()});var exports_team_manager={};__export(exports_team_manager,{validateBranchName:()=>validateBranchName,updateTeamConfig:()=>updateTeamConfig,setTeamStatus:()=>setTeamStatus,pruneStaleWorktrees:()=>pruneStaleWorktrees,listTeams:()=>listTeams2,listMembers:()=>listMembers,killTeamMembers:()=>killTeamMembers,hireAgent:()=>hireAgent,getTeam:()=>getTeam2,fireAgent:()=>fireAgent,disbandTeam:()=>disbandTeam,createTeam:()=>createTeam});import{existsSync as existsSync14}from"fs";import{mkdir as mkdir6,readFile as readFile5,readdir as readdir2,rm as rm3,unlink as unlink4,writeFile as writeFile5}from"fs/promises";import{homedir as homedir13}from"os";import path2,{join as join16}from"path";var{$:$3}=globalThis.Bun;function getGenieDir2(){return process.env.GENIE_HOME??join16(homedir13(),".genie")}function teamsDir(){return join16(getGenieDir2(),"teams")}function safeFileName(name){return name.replace(/\//g,"--")}function teamFilePath(name){let safeName=safeFileName(path2.basename(name)===name?name:name);return join16(teamsDir(),`${safeName}.json`)}function getWorktreeBase(repoPath){let base=loadGenieConfigSync().terminal?.worktreeBase;if(base&&base!==".worktrees"){if(path2.isAbsolute(base))return base;return join16(repoPath,base)}let projectName=path2.basename(repoPath);return join16(getGenieDir2(),"worktrees",projectName)}function validateBranchName(name){let errors3=[];if(/\s/.test(name))errors3.push("contains spaces");if(name.includes(".."))errors3.push('contains ".."');if(name.includes("~"))errors3.push('contains "~"');if(name.includes("^"))errors3.push('contains "^"');if(name.includes(":"))errors3.push('contains ":"');if(name.includes("?"))errors3.push('contains "?"');if(name.includes("*"))errors3.push('contains "*"');if(name.includes("["))errors3.push('contains "["');if(name.includes("\\"))errors3.push('contains "\\"');if(/[\x00-\x1f\x7f]/.test(name))errors3.push("contains control characters");if(name.endsWith(".lock"))errors3.push('ends with ".lock"');if(name.endsWith("/"))errors3.push('ends with "/"');if(name.endsWith("."))errors3.push('ends with "."');if(name.startsWith("-"))errors3.push('starts with "-"');if(errors3.length>0)throw Error(`Invalid team name '${name}': must be a valid git branch name (${errors3.join(", ")})`)}async function killWorkersByName(agentName,teamName){let matches=(await list()).filter((w)=>(w.role===agentName||w.id===agentName)&&(!teamName||w.team===teamName));for(let w of matches){try{if(w.paneId&&w.paneId!=="inline"){let{execSync:execSync3}=__require("child_process");execSync3(`tmux kill-pane -t ${w.paneId}`,{stdio:"ignore"})}}catch{}await unregister(w.id)}}async function ensureWorktree(repoPath,branchName,worktreePath,baseBranch){try{await $3`git -C ${repoPath} fetch origin ${baseBranch}`.quiet()}catch{}if(await mkdir6(path2.dirname(worktreePath),{recursive:!0}),existsSync14(worktreePath))return;let branchExists=!1;try{await $3`git -C ${repoPath} rev-parse --verify ${branchName}`.quiet(),branchExists=!0}catch{if(!branchExists)try{await $3`git -C ${repoPath} branch ${branchName} origin/${baseBranch}`.quiet()}catch{try{await $3`git -C ${repoPath} branch ${branchName} ${baseBranch}`.quiet()}catch{await $3`git -C ${repoPath} branch ${branchName}`.quiet()}}}await $3`git clone --shared --branch ${branchName} ${repoPath} ${worktreePath}`.quiet();try{let userName=(await $3`git -C ${repoPath} config user.name`.quiet()).text().trim(),userEmail=(await $3`git -C ${repoPath} config user.email`.quiet()).text().trim();if(userName)await $3`git -C ${worktreePath} config user.name ${userName}`.quiet();if(userEmail)await $3`git -C ${worktreePath} config user.email ${userEmail}`.quiet()}catch{}}async function createTeam(name,repo,baseBranch="dev"){validateBranchName(name);let repoPath=path2.resolve(repo),dir=teamsDir();await mkdir6(dir,{recursive:!0});let filePath=teamFilePath(name);if(existsSync14(filePath)){let content=await readFile5(filePath,"utf-8");return JSON.parse(content)}let worktreeBase=getWorktreeBase(repoPath),worktreePath=join16(worktreeBase,name);await ensureWorktree(repoPath,name,worktreePath,baseBranch);let now=new Date().toISOString(),config={name,repo:repoPath,baseBranch,worktreePath,members:[],status:"in_progress",createdAt:now};if(isInsideClaudeCode()){config.nativeTeamsEnabled=!0;try{let result=await registerAsTeamLead(name);config.nativeTeamParentSessionId=result.sessionId}catch{}}return await writeFile5(filePath,JSON.stringify(config,null,2)),config}async function hireAgent(teamName,agentName){let config=await getTeam2(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);let added;if(agentName==="council")added=BUILTIN_COUNCIL_MEMBERS.map((m)=>m.name).filter((n)=>!config.members.includes(n)),config.members.push(...added);else{if(config.members.includes(agentName))return[];config.members.push(agentName),added=[agentName]}let filePath=teamFilePath(teamName);return await writeFile5(filePath,JSON.stringify(config,null,2)),added}async function fireAgent(teamName,agentName){let config=await getTeam2(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);let idx=config.members.indexOf(agentName);if(idx===-1)return!1;config.members.splice(idx,1);let filePath=teamFilePath(teamName);await writeFile5(filePath,JSON.stringify(config,null,2));try{await killWorkersByName(agentName)}catch{}return!0}async function disbandTeam(teamName){let config=await getTeam2(teamName);if(!config)return!1;try{await deleteNativeTeam(teamName)}catch{}for(let member of config.members)try{await killWorkersByName(member,teamName)}catch{}if(config.worktreePath&&existsSync14(config.worktreePath))try{await rm3(config.worktreePath,{recursive:!0,force:!0})}catch{}let filePath=teamFilePath(teamName);try{await unlink4(filePath)}catch{return!1}return await pruneStaleWorktrees(config.repo),!0}async function pruneStaleWorktrees(_repoPath){let dir=teamsDir(),files;try{files=await readdir2(dir)}catch{return}for(let file of files){if(!file.endsWith(".json"))continue;try{let content=await readFile5(join16(dir,file),"utf-8"),config=JSON.parse(content);if(config.worktreePath&&!existsSync14(config.worktreePath)){try{await deleteNativeTeam(config.name)}catch{}await unlink4(join16(dir,file))}}catch{}}}async function updateTeamConfig(name,config){let filePath=teamFilePath(name);await writeFile5(filePath,JSON.stringify(config,null,2))}async function getTeam2(name){try{let content=await readFile5(teamFilePath(name),"utf-8");return JSON.parse(content)}catch{return null}}async function listTeams2(){let dir=teamsDir();try{let files=await readdir2(dir),teams=[];for(let file of files){if(!file.endsWith(".json"))continue;try{let content=await readFile5(join16(dir,file),"utf-8");teams.push(JSON.parse(content))}catch{}}return teams}catch{return[]}}async function listMembers(teamName){let config=await getTeam2(teamName);if(!config)return null;return config.members}async function killTeamMembers(teamName){let config=await getTeam2(teamName);if(!config)return;for(let member of config.members)try{await killWorkersByName(member,teamName)}catch{}}async function setTeamStatus(teamName,status){let filePath=teamFilePath(teamName),release=await acquireLock(filePath);try{let config=await getTeam2(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);config.status=status,await writeFile5(filePath,JSON.stringify(config,null,2))}finally{await release()}}var init_team_manager=__esm(()=>{init_agent_registry();init_builtin_agents();init_claude_native_teams();init_file_lock();init_genie_config2()});import{readdirSync as readdirSync4}from"fs";import{join as join17}from"path";function getMigrationsDir(){return join17(import.meta.dir,"..","db","migrations")}async function loadMigrationFiles(){let candidates=[getMigrationsDir(),join17(process.cwd(),"src","db","migrations")];for(let dir of candidates)try{let files=readdirSync4(dir).filter((f)=>f.endsWith(".sql")).sort();if(files.length===0)continue;let migrations=[];for(let file of files){let content=await Bun.file(join17(dir,file)).text();migrations.push({name:file.replace(/\.sql$/,""),sql:content})}return migrations}catch{}return[]}async function ensureMigrationsTable(sql){await sql`
132
132
  CREATE TABLE IF NOT EXISTS _genie_migrations (
133
133
  id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
134
134
  name TEXT UNIQUE NOT NULL,
@@ -160,7 +160,7 @@ ${errCtx.stack}`;d.reject(err)}else d.resolve(msg)}});return sub.requestSubject=
160
160
  from (select lo_tell64($1) as location) tell
161
161
  ) seek
162
162
  `};return resolve4(lo),new Promise(async(r)=>finish=r);async function readable({highWaterMark=16384,start=0,end=1/0}={}){let max=end-start;return start&&await lo.seek(start),new Stream2.Readable({highWaterMark,async read(size){let l=size>max?size-max:size;max-=size;let[{data}]=await lo.read(l);if(this.push(data),data.length<size)this.push(null)}})}async function writable({highWaterMark=16384,start=0}={}){return start&&await lo.seek(start),new Stream2.Writable({highWaterMark,write(chunk,encoding,callback){lo.write(chunk).then(()=>callback(),callback)}})}}).catch(reject)})}var init_large=()=>{};var exports_src={};__export(exports_src,{default:()=>src_default});import os from"os";import fs from"fs";function Postgres(a,b2){let options=parseOptions(a,b2),subscribe2=options.no_subscribe||Subscribe(Postgres,{...options}),ending=!1,queries=queue_default(),connecting=queue_default(),reserved=queue_default(),closed=queue_default(),ended=queue_default(),open2=queue_default(),busy=queue_default(),full=queue_default(),queues={connecting,reserved,closed,ended,open:open2,busy,full},connections=[...Array(options.max)].map(()=>connection_default(options,queues,{onopen,onend,onclose})),sql=Sql(handler);return Object.assign(sql,{get parameters(){return options.parameters},largeObject:largeObject.bind(null,sql),subscribe:subscribe2,CLOSE,END:CLOSE,PostgresError,options,reserve,listen,begin,close:close2,end}),sql;function Sql(handler2){return handler2.debug=options.debug,Object.entries(options.types).reduce((acc,[name,type2])=>{return acc[name]=(x)=>new Parameter(x,type2.to),acc},typed),Object.assign(sql2,{types:typed,typed,unsafe,notify,array,json:json2,file}),sql2;function typed(value,type2){return new Parameter(value,type2)}function sql2(strings,...args){return strings&&Array.isArray(strings.raw)?new Query(strings,args,handler2,cancel):typeof strings==="string"&&!args.length?new Identifier(options.transform.column.to?options.transform.column.to(strings):strings):new Builder(strings,args)}function unsafe(string,args=[],options2={}){return arguments.length===2&&!Array.isArray(args)&&(options2=args,args=[]),new Query([string],args,handler2,cancel,{prepare:!1,...options2,simple:"simple"in options2?options2.simple:args.length===0})}function file(path3,args=[],options2={}){return arguments.length===2&&!Array.isArray(args)&&(options2=args,args=[]),new Query([],args,(query2)=>{fs.readFile(path3,"utf8",(err,string)=>{if(err)return query2.reject(err);query2.strings=[string],handler2(query2)})},cancel,{...options2,simple:"simple"in options2?options2.simple:args.length===0})}}async function listen(name,fn,onlisten){let listener={fn,onlisten},sql2=listen.sql||(listen.sql=Postgres({...options,max:1,idle_timeout:null,max_lifetime:null,fetch_types:!1,onclose(){Object.entries(listen.channels).forEach(([name2,{listeners}])=>{delete listen.channels[name2],Promise.all(listeners.map((l)=>listen(name2,l.fn,l.onlisten).catch(()=>{})))})},onnotify(c,x){c in listen.channels&&listen.channels[c].listeners.forEach((l)=>l.fn(x))}})),channels=listen.channels||(listen.channels={});if(name in channels){channels[name].listeners.push(listener);let result2=await channels[name].result;return listener.onlisten&&listener.onlisten(),{state:result2.state,unlisten}}channels[name]={result:sql2`listen ${sql2.unsafe('"'+name.replace(/"/g,'""')+'"')}`,listeners:[listener]};let result=await channels[name].result;return listener.onlisten&&listener.onlisten(),{state:result.state,unlisten};async function unlisten(){if(name in channels===!1)return;if(channels[name].listeners=channels[name].listeners.filter((x)=>x!==listener),channels[name].listeners.length)return;return delete channels[name],sql2`unlisten ${sql2.unsafe('"'+name.replace(/"/g,'""')+'"')}`}}async function notify(channel,payload){return await sql`select pg_notify(${channel}, ${""+payload})`}async function reserve(){let queue=queue_default(),c=open2.length?open2.shift():await new Promise((resolve4,reject)=>{let query={reserve:resolve4,reject};queries.push(query),closed.length&&connect(closed.shift(),query)});move(c,reserved),c.reserved=()=>queue.length?c.execute(queue.shift()):move(c,reserved),c.reserved.release=!0;let sql2=Sql(handler2);return sql2.release=()=>{c.reserved=null,onopen(c)},sql2;function handler2(q){c.queue===full?queue.push(q):c.execute(q)||move(c,full)}}async function begin(options2,fn){!fn&&(fn=options2,options2="");let queries2=queue_default(),savepoints=0,connection2,prepare=null;try{return await sql.unsafe("begin "+options2.replace(/[^a-z ]/ig,""),[],{onexecute}).execute(),await Promise.race([scope(connection2,fn),new Promise((_,reject)=>connection2.onclose=reject)])}catch(error2){throw error2}async function scope(c,fn2,name){let sql2=Sql(handler2);sql2.savepoint=savepoint,sql2.prepare=(x)=>prepare=x.replace(/[^a-z0-9$-_. ]/gi);let uncaughtError,result;name&&await sql2`savepoint ${sql2(name)}`;try{if(result=await new Promise((resolve4,reject)=>{let x=fn2(sql2);Promise.resolve(Array.isArray(x)?Promise.all(x):x).then(resolve4,reject)}),uncaughtError)throw uncaughtError}catch(e){throw await(name?sql2`rollback to ${sql2(name)}`:sql2`rollback`),e instanceof PostgresError&&e.code==="25P02"&&uncaughtError||e}if(!name)prepare?await sql2`prepare transaction '${sql2.unsafe(prepare)}'`:await sql2`commit`;return result;function savepoint(name2,fn3){if(name2&&Array.isArray(name2.raw))return savepoint((sql3)=>sql3.apply(sql3,arguments));return arguments.length===1&&(fn3=name2,name2=null),scope(c,fn3,"s"+savepoints+++(name2?"_"+name2:""))}function handler2(q){q.catch((e)=>uncaughtError||(uncaughtError=e)),c.queue===full?queries2.push(q):c.execute(q)||move(c,full)}}function onexecute(c){connection2=c,move(c,reserved),c.reserved=()=>queries2.length?c.execute(queries2.shift()):move(c,reserved)}}function move(c,queue){return c.queue.remove(c),queue.push(c),c.queue=queue,queue===open2?c.idleTimer.start():c.idleTimer.cancel(),c}function json2(x){return new Parameter(x,3802)}function array(x,type2){if(!Array.isArray(x))return array(Array.from(arguments));return new Parameter(x,type2||(x.length?inferType(x)||25:0),options.shared.typeArrayMap)}function handler(query){if(ending)return query.reject(Errors.connection("CONNECTION_ENDED",options,options));if(open2.length)return go(open2.shift(),query);if(closed.length)return connect(closed.shift(),query);busy.length?go(busy.shift(),query):queries.push(query)}function go(c,query){return c.execute(query)?move(c,busy):move(c,full)}function cancel(query){return new Promise((resolve4,reject)=>{query.state?query.active?connection_default(options).cancel(query.state,resolve4,reject):query.cancelled={resolve:resolve4,reject}:(queries.remove(query),query.cancelled=!0,query.reject(Errors.generic("57014","canceling statement due to user request")),resolve4())})}async function end({timeout=null}={}){if(ending)return ending;await 1;let timer2;return ending=Promise.race([new Promise((r)=>timeout!==null&&(timer2=setTimeout(destroy,timeout*1000,r))),Promise.all(connections.map((c)=>c.end()).concat(listen.sql?listen.sql.end({timeout:0}):[],subscribe2.sql?subscribe2.sql.end({timeout:0}):[]))]).then(()=>clearTimeout(timer2))}async function close2(){await Promise.all(connections.map((c)=>c.end()))}async function destroy(resolve4){await Promise.all(connections.map((c)=>c.terminate()));while(queries.length)queries.shift().reject(Errors.connection("CONNECTION_DESTROYED",options));resolve4()}function connect(c,query){return move(c,connecting),c.connect(query),c}function onend(c){move(c,ended)}function onopen(c){if(queries.length===0)return move(c,open2);let max=Math.ceil(queries.length/(connecting.length+1)),ready=!0;while(ready&&queries.length&&max-- >0){let query=queries.shift();if(query.reserve)return query.reserve(c);ready=c.execute(query)}ready?move(c,busy):move(c,full)}function onclose(c,e){move(c,closed),c.reserved=null,c.onclose&&(c.onclose(e),c.onclose=null),options.onclose&&options.onclose(c.id),queries.length&&connect(c,queries.shift())}}function parseOptions(a,b2){if(a&&a.shared)return a;let env=process.env,o=(!a||typeof a==="string"?b2:a)||{},{url,multihost}=parseUrl(a),query=[...url.searchParams].reduce((a2,[b3,c])=>(a2[b3]=c,a2),{}),host=o.hostname||o.host||multihost||url.hostname||env.PGHOST||"localhost",port=o.port||url.port||env.PGPORT||5432,user=o.user||o.username||url.username||env.PGUSERNAME||env.PGUSER||osUsername();o.no_prepare&&(o.prepare=!1),query.sslmode&&(query.ssl=query.sslmode,delete query.sslmode),"timeout"in o&&(console.log("The timeout option is deprecated, use idle_timeout instead"),o.idle_timeout=o.timeout),query.sslrootcert==="system"&&(query.ssl="verify-full");let ints=["idle_timeout","connect_timeout","max_lifetime","max_pipeline","backoff","keep_alive"],defaults={max:globalThis.Cloudflare?3:10,ssl:!1,sslnegotiation:null,idle_timeout:null,connect_timeout:30,max_lifetime,max_pipeline:100,backoff,keep_alive:60,prepare:!0,debug:!1,fetch_types:!0,publications:"alltables",target_session_attrs:null};return{host:Array.isArray(host)?host:host.split(",").map((x)=>x.split(":")[0]),port:Array.isArray(port)?port:host.split(",").map((x)=>parseInt(x.split(":")[1]||port)),path:o.path||host.indexOf("/")>-1&&host+"/.s.PGSQL."+port,database:o.database||o.db||(url.pathname||"").slice(1)||env.PGDATABASE||user,user,pass:o.pass||o.password||url.password||env.PGPASSWORD||"",...Object.entries(defaults).reduce((acc,[k,d])=>{let value=k in o?o[k]:(k in query)?query[k]==="disable"||query[k]==="false"?!1:query[k]:env["PG"+k.toUpperCase()]||d;return acc[k]=typeof value==="string"&&ints.includes(k)?+value:value,acc},{}),connection:{application_name:env.PGAPPNAME||"postgres.js",...o.connection,...Object.entries(query).reduce((acc,[k,v])=>((k in defaults)||(acc[k]=v),acc),{})},types:o.types||{},target_session_attrs:tsa(o,url,env),onnotice:o.onnotice,onnotify:o.onnotify,onclose:o.onclose,onparameter:o.onparameter,socket:o.socket,transform:parseTransform(o.transform||{undefined:void 0}),parameters:{},shared:{retries:0,typeArrayMap:{}},...mergeUserTypes(o.types)}}function tsa(o,url,env){let x=o.target_session_attrs||url.searchParams.get("target_session_attrs")||env.PGTARGETSESSIONATTRS;if(!x||["read-write","read-only","primary","standby","prefer-standby"].includes(x))return x;throw Error("target_session_attrs "+x+" is not supported")}function backoff(retries){return(0.5+Math.random()/2)*Math.min(3**retries/100,20)}function max_lifetime(){return 60*(30+Math.random()*30)}function parseTransform(x){return{undefined:x.undefined,column:{from:typeof x.column==="function"?x.column:x.column&&x.column.from,to:x.column&&x.column.to},value:{from:typeof x.value==="function"?x.value:x.value&&x.value.from,to:x.value&&x.value.to},row:{from:typeof x.row==="function"?x.row:x.row&&x.row.from,to:x.row&&x.row.to}}}function parseUrl(url){if(!url||typeof url!=="string")return{url:{searchParams:new Map}};let host=url;host=host.slice(host.indexOf("://")+3).split(/[?/]/)[0],host=decodeURIComponent(host.slice(host.indexOf("@")+1));let urlObj=new URL(url.replace(host,host.split(",")[0]));return{url:{username:decodeURIComponent(urlObj.username),password:decodeURIComponent(urlObj.password),host:urlObj.host,hostname:urlObj.hostname,port:urlObj.port,pathname:urlObj.pathname,searchParams:urlObj.searchParams},multihost:host.indexOf(",")>-1&&host}}function osUsername(){try{return os.userInfo().username}catch(_){return process.env.USERNAME||process.env.USER||process.env.LOGNAME}}var src_default;var init_src=__esm(()=>{init_types3();init_connection();init_query();init_queue();init_errors3();init_large();Object.assign(Postgres,{PostgresError,toPascal,pascal,toCamel,camel,toKebab,kebab,fromPascal,fromCamel,fromKebab,BigInt:{to:20,from:[20],parse:(x)=>BigInt(x),serialize:(x)=>x.toString()}});src_default=Postgres});var exports_db={};__export(exports_db,{shutdown:()=>shutdown,isAvailable:()=>isAvailable2,getLockfilePath:()=>getLockfilePath,getDataDir:()=>getDataDir,getConnection:()=>getConnection,getActivePort:()=>getActivePort,ensurePgserve:()=>ensurePgserve});import{execSync as execSync3}from"child_process";import{existsSync as existsSync15,mkdirSync as mkdirSync6,readFileSync as readFileSync8,renameSync,unlinkSync as unlinkSync3,writeFileSync as writeFileSync5}from"fs";import{createConnection}from"net";import{homedir as homedir14}from"os";import{join as join18}from"path";function maskCredentials(url){return url.replace(/\/\/.*@/,"//***@")}function killOrphanedPostgres(dataDir){let pidFile=join18(dataDir,"postmaster.pid");if(!existsSync15(pidFile))return;try{let content=readFileSync8(pidFile,"utf-8"),pid=Number.parseInt(content.split(`
163
- `)[0],10);if(Number.isNaN(pid)||pid<=0)return;let cmdline;try{cmdline=execSync3(`ps -o command= -p ${pid} 2>/dev/null`,{encoding:"utf-8"}).trim()}catch{return}if(!cmdline.includes("postgres"))return;try{process.kill(pid,"SIGTERM")}catch{return}let deadline=Date.now()+5000;while(Date.now()<deadline)try{process.kill(pid,0),execSync3("sleep 0.2",{stdio:"ignore"})}catch{return}try{process.kill(pid,"SIGKILL")}catch{}}catch{}}function getPort(){let envPort=process.env.GENIE_PG_PORT;if(envPort){let parsed=Number.parseInt(envPort,10);if(!Number.isNaN(parsed)&&parsed>0&&parsed<65536)return parsed}return DEFAULT_PORT}function isPortListening(port,host){return new Promise((resolve4)=>{let socket=createConnection({port,host},()=>{socket.destroy(),resolve4(!0)});socket.on("error",()=>{socket.destroy(),resolve4(!1)}),socket.setTimeout(1000,()=>{socket.destroy(),resolve4(!1)})})}function readLockfile(){try{let content=readFileSync8(LOCKFILE_PATH,"utf-8").trim(),port=Number.parseInt(content,10);if(!Number.isNaN(port)&&port>0&&port<65536)return port}catch{}return null}function writeLockfile(port){try{mkdirSync6(GENIE_HOME2,{recursive:!0});let tmpPath=`${LOCKFILE_PATH}.tmp.${process.pid}`;writeFileSync5(tmpPath,String(port),"utf-8"),renameSync(tmpPath,LOCKFILE_PATH)}catch{}}function removeLockfile(){try{unlinkSync3(LOCKFILE_PATH)}catch{}}async function ensurePgserve(){if(ensurePromise)return ensurePromise;ensurePromise=_ensurePgserve();try{return await ensurePromise}finally{ensurePromise=null}}async function _ensurePgserve(){if(activePort!==null&&pgserveServer)return activePort;if(activePort!==null)return activePort;let port=getPort(),reusedPort=await tryReuseLockfile();if(reusedPort!==null)return reusedPort;if(await isPortListening(port,DEFAULT_HOST))return markPortActive(port,!0);mkdirSync6(DATA_DIR,{recursive:!0}),killOrphanedPostgres(DATA_DIR);try{let startedPort=await startPgserveOnPort(port);return registerExitHandler(),startedPort}catch(err){return tryFallbackPorts(port,err)}}async function tryReuseLockfile(){let lockfilePort=readLockfile();if(lockfilePort===null)return null;if(await isPortListening(lockfilePort,DEFAULT_HOST))return markPortActive(lockfilePort,!1);return removeLockfile(),null}function markPortActive(port,writeLock){if(activePort=port,process.env.GENIE_PG_AVAILABLE="true",writeLock)writeLockfile(port);return port}async function tryFallbackPorts(basePort,originalErr){for(let offset=1;offset<=MAX_PORT_RETRIES;offset++){let fallbackPort=basePort+offset;if(await isPortListening(fallbackPort,DEFAULT_HOST))return markPortActive(fallbackPort,!0);try{let startedPort=await startPgserveOnPort(fallbackPort);return registerExitHandler(),startedPort}catch{}}process.env.GENIE_PG_AVAILABLE="false";let message=originalErr instanceof Error?originalErr.message:String(originalErr);throw console.warn(`Warning: pgserve failed to start: ${maskCredentials(message)}`),Error(`pgserve failed to start on port ${basePort} (and fallbacks ${basePort+1}-${basePort+MAX_PORT_RETRIES}): ${maskCredentials(message)}`)}async function startPgserveOnPort(port){let{startMultiTenantServer}=await import("pgserve");return pgserveServer=await startMultiTenantServer({port,host:DEFAULT_HOST,baseDir:DATA_DIR,logLevel:"warn",autoProvision:!0}),activePort=port,ownsLockfile=!0,process.env.GENIE_PG_AVAILABLE="true",writeLockfile(port),port}function registerExitHandler(){if(exitHandlerRegistered)return;exitHandlerRegistered=!0;let cleanup=()=>{if(ownsLockfile)removeLockfile(),ownsLockfile=!1};process.on("exit",cleanup),process.on("SIGINT",()=>{cleanup(),process.exit(130)}),process.on("SIGTERM",()=>{cleanup(),process.exit(143)})}function migrationsDone(){try{let marker=readFileSync8(MIGRATION_MARKER,"utf-8").trim(),currentVersion=process.env.npm_package_version??"";return marker===currentVersion||currentVersion===""&&marker.length>0}catch{return!1}}function markMigrationsDone(){try{let version=process.env.npm_package_version??Date.now().toString();writeFileSync5(MIGRATION_MARKER,version,"utf-8")}catch{}}async function getConnection(){if(sqlClient)return sqlClient;let port=await ensurePgserve(),postgres2=(await Promise.resolve().then(() => (init_src(),exports_src))).default;if(sqlClient=postgres2({host:DEFAULT_HOST,port,database:DB_NAME,username:"postgres",password:"postgres",max:10,idle_timeout:1,connect_timeout:5}),!migrationsDone())await runMigrations(sqlClient),markMigrationsDone();return sqlClient}async function isAvailable2(){try{return await(await getConnection())`SELECT 1`,!0}catch{return!1}}async function shutdown(){if(sqlClient)await sqlClient.end({timeout:5}),sqlClient=null;if(ownsLockfile)removeLockfile(),ownsLockfile=!1}function getDataDir(){return DATA_DIR}function getActivePort(){return activePort??getPort()}function getLockfilePath(){return LOCKFILE_PATH}var DEFAULT_PORT=19642,DEFAULT_HOST="127.0.0.1",MAX_PORT_RETRIES=3,GENIE_HOME2,DATA_DIR,LOCKFILE_PATH,MIGRATION_MARKER,DB_NAME="genie",pgserveServer=null,sqlClient=null,activePort=null,ensurePromise=null,ownsLockfile=!1,exitHandlerRegistered=!1;var init_db=__esm(()=>{init_db_migrations();GENIE_HOME2=process.env.GENIE_HOME??join18(homedir14(),".genie"),DATA_DIR=join18(GENIE_HOME2,"data","pgserve"),LOCKFILE_PATH=join18(GENIE_HOME2,"pgserve.port"),MIGRATION_MARKER=join18(GENIE_HOME2,"pgserve.migrated")});var exports_wish_state={};__export(exports_wish_state,{startGroup:()=>startGroup,resolveRepoPath:()=>resolveRepoPath,resetGroup:()=>resetGroup,getState:()=>getState,getOrCreateState:()=>getOrCreateState,getGroupState:()=>getGroupState,findGroupByAssignee:()=>findGroupByAssignee,findAnyGroupByAssignee:()=>findAnyGroupByAssignee,createState:()=>createState,completeGroup:()=>completeGroup,WishStateSchema:()=>WishStateSchema,GroupStatusSchema:()=>GroupStatusSchema,GroupStateSchema:()=>GroupStateSchema});import{execSync as execSync4}from"child_process";import{dirname as dirname5}from"path";function resolveRepoPath(cwd){if(cwd)return cwd;try{let commonDir=execSync4("git rev-parse --path-format=absolute --git-common-dir",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();return dirname5(commonDir)}catch{return process.cwd()}}function wishFilePath(slug){return`.genie/wishes/${slug}/WISH.md`}function toISO(v){if(v==null)return;if(v instanceof Date)return v.toISOString();return String(v)}async function findParent(sql,slug,repoPath){let wishFile=wishFilePath(slug),rows=await sql`
163
+ `)[0],10);if(Number.isNaN(pid)||pid<=0)return;let cmdline;try{cmdline=execSync3(`ps -o command= -p ${pid} 2>/dev/null`,{encoding:"utf-8"}).trim()}catch{return}if(!cmdline.includes("postgres"))return;try{process.kill(pid,"SIGTERM")}catch{return}let deadline=Date.now()+5000;while(Date.now()<deadline)try{process.kill(pid,0),execSync3("sleep 0.2",{stdio:"ignore"})}catch{return}try{process.kill(pid,"SIGKILL")}catch{}}catch{}}function getPort(){let envPort=process.env.GENIE_PG_PORT;if(envPort){let parsed=Number.parseInt(envPort,10);if(!Number.isNaN(parsed)&&parsed>0&&parsed<65536)return parsed}return DEFAULT_PORT}function isPortListening(port,host){return new Promise((resolve4)=>{let socket=createConnection({port,host},()=>{socket.destroy(),resolve4(!0)});socket.on("error",()=>{socket.destroy(),resolve4(!1)}),socket.setTimeout(1000,()=>{socket.destroy(),resolve4(!1)})})}function readLockfile(){try{let content=readFileSync8(LOCKFILE_PATH,"utf-8").trim(),port=Number.parseInt(content,10);if(!Number.isNaN(port)&&port>0&&port<65536)return port}catch{}return null}function writeLockfile(port){try{mkdirSync6(GENIE_HOME2,{recursive:!0});let tmpPath=`${LOCKFILE_PATH}.tmp.${process.pid}`;writeFileSync5(tmpPath,String(port),"utf-8"),renameSync(tmpPath,LOCKFILE_PATH)}catch{}}function removeLockfile(){try{unlinkSync3(LOCKFILE_PATH)}catch{}}async function ensurePgserve(){if(ensurePromise)return ensurePromise;ensurePromise=_ensurePgserve();try{return await ensurePromise}finally{ensurePromise=null}}async function _ensurePgserve(){if(activePort!==null&&pgserveServer)return activePort;if(activePort!==null)return activePort;let port=getPort(),reusedPort=await tryReuseLockfile();if(reusedPort!==null)return reusedPort;if(await isPortListening(port,DEFAULT_HOST))return markPortActive(port,!0);mkdirSync6(DATA_DIR,{recursive:!0}),killOrphanedPostgres(DATA_DIR);try{let startedPort=await startPgserveOnPort(port);return registerExitHandler(),startedPort}catch(err){return tryFallbackPorts(port,err)}}async function tryReuseLockfile(){let lockfilePort=readLockfile();if(lockfilePort===null)return null;if(await isPortListening(lockfilePort,DEFAULT_HOST))return markPortActive(lockfilePort,!1);return removeLockfile(),null}function markPortActive(port,writeLock){if(activePort=port,process.env.GENIE_PG_AVAILABLE="true",writeLock)writeLockfile(port);return port}async function tryFallbackPorts(basePort,originalErr){for(let offset=1;offset<=MAX_PORT_RETRIES;offset++){let fallbackPort=basePort+offset;if(await isPortListening(fallbackPort,DEFAULT_HOST))return markPortActive(fallbackPort,!0);try{let startedPort=await startPgserveOnPort(fallbackPort);return registerExitHandler(),startedPort}catch{}}process.env.GENIE_PG_AVAILABLE="false";let message=originalErr instanceof Error?originalErr.message:String(originalErr);throw console.warn(`Warning: pgserve failed to start: ${maskCredentials(message)}`),Error(`pgserve failed to start on port ${basePort} (and fallbacks ${basePort+1}-${basePort+MAX_PORT_RETRIES}): ${maskCredentials(message)}`)}async function startPgserveOnPort(port){let{startMultiTenantServer}=await import("pgserve");return pgserveServer=await startMultiTenantServer({port,host:DEFAULT_HOST,baseDir:DATA_DIR,logLevel:"warn",autoProvision:!0}),activePort=port,ownsLockfile=!0,process.env.GENIE_PG_AVAILABLE="true",writeLockfile(port),port}function registerExitHandler(){if(exitHandlerRegistered)return;exitHandlerRegistered=!0;let cleanup=()=>{if(ownsLockfile)removeLockfile(),ownsLockfile=!1};process.on("exit",cleanup),process.on("SIGINT",()=>{cleanup(),process.exit(130)}),process.on("SIGTERM",()=>{cleanup(),process.exit(143)})}function migrationsDone(){try{let marker=readFileSync8(MIGRATION_MARKER,"utf-8").trim(),currentVersion=process.env.npm_package_version??"";return marker===currentVersion||currentVersion===""&&marker.length>0}catch{return!1}}function markMigrationsDone(){try{let version=process.env.npm_package_version??Date.now().toString();writeFileSync5(MIGRATION_MARKER,version,"utf-8")}catch{}}async function getConnection(){if(sqlClient)return sqlClient;let port=await ensurePgserve(),postgres2=(await Promise.resolve().then(() => (init_src(),exports_src))).default;if(sqlClient=postgres2({host:DEFAULT_HOST,port,database:DB_NAME,username:"postgres",password:"postgres",max:10,idle_timeout:1,connect_timeout:5}),!migrationsDone())await runMigrations(sqlClient),markMigrationsDone();return sqlClient}async function isAvailable2(){try{return await(await getConnection())`SELECT 1`,!0}catch{return!1}}async function shutdown(){if(sqlClient)await sqlClient.end({timeout:5}),sqlClient=null;if(ownsLockfile)removeLockfile(),ownsLockfile=!1}function getDataDir(){return DATA_DIR}function getActivePort(){return activePort??getPort()}function getLockfilePath(){return LOCKFILE_PATH}var DEFAULT_PORT=19642,DEFAULT_HOST="127.0.0.1",MAX_PORT_RETRIES=3,GENIE_HOME2,DATA_DIR,LOCKFILE_PATH,MIGRATION_MARKER,DB_NAME="genie",pgserveServer=null,sqlClient=null,activePort=null,ensurePromise=null,ownsLockfile=!1,exitHandlerRegistered=!1;var init_db=__esm(()=>{init_db_migrations();GENIE_HOME2=process.env.GENIE_HOME??join18(homedir14(),".genie"),DATA_DIR=join18(GENIE_HOME2,"data","pgserve"),LOCKFILE_PATH=join18(GENIE_HOME2,"pgserve.port"),MIGRATION_MARKER=join18(GENIE_HOME2,"pgserve.migrated")});var exports_wish_state={};__export(exports_wish_state,{startGroup:()=>startGroup,resolveRepoPath:()=>resolveRepoPath,resetGroup:()=>resetGroup,isWishComplete:()=>isWishComplete,getState:()=>getState,getOrCreateState:()=>getOrCreateState,getGroupState:()=>getGroupState,findGroupByAssignee:()=>findGroupByAssignee,findAnyGroupByAssignee:()=>findAnyGroupByAssignee,createState:()=>createState,completeGroup:()=>completeGroup,WishStateSchema:()=>WishStateSchema,GroupStatusSchema:()=>GroupStatusSchema,GroupStateSchema:()=>GroupStateSchema});import{execSync as execSync4}from"child_process";import{dirname as dirname5}from"path";function resolveRepoPath(cwd){if(cwd)return cwd;try{let commonDir=execSync4("git rev-parse --path-format=absolute --git-common-dir",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();return dirname5(commonDir)}catch{return process.cwd()}}function wishFilePath(slug){return`.genie/wishes/${slug}/WISH.md`}function toISO(v){if(v==null)return;if(v instanceof Date)return v.toISOString();return String(v)}async function findParent(sql,slug,repoPath){let wishFile=wishFilePath(slug),rows=await sql`
164
164
  SELECT * FROM tasks
165
165
  WHERE wish_file = ${wishFile} AND repo_path = ${repoPath} AND parent_id IS NULL
166
166
  LIMIT 1
@@ -237,7 +237,7 @@ ${errCtx.stack}`;d.reject(err)}else d.resolve(msg)}});return sub.requestSubject=
237
237
  `,depsMap={};for(let dep of deps){let taskId=dep.task_id;if(!depsMap[taskId])depsMap[taskId]=[];depsMap[taskId].push(dep.dep_group)}let actors=await sql`
238
238
  SELECT task_id, actor_id FROM task_actors
239
239
  WHERE task_id = ANY(${childIds}) AND role = 'assignee'
240
- `,assigneeMap={};for(let actor of actors)assigneeMap[actor.task_id]=actor.actor_id;let groups={};for(let child of children){let{id,group_name:groupName}=child;groups[groupName]={status:child.status,assignee:assigneeMap[id],dependsOn:depsMap[id]??[],startedAt:toISO(child.started_at),completedAt:toISO(child.ended_at)}}return{wish:slug,groups,createdAt:toISO(parent.created_at)??"",updatedAt:toISO(parent.updated_at)??""}}async function getGroupState(slug,groupName,cwd){let state2=await getState(slug,cwd);if(!state2)return null;return state2.groups[groupName]??null}var GroupStatusSchema,GroupStateSchema,WishStateSchema;var init_wish_state=__esm(()=>{init_zod();init_db();GroupStatusSchema=exports_external.enum(["blocked","ready","in_progress","done"]),GroupStateSchema=exports_external.object({status:GroupStatusSchema,assignee:exports_external.string().optional(),dependsOn:exports_external.array(exports_external.string()).default([]),startedAt:exports_external.string().optional(),completedAt:exports_external.string().optional()}),WishStateSchema=exports_external.object({wish:exports_external.string(),groups:exports_external.record(exports_external.string(),GroupStateSchema),createdAt:exports_external.string(),updatedAt:exports_external.string()})});var exports_protocol_router_spawn={};__export(exports_protocol_router_spawn,{spawnWorkerFromTemplate:()=>spawnWorkerFromTemplate,injectResumeContext:()=>injectResumeContext});import{exec as exec2}from"child_process";import{readFile as readFile6}from"fs/promises";import{join as join19}from"path";import{promisify as promisify2}from"util";async function resolveParentSession(_repoPath,team){let teamConfig=await getTeam2(team);if(teamConfig?.nativeTeamParentSessionId)return teamConfig.nativeTeamParentSessionId;return await discoverClaudeSessionId()??`genie-${team}`}function buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId){let isClaude=template.provider==="claude",sessionName=template.role?`${template.team}-${template.role}`:void 0,newSessionId=isClaude&&!resumeSessionId?crypto.randomUUID():void 0,params={provider:template.provider,team:template.team,role:template.role,skill:template.skill,extraArgs:template.extraArgs,sessionId:newSessionId,resume:isClaude?resumeSessionId:void 0,name:sessionName};if(isClaude)params.nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:template.role??"general-purpose",agentName:template.role};return params}function buildFullCommand(launch){if(launch.env&&Object.keys(launch.env).length>0)return`env ${Object.entries(launch.env).map(([k,v])=>`${k}=${v}`).join(" ")} ${launch.command}`;return launch.command}async function generateWorkerId(team,role){let base=role?`${team}-${role}`:team;return(await list()).some((w)=>w.id===base)?`${base}-${crypto.randomUUID().slice(0,8)}`:base}async function spawnPaneInSession(session,team,repoPath,fullCommand){let teamWindow=null;try{teamWindow=await ensureTeamWindow(session,team,repoPath)}catch{}let splitTarget=teamWindow?`-t '${teamWindow.windowId}'`:"",{stdout}=await execAsync(`tmux split-window -d ${splitTarget} -P -F '#{pane_id}' ${fullCommand}`),paneId=stdout.trim(),layoutTarget=`${session}:${teamWindow?.windowName??""}`;if(!teamWindow){let wins=await listWindows(session);layoutTarget=wins[0]?wins[0].id:`${session}:`}try{await execAsync(`tmux ${buildLayoutCommand(layoutTarget,resolveLayoutMode())}`)}catch{}return{paneId,teamWindow}}async function spawnWorkerFromTemplate(template,resumeSessionId){let repoPath=template.cwd??process.cwd(),team=template.team,parentSessionId=await resolveParentSession(repoPath,team);await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=await assignColor(team),params=buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId),launch=buildLaunchCommand(validateSpawnParams(params)),fullCommand=buildFullCommand(launch),workerId=await generateWorkerId(team,template.role),session=await getCurrentSessionName()??team,{paneId,teamWindow}=await spawnPaneInSession(session,team,repoPath,fullCommand),now=new Date().toISOString(),agentName=template.role??"worker",isClaude=template.provider==="claude",effectiveSessionId=resumeSessionId??params.sessionId,workerEntry={id:workerId,paneId,session,provider:template.provider,transport:"tmux",role:template.role,skill:template.skill,team,worktree:null,startedAt:now,state:"spawning",lastStateChange:now,repoPath,claudeSessionId:effectiveSessionId,nativeTeamEnabled:isClaude,nativeAgentId:`${agentName}@${team}`,nativeColor:spawnColor,parentSessionId,window:teamWindow?.windowName,windowName:teamWindow?.windowName,windowId:teamWindow?.windowId};if(await register(workerEntry),await registerNativeMember(team,{agentName,agentType:template.role??"general-purpose",color:spawnColor??"blue",tmuxPaneId:paneId,cwd:repoPath}),await writeNativeInbox(team,"team-lead",{from:agentName,text:`Worker ${agentName} (${template.provider}) auto-spawned${resumeSessionId?" with --resume":""}. Ready for tasks.`,summary:`${agentName} auto-spawned`,timestamp:now,color:spawnColor??"blue",read:!1}),spawnColor)await applyPaneColor(paneId,spawnColor,teamWindow?.windowId);return await injectResumeContext(repoPath,workerId,agentName,team),{worker:workerEntry,paneId,workerId}}function extractGroupSection(content,groupName){let escaped=groupName.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),pattern=new RegExp(`^### Group ${escaped}:`,"m"),match=content.match(pattern);if(!match||match.index===void 0)return null;let start=match.index,nextBoundary=content.slice(start).slice(1).search(/^### Group \d|^---$/m),end=nextBoundary!==-1?start+1+nextBoundary:content.length;return content.slice(start,end).trim()}async function getRecentGitLog(repoPath,count=3){try{let{stdout}=await execAsync(`git -C '${repoPath}' log --oneline -${count} 2>/dev/null`);return stdout.trim()}catch{return""}}async function getGitStatus(repoPath){try{let{stdout}=await execAsync(`git -C '${repoPath}' status --short 2>/dev/null`);return stdout.trim()}catch{return""}}async function injectResumeContext(repoPath,workerId,agentName,_team){try{let match=await findAnyGroupByAssignee(workerId,repoPath)??await findAnyGroupByAssignee(agentName,repoPath);if(!match)return;let{slug,groupName,group}=match,wishPath=join19(repoPath,".genie","wishes",slug,"WISH.md"),groupSection="";try{let wishContent=await readFile6(wishPath,"utf-8");groupSection=extractGroupSection(wishContent,groupName)??""}catch{}let gitLog=await getRecentGitLog(repoPath),gitStatus=await getGitStatus(repoPath),resumePrompt=[`RESUME CONTEXT: You were working on wish "${slug}", group "${groupName}".`,`Status: ${group.status}. Started at: ${group.startedAt??"unknown"}.`,`Wish file: .genie/wishes/${slug}/WISH.md`,"",groupSection?`Group section:
240
+ `,assigneeMap={};for(let actor of actors)assigneeMap[actor.task_id]=actor.actor_id;let groups={};for(let child of children){let{id,group_name:groupName}=child;groups[groupName]={status:child.status,assignee:assigneeMap[id],dependsOn:depsMap[id]??[],startedAt:toISO(child.started_at),completedAt:toISO(child.ended_at)}}return{wish:slug,groups,createdAt:toISO(parent.created_at)??"",updatedAt:toISO(parent.updated_at)??""}}async function getGroupState(slug,groupName,cwd){let state2=await getState(slug,cwd);if(!state2)return null;return state2.groups[groupName]??null}async function isWishComplete(slug,cwd){let state2=await getState(slug,cwd);if(!state2)return!1;let groups=Object.values(state2.groups);return groups.length>0&&groups.every((g)=>g.status==="done")}var GroupStatusSchema,GroupStateSchema,WishStateSchema;var init_wish_state=__esm(()=>{init_zod();init_db();GroupStatusSchema=exports_external.enum(["blocked","ready","in_progress","done"]),GroupStateSchema=exports_external.object({status:GroupStatusSchema,assignee:exports_external.string().optional(),dependsOn:exports_external.array(exports_external.string()).default([]),startedAt:exports_external.string().optional(),completedAt:exports_external.string().optional()}),WishStateSchema=exports_external.object({wish:exports_external.string(),groups:exports_external.record(exports_external.string(),GroupStateSchema),createdAt:exports_external.string(),updatedAt:exports_external.string()})});var exports_protocol_router_spawn={};__export(exports_protocol_router_spawn,{spawnWorkerFromTemplate:()=>spawnWorkerFromTemplate,injectResumeContext:()=>injectResumeContext});import{exec as exec2}from"child_process";import{readFile as readFile6}from"fs/promises";import{join as join19}from"path";import{promisify as promisify2}from"util";async function resolveParentSession(_repoPath,team){let teamConfig=await getTeam2(team);if(teamConfig?.nativeTeamParentSessionId)return teamConfig.nativeTeamParentSessionId;return await discoverClaudeSessionId()??`genie-${team}`}function buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId){let isClaude=template.provider==="claude",sessionName=template.role?`${template.team}-${template.role}`:void 0,newSessionId=isClaude&&!resumeSessionId?crypto.randomUUID():void 0,params={provider:template.provider,team:template.team,role:template.role,skill:template.skill,extraArgs:template.extraArgs,sessionId:newSessionId,resume:isClaude?resumeSessionId:void 0,name:sessionName};if(isClaude)params.nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:template.role??"general-purpose",agentName:template.role};return params}function buildFullCommand(launch){if(launch.env&&Object.keys(launch.env).length>0)return`env ${Object.entries(launch.env).map(([k,v])=>`${k}=${v}`).join(" ")} ${launch.command}`;return launch.command}async function generateWorkerId(team,role){let base=role?`${team}-${role}`:team;return(await list()).some((w)=>w.id===base)?`${base}-${crypto.randomUUID().slice(0,8)}`:base}async function spawnPaneInSession(session,team,repoPath,fullCommand){let teamWindow=null;try{teamWindow=await ensureTeamWindow(session,team,repoPath)}catch{}let splitTarget=teamWindow?`-t '${teamWindow.windowId}'`:"",{stdout}=await execAsync(`tmux split-window -d ${splitTarget} -P -F '#{pane_id}' ${fullCommand}`),paneId=stdout.trim(),layoutTarget=`${session}:${teamWindow?.windowName??""}`;if(!teamWindow){let wins=await listWindows(session);layoutTarget=wins[0]?wins[0].id:`${session}:`}try{await execAsync(`tmux ${buildLayoutCommand(layoutTarget,resolveLayoutMode())}`)}catch{}return{paneId,teamWindow}}async function spawnWorkerFromTemplate(template,resumeSessionId){let repoPath=template.cwd??process.cwd(),team=template.team,parentSessionId=await resolveParentSession(repoPath,team);await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=await assignColor(team),params=buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId),launch=buildLaunchCommand(validateSpawnParams(params)),fullCommand=buildFullCommand(launch),workerId=await generateWorkerId(team,template.role),session=await getCurrentSessionName()??team,{paneId,teamWindow}=await spawnPaneInSession(session,team,repoPath,fullCommand),now=new Date().toISOString(),agentName=template.role??"worker",isClaude=template.provider==="claude",effectiveSessionId=resumeSessionId??params.sessionId,workerEntry={id:workerId,paneId,session,provider:template.provider,transport:"tmux",role:template.role,skill:template.skill,team,worktree:null,startedAt:now,state:"spawning",lastStateChange:now,repoPath,claudeSessionId:effectiveSessionId,nativeTeamEnabled:isClaude,nativeAgentId:`${agentName}@${team}`,nativeColor:spawnColor,parentSessionId,window:teamWindow?.windowName,windowName:teamWindow?.windowName,windowId:teamWindow?.windowId};if(await register(workerEntry),await registerNativeMember(team,{agentName,agentType:template.role??"general-purpose",color:spawnColor??"blue",tmuxPaneId:paneId,cwd:repoPath}),await writeNativeInbox(team,"team-lead",{from:agentName,text:`Worker ${agentName} (${template.provider}) auto-spawned${resumeSessionId?" with --resume":""}. Ready for tasks.`,summary:`${agentName} auto-spawned`,timestamp:now,color:spawnColor??"blue",read:!1}),spawnColor)await applyPaneColor(paneId,spawnColor,teamWindow?.windowId);return await injectResumeContext(repoPath,workerId,agentName,team),{worker:workerEntry,paneId,workerId}}function extractGroupSection(content,groupName){let escaped=groupName.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),pattern=new RegExp(`^### Group ${escaped}:`,"m"),match=content.match(pattern);if(!match||match.index===void 0)return null;let start=match.index,nextBoundary=content.slice(start).slice(1).search(/^### Group \d|^---$/m),end=nextBoundary!==-1?start+1+nextBoundary:content.length;return content.slice(start,end).trim()}async function getRecentGitLog(repoPath,count=3){try{let{stdout}=await execAsync(`git -C '${repoPath}' log --oneline -${count} 2>/dev/null`);return stdout.trim()}catch{return""}}async function getGitStatus(repoPath){try{let{stdout}=await execAsync(`git -C '${repoPath}' status --short 2>/dev/null`);return stdout.trim()}catch{return""}}async function injectResumeContext(repoPath,workerId,agentName,_team){try{let match=await findAnyGroupByAssignee(workerId,repoPath)??await findAnyGroupByAssignee(agentName,repoPath);if(!match)return;let{slug,groupName,group}=match,wishPath=join19(repoPath,".genie","wishes",slug,"WISH.md"),groupSection="";try{let wishContent=await readFile6(wishPath,"utf-8");groupSection=extractGroupSection(wishContent,groupName)??""}catch{}let gitLog=await getRecentGitLog(repoPath),gitStatus=await getGitStatus(repoPath),resumePrompt=[`RESUME CONTEXT: You were working on wish "${slug}", group "${groupName}".`,`Status: ${group.status}. Started at: ${group.startedAt??"unknown"}.`,`Wish file: .genie/wishes/${slug}/WISH.md`,"",groupSection?`Group section:
241
241
  ${groupSection}`:"","",gitLog?`Last git log:
242
242
  ${gitLog}`:"","",gitStatus?`Uncommitted changes:
243
243
  ${gitStatus}`:"","","Pick up where you left off. Read the wish file for full context."].filter(Boolean).join(`
@@ -1025,7 +1025,7 @@ Done. ${results.length} migration${results.length===1?"":"s"} applied.`),await s
1025
1025
  (built-in ${resolved.entry.registeredAt==="(built-in)"?"agent":"agent"})`);console.log(""),printEntry(resolved.entry),console.log("")}async function listEntries(json2,includeBuiltins){let entries=await ls();if(json2){listEntriesJson(entries,includeBuiltins);return}if(entries.length===0&&!includeBuiltins){console.log(`
1026
1026
  No agents registered. Add one with: genie dir add <name> --dir <path>`),console.log(`Use --builtins to also see built-in roles and council members.
1027
1027
  `);return}if(entries.length>0)printRegisteredTable(entries);if(includeBuiltins)printBuiltinsTable()}function listEntriesJson(entries,includeBuiltins){let result=entries.map((e)=>({...e,builtin:!1}));if(includeBuiltins)for(let b2 of ALL_BUILTINS)result.push({name:b2.name,description:b2.description,model:b2.model,category:b2.category,scope:"built-in",builtin:!0});console.log(JSON.stringify(result,null,2))}function printRegisteredTable(entries){console.log(""),console.log("REGISTERED AGENTS"),console.log("-".repeat(85)),console.log(` ${"NAME".padEnd(22)}${"SCOPE".padEnd(10)}${"DIR".padEnd(30)}${"MODE".padEnd(8)}${"MODEL".padEnd(8)}ROLES`),console.log(` ${"-".repeat(20)} ${"-".repeat(8)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(6)} ${"-".repeat(15)}`);for(let entry of entries){let dir=contractPath(entry.dir),truncDir=dir.length>28?`${dir.slice(0,25)}...`:dir,roles=entry.roles?.join(", ")||"-";console.log(` ${entry.name.padEnd(22)}${entry.scope.padEnd(10)}${truncDir.padEnd(30)}${entry.promptMode.padEnd(8)}${(entry.model||"-").padEnd(8)}${roles}`)}console.log("")}function printBuiltinsTable(){console.log("BUILT-IN AGENTS"),console.log("-".repeat(80)),console.log(` ${"NAME".padEnd(22)}${"TYPE".padEnd(10)}${"MODEL".padEnd(8)}DESCRIPTION`),console.log(` ${"-".repeat(20)} ${"-".repeat(8)} ${"-".repeat(6)} ${"-".repeat(30)}`);for(let agent of ALL_BUILTINS)console.log(` ${agent.name.padEnd(22)}${agent.category.padEnd(10)}${(agent.model||"-").padEnd(8)}${agent.description}`);console.log("")}async function handleOmniRegistration(name,options){let omniUrl=await resolveOmniApiUrl();if(!omniUrl)return;console.log(`
1028
- Registering in Omni (${omniUrl})...`);let existingId=await findOmniAgent(name);if(existingId){console.log(` Agent already exists in Omni: ${existingId}`),await edit(name,{omniAgentId:existingId},{global:options.global}),console.log(" Linked existing Omni agent to directory entry.");return}let omniAgentId=await registerAgentInOmni(name,{model:options.model,roles:options.roles});if(omniAgentId)await edit(name,{omniAgentId},{global:options.global}),console.log(` Omni agent created: ${omniAgentId}`),console.log(" Session isolation: per-person + per-channel")}async function handleAgentRegister(name,options){let promptMode=validatePromptMode(options.promptMode),entry=await add({name,dir:resolvePath(options.dir),repo:options.repo?resolvePath(options.repo):void 0,promptMode,model:options.model,roles:options.roles},{global:options.global}),scope=options.global?"global":"project";if(console.log(`Agent "${entry.name}" registered (${scope}).`),printEntry(entry),!options.skipOmni)await handleOmniRegistration(name,options)}function registerAgentNamespace(program2){program2.command("agent").description("Agent lifecycle management").command("register <name>").description("Register an agent locally and auto-register in Omni when configured").requiredOption("--dir <path>","Agent folder (CWD + AGENTS.md)").option("--repo <path>","Default git repo (overridden by team)").option("--prompt-mode <mode>","Prompt mode: append or system","append").option("--model <model>","Default model (sonnet, opus, codex)").option("--roles <roles...>","Built-in roles this agent can orchestrate").option("--global","Write to global directory instead of project").option("--skip-omni","Skip Omni auto-registration").action(async(name,options)=>{try{await handleAgentRegister(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_protocol_router();init_wish_state();init_agents();import{execSync as execSync6}from"child_process";import{existsSync as existsSync19}from"fs";import{mkdir as mkdir8,readFile as readFile9,writeFile as writeFile7}from"fs/promises";import{tmpdir}from"os";import{join as join24}from"path";init_wish_state();import{execSync as execSync5}from"child_process";import{existsSync as existsSync18}from"fs";import{readFile as readFile8}from"fs/promises";import{dirname as dirname6,join as join23}from"path";function resolveWishPath(slug,cwd){let base=cwd??process.cwd(),cwdPath=join23(base,".genie","wishes",slug,"WISH.md");if(existsSync18(cwdPath))return cwdPath;try{let commonDir=execSync5("git rev-parse --path-format=absolute --git-common-dir",{encoding:"utf-8",cwd:base,stdio:["pipe","pipe","pipe"]}).trim(),repoRoot=dirname6(commonDir);if(repoRoot!==base){let repoPath=join23(repoRoot,".genie","wishes",slug,"WISH.md");if(existsSync18(repoPath))return repoPath}}catch{}return null}function parseRef(ref){let hashIdx=ref.indexOf("#");if(hashIdx===-1)throw Error(`Invalid reference "${ref}". Expected format: <slug>#<group>`);let slug=ref.slice(0,hashIdx),group=ref.slice(hashIdx+1);if(!slug||!group)throw Error(`Invalid reference "${ref}". Both slug and group are required.`);return{slug,group}}var STATUS_ICONS={blocked:"\uD83D\uDD12",ready:"\uD83D\uDFE2",in_progress:"\uD83D\uDD04",done:"\u2705"};function formatTimestamp(iso){if(!iso)return"";return new Date(iso).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1})}function padRight2(str2,len){return str2.length>=len?str2:str2+" ".repeat(len-str2.length)}async function detectWaveCompletion(slug,groupName,cwd){let wishPath=resolveWishPath(slug,cwd);if(!wishPath)return null;let content=await readFile8(wishPath,"utf-8"),targetWave=parseExecutionStrategy(content).find((w)=>w.groups.some((g)=>g.group===groupName));if(!targetWave)return null;let state2=await getState(slug,cwd);if(!state2)return null;let waveGroupNames=targetWave.groups.map((g)=>g.group);if(!waveGroupNames.every((g)=>state2.groups[g]?.status==="done"))return null;return{waveName:targetWave.name,waveGroups:waveGroupNames}}async function ensureWorkPushed(slug,group){try{if(execSync5("git status --porcelain",{encoding:"utf-8"}).trim())console.log(" Committing dirty working tree..."),execSync5("git add -A",{encoding:"utf-8"}),execSync5(`git commit -m "wip: ${slug}#${group}"`,{encoding:"utf-8"}),console.log(` Committed as "wip: ${slug}#${group}"`)}catch{}try{if(execSync5("git log @{u}..HEAD --oneline",{encoding:"utf-8"}).trim())console.log(" Pushing unpushed commits..."),execSync5("git push",{encoding:"utf-8",timeout:30000}),console.log(" Push complete.")}catch{try{let branch=execSync5("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim();if(branch&&branch!=="HEAD")execSync5(`git push -u origin ${branch}`,{encoding:"utf-8",timeout:30000}),console.log(" Push complete (set upstream).")}catch{console.log(" \u26A0\uFE0F Push failed \u2014 manual push may be needed.")}}}function autoKillPane(){let paneId=process.env.TMUX_PANE;if(paneId)setTimeout(()=>{try{execSync5(`tmux kill-pane -t '${paneId}'`,{encoding:"utf-8"})}catch{process.exit(0)}},1000);else process.exit(0)}async function doneCommand(ref){try{let{slug,group}=parseRef(ref),result=await completeGroup(slug,group);if(console.log(`\u2705 Group "${group}" marked as done in wish "${slug}"`),result.completedAt)console.log(` Completed at: ${formatTimestamp(result.completedAt)}`);let state2=await getState(slug);if(state2){let nowReady=Object.entries(state2.groups).filter(([,g])=>g.status==="ready"&&g.dependsOn.includes(group)).map(([name])=>name);if(nowReady.length>0)console.log(` Unblocked: ${nowReady.join(", ")}`)}await ensureWorkPushed(slug,group);let waveResult=await detectWaveCompletion(slug,group);if(waveResult){console.log(` \uD83C\uDF0A ${waveResult.waveName} complete! All groups done: ${waveResult.waveGroups.join(", ")}`);try{let protocolRouter=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router)),repoPath=process.cwd(),message=`${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(", ")}]. Run /review or advance to next wave.`,result2=await protocolRouter.sendMessage(repoPath,"cli","team-lead",message);if(result2&&typeof result2==="object"&&"delivered"in result2&&!result2.delivered)console.warn(" \u26A0\uFE0F Wave-complete notification may not have been delivered.");else console.log(" Notified team-lead of wave completion.")}catch{console.warn(" \u26A0\uFE0F Could not notify team-lead (messaging unavailable).")}}autoKillPane()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function statusCommand(slug){try{let state2=await getState(slug);if(!state2){let wishPath=resolveWishPath(slug);if(!wishPath)console.error(`\u274C No state found for wish "${slug}" and no WISH.md found in cwd or repo root`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);let content=await readFile8(wishPath,"utf-8"),groups=parseWishGroups(content);if(groups.length===0)console.error(`\u274C No execution groups found in ${wishPath}`),process.exit(1);state2=await createState(slug,groups),console.log(`\uD83D\uDCDD Auto-initialized state for wish "${slug}" (${groups.length} groups)`)}console.log(`
1028
+ Registering in Omni (${omniUrl})...`);let existingId=await findOmniAgent(name);if(existingId){console.log(` Agent already exists in Omni: ${existingId}`),await edit(name,{omniAgentId:existingId},{global:options.global}),console.log(" Linked existing Omni agent to directory entry.");return}let omniAgentId=await registerAgentInOmni(name,{model:options.model,roles:options.roles});if(omniAgentId)await edit(name,{omniAgentId},{global:options.global}),console.log(` Omni agent created: ${omniAgentId}`),console.log(" Session isolation: per-person + per-channel")}async function handleAgentRegister(name,options){let promptMode=validatePromptMode(options.promptMode),entry=await add({name,dir:resolvePath(options.dir),repo:options.repo?resolvePath(options.repo):void 0,promptMode,model:options.model,roles:options.roles},{global:options.global}),scope=options.global?"global":"project";if(console.log(`Agent "${entry.name}" registered (${scope}).`),printEntry(entry),!options.skipOmni)await handleOmniRegistration(name,options)}function registerAgentNamespace(program2){program2.command("agent").description("Agent lifecycle management").command("register <name>").description("Register an agent locally and auto-register in Omni when configured").requiredOption("--dir <path>","Agent folder (CWD + AGENTS.md)").option("--repo <path>","Default git repo (overridden by team)").option("--prompt-mode <mode>","Prompt mode: append or system","append").option("--model <model>","Default model (sonnet, opus, codex)").option("--roles <roles...>","Built-in roles this agent can orchestrate").option("--global","Write to global directory instead of project").option("--skip-omni","Skip Omni auto-registration").action(async(name,options)=>{try{await handleAgentRegister(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_protocol_router();init_wish_state();init_agents();import{execSync as execSync6}from"child_process";import{existsSync as existsSync19}from"fs";import{mkdir as mkdir8,readFile as readFile9,writeFile as writeFile7}from"fs/promises";import{tmpdir}from"os";import{join as join24}from"path";init_wish_state();import{execSync as execSync5}from"child_process";import{existsSync as existsSync18}from"fs";import{readFile as readFile8}from"fs/promises";import{dirname as dirname6,join as join23}from"path";function resolveWishPath(slug,cwd){let base=cwd??process.cwd(),cwdPath=join23(base,".genie","wishes",slug,"WISH.md");if(existsSync18(cwdPath))return cwdPath;try{let commonDir=execSync5("git rev-parse --path-format=absolute --git-common-dir",{encoding:"utf-8",cwd:base,stdio:["pipe","pipe","pipe"]}).trim(),repoRoot=dirname6(commonDir);if(repoRoot!==base){let repoPath=join23(repoRoot,".genie","wishes",slug,"WISH.md");if(existsSync18(repoPath))return repoPath}}catch{}return null}function parseRef(ref){let hashIdx=ref.indexOf("#");if(hashIdx===-1)throw Error(`Invalid reference "${ref}". Expected format: <slug>#<group>`);let slug=ref.slice(0,hashIdx),group=ref.slice(hashIdx+1);if(!slug||!group)throw Error(`Invalid reference "${ref}". Both slug and group are required.`);return{slug,group}}var STATUS_ICONS={blocked:"\uD83D\uDD12",ready:"\uD83D\uDFE2",in_progress:"\uD83D\uDD04",done:"\u2705"};function formatTimestamp(iso){if(!iso)return"";return new Date(iso).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1})}function padRight2(str2,len){return str2.length>=len?str2:str2+" ".repeat(len-str2.length)}async function detectWaveCompletion(slug,groupName,cwd){let wishPath=resolveWishPath(slug,cwd);if(!wishPath)return null;let content=await readFile8(wishPath,"utf-8"),targetWave=parseExecutionStrategy(content).find((w)=>w.groups.some((g)=>g.group===groupName));if(!targetWave)return null;let state2=await getState(slug,cwd);if(!state2)return null;let waveGroupNames=targetWave.groups.map((g)=>g.group);if(!waveGroupNames.every((g)=>state2.groups[g]?.status==="done"))return null;return{waveName:targetWave.name,waveGroups:waveGroupNames}}async function ensureWorkPushed(slug,group){try{if(execSync5("git status --porcelain",{encoding:"utf-8"}).trim())console.log(" Committing dirty working tree..."),execSync5("git add -A",{encoding:"utf-8"}),execSync5(`git commit -m "wip: ${slug}#${group}"`,{encoding:"utf-8"}),console.log(` Committed as "wip: ${slug}#${group}"`)}catch{}try{if(execSync5("git log @{u}..HEAD --oneline",{encoding:"utf-8"}).trim())console.log(" Pushing unpushed commits..."),execSync5("git push",{encoding:"utf-8",timeout:30000}),console.log(" Push complete.")}catch{try{let branch=execSync5("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim();if(branch&&branch!=="HEAD")execSync5(`git push -u origin ${branch}`,{encoding:"utf-8",timeout:30000}),console.log(" Push complete (set upstream).")}catch{console.log(" \u26A0\uFE0F Push failed \u2014 manual push may be needed.")}}}async function autoCleanupTeam(){let teamName=process.env.GENIE_TEAM;if(!teamName)return;try{let teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),config=await teamManager.getTeam(teamName);if(!config)return;if(config.status==="done")return;console.log(` \uD83E\uDDF9 Auto-cleaning team "${teamName}"...`),await teamManager.setTeamStatus(teamName,"done"),await teamManager.killTeamMembers(teamName),console.log(` \u2705 Team "${teamName}" marked done, members killed.`)}catch{console.log(` \u26A0\uFE0F Auto-cleanup skipped \u2014 run \`genie team done ${teamName}\` manually.`)}}function autoKillPane(){let paneId=process.env.TMUX_PANE;if(paneId)setTimeout(()=>{try{execSync5(`tmux kill-pane -t '${paneId}'`,{encoding:"utf-8"})}catch{process.exit(0)}},1000);else process.exit(0)}async function notifyWaveCompletion(waveResult,wishComplete){console.log(` \uD83C\uDF0A ${waveResult.waveName} complete! All groups done: ${waveResult.waveGroups.join(", ")}`);try{let protocolRouter=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router)),repoPath=process.cwd(),message=wishComplete?`WISH COMPLETE \u2014 all groups done: [${waveResult.waveGroups.join(", ")}]. Run \`genie team done\` to clean up.`:`${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(", ")}]. Run /review or advance to next wave.`,result=await protocolRouter.sendMessage(repoPath,"cli","team-lead",message);if(result&&typeof result==="object"&&"delivered"in result&&!result.delivered)console.warn(" \u26A0\uFE0F Wave-complete notification may not have been delivered.");else console.log(" Notified team-lead of wave completion.")}catch{console.warn(" \u26A0\uFE0F Could not notify team-lead (messaging unavailable).")}}async function doneCommand(ref){try{let{slug,group}=parseRef(ref),result=await completeGroup(slug,group);if(console.log(`\u2705 Group "${group}" marked as done in wish "${slug}"`),result.completedAt)console.log(` Completed at: ${formatTimestamp(result.completedAt)}`);let state2=await getState(slug);if(state2){let nowReady=Object.entries(state2.groups).filter(([,g])=>g.status==="ready"&&g.dependsOn.includes(group)).map(([name])=>name);if(nowReady.length>0)console.log(` Unblocked: ${nowReady.join(", ")}`)}await ensureWorkPushed(slug,group);let wishComplete=await isWishComplete(slug),waveResult=await detectWaveCompletion(slug,group);if(waveResult)await notifyWaveCompletion(waveResult,wishComplete);if(wishComplete)console.log(" \uD83C\uDF89 Wish fully complete \u2014 all groups done."),await autoCleanupTeam();autoKillPane()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function statusCommand(slug){try{let state2=await getState(slug);if(!state2){let wishPath=resolveWishPath(slug);if(!wishPath)console.error(`\u274C No state found for wish "${slug}" and no WISH.md found in cwd or repo root`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);let content=await readFile8(wishPath,"utf-8"),groups=parseWishGroups(content);if(groups.length===0)console.error(`\u274C No execution groups found in ${wishPath}`),process.exit(1);state2=await createState(slug,groups),console.log(`\uD83D\uDCDD Auto-initialized state for wish "${slug}" (${groups.length} groups)`)}console.log(`
1029
1029
  Wish: ${state2.wish}`),console.log("\u2500".repeat(60));let entries=Object.entries(state2.groups),maxNameLen=Math.max(...entries.map(([name])=>name.length),5);console.log(` ${padRight2("GROUP",maxNameLen)} STATUS ASSIGNEE STARTED COMPLETED`),console.log(` ${"\u2500".repeat(maxNameLen+62)}`);for(let[name,group]of entries){let icon=STATUS_ICONS[group.status]??"\u2753",status=padRight2(`${icon} ${group.status}`,13),assignee=padRight2(group.assignee??"-",13),started=padRight2(formatTimestamp(group.startedAt)||"-",14),completed=formatTimestamp(group.completedAt)||"-";console.log(` ${padRight2(name,maxNameLen)} ${status} ${assignee} ${started} ${completed}`)}let total=entries.length,done=entries.filter(([,g])=>g.status==="done").length,inProgress=entries.filter(([,g])=>g.status==="in_progress").length,ready=entries.filter(([,g])=>g.status==="ready").length,blocked=entries.filter(([,g])=>g.status==="blocked").length;console.log(""),console.log(` Progress: ${done}/${total} done | ${inProgress} in progress | ${ready} ready | ${blocked} blocked`),console.log("")}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}function registerStateCommands(program2){program2.command("done <ref>").description("Mark a wish group as done (format: <slug>#<group>)").action(async(ref)=>{await doneCommand(ref)}),program2.command("status <slug>").description("Show wish state overview for all groups").action(async(slug)=>{await statusCommand(slug)}),program2.command("reset <ref>").description("Reset an in-progress group back to ready (format: <slug>#<group>)").action(async(ref)=>{try{let{slug,group}=parseRef(ref),result=await resetGroup(slug,group);if(console.log(`\uD83D\uDD04 Group "${group}" reset to ready in wish "${slug}"`),result.status==="ready")console.log(" Status: ready (assignee cleared)")}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}})}async function writeContextFile(content){let dir=join24(tmpdir(),"genie-dispatch");await mkdir8(dir,{recursive:!0});let ts=Date.now().toString(36),rand=Math.random().toString(36).slice(2,8),filePath=join24(dir,`ctx-${ts}-${rand}.md`);return await writeFile7(filePath,content),filePath}function extractGroup(content,groupName){let pattern=new RegExp(`^### Group ${escapeRegExp(groupName)}:`,"m"),match=content.match(pattern);if(!match||match.index===void 0)return null;let start=match.index,nextBoundary=content.slice(start).slice(1).search(/^### Group \d|^---$/m),end=nextBoundary!==-1?start+1+nextBoundary:content.length;return content.slice(start,end).trim()}function extractWishContext(content){let execGroupsIdx=content.indexOf("## Execution Groups");if(execGroupsIdx!==-1)return content.slice(0,execGroupsIdx).trim();return content.slice(0,2000).trim()}function escapeRegExp(str2){return str2.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function buildContextPrompt(opts){let parts=[`# Dispatch Context (${opts.command})`,"",`**Source file:** \`${opts.filePath}\``,"(Read the full document at the path above for complete context)",""];if(opts.wishContext)parts.push("## Wish Context","",opts.wishContext,"");if(parts.push("## Assigned Section","",opts.sectionContent,""),opts.skill)parts.push("## Initial Command","",`Run \`/${opts.skill}\` to begin.`,"");return parts.join(`
1030
1030
  `)}function getGitDiff(){try{let diff=execSync6("git diff HEAD",{encoding:"utf-8",maxBuffer:1048576}),staged=execSync6("git diff --cached",{encoding:"utf-8",maxBuffer:1048576}),combined=[diff,staged].filter(Boolean).join(`
1031
1031
  `);if(combined.length>50000)return`${combined.slice(0,50000)}
@@ -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.260324.20",
5
+ "version": "4.260325.2",
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.260324.20",
3
+ "version": "4.260325.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.260324.20",
3
+ "version": "4.260325.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.260324.20",
3
+ "version": "4.260325.2",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -284,5 +284,26 @@ describe('Team Manager', () => {
284
284
  const disbanded = await disbandTeam('nonexistent');
285
285
  expect(disbanded).toBe(false);
286
286
  });
287
+
288
+ test('cleans up Claude teams settings directory', async () => {
289
+ const CLAUDE_DIR = join(TEST_DIR, 'claude-config');
290
+ process.env.CLAUDE_CONFIG_DIR = CLAUDE_DIR;
291
+
292
+ await createTeam('feat/claude-cleanup', TEST_REPO, 'dev');
293
+
294
+ // Simulate hook injection: create ~/.claude/teams/<name>/settings.json
295
+ const claudeTeamDir = join(CLAUDE_DIR, 'teams', 'feat-claude-cleanup');
296
+ await mkdir(claudeTeamDir, { recursive: true });
297
+ await writeFile(join(claudeTeamDir, 'settings.json'), '{"hooks":{}}');
298
+ expect(existsSync(join(claudeTeamDir, 'settings.json'))).toBe(true);
299
+
300
+ await disbandTeam('feat/claude-cleanup');
301
+
302
+ // Claude team directory should be gone
303
+ expect(existsSync(claudeTeamDir)).toBe(false);
304
+
305
+ // Clean up
306
+ process.env.CLAUDE_CONFIG_DIR = undefined;
307
+ });
287
308
  });
288
309
  });
@@ -321,13 +321,12 @@ export async function disbandTeam(teamName: string): Promise<boolean> {
321
321
  const config = await getTeam(teamName);
322
322
  if (!config) return false;
323
323
 
324
- // Clean up native teams if enabled
325
- if (config.nativeTeamsEnabled) {
326
- try {
327
- await nativeTeamsManager.deleteNativeTeam(teamName);
328
- } catch {
329
- // Best-effort
330
- }
324
+ // Clean up ~/.claude/teams/<name>/ (config.json, settings.json, inboxes)
325
+ // Always attempt — hook injection writes settings.json regardless of nativeTeamsEnabled
326
+ try {
327
+ await nativeTeamsManager.deleteNativeTeam(teamName);
328
+ } catch {
329
+ // Best-effort
331
330
  }
332
331
 
333
332
  // Kill all running team members (scoped to this team only)
@@ -366,7 +365,7 @@ export async function disbandTeam(teamName: string): Promise<boolean> {
366
365
  * Prune stale team configs.
367
366
  *
368
367
  * Scans all team configs — if a team's worktreePath (clone directory) no longer
369
- * exists on disk, deletes that team's config file.
368
+ * exists on disk, deletes that team's config file and its ~/.claude/teams/ dir.
370
369
  */
371
370
  export async function pruneStaleWorktrees(_repoPath: string): Promise<void> {
372
371
  const dir = teamsDir();
@@ -383,6 +382,12 @@ export async function pruneStaleWorktrees(_repoPath: string): Promise<void> {
383
382
  const content = await readFile(join(dir, file), 'utf-8');
384
383
  const config: TeamConfig = JSON.parse(content);
385
384
  if (config.worktreePath && !existsSync(config.worktreePath)) {
385
+ // Clean up orphaned ~/.claude/teams/<name>/ (settings.json, hooks)
386
+ try {
387
+ await nativeTeamsManager.deleteNativeTeam(config.name);
388
+ } catch {
389
+ // Best-effort
390
+ }
386
391
  await unlink(join(dir, file));
387
392
  }
388
393
  } catch {
@@ -20,6 +20,7 @@ import {
20
20
  getGroupState,
21
21
  getOrCreateState,
22
22
  getState,
23
+ isWishComplete,
23
24
  resetGroup,
24
25
  resolveRepoPath,
25
26
  startGroup,
@@ -566,6 +567,49 @@ describe('findAnyGroupByAssignee', () => {
566
567
  });
567
568
  });
568
569
 
570
+ // ============================================================================
571
+ // isWishComplete
572
+ // ============================================================================
573
+
574
+ describe('isWishComplete', () => {
575
+ test('returns false when no state exists', async () => {
576
+ expect(await isWishComplete('nonexistent', cwd)).toBe(false);
577
+ });
578
+
579
+ test('returns false when some groups are not done', async () => {
580
+ await createState('test-wish', sampleGroups, cwd);
581
+ await startGroup('test-wish', '1', 'agent-a', cwd);
582
+ await completeGroup('test-wish', '1', cwd);
583
+
584
+ expect(await isWishComplete('test-wish', cwd)).toBe(false);
585
+ });
586
+
587
+ test('returns true when all groups are done', async () => {
588
+ await createState('test-wish', sampleGroups, cwd);
589
+
590
+ await startGroup('test-wish', '1', 'a', cwd);
591
+ await completeGroup('test-wish', '1', cwd);
592
+
593
+ await startGroup('test-wish', '2', 'b', cwd);
594
+ await completeGroup('test-wish', '2', cwd);
595
+
596
+ await startGroup('test-wish', '3', 'c', cwd);
597
+ await completeGroup('test-wish', '3', cwd);
598
+
599
+ await startGroup('test-wish', '4', 'd', cwd);
600
+ await completeGroup('test-wish', '4', cwd);
601
+
602
+ expect(await isWishComplete('test-wish', cwd)).toBe(true);
603
+ });
604
+
605
+ test('returns false when groups are in_progress', async () => {
606
+ await createState('test-wish', [{ name: '1' }], cwd);
607
+ await startGroup('test-wish', '1', 'agent-a', cwd);
608
+
609
+ expect(await isWishComplete('test-wish', cwd)).toBe(false);
610
+ });
611
+ });
612
+
569
613
  // ============================================================================
570
614
  // resolveRepoPath — worktree normalization
571
615
  // ============================================================================
@@ -562,3 +562,14 @@ export async function getGroupState(slug: string, groupName: string, cwd?: strin
562
562
  if (!state) return null;
563
563
  return state.groups[groupName] ?? null;
564
564
  }
565
+
566
+ /**
567
+ * Check if all groups in a wish are done.
568
+ * Returns true only when every group has status === 'done'.
569
+ */
570
+ export async function isWishComplete(slug: string, cwd?: string): Promise<boolean> {
571
+ const state = await getState(slug, cwd);
572
+ if (!state) return false;
573
+ const groups = Object.values(state.groups);
574
+ return groups.length > 0 && groups.every((g) => g.status === 'done');
575
+ }
@@ -172,6 +172,37 @@ export async function ensureWorkPushed(slug: string, group: string): Promise<voi
172
172
  }
173
173
  }
174
174
 
175
+ // ============================================================================
176
+ // Team Auto-Cleanup
177
+ // ============================================================================
178
+
179
+ /**
180
+ * Auto-cleanup team when wish is fully complete.
181
+ * Looks up the active team (via GENIE_TEAM env) and marks it done + kills members.
182
+ * Best-effort — if team lookup fails, cleanup is skipped (manual `genie team done` still works).
183
+ */
184
+ async function autoCleanupTeam(): Promise<void> {
185
+ const teamName = process.env.GENIE_TEAM;
186
+ if (!teamName) return;
187
+
188
+ try {
189
+ const teamManager = await import('../lib/team-manager.js');
190
+ const config = await teamManager.getTeam(teamName);
191
+ if (!config) return;
192
+
193
+ // Only clean up if the team is still active
194
+ if (config.status === 'done') return;
195
+
196
+ console.log(` 🧹 Auto-cleaning team "${teamName}"...`);
197
+ await teamManager.setTeamStatus(teamName, 'done');
198
+ await teamManager.killTeamMembers(teamName);
199
+ console.log(` ✅ Team "${teamName}" marked done, members killed.`);
200
+ } catch {
201
+ // Best-effort — manual cleanup via `genie team done` still works
202
+ console.log(` ⚠️ Auto-cleanup skipped — run \`genie team done ${teamName}\` manually.`);
203
+ }
204
+ }
205
+
175
206
  // ============================================================================
176
207
  // Pane Auto-Kill
177
208
  // ============================================================================
@@ -196,6 +227,36 @@ export function autoKillPane(): void {
196
227
  }
197
228
  }
198
229
 
230
+ // ============================================================================
231
+ // Wave + Wish Completion Notifications
232
+ // ============================================================================
233
+
234
+ /**
235
+ * Notify team-lead of wave or wish completion via protocol-router.
236
+ * Best-effort — failures are logged but do not block the done flow.
237
+ */
238
+ async function notifyWaveCompletion(
239
+ waveResult: { waveName: string; waveGroups: string[] },
240
+ wishComplete: boolean,
241
+ ): Promise<void> {
242
+ console.log(` 🌊 ${waveResult.waveName} complete! All groups done: ${waveResult.waveGroups.join(', ')}`);
243
+ try {
244
+ const protocolRouter = await import('../lib/protocol-router.js');
245
+ const repoPath = process.cwd();
246
+ const message = wishComplete
247
+ ? `WISH COMPLETE — all groups done: [${waveResult.waveGroups.join(', ')}]. Run \`genie team done\` to clean up.`
248
+ : `${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(', ')}]. Run /review or advance to next wave.`;
249
+ const result = await protocolRouter.sendMessage(repoPath, 'cli', 'team-lead', message);
250
+ if (result && typeof result === 'object' && 'delivered' in result && !result.delivered) {
251
+ console.warn(' ⚠️ Wave-complete notification may not have been delivered.');
252
+ } else {
253
+ console.log(' Notified team-lead of wave completion.');
254
+ }
255
+ } catch {
256
+ console.warn(' ⚠️ Could not notify team-lead (messaging unavailable).');
257
+ }
258
+ }
259
+
199
260
  // ============================================================================
200
261
  // Commands
201
262
  // ============================================================================
@@ -229,23 +290,19 @@ export async function doneCommand(ref: string): Promise<void> {
229
290
  // Push enforcement: commit dirty tree + push unpushed commits
230
291
  await ensureWorkPushed(slug, group);
231
292
 
293
+ // Wish-level completion check — are ALL groups done?
294
+ const wishComplete = await wishState.isWishComplete(slug);
295
+
232
296
  // Wave completion detection + team-lead notification
233
297
  const waveResult = await detectWaveCompletion(slug, group);
234
298
  if (waveResult) {
235
- console.log(` 🌊 ${waveResult.waveName} complete! All groups done: ${waveResult.waveGroups.join(', ')}`);
236
- try {
237
- const protocolRouter = await import('../lib/protocol-router.js');
238
- const repoPath = process.cwd();
239
- const message = `${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(', ')}]. Run /review or advance to next wave.`;
240
- const result = await protocolRouter.sendMessage(repoPath, 'cli', 'team-lead', message);
241
- if (result && typeof result === 'object' && 'delivered' in result && !result.delivered) {
242
- console.warn(' ⚠️ Wave-complete notification may not have been delivered.');
243
- } else {
244
- console.log(' Notified team-lead of wave completion.');
245
- }
246
- } catch {
247
- console.warn(' ⚠️ Could not notify team-lead (messaging unavailable).');
248
- }
299
+ await notifyWaveCompletion(waveResult, wishComplete);
300
+ }
301
+
302
+ // If entire wish is complete, auto-trigger team cleanup
303
+ if (wishComplete) {
304
+ console.log(' 🎉 Wish fully complete all groups done.');
305
+ await autoCleanupTeam();
249
306
  }
250
307
 
251
308
  // Auto-kill the calling agent's tmux pane