+
    #j<                    z   R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t^ RI
Ht RtR R ltR0R R lltR	 R
 ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R  ltR! R" ltR# R$ ltR% R& ltR' R( ltR) R* ltR+ R, lt R0R- R. llt!]"R/8X  d
   ]!! 4        R# R# )1us  Linear GraphQL API CLI — zero dependencies, stdlib only.

Usage:
  linear_api.py <command> [args...]

Commands:
  whoami                                  Show authenticated user
  list-teams                              List all teams
  list-projects [--team KEY]              List projects (optionally filter by team)
  list-states [--team KEY]                List workflow states
  list-issues [filters]                   List issues
    --team KEY                            Filter by team key (e.g. ENG)
    --status NAME                         Filter by workflow state name
    --assignee NAME                       Filter by assignee name (exact)
    --label NAME                          Filter by label name
    --limit N                             Max results (default: 25)
  get-issue <IDENTIFIER>                  Full issue details (e.g. ENG-42)
  search-issues <query>                   Full-text search across issues
  create-issue [options]                  Create a new issue
    --title TITLE                         Required
    --team KEY                            Required
    --description DESC
    --priority 0-4                        0=none, 1=urgent, 4=low
    --label NAME
    --assignee NAME
    --parent IDENTIFIER                   Parent issue ID for sub-issues
  update-issue <IDENTIFIER> [options]     Update existing issue (same options as create)
  update-status <IDENTIFIER> <STATE>      Move issue to workflow state (by state name)
  add-comment <IDENTIFIER> <body>         Add comment to issue

  list-documents [--limit N]              List documents (docs, not issues)
  get-document <SLUG_OR_ID>               Fetch a document by slugId (from URL) or UUID
  search-documents <query>                Search documents by title

  raw <graphql_query> [variables_json]    Run an arbitrary GraphQL query
                                          Use --vars '{"key":"value"}' for variables

Auth:
  Set LINEAR_API_KEY environment variable (from Linear Settings -> API).
  Uses the personal API key header format: `Authorization: <KEY>` (no Bearer prefix).

Output:
  JSON to stdout. Errors to stderr with non-zero exit code.
)annotationsN)Anyzhttps://api.linear.app/graphqlc                   V ^8  d   QhRR/# )   returnstr )formats   "g/opt/hermes-venv/lib/python3.14/site-packages/../../../skills/productivity/linear/scripts/linear_api.py__annotate__r   ;   s     	 	# 	    c                     \         P                  P                  R R4      P                  4       p V '       g6   \        P
                  P                  R4       \        P                  ! ^4       V # )LINEAR_API_KEY zERROR: LINEAR_API_KEY not set.
Create one at https://linear.app/settings/api and export it,
or add `LINEAR_API_KEY=lin_api_...` to ~/.hermes/.env
)osenvirongetstripsysstderrwriteexit)keys    r
   _get_keyr   ;   sM    
**..)2
.
4
4
6C

F	

 	Jr   c               $    V ^8  d   QhRRRRRR/# )r   queryr   	variableszdict[str, Any] | Noner   zdict[str, Any]r   )r	   s   "r
   r   r   G   s"     !( !(s !(4 !( !(r   c           
        \        4       pRV /pV'       d   WR&   \        P                  ! V4      P                  R4      p\        P
                  P                  \        VRRRVRR/R	R
7      p \        P
                  P                  V^R7      ;_uu_ 4       pVP                  4       P                  R4      pRRR4       \        P&                  ! X4      p	RT	9   d}   T	R,          '       dn   \        P                  P                  R\        P                  ! T	R,          ^R7       R24       T	P)                  R4      '       g   \        P"                  ! ^4       T	P)                  R/ 4      ;'       g    / #   + '       g   i     L; i  \        P                  P                   dp   p\        P                  P                  RTP                    RTP                  4       P                  RR4       R24       \        P"                  ! ^4        Rp?ELQRp?i\        P                  P$                   dE   p\        P                  P                  RT R24       \        P"                  ! ^4        Rp?ELRp?ii ; i)zOExecute a GraphQL query against Linear. Raises on HTTP error or GraphQL errors.r   r   zutf-8zContent-Typezapplication/jsonAuthorizationz
