
    i|                        d Z ddlZddlZddlZddlZddlZddlmZ  ee      j                         j                  j                  Zedz  dz  Z ej                  d      Z ej                  d      Z ej                  d      Z ej                  d	      Z ej                  d
ej$                        Zh dZh dZ ej                  d      Z ej                  d      ZdedefdZdedefdZdefdZdefdZdefdZdefdZdededefdZ dedefdZ!dedefdZ"dedefdZ#dedefd Z$dedefd!Z%dTd"ed#efd$Z&dUd%ed&e'd'ed(efd)Z( ej                  d*       ej                  d+       ej                  d,       ej                  d-       ej                  d.       ej                  d/       ej                  d0       ej                  d1       ej                  d2      g	Z) ej                  d3ejT                         ej                  d4ejT                         ej                  d5ejT                         ej                  d6ejT                         ej                  d7ejT                         ej                  d8ejT                         ej                  d9ejT                         ej                  d:ejT                         ej                  d;ejT                         ej                  d<ejT                         ej                  d=ejT                        d>Z+d?ed@edefdAZ,dBe-de'fdCZ.dD Z/h dEZ0 ej                  dF      Z1 ej                  dG      Z2 ej                  dH      Z3 ej                  dI      Z4 ej                  dJ      Z5 ej                  dK      Z6de7fdLZ8dedMe9dedNe-fdOZ:dVd"edPe9fdQZ;dR Z<e=dSk(  r e<        yy)Wu  
modularity-check.py — 모듈화 수동 검증 도구 (아르고스 테스터)
Usage:
  python3 modularity-check.py scan [--json] [--exclude-tests]
  python3 modularity-check.py verify
  python3 modularity-check.py discover [--json] [--min-occurrences N]
    N)Pathconfigzmodule-registry.json
