agent-ide 0.2.0 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (575) hide show
  1. package/README.md +28 -355
  2. package/dist/application/events/event-bus.d.ts.map +1 -1
  3. package/dist/application/events/event-bus.js.map +1 -1
  4. package/dist/application/index.d.ts +3 -1
  5. package/dist/application/index.d.ts.map +1 -1
  6. package/dist/application/index.js +10 -5
  7. package/dist/application/index.js.map +1 -1
  8. package/dist/application/services/cache-coordinator.service.d.ts.map +1 -1
  9. package/dist/application/services/cache-coordinator.service.js.map +1 -1
  10. package/dist/application/services/error-handler.service.d.ts.map +1 -1
  11. package/dist/application/services/error-handler.service.js.map +1 -1
  12. package/dist/application/services/module-coordinator.service.d.ts +13 -2
  13. package/dist/application/services/module-coordinator.service.d.ts.map +1 -1
  14. package/dist/application/services/module-coordinator.service.js +63 -17
  15. package/dist/application/services/module-coordinator.service.js.map +1 -1
  16. package/dist/application/services/session-manager.service.d.ts.map +1 -1
  17. package/dist/application/services/session-manager.service.js.map +1 -1
  18. package/dist/application/services/workflow-engine.service.d.ts +1 -1
  19. package/dist/application/services/workflow-engine.service.d.ts.map +1 -1
  20. package/dist/application/services/workflow-engine.service.js.map +1 -1
  21. package/dist/application/state/state-manager.d.ts +3 -3
  22. package/dist/application/state/state-manager.d.ts.map +1 -1
  23. package/dist/application/state/state-manager.js +3 -3
  24. package/dist/application/state/state-manager.js.map +1 -1
  25. package/dist/application/types.d.ts.map +1 -1
  26. package/dist/application/workflows/analysis-workflow.d.ts +1 -1
  27. package/dist/application/workflows/analysis-workflow.d.ts.map +1 -1
  28. package/dist/application/workflows/analysis-workflow.js +1 -1
  29. package/dist/application/workflows/analysis-workflow.js.map +1 -1
  30. package/dist/application/workflows/base-workflow.d.ts.map +1 -1
  31. package/dist/application/workflows/base-workflow.js.map +1 -1
  32. package/dist/application/workflows/index.d.ts +2 -2
  33. package/dist/application/workflows/index.d.ts.map +1 -1
  34. package/dist/application/workflows/index.js +2 -2
  35. package/dist/application/workflows/index.js.map +1 -1
  36. package/dist/application/workflows/refactor-workflow.d.ts +1 -1
  37. package/dist/application/workflows/refactor-workflow.d.ts.map +1 -1
  38. package/dist/application/workflows/refactor-workflow.js +1 -1
  39. package/dist/application/workflows/refactor-workflow.js.map +1 -1
  40. package/dist/core/dependency/cycle-detector.d.ts +1 -1
  41. package/dist/core/dependency/cycle-detector.d.ts.map +1 -1
  42. package/dist/core/dependency/cycle-detector.js +1 -1
  43. package/dist/core/dependency/cycle-detector.js.map +1 -1
  44. package/dist/core/dependency/dependency-analyzer.d.ts +3 -1
  45. package/dist/core/dependency/dependency-analyzer.d.ts.map +1 -1
  46. package/dist/core/dependency/dependency-analyzer.js +25 -24
  47. package/dist/core/dependency/dependency-analyzer.js.map +1 -1
  48. package/dist/core/dependency/types.d.ts.map +1 -1
  49. package/dist/core/indexing/file-index.d.ts.map +1 -1
  50. package/dist/core/indexing/file-index.js +1 -1
  51. package/dist/core/indexing/file-index.js.map +1 -1
  52. package/dist/core/indexing/file-watcher.d.ts +4 -2
  53. package/dist/core/indexing/file-watcher.d.ts.map +1 -1
  54. package/dist/core/indexing/file-watcher.js +10 -6
  55. package/dist/core/indexing/file-watcher.js.map +1 -1
  56. package/dist/core/indexing/index-engine.d.ts +3 -1
  57. package/dist/core/indexing/index-engine.d.ts.map +1 -1
  58. package/dist/core/indexing/index-engine.js +24 -15
  59. package/dist/core/indexing/index-engine.js.map +1 -1
  60. package/dist/core/indexing/symbol-index.d.ts.map +1 -1
  61. package/dist/core/indexing/types.d.ts.map +1 -1
  62. package/dist/core/search/engines/text-engine.d.ts +3 -0
  63. package/dist/core/search/engines/text-engine.d.ts.map +1 -1
  64. package/dist/core/search/engines/text-engine.js +7 -6
  65. package/dist/core/search/engines/text-engine.js.map +1 -1
  66. package/dist/core/search/service.d.ts +2 -1
  67. package/dist/core/search/service.d.ts.map +1 -1
  68. package/dist/core/search/service.js +3 -3
  69. package/dist/core/search/service.js.map +1 -1
  70. package/dist/core/search/types.d.ts.map +1 -1
  71. package/dist/core/snapshot/index.d.ts +3 -19
  72. package/dist/core/snapshot/index.d.ts.map +1 -1
  73. package/dist/core/snapshot/index.js +2 -23
  74. package/dist/core/snapshot/index.js.map +1 -1
  75. package/dist/core/snapshot/snapshot-generator.d.ts +66 -0
  76. package/dist/core/snapshot/snapshot-generator.d.ts.map +1 -0
  77. package/dist/core/snapshot/snapshot-generator.js +258 -0
  78. package/dist/core/snapshot/snapshot-generator.js.map +1 -0
  79. package/dist/core/snapshot/types.d.ts +41 -190
  80. package/dist/core/snapshot/types.d.ts.map +1 -1
  81. package/dist/core/snapshot/types.js +13 -67
  82. package/dist/core/snapshot/types.js.map +1 -1
  83. package/dist/core/transform/index.d.ts +10 -0
  84. package/dist/core/transform/index.d.ts.map +1 -0
  85. package/dist/core/transform/index.js +21 -0
  86. package/dist/core/transform/index.js.map +1 -0
  87. package/dist/core/transform/location/index.d.ts +8 -0
  88. package/dist/core/transform/location/index.d.ts.map +1 -0
  89. package/dist/core/transform/location/index.js +9 -0
  90. package/dist/core/transform/location/index.js.map +1 -0
  91. package/dist/core/{move → transform/location/move-file}/import-resolver.d.ts +1 -1
  92. package/dist/core/transform/location/move-file/import-resolver.d.ts.map +1 -0
  93. package/dist/core/{move → transform/location/move-file}/import-resolver.js +11 -12
  94. package/dist/core/transform/location/move-file/import-resolver.js.map +1 -0
  95. package/dist/core/{move → transform/location/move-file}/index.d.ts +2 -2
  96. package/dist/core/transform/location/move-file/index.d.ts.map +1 -0
  97. package/dist/core/{move → transform/location/move-file}/index.js +2 -2
  98. package/dist/core/transform/location/move-file/index.js.map +1 -0
  99. package/dist/core/{move → transform/location/move-file}/move-service.d.ts +5 -3
  100. package/dist/core/transform/location/move-file/move-service.d.ts.map +1 -0
  101. package/dist/core/{move → transform/location/move-file}/move-service.js +67 -85
  102. package/dist/core/transform/location/move-file/move-service.js.map +1 -0
  103. package/dist/core/{move → transform/location/move-file}/types.d.ts +1 -1
  104. package/dist/core/transform/location/move-file/types.d.ts.map +1 -0
  105. package/dist/core/transform/location/move-file/types.js.map +1 -0
  106. package/dist/core/transform/location/move-member/index.d.ts +10 -0
  107. package/dist/core/transform/location/move-member/index.d.ts.map +1 -0
  108. package/dist/core/transform/location/move-member/index.js +12 -0
  109. package/dist/core/transform/location/move-member/index.js.map +1 -0
  110. package/dist/core/transform/location/move-member/member-extractor.d.ts +96 -0
  111. package/dist/core/transform/location/move-member/member-extractor.d.ts.map +1 -0
  112. package/dist/core/transform/location/move-member/member-extractor.js +439 -0
  113. package/dist/core/transform/location/move-member/member-extractor.js.map +1 -0
  114. package/dist/core/transform/location/move-member/move-member-service.d.ts +87 -0
  115. package/dist/core/transform/location/move-member/move-member-service.d.ts.map +1 -0
  116. package/dist/core/transform/location/move-member/move-member-service.js +412 -0
  117. package/dist/core/transform/location/move-member/move-member-service.js.map +1 -0
  118. package/dist/core/transform/location/move-member/types.d.ts +182 -0
  119. package/dist/core/transform/location/move-member/types.d.ts.map +1 -0
  120. package/dist/core/transform/location/move-member/types.js +75 -0
  121. package/dist/core/transform/location/move-member/types.js.map +1 -0
  122. package/dist/core/transform/location/shift/file-generator.d.ts +45 -0
  123. package/dist/core/transform/location/shift/file-generator.d.ts.map +1 -0
  124. package/dist/core/transform/location/shift/file-generator.js +94 -0
  125. package/dist/core/transform/location/shift/file-generator.js.map +1 -0
  126. package/dist/core/transform/location/shift/index.d.ts +11 -0
  127. package/dist/core/transform/location/shift/index.d.ts.map +1 -0
  128. package/dist/core/transform/location/shift/index.js +13 -0
  129. package/dist/core/transform/location/shift/index.js.map +1 -0
  130. package/dist/core/transform/location/shift/line-extractor.d.ts +55 -0
  131. package/dist/core/transform/location/shift/line-extractor.d.ts.map +1 -0
  132. package/dist/core/transform/location/shift/line-extractor.js +123 -0
  133. package/dist/core/transform/location/shift/line-extractor.js.map +1 -0
  134. package/dist/core/transform/location/shift/shift-service.d.ts +57 -0
  135. package/dist/core/transform/location/shift/shift-service.d.ts.map +1 -0
  136. package/dist/core/transform/location/shift/shift-service.js +199 -0
  137. package/dist/core/transform/location/shift/shift-service.js.map +1 -0
  138. package/dist/core/transform/location/shift/types.d.ts +195 -0
  139. package/dist/core/transform/location/shift/types.d.ts.map +1 -0
  140. package/dist/core/transform/location/shift/types.js +89 -0
  141. package/dist/core/transform/location/shift/types.js.map +1 -0
  142. package/dist/core/transform/shared/code-editor.d.ts +70 -0
  143. package/dist/core/transform/shared/code-editor.d.ts.map +1 -0
  144. package/dist/core/transform/shared/code-editor.js +206 -0
  145. package/dist/core/transform/shared/code-editor.js.map +1 -0
  146. package/dist/core/transform/shared/index.d.ts +7 -0
  147. package/dist/core/transform/shared/index.d.ts.map +1 -0
  148. package/dist/core/transform/shared/index.js +7 -0
  149. package/dist/core/transform/shared/index.js.map +1 -0
  150. package/dist/core/transform/shared/symbol-finder.d.ts +154 -0
  151. package/dist/core/transform/shared/symbol-finder.d.ts.map +1 -0
  152. package/dist/core/transform/shared/symbol-finder.js +433 -0
  153. package/dist/core/transform/shared/symbol-finder.js.map +1 -0
  154. package/dist/core/transform/structure/extract/extract-function.d.ts.map +1 -0
  155. package/dist/core/{refactor → transform/structure/extract}/extract-function.js +1 -1
  156. package/dist/core/transform/structure/extract/extract-function.js.map +1 -0
  157. package/dist/core/transform/structure/extract/index.d.ts +7 -0
  158. package/dist/core/transform/structure/extract/index.d.ts.map +1 -0
  159. package/dist/core/transform/structure/extract/index.js +9 -0
  160. package/dist/core/transform/structure/extract/index.js.map +1 -0
  161. package/dist/core/transform/structure/extract/swift-extractor.d.ts.map +1 -0
  162. package/dist/core/transform/structure/extract/swift-extractor.js.map +1 -0
  163. package/dist/core/transform/structure/index.d.ts +8 -0
  164. package/dist/core/transform/structure/index.d.ts.map +1 -0
  165. package/dist/core/transform/structure/index.js +8 -0
  166. package/dist/core/transform/structure/index.js.map +1 -0
  167. package/dist/core/transform/structure/inline/index.d.ts +6 -0
  168. package/dist/core/transform/structure/inline/index.d.ts.map +1 -0
  169. package/dist/core/transform/structure/inline/index.js +6 -0
  170. package/dist/core/transform/structure/inline/index.js.map +1 -0
  171. package/dist/core/transform/structure/inline/inline-function.d.ts.map +1 -0
  172. package/dist/core/{refactor → transform/structure/inline}/inline-function.js +37 -26
  173. package/dist/core/transform/structure/inline/inline-function.js.map +1 -0
  174. package/dist/core/transform/structure/patterns/design-patterns.d.ts.map +1 -0
  175. package/dist/core/transform/structure/patterns/design-patterns.js.map +1 -0
  176. package/dist/core/transform/structure/patterns/index.d.ts +6 -0
  177. package/dist/core/transform/structure/patterns/index.d.ts.map +1 -0
  178. package/dist/core/transform/structure/patterns/index.js +6 -0
  179. package/dist/core/transform/structure/patterns/index.js.map +1 -0
  180. package/dist/core/transform/symbol/change-signature/change-signature-service.d.ts +94 -0
  181. package/dist/core/transform/symbol/change-signature/change-signature-service.d.ts.map +1 -0
  182. package/dist/core/transform/symbol/change-signature/change-signature-service.js +544 -0
  183. package/dist/core/transform/symbol/change-signature/change-signature-service.js.map +1 -0
  184. package/dist/core/transform/symbol/change-signature/index.d.ts +10 -0
  185. package/dist/core/transform/symbol/change-signature/index.d.ts.map +1 -0
  186. package/dist/core/transform/symbol/change-signature/index.js +12 -0
  187. package/dist/core/transform/symbol/change-signature/index.js.map +1 -0
  188. package/dist/core/transform/symbol/change-signature/signature-parser.d.ts +76 -0
  189. package/dist/core/transform/symbol/change-signature/signature-parser.d.ts.map +1 -0
  190. package/dist/core/transform/symbol/change-signature/signature-parser.js +387 -0
  191. package/dist/core/transform/symbol/change-signature/signature-parser.js.map +1 -0
  192. package/dist/core/transform/symbol/change-signature/types.d.ts +244 -0
  193. package/dist/core/transform/symbol/change-signature/types.d.ts.map +1 -0
  194. package/dist/core/transform/symbol/change-signature/types.js +73 -0
  195. package/dist/core/transform/symbol/change-signature/types.js.map +1 -0
  196. package/dist/core/transform/symbol/index.d.ts +7 -0
  197. package/dist/core/transform/symbol/index.d.ts.map +1 -0
  198. package/dist/core/transform/symbol/index.js +7 -0
  199. package/dist/core/transform/symbol/index.js.map +1 -0
  200. package/dist/core/transform/symbol/rename/index.d.ts +9 -0
  201. package/dist/core/transform/symbol/rename/index.d.ts.map +1 -0
  202. package/dist/core/{rename → transform/symbol/rename}/index.js +5 -3
  203. package/dist/core/transform/symbol/rename/index.js.map +1 -0
  204. package/dist/core/{rename → transform/symbol/rename}/reference-updater.d.ts +2 -2
  205. package/dist/core/transform/symbol/rename/reference-updater.d.ts.map +1 -0
  206. package/dist/core/transform/symbol/rename/reference-updater.js.map +1 -0
  207. package/dist/core/{rename → transform/symbol/rename}/rename-engine.d.ts +2 -2
  208. package/dist/core/transform/symbol/rename/rename-engine.d.ts.map +1 -0
  209. package/dist/core/{rename → transform/symbol/rename}/rename-engine.js +3 -3
  210. package/dist/core/transform/symbol/rename/rename-engine.js.map +1 -0
  211. package/dist/core/{rename → transform/symbol/rename}/scope-analyzer.d.ts +3 -3
  212. package/dist/core/transform/symbol/rename/scope-analyzer.d.ts.map +1 -0
  213. package/dist/core/{rename → transform/symbol/rename}/scope-analyzer.js +2 -2
  214. package/dist/core/transform/symbol/rename/scope-analyzer.js.map +1 -0
  215. package/dist/core/{rename → transform/symbol/rename}/types.d.ts +2 -2
  216. package/dist/core/transform/symbol/rename/types.d.ts.map +1 -0
  217. package/dist/core/transform/symbol/rename/types.js.map +1 -0
  218. package/dist/core/transform/types.d.ts +185 -0
  219. package/dist/core/transform/types.d.ts.map +1 -0
  220. package/dist/core/transform/types.js +150 -0
  221. package/dist/core/transform/types.js.map +1 -0
  222. package/dist/infrastructure/cache/cache-manager.d.ts +1 -1
  223. package/dist/infrastructure/cache/cache-manager.d.ts.map +1 -1
  224. package/dist/infrastructure/cache/cache-manager.js +1 -1
  225. package/dist/infrastructure/cache/cache-manager.js.map +1 -1
  226. package/dist/infrastructure/cache/memory-cache.js +1 -1
  227. package/dist/infrastructure/cache/memory-cache.js.map +1 -1
  228. package/dist/infrastructure/cache/strategies.d.ts +1 -1
  229. package/dist/infrastructure/cache/strategies.d.ts.map +1 -1
  230. package/dist/infrastructure/cache/strategies.js +1 -1
  231. package/dist/infrastructure/cache/strategies.js.map +1 -1
  232. package/dist/infrastructure/formatters/diff-generator.d.ts +11 -0
  233. package/dist/infrastructure/formatters/diff-generator.d.ts.map +1 -0
  234. package/dist/infrastructure/formatters/diff-generator.js +217 -0
  235. package/dist/infrastructure/formatters/diff-generator.js.map +1 -0
  236. package/dist/infrastructure/formatters/index.d.ts +11 -0
  237. package/dist/infrastructure/formatters/index.d.ts.map +1 -0
  238. package/dist/infrastructure/formatters/index.js +13 -0
  239. package/dist/infrastructure/formatters/index.js.map +1 -0
  240. package/dist/infrastructure/formatters/preview-converter.d.ts +85 -0
  241. package/dist/infrastructure/formatters/preview-converter.d.ts.map +1 -0
  242. package/dist/infrastructure/formatters/preview-converter.js +209 -0
  243. package/dist/infrastructure/formatters/preview-converter.js.map +1 -0
  244. package/dist/infrastructure/formatters/preview-formatter.d.ts +61 -0
  245. package/dist/infrastructure/formatters/preview-formatter.d.ts.map +1 -0
  246. package/dist/infrastructure/formatters/preview-formatter.js +208 -0
  247. package/dist/infrastructure/formatters/preview-formatter.js.map +1 -0
  248. package/dist/infrastructure/formatters/query-formatter.d.ts +63 -0
  249. package/dist/infrastructure/formatters/query-formatter.d.ts.map +1 -0
  250. package/dist/infrastructure/formatters/query-formatter.js +218 -0
  251. package/dist/infrastructure/formatters/query-formatter.js.map +1 -0
  252. package/dist/infrastructure/formatters/query-types.d.ts +165 -0
  253. package/dist/infrastructure/formatters/query-types.d.ts.map +1 -0
  254. package/dist/infrastructure/formatters/query-types.js +30 -0
  255. package/dist/infrastructure/formatters/query-types.js.map +1 -0
  256. package/dist/infrastructure/formatters/types.d.ts +153 -0
  257. package/dist/infrastructure/formatters/types.d.ts.map +1 -0
  258. package/dist/infrastructure/formatters/types.js +27 -0
  259. package/dist/infrastructure/formatters/types.js.map +1 -0
  260. package/dist/infrastructure/parser/base.d.ts +1 -1
  261. package/dist/infrastructure/parser/base.d.ts.map +1 -1
  262. package/dist/infrastructure/parser/base.js +1 -1
  263. package/dist/infrastructure/parser/base.js.map +1 -1
  264. package/dist/infrastructure/parser/factory.d.ts +3 -3
  265. package/dist/infrastructure/parser/factory.d.ts.map +1 -1
  266. package/dist/infrastructure/parser/factory.js +1 -1
  267. package/dist/infrastructure/parser/factory.js.map +1 -1
  268. package/dist/infrastructure/parser/interface.d.ts +1 -1
  269. package/dist/infrastructure/parser/interface.d.ts.map +1 -1
  270. package/dist/infrastructure/parser/registry.d.ts +1 -1
  271. package/dist/infrastructure/parser/registry.d.ts.map +1 -1
  272. package/dist/infrastructure/parser/registry.js +1 -1
  273. package/dist/infrastructure/parser/registry.js.map +1 -1
  274. package/dist/infrastructure/parser/types.d.ts.map +1 -1
  275. package/dist/infrastructure/storage/file-system.d.ts +2 -1
  276. package/dist/infrastructure/storage/file-system.d.ts.map +1 -1
  277. package/dist/infrastructure/storage/file-system.interface.d.ts +69 -0
  278. package/dist/infrastructure/storage/file-system.interface.d.ts.map +1 -0
  279. package/dist/infrastructure/storage/file-system.interface.js +6 -0
  280. package/dist/infrastructure/storage/file-system.interface.js.map +1 -0
  281. package/dist/infrastructure/storage/file-system.js.map +1 -1
  282. package/dist/infrastructure/storage/index.d.ts +2 -2
  283. package/dist/infrastructure/storage/index.d.ts.map +1 -1
  284. package/dist/infrastructure/storage/index.js +1 -6
  285. package/dist/infrastructure/storage/index.js.map +1 -1
  286. package/dist/infrastructure/storage/mem-file-system.d.ts +44 -0
  287. package/dist/infrastructure/storage/mem-file-system.d.ts.map +1 -0
  288. package/dist/infrastructure/storage/mem-file-system.js +105 -0
  289. package/dist/infrastructure/storage/mem-file-system.js.map +1 -0
  290. package/dist/infrastructure/storage/path-utils.d.ts +1 -1
  291. package/dist/infrastructure/storage/path-utils.d.ts.map +1 -1
  292. package/dist/interfaces/cli/cli.d.ts +13 -94
  293. package/dist/interfaces/cli/cli.d.ts.map +1 -1
  294. package/dist/interfaces/cli/cli.js +53 -2450
  295. package/dist/interfaces/cli/cli.js.map +1 -1
  296. package/dist/interfaces/cli/commands/analyze.command.d.ts +11 -0
  297. package/dist/interfaces/cli/commands/analyze.command.d.ts.map +1 -0
  298. package/dist/interfaces/cli/commands/analyze.command.js +460 -0
  299. package/dist/interfaces/cli/commands/analyze.command.js.map +1 -0
  300. package/dist/interfaces/cli/commands/change-signature.command.d.ts +11 -0
  301. package/dist/interfaces/cli/commands/change-signature.command.d.ts.map +1 -0
  302. package/dist/interfaces/cli/commands/change-signature.command.js +299 -0
  303. package/dist/interfaces/cli/commands/change-signature.command.js.map +1 -0
  304. package/dist/interfaces/cli/commands/deps.command.d.ts +11 -0
  305. package/dist/interfaces/cli/commands/deps.command.d.ts.map +1 -0
  306. package/dist/interfaces/cli/commands/deps.command.js +143 -0
  307. package/dist/interfaces/cli/commands/deps.command.js.map +1 -0
  308. package/dist/interfaces/cli/commands/extract.command.d.ts +11 -0
  309. package/dist/interfaces/cli/commands/extract.command.d.ts.map +1 -0
  310. package/dist/interfaces/cli/commands/extract.command.js +314 -0
  311. package/dist/interfaces/cli/commands/extract.command.js.map +1 -0
  312. package/dist/interfaces/cli/commands/index.d.ts +16 -0
  313. package/dist/interfaces/cli/commands/index.d.ts.map +1 -0
  314. package/dist/interfaces/cli/commands/index.js +15 -0
  315. package/dist/interfaces/cli/commands/index.js.map +1 -0
  316. package/dist/interfaces/cli/commands/inline.command.d.ts +11 -0
  317. package/dist/interfaces/cli/commands/inline.command.d.ts.map +1 -0
  318. package/dist/interfaces/cli/commands/inline.command.js +208 -0
  319. package/dist/interfaces/cli/commands/inline.command.js.map +1 -0
  320. package/dist/interfaces/cli/commands/move-member.command.d.ts +11 -0
  321. package/dist/interfaces/cli/commands/move-member.command.d.ts.map +1 -0
  322. package/dist/interfaces/cli/commands/move-member.command.js +194 -0
  323. package/dist/interfaces/cli/commands/move-member.command.js.map +1 -0
  324. package/dist/interfaces/cli/commands/move.command.d.ts +11 -0
  325. package/dist/interfaces/cli/commands/move.command.d.ts.map +1 -0
  326. package/dist/interfaces/cli/commands/move.command.js +220 -0
  327. package/dist/interfaces/cli/commands/move.command.js.map +1 -0
  328. package/dist/interfaces/cli/commands/refactor.command.d.ts +11 -0
  329. package/dist/interfaces/cli/commands/refactor.command.d.ts.map +1 -0
  330. package/dist/interfaces/cli/commands/refactor.command.js +414 -0
  331. package/dist/interfaces/cli/commands/refactor.command.js.map +1 -0
  332. package/dist/interfaces/cli/commands/rename.command.d.ts +11 -0
  333. package/dist/interfaces/cli/commands/rename.command.d.ts.map +1 -0
  334. package/dist/interfaces/cli/commands/rename.command.js +272 -0
  335. package/dist/interfaces/cli/commands/rename.command.js.map +1 -0
  336. package/dist/interfaces/cli/commands/search.command.d.ts +11 -0
  337. package/dist/interfaces/cli/commands/search.command.d.ts.map +1 -0
  338. package/dist/interfaces/cli/commands/search.command.js +409 -0
  339. package/dist/interfaces/cli/commands/search.command.js.map +1 -0
  340. package/dist/interfaces/cli/commands/shift.command.d.ts +11 -0
  341. package/dist/interfaces/cli/commands/shift.command.d.ts.map +1 -0
  342. package/dist/interfaces/cli/commands/shift.command.js +143 -0
  343. package/dist/interfaces/cli/commands/shift.command.js.map +1 -0
  344. package/dist/interfaces/cli/commands/snapshot.command.d.ts +11 -0
  345. package/dist/interfaces/cli/commands/snapshot.command.d.ts.map +1 -0
  346. package/dist/interfaces/cli/commands/snapshot.command.js +68 -0
  347. package/dist/interfaces/cli/commands/snapshot.command.js.map +1 -0
  348. package/dist/interfaces/cli/commands/types.d.ts +18 -0
  349. package/dist/interfaces/cli/commands/types.d.ts.map +1 -0
  350. package/dist/interfaces/cli/commands/types.js +5 -0
  351. package/dist/interfaces/cli/commands/types.js.map +1 -0
  352. package/dist/interfaces/cli/index.d.ts +1 -1
  353. package/dist/interfaces/cli/index.d.ts.map +1 -1
  354. package/dist/interfaces/cli/index.js +1 -1
  355. package/dist/interfaces/cli/index.js.map +1 -1
  356. package/dist/interfaces/cli/preview-output-handler.d.ts +53 -0
  357. package/dist/interfaces/cli/preview-output-handler.d.ts.map +1 -0
  358. package/dist/interfaces/cli/preview-output-handler.js +73 -0
  359. package/dist/interfaces/cli/preview-output-handler.js.map +1 -0
  360. package/dist/interfaces/cli/unified-output-handler.d.ts +77 -0
  361. package/dist/interfaces/cli/unified-output-handler.d.ts.map +1 -0
  362. package/dist/interfaces/cli/unified-output-handler.js +140 -0
  363. package/dist/interfaces/cli/unified-output-handler.js.map +1 -0
  364. package/dist/plugins/javascript/index.d.ts +2 -2
  365. package/dist/plugins/javascript/index.d.ts.map +1 -1
  366. package/dist/plugins/javascript/index.js +1 -1
  367. package/dist/plugins/javascript/index.js.map +1 -1
  368. package/dist/plugins/javascript/types.d.ts.map +1 -1
  369. package/dist/plugins/javascript/types.js.map +1 -1
  370. package/dist/plugins/python/analyzers/complexity-analyzer.d.ts +50 -0
  371. package/dist/plugins/python/analyzers/complexity-analyzer.d.ts.map +1 -0
  372. package/dist/plugins/python/analyzers/complexity-analyzer.js +172 -0
  373. package/dist/plugins/python/analyzers/complexity-analyzer.js.map +1 -0
  374. package/dist/plugins/python/analyzers/duplication-detector.d.ts +39 -0
  375. package/dist/plugins/python/analyzers/duplication-detector.d.ts.map +1 -0
  376. package/dist/plugins/python/analyzers/duplication-detector.js +146 -0
  377. package/dist/plugins/python/analyzers/duplication-detector.js.map +1 -0
  378. package/dist/plugins/python/analyzers/error-handling-checker.d.ts +28 -0
  379. package/dist/plugins/python/analyzers/error-handling-checker.d.ts.map +1 -0
  380. package/dist/plugins/python/analyzers/error-handling-checker.js +143 -0
  381. package/dist/plugins/python/analyzers/error-handling-checker.js.map +1 -0
  382. package/dist/plugins/python/analyzers/naming-checker.d.ts +56 -0
  383. package/dist/plugins/python/analyzers/naming-checker.d.ts.map +1 -0
  384. package/dist/plugins/python/analyzers/naming-checker.js +220 -0
  385. package/dist/plugins/python/analyzers/naming-checker.js.map +1 -0
  386. package/dist/plugins/python/analyzers/pattern-detector.d.ts +44 -0
  387. package/dist/plugins/python/analyzers/pattern-detector.d.ts.map +1 -0
  388. package/dist/plugins/python/analyzers/pattern-detector.js +222 -0
  389. package/dist/plugins/python/analyzers/pattern-detector.js.map +1 -0
  390. package/dist/plugins/python/analyzers/security-checker.d.ts +56 -0
  391. package/dist/plugins/python/analyzers/security-checker.d.ts.map +1 -0
  392. package/dist/plugins/python/analyzers/security-checker.js +279 -0
  393. package/dist/plugins/python/analyzers/security-checker.js.map +1 -0
  394. package/dist/plugins/python/analyzers/type-safety-checker.d.ts +44 -0
  395. package/dist/plugins/python/analyzers/type-safety-checker.d.ts.map +1 -0
  396. package/dist/plugins/python/analyzers/type-safety-checker.js +177 -0
  397. package/dist/plugins/python/analyzers/type-safety-checker.js.map +1 -0
  398. package/dist/plugins/python/analyzers/unused-symbol-detector.d.ts +37 -0
  399. package/dist/plugins/python/analyzers/unused-symbol-detector.d.ts.map +1 -0
  400. package/dist/plugins/python/analyzers/unused-symbol-detector.js +141 -0
  401. package/dist/plugins/python/analyzers/unused-symbol-detector.js.map +1 -0
  402. package/dist/plugins/python/dependency-analyzer.d.ts +86 -0
  403. package/dist/plugins/python/dependency-analyzer.d.ts.map +1 -0
  404. package/dist/plugins/python/dependency-analyzer.js +252 -0
  405. package/dist/plugins/python/dependency-analyzer.js.map +1 -0
  406. package/dist/plugins/python/index.d.ts +17 -0
  407. package/dist/plugins/python/index.d.ts.map +1 -0
  408. package/dist/plugins/python/index.js +23 -0
  409. package/dist/plugins/python/index.js.map +1 -0
  410. package/dist/plugins/python/parser.d.ts +150 -0
  411. package/dist/plugins/python/parser.d.ts.map +1 -0
  412. package/dist/plugins/python/parser.js +476 -0
  413. package/dist/plugins/python/parser.js.map +1 -0
  414. package/dist/plugins/python/symbol-extractor.d.ts +108 -0
  415. package/dist/plugins/python/symbol-extractor.d.ts.map +1 -0
  416. package/dist/plugins/python/symbol-extractor.js +389 -0
  417. package/dist/plugins/python/symbol-extractor.js.map +1 -0
  418. package/dist/plugins/python/tree-sitter-bridge.d.ts +57 -0
  419. package/dist/plugins/python/tree-sitter-bridge.d.ts.map +1 -0
  420. package/dist/plugins/python/tree-sitter-bridge.js +267 -0
  421. package/dist/plugins/python/tree-sitter-bridge.js.map +1 -0
  422. package/dist/plugins/python/types.d.ts +179 -0
  423. package/dist/plugins/python/types.d.ts.map +1 -0
  424. package/dist/plugins/python/types.js +252 -0
  425. package/dist/plugins/python/types.js.map +1 -0
  426. package/dist/plugins/swift/analyzers/complexity-analyzer.d.ts.map +1 -1
  427. package/dist/plugins/swift/analyzers/unused-symbol-detector.d.ts.map +1 -1
  428. package/dist/plugins/swift/dependency-analyzer.d.ts +1 -1
  429. package/dist/plugins/swift/dependency-analyzer.d.ts.map +1 -1
  430. package/dist/plugins/swift/dependency-analyzer.js +1 -1
  431. package/dist/plugins/swift/dependency-analyzer.js.map +1 -1
  432. package/dist/plugins/swift/parser.js +2 -2
  433. package/dist/plugins/swift/parser.js.map +1 -1
  434. package/dist/plugins/swift/symbol-extractor.d.ts +1 -1
  435. package/dist/plugins/swift/symbol-extractor.d.ts.map +1 -1
  436. package/dist/plugins/swift/symbol-extractor.js.map +1 -1
  437. package/dist/plugins/swift/types.d.ts.map +1 -1
  438. package/dist/plugins/swift/types.js.map +1 -1
  439. package/dist/plugins/typescript/analyzers/complexity-analyzer.d.ts +1 -1
  440. package/dist/plugins/typescript/analyzers/complexity-analyzer.d.ts.map +1 -1
  441. package/dist/plugins/typescript/analyzers/unused-symbol-detector.d.ts +1 -1
  442. package/dist/plugins/typescript/analyzers/unused-symbol-detector.d.ts.map +1 -1
  443. package/dist/plugins/typescript/analyzers/unused-symbol-detector.js.map +1 -1
  444. package/dist/plugins/typescript/parser.d.ts.map +1 -1
  445. package/dist/plugins/typescript/parser.js +12 -3
  446. package/dist/plugins/typescript/parser.js.map +1 -1
  447. package/dist/plugins/typescript/symbol-extractor.d.ts.map +1 -1
  448. package/dist/plugins/typescript/symbol-extractor.js +12 -0
  449. package/dist/plugins/typescript/symbol-extractor.js.map +1 -1
  450. package/dist/plugins/typescript/types.d.ts.map +1 -1
  451. package/dist/plugins/typescript/types.js +10 -0
  452. package/dist/plugins/typescript/types.js.map +1 -1
  453. package/dist/shared/errors/config-error.d.ts +1 -1
  454. package/dist/shared/errors/config-error.d.ts.map +1 -1
  455. package/dist/shared/errors/config-error.js +1 -1
  456. package/dist/shared/errors/config-error.js.map +1 -1
  457. package/dist/shared/errors/file-error.d.ts +1 -1
  458. package/dist/shared/errors/file-error.d.ts.map +1 -1
  459. package/dist/shared/errors/file-error.js +1 -1
  460. package/dist/shared/errors/file-error.js.map +1 -1
  461. package/dist/shared/errors/index.d.ts +1 -1
  462. package/dist/shared/errors/index.d.ts.map +1 -1
  463. package/dist/shared/errors/index.js +4 -4
  464. package/dist/shared/errors/index.js.map +1 -1
  465. package/dist/shared/errors/parser-error.d.ts +1 -1
  466. package/dist/shared/errors/parser-error.d.ts.map +1 -1
  467. package/dist/shared/errors/parser-error.js +1 -1
  468. package/dist/shared/errors/parser-error.js.map +1 -1
  469. package/dist/shared/errors/validation-error.d.ts +1 -1
  470. package/dist/shared/errors/validation-error.d.ts.map +1 -1
  471. package/dist/shared/errors/validation-error.js +1 -1
  472. package/dist/shared/errors/validation-error.js.map +1 -1
  473. package/dist/shared/types/ast.d.ts +1 -1
  474. package/dist/shared/types/ast.d.ts.map +1 -1
  475. package/dist/shared/types/ast.js +1 -1
  476. package/dist/shared/types/ast.js.map +1 -1
  477. package/dist/shared/types/symbol.d.ts +1 -1
  478. package/dist/shared/types/symbol.d.ts.map +1 -1
  479. package/dist/shared/utils/index.d.ts +5 -5
  480. package/dist/shared/utils/index.d.ts.map +1 -1
  481. package/dist/shared/utils/index.js +5 -5
  482. package/dist/shared/utils/index.js.map +1 -1
  483. package/package.json +8 -6
  484. package/dist/core/analysis/index.d.ts +0 -6
  485. package/dist/core/analysis/index.d.ts.map +0 -1
  486. package/dist/core/analysis/index.js +0 -7
  487. package/dist/core/analysis/index.js.map +0 -1
  488. package/dist/core/analysis/quality-metrics.d.ts +0 -158
  489. package/dist/core/analysis/quality-metrics.d.ts.map +0 -1
  490. package/dist/core/analysis/quality-metrics.js +0 -442
  491. package/dist/core/analysis/quality-metrics.js.map +0 -1
  492. package/dist/core/move/import-resolver.d.ts.map +0 -1
  493. package/dist/core/move/import-resolver.js.map +0 -1
  494. package/dist/core/move/index.d.ts.map +0 -1
  495. package/dist/core/move/index.js.map +0 -1
  496. package/dist/core/move/move-service.d.ts.map +0 -1
  497. package/dist/core/move/move-service.js.map +0 -1
  498. package/dist/core/move/types.d.ts.map +0 -1
  499. package/dist/core/move/types.js.map +0 -1
  500. package/dist/core/refactor/design-patterns.d.ts.map +0 -1
  501. package/dist/core/refactor/design-patterns.js.map +0 -1
  502. package/dist/core/refactor/extract-function.d.ts.map +0 -1
  503. package/dist/core/refactor/extract-function.js.map +0 -1
  504. package/dist/core/refactor/index.d.ts +0 -8
  505. package/dist/core/refactor/index.d.ts.map +0 -1
  506. package/dist/core/refactor/index.js +0 -11
  507. package/dist/core/refactor/index.js.map +0 -1
  508. package/dist/core/refactor/inline-function.d.ts.map +0 -1
  509. package/dist/core/refactor/inline-function.js.map +0 -1
  510. package/dist/core/refactor/swift-extractor.d.ts.map +0 -1
  511. package/dist/core/refactor/swift-extractor.js.map +0 -1
  512. package/dist/core/rename/index.d.ts +0 -9
  513. package/dist/core/rename/index.d.ts.map +0 -1
  514. package/dist/core/rename/index.js.map +0 -1
  515. package/dist/core/rename/reference-updater.d.ts.map +0 -1
  516. package/dist/core/rename/reference-updater.js.map +0 -1
  517. package/dist/core/rename/rename-engine.d.ts.map +0 -1
  518. package/dist/core/rename/rename-engine.js.map +0 -1
  519. package/dist/core/rename/scope-analyzer.d.ts.map +0 -1
  520. package/dist/core/rename/scope-analyzer.js.map +0 -1
  521. package/dist/core/rename/types.d.ts.map +0 -1
  522. package/dist/core/rename/types.js.map +0 -1
  523. package/dist/core/shit-score/grading.d.ts +0 -39
  524. package/dist/core/shit-score/grading.d.ts.map +0 -1
  525. package/dist/core/shit-score/grading.js +0 -253
  526. package/dist/core/shit-score/grading.js.map +0 -1
  527. package/dist/core/shit-score/index.d.ts +0 -9
  528. package/dist/core/shit-score/index.d.ts.map +0 -1
  529. package/dist/core/shit-score/index.js +0 -8
  530. package/dist/core/shit-score/index.js.map +0 -1
  531. package/dist/core/shit-score/score-calculator.d.ts +0 -75
  532. package/dist/core/shit-score/score-calculator.d.ts.map +0 -1
  533. package/dist/core/shit-score/score-calculator.js +0 -240
  534. package/dist/core/shit-score/score-calculator.js.map +0 -1
  535. package/dist/core/shit-score/shit-score-analyzer.d.ts +0 -84
  536. package/dist/core/shit-score/shit-score-analyzer.d.ts.map +0 -1
  537. package/dist/core/shit-score/shit-score-analyzer.js +0 -595
  538. package/dist/core/shit-score/shit-score-analyzer.js.map +0 -1
  539. package/dist/core/shit-score/types.d.ts +0 -231
  540. package/dist/core/shit-score/types.d.ts.map +0 -1
  541. package/dist/core/shit-score/types.js +0 -73
  542. package/dist/core/shit-score/types.js.map +0 -1
  543. package/dist/core/snapshot/code-compressor.d.ts +0 -39
  544. package/dist/core/snapshot/code-compressor.d.ts.map +0 -1
  545. package/dist/core/snapshot/code-compressor.js +0 -211
  546. package/dist/core/snapshot/code-compressor.js.map +0 -1
  547. package/dist/core/snapshot/config.d.ts +0 -60
  548. package/dist/core/snapshot/config.d.ts.map +0 -1
  549. package/dist/core/snapshot/config.js +0 -136
  550. package/dist/core/snapshot/config.js.map +0 -1
  551. package/dist/core/snapshot/snapshot-differ.d.ts +0 -54
  552. package/dist/core/snapshot/snapshot-differ.d.ts.map +0 -1
  553. package/dist/core/snapshot/snapshot-differ.js +0 -262
  554. package/dist/core/snapshot/snapshot-differ.js.map +0 -1
  555. package/dist/core/snapshot/snapshot-engine.d.ts +0 -94
  556. package/dist/core/snapshot/snapshot-engine.d.ts.map +0 -1
  557. package/dist/core/snapshot/snapshot-engine.js +0 -492
  558. package/dist/core/snapshot/snapshot-engine.js.map +0 -1
  559. package/dist/plugins/swift/analyzers/test-coverage-checker.d.ts +0 -26
  560. package/dist/plugins/swift/analyzers/test-coverage-checker.d.ts.map +0 -1
  561. package/dist/plugins/swift/analyzers/test-coverage-checker.js +0 -63
  562. package/dist/plugins/swift/analyzers/test-coverage-checker.js.map +0 -1
  563. package/dist/plugins/typescript/analyzers/test-coverage-checker.d.ts +0 -22
  564. package/dist/plugins/typescript/analyzers/test-coverage-checker.d.ts.map +0 -1
  565. package/dist/plugins/typescript/analyzers/test-coverage-checker.js +0 -62
  566. package/dist/plugins/typescript/analyzers/test-coverage-checker.js.map +0 -1
  567. /package/dist/core/{move → transform/location/move-file}/types.js +0 -0
  568. /package/dist/core/{refactor → transform/structure/extract}/extract-function.d.ts +0 -0
  569. /package/dist/core/{refactor → transform/structure/extract}/swift-extractor.d.ts +0 -0
  570. /package/dist/core/{refactor → transform/structure/extract}/swift-extractor.js +0 -0
  571. /package/dist/core/{refactor → transform/structure/inline}/inline-function.d.ts +0 -0
  572. /package/dist/core/{refactor → transform/structure/patterns}/design-patterns.d.ts +0 -0
  573. /package/dist/core/{refactor → transform/structure/patterns}/design-patterns.js +0 -0
  574. /package/dist/core/{rename → transform/symbol/rename}/reference-updater.js +0 -0
  575. /package/dist/core/{rename → transform/symbol/rename}/types.js +0 -0
