runwork 0.15.1 → 0.16.0

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 (404) hide show
  1. package/bundled-types/task-delegation/task-delegation/types.d.ts +6 -0
  2. package/dist/index.js +20237 -116
  3. package/package.json +4 -4
  4. package/dist/__tests__/curl-parser.test.d.ts +0 -1
  5. package/dist/__tests__/curl-parser.test.js +0 -250
  6. package/dist/__tests__/install-scripts.test.d.ts +0 -1
  7. package/dist/__tests__/install-scripts.test.js +0 -280
  8. package/dist/__tests__/types-manager.test.d.ts +0 -1
  9. package/dist/__tests__/types-manager.test.js +0 -89
  10. package/dist/agents/__tests__/chatgpt-registry.test.d.ts +0 -1
  11. package/dist/agents/__tests__/chatgpt-registry.test.js +0 -61
  12. package/dist/agents/__tests__/claude-code-managed-block.test.d.ts +0 -1
  13. package/dist/agents/__tests__/claude-code-managed-block.test.js +0 -105
  14. package/dist/agents/__tests__/claude-code-plugin.test.d.ts +0 -1
  15. package/dist/agents/__tests__/claude-code-plugin.test.js +0 -47
  16. package/dist/agents/__tests__/claude-code-stats.test.d.ts +0 -1
  17. package/dist/agents/__tests__/claude-code-stats.test.js +0 -390
  18. package/dist/agents/__tests__/claude-desktop-memory.test.d.ts +0 -1
  19. package/dist/agents/__tests__/claude-desktop-memory.test.js +0 -154
  20. package/dist/agents/__tests__/claude-desktop-plugin-tree.test.d.ts +0 -1
  21. package/dist/agents/__tests__/claude-desktop-plugin-tree.test.js +0 -161
  22. package/dist/agents/__tests__/claude-desktop-plugin.test.d.ts +0 -1
  23. package/dist/agents/__tests__/claude-desktop-plugin.test.js +0 -117
  24. package/dist/agents/__tests__/claude-desktop-rpm.test.d.ts +0 -1
  25. package/dist/agents/__tests__/claude-desktop-rpm.test.js +0 -126
  26. package/dist/agents/__tests__/claude-desktop-stats.test.d.ts +0 -1
  27. package/dist/agents/__tests__/claude-desktop-stats.test.js +0 -280
  28. package/dist/agents/__tests__/codex-instructions.test.d.ts +0 -1
  29. package/dist/agents/__tests__/codex-instructions.test.js +0 -57
  30. package/dist/agents/__tests__/codex-minimum-permissions.test.d.ts +0 -1
  31. package/dist/agents/__tests__/codex-minimum-permissions.test.js +0 -82
  32. package/dist/agents/__tests__/codex-stats.test.d.ts +0 -1
  33. package/dist/agents/__tests__/codex-stats.test.js +0 -370
  34. package/dist/agents/__tests__/cursor-merge.test.d.ts +0 -1
  35. package/dist/agents/__tests__/cursor-merge.test.js +0 -66
  36. package/dist/agents/__tests__/cursor-stats.test.d.ts +0 -1
  37. package/dist/agents/__tests__/cursor-stats.test.js +0 -183
  38. package/dist/agents/__tests__/defaults-merge.test.d.ts +0 -1
  39. package/dist/agents/__tests__/defaults-merge.test.js +0 -268
  40. package/dist/agents/__tests__/detection.test.d.ts +0 -1
  41. package/dist/agents/__tests__/detection.test.js +0 -89
  42. package/dist/agents/__tests__/graceful-degradation.test.d.ts +0 -1
  43. package/dist/agents/__tests__/graceful-degradation.test.js +0 -129
  44. package/dist/agents/__tests__/instruction-hint.test.d.ts +0 -1
  45. package/dist/agents/__tests__/instruction-hint.test.js +0 -69
  46. package/dist/agents/__tests__/intro-skill.test.d.ts +0 -1
  47. package/dist/agents/__tests__/intro-skill.test.js +0 -193
  48. package/dist/agents/__tests__/schema-validation.test.d.ts +0 -1
  49. package/dist/agents/__tests__/schema-validation.test.js +0 -212
  50. package/dist/agents/__tests__/skill-slug.test.d.ts +0 -1
  51. package/dist/agents/__tests__/skill-slug.test.js +0 -77
  52. package/dist/agents/claude-code.d.ts +0 -59
  53. package/dist/agents/claude-code.js +0 -753
  54. package/dist/agents/claude-desktop-plugin-tree.d.ts +0 -59
  55. package/dist/agents/claude-desktop-plugin-tree.js +0 -143
  56. package/dist/agents/claude-desktop.d.ts +0 -47
  57. package/dist/agents/claude-desktop.js +0 -684
  58. package/dist/agents/cline.d.ts +0 -15
  59. package/dist/agents/cline.js +0 -130
  60. package/dist/agents/codex.d.ts +0 -44
  61. package/dist/agents/codex.js +0 -453
  62. package/dist/agents/conversation-skills.d.ts +0 -18
  63. package/dist/agents/conversation-skills.js +0 -229
  64. package/dist/agents/cursor.d.ts +0 -35
  65. package/dist/agents/cursor.js +0 -294
  66. package/dist/agents/default-config.d.ts +0 -30
  67. package/dist/agents/default-config.js +0 -67
  68. package/dist/agents/defaults-merge.d.ts +0 -87
  69. package/dist/agents/defaults-merge.js +0 -150
  70. package/dist/agents/detect.d.ts +0 -4
  71. package/dist/agents/detect.js +0 -67
  72. package/dist/agents/detection.d.ts +0 -18
  73. package/dist/agents/detection.js +0 -94
  74. package/dist/agents/gemini.d.ts +0 -15
  75. package/dist/agents/gemini.js +0 -122
  76. package/dist/agents/generic-adapter.d.ts +0 -25
  77. package/dist/agents/generic-adapter.js +0 -151
  78. package/dist/agents/intro-skill.d.ts +0 -45
  79. package/dist/agents/intro-skill.js +0 -447
  80. package/dist/agents/registry-data.d.ts +0 -192
  81. package/dist/agents/registry-data.js +0 -669
  82. package/dist/agents/registry.d.ts +0 -13
  83. package/dist/agents/registry.js +0 -36
  84. package/dist/agents/runtime-detection.d.ts +0 -82
  85. package/dist/agents/runtime-detection.js +0 -271
  86. package/dist/agents/session-start-hook.d.ts +0 -37
  87. package/dist/agents/session-start-hook.js +0 -159
  88. package/dist/agents/types.d.ts +0 -171
  89. package/dist/agents/types.js +0 -75
  90. package/dist/agents/utils/instruction-hint.d.ts +0 -21
  91. package/dist/agents/utils/instruction-hint.js +0 -108
  92. package/dist/agents/utils/json-config.d.ts +0 -19
  93. package/dist/agents/utils/json-config.js +0 -69
  94. package/dist/agents/utils/toml-config.d.ts +0 -8
  95. package/dist/agents/utils/toml-config.js +0 -27
  96. package/dist/agents/windsurf.d.ts +0 -14
  97. package/dist/agents/windsurf.js +0 -102
  98. package/dist/api/__tests__/client.test.d.ts +0 -1
  99. package/dist/api/__tests__/client.test.js +0 -272
  100. package/dist/api/client.d.ts +0 -326
  101. package/dist/api/client.js +0 -427
  102. package/dist/auth/__tests__/login-flow.test.d.ts +0 -1
  103. package/dist/auth/__tests__/login-flow.test.js +0 -101
  104. package/dist/auth/__tests__/store.test.d.ts +0 -1
  105. package/dist/auth/__tests__/store.test.js +0 -132
  106. package/dist/auth/login-flow.d.ts +0 -26
  107. package/dist/auth/login-flow.js +0 -137
  108. package/dist/auth/store.d.ts +0 -5
  109. package/dist/auth/store.js +0 -62
  110. package/dist/commands/__tests__/clone-args.test.d.ts +0 -1
  111. package/dist/commands/__tests__/clone-args.test.js +0 -51
  112. package/dist/commands/__tests__/clone-source-result.test.d.ts +0 -1
  113. package/dist/commands/__tests__/clone-source-result.test.js +0 -45
  114. package/dist/commands/__tests__/info-merge.test.d.ts +0 -1
  115. package/dist/commands/__tests__/info-merge.test.js +0 -55
  116. package/dist/commands/__tests__/mcp-entries.test.d.ts +0 -1
  117. package/dist/commands/__tests__/mcp-entries.test.js +0 -48
  118. package/dist/commands/__tests__/setup-persona.test.d.ts +0 -1
  119. package/dist/commands/__tests__/setup-persona.test.js +0 -31
  120. package/dist/commands/__tests__/setup.test.d.ts +0 -1
  121. package/dist/commands/__tests__/setup.test.js +0 -81
  122. package/dist/commands/__tests__/sync-redetect.test.d.ts +0 -1
  123. package/dist/commands/__tests__/sync-redetect.test.js +0 -96
  124. package/dist/commands/__tests__/sync-telemetry.test.d.ts +0 -1
  125. package/dist/commands/__tests__/sync-telemetry.test.js +0 -281
  126. package/dist/commands/__tests__/upgrade.test.d.ts +0 -1
  127. package/dist/commands/__tests__/upgrade.test.js +0 -157
  128. package/dist/commands/agents.d.ts +0 -2
  129. package/dist/commands/agents.js +0 -160
  130. package/dist/commands/api-keys.d.ts +0 -2
  131. package/dist/commands/api-keys.js +0 -95
  132. package/dist/commands/apps.d.ts +0 -2
  133. package/dist/commands/apps.js +0 -82
  134. package/dist/commands/build-plugin.d.ts +0 -2
  135. package/dist/commands/build-plugin.js +0 -135
  136. package/dist/commands/clone.d.ts +0 -60
  137. package/dist/commands/clone.js +0 -271
  138. package/dist/commands/components.d.ts +0 -2
  139. package/dist/commands/components.js +0 -43
  140. package/dist/commands/deploy.d.ts +0 -2
  141. package/dist/commands/deploy.js +0 -279
  142. package/dist/commands/dev.d.ts +0 -11
  143. package/dist/commands/dev.js +0 -1011
  144. package/dist/commands/doctor.d.ts +0 -2
  145. package/dist/commands/doctor.js +0 -230
  146. package/dist/commands/endpoints.d.ts +0 -2
  147. package/dist/commands/endpoints.js +0 -189
  148. package/dist/commands/entities.d.ts +0 -2
  149. package/dist/commands/entities.js +0 -198
  150. package/dist/commands/files.d.ts +0 -2
  151. package/dist/commands/files.js +0 -193
  152. package/dist/commands/inbox.d.ts +0 -11
  153. package/dist/commands/inbox.js +0 -60
  154. package/dist/commands/info.d.ts +0 -178
  155. package/dist/commands/info.js +0 -455
  156. package/dist/commands/init.d.ts +0 -21
  157. package/dist/commands/init.js +0 -185
  158. package/dist/commands/integrations.d.ts +0 -2
  159. package/dist/commands/integrations.js +0 -233
  160. package/dist/commands/login.d.ts +0 -2
  161. package/dist/commands/login.js +0 -44
  162. package/dist/commands/logout.d.ts +0 -2
  163. package/dist/commands/logout.js +0 -13
  164. package/dist/commands/logs.d.ts +0 -2
  165. package/dist/commands/logs.js +0 -232
  166. package/dist/commands/mcp-entries.d.ts +0 -13
  167. package/dist/commands/mcp-entries.js +0 -26
  168. package/dist/commands/mcp.d.ts +0 -2
  169. package/dist/commands/mcp.js +0 -245
  170. package/dist/commands/open.d.ts +0 -2
  171. package/dist/commands/open.js +0 -53
  172. package/dist/commands/resume.d.ts +0 -2
  173. package/dist/commands/resume.js +0 -289
  174. package/dist/commands/save-convo.d.ts +0 -8
  175. package/dist/commands/save-convo.js +0 -20
  176. package/dist/commands/schedules.d.ts +0 -2
  177. package/dist/commands/schedules.js +0 -221
  178. package/dist/commands/setup.d.ts +0 -27
  179. package/dist/commands/setup.js +0 -177
  180. package/dist/commands/share-convo.d.ts +0 -24
  181. package/dist/commands/share-convo.js +0 -167
  182. package/dist/commands/skills.d.ts +0 -2
  183. package/dist/commands/skills.js +0 -352
  184. package/dist/commands/sync-telemetry.d.ts +0 -33
  185. package/dist/commands/sync-telemetry.js +0 -186
  186. package/dist/commands/sync.d.ts +0 -37
  187. package/dist/commands/sync.js +0 -702
  188. package/dist/commands/uninstall.d.ts +0 -2
  189. package/dist/commands/uninstall.js +0 -145
  190. package/dist/commands/upgrade.d.ts +0 -14
  191. package/dist/commands/upgrade.js +0 -217
  192. package/dist/commands/validate.d.ts +0 -2
  193. package/dist/commands/validate.js +0 -89
  194. package/dist/commands/welcome.d.ts +0 -1
  195. package/dist/commands/welcome.js +0 -86
  196. package/dist/commands/workflows.d.ts +0 -2
  197. package/dist/commands/workflows.js +0 -187
  198. package/dist/deploy/__tests__/deploy-state.test.d.ts +0 -1
  199. package/dist/deploy/__tests__/deploy-state.test.js +0 -80
  200. package/dist/deploy/__tests__/deploy-status.test.d.ts +0 -1
  201. package/dist/deploy/__tests__/deploy-status.test.js +0 -41
  202. package/dist/deploy/__tests__/detach-args.test.d.ts +0 -1
  203. package/dist/deploy/__tests__/detach-args.test.js +0 -30
  204. package/dist/deploy/deploy-state.d.ts +0 -36
  205. package/dist/deploy/deploy-state.js +0 -63
  206. package/dist/deploy/deploy-status.d.ts +0 -21
  207. package/dist/deploy/deploy-status.js +0 -36
  208. package/dist/deploy/detach.d.ts +0 -32
  209. package/dist/deploy/detach.js +0 -71
  210. package/dist/dev/__tests__/attach.test.d.ts +0 -1
  211. package/dist/dev/__tests__/attach.test.js +0 -296
  212. package/dist/dev/__tests__/detach.test.d.ts +0 -1
  213. package/dist/dev/__tests__/detach.test.js +0 -404
  214. package/dist/dev/__tests__/preview-url-poller.test.d.ts +0 -1
  215. package/dist/dev/__tests__/preview-url-poller.test.js +0 -149
  216. package/dist/dev/__tests__/session.test.d.ts +0 -1
  217. package/dist/dev/__tests__/session.test.js +0 -446
  218. package/dist/dev/__tests__/startup-sync.test.d.ts +0 -1
  219. package/dist/dev/__tests__/startup-sync.test.js +0 -32
  220. package/dist/dev/__tests__/stop.test.d.ts +0 -1
  221. package/dist/dev/__tests__/stop.test.js +0 -172
  222. package/dist/dev/attach.d.ts +0 -120
  223. package/dist/dev/attach.js +0 -269
  224. package/dist/dev/detach.d.ts +0 -187
  225. package/dist/dev/detach.js +0 -292
  226. package/dist/dev/preview-url-poller.d.ts +0 -35
  227. package/dist/dev/preview-url-poller.js +0 -50
  228. package/dist/dev/session.d.ts +0 -175
  229. package/dist/dev/session.js +0 -258
  230. package/dist/dev/startup-sync.d.ts +0 -23
  231. package/dist/dev/startup-sync.js +0 -14
  232. package/dist/dev/stop.d.ts +0 -52
  233. package/dist/dev/stop.js +0 -101
  234. package/dist/devtools/registry-data.d.ts +0 -46
  235. package/dist/devtools/registry-data.js +0 -40
  236. package/dist/generated/bundled-types.d.ts +0 -1
  237. package/dist/generated/bundled-types.js +0 -46
  238. package/dist/generated/version.d.ts +0 -1
  239. package/dist/generated/version.js +0 -2
  240. package/dist/git/__tests__/auto-commit.test.d.ts +0 -1
  241. package/dist/git/__tests__/auto-commit.test.js +0 -431
  242. package/dist/git/__tests__/classify-sync-error.test.d.ts +0 -1
  243. package/dist/git/__tests__/classify-sync-error.test.js +0 -57
  244. package/dist/git/__tests__/credential-helper-e2e.test.d.ts +0 -21
  245. package/dist/git/__tests__/credential-helper-e2e.test.js +0 -195
  246. package/dist/git/__tests__/credentials.test.d.ts +0 -1
  247. package/dist/git/__tests__/credentials.test.js +0 -141
  248. package/dist/git/__tests__/deploy-guard.test.d.ts +0 -1
  249. package/dist/git/__tests__/deploy-guard.test.js +0 -61
  250. package/dist/git/__tests__/ensure-git-credential-helper.test.d.ts +0 -1
  251. package/dist/git/__tests__/ensure-git-credential-helper.test.js +0 -133
  252. package/dist/git/__tests__/ensure-runwork-remote.test.d.ts +0 -1
  253. package/dist/git/__tests__/ensure-runwork-remote.test.js +0 -109
  254. package/dist/git/__tests__/identity.test.d.ts +0 -1
  255. package/dist/git/__tests__/identity.test.js +0 -146
  256. package/dist/git/__tests__/manifest.test.d.ts +0 -1
  257. package/dist/git/__tests__/manifest.test.js +0 -446
  258. package/dist/git/__tests__/preflight-resolution.test.d.ts +0 -1
  259. package/dist/git/__tests__/preflight-resolution.test.js +0 -366
  260. package/dist/git/__tests__/preflight.test.d.ts +0 -1
  261. package/dist/git/__tests__/preflight.test.js +0 -36
  262. package/dist/git/__tests__/repo-config.test.d.ts +0 -1
  263. package/dist/git/__tests__/repo-config.test.js +0 -26
  264. package/dist/git/__tests__/sync.test.d.ts +0 -1
  265. package/dist/git/__tests__/sync.test.js +0 -405
  266. package/dist/git/auto-commit.d.ts +0 -35
  267. package/dist/git/auto-commit.js +0 -293
  268. package/dist/git/classify-sync-error.d.ts +0 -21
  269. package/dist/git/classify-sync-error.js +0 -111
  270. package/dist/git/credentials.d.ts +0 -75
  271. package/dist/git/credentials.js +0 -164
  272. package/dist/git/critical-files.d.ts +0 -20
  273. package/dist/git/critical-files.js +0 -68
  274. package/dist/git/deploy-guard.d.ts +0 -53
  275. package/dist/git/deploy-guard.js +0 -78
  276. package/dist/git/identity.d.ts +0 -44
  277. package/dist/git/identity.js +0 -133
  278. package/dist/git/preflight.d.ts +0 -57
  279. package/dist/git/preflight.js +0 -276
  280. package/dist/git/remote.d.ts +0 -37
  281. package/dist/git/remote.js +0 -75
  282. package/dist/git/repo-config.d.ts +0 -8
  283. package/dist/git/repo-config.js +0 -29
  284. package/dist/git/sync.d.ts +0 -15
  285. package/dist/git/sync.js +0 -159
  286. package/dist/health/__tests__/checks.test.d.ts +0 -1
  287. package/dist/health/__tests__/checks.test.js +0 -357
  288. package/dist/health/__tests__/cli-distribution-checks.test.d.ts +0 -1
  289. package/dist/health/__tests__/cli-distribution-checks.test.js +0 -196
  290. package/dist/health/__tests__/fix.test.d.ts +0 -1
  291. package/dist/health/__tests__/fix.test.js +0 -72
  292. package/dist/health/__tests__/runner-filter.test.d.ts +0 -1
  293. package/dist/health/__tests__/runner-filter.test.js +0 -61
  294. package/dist/health/checks.d.ts +0 -42
  295. package/dist/health/checks.js +0 -636
  296. package/dist/health/fix.d.ts +0 -22
  297. package/dist/health/fix.js +0 -62
  298. package/dist/health/runner.d.ts +0 -8
  299. package/dist/health/runner.js +0 -47
  300. package/dist/health/types.d.ts +0 -12
  301. package/dist/health/types.js +0 -1
  302. package/dist/index.d.ts +0 -1
  303. package/dist/logs/__tests__/tailer-format.test.d.ts +0 -1
  304. package/dist/logs/__tests__/tailer-format.test.js +0 -43
  305. package/dist/logs/tailer.d.ts +0 -17
  306. package/dist/logs/tailer.js +0 -217
  307. package/dist/sync/__tests__/change-detect.test.d.ts +0 -1
  308. package/dist/sync/__tests__/change-detect.test.js +0 -123
  309. package/dist/sync/__tests__/hash.test.d.ts +0 -1
  310. package/dist/sync/__tests__/hash.test.js +0 -18
  311. package/dist/sync/change-detect.d.ts +0 -19
  312. package/dist/sync/change-detect.js +0 -136
  313. package/dist/sync/conflict-ui.d.ts +0 -5
  314. package/dist/sync/conflict-ui.js +0 -78
  315. package/dist/sync/executor.d.ts +0 -23
  316. package/dist/sync/executor.js +0 -102
  317. package/dist/sync/hash.d.ts +0 -1
  318. package/dist/sync/hash.js +0 -4
  319. package/dist/sync/types.d.ts +0 -24
  320. package/dist/sync/types.js +0 -1
  321. package/dist/template/manifest.d.ts +0 -17
  322. package/dist/template/manifest.js +0 -145
  323. package/dist/tools/types.d.ts +0 -52
  324. package/dist/tools/types.js +0 -8
  325. package/dist/types-manager.d.ts +0 -1
  326. package/dist/types-manager.js +0 -35
  327. package/dist/types.d.ts +0 -349
  328. package/dist/types.js +0 -1
  329. package/dist/ui/__tests__/banner.test.d.ts +0 -1
  330. package/dist/ui/__tests__/banner.test.js +0 -98
  331. package/dist/ui/__tests__/colors.test.d.ts +0 -1
  332. package/dist/ui/__tests__/colors.test.js +0 -22
  333. package/dist/ui/__tests__/keyboard.test.d.ts +0 -1
  334. package/dist/ui/__tests__/keyboard.test.js +0 -34
  335. package/dist/ui/__tests__/status-line.test.d.ts +0 -1
  336. package/dist/ui/__tests__/status-line.test.js +0 -54
  337. package/dist/ui/banner.d.ts +0 -29
  338. package/dist/ui/banner.js +0 -153
  339. package/dist/ui/colors.d.ts +0 -4
  340. package/dist/ui/colors.js +0 -7
  341. package/dist/ui/keyboard.d.ts +0 -12
  342. package/dist/ui/keyboard.js +0 -61
  343. package/dist/ui/status-line.d.ts +0 -6
  344. package/dist/ui/status-line.js +0 -53
  345. package/dist/utils/__tests__/format-error.test.d.ts +0 -1
  346. package/dist/utils/__tests__/format-error.test.js +0 -43
  347. package/dist/utils/__tests__/help-json.test.d.ts +0 -1
  348. package/dist/utils/__tests__/help-json.test.js +0 -39
  349. package/dist/utils/__tests__/http.test.d.ts +0 -1
  350. package/dist/utils/__tests__/http.test.js +0 -381
  351. package/dist/utils/__tests__/ignore-matcher.test.d.ts +0 -1
  352. package/dist/utils/__tests__/ignore-matcher.test.js +0 -205
  353. package/dist/utils/__tests__/output.test.d.ts +0 -1
  354. package/dist/utils/__tests__/output.test.js +0 -38
  355. package/dist/utils/__tests__/prompt.test.d.ts +0 -1
  356. package/dist/utils/__tests__/prompt.test.js +0 -27
  357. package/dist/utils/__tests__/resolve.test.d.ts +0 -1
  358. package/dist/utils/__tests__/resolve.test.js +0 -95
  359. package/dist/utils/__tests__/sqlite.test.d.ts +0 -1
  360. package/dist/utils/__tests__/sqlite.test.js +0 -89
  361. package/dist/utils/agent-guidance.d.ts +0 -47
  362. package/dist/utils/agent-guidance.js +0 -128
  363. package/dist/utils/app-info.d.ts +0 -70
  364. package/dist/utils/app-info.js +0 -167
  365. package/dist/utils/curl-parser.d.ts +0 -12
  366. package/dist/utils/curl-parser.js +0 -162
  367. package/dist/utils/data-input.d.ts +0 -1
  368. package/dist/utils/data-input.js +0 -38
  369. package/dist/utils/format-error.d.ts +0 -10
  370. package/dist/utils/format-error.js +0 -38
  371. package/dist/utils/fs.d.ts +0 -5
  372. package/dist/utils/fs.js +0 -23
  373. package/dist/utils/help-json.d.ts +0 -70
  374. package/dist/utils/help-json.js +0 -88
  375. package/dist/utils/http.d.ts +0 -46
  376. package/dist/utils/http.js +0 -421
  377. package/dist/utils/ignore-matcher.d.ts +0 -47
  378. package/dist/utils/ignore-matcher.js +0 -132
  379. package/dist/utils/output.d.ts +0 -17
  380. package/dist/utils/output.js +0 -27
  381. package/dist/utils/prompt.d.ts +0 -5
  382. package/dist/utils/prompt.js +0 -42
  383. package/dist/utils/resolve.d.ts +0 -12
  384. package/dist/utils/resolve.js +0 -52
  385. package/dist/utils/setup-state.d.ts +0 -3
  386. package/dist/utils/setup-state.js +0 -19
  387. package/dist/utils/sqlite-adapter.d.ts +0 -48
  388. package/dist/utils/sqlite-adapter.js +0 -147
  389. package/dist/utils/sqlite.d.ts +0 -14
  390. package/dist/utils/sqlite.js +0 -14
  391. package/dist/utils/subprocess.d.ts +0 -19
  392. package/dist/utils/subprocess.js +0 -27
  393. package/dist/utils/which.d.ts +0 -14
  394. package/dist/utils/which.js +0 -47
  395. package/dist/utils/zip.d.ts +0 -7
  396. package/dist/utils/zip.js +0 -42
  397. package/dist/validate/__tests__/validate.test.d.ts +0 -1
  398. package/dist/validate/__tests__/validate.test.js +0 -42
  399. package/dist/validate/freshness.d.ts +0 -28
  400. package/dist/validate/freshness.js +0 -48
  401. package/dist/validate/preview.d.ts +0 -21
  402. package/dist/validate/preview.js +0 -39
  403. package/dist/workspace/resolve.d.ts +0 -19
  404. package/dist/workspace/resolve.js +0 -100