6937032012z/home/jay/workspacez\b(84px|64px|40px)\bz\bdev[1-8]-team\bz2^\s*(?:import\s+dq_rules|from\s+tools\.dq_rules\b)>   
.worktreesmemory/specsmemory/tasksmemory/eventsmemory/reportshooksr   output__pycache__node_modules.git>   .md.yml.json.yamlzos\.environ\.get\s*\(\s*[\"'][^\"']+[\"']\s*,\s*[\"']|os\.getenv\s*\(\s*[\"'][^\"']+[\"']\s*,\s*[\"']|default\s*=\s*os\.environ\.get\s*\(z;(?:get_constant|get_path|get_config)\s*\(.*\)\s*(?:if|or)\brelreturnc                 p    | j                         }t        D ]  }|j                  |dz         s||k(  s y y)u.   config/ 또는 문서 디렉토리이면 True./TF)as_posixEXCLUDE_DIRS
startswith)r   	parts_strexcls      G/home/jay/workspace/.worktrees/task-2116-dev1/tools/modularity-check.py_is_excluded_pathr   9   s=    I s
+yD/@     pathc                 B    | j                   j                         t        v S N)suffixlowerEXCLUDE_EXTSr    s    r   _is_excluded_extr'   B   s    ;;,..r   	workspacec                     g }| j                  d      D ]<  }|j                  |       }t        |      r t        |      r,|j	                  |       > t        |      S )u'   검사 대상 .py 파일 목록 반환.z*.py)rglobrelative_tor   r'   appendsorted)r(   resultspr   s       r   _collect_py_filesr0   F   s[    G__V$ mmI&S!Aq '?r   c                    h d}g }| j                  d      D ]^  }|j                         s|j                  j                         |vr1|j	                  |       }t        |      rN|j                  |       ` t        |      S )u8   모든 텍스트 파일 (.py, .sh, .js, .ts 등) 수집.>   .js.ts.jsx.tsx.py.sh.bash*)r*   is_filer#   r$   r+   r   r,   r-   )r(   target_extsr.   r/   r   s        r   _collect_all_text_filesr<   S   sw    GKG__S! yy{88>>;.mmI&S!q '?r   c                 f    	 | j                  d      j                         S # t        $ r g cY S w xY w)Nutf-8encoding)	read_text
splitlines	Exceptionr&   s    r   _read_linesrD   c   s5    ~~w~/::<< 	s   " 00sourcec                 V   t               }	 t        j                  |       }t        j                  |      D ]=  }t	        |t        j
                  t        j                  t        j                  t        j                  f      sM|j                  sZt	        |j                  d   t        j                        st	        |j                  d   j                  t        j                        st	        |j                  d   j                  j                  t              s|j                  d   }|j                  xs |j                  }t!        |j                  |dz         D ]  }|j#                  |        @ 	 |S # t$        $ r d}d}t'        | j)                         d      D ]  \  }	}
|
j+                         }|sLdD ]F  }||v s|j-                  |      }|dk\  r|j#                  |	        Kd}|}|j#                  |	        b d|j#                  |	       |sx||v s}d}d} Y |S w xY w)u   
    Python 소스에서 docstring이 차지하는 라인 범위(1-indexed) set 반환.
    ast로 파싱 불가 시 triple-quote 방식으로 fallback.
    r      FN)z"""z'''   T)setastparsewalk
isinstanceFunctionDefAsyncFunctionDefClassDefModulebodyExprvalueConstantstr
end_linenolinenorangeaddSyntaxError	enumeraterB   stripcount)rE   rangestreenodeds_nodeend_lnln	in_triple
quote_charilinestrippedqr^   s                 r   _get_docstring_rangesrk   j   s   
 UF%&yy HHTN 	'D$#2F2FVYV`V` abII"499Q<:"499Q<#5#5s||D"499Q<#5#5#;#;SA"iilG$//A7>>F#GNNFQJ? '

2'	'H M1  &	
 !2!2!4a8 	&GAtzz|H' "AH} (q 1 A:"JJqM!(,I)*J"JJqM!" 

1*"8 %I!%J'	&( M1&sE   A6E: E: 'E: 81E: *1E: AE: :AH(AH(H(H('H(rh   file_extc                 z    | j                         }|dk(  r|j                  d      S |dv r|j                  d      S y)Nr6   #)r7   r8   F)r]   r   )rh   rl   ri   s      r   _is_comment_linero      sC    zz|H5""3''	%	%""3''r   c                 
    d| v S )Nz# noqa: modularity rh   s    r   	_has_noqars      s    4''r   c                 >    t        t        j                  |             S )u6   os.environ.get / os.getenv fallback 패턴이면 True.)bool_ENV_FALLBACK_REsearchrr   s    r   _is_env_fallbackrx      s     ''-..r   c                 >    t        t        j                  |             S )u1   config getter + if/or fallback 패턴이면 True.)ru   _CONFIG_FALLBACK_RErw   rr   s    r   _is_config_fallbackr{      s    #**4011r   c                 0    | j                  d      xs d| v S )u*   tests/ 디렉토리 내 파일이면 True.ztests/z/tests/)r   )r   s    r   _is_in_testsr}      s    >>(#7yC'77r   c                     	 | j                  d      }t        t        j                  |            S # t        $ r Y yw xY w)Nr>   r?   F)rA   ru   DQ_IMPORT_PATTERNrw   rC   )r    contents     r   _file_has_dq_importr      s@    ..'.2%,,W566 s   /2 	>>output_jsonexclude_testsc                    g }g }g }g }g }d}d}t        t              }	t        t              }
|
D ]-  }t        |j	                  t                    }|j
                  j                         }t        |      }|dk(  }t        |      }|r	 |j                  d      }t        |      }n
t               }t        |d      D ]  \  }}t        ||      rt        |      r |r||v r't!        |      rEt"        j%                  |      s*t&        j%                  |      st(        j%                  |      r|dz  }wt+        |      rEt"        j%                  |      s*t&        j%                  |      st(        j%                  |      r|dz  }|j-                         }t"        j%                  |      r,|r|j/                  |||df       n|j/                  |||f       t&        j%                  |      r,|r|j/                  |||df       n|j/                  |||f       t(        j%                  |      sp|r|j/                  |||d	f       |j/                  |||f        0 |	D ]K  }t        |j	                  t                    }|j
                  j                         }t        |      }t1        |      rSt        |      }	 |j                  d      }t        |      }t        |d      D ]  \  }}t        ||      rt        |      r||v r$t!        |      rt2        j%                  |      r|dz  }Jt+        |      rt2        j%                  |      r|dz  }pt2        j%                  |      s|j-                         }|r|j/                  |||d
f       |j/                  |||f        N | r2|rdn
t5        |      }|D cg c]  \  }}}|||d c}}}|D cg c]  \  }}}|||d c}}}|D cg c]  \  }}}|||d c}}}|D cg c]  \  }}}|||d c}}}|s |D cg c]  \  }}}}||||d c}}}}ng t5        |      t5        |      z   t5        |      z   t5        |      |z   t5        |      t5        |      z   t5        |      z   t5        |      z   |z   ||t5        |      dd}t7        t9        j:                  |dd             n/t7        d       t=        d|d       t=        d|d       t=        d|d       t=        d|d       |sG|rEt7        dt5        |       d       |D ]  \  }}}}t7        d| d| d| d|          t7                t5        |      t5        |      z   t5        |      z   }t5        |      |rdn
t5        |      z   }||z   } t7        d        t7        d!| d"       t7        d#| d"       t7        d$t5        |       d"       t7                t7        d%|  d&| d'| d(       t5        |      t5        |      z   t5        |      z   }!|!dk(  rdS dS # t        $ r d}Y w xY w# t        $ r d}Y w xY wc c}}}w c c}}}w c c}}}w c c}}}w c c}}}}w ))u   전체 코드베이스 스캔.r   r6   r>   r?    rG   chat_idabsolute_path	team_namedq_font_size)filerh   r   )r   rh   r   category)
total_fail
total_warntotalskipped_env_fallbackskipped_config_fallbacktests_warnings)r   r   r   r   tests_violationssummaryFrH   ensure_asciiindentu    === 모듈화 스캔 결과 ===
u   ChatID 하드코딩FAIL)severityu   절대경로 하드코딩u    DQ 폰트 사이즈 하드코딩u   팀명 하드코딩WARNu)   [WARN] tests/ 위반 → WARNING 전환 (   건)z  - [z] :: u   === 오탐 제외 통계 ===u     ENV fallback 패턴 스킵: u   건u!     Config fallback 패턴 스킵: u     tests/ → WARNING 전환: u   총 위반: u   건 (FAIL: z, WARN: ))r0   	WORKSPACEr<   rV   r+   r#   r$   rD   r}   rA   rC   rk   rI   r\   ro   rs   rx   CHAT_ID_PATTERNrw   WORKSPACE_PATH_PATTERNTEAM_NAME_PATTERNr{   r]   r,   r   DQ_FONT_SIZES_PATTERNlenprintjsondumps_print_violations)"r   r   chat_id_violationsabspath_violationsdq_font_violationsteam_name_violationsr   r   r   py_files	all_filesfpathr   extlinesis_pyin_testsrE   docstring_linesrX   rh   ri   tests_warn_countfrd   ccatresultr   r   
fail_total
warn_totalr   
fail_counts"                                     r   run_scanr      s    +H'	2I  EI%##I./ll  "E"u$ ': 4F;O!eO%eQ/ 4	ILFDc*?2  %#**40-44T:(//5(A-( #4(#**40-44T:(//5+q0+zz|H %%d+$++S&(I,NO&--sFH.EF &,,T2$++S&(O,TU&--sFH.EF !''-$++S&(K,PQ(//fh0GHi4	I#EIP  #G%##I./ll  "$u%E"	__g_6F 07%eQ/ 	GLFDc*(%(//5(A-("4((//5+q0+$++D1::<$++S&(N,ST&--sFH.EF-	G#GL  -137G3HN`aa(!RB1=aTfgg2qq"CgSeffxq"aaBfPdeeHAr11bQ?e % ^nnnMArSTVY!RA3Gn ""45<N8OORUVhRii!"67:JJ/0()*()* *+, #	#
 )=+B"%&6"7