@@ -3,20 +3,13 @@
3
3
  * 提供命令列介面來操作 Agent IDE 功能
4
4
  */
5
5
  import { Command } from 'commander';
6
- import { IndexEngine } from '../../core/indexing/index-engine.js';
7
- import { DependencyAnalyzer } from '../../core/dependency/dependency-analyzer.js';
8
- import { RenameEngine } from '../../core/rename/rename-engine.js';
9
- import { MoveService } from '../../core/move/index.js';
10
- import { SearchService } from '../../core/search/service.js';
11
- import { createIndexConfig } from '../../core/indexing/types.js';
12
6
  import { ParserRegistry } from '../../infrastructure/parser/registry.js';
13
7
  import { TypeScriptParser } from '../../plugins/typescript/parser.js';
14
8
  import { JavaScriptParser } from '../../plugins/javascript/parser.js';
15
9
  import { SwiftParser } from '../../plugins/swift/parser.js';
16
- import { ShitScoreAnalyzer } from '../../core/shit-score/shit-score-analyzer.js';
17
- import { SnapshotEngine, SnapshotDiffer, ConfigManager, CompressionLevel } from '../../core/snapshot/index.js';
18
- import { OutputFormatter, OutputFormat } from './output-formatter.js';
19
- import * as fs from 'fs/promises';
10
+ import { PythonParser } from '../../plugins/python/parser.js';
11
+ import { FileSystem } from '../../infrastructure/storage/index.js';
12
+ import { setupShiftCommand, setupMoveCommand, setupMoveMemberCommand, setupRenameCommand, setupChangeSignatureCommand, setupExtractCommand, setupInlineCommand, setupSearchCommand, setupAnalyzeCommand, setupDepsCommand, setupSnapshotCommand } from '../cli/commands/index.js';
20
13
  import { readFileSync } from 'fs';