User-Agentzhermes-agent-linear-skill/1.0POST)dataheadersmethod)timeoutNzHTTP z: replace
zNetwork error: errorszGraphQL errors: )indentr    )r   jsondumpsencodeurllibrequestRequestAPI_URLurlopenreaddecodeerror	HTTPErrorr   r   r   coder   URLErrorloadsr   )
r   r   r   payloadr    reqrespbodyeresults
   &&        r
   gqlr=   G   s   
*CG(::g%%g.D
..
 
 .S9

  ! 	C^^##C#4499;%%g.D 5 ZZF6fX..

+DJJvh7GPQ,R+SSUVWzz&!!HHQK::fb!''R' 54<<!! 

51668??7I+N*OrRS<<   

?1#R01sI   .)F  E47F 4F	?F F I6&A$H I61I629I11I6c                    V ^8  d   QhRRRR/# )r   objr   r   Noner   )r	   s   "r
   r   r   k   s     2 2c 2d 2r   c                R    \        \        P                  ! V ^\        R7      4       R# )r   )r'   defaultN)printr(   r)   r   )r?   s   &r
   emitrD   k   s    	$**SC
01r   c                    V ^8  d   QhRRRR/# r   _argsargparse.Namespacer   r@   r   )r	   s   "r
   r   r   q   s      ( T r   c                P    R p\        \        V4      P                  R4      4       R# )z.query { viewer { id name email displayName } }viewerNrD   r=   r   rG   qs   & r
   
cmd_whoamirN   q   s    8AQH	r   c                    V ^8  d   QhRRRR/# rF   r   )r	   s   "r
   r   r   v   s     3 3, 3 3r   c                r    R p\        \        V4      P                  R/ 4      P                  R. 4      4       R# )zAquery { teams(first: 100) { nodes { id key name description } } }teamsnodesNrK   rL   s   & r
   cmd_list_teamsrS   v   s+    KAQGR	 	$	$Wb	12r   c                    V ^8  d   QhRRRR/# )r   key_or_namer   r   z
str | Noner   )r	   s   "r
   r   r   {   s      # * r   c                   Rp\        V4      P                  R/ 4      P                  R. 4      pV P                  4       pV FF  pVR,          P                  4       V8X  g   VR,          P                  4       V8X  g   K=  VR,          u # 	  R# )z%Map a team key (ENG) or name to UUID.z5query { teams(first: 100) { nodes { id key name } } }rQ   rR   r   nameidN)r=   r   lower)rU   rM   rQ   klts   &    r
   _resolve_team_idr\   {   ss    ?AFJJw#''4E				BU8>>r!QvY__%6"%<T7N  r   c                    V ^8  d   QhRRRR/# r   argsrH   r   r@   r   )r	   s   "r
   r   r      s     : :. :4 :r   c                   V P                   '       d   \        V P                   4      pV'       gD   \        P                  P	                  R V P                    R24       \        P
                  ! ^4       Rp\        VRV/4      p\        VP                  R/ 4      P                  R/ 4      P                  R. 4      4       R# Rp\        \        V4      P                  R/ 4      P                  R. 4      4       R# )	Team not found: r%   zxquery($id: String!) {
          team(id: $id) { projects(first: 100) { nodes { id name description state } } }
        }rX   teamprojectsrR   zFquery { projects(first: 100) { nodes { id name description state } } }N)	rb   r\   r   r   r   r   r=   rD   r   )r_   tidrM   r    s   &   r
   cmd_list_projectsre      s    yyytyy)JJ/		{"=>HHQK 1tSk"TXXfb!%%j"599'2FGTSVZZ
B'++GR89r   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r      s     @ @, @ @r   c                   V P                   '       d   \        V P                   4      pV'       gD   \        P                  P	                  R V P                    R24       \        P
                  ! ^4       Rp\        \        VRV/4      P                  R/ 4      P                  R/ 4      P                  R. 4      4       R	# Rp\        \        V4      P                  R/ 4      P                  R. 4      4       R	# )
ra   r%   zoquery($id: String!) {
          team(id: $id) { states(first: 100) { nodes { id name type color } } }
        }rX   rb   statesrR   zLquery { workflowStates(first: 200) { nodes { id name type team { key } } } }workflowStatesN)	rb   r\   r   r   r   r   rD   r=   r   )r_   rd   rM   s   &  r
   cmd_list_statesrj      s    yyytyy)JJ/		{"=>HHQK 	ST3K $$VR044XrBFFwPRSTZSVZZ("-11'2>?r   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r      s     2 2, 2 2r   c                   / pV P                   '       d   R RV P                   //VR&   V P                  '       d   RRV P                  //VR&   V P                  '       d   RRV P                  //VR&   V P                  '       d   RRV P                  //VR&   Rp\	        TRT;'       g    R	R
V P
                  /4      p\        VP                  R/ 4      P                  R. 4      4       R	# )r   eqrb   rW   stateassigneelabelsa!  query($filter: IssueFilter, $first: Int!) {
      issues(filter: $filter, first: $first, orderBy: updatedAt) {
        nodes {
          id identifier title
          state { name } priority
          assignee { name }
          team { key }
          updatedAt url
        }
      }
    }filterNfirstissuesrR   )rb   statusro   labelr=   limitrD   r   )r_   filtrM   r    s   &   r
   cmd_list_issuesrx      s    Dyyydii01V{{{$!45W}}}"T4==$9:Zzzz 4"45X
		A q8T\\T7DJJ?@D(B		#	#GR	01r   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r      s     7 7* 7t 7r   c                j    R p\        \        VRV P                  /4      P                  R4      4       R# )a  query($id: String!) {
      issue(id: $id) {
        id identifier title description
        state { name type }
        priority priorityLabel
        assignee { name email }
        creator { name }
        team { key name }
        project { name }
        labels { nodes { name } }
        parent { identifier title }
        children { nodes { identifier title state { name } } }
        comments { nodes { user { name } body createdAt } }
        createdAt updatedAt url
      }
    }rX   issueN)rD   r=   