. 	djjeA>?12 	/1CfU 	57ITZ[ 	<>P[ab 	/1EPVW !1=cBR>S=TTXYZ4D G0vwhZr%&G9EFGG+,s3E/FFM_I``
-.}!#N^J_`
Z',-./C.DCHI12I1J#NO-c2B.C-DCHIUG;zl(:,aPQ '(3/A+BBSI[E\\Ja1&Q&A  L  	F	@ bgfensB   	YY	Y%$Y,
?Y3Y:8ZYYY"!Y"label
violationsr   r#   c           	          t        |      }d| d}t        | d|  d| d|        |D ]  \  }}}t        d| d| d|         t                y )	N[] z (r     - r   r   )r   r   )	r   r   r   r#   r^   tagr   rX   r   s	            r   r   r   }  sm    
OEhZq/C	SE5'E7$vh
/0", 2vwUG1VHBwi012	Gr   zget_config\s*\(zget_constant\s*\(zload_config\s*\(zconfig\[zconstants\[zfrom\s+configzimport\s+configzconfig\.loaderzloader\.loadz"(chat_id|CHAT_ID|COKACDIR_CHAT_ID)zD(workspace_root|WORKSPACE_ROOT|roots\.workspace|/home/jay/workspace)z4(team_bot_mapping|TEAM_BOT|get_constant\(["\']teams)z(dq_rules|dq-rules)z>(palette|design.system|teamColors|team_colors|category_colors)zA(threshold|idle_hours|ghost_hours|IDLE_THRESHOLD|GHOST_THRESHOLD)z(COKACDIR_KEY|cokacdir_key)z(team_to_bot|TEAM_TO_BOT)z"(bots\b|bot_settings|BOT_SETTINGS)z(work_levels|WORK_LEVELS)z((font_sizes|font_size|dq.rules|dq_rules))r   workspace_rootteam_bot_mappingdq_rulesdesign_palette
thresholdscokacdir_keyteam_to_botbotswork_levels
font_sizesr   
source_keyc                     	 | j                  d      }t        j                  |      }|r|j	                  |      ryy# t        $ r Y yw xY w)u2   파일이 해당 source를 참조하는지 확인.r>   r?   FT)rA   rC   SOURCE_REF_PATTERNSgetrw   )r   r   r   patterns       r   _file_references_sourcer     sQ    //7/3
 "%%j1G7>>'*  s   > 	A
	A
registryc                    t        | j                  di       j                               }| j                  di       j                         D ]'  }|j                  d      }|s|j	                  |       ) t        j                  d      }g }t        t              }|D ]  }t        |j                  t                    }|j                  j                         }		 |j                  d      j                         }
|	dk(  r	 dj!                  |
      }t#        |      }n
t               }t%        |
d      D ]x  \  }}t'        ||	      rt)        |      r|	dk(  r||v r)|j+                  |      D ]<  }|j-                  d      }||vs|j-                  d	      }|j/                  ||f       > z  |S # t        $ r Y "w xY w# t        $ r t               }Y w xY w)
u   
    코드에서 get_constant/get_config 호출을 찾아 registry에 없는 것 반환.
    반환: [(rel_path, usage_string), ...]
    sourceskeyz2(?:get_constant|get_config)\s*\(\s*["\'](\w+)["\']r>   r?   r6   
rG   r   )rI   r   keysvaluesrZ   recompiler<   r   rV   r+   r#   r$   rA   rB   rC   joinrk   r\   ro   rs   finditergroupr,   )r   registered_keyssrcsrc_keycall_patternwarningsr   r   r   r   r   source_textr   rX   rh   mkey_usedusages                     r   _find_unregistered_config_refsr     s   
 (,,y"5::<=O||Ir*113 )''%.() ::STLH'	2I !2%##I./ll  "	OOWO5@@BE
 %<("ii."7"D "eO%eQ/ 	2LFDc*e|/ 9!**40 2771:?2GGAJEOOS%L12	2%!2F O=  		  ("%%(s$    F2?G2	F?>F?GGc            
         t         j                         st        dt                 y	 t        j                  t         j                  d            } | j                  di       }t        d       t        |      }d	}d	}d	}|j                         D ]  \  }}|j                  d
