zero-workspace 0.0.2

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 (507) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +187 -0
  3. package/config/component-versions.json +16 -0
  4. package/config/scenarioCapabilities.json +29 -0
  5. package/config/version-notes.yaml +244 -0
  6. package/dist/adapters/OutputAdapter.d.ts +79 -0
  7. package/dist/adapters/OutputAdapter.d.ts.map +1 -0
  8. package/dist/adapters/OutputAdapter.js +124 -0
  9. package/dist/adapters/OutputAdapter.js.map +1 -0
  10. package/dist/cli/check-node-version.d.ts +3 -0
  11. package/dist/cli/check-node-version.d.ts.map +1 -0
  12. package/dist/cli/check-node-version.js +153 -0
  13. package/dist/cli/check-node-version.js.map +1 -0
  14. package/dist/cli/plugins.d.ts +41 -0
  15. package/dist/cli/plugins.d.ts.map +1 -0
  16. package/dist/cli/plugins.js +742 -0
  17. package/dist/cli/plugins.js.map +1 -0
  18. package/dist/cli/rebuild.d.ts +63 -0
  19. package/dist/cli/rebuild.d.ts.map +1 -0
  20. package/dist/cli/rebuild.js +989 -0
  21. package/dist/cli/rebuild.js.map +1 -0
  22. package/dist/cli/repair.d.ts +7 -0
  23. package/dist/cli/repair.d.ts.map +1 -0
  24. package/dist/cli/repair.js +925 -0
  25. package/dist/cli/repair.js.map +1 -0
  26. package/dist/cli/setup.d.ts +7 -0
  27. package/dist/cli/setup.d.ts.map +1 -0
  28. package/dist/cli/setup.js +452 -0
  29. package/dist/cli/setup.js.map +1 -0
  30. package/dist/cli/update.d.ts +10 -0
  31. package/dist/cli/update.d.ts.map +1 -0
  32. package/dist/cli/update.js +426 -0
  33. package/dist/cli/update.js.map +1 -0
  34. package/dist/cli/webui.d.ts +6 -0
  35. package/dist/cli/webui.d.ts.map +1 -0
  36. package/dist/cli/webui.js +210 -0
  37. package/dist/cli/webui.js.map +1 -0
  38. package/dist/http/index.d.ts +3 -0
  39. package/dist/http/index.d.ts.map +1 -0
  40. package/dist/http/index.js +15 -0
  41. package/dist/http/index.js.map +1 -0
  42. package/dist/http/middleware/errorHandler.d.ts +16 -0
  43. package/dist/http/middleware/errorHandler.d.ts.map +1 -0
  44. package/dist/http/middleware/errorHandler.js +79 -0
  45. package/dist/http/middleware/errorHandler.js.map +1 -0
  46. package/dist/http/routes/admin.d.ts +3 -0
  47. package/dist/http/routes/admin.d.ts.map +1 -0
  48. package/dist/http/routes/admin.js +730 -0
  49. package/dist/http/routes/admin.js.map +1 -0
  50. package/dist/http/routes/backup.d.ts +3 -0
  51. package/dist/http/routes/backup.d.ts.map +1 -0
  52. package/dist/http/routes/backup.js +172 -0
  53. package/dist/http/routes/backup.js.map +1 -0
  54. package/dist/http/routes/config.d.ts +3 -0
  55. package/dist/http/routes/config.d.ts.map +1 -0
  56. package/dist/http/routes/config.js +157 -0
  57. package/dist/http/routes/config.js.map +1 -0
  58. package/dist/http/routes/context.d.ts +3 -0
  59. package/dist/http/routes/context.d.ts.map +1 -0
  60. package/dist/http/routes/context.js +82 -0
  61. package/dist/http/routes/context.js.map +1 -0
  62. package/dist/http/routes/log.d.ts +3 -0
  63. package/dist/http/routes/log.d.ts.map +1 -0
  64. package/dist/http/routes/log.js +105 -0
  65. package/dist/http/routes/log.js.map +1 -0
  66. package/dist/http/routes/memo.d.ts +6 -0
  67. package/dist/http/routes/memo.d.ts.map +1 -0
  68. package/dist/http/routes/memo.js +29 -0
  69. package/dist/http/routes/memo.js.map +1 -0
  70. package/dist/http/routes/node.d.ts +3 -0
  71. package/dist/http/routes/node.d.ts.map +1 -0
  72. package/dist/http/routes/node.js +251 -0
  73. package/dist/http/routes/node.js.map +1 -0
  74. package/dist/http/routes/state.d.ts +3 -0
  75. package/dist/http/routes/state.d.ts.map +1 -0
  76. package/dist/http/routes/state.js +48 -0
  77. package/dist/http/routes/state.js.map +1 -0
  78. package/dist/http/routes/workspace.d.ts +3 -0
  79. package/dist/http/routes/workspace.d.ts.map +1 -0
  80. package/dist/http/routes/workspace.js +249 -0
  81. package/dist/http/routes/workspace.js.map +1 -0
  82. package/dist/http/server.d.ts +10 -0
  83. package/dist/http/server.d.ts.map +1 -0
  84. package/dist/http/server.js +284 -0
  85. package/dist/http/server.js.map +1 -0
  86. package/dist/http/services.d.ts +93 -0
  87. package/dist/http/services.d.ts.map +1 -0
  88. package/dist/http/services.js +297 -0
  89. package/dist/http/services.js.map +1 -0
  90. package/dist/index.d.ts +3 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +1073 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/prompts/guidanceContent.d.ts +18 -0
  95. package/dist/prompts/guidanceContent.d.ts.map +1 -0
  96. package/dist/prompts/guidanceContent.js +814 -0
  97. package/dist/prompts/guidanceContent.js.map +1 -0
  98. package/dist/prompts/index.d.ts +2 -0
  99. package/dist/prompts/index.d.ts.map +1 -0
  100. package/dist/prompts/index.js +4 -0
  101. package/dist/prompts/index.js.map +1 -0
  102. package/dist/prompts/instructions.d.ts +56 -0
  103. package/dist/prompts/instructions.d.ts.map +1 -0
  104. package/dist/prompts/instructions.js +1343 -0
  105. package/dist/prompts/instructions.js.map +1 -0
  106. package/dist/services/BackupService.d.ts +104 -0
  107. package/dist/services/BackupService.d.ts.map +1 -0
  108. package/dist/services/BackupService.js +549 -0
  109. package/dist/services/BackupService.js.map +1 -0
  110. package/dist/services/CapabilityService.d.ts +38 -0
  111. package/dist/services/CapabilityService.d.ts.map +1 -0
  112. package/dist/services/CapabilityService.js +256 -0
  113. package/dist/services/CapabilityService.js.map +1 -0
  114. package/dist/services/ConfigService.d.ts +35 -0
  115. package/dist/services/ConfigService.d.ts.map +1 -0
  116. package/dist/services/ConfigService.js +105 -0
  117. package/dist/services/ConfigService.js.map +1 -0
  118. package/dist/services/ContextService.d.ts +65 -0
  119. package/dist/services/ContextService.d.ts.map +1 -0
  120. package/dist/services/ContextService.js +503 -0
  121. package/dist/services/ContextService.js.map +1 -0
  122. package/dist/services/DetectionService.d.ts +76 -0
  123. package/dist/services/DetectionService.d.ts.map +1 -0
  124. package/dist/services/DetectionService.js +262 -0
  125. package/dist/services/DetectionService.js.map +1 -0
  126. package/dist/services/DispatchService.d.ts +267 -0
  127. package/dist/services/DispatchService.d.ts.map +1 -0
  128. package/dist/services/DispatchService.js +1357 -0
  129. package/dist/services/DispatchService.js.map +1 -0
  130. package/dist/services/EventService.d.ts +81 -0
  131. package/dist/services/EventService.d.ts.map +1 -0
  132. package/dist/services/EventService.js +187 -0
  133. package/dist/services/EventService.js.map +1 -0
  134. package/dist/services/GuidanceService.d.ts +64 -0
  135. package/dist/services/GuidanceService.d.ts.map +1 -0
  136. package/dist/services/GuidanceService.js +259 -0
  137. package/dist/services/GuidanceService.js.map +1 -0
  138. package/dist/services/HealthService.d.ts +43 -0
  139. package/dist/services/HealthService.d.ts.map +1 -0
  140. package/dist/services/HealthService.js +276 -0
  141. package/dist/services/HealthService.js.map +1 -0
  142. package/dist/services/InstallationService.d.ts +62 -0
  143. package/dist/services/InstallationService.d.ts.map +1 -0
  144. package/dist/services/InstallationService.js +204 -0
  145. package/dist/services/InstallationService.js.map +1 -0
  146. package/dist/services/LogService.d.ts +35 -0
  147. package/dist/services/LogService.d.ts.map +1 -0
  148. package/dist/services/LogService.js +189 -0
  149. package/dist/services/LogService.js.map +1 -0
  150. package/dist/services/MemoService.d.ts +39 -0
  151. package/dist/services/MemoService.d.ts.map +1 -0
  152. package/dist/services/MemoService.js +288 -0
  153. package/dist/services/MemoService.js.map +1 -0
  154. package/dist/services/NodeService.d.ts +90 -0
  155. package/dist/services/NodeService.d.ts.map +1 -0
  156. package/dist/services/NodeService.js +958 -0
  157. package/dist/services/NodeService.js.map +1 -0
  158. package/dist/services/OpenSpecParser.d.ts +43 -0
  159. package/dist/services/OpenSpecParser.d.ts.map +1 -0
  160. package/dist/services/OpenSpecParser.js +191 -0
  161. package/dist/services/OpenSpecParser.js.map +1 -0
  162. package/dist/services/ReferenceService.d.ts +35 -0
  163. package/dist/services/ReferenceService.d.ts.map +1 -0
  164. package/dist/services/ReferenceService.js +195 -0
  165. package/dist/services/ReferenceService.js.map +1 -0
  166. package/dist/services/RepairService.d.ts +36 -0
  167. package/dist/services/RepairService.d.ts.map +1 -0
  168. package/dist/services/RepairService.js +429 -0
  169. package/dist/services/RepairService.js.map +1 -0
  170. package/dist/services/SearchService.d.ts +34 -0
  171. package/dist/services/SearchService.d.ts.map +1 -0
  172. package/dist/services/SearchService.js +293 -0
  173. package/dist/services/SearchService.js.map +1 -0
  174. package/dist/services/SessionService.d.ts +136 -0
  175. package/dist/services/SessionService.d.ts.map +1 -0
  176. package/dist/services/SessionService.js +297 -0
  177. package/dist/services/SessionService.js.map +1 -0
  178. package/dist/services/StateService.d.ts +97 -0
  179. package/dist/services/StateService.d.ts.map +1 -0
  180. package/dist/services/StateService.js +846 -0
  181. package/dist/services/StateService.js.map +1 -0
  182. package/dist/services/TutorialService.d.ts +114 -0
  183. package/dist/services/TutorialService.d.ts.map +1 -0
  184. package/dist/services/TutorialService.js +1262 -0
  185. package/dist/services/TutorialService.js.map +1 -0
  186. package/dist/services/WorkspaceService.d.ts +273 -0
  187. package/dist/services/WorkspaceService.d.ts.map +1 -0
  188. package/dist/services/WorkspaceService.js +1764 -0
  189. package/dist/services/WorkspaceService.js.map +1 -0
  190. package/dist/services/index.d.ts +15 -0
  191. package/dist/services/index.d.ts.map +1 -0
  192. package/dist/services/index.js +14 -0
  193. package/dist/services/index.js.map +1 -0
  194. package/dist/storage/FileSystemAdapter.d.ts +223 -0
  195. package/dist/storage/FileSystemAdapter.d.ts.map +1 -0
  196. package/dist/storage/FileSystemAdapter.js +384 -0
  197. package/dist/storage/FileSystemAdapter.js.map +1 -0
  198. package/dist/storage/JsonStorage.d.ts +158 -0
  199. package/dist/storage/JsonStorage.d.ts.map +1 -0
  200. package/dist/storage/JsonStorage.js +613 -0
  201. package/dist/storage/JsonStorage.js.map +1 -0
  202. package/dist/storage/MarkdownStorage.d.ts +178 -0
  203. package/dist/storage/MarkdownStorage.d.ts.map +1 -0
  204. package/dist/storage/MarkdownStorage.js +918 -0
  205. package/dist/storage/MarkdownStorage.js.map +1 -0
  206. package/dist/storage/SessionBindingStorage.d.ts +69 -0
  207. package/dist/storage/SessionBindingStorage.d.ts.map +1 -0
  208. package/dist/storage/SessionBindingStorage.js +131 -0
  209. package/dist/storage/SessionBindingStorage.js.map +1 -0
  210. package/dist/storage/index.d.ts +6 -0
  211. package/dist/storage/index.d.ts.map +1 -0
  212. package/dist/storage/index.js +6 -0
  213. package/dist/storage/index.js.map +1 -0
  214. package/dist/tools/capability.d.ts +18 -0
  215. package/dist/tools/capability.d.ts.map +1 -0
  216. package/dist/tools/capability.js +73 -0
  217. package/dist/tools/capability.js.map +1 -0
  218. package/dist/tools/config.d.ts +14 -0
  219. package/dist/tools/config.d.ts.map +1 -0
  220. package/dist/tools/config.js +61 -0
  221. package/dist/tools/config.js.map +1 -0
  222. package/dist/tools/context.d.ts +22 -0
  223. package/dist/tools/context.d.ts.map +1 -0
  224. package/dist/tools/context.js +139 -0
  225. package/dist/tools/context.js.map +1 -0
  226. package/dist/tools/dispatch.d.ts +41 -0
  227. package/dist/tools/dispatch.d.ts.map +1 -0
  228. package/dist/tools/dispatch.js +380 -0
  229. package/dist/tools/dispatch.js.map +1 -0
  230. package/dist/tools/help.d.ts +44 -0
  231. package/dist/tools/help.d.ts.map +1 -0
  232. package/dist/tools/help.js +227 -0
  233. package/dist/tools/help.js.map +1 -0
  234. package/dist/tools/import.d.ts +17 -0
  235. package/dist/tools/import.d.ts.map +1 -0
  236. package/dist/tools/import.js +96 -0
  237. package/dist/tools/import.js.map +1 -0
  238. package/dist/tools/index.d.ts +12 -0
  239. package/dist/tools/index.d.ts.map +1 -0
  240. package/dist/tools/index.js +13 -0
  241. package/dist/tools/index.js.map +1 -0
  242. package/dist/tools/log.d.ts +21 -0
  243. package/dist/tools/log.d.ts.map +1 -0
  244. package/dist/tools/log.js +93 -0
  245. package/dist/tools/log.js.map +1 -0
  246. package/dist/tools/memo.d.ts +26 -0
  247. package/dist/tools/memo.d.ts.map +1 -0
  248. package/dist/tools/memo.js +188 -0
  249. package/dist/tools/memo.js.map +1 -0
  250. package/dist/tools/node.d.ts +34 -0
  251. package/dist/tools/node.d.ts.map +1 -0
  252. package/dist/tools/node.js +328 -0
  253. package/dist/tools/node.js.map +1 -0
  254. package/dist/tools/search.d.ts +14 -0
  255. package/dist/tools/search.d.ts.map +1 -0
  256. package/dist/tools/search.js +95 -0
  257. package/dist/tools/search.js.map +1 -0
  258. package/dist/tools/session.d.ts +22 -0
  259. package/dist/tools/session.d.ts.map +1 -0
  260. package/dist/tools/session.js +127 -0
  261. package/dist/tools/session.js.map +1 -0
  262. package/dist/tools/state.d.ts +10 -0
  263. package/dist/tools/state.d.ts.map +1 -0
  264. package/dist/tools/state.js +79 -0
  265. package/dist/tools/state.js.map +1 -0
  266. package/dist/tools/workspace.d.ts +38 -0
  267. package/dist/tools/workspace.d.ts.map +1 -0
  268. package/dist/tools/workspace.js +240 -0
  269. package/dist/tools/workspace.js.map +1 -0
  270. package/dist/types/capability.d.ts +36 -0
  271. package/dist/types/capability.d.ts.map +1 -0
  272. package/dist/types/capability.js +3 -0
  273. package/dist/types/capability.js.map +1 -0
  274. package/dist/types/confirmation.d.ts +35 -0
  275. package/dist/types/confirmation.d.ts.map +1 -0
  276. package/dist/types/confirmation.js +3 -0
  277. package/dist/types/confirmation.js.map +1 -0
  278. package/dist/types/context.d.ts +174 -0
  279. package/dist/types/context.d.ts.map +1 -0
  280. package/dist/types/context.js +3 -0
  281. package/dist/types/context.js.map +1 -0
  282. package/dist/types/errors.d.ts +81 -0
  283. package/dist/types/errors.d.ts.map +1 -0
  284. package/dist/types/errors.js +154 -0
  285. package/dist/types/errors.js.map +1 -0
  286. package/dist/types/guidance.d.ts +162 -0
  287. package/dist/types/guidance.d.ts.map +1 -0
  288. package/dist/types/guidance.js +4 -0
  289. package/dist/types/guidance.js.map +1 -0
  290. package/dist/types/health.d.ts +61 -0
  291. package/dist/types/health.d.ts.map +1 -0
  292. package/dist/types/health.js +3 -0
  293. package/dist/types/health.js.map +1 -0
  294. package/dist/types/index.d.ts +10 -0
  295. package/dist/types/index.d.ts.map +1 -0
  296. package/dist/types/index.js +11 -0
  297. package/dist/types/index.js.map +1 -0
  298. package/dist/types/memo.d.ts +132 -0
  299. package/dist/types/memo.d.ts.map +1 -0
  300. package/dist/types/memo.js +3 -0
  301. package/dist/types/memo.js.map +1 -0
  302. package/dist/types/node.d.ts +316 -0
  303. package/dist/types/node.d.ts.map +1 -0
  304. package/dist/types/node.js +3 -0
  305. package/dist/types/node.js.map +1 -0
  306. package/dist/types/repair.d.ts +62 -0
  307. package/dist/types/repair.d.ts.map +1 -0
  308. package/dist/types/repair.js +4 -0
  309. package/dist/types/repair.js.map +1 -0
  310. package/dist/types/search.d.ts +58 -0
  311. package/dist/types/search.d.ts.map +1 -0
  312. package/dist/types/search.js +3 -0
  313. package/dist/types/search.js.map +1 -0
  314. package/dist/types/settings.d.ts +109 -0
  315. package/dist/types/settings.d.ts.map +1 -0
  316. package/dist/types/settings.js +30 -0
  317. package/dist/types/settings.js.map +1 -0
  318. package/dist/types/workspace.d.ts +357 -0
  319. package/dist/types/workspace.d.ts.map +1 -0
  320. package/dist/types/workspace.js +3 -0
  321. package/dist/types/workspace.js.map +1 -0
  322. package/dist/utils/contentValidation.d.ts +47 -0
  323. package/dist/utils/contentValidation.d.ts.map +1 -0
  324. package/dist/utils/contentValidation.js +93 -0
  325. package/dist/utils/contentValidation.js.map +1 -0
  326. package/dist/utils/devLog.d.ts +43 -0
  327. package/dist/utils/devLog.d.ts.map +1 -0
  328. package/dist/utils/devLog.js +94 -0
  329. package/dist/utils/devLog.js.map +1 -0
  330. package/dist/utils/errorLogger.d.ts +27 -0
  331. package/dist/utils/errorLogger.d.ts.map +1 -0
  332. package/dist/utils/errorLogger.js +105 -0
  333. package/dist/utils/errorLogger.js.map +1 -0
  334. package/dist/utils/git.d.ts +123 -0
  335. package/dist/utils/git.d.ts.map +1 -0
  336. package/dist/utils/git.js +400 -0
  337. package/dist/utils/git.js.map +1 -0
  338. package/dist/utils/hash.d.ts +32 -0
  339. package/dist/utils/hash.d.ts.map +1 -0
  340. package/dist/utils/hash.js +37 -0
  341. package/dist/utils/hash.js.map +1 -0
  342. package/dist/utils/id.d.ts +54 -0
  343. package/dist/utils/id.d.ts.map +1 -0
  344. package/dist/utils/id.js +96 -0
  345. package/dist/utils/id.js.map +1 -0
  346. package/dist/utils/index.d.ts +8 -0
  347. package/dist/utils/index.d.ts.map +1 -0
  348. package/dist/utils/index.js +9 -0
  349. package/dist/utils/index.js.map +1 -0
  350. package/dist/utils/logger.d.ts +42 -0
  351. package/dist/utils/logger.d.ts.map +1 -0
  352. package/dist/utils/logger.js +228 -0
  353. package/dist/utils/logger.js.map +1 -0
  354. package/dist/utils/manualChangeFormatter.d.ts +8 -0
  355. package/dist/utils/manualChangeFormatter.d.ts.map +1 -0
  356. package/dist/utils/manualChangeFormatter.js +21 -0
  357. package/dist/utils/manualChangeFormatter.js.map +1 -0
  358. package/dist/utils/paramValidator.d.ts +35 -0
  359. package/dist/utils/paramValidator.d.ts.map +1 -0
  360. package/dist/utils/paramValidator.js +214 -0
  361. package/dist/utils/paramValidator.js.map +1 -0
  362. package/dist/utils/port.d.ts +7 -0
  363. package/dist/utils/port.d.ts.map +1 -0
  364. package/dist/utils/port.js +28 -0
  365. package/dist/utils/port.js.map +1 -0
  366. package/dist/utils/processManager.d.ts +53 -0
  367. package/dist/utils/processManager.d.ts.map +1 -0
  368. package/dist/utils/processManager.js +267 -0
  369. package/dist/utils/processManager.js.map +1 -0
  370. package/dist/utils/sessionLogger.d.ts +28 -0
  371. package/dist/utils/sessionLogger.d.ts.map +1 -0
  372. package/dist/utils/sessionLogger.js +142 -0
  373. package/dist/utils/sessionLogger.js.map +1 -0
  374. package/dist/utils/time.d.ts +15 -0
  375. package/dist/utils/time.d.ts.map +1 -0
  376. package/dist/utils/time.js +32 -0
  377. package/dist/utils/time.js.map +1 -0
  378. package/dist/utils/validation.d.ts +23 -0
  379. package/dist/utils/validation.d.ts.map +1 -0
  380. package/dist/utils/validation.js +88 -0
  381. package/dist/utils/validation.js.map +1 -0
  382. package/docs//346/227/245/345/277/227/347/263/273/347/273/237.md +389 -0
  383. package/docs//347/224/250/346/210/267/346/211/213/345/206/214.md +1446 -0
  384. package/docs//347/224/250/346/210/267/346/211/213/345/206/214/344/270/216/346/212/200/346/234/257/346/214/207/345/215/227.md +873 -0
  385. package/package.json +94 -0
  386. package/plugin/README.md +141 -0
  387. package/plugin/agents/zero-executor.md +114 -0
  388. package/plugin/agents/zero-reviewer.md +133 -0
  389. package/plugin/docs/diagnostic-guide.md +128 -0
  390. package/plugin/hooks/hooks.json.deprecated +70 -0
  391. package/plugin/scripts/cursor-hook-entry.cjs +217 -0
  392. package/plugin/scripts/hook-entry.cjs +663 -0
  393. package/plugin/scripts/openspec-import.cjs +714 -0
  394. package/plugin/scripts/shared/binding.cjs +98 -0
  395. package/plugin/scripts/shared/config.cjs +65 -0
  396. package/plugin/scripts/shared/context.cjs +120 -0
  397. package/plugin/scripts/shared/index.cjs +34 -0
  398. package/plugin/scripts/shared/logger.cjs +196 -0
  399. package/plugin/scripts/shared/reminder.cjs +261 -0
  400. package/plugin/scripts/shared/utils.cjs +62 -0
  401. package/plugin/scripts/shared/workspace.cjs +322 -0
  402. package/plugin/skills/aligning-intent/SKILL.md +275 -0
  403. package/plugin/skills/analyzing-measurements/SKILL.md +223 -0
  404. package/plugin/skills/bootstrapping-workspace/SKILL.md +260 -0
  405. package/plugin/skills/designing-solutions/SKILL.md +363 -0
  406. package/plugin/skills/diagnosing-issues/SKILL.md +219 -0
  407. package/plugin/skills/discovering-context/SKILL.md +283 -0
  408. package/plugin/skills/dispatching-parent/SKILL.md +399 -0
  409. package/plugin/skills/executing-task/SKILL.md +340 -0
  410. package/plugin/skills/memo-create/SKILL.md +222 -0
  411. package/plugin/skills/planning-verification/SKILL.md +245 -0
  412. package/plugin/skills/preparing-dispatch/SKILL.md +299 -0
  413. package/plugin/skills/researching-tech/SKILL.md +223 -0
  414. package/plugin/skills/reviewing-quality/SKILL.md +354 -0
  415. package/plugin/skills/reviewing-spec/SKILL.md +333 -0
  416. package/plugin/skills/starting-info-flow/SKILL.md +196 -0
  417. package/web/README.md +5 -0
  418. package/web/dist/assets/DocsView-Bls_Vjsr.css +1 -0
  419. package/web/dist/assets/DocsView-Cxc0B63r.js +1447 -0
  420. package/web/dist/assets/HomeView-C7df9thb.js +9 -0
  421. package/web/dist/assets/HomeView-ufUdnfHk.css +1 -0
  422. package/web/dist/assets/MarkdownContent-DXp6CtSP.js +308 -0
  423. package/web/dist/assets/MarkdownContent-NFqiOBLH.css +1 -0
  424. package/web/dist/assets/NotFoundView-BYX1oZAn.css +1 -0
  425. package/web/dist/assets/NotFoundView-zrc0lT9q.js +1 -0
  426. package/web/dist/assets/WorkspaceView-BckqgNcX.js +27 -0
  427. package/web/dist/assets/WorkspaceView-J1dgpYMx.css +1 -0
  428. package/web/dist/assets/WsConfirmDialog-C1CvL4my.css +1 -0
  429. package/web/dist/assets/WsConfirmDialog-gLEP7uBD.js +4 -0
  430. package/web/dist/assets/arc-DPkKTkUT.js +1 -0
  431. package/web/dist/assets/architectureDiagram-VXUJARFQ-CEGpqUlZ.js +36 -0
  432. package/web/dist/assets/blockDiagram-VD42YOAC-Bv-mqdQH.js +122 -0
  433. package/web/dist/assets/c4Diagram-YG6GDRKO-DRyPatZ_.js +10 -0
  434. package/web/dist/assets/channel-B84mKLDZ.js +1 -0
  435. package/web/dist/assets/chunk-4BX2VUAB-c7DivX0u.js +1 -0
  436. package/web/dist/assets/chunk-55IACEB6-CGKTaLlo.js +1 -0
  437. package/web/dist/assets/chunk-B4BG7PRW-Czhx5Q_P.js +165 -0
  438. package/web/dist/assets/chunk-DI55MBZ5-CQVA7hcZ.js +220 -0
  439. package/web/dist/assets/chunk-FMBD7UC4-hEiPmi7V.js +15 -0
  440. package/web/dist/assets/chunk-QN33PNHL-rL6yYI-E.js +1 -0
  441. package/web/dist/assets/chunk-QZHKN3VN-BRyHBBzq.js +1 -0
  442. package/web/dist/assets/chunk-TZMSLE5B-D4PXmTz9.js +1 -0
  443. package/web/dist/assets/classDiagram-2ON5EDUG-CNn53ohi.js +1 -0
  444. package/web/dist/assets/classDiagram-v2-WZHVMYZB-CNn53ohi.js +1 -0
  445. package/web/dist/assets/cose-bilkent-S5V4N54A-BAREnRga.js +1 -0
  446. package/web/dist/assets/cytoscape.esm-BnkdMOzK.js +321 -0
  447. package/web/dist/assets/dagre-6UL2VRFP-DaYzb3MT.js +4 -0
  448. package/web/dist/assets/defaultLocale-C4B-KCzX.js +1 -0
  449. package/web/dist/assets/diagram-PSM6KHXK-BFltDqvd.js +24 -0
  450. package/web/dist/assets/diagram-QEK2KX5R-CR4VU2La.js +43 -0
  451. package/web/dist/assets/diagram-S2PKOQOG-0UfIeT-1.js +24 -0
  452. package/web/dist/assets/erDiagram-Q2GNP2WA-Bo17Xmng.js +60 -0
  453. package/web/dist/assets/flowDiagram-NV44I4VS-CzqhQp8s.js +162 -0
  454. package/web/dist/assets/ganttDiagram-JELNMOA3-TXwXtUcq.js +267 -0
  455. package/web/dist/assets/gitGraphDiagram-NY62KEGX-CoFQTy9O.js +65 -0
  456. package/web/dist/assets/graph-CIQcRIVd.js +1 -0
  457. package/web/dist/assets/index-BgLd_o_M.css +1 -0
  458. package/web/dist/assets/index-Cd_J3fZn.js +30 -0
  459. package/web/dist/assets/infoDiagram-WHAUD3N6-Dq0xXfVu.js +2 -0
  460. package/web/dist/assets/init-Gi6I4Gst.js +1 -0
  461. package/web/dist/assets/journeyDiagram-XKPGCS4Q-jIg5BOfC.js +139 -0
  462. package/web/dist/assets/kanban-definition-3W4ZIXB7-D2giu6aZ.js +89 -0
  463. package/web/dist/assets/katex-XbL3y5x-.js +261 -0
  464. package/web/dist/assets/layout-Bm-XCM-8.js +1 -0
  465. package/web/dist/assets/linear-FbekP9OZ.js +1 -0
  466. package/web/dist/assets/min-BrRCpYmF.js +1 -0
  467. package/web/dist/assets/mindmap-definition-VGOIOE7T-o-4ubbY9.js +68 -0
  468. package/web/dist/assets/noto-emoji-0-400-normal-BTQbhB77.woff +0 -0
  469. package/web/dist/assets/noto-emoji-0-400-normal-DHdy6Uhy.woff2 +0 -0
  470. package/web/dist/assets/noto-emoji-1-400-normal-0IvkdXBB.woff +0 -0
  471. package/web/dist/assets/noto-emoji-1-400-normal-BY9OovbM.woff2 +0 -0
  472. package/web/dist/assets/noto-emoji-10-400-normal-D9w4QCof.woff2 +0 -0
  473. package/web/dist/assets/noto-emoji-10-400-normal-DtCumcZR.woff +0 -0
  474. package/web/dist/assets/noto-emoji-11-400-normal-BboTlyvx.woff +0 -0
  475. package/web/dist/assets/noto-emoji-12-400-normal-BB5pgBKj.woff2 +0 -0
  476. package/web/dist/assets/noto-emoji-12-400-normal-g186qhiA.woff +0 -0
  477. package/web/dist/assets/noto-emoji-2-400-normal-BKCR1azW.woff2 +0 -0
  478. package/web/dist/assets/noto-emoji-2-400-normal-BYH0KhDr.woff +0 -0
  479. package/web/dist/assets/noto-emoji-3-400-normal-CnPTUeEK.woff +0 -0
  480. package/web/dist/assets/noto-emoji-3-400-normal-TrTb2VQM.woff2 +0 -0
  481. package/web/dist/assets/noto-emoji-4-400-normal-BxD0KVdj.woff +0 -0
  482. package/web/dist/assets/noto-emoji-4-400-normal-s_n9EyG1.woff2 +0 -0
  483. package/web/dist/assets/noto-emoji-5-400-normal-C190AIxR.woff +0 -0
  484. package/web/dist/assets/noto-emoji-5-400-normal-Ctfx4xc6.woff2 +0 -0
  485. package/web/dist/assets/noto-emoji-6-400-normal-DlXlXWt7.woff +0 -0
  486. package/web/dist/assets/noto-emoji-6-400-normal-NzsjD754.woff2 +0 -0
  487. package/web/dist/assets/noto-emoji-7-400-normal-BHP8KeA6.woff2 +0 -0
  488. package/web/dist/assets/noto-emoji-7-400-normal-CtuKhtAZ.woff +0 -0
  489. package/web/dist/assets/noto-emoji-8-400-normal-DR49ZFe7.woff +0 -0
  490. package/web/dist/assets/noto-emoji-8-400-normal-Dvmkf6b2.woff2 +0 -0
  491. package/web/dist/assets/noto-emoji-9-400-normal-BeHJQ2iK.woff2 +0 -0
  492. package/web/dist/assets/noto-emoji-9-400-normal-BlXmCgeQ.woff +0 -0
  493. package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  494. package/web/dist/assets/pieDiagram-ADFJNKIX-C45wSpld.js +30 -0
  495. package/web/dist/assets/quadrantDiagram-AYHSOK5B-tv-_fe-W.js +7 -0
  496. package/web/dist/assets/reduce-CoLNNlNb.js +1 -0
  497. package/web/dist/assets/requirementDiagram-UZGBJVZJ-Dn6PDfkL.js +64 -0
  498. package/web/dist/assets/sankeyDiagram-TZEHDZUN-CbXTZAsG.js +10 -0
  499. package/web/dist/assets/sequenceDiagram-WL72ISMW-B-o1CUJ5.js +145 -0
  500. package/web/dist/assets/stateDiagram-FKZM4ZOC-hzrupXQi.js +1 -0
  501. package/web/dist/assets/stateDiagram-v2-4FDKWEC3-DN-c2M96.js +1 -0
  502. package/web/dist/assets/timeline-definition-IT6M3QCI-R5SP9GDo.js +61 -0
  503. package/web/dist/assets/treemap-KMMF4GRG-Pg9KlUOt.js +128 -0
  504. package/web/dist/assets/xychartDiagram-PRI3JC2R-C3vbJhd1.js +7 -0
  505. package/web/dist/favicon.svg +13 -0
  506. package/web/dist/index.html +17 -0
  507. package//351/205/215/347/275/256/346/226/271/345/274/217.md +330 -0
