+
    3Uj                      a  0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RI	t^ RI
t]P                  P                  RR4      P                  R4      t/ tR]R&   / tR]R&   R	tR
tRtRtR R ltR R lt ! R R]4      tRaR R lltRbR 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!RcR# R$ llt"R% R& lt#R' R( lt$R) R* lt%RcR+ R, llt&R- R. lt'R/ R0 lt(R1 R2 lt)R3 R4 lt*R5 R6 lt+R7 R8 lt,R9 R: lt-RdR; R< llt.ReR= R> llt/RfR? R@ llt0RgRA RB llt1RhRC RD llt2RE RF lt3RiRG RH llt4RjRI RJ llt5RkRK RL llt6RgRM RN llt7RO RP lt8RQ RR lt9RlRS RT llt:RmRU RV llt;RW RX lt<RnRY RZ llt=R[ R\ lt>RkR] R^ llt?RoR_ R` llt@R# )pu  Asana — Ahmed's project management (projects, tasks, due dates, assignees).

THE single source of truth for Asana in this repo. Two doors use this ONE module:
  • the hosted connector  — jarvis-tools-service/mcp_server.py  (@mcp.tool wrappers)
  • voice Jarvis          — voice/asana_control.py               (thin SDK-MCP wrapper
                            that imports THIS file; no REST logic is duplicated)
Fix a bug here and both doors are fixed.

Plain HTTPS REST against https://app.asana.com/api/1.0 with a Personal Access
Token — no OAuth, no SDK, stdlib urllib only (the Docker image and the voice
engine both depend on there being no new deps).

Env: ASANA_PAT       — Personal Access Token (asana.com → Settings → Apps →
                       Developer apps → Personal access tokens).
     ASANA_WORKSPACE — optional; workspace gid or name to pin. If unset we
                       resolve (and cache) the account's first workspace.

Results are read aloud by voice Jarvis → return short plain prose / simple
dashed lists. No markdown, no raw JSON (the only exception is `raw()`, the
escape hatch, which is for the model, not for speech). Nothing here ever raises:
every failure comes back as one human sentence.

API gotchas honoured here (learned the hard way / from the OpenAPI spec):
  • {"data": …} envelope BOTH ways — wrap request bodies, unwrap responses.
  • opt_fields or you get NOTHING but gid/resource_type/name. Every call sends it.
  • projects/tags/followers are CREATE-ONLY on a task — changing them needs
    addProject / removeProject / addTag / addFollowers. A "move" is
    addProject + removeProject (add alone leaves the task in BOTH projects).
  • Project create needs a TEAM when the workspace is an organization.
  • Archive (PUT {archived:true}) ≠ delete. There is no /archive endpoint.
  • /tasks/search is PREMIUM (402 on free) → transparent typeahead fallback.
  • next_page lives OUTSIDE data → _paged() follows it.
  • 429 carries Retry-After; rejected calls still burn quota → we sleep + retry once.

NOTE: changing MCP tools needs a Railway redeploy (push to origin/jarvis-v2).
)annotationsN	ASANA_URLzhttps://app.asana.com/api/1.0/zdict[str, str]	_WS_CACHE_RECENTz&Asana isn't configured (no ASANA_PAT).z8name,due_on,due_at,completed,assignee.name,projects.namea  name,notes,completed,completed_at,due_on,due_at,start_on,assignee.name,projects.name,parent.name,tags.name,followers.name,num_subtasks,memberships.project.name,memberships.section.name,custom_fields.name,custom_fields.display_value,created_at,modified_at,permalink_urlzIname,archived,due_on,owner.name,current_status.title,current_status.colorc                   V ^8  d   QhRR/#    returnstr )formats   "H/Users/ahmed/devFolder/Ultron/claude-voice/jarvis-tools-service/asana.py__annotate__r   A   s     3 3c 3    c                 ^    \         P                  P                  R R4      P                  4       # )	ASANA_PAT )osenvirongetstripr   r   r   _patr   A   s     ::>>+r*0022r   c                   V ^8  d   QhRR/# r	   r
   boolr   )r   s   "r   r   r   E   s      D r   c                 (    \        \        4       4      # N)r   r   r   r   r   
configuredr   E   s    <r   c                  6   a  ] tR t^ItRtRR V 3R llltRtV ;t# )
AsanaErrorzIA short, speakable failure reason. `.code` = HTTP status (0 if not HTTP).c                    V ^8  d   QhRRRR/# )r	   msgr   codeintr   )r   s   "r   r   AsanaError.__annotate__L   s      C s r   c                	2   < \         SV `  V4       W n        R # r   )super__init__r#   )selfr"   r#   	__class__s   &&&r   r(   AsanaError.__init__L   s    	r   )r#   )    )__name__
__module____qualname____firstlineno____doc__r(   __static_attributes____classcell__)r*   s   @r   r    r    I   s    S  r   r    c               4    V ^8  d   QhRRRRRRRRRRR	R
RR
/# )r	   methodr   pathbodydict | Noneparamstimeoutr$   raw_enveloper   _retriedr   )r   s   "r   r   r   U   sA     =( =( =(C =({ =(=(.1=(FJ=(=(r   c                l   VP                  R4      '       g
   RV,           p\         V 2pV'       dk   VP                  4        UU	u/ uF  w  rV	R)9  g   K  WbK  	  p
pp	V
'       d7   TRV9   d   RMR\        P                  P                  V
4      ,           ,          pVe'   \        P                  ! RV/4      P                  4       MRp\        P                  P                  W{V P                  4       R7      pVP                  RR	\        4        24       VP                  R
R4       Ve   VP                  RR4        \        P                  P                  WR7      ;_uu_ 4       pVP                  4       pRRR4       X'       d   \        P$                  ! T4      M/ pT'       d   T# TP'                  RT4      # u up	pi   + '       g   i     LR; i  \        P                   P"                   Ed   pRp \        P$                  ! TP                  4       ;'       g    R4      pTP'                  R4      ;'       g    . pRP)                  R T 4       4      R,          pM  \*         d     Mi ; iTP,                  R8X  d   Rp \/        TP0                  P'                  R4      ;'       g    ^ 4      pM  \*         d    Rp Mi ; iT'       g<   T^8:  d5   \2        P4                  ! \7        TR4      4       \9        YY#YERR7      u Rp?# \;        R\=        T4      ;'       g    ^< R2R4      hTP,                  R8X  d   \;        RR4      hTP,                  R8X  d   \;        RR4      hTP,                  R8X  d    T'       d   R T 2MRp\;        R!T R"2R4      hTP,                  R#8X  d   \;        R$R#4      h\;        R%TP,                   R T;'       g    R& 2TP,                  4      hRp?i\        P                   P>                   d.   p\;        R'\A        TPB                  4      R(,           24      hRp?ii ; i)*u   One Asana REST call. Returns the unwrapped `data` payload (or the whole
envelope when raw_envelope=True, which is how _paged() sees `next_page`).
Raises AsanaError with a short human reason — callers turn that into a sentence.
r   Nr   ?&data)r@   r5   AuthorizationzBearer Acceptzapplication/jsonzContent-Type)r:   s   {}errors; c              3  X   "   T F   p\        VP                  R R4      4      x  K"  	  R# 5i)messager   Nr   r   .0xs   & r   	<genexpr>_api.<locals>.<genexpr>q   s#     G$Qs155B#788$s   (*N   Ni  g        zRetry-Afterg      ?T)r<   u1   Asana is rate-limiting us (429) — try again in z	 seconds.i  u3   Asana rejected the token (401) — check ASANA_PAT.  uG   that needs a paid Asana plan (402) — it's not on this free workspace.  : z'Asana says no permission for that (403).i  z$Asana couldn't find that item (404).zAsana error zrequest failedzcouldn't reach Asana: Nx   N)Nr   )"
startswith_BASEitemsurllibparse	urlencodejsondumpsencoderequestRequestupper
add_headerr   urlopenreaderror	HTTPErrorloadsr   join	Exceptionr#   floatheaderstimesleepmax_apir    r$   URLErrorr   reason)r5   r6   r7   r9   r:   r;   r<   urlkvcleanr@   reqrrawedetailerrerrswaitwhypayloads   &&&&&&&               r   rn   rn   U   sw    ??3TzGD6
C"(,,.H.$!AZ4G.H3$;CC6<<3I3I%3PPPC262B4::vtn%,,.D
..
 
 
 