g       }	t        d| d       |	D ]  }
d|
v sd|
v rt        d|
 d       |dz  } t        |
z  }|j                         st        d|
 d       |dz  }Nt        ||      }|rt        d|
 d       |dz  }qt        d|
 d       |dz  } t                 t        |       }|r?t        d       |D ]  \  }}t        d| d| d        |t        |      z  }t                t        d| d| d| d|        |d	k(  rd	S dS # t        j                  $ r}t        d|        Y d}~yd}~ww xY w)u#   config/module-registry.json 검증.u4   [ERROR] registry 파일을 찾을 수 없습니다: rG   r>   r?   u%   [ERROR] registry JSON 파싱 실패: Nr   u&   === 레지스트리 검증 결과 ===
r   used_byr   r   r9   (u
     ℹ️  u/    — 디렉토리 패턴 (개별 검증 생략)u
     ⚠️  u    — 파일 미존재u     ✅ u    — config 참조 확인u     ❌ u    — config 참조 미확인u"   미등록 config 참조 (WARNING):r   r   u     사용하나 registry 미등록u   총 검증: u    sources, 정상: u
   , 경고: u
   , 실패: )REGISTRY_PATHexistsr   r   loadsrA   JSONDecodeErrorr   r   itemsr   r   r   )r   er   total_sourcesok_count