@@ -0,0 +1,730 @@
1
+ // src/http/routes/admin.ts
2
+ // 索引管理 API 路由
3
+ import { exec } from "child_process";
4
+ import { promisify } from "util";
5
+ import os from "os";
6
+ import { existsSync, statSync, createWriteStream, readFileSync, cpSync, mkdirSync } from "fs";
7
+ import { mkdir, unlink } from "fs/promises";
8
+ import { basename, dirname, join } from "path";
9
+ import { pipeline } from "stream/promises";
10
+ import { scanForProjects, readWorkspacesFromProject, verifyWorkspace, readIndex, writeIndex, } from "../../cli/rebuild.js";
11
+ import { getServices } from "../services.js";
12
+ const execAsync = promisify(exec);
13
+ // 开发环境判断
14
+ const IS_DEV = process.env.NODE_ENV === "development" || process.env.ZERO_DEV === "true";
15
+ // 工作区目录名称(根据环境)
16
+ const FOLDER_NAME = IS_DEV ? ".zero-workspace-dev" : ".zero-workspace";
17
+ // ============================================================================
18
+ // 索引操作队列(防止并发写入竞态条件)
19
+ // ============================================================================
20
+ let indexOperationQueue = Promise.resolve();
21
+ /**
22
+ * 序列化索引操作,确保不会有并发读写冲突
23
+ * 所有修改索引的操作都应该通过这个函数执行
24
+ */
25
+ async function withIndexLock(operation) {
26
+ const currentOperation = indexOperationQueue.then(async () => {
27
+ return await operation();
28
+ });
29
+ // 更新队列,但不让错误阻塞后续操作
30
+ indexOperationQueue = currentOperation.catch(() => { });
31
+ return currentOperation;
32
+ }
33
+ /**
34
+ * 跨平台原生文件夹选择对话框
35
+ * 支持 macOS 和 Windows
36
+ */
37
+ async function selectFolder() {
38
+ const platform = os.platform();
39
+ try {
40
+ if (platform === "darwin") {
41
+ // macOS: 使用 AppleScript
42
+ const { stdout } = await execAsync(`osascript -e 'POSIX path of (choose folder with prompt "选择目录")'`);
43
+ return stdout.trim();
44
+ }
45
+ else if (platform === "win32") {
46
+ // Windows: 使用 PowerShell + .NET
47
+ const script = `
48
+ Add-Type -AssemblyName System.Windows.Forms
49
+ $dialog = New-Object System.Windows.Forms.FolderBrowserDialog
50
+ $dialog.Description = '选择目录'
51
+ $dialog.RootFolder = 'MyComputer'
52
+ if ($dialog.ShowDialog() -eq 'OK') {
53
+ Write-Output $dialog.SelectedPath
54
+ } else {
55
+ exit 1
56
+ }
57
+ `.replace(/\n/g, " ");
58
+ const { stdout } = await execAsync(`powershell -ExecutionPolicy Bypass -Command "${script}"`);
59
+ return stdout.trim();
60
+ }
61
+ else {
62
+ throw new Error(`不支持的平台: ${platform}`);
63
+ }
64
+ }
65
+ catch (error) {
66
+ // 用户取消或其他错误
67
+ return null;
68
+ }
69
+ }
70
+ /**
71
+ * 验证路径是否为有效目录
72
+ */
73
+ function validateDirectoryPath(inputPath) {
74
+ if (!existsSync(inputPath)) {
75
+ return { valid: false, error: "目录不存在" };
76
+ }
77
+ try {
78
+ const stat = statSync(inputPath);
79
+ if (!stat.isDirectory()) {
80
+ return { valid: false, error: "路径不是目录" };
81
+ }
82
+ }
83
+ catch (e) {
84
+ return { valid: false, error: `无法访问路径: ${e instanceof Error ? e.message : "权限不足"}` };
85
+ }
86
+ return { valid: true };
87
+ }
88
+ /**
89
+ * 尝试作为裸工作区导入(直接包含 workspace.json 或 config.json 的目录)
90
+ * @param dirPath 目录路径
91
+ * @returns 如果是有效的裸工作区,返回工作区信息;否则返回 null 或错误
92
+ */
93
+ function tryImportAsWorkspace(dirPath) {
94
+ // 检查 workspace.json 或 config.json
95
+ const workspacePath = join(dirPath, "workspace.json");
96
+ const configPath = join(dirPath, "config.json");
97
+ const actualPath = existsSync(workspacePath) ? workspacePath : existsSync(configPath) ? configPath : null;
98
+ if (!actualPath) {
99
+ return { success: false, error: "workspace.json 不存在" };
100
+ }
101
+ // 读取并解析配置文件
102
+ let config;
103
+ try {
104
+ const content = readFileSync(actualPath, "utf-8");
105
+ config = JSON.parse(content);
106
+ }
107
+ catch (e) {
108
+ return { success: false, error: `workspace.json 格式错误: ${e instanceof Error ? e.message : "JSON 解析失败"}` };
109
+ }
110
+ // 验证必要字段
111
+ const missingFields = [];
112
+ if (!config.id)
113
+ missingFields.push("id");
114
+ if (!config.name)
115
+ missingFields.push("name");
116
+ if (missingFields.length > 0) {
117
+ return { success: false, error: `workspace.json 缺少必要字段: ${missingFields.join(", ")}` };
118
+ }
119
+ // 构建工作区条目
120
+ // 裸工作区的 projectRoot 是其父目录的父目录(假设结构为 projectRoot/.zero-workspace/dirName)
121
+ // 但对于裸工作区,我们使用其父目录作为 projectRoot
122
+ const dirName = basename(dirPath);
123
+ const parentDir = dirname(dirPath);
124
+ // 检测是否在 .zero-workspace 目录下
125
+ const grandParentDir = dirname(parentDir);
126
+ const isUnderZeroWorkspace = basename(parentDir) === FOLDER_NAME;
127
+ const entry = {
128
+ id: config.id,
129
+ name: config.name,
130
+ projectRoot: isUnderZeroWorkspace ? grandParentDir : parentDir,
131
+ dirName: dirName,
132
+ status: config.status || "active",
133
+ createdAt: config.createdAt || new Date().toISOString(),
134
+ updatedAt: config.updatedAt || new Date().toISOString(),
135
+ };
136
+ return { success: true, entry };
137
+ }
138
+ /**
139
+ * 检测路径类型并智能导入
140
+ * 优先级:裸工作区 → .zero-workspace 目录 → 递归扫描
141
+ * @param inputPath 输入路径
142
+ * @param targetDir 目标目录(仅裸工作区导入时使用,复制工作区到此目录)
143
+ */
144
+ function smartImport(inputPath, targetDir) {
145
+ // 验证路径
146
+ const validation = validateDirectoryPath(inputPath);
147
+ if (!validation.valid) {
148
+ return { success: false, added: 0, existing: 0, workspaces: [], error: validation.error };
149
+ }
150
+ const index = readIndex() || { version: "1.0", workspaces: [] };
151
+ const existingIds = new Set(index.workspaces.map((ws) => ws.id));
152
+ const results = [];
153
+ let added = 0;
154
+ let existing = 0;
155
+ // 检测路径类型
156
+ const dirName = basename(inputPath);
157
+ // 优先级1:尝试作为裸工作区导入(直接包含 workspace.json/config.json 的目录)
158
+ const bareWorkspaceResult = tryImportAsWorkspace(inputPath);
159
+ if (bareWorkspaceResult.success && bareWorkspaceResult.entry) {
160
+ let entry = bareWorkspaceResult.entry;
161
+ // 检查是否是"裸工作区"(不在 .zero-workspace 目录下)
162
+ const parentDirName = basename(dirname(inputPath));
163
+ const isBareWorkspace = parentDirName !== FOLDER_NAME;
164
+ // 裸工作区必须提供目标目录
165
+ if (isBareWorkspace && !targetDir) {
166
+ console.log("[smartImport] 裸工作区缺少 targetDir");
167
+ return { success: false, added: 0, existing: 0, workspaces: [], error: "裸工作区导入需要指定目标目录" };
168
+ }
169
+ console.log("[smartImport] isBareWorkspace:", isBareWorkspace, "targetDir:", targetDir);
170
+ // 如果提供了目标目录,复制工作区到目标目录
171
+ if (targetDir) {
172
+ try {
173
+ // 展开 ~ 为用户主目录
174
+ const expandedTargetDir = targetDir.startsWith("~")
175
+ ? targetDir.replace("~", os.homedir())
176
+ : targetDir;
177
+ // 创建目标目录结构: targetDir/.zero-workspace/
178
+ const zeroDir = join(expandedTargetDir, FOLDER_NAME);
179
+ console.log("[smartImport] 复制目标:", { expandedTargetDir, zeroDir, FOLDER_NAME });
180
+ if (!existsSync(zeroDir)) {
181
+ mkdirSync(zeroDir, { recursive: true });
182
+ }
183
+ // 目标路径: targetDir/.zero-workspace/dirName
184
+ const destPath = join(zeroDir, entry.dirName);
185
+ console.log("[smartImport] 复制:", inputPath, "->", destPath);
186
+ // 检查目标是否已存在
187
+ if (existsSync(destPath)) {
188
+ return { success: false, added: 0, existing: 0, workspaces: [], error: `目标路径已存在: ${destPath}` };
189
+ }
190
+ // 复制工作区目录
191
+ cpSync(inputPath, destPath, { recursive: true });
192
+ console.log("[smartImport] 复制完成, 新 projectRoot:", expandedTargetDir);
193
+ // 更新 entry 的 projectRoot 为新位置
194
+ entry = {
195
+ ...entry,
196
+ projectRoot: expandedTargetDir,
197
+ };
198
+ }
199
+ catch (e) {
200
+ console.error("[smartImport] 复制失败:", e);
201
+ return { success: false, added: 0, existing: 0, workspaces: [], error: `复制工作区失败: ${e instanceof Error ? e.message : "未知错误"}` };
202
+ }
203
+ }
204
+ else {
205
+ console.log("[smartImport] 无 targetDir,原地注册");
206
+ }
207
+ if (existingIds.has(entry.id)) {
208
+ existing++;
209
+ results.push({ id: entry.id, name: entry.name, isNew: false });
210
+ }
211
+ else {
212
+ index.workspaces.push(entry);
213
+ existingIds.add(entry.id);
214
+ added++;
215
+ results.push({ id: entry.id, name: entry.name, isNew: true });
216
+ }
217
+ }
218
+ // 优先级2:是 .zero-workspace 目录
219
+ if (results.length === 0 && dirName === FOLDER_NAME) {
220
+ const projectRoot = dirname(inputPath);
221
+ const workspaces = readWorkspacesFromProject(projectRoot);
222
+ for (const ws of workspaces) {
223
+ if (existingIds.has(ws.id)) {
224
+ existing++;
225
+ results.push({ id: ws.id, name: ws.name, isNew: false });
226
+ }
227
+ else {
228
+ index.workspaces.push(ws);
229
+ existingIds.add(ws.id);
230
+ added++;
231
+ results.push({ id: ws.id, name: ws.name, isNew: true });
232
+ }
233
+ }
234
+ }
235
+ // 优先级3:使用 2 层递归扫描
236
+ if (results.length === 0) {
237
+ const projects = scanForProjects(inputPath, 2);
238
+ for (const project of projects) {
239
+ const workspaces = readWorkspacesFromProject(project);
240
+ for (const ws of workspaces) {
241
+ if (existingIds.has(ws.id)) {
242
+ existing++;
243
+ results.push({ id: ws.id, name: ws.name, isNew: false });
244
+ }
245
+ else {
246
+ index.workspaces.push(ws);
247
+ existingIds.add(ws.id);
248
+ added++;
249
+ results.push({ id: ws.id, name: ws.name, isNew: true });
250
+ }
251
+ }
252
+ }
253
+ }
254
+ // 根据不同情况返回具体错误信息
255
+ if (results.length === 0) {
256
+ // 检查是否是因为 workspace.json 问题
257
+ if (bareWorkspaceResult.error && bareWorkspaceResult.error !== "workspace.json 不存在") {
258
+ // workspace.json 存在但有问题(格式错误或缺少字段)
259
+ return { success: false, added: 0, existing: 0, workspaces: [], error: bareWorkspaceResult.error };
260
+ }
261
+ return { success: false, added: 0, existing: 0, workspaces: [], error: "未找到有效的工作区" };
262
+ }
263
+ // 保存索引
264
+ if (added > 0) {
265
+ writeIndex(index);
266
+ }
267
+ return { success: true, added, existing, workspaces: results };
268
+ }
269
+ /**
270
+ * 导入预检查:分析路径类型,决定是否需要二级弹窗
271
+ * - single: 单工作区(裸工作区或 .twsp),需要选择目标目录
272
+ * - multiple: 多工作区(项目目录或 .zero-workspace),原地注册
273
+ */
274
+ function importPreview(inputPath) {
275
+ // 建议的默认导入目录(根据环境)
276
+ const suggestedTargetDir = `~/${FOLDER_NAME}/import/`;
277
+ // 验证路径
278
+ const validation = validateDirectoryPath(inputPath);
279
+ if (!validation.valid) {
280
+ return { success: false, type: "single", workspaces: [], needsTargetDir: false, error: validation.error };
281
+ }
282
+ const index = readIndex() || { version: "1.0", workspaces: [] };
283
+ const existingIds = new Set(index.workspaces.map((ws) => ws.id));
284
+ const dirName = basename(inputPath);
285
+ // 优先级1:尝试作为裸工作区检测
286
+ const bareWorkspaceResult = tryImportAsWorkspace(inputPath);
287
+ if (bareWorkspaceResult.success && bareWorkspaceResult.entry) {
288
+ const entry = bareWorkspaceResult.entry;
289
+ const isNew = !existingIds.has(entry.id);
290
+ return {
291
+ success: true,
292
+ type: "single",
293
+ workspaces: [{ id: entry.id, name: entry.name, isNew }],
294
+ needsTargetDir: true, // 单工作区需要选择目标目录
295
+ suggestedTargetDir,
296
+ };
297
+ }
298
+ // 优先级2:是 .zero-workspace 目录
299
+ if (dirName === FOLDER_NAME) {
300
+ const projectRoot = dirname(inputPath);
301
+ const workspaces = readWorkspacesFromProject(projectRoot);
302
+ if (workspaces.length > 0) {
303
+ return {
304
+ success: true,
305
+ type: "multiple",
306
+ workspaces: workspaces.map((ws) => ({
307
+ id: ws.id,
308
+ name: ws.name,
309
+ isNew: !existingIds.has(ws.id),
310
+ })),
311
+ needsTargetDir: false, // 多工作区原地注册
312
+ };
313
+ }
314
+ }
315
+ // 优先级3:递归扫描项目目录
316
+ const projects = scanForProjects(inputPath, 2);
317
+ const allWorkspaces = [];
318
+ for (const project of projects) {
319
+ const workspaces = readWorkspacesFromProject(project);
320
+ for (const ws of workspaces) {
321
+ allWorkspaces.push({
322
+ id: ws.id,
323
+ name: ws.name,
324
+ isNew: !existingIds.has(ws.id),
325
+ });
326
+ }
327
+ }
328
+ if (allWorkspaces.length > 0) {
329
+ return {
330
+ success: true,
331
+ type: allWorkspaces.length === 1 ? "single" : "multiple",
332
+ workspaces: allWorkspaces,
333
+ needsTargetDir: allWorkspaces.length === 1, // 单工作区需要选择目标目录
334
+ suggestedTargetDir: allWorkspaces.length === 1 ? suggestedTargetDir : undefined,
335
+ };
336
+ }
337
+ // 没有找到任何工作区
338
+ // 返回详细错误信息
339
+ if (bareWorkspaceResult.error && bareWorkspaceResult.error !== "workspace.json 不存在") {
340
+ return { success: false, type: "single", workspaces: [], needsTargetDir: false, error: bareWorkspaceResult.error };
341
+ }
342
+ return { success: false, type: "single", workspaces: [], needsTargetDir: false, error: "未找到有效的工作区" };
343
+ }
344
+ /**
345
+ * 分析索引状态,找出需要添加和移除的工作区
346
+ * 公共逻辑,供 preview 和 execute 共用
347
+ */
348
+ function analyzeSyncClean() {
349
+ const index = readIndex();
350
+ if (!index || index.workspaces.length === 0) {
351
+ return null;
352
+ }
353
+ const existingIds = new Set(index.workspaces.map((ws) => ws.id));
354
+ const toAdd = [];
355
+ const toRemove = [];
356
+ // 收集所有唯一的 projectRoot
357
+ const projectRoots = new Set();
358
+ for (const ws of index.workspaces) {
359
+ if (ws.projectRoot) {
360
+ projectRoots.add(ws.projectRoot);
361
+ }
362
+ }
363
+ // 扫描每个 projectRoot 发现新工作区
364
+ for (const projectRoot of projectRoots) {
365
+ if (!existsSync(projectRoot)) {
366
+ continue;
367
+ }
368
+ const workspaces = readWorkspacesFromProject(projectRoot);
369
+ for (const ws of workspaces) {
370
+ if (!existingIds.has(ws.id)) {
371
+ toAdd.push({ id: ws.id, name: ws.name, projectRoot, entry: ws });
372
+ }
373
+ }
374
+ }
375
+ // 验证现有工作区,找出无效的
376
+ for (const ws of index.workspaces) {
377
+ const verification = verifyWorkspace(ws);
378
+ if (!verification.valid) {
379
+ toRemove.push({
380
+ id: ws.id,
381
+ name: ws.name,
382
+ reason: verification.reason || "未知原因",
383
+ });
384
+ }
385
+ }
386
+ return { index, existingIds, projectRoots, toAdd, toRemove };
387
+ }
388
+ /**
389
+ * 同步清理预览:扫描已索引路径,返回预览结果(只读操作)
390
+ */
391
+ function syncCleanPreview() {
392
+ const analysis = analyzeSyncClean();
393
+ if (!analysis) {
394
+ return { toAdd: [], toRemove: [] };
395
+ }
396
+ // 预览只返回简化信息,不包含完整 entry
397
+ return {
398
+ toAdd: analysis.toAdd.map(({ id, name, projectRoot }) => ({ id, name, projectRoot })),
399
+ toRemove: analysis.toRemove,
400
+ };
401
+ }
402
+ /**
403
+ * 执行同步清理(写入操作)
404
+ */
405
+ function syncCleanExecute() {
406
+ const analysis = analyzeSyncClean();
407
+ if (!analysis) {
408
+ return { added: 0, removed: 0, addedList: [], removedList: [] };
409
+ }
410
+ const { index, toAdd, toRemove } = analysis;
411
+ const addedList = [];
412
+ const removedList = [];
413
+ const removeIds = new Set(toRemove.map((r) => r.id));
414
+ // 添加新工作区
415
+ for (const item of toAdd) {
416
+ index.workspaces.push(item.entry);
417
+ addedList.push({ id: item.id, name: item.name });
418
+ }
419
+ // 过滤掉无效的工作区
420
+ index.workspaces = index.workspaces.filter((ws) => {
421
+ if (removeIds.has(ws.id)) {
422
+ removedList.push({ id: ws.id, name: ws.name });
423
+ return false;
424
+ }
425
+ return true;
426
+ });
427
+ // 只在有变更时写入
428
+ if (addedList.length > 0 || removedList.length > 0) {
429
+ writeIndex(index);
430
+ }
431
+ return {
432
+ added: addedList.length,
433
+ removed: removedList.length,
434
+ addedList,
435
+ removedList,
436
+ };
437
+ }
438
+ // JSON Schema 定义
439
+ const importSchema = {
440
+ body: {
441
+ type: "object",
442
+ required: ["path"],
443
+ properties: {
444
+ path: { type: "string", minLength: 1 },
445
+ targetDir: { type: "string" },
446
+ },
447
+ additionalProperties: false,
448
+ },
449
+ };
450
+ export async function adminRoutes(fastify) {
451
+ /**
452
+ * POST /api/admin/pick-directory - 打开原生目录选择对话框
453
+ */
454
+ fastify.post("/admin/pick-directory", async (_request, reply) => {
455
+ try {
456
+ const result = await selectFolder();
457
+ if (result) {
458
+ return { path: result };
459
+ }
460
+ else {
461
+ return { cancelled: true };
462
+ }
463
+ }
464
+ catch (error) {
465
+ fastify.log.error(error, "打开目录选择对话框失败");
466
+ return reply.status(500).send({
467
+ error: "DIALOG_ERROR",
468
+ message: error instanceof Error ? error.message : "无法打开目录选择对话框",
469
+ });
470
+ }
471
+ });
472
+ /**
473
+ * POST /api/admin/import - 智能导入工作区
474
+ * 支持:项目目录、.zero-workspace 目录、名称_id 工作区目录
475
+ * 使用 2 层递归扫描
476
+ * 使用操作队列防止并发写入冲突
477
+ */
478
+ fastify.post("/admin/import", { schema: importSchema }, async (request, reply) => {
479
+ const { path, targetDir } = request.body;
480
+ fastify.log.info({ path, targetDir, FOLDER_NAME }, "导入请求参数");
481
+ try {
482
+ // 使用锁序列化索引操作
483
+ const result = await withIndexLock(() => smartImport(path, targetDir));
484
+ fastify.log.info({ result }, "导入结果");
485
+ if (!result.success) {
486
+ return reply.status(400).send({
487
+ success: false,
488
+ error: "IMPORT_ERROR",
489
+ message: result.error,
490
+ });
491
+ }
492
+ return {
493
+ success: true,
494
+ added: result.added,
495
+ existing: result.existing,
496
+ workspaces: result.workspaces,
497
+ };
498
+ }
499
+ catch (error) {
500
+ fastify.log.error(error, "导入工作区失败");
501
+ return reply.status(500).send({
502
+ success: false,
503
+ error: "IMPORT_ERROR",
504
+ message: error instanceof Error ? error.message : "导入失败",
505
+ });
506
+ }
507
+ });
508
+ /**
509
+ * POST /api/admin/sync-clean-preview - 同步清理预览
510
+ * 扫描已索引路径,返回将要添加和清理的工作区
511
+ */
512
+ fastify.post("/admin/sync-clean-preview", async (_request, reply) => {
513
+ try {
514
+ const result = syncCleanPreview();
515
+ return {
516
+ toAdd: result.toAdd,
517
+ toRemove: result.toRemove,
518
+ hasChanges: result.toAdd.length > 0 || result.toRemove.length > 0,
519
+ };
520
+ }
521
+ catch (error) {
522
+ fastify.log.error(error, "同步清理预览失败");
523
+ return reply.status(500).send({
524
+ error: "PREVIEW_ERROR",
525
+ message: error instanceof Error ? error.message : "预览失败",
526
+ });
527
+ }
528
+ });
529
+ /**
530
+ * POST /api/admin/sync-clean-execute - 执行同步清理
531
+ * 使用操作队列防止并发写入冲突
532
+ */
533
+ fastify.post("/admin/sync-clean-execute", async (_request, reply) => {
534
+ try {
535
+ // 使用锁序列化索引操作
536
+ const result = await withIndexLock(() => syncCleanExecute());
537
+ return {
538
+ success: true,
539
+ added: result.added,
540
+ removed: result.removed,
541
+ addedList: result.addedList,
542
+ removedList: result.removedList,
543
+ };
544
+ }
545
+ catch (error) {
546
+ fastify.log.error(error, "同步清理执行失败");
547
+ return reply.status(500).send({
548
+ error: "EXECUTE_ERROR",
549
+ message: error instanceof Error ? error.message : "执行失败",
550
+ });
551
+ }
552
+ });
553
+ /**
554
+ * GET /api/admin/index-stats - 获取索引统计信息
555
+ */
556
+ fastify.get("/admin/index-stats", async () => {
557
+ const index = readIndex();
558
+ if (!index) {
559
+ return { total: 0, valid: 0, invalid: 0 };
560
+ }
561
+ let valid = 0;
562
+ let invalid = 0;
563
+ for (const ws of index.workspaces) {
564
+ const verification = verifyWorkspace(ws);
565
+ if (verification.valid) {
566
+ valid++;
567
+ }
568
+ else {
569
+ invalid++;
570
+ }
571
+ }
572
+ return {
573
+ total: index.workspaces.length,
574
+ valid,
575
+ invalid,
576
+ };
577
+ });
578
+ /**
579
+ * GET /api/admin/export-workspace/:workspaceId/check - 预检查工作区导出
580
+ * 返回警告信息,不执行导出
581
+ */
582
+ fastify.get("/admin/export-workspace/:workspaceId/check", async (request, reply) => {
583
+ const { workspaceId } = request.params;
584
+ try {
585
+ const services = getServices();
586
+ const warnings = await services.workspace.checkExportWarnings(workspaceId);
587
+ return reply.send({
588
+ canExport: true,
589
+ warnings,
590
+ });
591
+ }
592
+ catch (error) {
593
+ fastify.log.error(error, "检查工作区导出失败");
594
+ return reply.status(500).send({
595
+ error: "CHECK_ERROR",
596
+ message: error instanceof Error ? error.message : "检查失败",
597
+ });
598
+ }
599
+ });
600
+ /**
601
+ * GET /api/admin/export-workspace/:workspaceId - 导出工作区为 .twsp 文件
602
+ * 响应: application/zip 文件流
603
+ */
604
+ fastify.get("/admin/export-workspace/:workspaceId", async (request, reply) => {
605
+ const { workspaceId } = request.params;
606
+ try {
607
+ const services = getServices();
608
+ const { buffer, filename, warnings } = await services.workspace.exportAsTwsp(workspaceId);
609
+ // 设置响应头
610
+ reply.header("Content-Type", "application/zip");
611
+ reply.header("Content-Disposition", `attachment; filename="${encodeURIComponent(filename)}"`);
612
+ reply.header("Content-Length", buffer.length);
613
+ // 如果有警告,通过自定义 header 传递(Base64 编码避免非 ASCII 字符问题)
614
+ if (warnings.length > 0) {
615
+ reply.header("X-Export-Warnings", Buffer.from(JSON.stringify(warnings)).toString("base64"));
616
+ }
617
+ return reply.send(buffer);
618
+ }
619
+ catch (error) {
620
+ fastify.log.error(error, "导出工作区失败");
621
+ return reply.status(500).send({
622
+ error: "EXPORT_ERROR",
623
+ message: error instanceof Error ? error.message : "导出失败",
624
+ });
625
+ }
626
+ });
627
+ /**
628
+ * POST /api/admin/import-preview - 导入预检查
629
+ * 请求: { path: string }
630
+ * 响应: { success, type: "single"|"multiple", workspaces, needsTargetDir, error? }
631
+ */
632
+ fastify.post("/admin/import-preview", async (request, reply) => {
633
+ const { path: inputPath } = request.body;
634
+ if (!inputPath) {
635
+ return reply.status(400).send({
636
+ error: "MISSING_PATH",
637
+ message: "缺少 path 参数",
638
+ });
639
+ }
640
+ const result = importPreview(inputPath);
641
+ if (!result.success) {
642
+ return reply.status(400).send({
643
+ error: "INVALID_PATH",
644
+ message: result.error,
645
+ ...result,
646
+ });
647
+ }
648
+ return reply.send(result);
649
+ });
650
+ /**
651
+ * POST /api/admin/import-twsp - 导入 .twsp 工作区文件
652
+ * 请求: multipart/form-data,文件字段名 file,可选字段 targetDir
653
+ * 响应: { workspaceId, name, path, warnings }
654
+ */
655
+ fastify.post("/admin/import-twsp", async (request, reply) => {
656
+ let tempFilePath = null;
657
+ try {
658
+ fastify.log.info("开始处理 .twsp 导入请求");
659
+ // 解析 multipart 数据
660
+ // 注意:file 流必须在遍历过程中立即消费,不能等到循环结束后
661
+ const parts = request.parts();
662
+ let filename = null;
663
+ let targetDir;
664
+ // 先准备临时目录
665
+ const tempDir = join(os.tmpdir(), "twsp-upload");
666
+ await mkdir(tempDir, { recursive: true });
667
+ for await (const part of parts) {
668
+ if (part.type === "file" && part.fieldname === "file") {
669
+ filename = part.filename || "upload.twsp";
670
+ fastify.log.info({ filename }, "接收到文件");
671
+ // 验证文件扩展名
672
+ if (!filename.endsWith(".twsp")) {
673
+ // 必须消费掉流,否则会导致连接挂起
674
+ await part.file.resume();
675
+ return reply.status(400).send({
676
+ error: "INVALID_FILE_TYPE",
677
+ message: "仅支持 .twsp 文件",
678
+ });
679
+ }
680
+ // 立即保存到临时文件(在循环内消费流)
681
+ tempFilePath = join(tempDir, `${Date.now()}-${basename(filename)}`);
682
+ const writeStream = createWriteStream(tempFilePath);
683
+ await pipeline(part.file, writeStream);
684
+ fastify.log.info({ tempFilePath }, "文件已保存到临时路径");
685
+ }
686
+ else if (part.type === "field" && part.fieldname === "targetDir") {
687
+ targetDir = part.value;
688
+ fastify.log.info({ targetDir }, "目标目录");
689
+ }
690
+ }
691
+ if (!tempFilePath || !filename) {
692
+ return reply.status(400).send({
693
+ error: "NO_FILE",
694
+ message: "未上传文件",
695
+ });
696
+ }
697
+ // 调用导入服务
698
+ fastify.log.info("开始导入工作区");
699
+ const services = getServices();
700
+ const result = await services.workspace.importFromTwsp(tempFilePath, targetDir || undefined);
701
+ fastify.log.info({ workspaceId: result.workspaceId }, "导入完成");
702
+ return reply.send({
703
+ success: true,
704
+ workspaceId: result.workspaceId,
705
+ name: result.name,
706
+ path: result.path,
707
+ warnings: result.warnings,
708
+ });
709
+ }
710
+ catch (error) {
711
+ fastify.log.error(error, "导入 .twsp 文件失败");
712
+ return reply.status(500).send({
713
+ error: "IMPORT_ERROR",
714
+ message: error instanceof Error ? error.message : "导入失败",
715
+ });
716
+ }
717
+ finally {
718
+ // 清理临时文件
719
+ if (tempFilePath && existsSync(tempFilePath)) {
720
+ try {
721
+ await unlink(tempFilePath);
722
+ }
723
+ catch {
724
+ // 清理失败不影响响应
725
+ }
726
+ }
727
+ }
728
+ });
729
+ }
730
+ //# sourceMappingURL=admin.js.map