11import { readFileSync } from "node:fs" ;
22import { dirname , join , resolve } from "node:path" ;
33import { parse as parseYaml , stringify as stringifyYaml } from "yaml" ;
4+ import { log } from "../lib/log" ;
45import type {
56 AgentId ,
67 AgentKind ,
@@ -393,25 +394,7 @@ function parseProjectConfig(parsed: Record<string, unknown>): ProjectConfig {
393394 ? parsed . integrations . github . autoRemoveOnMerge
394395 : DEFAULT_CONFIG . integrations . github . autoRemoveOnMerge ,
395396 } ,
396- linear : {
397- enabled : isRecord ( parsed . integrations ) && isRecord ( parsed . integrations . linear ) && typeof parsed . integrations . linear . enabled === "boolean"
398- ? parsed . integrations . linear . enabled
399- : DEFAULT_CONFIG . integrations . linear . enabled ,
400- autoCreateWorktrees : isRecord ( parsed . integrations ) && isRecord ( parsed . integrations . linear ) && typeof parsed . integrations . linear . autoCreateWorktrees === "boolean"
401- ? parsed . integrations . linear . autoCreateWorktrees
402- : DEFAULT_CONFIG . integrations . linear . autoCreateWorktrees ,
403- createTicketOption : isRecord ( parsed . integrations ) &&
404- isRecord ( parsed . integrations . linear ) &&
405- typeof parsed . integrations . linear . createTicketOption === "boolean"
406- ? parsed . integrations . linear . createTicketOption
407- : DEFAULT_CONFIG . integrations . linear . createTicketOption ,
408- ...( isRecord ( parsed . integrations ) &&
409- isRecord ( parsed . integrations . linear ) &&
410- typeof parsed . integrations . linear . teamId === "string" &&
411- parsed . integrations . linear . teamId . trim ( )
412- ? { teamId : parsed . integrations . linear . teamId . trim ( ) }
413- : { } ) ,
414- } ,
397+ linear : parseLinearIntegration ( parsed ) ,
415398 } ,
416399 lifecycleHooks : parseLifecycleHooks ( parsed . lifecycleHooks ) ,
417400 autoName : parseAutoName ( parsed . auto_name ) ,
@@ -423,6 +406,46 @@ function defaultConfig(): ProjectConfig {
423406 return parseProjectConfig ( { } ) ;
424407}
425408
409+ function parseTeamKeyList ( raw : unknown ) : string [ ] | undefined {
410+ if ( ! Array . isArray ( raw ) ) return undefined ;
411+ const keys = raw
412+ . filter ( ( entry ) : entry is string => typeof entry === "string" )
413+ . map ( ( entry ) => entry . trim ( ) . toUpperCase ( ) )
414+ . filter ( ( entry ) => entry . length > 0 ) ;
415+ return keys . length > 0 ? Array . from ( new Set ( keys ) ) : undefined ;
416+ }
417+
418+ /** Track whether the deprecation warning for `integrations.linear.teamId` has
419+ * already been logged this process so config reloads don't spam the log. */
420+ let warnedLegacyLinearTeamId = false ;
421+
422+ function parseLinearIntegration ( parsed : Record < string , unknown > ) : LinearIntegrationConfig {
423+ const defaults = DEFAULT_CONFIG . integrations . linear ;
424+ const linear = isRecord ( parsed . integrations ) && isRecord ( parsed . integrations . linear )
425+ ? parsed . integrations . linear
426+ : null ;
427+
428+ if ( ! linear ) return { ...defaults } ;
429+
430+ if ( typeof linear . teamId === "string" && ! warnedLegacyLinearTeamId ) {
431+ warnedLegacyLinearTeamId = true ;
432+ log . warn ( "[config] integrations.linear.teamId is no longer used — the ticket team is now picked at creation time in the dashboard" ) ;
433+ }
434+
435+ const watchTeams = parseTeamKeyList ( linear . watchTeams ) ;
436+
437+ return {
438+ enabled : typeof linear . enabled === "boolean" ? linear . enabled : defaults . enabled ,
439+ autoCreateWorktrees : typeof linear . autoCreateWorktrees === "boolean"
440+ ? linear . autoCreateWorktrees
441+ : defaults . autoCreateWorktrees ,
442+ createTicketOption : typeof linear . createTicketOption === "boolean"
443+ ? linear . createTicketOption
444+ : defaults . createTicketOption ,
445+ ...( watchTeams ? { watchTeams } : { } ) ,
446+ } ;
447+ }
448+
426449function parseLocalLinearOverlay ( parsed : Record < string , unknown > ) : Partial < LinearIntegrationConfig > | null {
427450 if ( ! isRecord ( parsed . integrations ) ) return null ;
428451 const linear = parsed . integrations . linear ;
@@ -432,7 +455,8 @@ function parseLocalLinearOverlay(parsed: Record<string, unknown>): Partial<Linea
432455 if ( typeof linear . enabled === "boolean" ) overlay . enabled = linear . enabled ;
433456 if ( typeof linear . autoCreateWorktrees === "boolean" ) overlay . autoCreateWorktrees = linear . autoCreateWorktrees ;
434457 if ( typeof linear . createTicketOption === "boolean" ) overlay . createTicketOption = linear . createTicketOption ;
435- if ( typeof linear . teamId === "string" && linear . teamId . trim ( ) ) overlay . teamId = linear . teamId . trim ( ) ;
458+ const watchTeams = parseTeamKeyList ( linear . watchTeams ) ;
459+ if ( watchTeams ) overlay . watchTeams = watchTeams ;
436460 return Object . keys ( overlay ) . length > 0 ? overlay : null ;
437461}
438462
0 commit comments