identifierr   r_   rM   s   & r
   cmd_get_issuer~      s.    		A  	Qt'	(	,	,W	56r   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r      s     e e. e4 er   c           	         R p\        \        VRV P                  RV P                  /4      P	                  R/ 4      P	                  R. 4      4       R# )zquery($term: String!, $first: Int!) {
      searchIssues(term: $term, first: $first) {
        nodes { id identifier title state { name } url }
      }
    }termrr   searchIssuesrR   NrD   r=   r   rv   r   r}   s   & r
   cmd_search_issuesr      sG    		A
 	QWdjj9	:	>	>~r	R	V	VW^`b	cdr   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r      s     4 4- 4$ 4r   c                   \        V P                  4      pV'       gD   \        P                  P	                  R V P                   R24       \        P
                  ! ^4       RV P                  RV/pV P                  '       d   V P                  VR&   V P                  e   V P                  VR&   V P                  '       d   V P                  VR&   Rp\        \        VR	V/4      P                  R
4      4       R# )ra   r%   titleteamIddescriptionNpriorityparentIdzmutation($input: IssueCreateInput!) {
      issueCreate(input: $input) {
        success issue { id identifier title url }
      }
    }inputissueCreate)r\   rb   r   r   r   r   r   r   r   parentrD   r=   r   )r_   rd   inprM   s   &   r
   cmd_create_issuer      s    
499
%C

+DII;b9:"DJJ#>C!--M}} --J{{{++J		A
 	Q#		#	#M	23r   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r      s     K K- K$ Kr   c           	        / pV P                   '       d   V P                   VR &   V P                  '       d   V P                  VR&   V P                  e   V P                  VR&   V'       g6   \        P                  P                  R4       \        P                  ! ^4       Rp\        \        VRV P                  RV/4      P                  R4      4       R# )	r   r   Nr   zNo update fields provided.
zmutation($id: String!, $input: IssueUpdateInput!) {
      issueUpdate(id: $id, input: $input) {
        success issue { identifier title url }
      }
    }rX   r   issueUpdate)r   r   r   r   r   r   r   rD   r=   r|   r   )r_   r   rM   s   &  r
   cmd_update_issuer      s    CzzzzzG!--M}} --J

78		A
 	Qt5	6	:	:=	IJr   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r     s     U U. U4 Ur   c           
       a R p\        VRV P                  /4      P                  R4      pV'       gD   \        P                  P                  RV P                   R24       \        P                  ! ^4       V P                  P                  4       o\        V3R lVR,          R,          R,           4       R	4      pV'       gr   \        P                  P                  R
V P                   RVR,          R,          R,           Uu. uF  qDR,          NK  	  up R24       \        P                  ! ^4       Rp\        \        VRV P                  RVR,          /4      P                  R4      4       R	# u upi )ziquery($id: String!) {
      issue(id: $id) { team { id states(first: 100) { nodes { id name } } } }
    }rX   r{   zIssue not found: r%   c              3  b   <"   T F$  qR ,          P                  4       S8X  g   K   Vx  K&  	  R# 5i)rW   N)rY   ).0ssls   & r
   	<genexpr>$cmd_update_status.<locals>.<genexpr>  s&     Y=6ARVXAX!!=s   /
/rb   rh   rR   NzState 'z' not found. Available: rW   zmutation($id: String!, $stateId: String!) {
      issueUpdate(id: $id, input: { stateId: $stateId }) {
        success issue { identifier state { name } url }
      }
    }stateIdr   )r=   r|   r   r   r   r   r   rn   rY   nextrD   )r_   get_qr{   matchr   rM   r   s   &     @r
   cmd_update_statusr     s)   	E doo./33G<E

,T__,=R@A				BYU6]84W=Y[_`E