GCNN?gdfX$67NN8/0~'9:%I^^##C#99Q&&(C :J "%djjo"G;;vw''e I :9<<!!  X	**QVVX../C778$**DYYG$GGMF 		66S=DQYY]]=9>>Q? 


3tS>*F$%)+ +Ct9??#9./24 4 66S=RTWXX66S=!# # 66S=#)Bvh-rCFse1MsSS66S=CSII<xr&2L2L<L1MNPQPVPVWW<<   I1#ahh-2E1FGHHIs   F;F;>(G &G7G G	G G  P34O$7AI ="I O$ I.+O$-I..O$&J4,J43O$4KO$K
O$5O$P3
O$"AO$ AO$O$$ P3P3(P..P3c               (    V ^8  d   QhRRRRRRRR/# )	r	   r6   r   r9   dictcapr$   r
   
list[dict]r   )r   s   "r   r   r      s(       d  z r   c                   . p\        V4      pVP                  R^d4       \        ^
4       F  p\        RWRR7      ;'       g    / pVP	                  R4      ;'       g    . pW7,          pVP	                  R4      ;'       g    / p\        V\         4      '       d   VP	                  R4      MRp	V	'       d   \        V4      V8  d    MWR&   K  	  VRV # )	zJGET a collection, following `next_page.offset` (which lives OUTSIDE data).limitGETT)r9   r;   r@   	next_pageoffsetN)r   
setdefaultrangern   r   
isinstancelen)
r6   r9   r   outp_envrowsnxtr   s
   &&&       r   _pagedr      s    CVALL#2Y5$t<BBwwv$$"ggk"((b&0d&;&;"SS(  t9r   c               $    V ^8  d   QhRRRRRR/# )r	   toolr   rx   rh   r
   r   )r   s   "r   r   r      s!     , , ,	 ,c ,r   c                0    V  R \        V4      R,           2# )z	 failed: rM   )r   )r   rx   s   &&r   _failr      s    V9SVD\N++r   c                   V ^8  d   QhRR/# )r	   r
   z	list[str]r   )r   s   "r   r   r      s     = =) =r   c                8   V '       g   . # \        V \        \        34      '       d   \        V 4      pM\        V 4      P	                  R4      pV Uu. uF=  p\        V4      P                  4       '       g   K$  \        V4      P                  4       NK?  	  up# u upi )u!   'a, b' | ['a','b'] → ['a','b'].,)r   listtupler   splitr   )valrW   rJ   s   &  r   _listr      sf    	#e}%%S	Cs#$)<EqSV\\^NCFLLNE<<<s   !B7Bc                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r      s      $ r   c                    \        V \        4      '       d"   V P                  4       P                  4       R9   # \	        V 4      # )1)r   trueyesydonecomplete)r   r   r   lowerr   )rs   s   &r   _truthyr      s1    !Swwy $QQQ7Nr   c                   V ^8  d   QhRR/# )r	   r
   r   r   )r   s   "r   r   r      s      $ r   c                   aa \         P                  R4      '       d   \        \         4      # \        P                  P                  RR4      P                  4       o\        RRRRR^d/R	7      ;'       g    . p V '       g   \        R
4      hRpSP                  4       '       d   \        V3R lV  4       R4      pMNS'       dG   SP                  4       o\        V3R lV  4       R4      pT;'       g    \        V3R lV  4       R4      pT;'       g
    V ^ ,          p\        VP                  R4      4      \         R&   \        VP                  RR4      4      \         R&   VP                  R4      '       d   RMR\         R&   \        \         4      # )zA{'gid','name','is_org'} for the workspace we operate in (cached).gidASANA_WORKSPACEr   r   /workspaces
opt_fieldsname,is_organizationr   r9   z%that Asana account has no workspaces.Nc              3  j   <"   T F(  p\        VP                  R 4      4      S8X  g   K$  Vx  K*  	  R# 5i)r   NrG   )rI   wwants   & r   rK   _ws_info.<locals>.<genexpr>   s'     D1#aeeEl*;t*CQQs   !3
3c              3     <"   T F7  p\        VP                  R R4      4      P                  4       S8X  g   K3  Vx  K9  	  R# 5inamer   Nr   r   r   rI   r   lows   & r   rK   r      s9      ?1AEE&"-.446#= Q
   0A
Ac              3     <"   T F7  pS\        VP                  R R4      4      P                  4       9   g   K3  Vx  K9  	  R# 5ir   r   r   s   & r   rK   r      s:      G1 #s155+<'='C'C'E E Qr   r   is_organizationr   is_org)r   r   r   r   r   r   rn   r    isdigitnextr   r   )spacespickr   r   s     @@r   _ws_infor      sR   }}UI::>>+R0668D%&(>MO U URT @AAD||~~DDdK	jjl ? ?@DF N Nt G GHLN6!9D488E?+IeDHHVR01If!%*;!<!<#"Ih	?r   c                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r      s      C r   c                 $    \        4       R,          # )z,Gid of the workspace we operate in (cached).r   )r   r   r   r   
_workspacer      s    :er   c               $    V ^8  d   QhRRRRRR/# )r	   rW   r   refr   r
   r8   r   )r   s   "r   r   r      s&     I I* I3 I; Ir   c                   a VP                  4       o\        V3R lV  4       R4      pT;'       g    \        V3R lV  4       R4      # )z;Exact (case-insensitive) name match, then a contains-match.c              3     <"   T F7  p\        VP                  R R4      4      P                  4       S8X  g   K3  Vx  K9  	  R# 5ir   r   rI   ir   s   & r   rK   _match.<locals>.<genexpr>   s3     J5aCfb(9$:$@$@$Bc$I5r   Nc              3     <"   T F7  pS\        VP                  R R4      4      P                  4       9   g   K3  Vx  K9  	  R# 5ir   r   r   s   & r   rK   r      s:      B5a#aeeFB&7"8">">"@@ 5r   )r   r   )rW   r   hitr   s   && @r   _matchr      sN    
))+C
J5JD
QC I I$ B5 BCGI Ir   c               $    V ^8  d   QhRRRRRR/# )r	   r   r   archivedzbool | Noner
   
str | Noner   )r   s   "r   r   r      s!     , ,c ,[ ,Z ,r   c           	        \        T ;'       g    R4      P                  4       p V '       g   R# V P                  4       '       d   V # R\        4       RRR^d/pVe   V'       d   RMRVR	&   \	        R
V4      p\        W04      pV'       g,   VRJ d&   \	        R
R\        4       RRR^d/4      p\        W04      pV'       d   \        VR,          4      # R# )u;   Resolve a project name (or gid) → gid. None if not found.r   N	workspacer   r   r   r   falser   	/projectsFr   )r   r   r   r   r   r   )r   r   r9   projsr   s   &&   r   _project_gidr      s    
ciiR.


 C
{{}}
|VWcRF'/VWz;'E

C8u${[*,%167C%I JU !3s5z?+t+r   c                    V ^8  d   QhRRRR/# r	   r   r   r
   r   r   )r   s   "r   r   r      s     ! !3 !: !r   c                   \        T ;'       g    R4      P                  4       p V '       g   R# V P                  4       '       d   V # \        P	                  V P                  4       4      pV'       d   V# \        4       p\        RRV R2RRRV R	^R
R/R7      ;'       g    . p\        W04      pV'       g0   \        RRV R2RRRRR	^dR
R/R7      ;'       g    . p\        WP4      pT;'       g    V'       d
   V^ ,          MRpV'       d:   \        VR,          4      \        V P                  4       &   \        VR,          4      # R# )u  Resolve a task name (or gid) → gid. None if not found.

Three tiers, because Asana's typeahead TEXT index lags several minutes
(verified against the live API): a task created 2 seconds ago is invisible to
`typeahead?query=…`, which would break "create it, now comment on it".
  1. gids and tasks we created in this process (instant),
  2. typeahead by name (the normal path for anything older),
  3. typeahead with an EMPTY query = the workspace's most-RECENT tasks, which
     is not text-indexed and therefore does see brand-new tasks.
r   Nr   /workspaces/
/typeaheadresource_typetaskquerycountr   r   r   r   )	r   r   r   r   r   r   r   rn   r   )r   cachedwshitsr   freshs   &     r   	_task_gidr      s.    ciiR.


 C
{{}}
[[%F	BbT4#wf>  % %"$ 	 
CUl2$j9VWb'3&C" # ) )&( 	 U 

,
,T$q'tC
"3u:		3u:r   c               $    V ^8  d   QhRRRRRR/# )r	   project_gidr   r   r
   r   r   )r   s   "r   r   r     s!     
, 
,c 
, 
,
 
,r   c                *   \        T;'       g    R4      P                  4       pV'       d	   V '       g   R# VP                  4       '       d   V# \        RRV  R2RRR^d/R	7      ;'       g    . p\	        W!4      pV'       d   \        VR
,          4      # R# )uA   Resolve a section (board column) name → gid inside one project.r   Nr   
/projects/z	/sectionsr   r   r   r   r   )r   r   r   rn   r   )r   r   secsr   s   &&  r   _section_gidr     s    
ciiR.


 Ck
{{}}
K=	:$fgs;= C C@B 	