warn_countr   r   source_infor   ub_relub_pathrefsunregisteredr   r   s                   r   
run_verifyr     s#   !D]OTU::m55w5GH
 ll9b)G	
34LMHJJ#*==? 
K//)R0*Q  	 Ff}v
6(*YZ[A&(G>>#
6(*?@Aa
*7J?Dvh&?@AAvh&BCDa
)	 , 	5: 2(;L23& 	IJCDRw&FGH	Ic,''
	L'9(:j\Ycdnco
pqa1&Q&i  5aS9:s   )F8 8G#GG#>u                
     '  '         ,  -  .  /  0  3  4  8  @  @  @  V  X  h  w                                                                                r         rG   rH                     	   
                                              $   (   0   2   <   @   H   P   Z   `   c   d   x                                       zhttps?://[^\s\'"<>]+z:(\d{2,5})\bz\b(\d{4,})\bz"#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\bz4\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\bz^\s*(?:import\s|from\s)c                  4   t         dz  dz  } t               }| j                         s|S 	 t        j                  | j                  d            }|j                  dg       D ]#  }|j                  |j                  dd             % 	 |S # t        $ r Y |S w xY w)ug   lint-patterns.json에 등록된 패턴 regex 값 집합 반환 (이미 등록된 값 중복 방지용).r   zlint-patterns.jsonr>   r?   patternsregexr   )	r   rI   r   r   r   rA   r   rZ   rC   )	lint_pathknowndatapats       r   _load_lint_patterns_valuesrv  M  s    H$';;IEEzz)--w-?@88J+ 	,CIIcgggr*+	, L  Ls   AB
 
	BBrX   
collectorsc                    t         j                  |       ryt        j                  |       D ]H  j	                  d      j                  d      }|d   j                  |g       j                  ||f       J t        j                  |       D cg c]"  }|j                         |j                         f$ }}t        j                  |       D ]u  t        fd|D              }|rj	                  d      }t        j	                  d            }	|	dk  s|	dkD  rP|d	   j                  |g       j                  ||f       w t        j                  |       D ]c  t        j	                  d            }
|
t        v r&d
|
cxk  rdk  rn n5|d   j                  t        |
      g       j                  ||f       e t         j                  |       D ]J  dj	                  d      j#                         z   }|d   j                  |g       j                  ||f       L t$        j                  |       D ]9  j	                  d      }|d   j                  |g       j                  ||f       ; yc c}w )u   
    한 줄에서 각 카테고리별 후보 값을 추출하여 collectors에 누적.
    collectors: { category: { value: [(rel, lineno), ...] } }
    Nr   z.,;)api_urlc              3   `   K   | ]%  \  }}|j                         cxk  xr |k  nc  ' y wr"   )start).0sr   r   s      r   	<genexpr>z0_extract_candidates_from_line.<locals>.<genexpr>o  s(     >DAqQ!'')'a''>s   +.rG   r?  i  portra  iW  magic_numberrn   coloremail)_IMPORT_LINE_REmatch_URL_REr   r   rstrip
