@@ -61,6 +61,15 @@ function jsonWrap(data: Record<string, any>): Record<string, any> {
6161 return { schemaVersion : SCHEMA_VERSION , codesessionVersion : VERSION , ...data } ;
6262}
6363
64+ /** Resolve the active session for the current directory (supports parallel sessions). */
65+ async function resolveActiveSession ( ) {
66+ const cwd = process . cwd ( ) ;
67+ const gitRoot = await getGitRoot ( cwd ) ;
68+ const scopeDir = gitRoot || cwd ;
69+ // Prefer session matching this directory/git root
70+ return getActiveSessionForDir ( scopeDir ) || getActiveSession ( ) ;
71+ }
72+
6473function sessionToJSON ( session : any , extras ?: { files ?: any [ ] ; commits ?: any [ ] ; aiUsage ?: any [ ] ; notes ?: any [ ] } ) {
6574 const obj : any = {
6675 schemaVersion : SCHEMA_VERSION ,
@@ -181,22 +190,26 @@ program
181190 console . log ( chalk . gray ( ` Closed ${ allActive . length } stale session(s)` ) ) ;
182191 }
183192 } else if ( ! options . resume ) {
184- // Default: error about active session
185- const active = allActive [ 0 ] ;
186- if ( options . json ) {
187- jsonError ( 'session_active' , `Session "${ active . name } " is already active` , {
188- activeSession : active . name ,
189- id : active . id ,
190- hint : 'Use --resume to reattach or --close-stale to auto-close' ,
191- } ) ;
192- } else {
193- console. log ( chalk . yellow ( `\nSession "${ active . name } " is already active (id: ${ active . id } ).` ) ) ;
194- console . log ( chalk . gray ( ' Options:' ) ) ;
195- console . log ( chalk . gray ( ' cs end — end it manually' ) ) ;
196- console . log ( chalk . gray ( ' cs start --resume — reuse session for this directory' ) ) ;
197- console . log ( chalk . gray ( ' cs start --close-stale — auto-close stale sessions\n' ) ) ;
193+ // Only block if there's already an active session for THIS directory
194+ // Different directories/git roots can run parallel sessions
195+ const sameDir = getActiveSessionForDir ( scopeDir ) ;
196+ if ( sameDir ) {
197+ if ( options . json ) {
198+ jsonError ( 'session_active' , `Session "${ sameDir . name } " is already active for this directory` , {
199+ activeSession : sameDir . name ,
200+ id : sameDir . id ,
201+ hint : 'Use --resume to reattach or --close-stale to auto-close' ,
202+ } ) ;
203+ } else {
204+ console . log ( chalk . yellow ( `\nSession "${ sameDir . name } " is already active for this directory (id: ${ sameDir . id } ).` ) ) ;
205+ console . log ( chalk . gray ( ' Options:' ) ) ;
206+ console . log ( chalk . gray ( ' cs end — end it manually' ) ) ;
207+ console . log ( chalk . gray ( ' cs start --resume — reuse session for this directory' ) ) ;
208+ console . log ( chalk . gray ( ' cs start --close-stale — auto-close stale sessions\n' ) ) ;
209+ }
210+ return ;
198211 }
199- return ;
212+ // Different directory — allow parallel session
200213 }
201214 }
202215
@@ -267,7 +280,7 @@ program
267280 return ;
268281 }
269282 } else {
270- session = getActiveSession ( ) ;
283+ session = await resolveActiveSession ( ) ;
271284 }
272285
273286 if ( ! session ) {
@@ -434,7 +447,7 @@ program
434447 . option ( '--agent <name>' , 'Agent name (optional)' )
435448 . option ( '-s, --session <id>' , 'Target a specific session by ID' , parseInt )
436449 . option ( '--json' , 'Output JSON (for agents)' )
437- . action ( ( options ) => {
450+ . action ( async ( options ) => {
438451 let session ;
439452 if ( options . session ) {
440453 session = getSession ( options . session ) ;
@@ -447,7 +460,7 @@ program
447460 return ;
448461 }
449462 } else {
450- session = getActiveSession ( ) ;
463+ session = await resolveActiveSession ( ) ;
451464 }
452465 if ( ! session ) {
453466 if ( options . json ) {
@@ -536,12 +549,12 @@ program
536549 . description ( 'Show active session status' )
537550 . option ( '-s, --session <id>' , 'Show a specific session by ID' , parseInt )
538551 . option ( '--json' , 'Output JSON (for agents)' )
539- . action ( ( options ) => {
552+ . action ( async ( options ) => {
540553 let session ;
541554 if ( options . session ) {
542555 session = getSession ( options . session ) ;
543556 } else {
544- session = getActiveSession ( ) ;
557+ session = await resolveActiveSession ( ) ;
545558 }
546559 if ( ! session ) {
547560 if ( options . json ) {
@@ -640,7 +653,7 @@ program
640653 . argument ( '<message>' , 'Note message' )
641654 . option ( '-s, --session <id>' , 'Target a specific session by ID' , parseInt )
642655 . option ( '--json' , 'Output JSON (for agents)' )
643- . action ( ( message : string , options ) => {
656+ . action ( async ( message : string , options ) => {
644657 let session ;
645658 if ( options . session ) {
646659 session = getSession ( options . session ) ;
@@ -653,7 +666,7 @@ program
653666 return ;
654667 }
655668 } else {
656- session = getActiveSession ( ) ;
669+ session = await resolveActiveSession ( ) ;
657670 }
658671 if ( ! session ) {
659672 if ( options . json ) {
@@ -757,7 +770,7 @@ program
757770
758771 // Must have an active codesession — if not, exit WITHOUT saving position
759772 // so tokens aren't lost (they'll be picked up on the next call after cs start)
760- const session = getActiveSession ( ) ;
773+ const session = await resolveActiveSession ( ) ;
761774 if ( ! session ) process . exit ( 0 ) ;
762775
763776 // Track position so we don't double-count across multiple Stop events
0 commit comments