djj\!9#(=#:7#CD#Ca&		#CDERI	
 			A
 	Qt	5;?	@	D	D]	ST Es   E1c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r     s     b b, b br   c           
         R p\        \        VRRV P                  RV P                  //4      P	                  R4      4       R# )zmutation($input: CommentCreateInput!) {
      commentCreate(input: $input) {
        success comment { id body createdAt }
      }
    }r   issueIdr:   commentCreateN)rD   r=   r|   r:   r   r}   s   & r
   cmd_add_commentr     s<    		A
 	Q9doovtyyIJ	K	O	OP_	`ar   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r   *  s     N N/ ND Nr   c                    R p\        \        VRV P                  /4      P                  R/ 4      P                  R. 4      4       R# )zquery($first: Int!) {
      documents(first: $first, orderBy: updatedAt) {
        nodes { id title slugId updatedAt url project { name } creator { name } }
      }
    }rr   	documentsrR   N)rD   r=   rv   r   r}   s   & r
   cmd_list_documentsr   *  s=    		A
 	Q$**%	&	*	*;	;	?	?	LMr   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r   3  s     * *- *$ *r   c                n   V P                   p\        V4      ^$8H  ;'       d    VP                  R4      ^8H  pV'       d+   Rp\        \	        VRV/4      P                  R4      4       R	# Rp\	        VRV/4      P                  R/ 4      P                  R. 4      p\        V'       d
   V^ ,          MR	4       R	# )
zFetch a document by slugId (from URL) OR full UUID.

Linear document URLs look like:
  https://linear.app/<workspace>/document/<slug>-<shortid>
The part we want is the final hex segment (the slugId).
-zquery($id: String!) {
          document(id: $id) {
            id title content contentState slugId
            createdAt updatedAt url
            creator { name } project { name }
          }
        }rX   documenta  query($slug: String!) {
          documents(filter: { slugId: { eq: $slug } }, first: 1) {
            nodes {
              id title content contentState slugId
              createdAt updatedAt url
              creator { name } project { name }
            }
          }
        }slugr   rR   N)reflencountrD   r=   r   )r_   r   is_uuidrM   rR   s   &    r
   cmd_get_documentr   3  s     ((C#h"n4431!4G 	ST3K $$Z01 A}%))+r:>>wKU1XD)r   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r   U  s     b b1 bd br   c           	         R p\        \        VRV P                  RV P                  /4      P	                  R/ 4      P	                  R. 4      4       R# )zquery($term: String!, $first: Int!) {
      documents(filter: { title: { containsIgnoreCase: $term } }, first: $first) {
        nodes { id title slugId url updatedAt }
      }
    }r   rr   r   rR   Nr   r}   s   & r
   cmd_search_documentsr   U  sG    		A
 	QWdjj9	:	>	>{B	O	S	ST[]_	`ar   c                    V ^8  d   QhRRRR/# r^   r   )r	   s   "r
   r   r   _  s     % %$ % %r   c                    V P                   '       d!   \        P                  ! V P                   4      MR p\        \	        V P
                  V4      4       R # N)varsr(   r6   rD   r=   r   )r_   r   s   & r
   cmd_rawr   _  s1    )-