setdefaultr,   r{  end_PORT_REanyint_INT_RE_COMMON_NUMBERSrV   	_COLOR_REupper	_EMAIL_RE)rh   rX   r   rw  urlr   	url_spansin_urlport_valport_numnumr  r  s        `       r   _extract_candidates_from_liner  \  sJ    T" d# Hggaj'9((b188#vGH 07/?/?/EF!!'')QUUW%FIFt$ 	J>I>>771:qwwqz?d?h.6%%h3::C=I	J d# R!''!*o/!#>"--c#h;BBC=QR % Haggaj&&((7&&ub188#vGH
 % H
7&&ub188#vGH9 Gs   'I4min_occurrencesc                     t               }t        t              }h ddt        dt        ffd}i i i i i d}|D ]  } ||      rt        |j                  t                    }|j                  j                         }t        |      }	|dk(  }
|
r	 |j                  d      }t        |      }n
t               }t        |	d
      D ]3  \  }}t        ||      rt!        |      r|
r||v r&t#        ||||       5  dddddd}g }|j%                         D ]  \  }}|j%                         D ]  \  }}t'        |D ch c]	  \  }}||f c}}      }|j)                          t+        |      |k  rEd}|D ]  }|st-        j.                  ||      sd} n |rn|j1                  ||t+        |      |D cg c]
  \  }}||d c}}d         |j)                  d        | rEt3        d |D              }|t+        |      |dd}t5        t7        j8                  |dd             y.t5        d       |st5        d| d       ne|D ]`  }|j;                  |d   |d         }t5        d| d |d!    d"|d#    d$       |d%   D ]  }t5        d&|d'    d(|d)            t5                b t3        d* |D              }t5        d+t+        |       d,| d-| d       y.# t        $ r d	}Y Lw xY wc c}}w c c}}w )/uY   코드베이스에서 반복 등장하는 잠재적 하드코딩 후보를 자동 식별.>   .venvsite-packagesvenvr   _backupr   r   r   r   r   c                 ^    | j                  t              j                  }|D ]  }|v s y y)NTF)r+   r   parts)r   r  part_DISCOVER_EXCLUDE_PARTSs      r   _is_excluded_for_discoverz/run_discover.<locals>._is_excluded_for_discover  s9    !!),22 	D..	 r   )ry  r  r  r  r  r6   r>   r?   r   rG   zAPI URLu   포트번호u   매직넘버u   색상코드u   이메일/도메인FT)r   rh   )r   rT   occurrences	locationsc                      | d    | d   | d   fS )Nr  r   rT   rq   )xs    r   <lambda>zrun_discover.<locals>.<lambda>  s    Am$4#4a