C!3s5z?+t+r   c                    V ^8  d   QhRRRR/# r   r   )r   s   "r   r   r   )  s     	, 	,3 	,: 	,r   c                *   \        T ;'       g    R 4      P                  4       p V '       g   R# V P                  4       '       d   V # \        RR\	        4        R2RRR^d/R7      ;'       g    . p\        W4      pV'       d   \        VR	,          4      # R# )
r   Nr   r   /teamsr   r   r   r   r   )r   r   r   rn   r   r   )r   teamsr   s   &  r   	_team_gidr   )  s    
ciiR.


 C
{{}}
,z|nF;%vw<> D DAC 


C!3s5z?+t+r   c               $    V ^8  d   QhRRRRRR/# )r	   r   r   creater   r
   r   r   )r   s   "r   r   r   5  s!      # t 
 r   c                   \        T ;'       g    R 4      P                  4       p V '       g   R# V P                  4       '       d   V # \        R\	        4        R2RR/4      p\        W 4      pV'       d   \        VR,          4      # V'       d]   \        RR\	        4        R2RV /RR/R7      ;'       g    / pVP                  R4      '       d   \        VP                  R4      4      # R# R# )	r   Nr   z/tagsr   r   r   POSTr   )r   r   r   r   r   r   rn   r   )r   r   tagsr   mades   &&   r   _tag_gidr   5  s    
ciiR.


 C
{{}}
Le4|V6LMD

C
3u:Fl:<.>SM<*@B H HEG 	'+xxs488E?#@D@r   c                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r   F  s       r   c                 (   \         P                  R4      '       d   \         R,          # \        RRRR/R7      ;'       g    / p \        V P                  RR4      4      \         R&   \        V P                  RR4      4      \         R	&   \         R,          # )
uK   My own user gid (cached) — needed to filter 'assigned to me' client-side.mer   z	/users/mer   r   r   r   r   me_name)r   r   rn   r   )r   s    r   _me_gidr   F  ss    }}T	e[,)?	@	F	FBB"&&+,IdOrvvfb12IiT?r   c                    V ^8  d   QhRRRR/# )r	   r   r   r
   r   )r   s   "r   r   r   P  s      s s r   c                   \        T ;'       g    R4      P                  4       p V '       d   V P                  4       R9   d   R# V P                  4       '       g   RV 9   d   V #  \	        RR\        4        R2RRR	V R
^
RR/R7      ;'       g    . p\        W4      pV'       d   \        VR,          4      #  V #   \         d     T # i ; i)u   A user reference Asana accepts anywhere a user is expected.

Asana takes a gid, an EMAIL, or the literal 'me' interchangeably — so there
is no email→gid lookup to build. Only a spoken NAME ('Sara') needs resolving.
r   r   @r   r   r   r   userr   r   r   z
name,emailr   r   r   myselfr   ahmedmine)r   r   r   r   rn   r   r   rh   )r   r   r   s   &  r   _userr  P  s     ciiR.


 C#))+!GG
{{}}s

E\*,zBVWc7B,L( ) / /,. 	 Ts5z?"  J  Js   #(B7 'B7 7CCc                    V ^8  d   QhRRRR/# r	   tr   r
   r   r   )r   s   "r   r   r   k  s     % %D %S %r   c                    V P                  R 4      ;'       g#    V P                  R4      ;'       g    RR,          pV'       d   RV 2# R# )due_ondue_atr   N
   N, due )r   )r
  ds   & r   _duer  k  s?    	h77AEE(O11r37AVA3<$"$r   c                    V ^8  d   QhRRRR/# r	  r   )r   s   "r   r   r   p  s     ( (D (S (r   c                    V P                  R 4      ;'       g    / p\        V\        4      '       d   VP                  R4      MRpV'       d   RV R2# R# )assigneer   N []r   )r   r   r   )r
  ar   s   &  r   _whor  p  sH    	jRA&q$//155=TDRvQ<'R'r   c                    V ^8  d   QhRRRR/# r	  r   )r   s   "r   r   r   v  s     F F$ F3 Fr   c                    V P                  R 4      '       d   RMRpRV P                  RR4       \        V 4       \        V 4       V 2# )	completed (done)r   - r   
(untitled))r   r  r  )r
  r   s   & r   
_task_liner   v  sD    k**9Dfl+,T!WId1gYtfEEr   c                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r   {  s     ? ?3 ?r   c                F    R P                  R T ;'       g    .  4       4      # ), c              3     "   T F[  p\        V\        4      '       g   K  VP                  R 4      '       g   K4  \        T;'       g    / P                  R R4      4      x  K]  	  R# 5ir   )r   r   r   r   rH   s   & r   rK   _names.<locals>.<genexpr>|  sJ      ?i"1d+ 401f 4S!''rvr233is   A%A%A%A%)rg   )seqs   &r   _namesr'  {  s'    99 ?ciiRi ? ? ?r   c                   V ^8  d   QhRR/# r   r   )r   s   "r   r   r     s     , ,C ,r   c                    \        4       '       g   \        #  \        R RRR/R7      ;'       g    . p V '       g   R# \        4       p. pV  Fi  pVP	                  R4      '       d   RMRp\        VP	                  R	4      4      V8X  d   R
MRpVP                  RVP	                  R4       RV V 24       Kk  	  \        V 4       R2RP                  V4      ,           #   \         d   p\        RT4      u Rp?# Rp?ii ; i)r   r   r   r   r   z%That Asana account has no workspaces.r   organizationzpersonal workspacer   z
 (default)r   r  r       — z Asana workspace(s):

asana_workspacesN)r   _UNCONFIGUREDrn   r   r   r   appendr   rg   rh   r   )r   defaultlinesr   kindtagrx   s          r   
workspacesr4    s    <<,e]*,BCE K KHJ 	:,A%&UU+<%=%=>CWD"%aeeEl"3w">,BCLL2aeeFm_E$u=>  f+45		%8HHH ,'++,s(   C 
C BC C<&C71C<7C<c               $    V ^8  d   QhRRRRRR/# )r	   r   r   r   r   r
   r   )r   s   "r   r   r     s!     * *C * * *r   c           
     x   \        4       '       g   \        #  \        R R\        4       R\	        V4      '       d   RMRR\
        /4      p\        T ;'       g    R4      P                  4       P                  4       pV'       dA   V Uu. uF4  qC\        VP                  RR4      4      P                  4       9   g   K2  VNK6  	  ppV'       g   V'       d   RV : R	2# R
# . pV F  pVP                  R4      ;'       g    / p\        V\        4      '       d   VP                  R4      MRp\        VP                  R4      \        4      '       d+   VP                  R4      ;'       g    / P                  R4      MRpTP                  RVP                  R4       2\        V4      ,           V'       d   RV R2MR,           V'       d   RV 2MR,           4       K  	  \	        V4      '       d   RMRp	\        V4       RV	 R2RP                  V4      ,           # u upi   \          d   p
\#        RT
4      u Rp
?
# Rp
?
ii ; i)r   r   r   r   r   r   r   r   zNo Asana projects matching rR   z(No projects in that Asana workspace yet.current_statustitleNownerr  r  r  r+  zarchived Asana project(s)zAsana project(s) :
r,  asana_projects)r   r.  r   r   r   
_F_PROJECTr   r   r   r   r   r   r/  r  r   rg   rh   r   )r   r   r   qr   r1  str8  r9  labelrx   s   &&         r   projectsrA    s   <<*{'("3"3*%& ' ""$**, %M1c!%%2C.D.J.J.L)LQQEM@A1%!< ECEA'(..BB'1"d';';BFF7OE:Dg;& ;&QUU7^))r..v6+/ LL2aeeFm_-Q7-2bqM</4eE7O"> ?  07x/@/@+# 	e*QugS)DIIe,<<< N   *%q))*sn   >H *H H /H:H 	H 
H H H H :A(H #AH AH H H9#H4.H94H9c               0    V ^8  d   QhRRRRRRRRRRRR/# )r	   r   r   teamnotesr  privacyr
   r   )r   s   "r   r   r     s8     '0 '0 '0C '0S '0s '0'0),'0r   c                   \        4       '       g   \        # \        T ;'       g    R4      P                  4       p V '       g   R#  \	        4       pRV /pV'       d   \        V4      VR&   V'       d$   \        V4      P                  4       R,          VR&   V'       d   \        V4      P                  4       VR&   V'       d   \        V4      MRpV'       d   V'       g   R	V: R