@@ -1,146 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
2
- import { execFileSync } from 'child_process';
3
- import { mkdtempSync, rmSync } from 'fs';
4
- import { join } from 'path';
5
- import { tmpdir } from 'os';
6
- import { ensureGitIdentity, __testing } from '../identity.js';
7
- const tempDirs = [];
8
- // Isolate every git invocation in this file from the developer's real
9
- // `~/.gitconfig` (and any system-level config). Without this, anyone running
10
- // the suite locally with `git config --global user.email` already set would
11
- // see the helper short-circuit (correctly) and the "fresh machine" assertions
12
- // would never exercise the write path. We point GIT_CONFIG_GLOBAL/SYSTEM at
13
- // paths that don't exist; git treats missing config files as empty.
14
- const ISOLATION_DIR = mkdtempSync(join(tmpdir(), 'runwork-identity-isolation-'));
15
- const NON_EXISTENT = join(ISOLATION_DIR, 'no-such-config');
16
- const PREVIOUS_ENV = {};
17
- beforeAll(() => {
18
- for (const key of ['GIT_CONFIG_GLOBAL', 'GIT_CONFIG_SYSTEM', 'HOME', 'XDG_CONFIG_HOME']) {
19
- PREVIOUS_ENV[key] = process.env[key];
20
- }
21
- process.env.GIT_CONFIG_GLOBAL = NON_EXISTENT;
22
- process.env.GIT_CONFIG_SYSTEM = NON_EXISTENT;
23
- // Some git builds still consult $HOME/.gitconfig even with GIT_CONFIG_GLOBAL
24
- // set. Pointing HOME at the empty isolation directory closes that hole.
25
- process.env.HOME = ISOLATION_DIR;
26
- process.env.XDG_CONFIG_HOME = ISOLATION_DIR;
27
- });
28
- afterAll(() => {
29
- for (const [key, value] of Object.entries(PREVIOUS_ENV)) {
30
- if (value === undefined)
31
- delete process.env[key];
32
- else
33
- process.env[key] = value;
34
- }
35
- try {
36
- rmSync(ISOLATION_DIR, { recursive: true, force: true });
37
- }
38
- catch { /* best-effort */ }
39
- });
40
- function makeRepo() {
41
- const dir = mkdtempSync(join(tmpdir(), 'runwork-identity-test-'));
42
- tempDirs.push(dir);
43
- execFileSync('git', ['init', dir]);
44
- return dir;
45
- }
46
- function readConfig(cwd, key) {
47
- try {
48
- return execFileSync('git', ['config', '--local', '--get', key], { cwd }).toString('utf-8').trim();
49
- }
50
- catch {
51
- return '';
52
- }
53
- }
54
- afterEach(() => {
55
- for (const dir of tempDirs) {
56
- try {
57
- rmSync(dir, { recursive: true, force: true });
58
- }
59
- catch { /* best-effort */ }
60
- }
61
- tempDirs.length = 0;
62
- });
63
- describe('deriveNameFromEmail', () => {
64
- it('uppercases the local part', () => {
65
- expect(__testing.deriveNameFromEmail('oytun@motaword.com')).toBe('Oytun');
66
- });
67
- it('splits separators into words', () => {
68
- expect(__testing.deriveNameFromEmail('anna.maria@example.com')).toBe('Anna Maria');
69
- expect(__testing.deriveNameFromEmail('jean-luc@example.com')).toBe('Jean Luc');
70
- expect(__testing.deriveNameFromEmail('user_name@example.com')).toBe('User Name');
71
- });
72
- it('falls back to default when local part is empty', () => {
73
- expect(__testing.deriveNameFromEmail('@example.com')).toBe(__testing.FALLBACK_NAME);
74
- });
75
- });
76
- describe('pickIdentity', () => {
77
- it('uses the credentials email when it looks like an email', () => {
78
- expect(__testing.pickIdentity({ apiKey: 'k', email: 'oytun@motaword.com', baseUrl: '' }))
79
- .toEqual({ email: 'oytun@motaword.com', name: 'Oytun' });
80
- });
81
- it('falls back when email is the api-key-auth placeholder', () => {
82
- expect(__testing.pickIdentity({ apiKey: 'k', email: 'api-key-auth', baseUrl: '' }))
83
- .toEqual({ email: __testing.FALLBACK_EMAIL, name: __testing.FALLBACK_NAME });
84
- });
85
- it('falls back when no credentials are passed', () => {
86
- expect(__testing.pickIdentity(null))
87
- .toEqual({ email: __testing.FALLBACK_EMAIL, name: __testing.FALLBACK_NAME });
88
- });
89
- });
90
- describe('ensureGitIdentity', () => {
91
- it('writes local user.email and user.name when none are configured', () => {
92
- const dir = makeRepo();
93
- expect(readConfig(dir, 'user.email')).toBe('');
94
- expect(readConfig(dir, 'user.name')).toBe('');
95
- ensureGitIdentity(dir, { apiKey: 'k', email: 'oytun@motaword.com', baseUrl: '' });
96
- expect(readConfig(dir, 'user.email')).toBe('oytun@motaword.com');
97
- expect(readConfig(dir, 'user.name')).toBe('Oytun');
98
- });
99
- it('overwrites local config with Runwork credentials when they are usable', () => {
100
- // Commits to a Runwork remote should always be attributed to the
101
- // authenticated Runwork user, even if the developer set a local identity
102
- // earlier (e.g. a stale value from a previous account).
103
- const dir = makeRepo();
104
- execFileSync('git', ['config', '--local', 'user.email', 'preset@example.com'], { cwd: dir });
105
- execFileSync('git', ['config', '--local', 'user.name', 'Preset User'], { cwd: dir });
106
- ensureGitIdentity(dir, { apiKey: 'k', email: 'oytun@motaword.com', baseUrl: '' });
107
- expect(readConfig(dir, 'user.email')).toBe('oytun@motaword.com');
108
- expect(readConfig(dir, 'user.name')).toBe('Oytun');
109
- });
110
- it('respects existing local config when no Runwork credentials are available', () => {
111
- // Without a real Runwork identity to attribute the commit to, leave any
112
- // pre-existing local identity (or anything inherited from --global) alone.
113
- const dir = makeRepo();
114
- execFileSync('git', ['config', '--local', 'user.email', 'preset@example.com'], { cwd: dir });
115
- execFileSync('git', ['config', '--local', 'user.name', 'Preset User'], { cwd: dir });
116
- ensureGitIdentity(dir, null);
117
- expect(readConfig(dir, 'user.email')).toBe('preset@example.com');
118
- expect(readConfig(dir, 'user.name')).toBe('Preset User');
119
- });
120
- it('respects existing local config when credentials carry only a placeholder email', () => {
121
- const dir = makeRepo();
122
- execFileSync('git', ['config', '--local', 'user.email', 'preset@example.com'], { cwd: dir });
123
- execFileSync('git', ['config', '--local', 'user.name', 'Preset User'], { cwd: dir });
124
- ensureGitIdentity(dir, { apiKey: 'k', email: 'api-key-auth', baseUrl: '' });
125
- expect(readConfig(dir, 'user.email')).toBe('preset@example.com');
126
- expect(readConfig(dir, 'user.name')).toBe('Preset User');
127
- });
128
- it('falls back to a synthetic identity when credentials are missing', () => {
129
- const dir = makeRepo();
130
- ensureGitIdentity(dir, null);
131
- expect(readConfig(dir, 'user.email')).toBe(__testing.FALLBACK_EMAIL);
132
- expect(readConfig(dir, 'user.name')).toBe(__testing.FALLBACK_NAME);
133
- });
134
- it('lets a real commit succeed on a fresh repo (windows-style "no identity" path)', () => {
135
- const dir = makeRepo();
136
- ensureGitIdentity(dir, { apiKey: 'k', email: 'oytun@motaword.com', baseUrl: '' });
137
- execFileSync('git', ['commit', '--allow-empty', '-m', 'identity probe'], { cwd: dir, stdio: 'pipe' });
138
- const log = execFileSync('git', ['log', '--pretty=%an <%ae>'], { cwd: dir }).toString('utf-8').trim();
139
- expect(log).toContain('Oytun <oytun@motaword.com>');
140
- });
141
- it('is a no-op for non-git directories', () => {
142
- const dir = mkdtempSync(join(tmpdir(), 'runwork-identity-non-git-'));
143
- tempDirs.push(dir);
144
- expect(() => ensureGitIdentity(dir, null)).not.toThrow();
145
- });
146
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,446 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import { execFileSync } from 'child_process';
3
- import { mkdtempSync, writeFileSync, rmSync, existsSync, mkdirSync, symlinkSync, } from 'fs';
4
- import { join } from 'path';
5
- import { tmpdir } from 'os';
6
- import { generateManifest, saveManifest, loadManifest, detectUserEdits, } from '../../template/manifest.js';
7
- const tempDirs = [];
8
- function makeTempDir(prefix) {
9
- const dir = mkdtempSync(join(tmpdir(), `runwork-manifest-test-${prefix}-`));
10
- tempDirs.push(dir);
11
- return dir;
12
- }
13
- function initGitRepo(dir) {
14
- execFileSync('git', ['init', dir]);
15
- execFileSync('git', ['config', 'user.email', 'test@test.com'], { cwd: dir });
16
- execFileSync('git', ['config', 'user.name', 'Test'], { cwd: dir });
17
- }
18
- function commitFile(repoDir, filename, content, message) {
19
- const fullPath = join(repoDir, filename);
20
- const dir = fullPath.substring(0, fullPath.lastIndexOf('/'));
21
- if (dir !== repoDir && !existsSync(dir)) {
22
- mkdirSync(dir, { recursive: true });
23
- }
24
- writeFileSync(fullPath, content);
25
- execFileSync('git', ['add', filename], { cwd: repoDir });
26
- execFileSync('git', ['commit', '-m', message], { cwd: repoDir });
27
- }
28
- afterEach(() => {
29
- for (const dir of tempDirs) {
30
- try {
31
- rmSync(dir, { recursive: true, force: true });
32
- }
33
- catch { /* best effort */ }
34
- }
35
- tempDirs.length = 0;
36
- });
37
- describe('generateManifest()', () => {
38
- it('generates manifest for a simple directory', async () => {
39
- const dir = makeTempDir('simple');
40
- writeFileSync(join(dir, 'a.txt'), 'hello');
41
- writeFileSync(join(dir, 'b.txt'), 'world');
42
- const manifest = await generateManifest(dir);
43
- expect(manifest.version).toBe(1);
44
- expect(Object.keys(manifest.files)).toHaveLength(2);
45
- expect(manifest.files['a.txt']).toMatch(/^sha256:/);
46
- expect(manifest.files['b.txt']).toMatch(/^sha256:/);
47
- });
48
- it('skips .git, node_modules, and .runwork directories', async () => {
49
- const dir = makeTempDir('skip');
50
- writeFileSync(join(dir, 'a.txt'), 'hello');
51
- mkdirSync(join(dir, '.git'));
52
- writeFileSync(join(dir, '.git', 'config'), 'git config');
53
- mkdirSync(join(dir, 'node_modules'));
54
- writeFileSync(join(dir, 'node_modules', 'pkg.js'), 'module');
55
- mkdirSync(join(dir, '.runwork'));
56
- writeFileSync(join(dir, '.runwork', 'manifest.json'), '{}');
57
- const manifest = await generateManifest(dir);
58
- expect(Object.keys(manifest.files)).toHaveLength(1);
59
- expect(manifest.files['a.txt']).toBeDefined();
60
- });
61
- it('skips .dev.vars and .env files', async () => {
62
- const dir = makeTempDir('skipfiles');
63
- writeFileSync(join(dir, 'a.txt'), 'hello');
64
- writeFileSync(join(dir, '.dev.vars'), 'SECRET=123');
65
- writeFileSync(join(dir, '.env'), 'OTHER=456');
66
- const manifest = await generateManifest(dir);
67
- expect(Object.keys(manifest.files)).toHaveLength(1);
68
- expect(manifest.files['a.txt']).toBeDefined();
69
- });
70
- it('skips .bun-cache directory by default (regression: hung dev startup)', async () => {
71
- // Bun's per-project cache used to be walked recursively here. When it
72
- // grew to ~1 GB / 25k+ files, generateManifest spent minutes hashing
73
- // every file and starved the rest of `runwork dev`. The default
74
- // ignore-matcher now skips .bun-cache/ even without a .gitignore.
75
- const dir = makeTempDir('bun-cache');
76
- writeFileSync(join(dir, 'a.txt'), 'hello');
77
- mkdirSync(join(dir, '.bun-cache'));
78
- writeFileSync(join(dir, '.bun-cache', 'pkg.tgz'), 'tarball');
79
- mkdirSync(join(dir, '.bun-cache', 'react@19.0.0'));
80
- writeFileSync(join(dir, '.bun-cache', 'react@19.0.0', 'index.js'), 'r');
81
- const manifest = await generateManifest(dir);
82
- expect(Object.keys(manifest.files)).toEqual(['a.txt']);
83
- });
84
- it('honors simple .gitignore directory entries (e.g. dist/, .next/, coverage)', async () => {
85
- const dir = makeTempDir('gitignored-dirs');
86
- writeFileSync(join(dir, '.gitignore'), 'dist/\n.next/\ncoverage\n');
87
- writeFileSync(join(dir, 'a.txt'), 'hello');
88
- mkdirSync(join(dir, 'dist'));
89
- writeFileSync(join(dir, 'dist', 'bundle.js'), 'compiled');
90
- mkdirSync(join(dir, '.next'));
91
- writeFileSync(join(dir, '.next', 'build.json'), '{}');
92
- mkdirSync(join(dir, 'coverage'));
93
- writeFileSync(join(dir, 'coverage', 'lcov.info'), 'data');
94
- const manifest = await generateManifest(dir);
95
- const keys = Object.keys(manifest.files).sort();
96
- expect(keys).toEqual(['.gitignore', 'a.txt']);
97
- });
98
- it('falls through to defaults when .gitignore patterns are too complex', async () => {
99
- // The walker only honors simple directory/basename entries. Globbed
100
- // patterns are skipped, so files matching them still appear in the
101
- // manifest. This is acceptable: the goal is to avoid the runaway
102
- // cases (huge gitignored caches), not to be a full gitignore matcher.
103
- const dir = makeTempDir('complex-gitignore');
104
- writeFileSync(join(dir, '.gitignore'), '*.local\nsrc/generated/\n');
105
- writeFileSync(join(dir, 'a.txt'), 'hello');
106
- writeFileSync(join(dir, 'config.local'), 'should-still-appear');
107
- const manifest = await generateManifest(dir);
108
- expect(manifest.files['a.txt']).toBeDefined();
109
- expect(manifest.files['config.local']).toBeDefined();
110
- });
111
- it('handles files with spaces in names', async () => {
112
- const dir = makeTempDir('spaces');
113
- writeFileSync(join(dir, 'hello world.txt'), 'content');
114
- writeFileSync(join(dir, 'normal.txt'), 'content');
115
- const manifest = await generateManifest(dir);
116
- expect(Object.keys(manifest.files)).toHaveLength(2);
117
- expect(manifest.files['hello world.txt']).toBeDefined();
118
- });
119
- it('handles nested directories', async () => {
120
- const dir = makeTempDir('nested');
121
- mkdirSync(join(dir, 'src', 'components'), { recursive: true });
122
- writeFileSync(join(dir, 'src', 'index.ts'), 'export {}');
123
- writeFileSync(join(dir, 'src', 'components', 'App.tsx'), '<App/>');
124
- const manifest = await generateManifest(dir);
125
- expect(manifest.files['src/index.ts']).toBeDefined();
126
- expect(manifest.files['src/components/App.tsx']).toBeDefined();
127
- });
128
- });
129
- describe('walkDir() symlink edge cases', () => {
130
- it('BUG PROBE: symlink cycle causes infinite recursion in walkDir/generateManifest', async () => {
131
- // walkDir uses readdirSync with withFileTypes: true.
132
- // Dirent.isDirectory() follows symlinks, so a symlink pointing
133
- // to a parent directory will cause infinite recursion.
134
- const dir = makeTempDir('symlink-cycle');
135
- writeFileSync(join(dir, 'a.txt'), 'hello');
136
- mkdirSync(join(dir, 'subdir'));
137
- writeFileSync(join(dir, 'subdir', 'b.txt'), 'world');
138
- // Create a symlink cycle: subdir/link -> parent dir
139
- symlinkSync(dir, join(dir, 'subdir', 'link'));
140
- // This should not hang or crash, but walkDir follows symlinks
141
- // via isDirectory() which returns true for symlink-to-directory.
142
- // Expected: either it handles symlinks gracefully, or it crashes
143
- // with max call stack exceeded.
144
- let threw = false;
145
- let errorMessage = '';
146
- try {
147
- // Set a timeout to detect infinite loops - if this takes too long, it's a bug
148
- await generateManifest(dir);
149
- }
150
- catch (error) {
151
- threw = true;
152
- errorMessage = error instanceof Error ? error.message : String(error);
153
- }
154
- // If we get here without hanging, check if it threw
155
- // The bug is that walkDir WILL follow the symlink and recurse infinitely
156
- // until it hits ENAMETOOLONG or max call stack.
157
- // We expect this to either throw or produce duplicate entries.
158
- // If it doesn't throw, it somehow handled it (unlikely).
159
- if (threw) {
160
- // Bug confirmed: walkDir crashes on symlink cycles
161
- expect(errorMessage).toBeTruthy();
162
- }
163
- else {
164
- // If it somehow succeeds, check that it didn't include
165
- // infinitely nested paths
166
- expect(true).toBe(true); // pass - it handled it somehow
167
- }
168
- });
169
- it('symlinked directories are silently skipped by walkDir (not followed)', async () => {
170
- // Node.js readdirSync withFileTypes: Dirent.isDirectory() returns false for symlinks.
171
- // So walkDir silently skips symlinked directories entirely.
172
- // This is safe (no infinite recursion) but means symlinked content is invisible.
173
- const dir = makeTempDir('symlink-dir');
174
- const targetDir = makeTempDir('symlink-target');
175
- writeFileSync(join(targetDir, 'external.txt'), 'external content');
176
- writeFileSync(join(dir, 'local.txt'), 'local');
177
- symlinkSync(targetDir, join(dir, 'linked'));
178
- const manifest = await generateManifest(dir);
179
- const keys = Object.keys(manifest.files);
180
- const hasLinkedFile = keys.some(k => k.includes('linked'));
181
- // Symlinked directories are NOT followed -- content is invisible
182
- expect(hasLinkedFile).toBe(false);
183
- expect(keys).toEqual(['local.txt']);
184
- });
185
- });
186
- describe('saveManifest() and loadManifest()', () => {
187
- it('creates .runwork directory if it does not exist', async () => {
188
- const dir = makeTempDir('norunwork');
189
- const manifest = { version: 1, files: { 'a.txt': 'sha256:abc' } };
190
- expect(existsSync(join(dir, '.runwork'))).toBe(false);
191
- await saveManifest(dir, manifest);
192
- expect(existsSync(join(dir, '.runwork', 'template-manifest.json'))).toBe(true);
193
- const loaded = await loadManifest(dir);
194
- expect(loaded).toEqual(manifest);
195
- });
196
- it('returns null when .runwork directory does not exist', async () => {
197
- const dir = makeTempDir('nomanifest');
198
- const result = await loadManifest(dir);
199
- expect(result).toBeNull();
200
- });
201
- it('returns null for corrupted manifest file', async () => {
202
- const dir = makeTempDir('corrupt');
203
- mkdirSync(join(dir, '.runwork'));
204
- writeFileSync(join(dir, '.runwork', 'template-manifest.json'), 'not json{{{');
205
- const result = await loadManifest(dir);
206
- expect(result).toBeNull();
207
- });
208
- });
209
- describe('detectUserEdits() edge cases', () => {
210
- it('returns empty array when git diff and ls-files return nothing', async () => {
211
- // Edge case: empty strings from git commands.
212
- // "".split('\n') returns [''] (array with one empty string).
213
- // The code should handle this gracefully.
214
- const dir = makeTempDir('clean');
215
- initGitRepo(dir);
216
- const content = 'hello';
217
- writeFileSync(join(dir, 'a.txt'), content);
218
- execFileSync('git', ['add', 'a.txt'], { cwd: dir });
219
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
220
- // Use correct hash so manifest scan doesn't flag it as modified
221
- const { createHash } = await import('crypto');
222
- const hash = 'sha256:' + createHash('sha256').update(Buffer.from(content)).digest('hex');
223
- const manifest = {
224
- version: 1,
225
- files: { 'a.txt': hash },
226
- };
227
- const edits = await detectUserEdits(dir, manifest);
228
- expect(edits).toEqual([]);
229
- });
230
- it('BUG PROBE: files with spaces in names from git diff output', async () => {
231
- // git diff --name-only returns filenames with spaces as-is.
232
- // The code splits by \n and processes each line.
233
- // This should work unless there's quoting involved.
234
- const dir = makeTempDir('spaces');
235
- initGitRepo(dir);
236
- writeFileSync(join(dir, 'hello world.txt'), 'original');
237
- execFileSync('git', ['add', 'hello world.txt'], { cwd: dir });
238
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
239
- // Modify the file with spaces
240
- writeFileSync(join(dir, 'hello world.txt'), 'modified');
241
- const manifest = { version: 1, files: {} };
242
- const edits = await detectUserEdits(dir, manifest);
243
- // Should contain the file with spaces
244
- expect(edits).toContain('hello world.txt');
245
- });
246
- it('returns correct unicode filenames (not octal-escaped)', async () => {
247
- const dir = makeTempDir('unicode');
248
- initGitRepo(dir);
249
- const filename = 'caf\u00e9.txt';
250
- writeFileSync(join(dir, filename), 'original');
251
- execFileSync('git', ['add', filename], { cwd: dir });
252
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
253
- writeFileSync(join(dir, filename), 'modified');
254
- const manifest = { version: 1, files: {} };
255
- const edits = await detectUserEdits(dir, manifest);
256
- expect(edits).toContain(filename);
257
- // Should NOT contain octal-escaped garbage
258
- expect(edits.some(e => e.includes('\\303'))).toBe(false);
259
- });
260
- it('BUG PROBE: file deleted by user that was in manifest (untracked section)', async () => {
261
- // If a user deletes a file that was in the manifest, it won't
262
- // appear in git ls-files --others (because it doesn't exist on disk).
263
- // It also won't appear in git diff --name-only HEAD because it was
264
- // never committed. So detectUserEdits will miss it entirely.
265
- // This is arguably correct behavior (delete = no edit to detect),
266
- // but worth documenting.
267
- const dir = makeTempDir('deleted');
268
- initGitRepo(dir);
269
- writeFileSync(join(dir, 'committed.txt'), 'hello');
270
- execFileSync('git', ['add', '.'], { cwd: dir });
271
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
272
- // Template manifest includes both a committed file and a template file
273
- const manifest = {
274
- version: 1,
275
- files: {
276
- 'committed.txt': 'sha256:abc',
277
- 'template-only.txt': 'sha256:def',
278
- },
279
- };
280
- // template-only.txt never existed on disk (or was deleted)
281
- // detectUserEdits should not crash
282
- const edits = await detectUserEdits(dir, manifest);
283
- // No crash is the main assertion
284
- expect(Array.isArray(edits)).toBe(true);
285
- });
286
- it('detects untracked files that are not in the manifest', async () => {
287
- const dir = makeTempDir('newfile');
288
- initGitRepo(dir);
289
- writeFileSync(join(dir, 'a.txt'), 'hello');
290
- execFileSync('git', ['add', '.'], { cwd: dir });
291
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
292
- // User creates a new file
293
- writeFileSync(join(dir, 'user-created.txt'), 'my new file');
294
- const manifest = {
295
- version: 1,
296
- files: { 'a.txt': 'sha256:abc' },
297
- };
298
- const edits = await detectUserEdits(dir, manifest);
299
- expect(edits).toContain('user-created.txt');
300
- });
301
- it('skips untracked file that matches template manifest hash (pristine template file)', async () => {
302
- const dir = makeTempDir('pristine');
303
- initGitRepo(dir);
304
- writeFileSync(join(dir, 'committed.txt'), 'hello');
305
- execFileSync('git', ['add', '.'], { cwd: dir });
306
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
307
- // Create an untracked file that matches the template hash exactly
308
- const content = 'template content';
309
- writeFileSync(join(dir, 'template-file.txt'), content);
310
- // Generate the correct hash for this content
311
- const { createHash } = await import('crypto');
312
- const hash = 'sha256:' + createHash('sha256').update(Buffer.from(content)).digest('hex');
313
- const manifest = {
314
- version: 1,
315
- files: {
316
- 'committed.txt': 'sha256:abc',
317
- 'template-file.txt': hash,
318
- },
319
- };
320
- const edits = await detectUserEdits(dir, manifest);
321
- // Pristine template file should NOT be in edits
322
- expect(edits).not.toContain('template-file.txt');
323
- });
324
- it('BUG PROBE: detects edit when untracked file has different content than manifest hash', async () => {
325
- const dir = makeTempDir('modified-template');
326
- initGitRepo(dir);
327
- writeFileSync(join(dir, 'committed.txt'), 'hello');
328
- execFileSync('git', ['add', '.'], { cwd: dir });
329
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
330
- // Create an untracked file with content that differs from manifest
331
- writeFileSync(join(dir, 'template-file.txt'), 'user modified this');
332
- const manifest = {
333
- version: 1,
334
- files: {
335
- 'committed.txt': 'sha256:abc',
336
- 'template-file.txt': 'sha256:different_hash_from_template',
337
- },
338
- };
339
- const edits = await detectUserEdits(dir, manifest);
340
- // Modified template file SHOULD be in edits
341
- expect(edits).toContain('template-file.txt');
342
- });
343
- it('does not duplicate files that appear in both git diff and git diff --cached', async () => {
344
- const dir = makeTempDir('dedup');
345
- initGitRepo(dir);
346
- writeFileSync(join(dir, 'a.txt'), 'original');
347
- execFileSync('git', ['add', '.'], { cwd: dir });
348
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
349
- // Stage a change AND have unstaged changes to same file
350
- writeFileSync(join(dir, 'a.txt'), 'staged change');
351
- execFileSync('git', ['add', 'a.txt'], { cwd: dir });
352
- writeFileSync(join(dir, 'a.txt'), 'unstaged further change');
353
- const manifest = { version: 1, files: {} };
354
- const edits = await detectUserEdits(dir, manifest);
355
- // File should appear only once even though it's in both diff outputs
356
- const count = edits.filter(f => f === 'a.txt').length;
357
- expect(count).toBe(1);
358
- });
359
- it('BUG PROBE: git diff --name-only HEAD with no commits yet', async () => {
360
- // When repo has no commits, git rev-parse HEAD fails.
361
- // The code wraps this in try/catch, so it should skip the git diff section.
362
- // But what about git ls-files --others?
363
- const dir = makeTempDir('nocommits');
364
- initGitRepo(dir);
365
- // Create a file but don't commit
366
- writeFileSync(join(dir, 'new-file.txt'), 'hello');
367
- const manifest = { version: 1, files: {} };
368
- const edits = await detectUserEdits(dir, manifest);
369
- // Should detect the untracked file via ls-files --others
370
- expect(edits).toContain('new-file.txt');
371
- });
372
- it('detects modified gitignored files that are in the template manifest', async () => {
373
- // Real-world scenario: template ships with a config file, user adds it
374
- // to .gitignore (e.g., to keep local secrets), then modifies it.
375
- // detectUserEdits should still detect it by scanning manifest files on disk.
376
- const dir = makeTempDir('gitignore');
377
- initGitRepo(dir);
378
- writeFileSync(join(dir, 'a.txt'), 'hello');
379
- writeFileSync(join(dir, '.gitignore'), 'ignored.txt\n');
380
- execFileSync('git', ['add', '.'], { cwd: dir });
381
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
382
- // Create a file that's gitignored but was in the template manifest
383
- writeFileSync(join(dir, 'ignored.txt'), 'user modified this ignored file');
384
- const manifest = {
385
- version: 1,
386
- files: {
387
- 'a.txt': 'sha256:abc',
388
- 'ignored.txt': 'sha256:original_template_hash',
389
- },
390
- };
391
- const edits = await detectUserEdits(dir, manifest);
392
- // Fixed: gitignored file IS detected because we scan manifest files on disk
393
- expect(edits).toContain('ignored.txt');
394
- });
395
- it('does not flag gitignored file if it matches template manifest hash', async () => {
396
- // If the gitignored file has NOT been modified (hash matches manifest),
397
- // it should NOT appear in edits.
398
- const dir = makeTempDir('gitignore-pristine');
399
- initGitRepo(dir);
400
- writeFileSync(join(dir, 'a.txt'), 'hello');
401
- writeFileSync(join(dir, '.gitignore'), 'config.txt\n');
402
- execFileSync('git', ['add', '.'], { cwd: dir });
403
- execFileSync('git', ['commit', '-m', 'init'], { cwd: dir });
404
- const content = 'pristine template content';
405
- writeFileSync(join(dir, 'config.txt'), content);
406
- const { createHash } = await import('crypto');
407
- const hash = 'sha256:' + createHash('sha256').update(Buffer.from(content)).digest('hex');
408
- const manifest = {
409
- version: 1,
410
- files: {
411
- 'a.txt': 'sha256:abc',
412
- 'config.txt': hash,
413
- },
414
- };
415
- const edits = await detectUserEdits(dir, manifest);
416
- expect(edits).not.toContain('config.txt');
417
- });
418
- it('does not flag tracked file whose committed content differs from template manifest', async () => {
419
- // Reproduces the post-`runwork clone` scenario: the manifest was
420
- // generated from the pristine template, then the AI-customized
421
- // version was overlaid via `git fetch` + `git checkout .` and is now
422
- // committed at HEAD with no local edits. Method 3's scan would
423
- // false-flag every such file on every clone if it didn't skip
424
- // tracked files.
425
- const dir = makeTempDir('clone-overlay');
426
- initGitRepo(dir);
427
- // Simulate: AI-customized version is committed at HEAD.
428
- const overlaidContent = 'AI-customized content';
429
- writeFileSync(join(dir, 'worker/agents.ts'.replace('/', '-')), 'placeholder');
430
- const aiFilePath = 'agents.ts';
431
- writeFileSync(join(dir, aiFilePath), overlaidContent);
432
- execFileSync('git', ['add', '.'], { cwd: dir });
433
- execFileSync('git', ['commit', '-m', 'overlay'], { cwd: dir });
434
- // Manifest captured the PRISTINE template hash (different from the
435
- // overlaid HEAD content). detectUserEdits must not treat this as a
436
- // user edit.
437
- const manifest = {
438
- version: 1,
439
- files: {
440
- [aiFilePath]: 'sha256:pristine_template_hash_unrelated_to_disk',
441
- },
442
- };
443
- const edits = await detectUserEdits(dir, manifest);
444
- expect(edits).not.toContain(aiFilePath);
445
- });
446
- });
@@ -1 +0,0 @@
1
- export {};