mQwZ"P r   )r   c              3   &   K   | ]	  }|d      ywr  Nrq   r|  r   s     r   r~  zrun_discover.<locals>.<genexpr>       EQ- 0E   )total_candidatestotal_occurrences)
candidatesr   rH   r   u-   === 잠재적 하드코딩 후보 발견 ===
u!   발견된 후보 없음 (기준: u   곳 이상)r   r   z] "rT   u   " — r  u   곳에서 발견r  r   r   r   rh   c              3   &   K   | ]	  }|d      ywr  rq   r  s     r   r~  zrun_discover.<locals>.<genexpr>  r  r  u   총 후보: u   건, 총 등장: u   회 (기준: r   )rv  r<   r   r   ru   rV   r+   r#   r$   rD   rA   rC   rk   rI   r\   ro   rs   r  r   listsortr   r   rw   r,   sumr   r   r   r   ) r   r  known_patternsr   r  rw  r   r   r   r   r   rE   r   rX   rh   category_labelsr  r   	value_maprT   r  rrd   unique_locationsskip	pat_regexr  r   r   r   locr  s                                   @r   run_discoverr    s    01N'	2I } $  J  I$U+%##I./ll  "E"u ': 4F;O!eO%eQ/ 	ILFDc*?2)$ZH	I%IB &&O J)//1 ) ) 1 	E9#)$DBaW$DE!!##$6 D+ 	9e!<D $"#34CST%!Rq"5T	 #	4 OOPOQ E*EE$$'
O%6
 	djjeA>?"  	>?5o5FkRS '++AjM1Z=I%AgJ<va6F5GGWXY[> >CDVQs6{m<=>  E*EES_-->?P>QQ^_n^ooz{|k  B %E& Us   K">K48K:"K10K1c                     t        j                  d      } | j                  dd      }|j                  dd      }|j	                  d	d
dd       |j	                  dd
dd       |j                  dd       |j                  dd      }|j	                  d	d
dd       |j	                  dt
        dddd       | j                         }|j                  dk(  r"t        |j                  |j                        }n]|j                  dk(  rt               }nC|j                  dk(  r"t        |j                  |j                        }n| j                          d}t        j                   |       y )Nu7   모듈화 수동 검증 도구 (아르고스 테스터))descriptioncommandT)destrequiredscanuD   전체 코드베이스에서 하드코딩된 값을 스캔합니다.)helpz--json
store_truer   u,   결과를 JSON 형식으로 출력합니다.)actionr  r  z--exclude-testsr   u+   tests/ 위반을 표시하지 않습니다.verifyu@   module-registry.json과 실제 코드를 대조 검증합니다.discoverub   코드베이스에서 반복 등장하는 잠재적 하드코딩 후보를 자동 식별합니다.z--min-occurrencesr@  r  NuP   동일 값이 N곳 이상 등장해야 후보로 보고합니다. (기본값: 3))typedefaultr  metavarr  )r   r   )r   r  rG   )argparseArgumentParseradd_subparsers
add_parseradd_argumentr  
parse_argsr  r   r   r   r   r  r  
print_helpsysexit)parser
subparsersscan_parserdiscover_parserargs	exit_codes         r   mainr    s}   $$1jkF&&I&EJ ''5{'|KXl  VD  E,_Kx  
 ()kl !++} , O   M@n !    _ !  D||v)9)9I[I[\			!L			# T-=-=tOcOcd		HHYr   __main__)FF)r   )Fr@  )>__doc__r  rJ   r   r   r  pathlibr   __file__resolveparentr   r   r   r   r   r   r   	MULTILINEr   r   r%   rv   rz   ru   r   r'   r0   r<   rD   rV   rk   ro   rs   rx   r{   r}   r   r   r  r   CONFIG_REF_PATTERNS
IGNORECASEr   r   dictr   r   r  r  r  r  r  r  r  rI   rv  r  r  r  r  __name__rq   r   r   <module>r     s    
  	 
  N""$++22	H$'== "**]+#$:; "

#:; BJJ34  BJJTVXVbVbc  1 2::, 
 !bjj!_` 
4 D /4 /D /
 
t  d ,# ,^3 # $ (C (D (/3 /4 /
2c 2d 2
8c 8d 8
d t z'$ z't z'zS d c 3  BJJ!"BJJ#$BJJ"#BJJ{BJJ~BJJ BJJ!"BJJ !BJJ
  rzz?O bjjOQSQ^Q^ #

#Z\^\i\ij

12==A bjj!bdfdqdqr"**acecpcpqBJJ=r}}M2:::BMMJBJJ<bmmL2:::BMMJ"**H"--X "4 S T 5T 5d 5p<'D* "**,
-2::o&
"**_
%BJJ<=	BJJNO	"**78C .H .HS .Hs .HPT .Hb|d |S |D*Z zF r   