2# V'       d!   \        RRV R2VRR/R7      ;'       g    / pMVP                  R4      '       dd   \        RRVR,           R2RRR^d/R7      ;'       g    . p	RP                  R V	R,           4       4      p
RV
'       d   RV
 R2,           # R,           # VR,          VR&   \        RRVRR/R7      ;'       g    / pV'       d   RV 2MRpRVP                  RV 4      : V R VP                  R4       R2#   \         d   p\        R!T4      u Rp?# Rp?ii ; i)"zgCreate a project. In an ORGANIZATION a team is required (Asana rule);
in a personal workspace it isn't.r   z1asana_project_create failed: need a project name.r   rD  r  r  privacy_settingNzNo Asana team called uU    — say which team it belongs to (asana_api GET /workspaces/{gid}/teams lists them).r   z/teams/r   r   zname,permalink_urlr   r   r   r   r   r   r   r#  c              3  V   "   T F  p\        VP                  R 4      4      x  K!  	  R# 5ir   NrG   )rI   r
  s   & r   rK   !project_create.<locals>.<genexpr>  s       HZQUU6]!3!3Z   ')uf   That Asana workspace is an organization, so a new project must belong to a team — tell me which teamz (available: ).rR   r   z	 in team zCreated Asana project  (id asana_project_create)r   r.  r   r   r   r   rn   r   rg   rh   r   )r   rC  rD  r  rE  r   r7   tidr   r   havewhererx   s   &&&&&        r   project_createrR    s    <<tzzr?  "DB0Zd^JDM [..05DN&)'l&8&8&:D"#!%io4+D8 4M M NvY7+-ABD J JGI  vvhUl2e9+V$D%167C$HJ P PMO yy HU3Z HHG7;]4&3F GADF G !#5	Dv{D+-ABD J JGI &))D6"r'(='@uSWWUZ^L\\^__ 0+Q//0sx   +G' 1*G' #G'  G' G' G' $G' ,G' G' ##G' *G' 2G' >G' #G' *
G' 51G' 'H2H=HHc               0    V ^8  d   QhRRRRRRRRRRRR/# )r	   projectr   r   rD  r  r9  r
   r   )r   s   "r   r   r     s8      0  0C  0s  0  0#  0 058 0r   c                   \        4       '       g   \        #  \        V RR7      pV'       g   RV : R2# / pV'       d   \        V4      P	                  4       VR&   V'       d   \        V4      VR&   V'       d$   \        V4      P	                  4       R,          VR&   V'       d   \        V4      VR	&   Ve   \        V4      VR
&   V'       g   R# \        RRV 2VRR/R7      ;'       g    / pVP                  RV 4      p	R
V9   d   VR
,          '       d   RMR RV	: R2# RV	: RRP                  VP                  4       4       R2#   \         d   p
\        RT
4      u Rp
?
# Rp
?
ii ; i)u   Rename / re-note / re-date / re-own a project, or ARCHIVE it
(archive=True → PUT {archived:true}; reversible — this is NOT a delete).Nr   No Asana project matching rR   r   rD  r  r  r9  r   uI   Nothing to change — give a name, notes, due_on, owner, or archive=true.PUTr   r   zname,archivedr   ArchivedzUn-archivedz the Asana project u.   . (Archive is reversible — nothing deleted.)zUpdated Asana project  (r#  rL  asana_project_update)r   r.  r   r   r   r  r   rn   r   rg   keysrh   r   )rT  r   rD  r  r9  archivepidr7   r   pnamerx   s   &&&&&&     r   project_updater`    sc    <<07T2/{!<<t9??,DLJDM [..05DN!%LDM&w/D$5Jse,d'9; A A>@ 	(%)*%5%5z=I J$i'UW X'y499TYY[3I2J"MM 0+Q//0sQ   E 	E #E "E 8*E #'E E (*E 
E &E E&E!E&!E&c          
     ,    V ^8  d   QhRRRRRRRRRR/# )r	   rT  r   addremoveaccess_levelr
   r   )r   s   "r   r   r     s1     &1 &1S &1s &1 &1"%&1/2&1r   c                b   \        4       '       g   \        #  \        V RR7      pV'       g   RV : R2# \        V4       Uu. uF  p\	        V4      NK  	  pp\        V4       Uu. uF  p\	        V4      NK  	  pp. pV'       d   V'       dU   V F-  p	\        RRRVRV	R	\        V4      P                  4       /4       K/  	  VP                  R
\        V4       RV 24       M6\        RRV R2RV/4       VP                  R
RP                  V4       24       V'       d7   \        RRV R2RV/4       VP                  RRP                  V4       24       \        RRV R2RRR^d/R7      ;'       g    . p
. pV
 F  pVP                  R4      ;'       g    / pVP                  R	4      pTP                  RVP                  R4      ;'       g    VP                  R4      ;'       g    R 2V'       d   RV R2MR,           4       K  	  V'       d7   R \        V 4      ,           R!,           R"P                  V4      ,           R,           M\        V4       R#V  R$2pY'       d   R%R%P                  V4      ,           ,           # R,           # u upi u upi   \         d   p\        R&T4      u Rp?# Rp?ii ; i)'zList / add / remove the people on a project ("invite people to a project").
add / remove take gids, EMAILS, or 'me' (comma-separated).NrV  rW  rR   r   z/membershipsparentmemberrd  zadded z as r   z/addMembersmembersr#  z/removeMemberszremoved r   z/project_membershipsr   z!user.name,user.email,access_levelr   r   r  r  r   emailr>   rZ  )r   zProject rQ   rD   z member(s) on :r,  asana_project_members)r   r.  r   r   r  rn   r   r   r/  r   rg   r   rh   r   )rT  rb  rc  rd  r^  rJ   addsremssaidmr   whorv   ulvlheadrx   s   &&&&             r   project_membersru    s    << 17T2/{!<<"'*-*Qa*-"'-0-Qa-0A"C1(#l*;*A*A*CEF  fSYKtL>BCVz#k:Y<MNfTYYt_$567:cU.9It;LMKK(499T?"345EZu,@A=wKM N T TQS 	Af##A%%'CJJAEE&MBBQUU7^BBsCD),Bse1+"6 7 
 NR
S\)D0499T?BSHH:^G9A6 	tdiin,<<<<5 .04  1,a001sy   J J JJ $J8J BJ AJ 1!J 7J J $J 0J A,J ;J 
J J.J)#J.)J.c          
     ,    V ^8  d   QhRRRRRRRRRR/# )r	   rT  r   textstatus_typer8  r
   r   )r   s   "r   r   r   +  s1     0 0C 0s 0 00'*0r   c                r   \        4       '       g   \        # \        T;'       g    R4      P                  4       pV'       g   R#  \	        V RR7      pV'       g   RV : R2# \        T;'       g    R4      P                  4       P                  4       P                  RR	4      pRpWV9  d   R
RP                  V4       R2# RVRVRV/pV'       d   \        V4      P                  4       VR&   \        RRVRR/R7       RVP                  R	R4       RV  R2#   \         d   p\        RT4      u Rp?# Rp?ii ; i)zSPost a project status update (on_track / at_risk / off_track / on_hold /
complete).r   z2asana_project_status failed: need the status text.NrV  rW  rR   on_trackr:  r   zstatus_type must be one of: r#  rf  rw  rx  r8  r   z/status_updatesr   r   z	Posted a z status update on asana_project_status)rz  at_risk	off_trackon_holdr   )r   r.  r   r   r   r   replacerg   rn   rh   r   )	rT  rw  rx  r8  r^  r?  okr7   rx   s	   &&&&     r   project_statusr  +  s#    <<tzzr?  "DC07T2/{!<<**
+11399;CCCMH<1$))B-BBVT="EJ,,.DMV&lG5LM2::c3/00B7)1MM 0+Q//0s8   D !D /AD ?D AD D6 D1+D61D6c                    V ^8  d   QhRRRR/# r	   r   r   r
   r   )r   s   "r   r   r   I  s     E& E&s E&s E&r   c           	        \        4       '       g   \        #  \        V 4      pV'       g   RV : R2#  \        RRV 2R\        R,           /R7      ;'       g    / pVP                  RR	4       VP                  R