499%ITZZ	#$r   c                   V ^8  d   QhRR/# )r   r   zargparse.ArgumentParserr   )r	   s   "r
   r   r   f  s     M M- Mr   c                 p	   \         P                  ! R RR7      p V P                  RRR7      pVP                  R4      P	                  \
        R7       VP                  R4      P	                  \        R7       VP                  R	4      pVP                  R
4       VP	                  \        R7       VP                  R4      pVP                  R
4       VP	                  \        R7       VP                  R4      pVP                  R
4       VP                  R4       VP                  R4       VP                  R4       VP                  R\        ^R7       VP	                  \        R7       VP                  R4      pVP                  R4       VP	                  \        R7       VP                  R4      pVP                  R4       VP                  R\        ^R7       VP	                  \        R7       VP                  R4      pVP                  RRR7       VP                  R
RR7       VP                  R4       VP                  R\        . R+OR7       VP                  R4       VP                  R4       VP                  R4       VP	                  \        R7       VP                  R4      pVP                  R4       VP                  R4       VP                  R4       VP                  R\        . R+OR7       VP	                  \        R7       VP                  R4      p	V	P                  R4       V	P                  R4       V	P	                  \         R7       VP                  R 4      p
V
P                  R4       V
P                  R!4       V
P	                  \"        R7       VP                  R"4      pVP                  R\        ^2R7       VP	                  \$        R7       VP                  R#4      pVP                  R$R%R&7       VP	                  \&        R7       VP                  R'4      pVP                  R4       VP                  R\        ^R7       VP	                  \(        R7       VP                  R(4      pVP                  R4       VP                  R)R*R&7       VP	                  \*        R7       V # ),zlinear_api.pyzLinear GraphQL CLI)progr   cmdT)destrequiredwhoami)funcz
list-teamszlist-projectsz--teamzlist-stateszlist-issuesz--statusz
--assigneez--labelz--limit)typerB   z	get-issuer|   zsearch-issuesr   zcreate-issuez--title)r   z--descriptionz
--priority)r   choicesz--parentzupdate-issuezupdate-statusrn   zadd-commentr:   zlist-documentszget-documentr   z)slugId (hex suffix from URL) or full UUID)helpzsearch-documentsrawz--varszJSON string of variables)       r         )argparseArgumentParseradd_subparsers
add_parserset_defaultsrN   rS   add_argumentre   rj   intrx   r~   r   r   r   r   r   r   r   r   r   )psublplsligisiciuiusacldgdsdrs                  r
   build_parserr   f  s   _BVWA




5CNN8))z):NN< -->-B		(BOOHOO*O+		&BOOHOOO)		&BOOHOOJOOL!OOIOOICO4OOO)		$BOOL!OOO'		(BOOGOOICO4OO*O+		'BOOIO-OOHtO,OOO$OOLsOODOOIOOL!OOJOO)O*		'BOOL!OOIOOO$OOLsOODOO)O*		(BOOL!OOGOO*O+		&BOOL!OOFOOO)	(	)BOOICO4OO+O,		'BOOE KOLOO)O*	*	+BOOGOOICO4OO-O.uANN7NN8"<N=NNN Hr   c                    V ^8  d   QhRRRR/# )r   argvzlist[str] | Noner   r@   r   )r	   s   "r
   r   r     s       4 r   c                ^    \        4       pVP                  V 4      pVP                  V4       R # r   )r   
parse_argsr   )r   parserr_   s   &  r
   mainr     s$    ^FT"DIIdOr   __main__r   )#__doc__
__future__r   r   r(   r   r   urllib.errorr+   urllib.requesttypingr   r.   r   r=   rD   rN   rS   r\   re   rj   rx   r~   r   r   r   r   r   r   r   r   r   r   r   __name__r   r   r
   <module>r      s   +X #   	 
   
*	!(H2
3
: @247(e4,K&U4bN*Db%M` zF r   