21
14
  import * as path from 'path';
22
15
  import { fileURLToPath } from 'url';
@@ -34,17 +27,24 @@ catch {
34
27
  }
35
28
  export class AgentIdeCLI {
36
29
  program;
37
- indexEngine;
38
- dependencyAnalyzer;
39
- renameEngine;
40
- importResolver;
41
- moveService;
42
- searchService;
43
- constructor() {
30
+ fileSystem;
31
+ /**
32
+ * 建立 CLI 實例
33
+ * @param fileSystem - 檔案系統實例(可選,預設使用真實檔案系統)
34
+ */
35
+ constructor(fileSystem) {
44
36
  this.program = new Command();
37
+ // eslint-disable-next-line custom/no-default-instance-in-constructor -- CLI 入口點需要預設 FileSystem
38
+ this.fileSystem = fileSystem ?? new FileSystem();
45
39
  this.setupCommands();
46
40
  this.initializeParsers();
47
41
  }
42
+ /**
43
+ * 建立測試用 CLI 實例
44
+ */
45
+ static createForTesting(fileSystem) {
46
+ return new AgentIdeCLI(fileSystem);
47
+ }
48
48
  /**
49
49
  * 執行 CLI 程式
50
50
  */
@@ -106,6 +106,18 @@ export class AgentIdeCLI {
106
106
  console.debug('Swift parser loading failed:', swiftError);
107
107
  console.debug('Swift Parser initialization warning:', swiftError);
108
108
  }
109
+ // 嘗試註冊內建的 Python Parser
110
+ try {
111
+ const pythonParser = new PythonParser();
112
+ if (!registry.getParserByName('python')) {
113
+ registry.register(pythonParser);
114
+ }
115
+ }
116
+ catch (pythonError) {
117
+ // 如果 Python Parser 載入失敗,記錄錯誤
118
+ console.debug('Python parser loading failed:', pythonError);
119
+ console.debug('Python Parser initialization warning:', pythonError);
120
+ }
109
121
  }
110
122
  catch (error) {
111
123
  // 靜默處理初始化錯誤,避免影響 CLI 啟動
@@ -117,2441 +129,32 @@ export class AgentIdeCLI {
117
129
  .name('agent-ide')
118
130
  .description('程式碼智能工具集 for AI Agents')
119
131
  .version(packageVersion);
120
- this.setupIndexCommand();
121
- this.setupRenameCommand();
122
- this.setupRefactorCommand();
123
- this.setupMoveCommand();
124
- this.setupSearchCommand();
125
- this.setupAnalyzeCommand();
126
- this.setupDepsCommand();
127
- this.setupShitCommand();
128
- this.setupSnapshotCommand();
129
- this.setupPluginsCommand();
130
- }
131
- setupIndexCommand() {
132
- this.program
133
- .command('index')
134
- .description('建立或更新程式碼索引')
135
- .option('-p, --path <path>', '專案路徑', process.cwd())
136
- .option('-u, --update', '增量更新索引')
137
- .option('-e, --extensions <exts>', '包含的檔案副檔名', '.ts,.js,.tsx,.jsx,.swift')
138
- .option('-x, --exclude <patterns>', '排除模式', 'node_modules/**,*.test.*')
139
- .option('--format <format>', '輸出格式 (markdown|plain|json|minimal)', 'plain')
140
- .action(async (options) => {
141
- await this.handleIndexCommand(options);
142
- });
143
- }
144
- setupRenameCommand() {
145
- this.program
146
- .command('rename')
147
- .description('重新命名程式碼元素')
148
- .option('-t, --type <type>', '符號類型 (variable|function|class|interface)', 'variable')
149
- .option('-s, --symbol <name>', '要重新命名的符號')
150
- .option('-f, --from <name>', '原始名稱(--symbol 的別名)')
151
- .option('-n, --new-name <name>', '新名稱')
152
- .option('-o, --to <name>', '新名稱(--new-name 的別名)')
153
- .option('-p, --path <path>', '檔案或目錄路徑', '.')
154
- .option('--preview', '預覽變更而不執行')
155
- .option('--format <format>', '輸出格式 (markdown|plain|json|minimal)', 'plain')
156
- .action(async (options) => {
157
- await this.handleRenameCommand(options);
158
- });
159
- }
160
- setupRefactorCommand() {
161
- this.program
162
- .command('refactor <action>')
163
- .description('重構程式碼 (extract-function | extract-closure | inline-function)')
164
- .option('-f, --file <file>', '檔案路徑')
165
- .option('--path <path>', '檔案路徑(--file 的別名)')
166
- .option('-s, --start-line <line>', '起始行號')
167
- .option('-e, --end-line <line>', '結束行號')
168
- .option('-n, --function-name <name>', '函式名稱')
169
- .option('--new-name <name>', '新名稱(--function-name 的別名)')
170
- .option('-t, --target-file <file>', '目標檔案路徑(跨檔案提取)')
171
- .option('--preview', '預覽變更而不執行')
172
- .option('--format <format>', '輸出格式 (markdown|plain|json|minimal)', 'plain')
173
- .action(async (action, options) => {
174
- await this.handleRefactorCommand(action, options);
175
- });
176
- }
177
- setupMoveCommand() {
178
- this.program
179
- .command('move [source] [target]')
180
- .description('移動檔案或目錄')
181
- .option('-s, --source <path>', '來源路徑')
182
- .option('-t, --target <path>', '目標路徑')
183
- .option('--update-imports', '自動更新 import 路徑', true)
184
- .option('--preview', '預覽變更而不執行')
185
- .option('--format <format>', '輸出格式 (markdown|plain|json|minimal)', 'plain')
186
- .action(async (sourceArg, targetArg, options) => {
187
- // 支援兩種語法:
188
- // 1. move <source> <target> (位置參數)
189
- // 2. move --source <source> --target <target> (選項參數)
190
- const source = sourceArg || options.source;
191
- const target = targetArg || options.target;
192
- if (!source || !target) {
193
- console.error('❌ 必須指定來源和目標路徑');
194
- console.error(' 使用方式: agent-ide move <source> <target>');
195
- console.error(' 或: agent-ide move --source <source> --target <target>');
196
- if (process.env.NODE_ENV !== 'test') {
197
- process.exit(1);
198
- }
199
- return;
200
- }
201
- await this.handleMoveCommand(source, target, options);
202
- });
203
- }
204
- setupSearchCommand() {
205
- this.program
206
- .command('search')
207
- .description('搜尋程式碼')
208
- .argument('[query]', '搜尋查詢字串(簡化語法,等同於 text 搜尋)')
209
- .option('-t, --type <type>', '搜尋類型 (text|regex|fuzzy|symbol|function|class|protocol|variable|enum)', 'text')
210
- .option('-p, --path <path>', '搜尋路徑', '.')
211
- .option('-e, --extensions <exts>', '檔案副檔名', '.ts,.js,.tsx,.jsx,.swift')
212
- .option('-l, --limit <num>', '結果數量限制', '50')
213
- .option('-c, --context <lines>', '上下文行數', '2')
214
- .option('--case-sensitive', '大小寫敏感')
215
- .option('--case-insensitive', '大小寫不敏感')
216
- .option('--whole-word', '全字匹配')
217
- .option('--multiline', '多行匹配')
218
- .option('--include <patterns>', '包含模式')
219
- .option('--exclude <patterns>', '排除模式', 'node_modules/**,*.test.*')
220
- .option('--format <format>', '輸出格式 (list|json|minimal|summary)', 'list')
221
- .option('-q, --query <name>', '搜尋查詢字串')
222
- .option('--pattern <pattern>', '符號名稱模式(用於 structural 搜尋)')
223
- .option('--regex', '使用正則表達式')
224
- .option('--file-pattern <pattern>', '檔案模式過濾')
225
- .option('--with-attribute <attr>', '過濾帶有特定屬性的符號')
226
- .option('--with-modifier <mod>', '過濾帶有特定修飾符的符號')
227
- .option('--implements <protocol>', '過濾實作特定協定的類別')
228
- .option('--extends <class>', '過濾繼承特定類別的子類別')
229
- .action(async (queryOrSubcommand, options) => {
230
- // 支援三種語法:
231
- // 1. search <query> --path <path> (簡化語法,預設為 text 搜尋)
232
- // 2. search text --query <query> --path <path>
233
- // 3. search symbol --query <query> --path <path>
234
- // 4. search structural --type <type> --path <path>
235
- // 檢查空字串或未提供
236
- if (!queryOrSubcommand || queryOrSubcommand.trim() === '') {
237
- console.error('❌ 請提供搜尋查詢或子命令');
238
- console.error(' 使用方式: agent-ide search <query>');
239
- console.error(' 或: agent-ide search text --query <query>');
240
- console.error(' 或: agent-ide search symbol --query <query>');
241
- process.exitCode = 1;
242
- if (process.env.NODE_ENV !== 'test') {
243
- process.exit(1);
244
- }
245
- return;
246
- }
247
- // 判斷是子命令還是查詢字串
248
- const knownSubcommands = ['text', 'symbol', 'structural'];
249
- const isSubcommand = knownSubcommands.includes(queryOrSubcommand);
250
- if (isSubcommand) {
251
- // 使用子命令語法
252
- if (queryOrSubcommand === 'symbol') {
253
- await this.handleSymbolSearchCommand(options);
254
- }
255
- else if (queryOrSubcommand === 'text') {
256
- await this.handleTextSearchCommand(options);
257
- }
258
- else if (queryOrSubcommand === 'structural') {
259
- await this.handleStructuralSearchCommand(options);
260
- }
261
- }
262
- else {
263
- // 簡化語法:直接使用查詢字串
264
- await this.handleSearchCommand(queryOrSubcommand, options);
265
- }
266
- });
267
- }
268
- setupAnalyzeCommand() {
269
- this.program
270
- .command('analyze [type]')
271
- .description('分析程式碼品質')
272
- .option('-p, --path <path>', '分析路徑', '.')
273
- .option('--pattern <pattern>', '分析模式')
274
- .option('--format <format>', '輸出格式 (json|table|summary)', 'summary')
275
- .option('--all', '顯示所有掃描結果(預設只顯示有問題的項目)', false)
276
- .action(async (type, options) => {
277
- await this.handleAnalyzeCommand(type, options);
278
- });
279
- }
280
- setupDepsCommand() {
281
- this.program
282
- .command('deps [subcommand]')
283
- .description('分析依賴關係 (subcommand: graph|cycles|impact|orphans)')
284
- .option('-p, --path <path>', '分析路徑', '.')
285
- .option('-f, --file <file>', '特定檔案分析')
286
- .option('--format <format>', '輸出格式 (json|dot|summary)', 'summary')
287
- .option('--all', '顯示完整依賴圖(預設只顯示循環依賴和孤立檔案)', false)
288
- .action(async (subcommand, options) => {
289
- await this.handleDepsCommand(subcommand, options);
290
- });
291
- }
292
- setupShitCommand() {
293
- this.program
294
- .command('shit')
295
- .description('分析程式碼垃圾度(分數越高越糟糕)')
296
- .option('-p, --path <path>', '分析路徑', '.')
297
- .option('-d, --detailed', '顯示詳細資訊(topShit + recommendations)', false)
298
- .option('-t, --top <num>', '顯示前 N 個最糟項目', '10')
299
- .option('-m, --max-allowed <score>', '最大允許分數(超過則 exit 1)')
300
- .option('--format <format>', '輸出格式 (json|summary)', 'summary')
301
- .option('--show-files', '顯示問題檔案列表(detailedFiles)', false)
302
- .option('-o, --output <file>', '輸出到檔案')
303
- .action(async (options) => {
304
- await this.handleShitCommand(options);
305
- });
306
- }
307
- setupPluginsCommand() {
308
- const pluginsCmd = this.program
309
- .command('plugins')
310
- .description('管理 Parser 插件');
311
- pluginsCmd
312
- .command('list')
313
- .option('--enabled', '只顯示啟用的插件')
314
- .option('--disabled', '只顯示停用的插件')
315
- .description('列出所有插件')
316
- .action(async (options) => {
317
- await this.handlePluginsListCommand(options);
318
- });
319
- pluginsCmd
320
- .command('info <plugin>')
321
- .description('顯示插件資訊')
322
- .action(async (pluginName) => {
323
- await this.handlePluginInfoCommand(pluginName);
324
- });
325
- }
326
- setupSnapshotCommand() {
327
- this.program
328
- .command('snapshot [action]')
329
- .description('生成或管理程式碼快照')
330
- .option('-p, --path <path>', '專案路徑', process.cwd())
331
- .option('-o, --output <path>', '輸出檔案路徑')
332
- .option('-i, --incremental', '增量更新', false)
333
- .option('-l, --level <level>', '壓縮層級 (minimal|medium|full)', 'full')
334
- .option('--multi-level', '生成多層級快照', false)
335
- .option('--output-dir <dir>', '多層級輸出目錄', './snapshots')
336
- .option('--format <format>', '輸出格式 (json|summary)', 'summary')
337
- .option('--include-tests', '包含測試檔案', false)
338
- .action(async (action, options) => {
339
- await this.handleSnapshotCommand(action || 'generate', options);
340
- });
341
- }
342
- // Command handlers
343
- async handleIndexCommand(options) {
344
- const formatter = this.createFormatter(options.format);
345
- const startTime = Date.now();
346
- if (options.format !== 'json' && options.format !== 'minimal') {
347
- console.log(formatter.formatTitle('程式碼索引', 1));
348
- console.log('\n🔍 開始建立程式碼索引...\n');
349
- }
350
- try {
351
- const config = createIndexConfig(options.path, {
352
- includeExtensions: options.extensions.split(','),
353
- excludePatterns: options.exclude.split(',')
354
- });
355
- this.indexEngine = new IndexEngine(config);
356
- if (options.update) {
357
- if (options.format !== 'json' && options.format !== 'minimal') {
358
- console.log('📝 執行增量索引更新...');
359
- }
360
- }
361
- else {
362
- await this.indexEngine.indexProject(options.path);
363
- }
364
- const stats = await this.indexEngine.getStats();
365
- const duration = Date.now() - startTime;
366
- const statsData = {
367
- 檔案數: stats.totalFiles,
368
- 符號數: stats.totalSymbols,
369
- '執行時間(ms)': duration
370
- };
371
- if (options.format === 'json') {
372
- console.log(formatter.formatSuccess('索引完成', statsData));
373
- }
374
- else if (options.format === 'minimal') {
375
- console.log(`index:success files=${stats.totalFiles} symbols=${stats.totalSymbols} time=${duration}ms`);
376
- }
377
- else {
378
- console.log('\n' + formatter.formatSuccess('索引完成'));
379
- console.log('\n' + formatter.formatTitle('統計資訊', 2));
380
- console.log(formatter.formatStats(statsData));
381
- }
382
- }
383
- catch (error) {
384
- const errorMessage = error instanceof Error ? error.message : String(error);
385
- if (options.format === 'json') {
386
- console.error(formatter.formatError(errorMessage));
387
- }
388
- else if (options.format === 'minimal') {
389
- console.error(`index:error ${errorMessage}`);
390
- }
391
- else {
392
- console.error('\n' + formatter.formatError(`索引失敗: ${errorMessage}`));
393
- }
394
- if (process.env.NODE_ENV !== 'test') {
395
- process.exit(1);
396
- }
397
- }
398
- }
399
- async handleRenameCommand(options) {
400
- // 支援多種參數名稱
401
- const from = options.symbol || options.from;
402
- const to = options.newName || options.to;
403
- const isJsonFormat = options.format === 'json';
404
- if (!from || !to) {
405
- if (isJsonFormat) {
406
- console.error(JSON.stringify({ error: '必須指定符號名稱和新名稱' }));
407
- }
408
- else {
409
- console.error('❌ 必須指定符號名稱和新名稱');
410
- console.error(' 使用方式: agent-ide rename --symbol <name> --new-name <name>');
411
- }
412
- if (process.env.NODE_ENV !== 'test') {
413
- process.exit(1);
414
- }
415
- return;
416
- }
417
- if (!isJsonFormat) {
418
- console.log(`🔄 重新命名 ${from} → ${to}`);
419
- }
420
- try {
421
- let workspacePath = options.path || process.cwd();
422
- // 如果路徑指向檔案,取其所在目錄
423
- const stats = await fs.stat(workspacePath);
424
- if (stats.isFile()) {
425
- workspacePath = path.dirname(workspacePath);
426
- // 往上查找專案根目錄(包含 package.json、.git 等)
427
- let currentDir = workspacePath;
428
- while (currentDir !== path.dirname(currentDir)) {
429
- const hasPackageJson = await this.fileExists(path.join(currentDir, 'package.json'));
430
- const hasGit = await this.fileExists(path.join(currentDir, '.git'));
431
- const hasSwiftPackage = await this.fileExists(path.join(currentDir, 'Package.swift'));
432
- if (hasPackageJson || hasGit || hasSwiftPackage) {
433
- workspacePath = currentDir;
434
- break;
435
- }
436
- currentDir = path.dirname(currentDir);
437
- }
438
- }
439
- // 初始化索引引擎(每次都重新索引以確保資料是最新的)
440
- const config = createIndexConfig(workspacePath, {
441
- includeExtensions: ['.ts', '.tsx', '.js', '.jsx', '.swift'],
442
- excludePatterns: ['node_modules/**', '*.test.*']
443
- });
444
- this.indexEngine = new IndexEngine(config);
445
- await this.indexEngine.indexProject(workspacePath);
446
- // 初始化重新命名引擎
447
- if (!this.renameEngine) {
448
- this.renameEngine = new RenameEngine();
449
- }
450
- // 1. 查找符號
451
- if (!isJsonFormat) {
452
- console.log(`🔍 查找符號 "${from}"...`);
453
- }
454
- const searchResults = await this.indexEngine.findSymbol(from);
455
- if (searchResults.length === 0) {
456
- if (isJsonFormat) {
457
- console.error(JSON.stringify({ error: `找不到符號 "${from}"` }));
458
- }
459
- else {
460
- console.log(`❌ 找不到符號 "${from}"`);
461
- }
462
- process.exit(1);
463
- }
464
- if (searchResults.length > 1 && !isJsonFormat) {
465
- console.log('⚠️ 找到多個符號,使用第一個:');
466
- searchResults.forEach((result, index) => {
467
- console.log(` ${index + 1}. ${result.symbol.name} 在 ${result.symbol.location.filePath}:${result.symbol.location.range.start.line}`);
468
- });
469
- }
470
- const targetSymbol = searchResults[0].symbol;
471
- // 2. 預覽變更
472
- if (options.preview) {
473
- if (!isJsonFormat) {
474
- console.log('🔍 預覽變更...');
475
- }
476
- try {
477
- // 取得所有專案檔案以進行跨檔案引用查找
478
- // 使用 workspacePath(已解析為目錄)而不是 options.path(可能是檔案)
479
- const allProjectFiles = await this.getAllProjectFiles(workspacePath);
480
- const preview = await this.renameEngine.previewRename({
481
- symbol: targetSymbol,
482
- newName: to,
483
- filePaths: allProjectFiles
484
- });
485
- if (isJsonFormat) {
486
- console.log(JSON.stringify({
487
- preview: true,
488
- affectedFiles: preview.affectedFiles.length,
489
- operations: preview.operations.length,
490
- conflicts: preview.conflicts
491
- }, null, 2));
492
- }
493
- else {
494
- console.log('📝 預計變更:');
495
- console.log(` 檔案數: ${preview.affectedFiles.length}`);
496
- console.log(` 操作數: ${preview.operations.length}`);
497
- if (preview.conflicts.length > 0) {
498
- console.log('⚠️ 發現衝突:');
499
- preview.conflicts.forEach(conflict => {
500
- console.log(` - ${conflict.message}`);
501
- });
502
- }
503
- preview.operations.forEach(op => {
504
- console.log(` ${op.filePath}: "${op.oldText}" → "${op.newText}"`);
505
- });
506
- console.log('✅ 預覽完成');
507
- }
508
- return;
509
- }
510
- catch (previewError) {
511
- if (isJsonFormat) {
512
- console.error(JSON.stringify({ error: previewError instanceof Error ? previewError.message : String(previewError) }));
513
- }
514
- else {
515
- console.error('❌ 預覽失敗:', previewError instanceof Error ? previewError.message : previewError);
516
- }
517
- if (process.env.NODE_ENV !== 'test') {
518
- process.exit(1);
519
- }
520
- }
521
- }
522
- // 3. 執行重新命名(處理跨檔案引用)
523
- if (!isJsonFormat) {
524
- console.log('✏️ 執行重新命名...');
525
- }
526
- // 取得所有專案檔案(使用與 preview 相同的邏輯)
527
- // 使用 workspacePath(已解析為目錄)而不是 options.path(可能是檔案)
528
- const allProjectFiles = await this.getAllProjectFiles(workspacePath);
529
- // 使用 renameEngine 執行重新命名(與 preview 使用相同的引擎)
530
- const renameResult = await this.renameEngine.rename({
531
- symbol: targetSymbol,
532
- newName: to,
533
- filePaths: allProjectFiles
534
- });
535
- if (renameResult.success) {
536
- if (isJsonFormat) {
537
- console.log(JSON.stringify({
538
- success: true,
539
- affectedFiles: renameResult.affectedFiles.length,
540
- operations: renameResult.operations.length,
541
- files: renameResult.affectedFiles
542
- }, null, 2));
543
- }
544
- else {
545
- console.log('✅ 重新命名成功!');
546
- console.log(`📊 統計: ${renameResult.affectedFiles.length} 檔案, ${renameResult.operations.length} 變更`);
547
- renameResult.operations.forEach(operation => {
548
- console.log(` ✓ ${operation.filePath}: "${operation.oldText}" → "${operation.newText}"`);
549
- });
550
- }
551
- }
552
- else {
553
- if (isJsonFormat) {
554
- console.error(JSON.stringify({
555
- success: false,
556
- errors: renameResult.errors || ['重新命名失敗']
557
- }));
558
- }
559
- else {
560
- console.error('❌ 重新命名失敗:');
561
- renameResult.errors?.forEach(error => {
562
- console.error(` - ${error}`);
563
- });
564
- }
565
- if (process.env.NODE_ENV !== 'test') {
566
- process.exit(1);
567
- }
568
- }
569
- }
570
- catch (error) {
571
- if (isJsonFormat) {
572
- console.error(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
573
- }
574
- else {
575
- console.error('❌ 重新命名失敗:', error instanceof Error ? error.message : error);
576
- }
577
- if (process.env.NODE_ENV !== 'test') {
578
- process.exit(1);
579
- }
580
- }
581
- }
582
- async handleRefactorCommand(action, options) {
583
- // 支援 --path 作為 --file 的別名
584
- const fileOption = options.file || options.path;
585
- if (!fileOption) {
586
- console.error('❌ 必須指定 --file 或 --path 參數');
587
- process.exitCode = 1;
588
- if (process.env.NODE_ENV !== 'test') {
589
- process.exit(1);
590
- }
591
- return;
592
- }
593
- // 支援 --new-name 作為 --function-name 的別名
594
- const functionNameOption = options.functionName || options.newName;
595
- const isJsonFormat = options.format === 'json';
596
- if (!isJsonFormat) {
597
- console.log(`🔧 重構: ${action}`);
598
- }
599
- try {
600
- const filePath = path.resolve(fileOption);
601
- if (action === 'extract-function' || action === 'extract-closure') {
602
- if (!options.startLine || !options.endLine || !functionNameOption) {
603
- console.error(`❌ ${action} 缺少必要參數: --start-line, --end-line 和 --function-name (或 --new-name)`);
604
- process.exitCode = 1;
605
- if (process.env.NODE_ENV !== 'test') {
606
- process.exit(1);
607
- }
608
- return;
609
- }
610
- // 驗證行號範圍
611
- const startLine = parseInt(options.startLine);
612
- const endLine = parseInt(options.endLine);
613
- if (startLine > endLine) {
614
- console.error(`❌ 無效的行號範圍: 起始行號 (${startLine}) 大於結束行號 (${endLine})`);
615
- process.exitCode = 1;
616
- if (process.env.NODE_ENV !== 'test') {
617
- process.exit(1);
618
- }
619
- return;
620
- }
621
- // 讀取檔案內容
622
- const fs = await import('fs/promises');
623
- // 檢查檔案是否存在
624
- try {
625
- await fs.access(filePath);
626
- }
627
- catch {
628
- console.error(`❌ 找不到檔案: ${filePath}`);
629
- process.exitCode = 1;
630
- if (process.env.NODE_ENV !== 'test') {
631
- process.exit(1);
632
- }
633
- return;
634
- }
635
- const code = await fs.readFile(filePath, 'utf-8');
636
- // 建立範圍
637
- const range = {
638
- start: { line: startLine, column: 0 },
639
- end: { line: endLine, column: 0 }
640
- };
641
- // 檢測檔案類型
642
- const isSwift = filePath.endsWith('.swift');
643
- if (isSwift) {
644
- // 使用 Swift 提取器
645
- const { SwiftExtractor } = await import('../../core/refactor/swift-extractor.js');
646
- const extractor = new SwiftExtractor();
647
- const extractConfig = {
648
- functionName: functionNameOption,
649
- generateComments: true,
650
- preserveFormatting: true
651
- };
652
- const result = action === 'extract-closure'
653
- ? await extractor.extractClosure(code, range, extractConfig)
654
- : await extractor.extractFunction(code, range, extractConfig);
655
- if (result.success) {
656
- if (isJsonFormat) {
657
- console.log(JSON.stringify({
658
- success: true,
659
- extractedFunction: result.extractedFunction
660
- }, null, 2));
661
- }
662
- else {
663
- console.log('✅ 重構完成');
664
- console.log(`📝 提取的函式: ${result.extractedFunction.signature}`);
665
- }
666
- if (!options.preview) {
667
- await fs.writeFile(filePath, result.modifiedCode, 'utf-8');
668
- if (!isJsonFormat) {
669
- console.log(`✓ 已更新 ${filePath}`);
670
- }
671
- }
672
- else {
673
- if (!isJsonFormat) {
674
- console.log('預覽模式 - 未寫入檔案');
675
- }
676
- }
677
- }
678
- else {
679
- if (isJsonFormat) {
680
- console.error(JSON.stringify({ success: false, errors: result.errors }));
681
- }
682
- else {
683
- console.error('❌ 重構失敗:', result.errors.join(', '));
684
- }
685
- if (process.env.NODE_ENV !== 'test') {
686
- process.exit(1);
687
- }
688
- }
689
- return;
690
- }
691
- // TypeScript/JavaScript 提取器(原有邏輯)
692
- const { FunctionExtractor } = await import('../../core/refactor/extract-function.js');
693
- const extractor = new FunctionExtractor();
694
- // 執行提取
695
- const extractConfig = {
696
- functionName: functionNameOption,
697
- generateComments: true,
698
- preserveFormatting: true,
699
- validateExtraction: true,
700
- ...(options.targetFile ? {
701
- targetFile: path.resolve(options.targetFile),
702
- sourceFile: filePath
703
- } : {})
704
- };
705
- const result = await extractor.extract(code, range, extractConfig);
706
- if (result.success) {
707
- // 套用編輯(按正確順序)
708
- let modifiedCode = code;
709
- // 先處理所有 insert 類型(在檔案開頭插入函式定義)
710
- const insertEdits = result.edits.filter(e => e.type === 'insert');
711
- const replaceEdits = result.edits.filter(e => e.type === 'replace');
712
- // 先應用 replace(替換選取範圍為函式呼叫)
713
- for (const edit of replaceEdits) {
714
- modifiedCode = this.applyEditCorrectly(modifiedCode, edit);
715
- }
716
- // 再應用 insert(插入函式定義)
717
- for (const edit of insertEdits) {
718
- modifiedCode = this.applyEditCorrectly(modifiedCode, edit);
719
- }
720
- // 提取函式簽名(從修改後的程式碼中)
721
- const functionSignatureMatch = modifiedCode.match(new RegExp(`(async\\s+)?function\\s+${result.functionName}\\s*\\([^)]*\\)`));
722
- const functionSignature = functionSignatureMatch ? functionSignatureMatch[0] : `function ${result.functionName}`;
723
- console.log('✅ 重構完成');
724
- console.log(`📝 提取的函式: ${functionSignature}`);
725
- console.log(functionSignature);
726
- if (!options.preview) {
727
- // 寫入原始檔案
728
- await fs.writeFile(filePath, modifiedCode, 'utf-8');
729
- console.log(`✓ 已更新 ${filePath}`);
730
- // 如果是跨檔案提取,寫入目標檔案
731
- if (result.targetFileContent && options.targetFile) {
732
- const targetPath = path.resolve(options.targetFile);
733
- // 確保目標目錄存在
734
- const targetDir = path.dirname(targetPath);
735
- await fs.mkdir(targetDir, { recursive: true });
736
- // 寫入目標檔案
737
- await fs.writeFile(targetPath, result.targetFileContent, 'utf-8');
738
- console.log(`✓ 已建立/更新目標檔案 ${targetPath}`);
739
- if (result.importStatement) {
740
- console.log(`✓ 已加入 import: ${result.importStatement}`);
741
- }
742
- }
743
- }
744
- else {
745
- console.log('\n🔍 預覽模式 - 未寫入檔案');
746
- console.log(`📊 參數: ${result.parameters.map(p => p.name).join(', ')}`);
747
- if (result.targetFileContent && options.targetFile) {
748
- console.log(`📁 目標檔案: ${options.targetFile}`);
749
- console.log(`📥 Import: ${result.importStatement || '(無)'}`);
750
- }
751
- }
752
- }
753
- else {
754
- console.error('❌ 重構失敗:', result.errors.join(', '));
755
- process.exitCode = 1;
756
- if (process.env.NODE_ENV !== 'test') {
757
- process.exit(1);
758
- }
759
- }
760
- }
761
- else if (action === 'inline-function') {
762
- console.error('❌ inline-function 尚未實作');
763
- process.exitCode = 1;
764
- if (process.env.NODE_ENV !== 'test') {
765
- process.exit(1);
766
- }
767
- }
768
- else {
769
- console.error(`❌ 未知的重構操作: ${action}`);
770
- process.exitCode = 1;
771
- if (process.env.NODE_ENV !== 'test') {
772
- process.exit(1);
773
- }
774
- }
775
- }
776
- catch (error) {
777
- console.error('❌ 重構失敗:', error instanceof Error ? error.message : error);
778
- process.exitCode = 1;
779
- if (process.env.NODE_ENV !== 'test') {
780
- process.exit(1);
781
- }
782
- }
783
- }
784
- async handleMoveCommand(source, target, options) {
785
- const isJsonFormat = options.format === 'json';
786
- if (!isJsonFormat) {
787
- console.log(`📦 移動 ${source} → ${target}`);
788
- }
789
- try {
790
- // 檢查源檔案是否存在
791
- const sourceExists = await this.fileExists(source);
792
- if (!sourceExists) {
793
- const errorMsg = `源檔案找不到: ${source}`;
794
- if (isJsonFormat) {
795
- console.log(JSON.stringify({
796
- success: false,
797
- error: errorMsg
798
- }, null, 2));
799
- }
800
- else {
801
- console.log(`❌ 移動失敗: ${errorMsg}`);
802
- }
803
- process.exitCode = 1;
804
- if (process.env.NODE_ENV !== 'test') {
805
- process.exit(1);
806
- }
807
- return;
808
- }
809
- // 檢查源和目標是否相同
810
- const normalizedSource = path.resolve(source);
811
- const normalizedTarget = path.resolve(target);
812
- if (normalizedSource === normalizedTarget) {
813
- // 源和目標相同時,視為 no-op,成功返回
814
- const message = 'Source and target are identical. No changes made.';
815
- if (isJsonFormat) {
816
- console.log(JSON.stringify({
817
- success: true,
818
- message,
819
- changes: []
820
- }, null, 2));
821
- }
822
- else {
823
- console.log(`✓ ${message}`);
824
- }
825
- return;
826
- }
827
- // 初始化移動服務
828
- if (!this.moveService) {
829
- // 讀取 tsconfig.json 路徑別名
830
- const pathAliases = await this.loadPathAliases(process.cwd());
831
- this.moveService = new MoveService({
832
- pathAliases,
833
- supportedExtensions: ['.ts', '.tsx', '.js', '.jsx', '.vue', '.swift'],
834
- includeNodeModules: false
835
- });
836
- }
837
- const moveOperation = {
838
- source: normalizedSource,
839
- target: normalizedTarget,
840
- updateImports: options.updateImports
841
- };
842
- const moveOptions = {
843
- preview: options.preview,
844
- projectRoot: process.cwd()
845
- };
846
- // 執行移動操作
847
- const result = await this.moveService.moveFile(moveOperation, moveOptions);
848
- if (result.success) {
849
- if (isJsonFormat) {
850
- console.log(JSON.stringify({
851
- moved: result.moved,
852
- affectedFiles: result.pathUpdates.length,
853
- pathUpdates: result.pathUpdates
854
- }, null, 2));
855
- }
856
- else {
857
- if (options.preview) {
858
- console.log('🔍 預覽移動操作:');
859
- }
860
- else {
861
- console.log('✅ 移動成功!');
862
- }
863
- console.log(`📊 統計: ${result.pathUpdates.length} 個 import 需要更新`);
864
- if (result.pathUpdates.length > 0) {
865
- console.log('📝 影響的檔案:');
866
- const fileGroups = new Map();
867
- result.pathUpdates.forEach(update => {
868
- if (!fileGroups.has(update.filePath)) {
869
- fileGroups.set(update.filePath, []);
870
- }
871
- fileGroups.get(update.filePath).push(update);
872
- });
873
- for (const [filePath, updates] of fileGroups) {
874
- console.log(` 📄 ${path.relative(process.cwd(), filePath)}:`);
875
- updates.forEach(update => {
876
- console.log(` 第 ${update.line} 行: "${path.basename(source)}" → "${path.basename(target)}"`);
877
- });
878
- }
879
- }
880
- }
881
- }
882
- else {
883
- if (isJsonFormat) {
884
- console.log(JSON.stringify({
885
- success: false,
886
- error: result.error
887
- }, null, 2));
888
- }
889
- else {
890
- console.error('❌ 移動失敗:', result.error);
891
- }
892
- process.exitCode = 1;
893
- if (process.env.NODE_ENV !== 'test') {
894
- process.exit(1);
895
- }
896
- }
897
- }
898
- catch (error) {
899
- const errorMsg = error instanceof Error ? error.message : String(error);
900
- if (isJsonFormat) {
901
- console.log(JSON.stringify({
902
- success: false,
903
- error: errorMsg
904
- }, null, 2));
905
- }
906
- else {
907
- console.error('❌ 移動失敗:', errorMsg);
908
- }
909
- process.exitCode = 1;
910
- if (process.env.NODE_ENV !== 'test') {
911
- process.exit(1);
912
- }
913
- }
914
- }
915
- async handleSearchCommand(query, options) {
916
- const isMinimalOrJson = options.format === 'minimal' || options.format === 'json';
917
- if (!isMinimalOrJson) {
918
- console.log(`🔍 搜尋: "${query}"`);
919
- }
920
- try {
921
- // 初始化搜尋服務
922
- if (!this.searchService) {
923
- this.searchService = new SearchService();
924
- }
925
- // 建構搜尋選項
926
- const searchOptions = this.buildSearchOptions(options);
927
- // 根據搜尋類型建立查詢
928
- const searchQuery = {
929
- type: 'text',
930
- query,
931
- options: searchOptions
932
- };
933
- // 執行搜尋
934
- const startTime = Date.now();
935
- const result = await this.searchService.searchText(searchQuery);
936
- const searchTime = Date.now() - startTime;
937
- // 顯示結果
938
- if (result.matches.length === 0) {
939
- if (!isMinimalOrJson) {
940
- console.log('📝 沒有找到匹配結果');
941
- }
942
- else if (options.format === 'json') {
943
- // JSON 格式輸出空結果
944
- console.log(JSON.stringify({ results: [] }, null, 2));
945
- }
946
- return;
947
- }
948
- if (!isMinimalOrJson) {
949
- console.log(`✅ 找到 ${result.matches.length} 個結果 (${searchTime}ms)`);
950
- if (result.truncated) {
951
- console.log(`⚠️ 結果已截斷,顯示前 ${options.limit} 個結果`);
952
- }
953
- }
954
- // 格式化輸出
955
- this.formatSearchResults(result, options);
956
- }
957
- catch (error) {
958
- if (isMinimalOrJson) {
959
- // 對於 minimal 和 json 格式,輸出空結果或錯誤
960
- if (options.format === 'json') {
961
- console.log(JSON.stringify({ matches: [], error: error instanceof Error ? error.message : String(error) }));
962
- }
963
- else {
964
- console.error(`Error: ${error instanceof Error ? error.message : error}`);
965
- }
966
- }
967
- else {
968
- console.error('❌ 搜尋失敗:', error instanceof Error ? error.message : error);
969
- }
970
- // 測試環境不 exit
971
- if (process.env.NODE_ENV !== 'test') {
972
- if (process.env.NODE_ENV !== 'test') {
973
- process.exit(1);
974
- }
975
- }
976
- }
977
- }
978
- /**
979
- * 建構搜尋選項
980
- */
981
- buildSearchOptions(options) {
982
- let includeFiles = options.include ? options.include.split(',') : undefined;
983
- const excludeFiles = options.exclude ? options.exclude.split(',') : undefined;
984
- // --file-pattern 參數轉換為 includeFiles
985
- if (options.filePattern) {
986
- includeFiles = [options.filePattern];
987
- }
132
+ const context = this.createCommandContext();
133
+ // Transform 群組
134
+ const transformCmd = this.program
135
+ .command('transform')
136
+ .description('程式碼變換工具');
137
+ // 直接掛載到 transform(無中間層)
138
+ setupRenameCommand(transformCmd, context);
139
+ setupChangeSignatureCommand(transformCmd, context);
140
+ setupMoveCommand(transformCmd, context);
141
+ setupMoveMemberCommand(transformCmd, context);
142
+ setupShiftCommand(transformCmd, context);
143
+ setupExtractCommand(transformCmd, context);
144
+ setupInlineCommand(transformCmd, context);
145
+ // Query 命令(扁平式)
146
+ setupSearchCommand(this.program, context);
147
+ setupAnalyzeCommand(this.program, context);
148
+ setupDepsCommand(this.program, context);
149
+ setupSnapshotCommand(this.program, context);
150
+ }
151
+ /**
152
+ * 建立命令共用 Context
153
+ */
154
+ createCommandContext() {
988
155
  return {
989
- scope: {
990
- type: 'directory',
991
- path: path.resolve(options.path),
992
- recursive: true
993
- },
994
- maxResults: parseInt(options.limit),
995
- caseSensitive: options.caseInsensitive ? false : (options.caseSensitive || false),
996
- wholeWord: options.wholeWord || false,
997
- regex: options.regex || options.type === 'regex',
998
- fuzzy: options.type === 'fuzzy',
999
- multiline: options.multiline || false,
1000
- showContext: options.context > 0,
1001
- contextLines: parseInt(options.context),
1002
- includeFiles,
1003
- excludeFiles,
1004
- timeout: 30000
156
+ fileSystem: this.fileSystem
1005
157
  };
1006
158
  }
1007
- /**
1008
- * 格式化搜尋結果輸出
1009
- */
1010
- formatSearchResults(result, options) {
1011
- switch (options.format) {
1012
- case 'json':
1013
- // 測試期望的格式是 { results: [...] } 而不是 { matches: [...] }
1014
- // 將絕對路徑轉換為相對路徑,並增加 contextBefore/contextAfter
1015
- const resultsWithRelativePaths = result.matches.map((match) => {
1016
- const formatted = {
1017
- ...match,
1018
- filePath: this.formatFilePath(match.file)
1019
- };
1020
- // 移除 'file'
1021
- delete formatted.file;
1022
- // 增加 contextBefore/contextAfter(測試需要這些欄位)
1023
- if (match.context) {
1024
- formatted.contextBefore = match.context.before || [];
1025
- formatted.contextAfter = match.context.after || [];
1026
- }
1027
- return formatted;
1028
- });
1029
- console.log(JSON.stringify({ results: resultsWithRelativePaths }, null, 2));
1030
- break;
1031
- case 'minimal':
1032
- // AI Agent 友善的最小輸出
1033
- result.matches.forEach((match) => {
1034
- console.log(`${match.file}:${match.line}:${match.column}:${match.content.trim()}`);
1035
- });
1036
- break;
1037
- case 'list':
1038
- default:
1039
- result.matches.forEach((match, index) => {
1040
- console.log(`\n${index + 1}. ${this.formatFilePath(match.file)}:${match.line}:${match.column}`);
1041
- console.log(` ${this.highlightMatch(match.content, options.query)}`);
1042
- // 顯示上下文
1043
- if (options.context > 0 && match.context) {
1044
- if (match.context.before.length > 0) {
1045
- match.context.before.forEach((line, i) => {
1046
- const lineNum = match.line - match.context.before.length + i;
1047
- console.log(` ${lineNum.toString().padStart(3, ' ')}: ${line}`);
1048
- });
1049
- }
1050
- console.log(`>> ${match.line.toString().padStart(3, ' ')}: ${this.highlightMatch(match.content, options.query)}`);
1051
- if (match.context.after.length > 0) {
1052
- match.context.after.forEach((line, i) => {
1053
- const lineNum = match.line + i + 1;
1054
- console.log(` ${lineNum.toString().padStart(3, ' ')}: ${line}`);
1055
- });
1056
- }
1057
- }
1058
- });
1059
- break;
1060
- }
1061
- }
1062
- /**
1063
- * 格式化檔案路徑(顯示相對路徑)
1064
- */
1065
- formatFilePath(filePath) {
1066
- const cwd = process.cwd();
1067
- const relativePath = path.relative(cwd, filePath);
1068
- return relativePath.startsWith('..') ? filePath : relativePath;
1069
- }
1070
- /**
1071
- * 高亮匹配內容
1072
- */
1073
- highlightMatch(text, query) {
1074
- if (!text || !query) {
1075
- return text;
1076
- }
1077
- // 簡單的高亮實作
1078
- try {
1079
- const regex = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
1080
- return text.replace(regex, `[${query}]`);
1081
- }
1082
- catch {
1083
- return text;
1084
- }
1085
- }
1086
- /**
1087
- * 處理文字搜尋命令
1088
- */
1089
- async handleTextSearchCommand(options) {
1090
- const query = options.query;
1091
- if (!query) {
1092
- console.error('❌ 文字搜尋需要指定 --query 參數');
1093
- console.error(' 使用方式: agent-ide search text --query <text>');
1094
- if (process.env.NODE_ENV !== 'test') {
1095
- process.exit(1);
1096
- }
1097
- return;
1098
- }
1099
- // 使用既有的 handleSearchCommand 邏輯
1100
- await this.handleSearchCommand(query, options);
1101
- }
1102
- /**
1103
- * 處理結構化搜尋命令
1104
- */
1105
- async handleStructuralSearchCommand(options) {
1106
- const pattern = options.pattern;
1107
- const type = options.type;
1108
- if (!type) {
1109
- console.error('❌ 結構化搜尋需要指定 --type 參數');
1110
- console.error(' 使用方式: agent-ide search structural --type <class|protocol|function|...> [--pattern <pattern>]');
1111
- if (process.env.NODE_ENV !== 'test') {
1112
- process.exit(1);
1113
- }
1114
- return;
1115
- }
1116
- const isMinimalOrJson = options.format === 'minimal' || options.format === 'json';
1117
- if (!isMinimalOrJson) {
1118
- console.log(`🔍 結構化搜尋: ${type}${pattern ? ` (pattern: ${pattern})` : ''}`);
1119
- }
1120
- try {
1121
- const searchPath = path.resolve(options.path || process.cwd());
1122
- // 初始化索引引擎
1123
- const config = createIndexConfig(searchPath, {
1124
- includeExtensions: ['.ts', '.tsx', '.js', '.jsx', '.swift'],
1125
- excludePatterns: ['node_modules/**', '*.test.*', 'dist/**']
1126
- });
1127
- this.indexEngine = new IndexEngine(config);
1128
- // 建立索引
1129
- if (!isMinimalOrJson) {
1130
- console.log('📝 正在建立索引...');
1131
- }
1132
- await this.indexEngine.indexProject(searchPath);
1133
- // 獲取所有符號
1134
- const allSymbols = await this.indexEngine.getAllSymbols();
1135
- // 過濾符號
1136
- let filteredSymbols = allSymbols.filter(symbolResult => {
1137
- const symbol = symbolResult.symbol;
1138
- // 1. 過濾檔案模式
1139
- if (options.filePattern) {
1140
- const regex = new RegExp(options.filePattern.replace(/\*/g, '.*').replace(/\?/g, '.'));
1141
- if (!regex.test(symbol.location.filePath)) {
1142
- return false;
1143
- }
1144
- }
1145
- // 2. 過濾符號類型
1146
- if (type) {
1147
- if (symbol.type !== type) {
1148
- return false;
1149
- }
1150
- }
1151
- // 3. 過濾符號名稱模式
1152
- if (pattern) {
1153
- const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.'));
1154
- if (!regex.test(symbol.name)) {
1155
- return false;
1156
- }
1157
- }
1158
- // 4. 過濾屬性
1159
- if (options.withAttribute) {
1160
- if (!symbol.attributes || !symbol.attributes.includes(options.withAttribute)) {
1161
- return false;
1162
- }
1163
- }
1164
- // 5. 過濾修飾符
1165
- if (options.withModifier) {
1166
- if (!symbol.modifiers || !symbol.modifiers.includes(options.withModifier)) {
1167
- return false;
1168
- }
1169
- }
1170
- // 6. 過濾實作的協定
1171
- if (options.implements) {
1172
- if (!symbol.implements || !symbol.implements.includes(options.implements)) {
1173
- return false;
1174
- }
1175
- }
1176
- // 7. 過濾繼承的類別
1177
- if (options.extends) {
1178
- if (symbol.superclass !== options.extends) {
1179
- return false;
1180
- }
1181
- }
1182
- return true;
1183
- });
1184
- // 應用 limit
1185
- const limit = options.limit ? parseInt(options.limit) : 50;
1186
- if (filteredSymbols.length > limit) {
1187
- filteredSymbols = filteredSymbols.slice(0, limit);
1188
- }
1189
- if (filteredSymbols.length === 0) {
1190
- if (options.format === 'json') {
1191
- console.log(JSON.stringify({ results: [] }, null, 2));
1192
- }
1193
- else if (!isMinimalOrJson) {
1194
- console.log('📝 沒有找到符合條件的符號');
1195
- }
1196
- return;
1197
- }
1198
- if (!isMinimalOrJson && options.format !== 'summary') {
1199
- console.log(`✅ 找到 ${filteredSymbols.length} 個符號`);
1200
- }
1201
- // 格式化輸出
1202
- this.formatSymbolSearchResults(filteredSymbols, options);
1203
- }
1204
- catch (error) {
1205
- if (isMinimalOrJson) {
1206
- if (options.format === 'json') {
1207
- console.log(JSON.stringify({
1208
- results: [],
1209
- error: error instanceof Error ? error.message : String(error)
1210
- }));
1211
- }
1212
- else {
1213
- console.error(`Error: ${error instanceof Error ? error.message : error}`);
1214
- }
1215
- }
1216
- else {
1217
- console.error('❌ 結構化搜尋失敗:', error instanceof Error ? error.message : error);
1218
- }
1219
- if (process.env.NODE_ENV !== 'test') {
1220
- process.exit(1);
1221
- }
1222
- }
1223
- }
1224
- /**
1225
- * 處理符號搜尋命令
1226
- */
1227
- async handleSymbolSearchCommand(options) {
1228
- const symbolName = options.query;
1229
- if (!symbolName) {
1230
- console.error('❌ 符號搜尋需要指定 --query 參數');
1231
- console.error(' 使用方式: agent-ide search symbol --query <name>');
1232
- if (process.env.NODE_ENV !== 'test') {
1233
- process.exit(1);
1234
- }
1235
- return;
1236
- }
1237
- const isMinimalOrJson = options.format === 'minimal' || options.format === 'json';
1238
- if (!isMinimalOrJson) {
1239
- console.log(`🔍 搜尋符號: "${symbolName}"`);
1240
- }
1241
- try {
1242
- const searchPath = path.resolve(options.path || process.cwd());
1243
- // 初始化索引引擎(每次都重新建立以確保索引是最新的)
1244
- const config = createIndexConfig(searchPath, {
1245
- includeExtensions: ['.ts', '.tsx', '.js', '.jsx', '.swift'],
1246
- excludePatterns: ['node_modules/**', '*.test.*', 'dist/**']
1247
- });
1248
- this.indexEngine = new IndexEngine(config);
1249
- // 建立索引
1250
- if (!isMinimalOrJson) {
1251
- console.log('📝 正在建立索引...');
1252
- }
1253
- await this.indexEngine.indexProject(searchPath);
1254
- // 搜尋符號:如果包含 wildcard,使用模式搜尋
1255
- let results;
1256
- if (symbolName.includes('*') || symbolName.includes('?')) {
1257
- // Wildcard 模式搜尋
1258
- const allSymbols = await this.indexEngine.getAllSymbols();
1259
- const pattern = symbolName
1260
- .replace(/\*/g, '.*')
1261
- .replace(/\?/g, '.');
1262
- const regex = new RegExp(`^${pattern}$`);
1263
- results = allSymbols.filter(result => regex.test(result.symbol.name));
1264
- // 應用 limit
1265
- const limit = options.limit ? parseInt(options.limit) : 50;
1266
- if (results.length > limit) {
1267
- results = results.slice(0, limit);
1268
- }
1269
- }
1270
- else {
1271
- // 精確名稱搜尋
1272
- results = await this.indexEngine.findSymbol(symbolName);
1273
- }
1274
- if (results.length === 0) {
1275
- if (options.format === 'json') {
1276
- console.log(JSON.stringify({ results: [] }, null, 2));
1277
- }
1278
- else if (!isMinimalOrJson) {
1279
- console.log(`📝 找不到符號 "${symbolName}"`);
1280
- }
1281
- return;
1282
- }
1283
- if (!isMinimalOrJson) {
1284
- console.log(`✅ 找到 ${results.length} 個符號`);
1285
- }
1286
- // 格式化輸出
1287
- this.formatSymbolSearchResults(results, options);
1288
- }
1289
- catch (error) {
1290
- if (isMinimalOrJson) {
1291
- if (options.format === 'json') {
1292
- console.log(JSON.stringify({
1293
- results: [],
1294
- error: error instanceof Error ? error.message : String(error)
1295
- }));
1296
- }
1297
- else {
1298
- console.error(`Error: ${error instanceof Error ? error.message : error}`);
1299
- }
1300
- }
1301
- else {
1302
- console.error('❌ 符號搜尋失敗:', error instanceof Error ? error.message : error);
1303
- }
1304
- if (process.env.NODE_ENV !== 'test') {
1305
- process.exit(1);
1306
- }
1307
- }
1308
- }
1309
- /**
1310
- * 格式化符號搜尋結果輸出
1311
- */
1312
- formatSymbolSearchResults(results, options) {
1313
- switch (options.format) {
1314
- case 'json':
1315
- // 轉換為測試期望的格式
1316
- const formattedResults = results.map(result => {
1317
- const formatted = {
1318
- name: result.symbol.name,
1319
- type: result.symbol.type,
1320
- filePath: this.formatFilePath(result.symbol.location.filePath),
1321
- line: result.symbol.location.range.start.line,
1322
- column: result.symbol.location.range.start.column
1323
- };
1324
- // 只在有值時才加入可選欄位
1325
- if (result.symbol.attributes && result.symbol.attributes.length > 0) {
1326
- formatted.attributes = result.symbol.attributes;
1327
- }
1328
- if (result.symbol.modifiers && result.symbol.modifiers.length > 0) {
1329
- formatted.modifiers = result.symbol.modifiers;
1330
- }
1331
- if (result.symbol.superclass) {
1332
- formatted.superclass = result.symbol.superclass;
1333
- }
1334
- if (result.symbol.implements && result.symbol.implements.length > 0) {
1335
- formatted.implements = result.symbol.implements;
1336
- }
1337
- return formatted;
1338
- });
1339
- console.log(JSON.stringify({ results: formattedResults }, null, 2));
1340
- break;
1341
- case 'minimal':
1342
- results.forEach(result => {
1343
- const symbol = result.symbol;
1344
- console.log(`${symbol.location.filePath}:${symbol.location.range.start.line}:${symbol.location.range.start.column}:${symbol.type}:${symbol.name}`);
1345
- });
1346
- break;
1347
- case 'list':
1348
- default:
1349
- results.forEach((result, index) => {
1350
- const symbol = result.symbol;
1351
- console.log(`\n${index + 1}. ${symbol.name} (${symbol.type})`);
1352
- console.log(` ${this.formatFilePath(symbol.location.filePath)}:${symbol.location.range.start.line}:${symbol.location.range.start.column}`);
1353
- if (symbol.attributes && symbol.attributes.length > 0) {
1354
- console.log(` 屬性: ${symbol.attributes.join(', ')}`);
1355
- }
1356
- if (symbol.modifiers && symbol.modifiers.length > 0) {
1357
- console.log(` 修飾符: ${symbol.modifiers.join(', ')}`);
1358
- }
1359
- });
1360
- break;
1361
- }
1362
- }
1363
- async handleAnalyzeCommand(type, options) {
1364
- const analyzeType = type || 'complexity';
1365
- if (options.format !== 'json') {
1366
- console.log('📊 分析程式碼品質...');
1367
- }
1368
- try {
1369
- const analyzePath = options.path || process.cwd();
1370
- // 根據分析類型執行對應分析
1371
- if (analyzeType === 'complexity') {
1372
- // 使用 ParserPlugin 分析複雜度
1373
- const registry = ParserRegistry.getInstance();
1374
- const files = await this.getAllProjectFiles(analyzePath);
1375
- const results = [];
1376
- for (const file of files) {
1377
- try {
1378
- const parser = registry.getParser(path.extname(file));
1379
- if (!parser) {
1380
- continue;
1381
- }
1382
- const content = await fs.readFile(file, 'utf-8');
1383
- const ast = await parser.parse(content, file);
1384
- const complexity = await parser.analyzeComplexity(content, ast);
1385
- results.push({ file, complexity });
1386
- }
1387
- catch {
1388
- // 忽略無法分析的檔案
1389
- }
1390
- }
1391
- // 過濾高複雜度檔案(evaluation === 'high' 或 complexity > 10)
1392
- const highComplexityFiles = results.filter(r => r.complexity.evaluation === 'high' || r.complexity.cyclomaticComplexity > 10);
1393
- // 計算統計資訊
1394
- const complexities = results.map(r => r.complexity.cyclomaticComplexity);
1395
- const averageComplexity = complexities.length > 0
1396
- ? complexities.reduce((sum, c) => sum + c, 0) / complexities.length
1397
- : 0;
1398
- const maxComplexity = complexities.length > 0
1399
- ? Math.max(...complexities)
1400
- : 0;
1401
- if (options.format === 'json') {
1402
- const outputData = {
1403
- summary: {
1404
- totalScanned: results.length,
1405
- issuesFound: highComplexityFiles.length,
1406
- averageComplexity,
1407
- maxComplexity
1408
- },
1409
- issues: highComplexityFiles.map(r => ({
1410
- path: r.file,
1411
- complexity: r.complexity.cyclomaticComplexity,
1412
- cognitiveComplexity: r.complexity.cognitiveComplexity,
1413
- evaluation: r.complexity.evaluation
1414
- }))
1415
- };
1416
- if (options.all) {
1417
- outputData.all = results.map(r => ({
1418
- path: r.file,
1419
- complexity: r.complexity.cyclomaticComplexity,
1420
- cognitiveComplexity: r.complexity.cognitiveComplexity,
1421
- evaluation: r.complexity.evaluation
1422
- }));
1423
- }
1424
- console.log(JSON.stringify(outputData, null, 2));
1425
- }
1426
- else {
1427
- console.log('✅ 複雜度分析完成!');
1428
- console.log(`📊 統計: ${results.length} 個檔案,${highComplexityFiles.length} 個高複雜度檔案`);
1429
- console.log(` 平均複雜度: ${averageComplexity.toFixed(2)}`);
1430
- console.log(` 最高複雜度: ${maxComplexity}`);
1431
- if (!options.all && highComplexityFiles.length > 0) {
1432
- console.log('\n⚠️ 高複雜度檔案:');
1433
- highComplexityFiles.forEach(r => {
1434
- console.log(` - ${r.file}: ${r.complexity.cyclomaticComplexity}`);
1435
- });
1436
- }
1437
- }
1438
- }
1439
- else if (analyzeType === 'dead-code') {
1440
- // 使用 ParserPlugin 檢測死代碼
1441
- const registry = ParserRegistry.getInstance();
1442
- const files = await this.getAllProjectFiles(analyzePath);
1443
- const results = [];
1444
- for (const file of files) {
1445
- try {
1446
- const parser = registry.getParser(path.extname(file));
1447
- if (!parser) {
1448
- continue;
1449
- }
1450
- const content = await fs.readFile(file, 'utf-8');
1451
- const ast = await parser.parse(content, file);
1452
- const symbols = await parser.extractSymbols(ast);
1453
- const deadCode = await parser.detectUnusedSymbols(ast, symbols);
1454
- results.push({ file, deadCode });
1455
- }
1456
- catch {
1457
- // 忽略無法分析的檔案
1458
- }
1459
- }
1460
- // 過濾有 dead code 的檔案
1461
- const filesWithDeadCode = results.filter(r => r.deadCode.length > 0);
1462
- // 統計結果
1463
- const allDeadCode = results.flatMap(r => r.deadCode);
1464
- const deadFunctions = allDeadCode.filter(d => d.type === 'function');
1465
- const deadVariables = allDeadCode.filter(d => d.type === 'variable');
1466
- if (options.format === 'json') {
1467
- const outputData = {
1468
- summary: {
1469
- totalScanned: results.length,
1470
- filesWithIssues: filesWithDeadCode.length,
1471
- totalDeadFunctions: deadFunctions.length,
1472
- totalDeadVariables: deadVariables.length,
1473
- totalDeadCode: allDeadCode.length
1474
- },
1475
- issues: filesWithDeadCode.map(r => ({
1476
- path: r.file,
1477
- deadCode: r.deadCode
1478
- }))
1479
- };
1480
- if (options.all) {
1481
- outputData.all = results.map(r => ({
1482
- path: r.file,
1483
- deadCode: r.deadCode
1484
- }));
1485
- }
1486
- outputData.deadFunctions = deadFunctions;
1487
- outputData.deadVariables = deadVariables;
1488
- console.log(JSON.stringify(outputData, null, 2));
1489
- }
1490
- else {
1491
- console.log('✅ 死代碼檢測完成!');
1492
- console.log(`📊 統計: ${results.length} 個檔案,${filesWithDeadCode.length} 個有死代碼`);
1493
- console.log('📊 發現:');
1494
- console.log(` 未使用函式: ${deadFunctions.length} 個`);
1495
- console.log(` 未使用變數: ${deadVariables.length} 個`);
1496
- if (!options.all && filesWithDeadCode.length > 0) {
1497
- console.log('\n⚠️ 有死代碼的檔案:');
1498
- filesWithDeadCode.forEach(r => {
1499
- console.log(` - ${r.file}: ${r.deadCode.length} 項`);
1500
- });
1501
- }
1502
- }
1503
- }
1504
- else if (analyzeType === 'best-practices') {
1505
- // 檢查最佳實踐
1506
- const files = await this.getAllProjectFiles(analyzePath);
1507
- const issues = [];
1508
- const recommendations = [];
1509
- // 檢查 ES Module 使用情況
1510
- const hasEsmImports = files.some(async (file) => {
1511
- const content = await fs.readFile(file, 'utf-8');
1512
- return content.includes('import ') && content.includes('from ');
1513
- });
1514
- if (hasEsmImports) {
1515
- recommendations.push({
1516
- type: 'es-modules',
1517
- status: 'good',
1518
- message: '專案使用 ES Module'
1519
- });
1520
- }
1521
- if (options.format === 'json') {
1522
- console.log(JSON.stringify({
1523
- issues,
1524
- recommendations
1525
- }, null, 2));
1526
- }
1527
- else {
1528
- console.log('✅ 最佳實踐檢查完成!');
1529
- console.log(`📊 建議數: ${recommendations.length}`);
1530
- }
1531
- }
1532
- else if (analyzeType === 'patterns') {
1533
- // 檢測程式碼模式
1534
- const files = await this.getAllProjectFiles(analyzePath);
1535
- const patterns = [];
1536
- let asyncFunctionCount = 0;
1537
- for (const file of files) {
1538
- const content = await fs.readFile(file, 'utf-8');
1539
- // 檢測 async 函式
1540
- if (content.includes('async ')) {
1541
- asyncFunctionCount++;
1542
- if (!patterns.includes('async-functions')) {
1543
- patterns.push('async-functions');
1544
- }
1545
- }
1546
- // 檢測 Promise 使用
1547
- if (content.includes('Promise') || content.includes('.then(')) {
1548
- if (!patterns.includes('promise-usage')) {
1549
- patterns.push('promise-usage');
1550
- }
1551
- }
1552
- // TypeScript 特定模式
1553
- if (file.endsWith('.ts') || file.endsWith('.tsx')) {
1554
- // 檢測 interface 使用
1555
- if (content.includes('interface ') && !patterns.includes('interface-usage')) {
1556
- patterns.push('interface-usage');
1557
- }
1558
- // 檢測泛型類型
1559
- if (content.match(/<[A-Z]\w*(\s*extends\s+\w+)?>/g) && !patterns.includes('generic-types')) {
1560
- patterns.push('generic-types');
1561
- }
1562
- // 檢測 enum 使用
1563
- if (content.includes('enum ') && !patterns.includes('enum-usage')) {
1564
- patterns.push('enum-usage');
1565
- }
1566
- }
1567
- }
1568
- if (options.format === 'json') {
1569
- console.log(JSON.stringify({
1570
- patterns,
1571
- statistics: {
1572
- asyncFunctions: asyncFunctionCount
1573
- }
1574
- }, null, 2));
1575
- }
1576
- else {
1577
- console.log('✅ 模式檢測完成!');
1578
- console.log(`📊 發現模式: ${patterns.join(', ')}`);
1579
- }
1580
- }
1581
- else if (analyzeType === 'quality') {
1582
- // 品質分析(整合多個維度)
1583
- const registry = ParserRegistry.getInstance();
1584
- const files = await this.getAllProjectFiles(analyzePath);
1585
- // 檢查路徑是否存在或沒有找到檔案
1586
- if (files.length === 0) {
1587
- const pathExists = await this.fileExists(analyzePath);
1588
- if (!pathExists) {
1589
- throw new Error(`路徑不存在: ${analyzePath}`);
1590
- }
1591
- throw new Error(`在路徑 ${analyzePath} 中找不到支援的檔案`);
1592
- }
1593
- // 統計資料
1594
- const summary = {
1595
- totalScanned: files.length,
1596
- totalIssues: 0,
1597
- qualityScore: 0
1598
- };
1599
- // 各維度問題列表
1600
- const allIssues = [];
1601
- const recommendations = [];
1602
- // 各維度分數(權重參考 ShitScore QA 維度)
1603
- let typeSafetyScore = 100;
1604
- let errorHandlingScore = 100;
1605
- let securityScore = 100;
1606
- let namingScore = 100;
1607
- let testCoverageScore = 0;
1608
- let testFileCount = 0;
1609
- for (const file of files) {
1610
- try {
1611
- const parser = registry.getParser(path.extname(file));
1612
- if (!parser) {
1613
- continue;
1614
- }
1615
- const content = await fs.readFile(file, 'utf-8');
1616
- const ast = await parser.parse(content, file);
1617
- // 判斷是否為測試檔案
1618
- if (parser.isTestFile && parser.isTestFile(file)) {
1619
- testFileCount++;
1620
- continue; // 跳過測試檔案
1621
- }
1622
- // 1. 型別安全檢測
1623
- if (parser.checkTypeSafety) {
1624
- const typeSafetyIssues = await parser.checkTypeSafety(content, ast);
1625
- typeSafetyIssues.forEach((issue) => {
1626
- allIssues.push({
1627
- type: 'type-safety',
1628
- severity: issue.severity === 'error' ? 'high' : 'medium',
1629
- message: issue.message,
1630
- filePath: issue.location.filePath,
1631
- line: issue.location.line
1632
- });
1633
- typeSafetyScore -= issue.severity === 'error' ? 10 : 5;
1634
- });
1635
- }
1636
- // 2. 錯誤處理檢測
1637
- if (parser.checkErrorHandling) {
1638
- const errorHandlingIssues = await parser.checkErrorHandling(content, ast);
1639
- errorHandlingIssues.forEach((issue) => {
1640
- allIssues.push({
1641
- type: 'error-handling',
1642
- severity: issue.severity === 'error' ? 'high' : 'medium',
1643
- message: issue.message,
1644
- filePath: issue.location.filePath,
1645
- line: issue.location.line
1646
- });
1647
- errorHandlingScore -= issue.severity === 'error' ? 10 : 5;
1648
- });
1649
- }
1650
- // 3. 安全性檢測
1651
- if (parser.checkSecurity) {
1652
- const securityIssues = await parser.checkSecurity(content, ast);
1653
- securityIssues.forEach((issue) => {
1654
- allIssues.push({
1655
- type: 'security',
1656
- severity: issue.severity === 'critical' ? 'high' : 'medium',
1657
- message: issue.message,
1658
- filePath: issue.location.filePath,
1659
- line: issue.location.line
1660
- });
1661
- securityScore -= issue.severity === 'critical' ? 15 : 10;
1662
- });
1663
- }
1664
- // 4. 命名規範檢測
1665
- if (parser.checkNamingConventions) {
1666
- const symbols = await parser.extractSymbols(ast);
1667
- const namingIssues = await parser.checkNamingConventions(symbols, file);
1668
- namingIssues.forEach((issue) => {
1669
- allIssues.push({
1670
- type: 'naming',
1671
- severity: 'low',
1672
- message: issue.message,
1673
- filePath: issue.location.filePath,
1674
- line: issue.location.line
1675
- });
1676
- namingScore -= 3;
1677
- });
1678
- }
1679
- }
1680
- catch {
1681
- // 忽略無法分析的檔案
1682
- }
1683
- }
1684
- // 5. 測試覆蓋率評估
1685
- const testFileRatio = files.length > 0 ? testFileCount / files.length : 0;
1686
- testCoverageScore = Math.min(100, testFileRatio * 200); // 50% 測試覆蓋率 = 100 分
1687
- // 確保分數不低於 0
1688
- typeSafetyScore = Math.max(0, typeSafetyScore);
1689
- errorHandlingScore = Math.max(0, errorHandlingScore);
1690
- securityScore = Math.max(0, securityScore);
1691
- namingScore = Math.max(0, namingScore);
1692
- // 計算整體品質評分(加權平均,參考 ShitScore QA 維度權重)
1693
- const overallScore = Math.round(typeSafetyScore * 0.30 + // Type Safety 30%
1694
- testCoverageScore * 0.25 + // Test Coverage 25%
1695
- errorHandlingScore * 0.20 + // Error Handling 20%
1696
- namingScore * 0.15 + // Naming 15%
1697
- securityScore * 0.10 // Security 10%
1698
- );
1699
- summary.totalIssues = allIssues.length;
1700
- summary.qualityScore = overallScore;
1701
- // 產生改善建議
1702
- if (typeSafetyScore < 80) {
1703
- recommendations.push('型別安全:建議使用可選綁定(if let, guard let)代替強制解包');
1704
- }
1705
- if (errorHandlingScore < 80) {
1706
- recommendations.push('錯誤處理:建議使用 do-catch 明確處理錯誤,避免空 catch 區塊');
1707
- }
1708
- if (securityScore < 80) {
1709
- recommendations.push('安全性:建議使用 Keychain 或環境變數儲存敏感資訊');
1710
- }
1711
- if (namingScore < 80) {
1712
- recommendations.push('命名規範:建議遵循 Swift API Design Guidelines 命名規範');
1713
- }
1714
- if (testCoverageScore < 50) {
1715
- recommendations.push('測試覆蓋率:建議提升測試覆蓋率至 50% 以上');
1716
- }
1717
- if (options.format === 'json') {
1718
- console.log(JSON.stringify({
1719
- summary,
1720
- issues: allIssues,
1721
- complexity: {
1722
- score: 100 // 預留位置(可選擇整合複雜度分析)
1723
- },
1724
- maintainability: {
1725
- score: 100 // 預留位置(可選擇整合維護性分析)
1726
- },
1727
- typeSafety: {
1728
- score: typeSafetyScore,
1729
- issues: allIssues.filter((i) => i.type === 'type-safety')
1730
- },
1731
- errorHandling: {
1732
- score: errorHandlingScore,
1733
- issues: allIssues.filter((i) => i.type === 'error-handling')
1734
- },
1735
- security: {
1736
- score: securityScore,
1737
- issues: allIssues.filter((i) => i.type === 'security')
1738
- },
1739
- namingConventions: {
1740
- score: namingScore,
1741
- issues: allIssues.filter((i) => i.type === 'naming')
1742
- },
1743
- testCoverage: {
1744
- score: testCoverageScore,
1745
- testFileRatio,
1746
- testFiles: testFileCount,
1747
- totalFiles: files.length
1748
- },
1749
- overallScore,
1750
- recommendations
1751
- }, null, 2));
1752
- }
1753
- else {
1754
- console.log('✅ 品質分析完成!');
1755
- console.log(`📊 整體評分: ${overallScore}/100`);
1756
- console.log(` 總問題數: ${summary.totalIssues}`);
1757
- console.log('\n維度評分:');
1758
- console.log(` 型別安全: ${typeSafetyScore.toFixed(1)}/100`);
1759
- console.log(` 錯誤處理: ${errorHandlingScore.toFixed(1)}/100`);
1760
- console.log(` 安全性: ${securityScore.toFixed(1)}/100`);
1761
- console.log(` 命名規範: ${namingScore.toFixed(1)}/100`);
1762
- console.log(` 測試覆蓋率: ${testCoverageScore.toFixed(1)}/100 (${(testFileRatio * 100).toFixed(1)}%)`);
1763
- if (recommendations.length > 0) {
1764
- console.log('\n改善建議:');
1765
- recommendations.forEach((rec, index) => {
1766
- console.log(` ${index + 1}. ${rec}`);
1767
- });
1768
- }
1769
- }
1770
- }
1771
- else {
1772
- throw new Error(`不支援的分析類型: ${analyzeType}`);
1773
- }
1774
- }
1775
- catch (error) {
1776
- const errorMessage = error instanceof Error ? error.message : String(error);
1777
- if (options.format === 'json') {
1778
- console.error(JSON.stringify({ error: errorMessage }));
1779
- }
1780
- else {
1781
- console.error('❌ 分析失敗:', errorMessage);
1782
- }
1783
- process.exitCode = 1;
1784
- if (process.env.NODE_ENV !== 'test') {
1785
- process.exit(1);
1786
- }
1787
- }
1788
- }
1789
- async handleShitCommand(options) {
1790
- if (options.format !== 'json') {
1791
- console.log('💩 分析程式碼垃圾度...');
1792
- }
1793
- try {
1794
- const analyzePath = options.path || process.cwd();
1795
- const topCount = parseInt(options.top) || 10;
1796
- const maxAllowed = options.maxAllowed ? parseFloat(options.maxAllowed) : undefined;
1797
- const registry = ParserRegistry.getInstance();
1798
- const analyzer = new ShitScoreAnalyzer(registry);
1799
- const result = await analyzer.analyze(analyzePath, {
1800
- detailed: options.detailed,
1801
- topCount,
1802
- maxAllowed,
1803
- showFiles: options.showFiles
1804
- });
1805
- if (options.format === 'json') {
1806
- const output = JSON.stringify(result, null, 2);
1807
- if (options.output) {
1808
- await fs.writeFile(options.output, output, 'utf-8');
1809
- console.log(`✅ 結果已儲存至 ${options.output}`);
1810
- }
1811
- else {
1812
- console.log(output);
1813
- }
1814
- }
1815
- else {
1816
- console.log('\n' + '='.repeat(50));
1817
- console.log(`垃圾度評分報告 ${result.gradeInfo.emoji}`);
1818
- console.log('='.repeat(50));
1819
- console.log(`\n總分: ${result.shitScore} / 100 [${result.gradeInfo.emoji} ${result.grade}級]`);
1820
- console.log(`評語: ${result.gradeInfo.message}\n`);
1821
- console.log('維度分析:');
1822
- console.log(` 複雜度垃圾: ${result.dimensions.complexity.score.toFixed(1)} (${(result.dimensions.complexity.weight * 100).toFixed(0)}%) → 貢獻 ${result.dimensions.complexity.weightedScore.toFixed(1)} 分`);
1823
- console.log(` 維護性垃圾: ${result.dimensions.maintainability.score.toFixed(1)} (${(result.dimensions.maintainability.weight * 100).toFixed(0)}%) → 貢獻 ${result.dimensions.maintainability.weightedScore.toFixed(1)} 分`);
1824
- console.log(` 架構垃圾: ${result.dimensions.architecture.score.toFixed(1)} (${(result.dimensions.architecture.weight * 100).toFixed(0)}%) → 貢獻 ${result.dimensions.architecture.weightedScore.toFixed(1)} 分\n`);
1825
- const criticalCount = result.topShit ? result.topShit.filter(s => s.severity === 'critical').length : 0;
1826
- const highCount = result.topShit ? result.topShit.filter(s => s.severity === 'high').length : 0;
1827
- const mediumCount = result.topShit ? result.topShit.filter(s => s.severity === 'medium').length : 0;
1828
- const lowCount = result.topShit ? result.topShit.filter(s => s.severity === 'low').length : 0;
1829
- console.log('問題統計:');
1830
- console.log(` 🔴 嚴重問題: ${criticalCount} 個`);
1831
- console.log(` 🟠 高優先級: ${highCount} 個`);
1832
- console.log(` 🟡 中優先級: ${mediumCount} 個`);
1833
- console.log(` 🟢 低優先級: ${lowCount} 個\n`);
1834
- console.log(`掃描檔案: ${result.summary.analyzedFiles} 個(共 ${result.summary.totalFiles} 個)`);
1835
- console.log(`總問題數: ${result.summary.totalShit} 個`);
1836
- if (options.detailed && result.topShit && result.topShit.length > 0) {
1837
- console.log('\n' + '='.repeat(50));
1838
- console.log(`最糟的 ${result.topShit.length} 個項目:`);
1839
- console.log('='.repeat(50));
1840
- result.topShit.forEach((item, index) => {
1841
- console.log(`\n${index + 1}. [${item.severity.toUpperCase()}] ${item.type}`);
1842
- console.log(` 檔案: ${item.filePath}${item.location ? `:${item.location.line}` : ''}`);
1843
- console.log(` 分數: ${item.score.toFixed(1)}`);
1844
- console.log(` 描述: ${item.description}`);
1845
- });
1846
- if (result.recommendations && result.recommendations.length > 0) {
1847
- console.log('\n' + '='.repeat(50));
1848
- console.log('修復建議:');
1849
- console.log('='.repeat(50));
1850
- result.recommendations.forEach((rec, index) => {
1851
- console.log(`\n${index + 1}. [優先級 ${rec.priority}] ${rec.category}`);
1852
- console.log(` 建議: ${rec.suggestion}`);
1853
- console.log(` 預期改善: ${rec.estimatedImpact.toFixed(1)} 分`);
1854
- console.log(` 影響檔案: ${rec.affectedFiles.length} 個`);
1855
- });
1856
- }
1857
- }
1858
- console.log('\n' + '='.repeat(50));
1859
- }
1860
- }
1861
- catch (error) {
1862
- const errorMessage = error instanceof Error ? error.message : String(error);
1863
- if (options.format === 'json') {
1864
- console.error(JSON.stringify({ error: errorMessage }));
1865
- }
1866
- else {
1867
- console.error('\n❌ 垃圾度分析失敗:', errorMessage);
1868
- }
1869
- process.exitCode = 1;
1870
- if (process.env.NODE_ENV !== 'test') {
1871
- process.exit(1);
1872
- }
1873
- }
1874
- }
1875
- async handleSnapshotCommand(action, options) {
1876
- const isJsonFormat = options.format === 'json';
1877
- try {
1878
- const projectPath = options.path || process.cwd();
1879
- const configManager = new ConfigManager();
1880
- // 讀取配置檔
1881
- const projectConfig = await configManager.loadConfig(projectPath);
1882
- // 合併選項
1883
- const snapshotOptions = {
1884
- projectPath,
1885
- outputPath: options.output,
1886
- incremental: options.incremental,
1887
- level: options.level,
1888
- includeTests: options.includeTests,
1889
- multiLevel: options.multiLevel,
1890
- outputDir: options.outputDir,
1891
- silent: isJsonFormat
1892
- };
1893
- const finalOptions = configManager.mergeOptions(projectPath, snapshotOptions, projectConfig);
1894
- // 如果沒有指定輸出路徑,使用預設值
1895
- if (!finalOptions.outputPath) {
1896
- finalOptions.outputPath = path.join(projectPath, '.agent-ide', 'snapshot.json');
1897
- }
1898
- const engine = new SnapshotEngine();
1899
- switch (action) {
1900
- case 'generate':
1901
- await this.handleSnapshotGenerate(engine, finalOptions, isJsonFormat);
1902
- break;
1903
- case 'info':
1904
- await this.handleSnapshotInfo(finalOptions, isJsonFormat);
1905
- break;
1906
- case 'diff':
1907
- await this.handleSnapshotDiff(options, isJsonFormat);
1908
- break;
1909
- case 'init':
1910
- await this.handleSnapshotInit(configManager, projectPath, isJsonFormat);
1911
- break;
1912
- default:
1913
- // 預設執行生成
1914
- await this.handleSnapshotGenerate(engine, finalOptions, isJsonFormat);
1915
- break;
1916
- }
1917
- }
1918
- catch (error) {
1919
- const errorMessage = error instanceof Error ? error.message : String(error);
1920
- if (isJsonFormat) {
1921
- console.error(JSON.stringify({ error: errorMessage }));
1922
- }
1923
- else {
1924
- console.error('\n❌ 快照操作失敗:', errorMessage);
1925
- }
1926
- process.exitCode = 1;
1927
- if (process.env.NODE_ENV !== 'test') {
1928
- process.exit(1);
1929
- }
1930
- }
1931
- }
1932
- async handleSnapshotGenerate(engine, options, isJsonFormat) {
1933
- if (!isJsonFormat) {
1934
- console.log('📸 生成程式碼快照...');
1935
- if (options.incremental) {
1936
- console.log(' 模式: 增量更新');
1937
- }
1938
- else {
1939
- console.log(' 模式: 完整生成');
1940
- }
1941
- console.log(` 壓縮層級: ${options.level}`);
1942
- }
1943
- const startTime = Date.now();
1944
- const snapshot = await engine.generate(options);
1945
- const stats = engine.getStats(snapshot);
1946
- const duration = Date.now() - startTime;
1947
- stats.generationTime = duration;
1948
- // 保存快照
1949
- if (options.outputPath) {
1950
- await engine.save(snapshot, options.outputPath);
1951
- }
1952
- // 如果是多層級模式,生成其他層級
1953
- if (options.multiLevel && options.outputDir) {
1954
- if (!isJsonFormat) {
1955
- console.log('\n📚 生成多層級快照...');
1956
- }
1957
- const levels = [
1958
- CompressionLevel.Minimal,
1959
- CompressionLevel.Medium,
1960
- CompressionLevel.Full
1961
- ];
1962
- for (const level of levels) {
1963
- const levelOptions = { ...options, level, incremental: false };
1964
- const levelSnapshot = await engine.generate(levelOptions);
1965
- const outputPath = path.join(options.outputDir, `snapshot-${level}.json`);
1966
- await engine.save(levelSnapshot, outputPath);
1967
- if (!isJsonFormat) {
1968
- const levelStats = engine.getStats(levelSnapshot);
1969
- console.log(` ✅ ${level}: ${levelStats.estimatedTokens} tokens`);
1970
- }
1971
- }
1972
- }
1973
- if (isJsonFormat) {
1974
- console.log(JSON.stringify({
1975
- success: true,
1976
- snapshot: options.outputPath,
1977
- stats
1978
- }, null, 2));
1979
- }
1980
- else {
1981
- console.log('\n✅ 快照生成完成');
1982
- console.log(` 輸出位置: ${options.outputPath}`);
1983
- console.log('\n統計資訊:');
1984
- console.log(` 檔案數量: ${stats.fileCount}`);
1985
- console.log(` 程式碼行數: ${stats.totalLines}`);
1986
- console.log(` 符號數量: ${stats.symbolCount}`);
1987
- console.log(` 依賴關係: ${stats.dependencyCount}`);
1988
- console.log(` 估計 token 數: ${stats.estimatedTokens}`);
1989
- console.log(` 壓縮率: ${stats.compressionRatio.toFixed(1)}%`);
1990
- console.log(` 生成耗時: ${stats.generationTime}ms`);
1991
- }
1992
- }
1993
- async handleSnapshotInfo(options, isJsonFormat) {
1994
- if (!options.outputPath) {
1995
- throw new Error('請指定快照檔案路徑 (--output)');
1996
- }
1997
- const engine = new SnapshotEngine();
1998
- const snapshot = await engine.load(options.outputPath);
1999
- const stats = engine.getStats(snapshot);
2000
- if (isJsonFormat) {
2001
- console.log(JSON.stringify({
2002
- snapshot: {
2003
- version: snapshot.v,
2004
- project: snapshot.p,
2005
- timestamp: snapshot.t,
2006
- level: snapshot.l
2007
- },
2008
- stats
2009
- }, null, 2));
2010
- }
2011
- else {
2012
- console.log('\n📊 快照資訊');
2013
- console.log('='.repeat(50));
2014
- console.log(` 專案: ${snapshot.p}`);
2015
- console.log(` 版本: ${snapshot.v}`);
2016
- console.log(` 時間: ${new Date(snapshot.t).toLocaleString()}`);
2017
- console.log(` 壓縮層級: ${snapshot.l}`);
2018
- console.log('\n統計資訊:');
2019
- console.log(` 檔案數量: ${stats.fileCount}`);
2020
- console.log(` 程式碼行數: ${stats.totalLines}`);
2021
- console.log(` 符號數量: ${stats.symbolCount}`);
2022
- console.log(` 估計 token 數: ${stats.estimatedTokens}`);
2023
- console.log(` 語言: ${snapshot.md.lg.join(', ')}`);
2024
- console.log('='.repeat(50));
2025
- }
2026
- }
2027
- async handleSnapshotDiff(options, isJsonFormat) {
2028
- const oldPath = options.old;
2029
- const newPath = options.new;
2030
- if (!oldPath || !newPath) {
2031
- throw new Error('請指定兩個快照檔案路徑 (--old <path> --new <path>)');
2032
- }
2033
- const engine = new SnapshotEngine();
2034
- const differ = new SnapshotDiffer();
2035
- const oldSnapshot = await engine.load(oldPath);
2036
- const newSnapshot = await engine.load(newPath);
2037
- const diff = differ.diff(oldSnapshot, newSnapshot);
2038
- if (isJsonFormat) {
2039
- console.log(JSON.stringify(diff, null, 2));
2040
- }
2041
- else {
2042
- console.log('\n📊 快照差異');
2043
- console.log('='.repeat(50));
2044
- console.log(` 新增檔案: ${diff.added.length}`);
2045
- console.log(` 修改檔案: ${diff.modified.length}`);
2046
- console.log(` 刪除檔案: ${diff.deleted.length}`);
2047
- console.log(` 總變更: ${diff.summary.totalChanges}`);
2048
- console.log(` 變更行數: ${diff.summary.linesChanged}`);
2049
- console.log('='.repeat(50));
2050
- if (diff.added.length > 0) {
2051
- console.log('\n新增檔案:');
2052
- diff.added.forEach(file => console.log(` + ${file}`));
2053
- }
2054
- if (diff.modified.length > 0) {
2055
- console.log('\n修改檔案:');
2056
- diff.modified.forEach(file => console.log(` ~ ${file}`));
2057
- }
2058
- if (diff.deleted.length > 0) {
2059
- console.log('\n刪除檔案:');
2060
- diff.deleted.forEach(file => console.log(` - ${file}`));
2061
- }
2062
- }
2063
- }
2064
- async handleSnapshotInit(configManager, projectPath, isJsonFormat) {
2065
- await configManager.createExampleConfig(projectPath);
2066
- if (isJsonFormat) {
2067
- console.log(JSON.stringify({ success: true, config: '.agent-ide.json' }));
2068
- }
2069
- else {
2070
- console.log('✅ 已建立配置檔: .agent-ide.json');
2071
- }
2072
- }
2073
- async handleDepsCommand(subcommand, options) {
2074
- if (options.format !== 'json') {
2075
- const titles = {
2076
- 'graph': '🕸️ 依賴圖分析...',
2077
- 'cycles': '🔄 循環依賴分析...',
2078
- 'impact': '💥 影響分析...',
2079
- 'orphans': '🏝️ 孤立檔案分析...'
2080
- };
2081
- console.log(titles[subcommand] || '🕸️ 分析依賴關係...');
2082
- }
2083
- try {
2084
- const analyzePath = options.path || process.cwd();
2085
- // 初始化依賴分析器
2086
- if (!this.dependencyAnalyzer) {
2087
- this.dependencyAnalyzer = new DependencyAnalyzer();
2088
- }
2089
- // 分析專案依賴
2090
- const projectDeps = await this.dependencyAnalyzer.analyzeProject(analyzePath);
2091
- // 獲取統計資訊
2092
- const stats = this.dependencyAnalyzer.getStats();
2093
- // 使用 CycleDetector 檢測循環依賴
2094
- const cycleDetector = new (await import('../../core/dependency/cycle-detector.js')).CycleDetector();
2095
- const graph = await this.buildGraphFromProjectDeps(projectDeps);
2096
- const cycles = cycleDetector.detectCycles(graph);
2097
- // 輸出結果
2098
- if (options.format === 'json') {
2099
- // 根據子命令決定輸出格式
2100
- if (subcommand === 'graph') {
2101
- // graph 子命令:輸出完整依賴圖(nodes, edges, summary)
2102
- const allNodes = graph.getAllNodes();
2103
- const allNodesSet = new Set(allNodes);
2104
- // 計算每個節點的入度和出度
2105
- const inDegreeMap = new Map();
2106
- const outDegreeMap = new Map();
2107
- for (const nodeId of allNodes) {
2108
- const deps = graph.getDependencies(nodeId);
2109
- outDegreeMap.set(nodeId, deps.length);
2110
- for (const depId of deps) {
2111
- if (allNodesSet.has(depId)) {
2112
- inDegreeMap.set(depId, (inDegreeMap.get(depId) || 0) + 1);
2113
- }
2114
- }
2115
- }
2116
- const nodes = allNodes.map((nodeId) => ({
2117
- id: nodeId,
2118
- dependencies: graph.getDependencies(nodeId),
2119
- inDegree: inDegreeMap.get(nodeId) || 0,
2120
- outDegree: outDegreeMap.get(nodeId) || 0
2121
- }));
2122
- // 判斷是否為系統框架
2123
- const isSystemFramework = (name) => {
2124
- const systemFrameworks = [
2125
- 'Foundation', 'UIKit', 'SwiftUI', 'Combine', 'CoreData',
2126
- 'CoreGraphics', 'CoreLocation', 'AVFoundation', 'MapKit',
2127
- 'WebKit', 'Security', 'PackageDescription'
2128
- ];
2129
- return systemFrameworks.includes(name);
2130
- };
2131
- const edges = [];
2132
- for (const nodeId of allNodes) {
2133
- for (const depId of graph.getDependencies(nodeId)) {
2134
- // 系統框架一律標記為 external
2135
- const isExternal = isSystemFramework(depId) || !allNodesSet.has(depId);
2136
- edges.push({
2137
- source: nodeId,
2138
- target: depId,
2139
- type: isExternal ? 'external' : 'internal'
2140
- });
2141
- }
2142
- }
2143
- // graph 子命令:保持原格式(nodes, edges, summary)
2144
- console.log(JSON.stringify({
2145
- nodes,
2146
- edges,
2147
- summary: {
2148
- totalFiles: stats.totalFiles,
2149
- totalDependencies: stats.totalDependencies,
2150
- averageDependenciesPerFile: stats.averageDependenciesPerFile,
2151
- maxDependenciesInFile: stats.maxDependenciesInFile
2152
- }
2153
- }, null, 2));
2154
- }
2155
- else if (subcommand === 'impact' && options.file) {
2156
- // impact 子命令:分析檔案修改的影響範圍
2157
- const targetFile = path.resolve(options.file);
2158
- let actualTargetFile = targetFile;
2159
- const directDependents = graph.getDependents(targetFile);
2160
- // 如果找不到依賴關係,可能是路徑格式不匹配
2161
- if (directDependents.length === 0) {
2162
- // 嘗試在 graph 中找到匹配的路徑
2163
- const allNodes = graph.getAllNodes();
2164
- const matchingNode = allNodes.find((node) => node.endsWith(options.file) || options.file.endsWith(node));
2165
- if (matchingNode) {
2166
- // 找到匹配的節點,使用該路徑重新查詢
2167
- actualTargetFile = matchingNode;
2168
- const altDependents = graph.getDependents(matchingNode);
2169
- directDependents.length = 0;
2170
- directDependents.push(...altDependents);
2171
- }
2172
- else {
2173
- // 檔案不在專案中或未被索引
2174
- console.error(`❌ 錯誤:檔案不存在或未被索引: ${options.file}`);
2175
- process.exit(1);
2176
- }
2177
- }
2178
- // BFS 計算傳遞依賴
2179
- const transitiveDependents = new Set();
2180
- const visited = new Set();
2181
- const queue = [...directDependents];
2182
- while (queue.length > 0) {
2183
- const current = queue.shift();
2184
- if (visited.has(current)) {
2185
- continue;
2186
- }
2187
- visited.add(current);
2188
- transitiveDependents.add(current);
2189
- const deps = graph.getDependents(current);
2190
- for (const dep of deps) {
2191
- if (!visited.has(dep)) {
2192
- queue.push(dep);
2193
- }
2194
- }
2195
- }
2196
- // 計算影響等級
2197
- const totalImpacted = transitiveDependents.size;
2198
- let impactLevel;
2199
- if (totalImpacted > 10) {
2200
- impactLevel = 'high';
2201
- }
2202
- else if (totalImpacted > 3) {
2203
- impactLevel = 'medium';
2204
- }
2205
- else {
2206
- impactLevel = 'low';
2207
- }
2208
- // 計算影響評分 (0-100)
2209
- const impactScore = Math.min(100, Math.round((totalImpacted / stats.totalFiles) * 100 * 2));
2210
- console.log(JSON.stringify({
2211
- file: options.file,
2212
- impactLevel,
2213
- impactScore,
2214
- directDependents,
2215
- transitiveDependents: Array.from(transitiveDependents),
2216
- summary: {
2217
- totalImpacted,
2218
- directCount: directDependents.length,
2219
- transitiveCount: transitiveDependents.size
2220
- }
2221
- }, null, 2));
2222
- }
2223
- else if (subcommand === 'orphans') {
2224
- // orphans 子命令:檢測孤立檔案
2225
- const allNodes = graph.getAllNodes();
2226
- const orphans = [];
2227
- for (const node of allNodes) {
2228
- const dependents = graph.getDependents(node);
2229
- if (dependents.length === 0) {
2230
- orphans.push({
2231
- filePath: node,
2232
- reason: 'No files depend on this file'
2233
- });
2234
- }
2235
- }
2236
- console.log(JSON.stringify({
2237
- orphans,
2238
- summary: {
2239
- totalOrphans: orphans.length,
2240
- totalFiles: stats.totalFiles,
2241
- orphanPercentage: Math.round((orphans.length / stats.totalFiles) * 100)
2242
- }
2243
- }, null, 2));
2244
- }
2245
- else if (options.file) {
2246
- // 單檔案依賴查詢模式
2247
- const targetFile = path.resolve(options.file);
2248
- const dependencies = {};
2249
- dependencies[options.file] = graph.getDependencies(targetFile);
2250
- console.log(JSON.stringify({
2251
- dependencies
2252
- }, null, 2));
2253
- }
2254
- else {
2255
- // 其他子命令(cycles)或無子命令:輸出問題導向格式
2256
- const outputData = {
2257
- issues: {
2258
- cycles: cycles.map(c => ({
2259
- cycle: c.cycle,
2260
- length: c.length,
2261
- severity: c.severity
2262
- })),
2263
- circularDependencies: cycles.length,
2264
- orphanedFiles: stats.orphanedFiles
2265
- },
2266
- summary: {
2267
- totalFiles: stats.totalFiles,
2268
- totalDependencies: stats.totalDependencies,
2269
- averageDependenciesPerFile: stats.averageDependenciesPerFile,
2270
- maxDependenciesInFile: stats.maxDependenciesInFile,
2271
- cyclesFound: cycles.length,
2272
- issuesFound: cycles.length + stats.orphanedFiles
2273
- }
2274
- };
2275
- // 只有在 --all 時才輸出完整依賴圖
2276
- if (options.all) {
2277
- const nodes = graph.getAllNodes().map((nodeId) => ({
2278
- id: nodeId,
2279
- dependencies: graph.getDependencies(nodeId)
2280
- }));
2281
- const edges = [];
2282
- for (const nodeId of graph.getAllNodes()) {
2283
- for (const depId of graph.getDependencies(nodeId)) {
2284
- edges.push({ source: nodeId, target: depId });
2285
- }
2286
- }
2287
- outputData.all = {
2288
- nodes,
2289
- edges
2290
- };
2291
- }
2292
- console.log(JSON.stringify(outputData, null, 2));
2293
- }
2294
- }
2295
- else {
2296
- const completeTitles = {
2297
- 'graph': '✅ 依賴圖分析',
2298
- 'cycles': '✅ 循環依賴分析',
2299
- 'impact': '✅ 影響分析',
2300
- 'orphans': '✅ 孤立檔案分析'
2301
- };
2302
- console.log(completeTitles[subcommand] || '✅ 依賴分析完成!');
2303
- console.log('📊 統計:');
2304
- console.log(` 總檔案數: ${stats.totalFiles}`);
2305
- console.log(` 總依賴數: ${stats.totalDependencies}`);
2306
- console.log(` 平均依賴數: ${stats.averageDependenciesPerFile.toFixed(2)}`);
2307
- console.log(` 最大依賴數: ${stats.maxDependenciesInFile}`);
2308
- if (cycles.length > 0) {
2309
- console.log(`⚠️ 發現 ${cycles.length} 個循環依賴:`);
2310
- cycles.forEach((cycle, index) => {
2311
- console.log(` ${index + 1}. ${cycle.cycle.join(' → ')} (長度: ${cycle.length}, 嚴重性: ${cycle.severity})`);
2312
- });
2313
- }
2314
- else {
2315
- console.log('✓ 無循環依賴');
2316
- }
2317
- if (stats.orphanedFiles > 0) {
2318
- console.log(`⚠️ 發現 ${stats.orphanedFiles} 個孤立檔案`);
2319
- }
2320
- }
2321
- }
2322
- catch (error) {
2323
- if (options.format === 'json') {
2324
- console.log(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
2325
- }
2326
- else {
2327
- console.error('❌ 依賴分析失敗:', error instanceof Error ? error.message : error);
2328
- }
2329
- if (process.env.NODE_ENV !== 'test') {
2330
- if (process.env.NODE_ENV !== 'test') {
2331
- process.exit(1);
2332
- }
2333
- }
2334
- }
2335
- }
2336
- /**
2337
- * 從專案依賴資訊建立依賴圖
2338
- */
2339
- async buildGraphFromProjectDeps(projectDeps) {
2340
- const { DependencyGraph } = await import('../../core/dependency/dependency-graph.js');
2341
- const graph = new DependencyGraph();
2342
- // 新增所有檔案節點及其依賴關係
2343
- for (const fileDep of projectDeps.fileDependencies) {
2344
- graph.addNode(fileDep.filePath);
2345
- for (const dep of fileDep.dependencies) {
2346
- graph.addDependency(fileDep.filePath, dep.path);
2347
- }
2348
- }
2349
- return graph;
2350
- }
2351
- async handlePluginsListCommand(options) {
2352
- console.log('🔌 插件列表:');
2353
- const registry = ParserRegistry.getInstance();
2354
- // 確保 registry 存在且有 listParsers 方法
2355
- if (!registry || typeof registry.listParsers !== 'function') {
2356
- console.log('📝 插件系統尚未初始化');
2357
- return;
2358
- }
2359
- const parsers = registry.listParsers();
2360
- if (!parsers || parsers.length === 0) {
2361
- console.log('📝 未找到已註冊的插件');
2362
- return;
2363
- }
2364
- console.table(parsers.map(p => ({
2365
- 名稱: p.name,
2366
- 版本: p.version,
2367
- 支援副檔名: p.supportedExtensions.join(', '),
2368
- 支援語言: p.supportedLanguages.join(', '),
2369
- 註冊時間: p.registeredAt.toLocaleString()
2370
- })));
2371
- }
2372
- async handlePluginInfoCommand(pluginName) {
2373
- const registry = ParserRegistry.getInstance();
2374
- // 確保 registry 存在且有 getParserByName 方法
2375
- if (!registry || typeof registry.getParserByName !== 'function') {
2376
- console.error('❌ 插件系統尚未初始化');
2377
- if (process.env.NODE_ENV !== 'test') {
2378
- process.exit(1);
2379
- }
2380
- }
2381
- const plugin = registry.getParserByName(pluginName);
2382
- if (!plugin) {
2383
- console.error(`❌ 找不到插件: ${pluginName}`);
2384
- if (process.env.NODE_ENV !== 'test') {
2385
- process.exit(1);
2386
- }
2387
- }
2388
- console.log(`🔌 插件資訊: ${pluginName}`);
2389
- // TODO: 顯示詳細插件資訊
2390
- }
2391
- /**
2392
- * 檢查檔案是否存在
2393
- */
2394
- async fileExists(filePath) {
2395
- try {
2396
- await fs.access(filePath);
2397
- return true;
2398
- }
2399
- catch {
2400
- return false;
2401
- }
2402
- }
2403
- /**
2404
- * 正確套用程式碼編輯
2405
- */
2406
- applyEditCorrectly(code, edit) {
2407
- const lines = code.split('\n');
2408
- switch (edit.type) {
2409
- case 'replace': {
2410
- // 計算起始和結束位置的偏移量
2411
- const startOffset = this.positionToOffset(lines, edit.range.start);
2412
- const endOffset = this.positionToOffset(lines, edit.range.end);
2413
- return code.substring(0, startOffset) + edit.newText + code.substring(endOffset);
2414
- }
2415
- case 'insert': {
2416
- const offset = this.positionToOffset(lines, edit.range.start);
2417
- return code.substring(0, offset) + edit.newText + code.substring(offset);
2418
- }
2419
- case 'delete': {
2420
- const startOffset = this.positionToOffset(lines, edit.range.start);
2421
- const endOffset = this.positionToOffset(lines, edit.range.end);
2422
- return code.substring(0, startOffset) + code.substring(endOffset);
2423
- }
2424
- default:
2425
- return code;
2426
- }
2427
- }
2428
- /**
2429
- * 將行列位置轉換為字元偏移量
2430
- */
2431
- positionToOffset(lines, position) {
2432
- let offset = 0;
2433
- for (let i = 0; i < position.line - 1 && i < lines.length; i++) {
2434
- offset += lines[i].length + 1; // +1 for newline
2435
- }
2436
- offset += position.column;
2437
- return Math.min(offset, lines.join('\n').length);
2438
- }
2439
- /**
2440
- * @deprecated 使用 applyEditCorrectly 代替
2441
- */
2442
- applyCodeEdit(code, edit) {
2443
- const lines = code.split('\n');
2444
- const startLine = edit.range.start.line - 1; // 轉為 0-based
2445
- const endLine = edit.range.end.line - 1;
2446
- // 取得編輯範圍前後的內容
2447
- const before = lines.slice(0, startLine);
2448
- const after = lines.slice(endLine + 1);
2449
- // 組合新的內容
2450
- return [...before, edit.newText, ...after].join('\n');
2451
- }
2452
- /**
2453
- * 獲取專案中的所有檔案
2454
- */
2455
- async getAllProjectFiles(projectPath) {
2456
- const files = [];
2457
- // 從 ParserRegistry 獲取所有支援的副檔名
2458
- const registry = ParserRegistry.getInstance();
2459
- const allowedExtensions = registry.getSupportedExtensions();
2460
- const excludePatterns = ['node_modules', 'dist', '.git', 'coverage'];
2461
- // 檢查路徑是檔案還是目錄
2462
- try {
2463
- const stats = await fs.stat(projectPath);
2464
- if (stats.isFile()) {
2465
- // 如果是單一檔案,直接返回
2466
- if (allowedExtensions.some(ext => projectPath.endsWith(ext))) {
2467
- return [projectPath];
2468
- }
2469
- return [];
2470
- }
2471
- }
2472
- catch (error) {
2473
- // 路徑不存在
2474
- return [];
2475
- }
2476
- async function walkDir(dir) {
2477
- try {
2478
- const entries = await fs.readdir(dir, { withFileTypes: true });
2479
- for (const entry of entries) {
2480
- const fullPath = path.join(dir, entry.name);
2481
- if (entry.isDirectory()) {
2482
- // 跳過排除的目錄
2483
- if (excludePatterns.some(pattern => entry.name.includes(pattern))) {
2484
- continue;
2485
- }
2486
- await walkDir(fullPath);
2487
- }
2488
- else if (entry.isFile()) {
2489
- // 只包含支援的副檔名
2490
- if (allowedExtensions.some(ext => entry.name.endsWith(ext))) {
2491
- files.push(fullPath);
2492
- }
2493
- }
2494
- }
2495
- }
2496
- catch (error) {
2497
- // 忽略無法存取的目錄
2498
- }
2499
- }
2500
- await walkDir(projectPath);
2501
- return files;
2502
- }
2503
- /**
2504
- * 讀取 tsconfig.json 的路徑別名設定
2505
- */
2506
- async loadPathAliases(projectRoot) {
2507
- const pathAliases = {};
2508
- try {
2509
- const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
2510
- const tsconfigContent = await fs.readFile(tsconfigPath, 'utf-8');
2511
- const tsconfig = JSON.parse(tsconfigContent);
2512
- if (tsconfig.compilerOptions?.paths) {
2513
- const baseUrl = tsconfig.compilerOptions.baseUrl || '.';
2514
- const basePath = path.resolve(projectRoot, baseUrl);
2515
- for (const [alias, paths] of Object.entries(tsconfig.compilerOptions.paths)) {
2516
- if (Array.isArray(paths) && paths.length > 0) {
2517
- // 移除 /* 後綴
2518
- const cleanAlias = alias.replace(/\/\*$/, '');
2519
- const cleanPath = paths[0].replace(/\/\*$/, '');
2520
- // 轉換為絕對路徑
2521
- pathAliases[cleanAlias] = path.resolve(basePath, cleanPath);
2522
- }
2523
- }
2524
- }
2525
- }
2526
- catch (error) {
2527
- // tsconfig.json 不存在或解析失敗,使用空的路徑別名
2528
- if (process.env.NODE_ENV !== 'test') {
2529
- console.warn('⚠️ 無法讀取 tsconfig.json 路徑別名設定');
2530
- }
2531
- }
2532
- return pathAliases;
2533
- }
2534
- /**
2535
- * 建立輸出格式化器
2536
- */
2537
- createFormatter(format) {
2538
- let outputFormat;
2539
- switch (format?.toLowerCase()) {
2540
- case 'markdown':
2541
- outputFormat = OutputFormat.Markdown;
2542
- break;
2543
- case 'json':
2544
- outputFormat = OutputFormat.Json;
2545
- break;
2546
- case 'minimal':
2547
- outputFormat = OutputFormat.Minimal;
2548
- break;
2549
- case 'plain':
2550
- default:
2551
- outputFormat = OutputFormat.Plain;
2552
- break;
2553
- }
2554
- return new OutputFormatter(outputFormat);
2555
- }
2556
159
  }
2557
160
  //# sourceMappingURL=cli.js.map