4      '       d   RMR,           .pVP                  R4      ;'       g    / P                  R4      pV'       d   VP                  RV 24       VP                  R4      '       g   VP                  R4      '       dN   TP                  RVP                  R4      ;'       g#    VP                  R4      ;'       g    RR,           24       VP                  R4      '       d   VP                  RVR,           24       \        VP                  R4      4      pV'       d   VP                  RV 24       VP                  R4      ;'       g    .  F  pVP                  R4      ;'       g    / P                  R4      pVP                  R4      ;'       g    / P                  R4      pV'       g   Ka  TP                  RV 2V'       d   RV R2MR,           4       K  	  VP                  R4      ;'       g    / P                  R4      p	V	'       d   VP                  RV	 24       \        VP                  R4      4      p
V
'       d   VP                  R V
 24       \        VP                  R!4      4      pV'       d   VP                  R"V 24       \        VP                  R#4      4      pV'       d   VP                  R$V 24       VP                  R%4      ;'       g    .  FC  pVP                  R&4      pV'       g   K  VP                  VP                  R4       R'V 24       KE  	  VP                  R(4      '       di   \        VR(,          4      P                  4       P                  R)R*4      pTP                  R+VR,,          \        V4      R-8  d   R.MR,           ,           4       \        RRV R/2RR0R1^2/R7      ;'       g    . pV'       dq   VP                  R2\        V4       R324       TVR4,           Uu. uF7  pR5VP                  R4       2VP                  R
4      '       d   R6MR,           NK9  	  up,          p\        RRV R72RR8R1^d/R7      ;'       g    . pV Uu. uF  pVP                  R94      R:8X  g   K  VNK  	  ppV'       d   VP                  R;\        V4       R<24       VRFR=  F  pVP                  R>4      ;'       g    / P                  RR?4      p\        VP                  R@R4      4      R4,          p\        VP                  RAR4      4      P                  4       P                  R)R*4      RB,          pVP                  R5V RCV RDV 24       K  	  R)P                  V4      #   \
         d&    \        RRT 2R\        /R7      ;'       g    / p ELi ; iu upi u upi   \         d   p\        RET4      u R=p?# R=p?ii ; i)GzrEverything about ONE task: notes, assignee, due, section, tags, subtasks,
custom fields and the last few comments.No Asana task matching rR   r   /tasks/r   z,dependencies.namer   r   r  r  u	    — DONEr   r  z
Assignee: r  r  zDue: :N   Nstart_onzStarts: rA  z	Project: membershipssectionrT  z	Section: z (in rj  rf  zSubtask of: r   zTags: 	followerszFollowers: dependencieszWaiting on: custom_fieldsdisplay_valuerQ   rD  r,  r:  zNotes: :Ni  Ni  u   …	/subtaskszname,completedr   z
Subtasks (z):r  z  - r  /storiesz0text,created_at,created_by.name,resource_subtyperesource_subtypecomment_addedz
Comments (z
), latest:N
created_bysomeone
created_atrw  :N   NrZ  z): 
asana_task)r   r.  r   rn   _F_TASK_FULLr    r   r/  r'  r   r   r  r   rg   rh   r   )r   rO  r
  r1  rq  projrp  secprparr   folldepscfr   r7   subssstoriescmtsbywhentxtrx   s   &                       r   r   r   I  s    <<@&o,THA66	XUgcUOl-AA5C D J JGI  EE&,/0#$55#5#5K2? @uuZ &&B++F3LL:cU+,55??aeeHooLL5x!P!PQUU8_5J5JC4P QRS55LL8AjM?34aeeJ'(LL9TF+,%%&,,",A55#))r..v6C%%	"((b--f5Bsy.2E"Q-2NO	 -
 uuX$$"))&1LL<u-.aeeFm$LL6$)aeeK()LL;tf-.aeeN+,LL<v./55)//R/B&&)Csv/r#78 0 55>>qz?((*224=DLLd4jSY_ERT&UVWEWSE3*GR=9 : @ @=? 	LL:c$i[34#Cy*(! QUU6]O,QUU;=O=O	UWXX(* *EuuH5LS?  ! !  	 #S7aaee,>&??&R7SLL:c$i[
;<"#YeeL)//R44VYG155r23C8!%%+,224<<T3GMtB4r$s3%89	 
 yyo  	XUgcUO\<4PQWWUWA	XT*
 T  &\1%%&s   X2 $W5 W5 (X2 "X2 'X2 *X2 ,X2 #X2 'X2  &X2 'A(X2 X2 .)X2 X2 4X2 -X2 9X2 CX2 !X2 ?X2 BX2 
X2 *X2 <.X(+X(9%X2 X2 &X-X-	AX2 B#X2 5(X%X%!X2 $X%%X2 2Y=YYYc               0    V ^8  d   QhRRRRRRRRRRR	R/# )
r	   rT  r   r  r   r  r  r   r$   r
   r   )r   s   "r   r   r     s9     1' 1'3 1'4 1' 1'1'*-1'7:1'r   c           	        \        4       '       g   \        #   \        ^\        \	        T;'       g    ^24      ^d4      4      p\        V4      pRpV '       d   \        V RR7      pV'       g   RV : R2# V'       da   V'       g   R# \        Wb4      pV'       g   RV: RV  R2# R\        R	^d/pV'       g   R
VR&   \        RV R2W^,          R7      p	RV RV  R2p
MV'       d4   RVR\        R	^d/pV'       g   R
VR&   \        RW^,          R7      p	RV  2p
MM\        V4      '       d;   RRR\        4       R\        R	^d/pV'       g   R
VR&   \        RW^,          R7      p	Rp
MR# V	 Uu. uF&  p\        VP                  R4      4      V8X  g   K$  VNK(  	  p	pV'       d   RMRpV	'       g
   RV RV
 R2# V	RV  Uu. uF  p\        V4      NK  	  pp\        V	4      V8  d   R\        V	4      V,
           R2MRp\        V	4       RV RV
 R2R P!                  V4      ,           V,           #   \
         d    ^2p ELi ; iu upi u upi   \
         d   p\#        R!T4      u Rp?# Rp?ii ; i)"   NrV  rW  rR   u>   Give a project too — a section only exists inside a project.No section called  in r   r   nowcompleted_since
/sections//tasks)r   ztask(s) in rZ  rj  rT  r  r   r   ztask(s) assigned to youz9Give a project name, or set mine=true for your own tasks.r  openzNo r:     
…and  more.r   r;  r,  asana_tasks)r   r.  rm   minr$   rh   r   r   r   _F_TASKr   r   r   r   r   r   rg   r   )rT  r  r  r  r   	want_doner^  sidr9   r   r@  r
  stater1  morerx   s   &&&&&           r   tasksr    sf   <<-'	3s5;;B/56E I&	w6C3G;a@@Ws,C+G;d7)1EE"GWc:F,1()Jse62F	JD!'"WIQ7ElGWcJF,1()(F	:D!'+ET]] $Z\"GWc;F,1()(F	:D-ENI4a4k(:#;y#H4I(fqq))(,Ve51A58;D	E8I3t9u,-V4rd)AeWAeWC0499U3CCdJJQ  	E	D J 6  ']A&&'s   )H( 0I 3I ;I I "I 4.I #I 7A*I #I '!H<H<I )I 2I 9IAI (H95I 8H99I I'I"I'"I'c               8    V ^8  d   QhRRRRRRRRRRRRRRR	R/# )
r	   r   r   rD  rT  r  r  r  rf  r
   r   )r   s   "r   r   r     sH     3- 3-c 3-# 3-S 3- 3-3-+.3-=@3-JM3-r   c           	         \        4       '       g   \        # \        T ;'       g    R 4      P                  4       p V '       g   R#  RV /pV'       d   \        V4      VR&   V'       d$   \        V4      P                  4       R,          VR&   V'       d   \	        V4      VR&   RpV'       d   \        VRR7      pV'       g   R	V: R
2# V'       d   \        V4      MRp	V'       d   V	'       g   RV: R2# V	'       dV   \        RRV	 R2VRR/R7      ;'       g    / p
\        V
P                  R4      4      pV'       d   \        RRV R2RV/4       MQV'       d   V.VR&   M\        4       VR&   \        RRVRR/R7      ;'       g    / p
\        V
P                  R4      4      pV\        V P                  4       &   V'       dA   V'       d9   \        W4      pV'       d   \        RRV R2RV/4       MRV : RV RV RV: R2	# V'       d   RV 2MR pV'       d   R V 2MR pV'       d   V'       d   R!V R"2MR pVP                  R4      '       d   R#VR,           2MR pR$V : V V V V RV R%2	#   \         d   p\        R&T4      u Rp?# Rp?ii ; i)'r   z+asana_task_create failed: need a task name.r   rD  r  r  r  NrV  rW  u    — task not created.r  u    — subtask not created.r   r  r  r   r   r   /addProjectrT  rA  r   r  r  /addTaskr   zCreated r  rM  u$   ) — but there's no section called z, so it's in the default one.z under rZ  rj  r  zCreated Asana task rL  asana_task_create)r   r.  r   r   r  r   r   rn   r   r   r   r   r   rh   r   )r   rD  rT  r  r  r  rf  r7   r^  r  r   rO  r  rQ  undercolr  rx   s   &&&&&&&           r   task_creater    sr   <<tzzr?  "D<,-d^JDM [..05DN$XDw6C3G;>TUU#)it#,VJ6OPPvY7+V46 < <9; cggen%CVwse;7)S9IJ$'5Z $.L[!vx|V6LMSSQSCcggen%C #

ss,CVz#h7&#G"4($wiuSE B**14QS T$+$wi &,'&""!(S7)1ob,0HHX,>,>X'(B$THUGC5vU3%rRR -(!,,-s   I, I, '*I, I, *I, I, I, #I, +I, 2I, :I, A I, +I, ;I, ?I, 7I, ?I, I, I, %I, -I, I, ,J7JJJc               4    V ^8  d   QhRRRRRRRRRRRRRR/# )	r	   r   r   r   rD  r  r  r  r
   r   )r   s   "r   r   r     s@     )- )-c )- )-# )-C )-)-.1)-JM)-r   c                   \        4       '       g   \        #  \        V 4      pV'       g   R V : R2# / pV'       d   \        V4      P	                  4       VR&   V'       d   \        V4      VR&   V'       d$   \        V4      P	                  4       R,          VR&   V'       d   \        V4      P	                  4       R,          VR&   RV9  dg   \        RRV 2R	R
/R7      ;'       g    / p	V	P                  R4      ;'       g#    V	P                  R4      ;'       g    RR,          p
V
'       g   R# WR&   V'       d   \        V4      VR&   Ve   \        V4      VR&   V'       g   R# \        RRV 2VR	R/R7      ;'       g    / pVP                  R4      '       dZ   \        P                  \        V 4      P	                  4       P                  4       R4       V\        VR,          P                  4       &   RVP                  RV 4      : RRP                  VP                  4       4       R2#   \         d   p\        RT4      u Rp?# Rp?ii ; i)r  rR   r   rD  r  r  r  r   r  r   zdue_on,due_atr   r  r   uS   Asana needs a due date before it will take a start date — give me due_on as well.r  Nr  uR   Nothing to update — give a name, notes, due_on, start_on, assignee, or complete.rX  zUpdated Asana task rZ  r#  rL  asana_task_update)r   r.  r   r   r   rn   r   r  r   r   popr   rg   r\  rh   r   )r   r   rD  r  r  r  r   rO  r7   currP  r   rx   s   &&&&&&&      r   task_updater    s   <<%-o,THA66t9??,DLJDM [..05DN"8}224S9D t#5GC5/#/"AC I IFH wwx(JJSWWX->-D-D"c,J:!%X$XD ' 1D-5GC5/4v8NOUUSU88FKKD	)//148,/GDL&&()$SWWVT%:$=R		$))+@V?WWYZZ -(!,,-sk   H( 	H( #H(  H( 6*H( !AH( %H(  H( H( -H( 9'H( #H( >H( BH( (I	3I>I	I	c          
     ,    V ^8  d   QhRRRRRRRRRR/# )r	   r   r   rT  r  remove_fromr
   r   )r   s   "r   r   r   '  s1     9+ 9+C 9+# 9+S 9+9+(+9+r   c           
        \        4       '       g   \        #  \        V 4      pV'       g   RV : R2# V'       g   V'       g   V'       g   R# \        RRV 2RR/R7      ;'       g    / pVP	                  R	V 4      pVP	                  R
4      ;'       g    .  Uu. uF  qwP	                  R4      '       g   K  VNK  	  pp. p	Rp
V'       d   \        VRR7      p
V
'       g   RV: R2# RV
/pV'       d#   \        W4      pV'       d   WR&   M
RV: RV R2# \        RRV R2V4       T	P                  RV 2V'       d   RV 2MR,           4       MV'       d   V'       g   R# V^ ,          p\        \        VR,          4      V4      pV'       g   RV: RVP	                  R	4       R2# \        RRV R2RV/4       V	P                  RV RVP	                  R	4       24       . pV'       d%   \        VRR7      pV'       g   RV: R2# RVR	V/.pM:V
'       d3   V Uu. uF&  p\        VP	                  R4      4      V
8w  g   K$  VNK(  	  ppV F&  p\        RRV R2R\        VR,          4      /4       K(  	  V'       d/   V	P                  RR P                  R! V 4       4      ,           4       V R"2R#P                  V	4      ,           R,           # u upi u upi   \         d   p\        R$T4      u Rp?# Rp?ii ; i)%zMove a task. A real move: adding it to a project REMOVES it from the ones it
was in (Asana's addProject alone would leave it in both).r  rR   u4   Say where to move it — a project and/or a section.r   r  r   zname,projects.namer   r   rA  r   NrV  rW  u    — nothing moved.rT  r  r  r  r   r  z	added to z / r   z4That task isn't in a project, so it has no sections.r  r  r   z	moved to z/removeProjectzremoved from r#  c              3  x   "   T F0  p\        VP                  R 4      ;'       g
    VR,          4      x  K2  	  R# 5i)r   r   NrG   rI   r   s   & r   rK   task_move.<locals>.<genexpr>\  s4      4C=A 58f8Q8Q54R4R=As   !::rQ   rD   asana_task_move)r   r.  r   rn   r   r   r   r/  r   rg   rh   r   )r   rT  r  r  rO  r  tnamer   wasro  r^  r7   r  hostdropridrx   s   &&&&             r   	task_mover  '  s    <<3+o,THA66w{I5GC5/')=>@ F FCE 	%77:.44"4F4Quqq4Fw6C3G;>QRR#S)D"30&)O/{$wiGZ[[73%{3T:KK)G9-G3wiQSTUMq6Ds4;/9C+G;d488F;K:LANN:cU(3fc]CKK)G9D&1A0BCD{T:C3K?!DDC56D"?s!c!%%,&73&>AAsD?A73%~6C%M8RS KK$)) 4C=A4C +C C D|diio-33Q GD @  +&**+s   K K K K K &,K K K
5K
;)K %K 1!K AK K AK %AK (K K K !K>K5K :AK 

K K5K0*K50K5c                    V ^8  d   QhRRRR/# r  r   )r   s   "r   r   r   c  s     / / / /r   c                   \        4       '       g   \        #  \        V 4      pV'       g   R V : R2# \        RRV 2RR/RR/R7      ;'       g    / pR	VP	                  RV 4      : R
2#   \
         d   p\        RT4      u Rp?# Rp?ii ; i)r  rR   rX  r  r  Tr   r   r   zMarked z complete in Asana.asana_task_completeN)r   r.  r   rn   r   rh   r   )r   rO  r   rx   s   &   r   task_completer  c  s    <</o,THA665GC5/K+>'02 8 857 	.11DEE /*A../s(   A) A) A) )B
4B?B
B
c                    V ^8  d   QhRRRR/# r  r   )r   s   "r   r   r   q  s     - -c -c -r   c                   \        4       '       g   \        #  \        V 4      pV'       g   RV : R2# \        RRV 2RR/R7      ;'       g    / pVP	                  RV 4      p\        RRV 24       \        V 4      P                  4       P                  4       \        V4      P                  4       P                  4       3 F  p\        P                  VR	4       K  	  R
V: R2#   \         d   p\        RT4      u R	p?# R	p?ii ; i)uR   Delete a task — it goes to Asana's Deleted Items and is recoverable for 30 days.r  rR   r   r  r   r   r   DELETENzDeleted the Asana task z8. It's in Deleted Items and can be restored for 30 days.asana_task_delete)r   r.  r   rn   r   r   r   r   r   r  rh   r   )r   rO  gotr  rr   rx   s   &     r   task_deleter  q  s    <<-o,THA665GC5/<2HIOOR%X'd)//#))+SZ-=-=-?-E-E-GHAKK4  I)% 3( ( 	) -(!,,-s)   C" C" BC" "D-C>8D>Dc               4    V ^8  d   QhRRRRRRRRRRRRRR/# )	r	   r   r   r  add_followersremove_followersadd_tag
remove_tagr
   r   )r   s   "r   r   r     sA     .- .-c .-S .-c .-"%.-58.-.-),.-r   c                <   \        4       '       g   \        #  \        V 4      pV'       g   RV : R2# . pV'       d0   \        RRV 2R\	        V4      /4       VP                  RV 24       \        V4       Uu. uF  p\	        V4      NK  	  p	pV	'       d7   \        RRV R2R	V	/4       VP                  R
RP                  V	4       24       \        V4       Uu. uF  p\	        V4      NK  	  p
pV
'       d7   \        RRV R2R	V
/4       VP                  RRP                  V
4       24       \        V4       FH  p\        VRR7      pV'       g
   RV: R2u # \        RRV R2RV/4       VP                  RV 24       KJ  	  \        V4       F?  p\        V4      pV'       g   K  \        RRV R2RV/4       VP                  RV 24       KA  	  \        RRV 2RR/R7      ;'       g    / pVP                  R4      ;'       g    / P                  R4      pV'       d   RV 2MR.p\        VP                  R	4      4      pV'       d   VP                  RV 24       \        VP                  R4      4      pV'       d   VP                  RV 24       V'       d    R R!P                  V4      ,           R",           MR#pVVP                  RV 4       R$2,           R%P                  V4      ,           # u upi u upi   \         d   p\        R&T4      u R'p?# R'p?ii ; i)(zLWho and what is on a task: assignee, followers, tags. No args = just report.r  rR   rX  r  r  zassigned to r   z/addFollowersr  zfollowing: +r#  z/removeFollowerszun-followed: T)r   z&Couldn't find or create the Asana tag z/addTagr3  ztagged z
/removeTagz	untagged r   r   z+name,assignee.name,followers.name,tags.namer   r   z	assignee 
unassignedz
followers r   ztags u	   Done — rD   z. r   rQ   u    · asana_task_peopleN)r   r.  r   rn   r  r/  r   rg   r   r   r'  rh   r   )r   r  r  r  r  r  rO  ro  rJ   rm  rn  r   r   r
  rq  bitsr  r   rt  rx   s   &&&&&&              r   task_peopler    s    <<(-o,THA66'#*eHo)FGKK,xj12"'"67"6Qa"6773%}5T7JKKK,tyy&789"'(8"9:"9Qa"9:73%'78;:MNKK-		$'89:'ND4-C?xqII73%w/%>KK'$() # *%D4.CsVwse:6Eiv./	 &
 '#G1I J P PMO 	
uuZ &&B++F3%()C5!l;aeeK()KK*TF+,aeeFm$KK%v'9=diio-42vt,-R006;;t3DDD= 8 ;6  -(!,,-so   K: 	K: <K: 8K0AK: K5-A+K: A	K: 'AK: ,K: K: !A;K: AK: 0
K: :LLLLc               $    V ^8  d   QhRRRRRR/# )r	   r   r   rb  r
   r   )r   s   "r   r   r     s!     * *3 *S *# *r   c                &   \        4       '       g   \        #  \        V 4      pV'       g   RV : R2# . p\        V4       Fs  p\	        RRV R2RV/RR/R7      ;'       g    / pVP                  R	4      '       d(   \        VR	,          4      \        VP                  4       &   VP                  V4       Ku  	  \	        R
RV R2RRR^d/R7      ;'       g    . pV'       d%   R\        V4       RV  RRP                  V4       R2MRpV'       g   T;'       g    V  R2# VR,           Uu. uF  p\        V4      NK  	  p	pV\        V4       RV  R2,           RP                  V	4      ,           # u upi   \         d   p
\        RT
4      u Rp
?
# Rp
?
ii ; i)zEList a task's subtasks, or create them (add = comma-separated names).r  rR   r   r  r  r   r   r   r   r   z#name,completed,due_on,assignee.namer   zAdded z subtask(s) to rQ   r#  z.
r   z has no subtasks.:N(   Nz subtask(s) on r;  r,  asana_subtasksN)r   r.  r   r   rn   r   r   r   r   r/  r   rg   r   rh   r   )r   rb  rO  r   nmsubr  rt  r  r1  rx   s   &&         r   subtasksr    s   <<*o,THA66*BvY7&"+V46 < <9; wwu~~&)#e*o
#KKO  EWSE3?#=O P V VSU 	  T?4&499T?:K3O 	55dV#455(,S	2	1A	2T?4&<<tyy?OOO 3 *%q))*s_   E/ -E/  E/ :AE/ 
E/ -E/ E/ E/ E/ &E*:/E/ *E/ /F:FFFc               $    V ^8  d   QhRRRRRR/# )r	   r   r   rw  r
   r   )r   s   "r   r   r     s!     ) )# )S )S )r   c                :   \        4       '       g   \        # \        T;'       g    R 4      P                  4       pV'       g   R#  \	        V 4      pV'       g   RV : R2# \        RRV R2RV/4       RV : R2#   \         d   p\        R	T4      u R
p?# R
p?ii ; i)r   z,asana_comment failed: need something to say.r  rR   r   r  r  rw  zCommented on Asana task asana_commentN)r   r.  r   r   r   rn   rh   r   )r   rw  rO  rx   s   &&  r   commentr    s    <<tzzr?  "D=)o,THA66Vwse8,vtn=)$33 )_a(()s$   A9 A9 9BBBBc               $    V ^8  d   QhRRRRRR/# )r	   r   r   r   r$   r
   r   )r   s   "r   r   r     s!     * *3 *s *C *r   c                &   \        4       '       g   \        #  \        V 4      pV'       g   RV : R2#  \        ^\	        \        T;'       g    ^
4      ^24      4      p\        RV R2RR/4      pV Uu. uF  qDP                  R4      R8X  g   K  VNK  	  ppV'       g   R	V  R2# . pWQ) R
  F  pVP                  R4      ;'       g    / P                  RR4      p\        VP                  RR4      4      R,          p\        VP                  RR4      4      P                  4       P                  RR4      p	VP                  R4      '       d   RMRp
VP                  RV RV V
 RV	R,           24       K  	  \        V4       RV  2pT\        V4      \        V4      8  d   R\        V4       R2MR,          pVR,           RP                  V4      ,           #   \         d    ^
p ELi ; iu upi   \         d   p\        RT4      u R
p?# R
p?ii ; i)zPRead a task's comment thread (system events like 'X assigned this' are skipped).r  rR   r  r  r   z:text,created_at,created_by.name,resource_subtype,is_pinnedr  r  zNo comments on Nr  r   r  r  r   r  rw  r,  r:  	is_pinnedz	 (pinned)r  r#  rQ   :Ni,  Nz comment(s) on z	, latest rk  asana_comments)r   r.  r   rm   r  r$   rh   r   r   r   r   r  r/  r   rg   r   )r   r   rO  r  r  r  r1  r  r  r  pinrt  rx   s   &&           r   commentsr    s   <<*o,THA66	3s5;;B/45E 73%x0V3X Y"S7aee,>&??&R7S$TF!,,fgA%%%++00CBquu\2./4DaeeFB'(..088sCC!"{!3!3+CLL2bTD6#bT<=  d)OD62SYU-C)CJ<q)Ld{TYYu---!  	E	 T  *%q))*s_   G/ )G G/ 2G*G*G/ $!G/ DG/ G'#G/ &G''G/ /H:HHHc               0    V ^8  d   QhRRRRRRRRRRRR/# )	r	   r   r   r^  r   rq  
due_before	due_afterr
   r   )r   s   "r   r   r     s@     @' @'C @'j @'s @' @' #@'36@'r   c                   \        T ;'       g    R4      P                  4       p\        4       p. pV'       d{   RVR\        R^d/p	VRJ d   RV	R&   \	        RV	4      pV'       dO   V U
u. uFB  qP                  4       \        V
P                  R	R4      4      P                  4       9   g   K@  V
NKD  	  pp
M\        R
RV R2RRRVR^dR\        /R7      ;'       g    . pV'       d   V U
u0 uF  p
\        V
P                  R4      4      kK  	  pp
\        \        P                  4       4       FU  w  rVP                  4       V9  g   W9   d   K"   \        R
RV 2R\        /R7      pT'       g   KD  TP                  T4       KW  	  V'       Ed   VP                  4       P                  4       pVR"9   dX   \        4       pV U
u. uF@  p
\        V
P                  R4      ;'       g    / P                  RR4      4      V8X  g   K>  V
NKB  	  pp
MV U
u. uF  p
T\        V
P                  R4      ;'       g    / P                  R	R4      4      P                  4       9   g<   T\        V
P                  R4      ;'       g    / P                  RR4      4      8X  g   K  V
NK  	  pp
V'       d:   V U
u. uF-  qP                  R4      '       g   K  V
R,          V8:  g   K+  V
NK/  	  pp
V'       d:   V U
u. uF-  qP                  R4      '       g   K  V
R,          V8  g   K+  V
NK/  	  pp
Ve3   V U
u. uF&  p
\        V
P                  R4      4      V8X  g   K$  V
NK(  	  pp
V'       gn   RpV'       g[   \        R
R\        4        R2RRRVR^RR	/R7      ;'       g    . pV'       d'   RRP                  R V 4       4      ,           R,           pRV : RV 2# VR,           U
u. uF  p
\!        V
4      NK  	  pp
\#        V4      ^8  d   R\#        V4      ^,
           R2MRp\#        V4       RV : R 2R!P                  V4      ,           V,           # u up
i u up
i   \         d     EK/  i ; iu up
i u up
i u up
i u up
i u up
i u up
i )#zZFree-tier path: typeahead (name-match only) or a project listing, then
filter client-side.r   rT  r   r   Fr  r  r  r   r   r   r   r   r   r   r   r   r   r  r  r  r  z
Matching projects: r#  c              3  V   "   T F  p\        VP                  R 4      4      x  K!  	  R# 5irI  rG   r  s   & r   rK   #_search_fallback.<locals>.<genexpr>A  s      $GASv%7%7rK  rR   Nothing in Asana matches N   Nr  r   Asana task(s) matching z6 (free-tier search: matches names, not descriptions):
r,  r  )r   r   r   r  r   r   r   rn   r   r   rW   r    r/  r   r   rg   r   r   )r   r^  rq  r  r  r  r>  r   r   r9   r
  seenr  r   r   r   r  extrar   r1  r  s   &&&&&&               r   _search_fallbackr    s&    	EKKR A	BD
S,#F(-F$%h'#St!wwyCfb8I4J4P4P4R'RAAtDSE\"Z8VWa#'B# $ * *') 	 /34t!Ce%tD40779B&#+ '#)5w(?AE 5KK& 1 siik!889D# Nt!AEE*-3388CDL AtD ND  $ Mt!c155#4#:#:"?"?"KLRRTTc155#4#:#:"?"?r"JKK AtD M Q4a55?q{j7P4QP4a55?q{i7O4PI4a4k(:#;y#H4I,z|nJ GGQfQ& ' - -*,  099$G$GGHJMN*5)1UG<<$(I.IqZ]IE.14TRYs4y2~&f-RD4yk1% ;E Eii!%& 'c T 5 " NM RPI /s   '=P>)P>&#QQ=!QQ=Q
"Q -AQ 4Q Q %Q%Q%Q%&Q*Q*Q*#!Q/	Q/Q4QQc               0    V ^8  d   QhRRRRRRRRRRRR/# )r	   r   r   rT  r  r  r  r
   r   )r   s   "r   r   r   J  s9     1( 1(# 1(S 1( 1(1(,/1(IL1(r   c                Z   \        4       '       g   \        # \        T ;'       g    R4      P                  4       p V '       g#   V'       g   V'       g   V'       g   V'       g   R#  Vf   RM
\	        V4      pRpV'       d   \        VRR7      pV'       g   RV: R2# V'       d   \        V4      MRpR\        R^dR	R
/p	V '       d   W	R&   V'       d   WyR&   V'       d   WR&   V'       d$   \        V4      P                  4       R,          V	R&   V'       d$   \        V4      P                  4       R,          V	R&   Ve   V'       d   RMRV	R&    \        RR\        4        R2V	R7      ;'       g    . p
T
'       g   RT : R2# T
R,           Uu. uF  p\        T4      NK  	  pp\        T
4      ^8  d   R\        T
4      ^,
           R2MRp\        T
4       RT : R2RP                  T4      ,           T,           #   \         d   pTP                  R 9   dj   \        Y\        T;'       g    R4      P                  4       \        T;'       g    R4      R,          \        T;'       g    R4      R,          T4      u Rp?# h Rp?ii ; iu upi   \          d   p\#        RT4      u Rp?# Rp?ii ; i)!u   Find tasks. Uses Asana's premium search when the plan has it, else falls back
to typeahead. NOTE: the search index lags 10-60s, so a task created seconds ago
may not show — read it with asana_task instead.r   z;asana_search failed: give a phrase, a project, or a filter.NrV  rW  rR   r   r   sort_bymodified_atrw  zprojects.anyzassignee.anyr  zdue_on.beforezdue_on.afterr   r   r  r   r   z/tasks/searchr   r  r  r  r  r  r;  r,  asana_search)rO   rP   )r   r.  r   r   r   r   r  r  rn   r   r    r#   r  r   r   rg   rh   r   )r   rT  r  r  r  r  r  r^  rq  r9   r   rx   r
  r1  r  s   &&&&&&         r   searchr  J  sW   
 <<""$E*YL'(%-D793E	w6C3G;a@@!)eHor$gw!=2"6N%(>"%(>"&)*o&;&;&=c&BF?#%(^%9%9%;C%@F>" ,5&7F;		jl^=I%' - -*,  .uiq99(,S	2	1A	258Y^3t9r>*&1t9+5eYcB))E"#%)* 	+  	vv#'(+HNN(;(A(A(C(+J,<,<"(=c(B(+IOO(<S(A9N N 	 3  (^Q''(s   %J	  J	 J	 $ J	 J	 J	 J	 )*J	 .J	 J	  G6 ,G6 /J	 7J	 >J	 	JAJ	 6JAI<I<#I<5J6J	 ;I<<JJ	 	J*J%J*%J*c          
     ,    V ^8  d   QhRRRRRRRRRR/# )r	   r5   r   r6   	body_jsonparams_jsonr
   r   )r   s   "r   r   r     s/     % % %3 %3 %# %s %r   c                   \        4       '       g   \        # RpRpV'       d    \        P                  ! V4      pV'       d    \        P                  ! V4      p \        \        V 4      P                  4       \        V4      WE4      p\        P                  ! VRR7      pVR,          \        V4      R8  d	   R	,           # R
,           #   \         d    pR\        T4      R,           2u Rp?# Rp?ii ; i  \         d    pR\        T4      R,           2u Rp?# Rp?ii ; i  \         d   p\        RT4      u Rp?# Rp?ii ; i)zOCall ANY Asana REST endpoint. Returns raw JSON (for the model, not for speech).Nzbody_json isn't valid JSON: rS   zparams_json isn't valid JSON: F)ensure_ascii:Ni  Ni  u    …[truncated]r   	asana_api)r   r.  r[   rf   rh   r   rn   r`   r\   r   r   )	r5   r6   r  r  r7   r9   rx   r   r  s	   &&&&     r   rw   rw     s   <<DF	A::i(D 	CZZ,F%3v;$$&D	4@JJs/xA,FF2FF  	A1#a&,@@	A
  	C3CF4L>BB	C  %[!$$%s`   C C4 A$D! ?D! C1C,&C1,C14D?DDD!E,D=7E=E)NNr  FF)i,  )F)r   F)r   r   r   r   )r   r   r   r   N)r   r   r   )rz  r   )r   Tr   F2   )r   r   r   r   r   r   )r   r   r   r   r   N)r   r   r   r   r   )r   )r  )r   r   )A__conditional_annotations__r1   
__future__r   r[   r   rk   urllib.errorrX   urllib.parseurllib.requestr   r   rstriprV   r   __annotations__r   r.  r  r  r=  r   r   RuntimeErrorr    rn   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r   r'  r4  rA  rR  r`  ru  r  r   r  r  r  r  r  r  r  r  r  r  r  r  rw   )r  s   @r   <module>r     si  #H #  	    


{$CDKKCP	>   8 EG 
 Y
3 =(@",=2
I,&!H
,	,"6%
(F
?,&*<'0T 0F&1R0<E&P1'h3-l)-X9+x/-&.-b*>) *F@'F1(p% %r   