runwork 0.15.0 → 0.15.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 (403) hide show
  1. package/dist/index.js +19272 -116
  2. package/package.json +4 -4
  3. package/dist/__tests__/curl-parser.test.d.ts +0 -1
  4. package/dist/__tests__/curl-parser.test.js +0 -250
  5. package/dist/__tests__/install-scripts.test.d.ts +0 -1
  6. package/dist/__tests__/install-scripts.test.js +0 -280
  7. package/dist/__tests__/types-manager.test.d.ts +0 -1
  8. package/dist/__tests__/types-manager.test.js +0 -89
  9. package/dist/agents/__tests__/chatgpt-registry.test.d.ts +0 -1
  10. package/dist/agents/__tests__/chatgpt-registry.test.js +0 -61
  11. package/dist/agents/__tests__/claude-code-managed-block.test.d.ts +0 -1
  12. package/dist/agents/__tests__/claude-code-managed-block.test.js +0 -97
  13. package/dist/agents/__tests__/claude-code-plugin.test.d.ts +0 -1
  14. package/dist/agents/__tests__/claude-code-plugin.test.js +0 -47
  15. package/dist/agents/__tests__/claude-code-stats.test.d.ts +0 -1
  16. package/dist/agents/__tests__/claude-code-stats.test.js +0 -390
  17. package/dist/agents/__tests__/claude-desktop-memory.test.d.ts +0 -1
  18. package/dist/agents/__tests__/claude-desktop-memory.test.js +0 -154
  19. package/dist/agents/__tests__/claude-desktop-plugin-tree.test.d.ts +0 -1
  20. package/dist/agents/__tests__/claude-desktop-plugin-tree.test.js +0 -161
  21. package/dist/agents/__tests__/claude-desktop-plugin.test.d.ts +0 -1
  22. package/dist/agents/__tests__/claude-desktop-plugin.test.js +0 -117
  23. package/dist/agents/__tests__/claude-desktop-rpm.test.d.ts +0 -1
  24. package/dist/agents/__tests__/claude-desktop-rpm.test.js +0 -126
  25. package/dist/agents/__tests__/claude-desktop-stats.test.d.ts +0 -1
  26. package/dist/agents/__tests__/claude-desktop-stats.test.js +0 -280
  27. package/dist/agents/__tests__/codex-instructions.test.d.ts +0 -1
  28. package/dist/agents/__tests__/codex-instructions.test.js +0 -57
  29. package/dist/agents/__tests__/codex-minimum-permissions.test.d.ts +0 -1
  30. package/dist/agents/__tests__/codex-minimum-permissions.test.js +0 -82
  31. package/dist/agents/__tests__/codex-stats.test.d.ts +0 -1
  32. package/dist/agents/__tests__/codex-stats.test.js +0 -370
  33. package/dist/agents/__tests__/cursor-merge.test.d.ts +0 -1
  34. package/dist/agents/__tests__/cursor-merge.test.js +0 -66
  35. package/dist/agents/__tests__/cursor-stats.test.d.ts +0 -1
  36. package/dist/agents/__tests__/cursor-stats.test.js +0 -183
  37. package/dist/agents/__tests__/defaults-merge.test.d.ts +0 -1
  38. package/dist/agents/__tests__/defaults-merge.test.js +0 -268
  39. package/dist/agents/__tests__/detection.test.d.ts +0 -1
  40. package/dist/agents/__tests__/detection.test.js +0 -89
  41. package/dist/agents/__tests__/graceful-degradation.test.d.ts +0 -1
  42. package/dist/agents/__tests__/graceful-degradation.test.js +0 -129
  43. package/dist/agents/__tests__/instruction-hint.test.d.ts +0 -1
  44. package/dist/agents/__tests__/instruction-hint.test.js +0 -69
  45. package/dist/agents/__tests__/intro-skill.test.d.ts +0 -1
  46. package/dist/agents/__tests__/intro-skill.test.js +0 -193
  47. package/dist/agents/__tests__/schema-validation.test.d.ts +0 -1
  48. package/dist/agents/__tests__/schema-validation.test.js +0 -212
  49. package/dist/agents/__tests__/skill-slug.test.d.ts +0 -1
  50. package/dist/agents/__tests__/skill-slug.test.js +0 -77
  51. package/dist/agents/claude-code.d.ts +0 -53
  52. package/dist/agents/claude-code.js +0 -754
  53. package/dist/agents/claude-desktop-plugin-tree.d.ts +0 -59
  54. package/dist/agents/claude-desktop-plugin-tree.js +0 -143
  55. package/dist/agents/claude-desktop.d.ts +0 -47
  56. package/dist/agents/claude-desktop.js +0 -684
  57. package/dist/agents/cline.d.ts +0 -15
  58. package/dist/agents/cline.js +0 -130
  59. package/dist/agents/codex.d.ts +0 -44
  60. package/dist/agents/codex.js +0 -453
  61. package/dist/agents/conversation-skills.d.ts +0 -18
  62. package/dist/agents/conversation-skills.js +0 -229
  63. package/dist/agents/cursor.d.ts +0 -48
  64. package/dist/agents/cursor.js +0 -324
  65. package/dist/agents/default-config.d.ts +0 -30
  66. package/dist/agents/default-config.js +0 -67
  67. package/dist/agents/defaults-merge.d.ts +0 -72
  68. package/dist/agents/defaults-merge.js +0 -131
  69. package/dist/agents/detect.d.ts +0 -4
  70. package/dist/agents/detect.js +0 -67
  71. package/dist/agents/detection.d.ts +0 -18
  72. package/dist/agents/detection.js +0 -94
  73. package/dist/agents/gemini.d.ts +0 -15
  74. package/dist/agents/gemini.js +0 -122
  75. package/dist/agents/generic-adapter.d.ts +0 -25
  76. package/dist/agents/generic-adapter.js +0 -151
  77. package/dist/agents/intro-skill.d.ts +0 -45
  78. package/dist/agents/intro-skill.js +0 -447
  79. package/dist/agents/registry-data.d.ts +0 -192
  80. package/dist/agents/registry-data.js +0 -669
  81. package/dist/agents/registry.d.ts +0 -13
  82. package/dist/agents/registry.js +0 -36
  83. package/dist/agents/runtime-detection.d.ts +0 -82
  84. package/dist/agents/runtime-detection.js +0 -271
  85. package/dist/agents/session-start-hook.d.ts +0 -37
  86. package/dist/agents/session-start-hook.js +0 -159
  87. package/dist/agents/types.d.ts +0 -171
  88. package/dist/agents/types.js +0 -75
  89. package/dist/agents/utils/instruction-hint.d.ts +0 -21
  90. package/dist/agents/utils/instruction-hint.js +0 -108
  91. package/dist/agents/utils/json-config.d.ts +0 -19
  92. package/dist/agents/utils/json-config.js +0 -69
  93. package/dist/agents/utils/toml-config.d.ts +0 -8
  94. package/dist/agents/utils/toml-config.js +0 -27
  95. package/dist/agents/windsurf.d.ts +0 -14
  96. package/dist/agents/windsurf.js +0 -102
  97. package/dist/api/__tests__/client.test.d.ts +0 -1
  98. package/dist/api/__tests__/client.test.js +0 -272
  99. package/dist/api/client.d.ts +0 -326
  100. package/dist/api/client.js +0 -427
  101. package/dist/auth/__tests__/login-flow.test.d.ts +0 -1
  102. package/dist/auth/__tests__/login-flow.test.js +0 -101
  103. package/dist/auth/__tests__/store.test.d.ts +0 -1
  104. package/dist/auth/__tests__/store.test.js +0 -132
  105. package/dist/auth/login-flow.d.ts +0 -26
  106. package/dist/auth/login-flow.js +0 -137
  107. package/dist/auth/store.d.ts +0 -5
  108. package/dist/auth/store.js +0 -62
  109. package/dist/commands/__tests__/clone-args.test.d.ts +0 -1
  110. package/dist/commands/__tests__/clone-args.test.js +0 -51
  111. package/dist/commands/__tests__/clone-source-result.test.d.ts +0 -1
  112. package/dist/commands/__tests__/clone-source-result.test.js +0 -45
  113. package/dist/commands/__tests__/info-merge.test.d.ts +0 -1
  114. package/dist/commands/__tests__/info-merge.test.js +0 -55
  115. package/dist/commands/__tests__/mcp-entries.test.d.ts +0 -1
  116. package/dist/commands/__tests__/mcp-entries.test.js +0 -48
  117. package/dist/commands/__tests__/setup-persona.test.d.ts +0 -1
  118. package/dist/commands/__tests__/setup-persona.test.js +0 -31
  119. package/dist/commands/__tests__/setup.test.d.ts +0 -1
  120. package/dist/commands/__tests__/setup.test.js +0 -81
  121. package/dist/commands/__tests__/sync-redetect.test.d.ts +0 -1
  122. package/dist/commands/__tests__/sync-redetect.test.js +0 -96
  123. package/dist/commands/__tests__/sync-telemetry.test.d.ts +0 -1
  124. package/dist/commands/__tests__/sync-telemetry.test.js +0 -281
  125. package/dist/commands/__tests__/upgrade.test.d.ts +0 -1
  126. package/dist/commands/__tests__/upgrade.test.js +0 -157
  127. package/dist/commands/agents.d.ts +0 -2
  128. package/dist/commands/agents.js +0 -160
  129. package/dist/commands/api-keys.d.ts +0 -2
  130. package/dist/commands/api-keys.js +0 -95
  131. package/dist/commands/apps.d.ts +0 -2
  132. package/dist/commands/apps.js +0 -82
  133. package/dist/commands/build-plugin.d.ts +0 -2
  134. package/dist/commands/build-plugin.js +0 -135
  135. package/dist/commands/clone.d.ts +0 -60
  136. package/dist/commands/clone.js +0 -271
  137. package/dist/commands/components.d.ts +0 -2
  138. package/dist/commands/components.js +0 -43
  139. package/dist/commands/deploy.d.ts +0 -2
  140. package/dist/commands/deploy.js +0 -279
  141. package/dist/commands/dev.d.ts +0 -11
  142. package/dist/commands/dev.js +0 -1011
  143. package/dist/commands/doctor.d.ts +0 -2
  144. package/dist/commands/doctor.js +0 -230
  145. package/dist/commands/endpoints.d.ts +0 -2
  146. package/dist/commands/endpoints.js +0 -189
  147. package/dist/commands/entities.d.ts +0 -2
  148. package/dist/commands/entities.js +0 -198
  149. package/dist/commands/files.d.ts +0 -2
  150. package/dist/commands/files.js +0 -193
  151. package/dist/commands/inbox.d.ts +0 -11
  152. package/dist/commands/inbox.js +0 -60
  153. package/dist/commands/info.d.ts +0 -178
  154. package/dist/commands/info.js +0 -455
  155. package/dist/commands/init.d.ts +0 -21
  156. package/dist/commands/init.js +0 -185
  157. package/dist/commands/integrations.d.ts +0 -2
  158. package/dist/commands/integrations.js +0 -233
  159. package/dist/commands/login.d.ts +0 -2
  160. package/dist/commands/login.js +0 -44
  161. package/dist/commands/logout.d.ts +0 -2
  162. package/dist/commands/logout.js +0 -13
  163. package/dist/commands/logs.d.ts +0 -2
  164. package/dist/commands/logs.js +0 -232
  165. package/dist/commands/mcp-entries.d.ts +0 -13
  166. package/dist/commands/mcp-entries.js +0 -26
  167. package/dist/commands/mcp.d.ts +0 -2
  168. package/dist/commands/mcp.js +0 -245
  169. package/dist/commands/open.d.ts +0 -2
  170. package/dist/commands/open.js +0 -53
  171. package/dist/commands/resume.d.ts +0 -2
  172. package/dist/commands/resume.js +0 -289
  173. package/dist/commands/save-convo.d.ts +0 -8
  174. package/dist/commands/save-convo.js +0 -20
  175. package/dist/commands/schedules.d.ts +0 -2
  176. package/dist/commands/schedules.js +0 -221
  177. package/dist/commands/setup.d.ts +0 -27
  178. package/dist/commands/setup.js +0 -177
  179. package/dist/commands/share-convo.d.ts +0 -24
  180. package/dist/commands/share-convo.js +0 -167
  181. package/dist/commands/skills.d.ts +0 -2
  182. package/dist/commands/skills.js +0 -352
  183. package/dist/commands/sync-telemetry.d.ts +0 -33
  184. package/dist/commands/sync-telemetry.js +0 -186
  185. package/dist/commands/sync.d.ts +0 -37
  186. package/dist/commands/sync.js +0 -702
  187. package/dist/commands/uninstall.d.ts +0 -2
  188. package/dist/commands/uninstall.js +0 -145
  189. package/dist/commands/upgrade.d.ts +0 -14
  190. package/dist/commands/upgrade.js +0 -217
  191. package/dist/commands/validate.d.ts +0 -2
  192. package/dist/commands/validate.js +0 -89
  193. package/dist/commands/welcome.d.ts +0 -1
  194. package/dist/commands/welcome.js +0 -86
  195. package/dist/commands/workflows.d.ts +0 -2
  196. package/dist/commands/workflows.js +0 -187
  197. package/dist/deploy/__tests__/deploy-state.test.d.ts +0 -1
  198. package/dist/deploy/__tests__/deploy-state.test.js +0 -80
  199. package/dist/deploy/__tests__/deploy-status.test.d.ts +0 -1
  200. package/dist/deploy/__tests__/deploy-status.test.js +0 -41
  201. package/dist/deploy/__tests__/detach-args.test.d.ts +0 -1
  202. package/dist/deploy/__tests__/detach-args.test.js +0 -30
  203. package/dist/deploy/deploy-state.d.ts +0 -36
  204. package/dist/deploy/deploy-state.js +0 -63
  205. package/dist/deploy/deploy-status.d.ts +0 -21
  206. package/dist/deploy/deploy-status.js +0 -36
  207. package/dist/deploy/detach.d.ts +0 -32
  208. package/dist/deploy/detach.js +0 -71
  209. package/dist/dev/__tests__/attach.test.d.ts +0 -1
  210. package/dist/dev/__tests__/attach.test.js +0 -296
  211. package/dist/dev/__tests__/detach.test.d.ts +0 -1
  212. package/dist/dev/__tests__/detach.test.js +0 -404
  213. package/dist/dev/__tests__/preview-url-poller.test.d.ts +0 -1
  214. package/dist/dev/__tests__/preview-url-poller.test.js +0 -149
  215. package/dist/dev/__tests__/session.test.d.ts +0 -1
  216. package/dist/dev/__tests__/session.test.js +0 -446
  217. package/dist/dev/__tests__/startup-sync.test.d.ts +0 -1
  218. package/dist/dev/__tests__/startup-sync.test.js +0 -32
  219. package/dist/dev/__tests__/stop.test.d.ts +0 -1
  220. package/dist/dev/__tests__/stop.test.js +0 -172
  221. package/dist/dev/attach.d.ts +0 -120
  222. package/dist/dev/attach.js +0 -269
  223. package/dist/dev/detach.d.ts +0 -187
  224. package/dist/dev/detach.js +0 -292
  225. package/dist/dev/preview-url-poller.d.ts +0 -35
  226. package/dist/dev/preview-url-poller.js +0 -50
  227. package/dist/dev/session.d.ts +0 -175
  228. package/dist/dev/session.js +0 -258
  229. package/dist/dev/startup-sync.d.ts +0 -23
  230. package/dist/dev/startup-sync.js +0 -14
  231. package/dist/dev/stop.d.ts +0 -52
  232. package/dist/dev/stop.js +0 -101
  233. package/dist/devtools/registry-data.d.ts +0 -46
  234. package/dist/devtools/registry-data.js +0 -40
  235. package/dist/generated/bundled-types.d.ts +0 -1
  236. package/dist/generated/bundled-types.js +0 -46
  237. package/dist/generated/version.d.ts +0 -1
  238. package/dist/generated/version.js +0 -2
  239. package/dist/git/__tests__/auto-commit.test.d.ts +0 -1
  240. package/dist/git/__tests__/auto-commit.test.js +0 -431
  241. package/dist/git/__tests__/classify-sync-error.test.d.ts +0 -1
  242. package/dist/git/__tests__/classify-sync-error.test.js +0 -57
  243. package/dist/git/__tests__/credential-helper-e2e.test.d.ts +0 -21
  244. package/dist/git/__tests__/credential-helper-e2e.test.js +0 -195
  245. package/dist/git/__tests__/credentials.test.d.ts +0 -1
  246. package/dist/git/__tests__/credentials.test.js +0 -141
  247. package/dist/git/__tests__/deploy-guard.test.d.ts +0 -1
  248. package/dist/git/__tests__/deploy-guard.test.js +0 -61
  249. package/dist/git/__tests__/ensure-git-credential-helper.test.d.ts +0 -1
  250. package/dist/git/__tests__/ensure-git-credential-helper.test.js +0 -133
  251. package/dist/git/__tests__/ensure-runwork-remote.test.d.ts +0 -1
  252. package/dist/git/__tests__/ensure-runwork-remote.test.js +0 -109
  253. package/dist/git/__tests__/identity.test.d.ts +0 -1
  254. package/dist/git/__tests__/identity.test.js +0 -146
  255. package/dist/git/__tests__/manifest.test.d.ts +0 -1
  256. package/dist/git/__tests__/manifest.test.js +0 -446
  257. package/dist/git/__tests__/preflight-resolution.test.d.ts +0 -1
  258. package/dist/git/__tests__/preflight-resolution.test.js +0 -366
  259. package/dist/git/__tests__/preflight.test.d.ts +0 -1
  260. package/dist/git/__tests__/preflight.test.js +0 -36
  261. package/dist/git/__tests__/repo-config.test.d.ts +0 -1
  262. package/dist/git/__tests__/repo-config.test.js +0 -26
  263. package/dist/git/__tests__/sync.test.d.ts +0 -1
  264. package/dist/git/__tests__/sync.test.js +0 -405
  265. package/dist/git/auto-commit.d.ts +0 -35
  266. package/dist/git/auto-commit.js +0 -293
  267. package/dist/git/classify-sync-error.d.ts +0 -21
  268. package/dist/git/classify-sync-error.js +0 -111
  269. package/dist/git/credentials.d.ts +0 -75
  270. package/dist/git/credentials.js +0 -164
  271. package/dist/git/critical-files.d.ts +0 -20
  272. package/dist/git/critical-files.js +0 -68
  273. package/dist/git/deploy-guard.d.ts +0 -53
  274. package/dist/git/deploy-guard.js +0 -78
  275. package/dist/git/identity.d.ts +0 -44
  276. package/dist/git/identity.js +0 -133
  277. package/dist/git/preflight.d.ts +0 -57
  278. package/dist/git/preflight.js +0 -276
  279. package/dist/git/remote.d.ts +0 -37
  280. package/dist/git/remote.js +0 -75
  281. package/dist/git/repo-config.d.ts +0 -8
  282. package/dist/git/repo-config.js +0 -29
  283. package/dist/git/sync.d.ts +0 -15
  284. package/dist/git/sync.js +0 -159
  285. package/dist/health/__tests__/checks.test.d.ts +0 -1
  286. package/dist/health/__tests__/checks.test.js +0 -357
  287. package/dist/health/__tests__/cli-distribution-checks.test.d.ts +0 -1
  288. package/dist/health/__tests__/cli-distribution-checks.test.js +0 -196
  289. package/dist/health/__tests__/fix.test.d.ts +0 -1
  290. package/dist/health/__tests__/fix.test.js +0 -72
  291. package/dist/health/__tests__/runner-filter.test.d.ts +0 -1
  292. package/dist/health/__tests__/runner-filter.test.js +0 -61
  293. package/dist/health/checks.d.ts +0 -42
  294. package/dist/health/checks.js +0 -636
  295. package/dist/health/fix.d.ts +0 -22
  296. package/dist/health/fix.js +0 -62
  297. package/dist/health/runner.d.ts +0 -8
  298. package/dist/health/runner.js +0 -47
  299. package/dist/health/types.d.ts +0 -12
  300. package/dist/health/types.js +0 -1
  301. package/dist/index.d.ts +0 -1
  302. package/dist/logs/__tests__/tailer-format.test.d.ts +0 -1
  303. package/dist/logs/__tests__/tailer-format.test.js +0 -43
  304. package/dist/logs/tailer.d.ts +0 -17
  305. package/dist/logs/tailer.js +0 -217
  306. package/dist/sync/__tests__/change-detect.test.d.ts +0 -1
  307. package/dist/sync/__tests__/change-detect.test.js +0 -123
  308. package/dist/sync/__tests__/hash.test.d.ts +0 -1
  309. package/dist/sync/__tests__/hash.test.js +0 -18
  310. package/dist/sync/change-detect.d.ts +0 -19
  311. package/dist/sync/change-detect.js +0 -136
  312. package/dist/sync/conflict-ui.d.ts +0 -5
  313. package/dist/sync/conflict-ui.js +0 -78
  314. package/dist/sync/executor.d.ts +0 -23
  315. package/dist/sync/executor.js +0 -102
  316. package/dist/sync/hash.d.ts +0 -1
  317. package/dist/sync/hash.js +0 -4
  318. package/dist/sync/types.d.ts +0 -24
  319. package/dist/sync/types.js +0 -1
  320. package/dist/template/manifest.d.ts +0 -17
  321. package/dist/template/manifest.js +0 -145
  322. package/dist/tools/types.d.ts +0 -52
  323. package/dist/tools/types.js +0 -8
  324. package/dist/types-manager.d.ts +0 -1
  325. package/dist/types-manager.js +0 -35
  326. package/dist/types.d.ts +0 -349
  327. package/dist/types.js +0 -1
  328. package/dist/ui/__tests__/banner.test.d.ts +0 -1
  329. package/dist/ui/__tests__/banner.test.js +0 -98
  330. package/dist/ui/__tests__/colors.test.d.ts +0 -1
  331. package/dist/ui/__tests__/colors.test.js +0 -22
  332. package/dist/ui/__tests__/keyboard.test.d.ts +0 -1
  333. package/dist/ui/__tests__/keyboard.test.js +0 -34
  334. package/dist/ui/__tests__/status-line.test.d.ts +0 -1
  335. package/dist/ui/__tests__/status-line.test.js +0 -54
  336. package/dist/ui/banner.d.ts +0 -29
  337. package/dist/ui/banner.js +0 -153
  338. package/dist/ui/colors.d.ts +0 -4
  339. package/dist/ui/colors.js +0 -7
  340. package/dist/ui/keyboard.d.ts +0 -12
  341. package/dist/ui/keyboard.js +0 -61
  342. package/dist/ui/status-line.d.ts +0 -6
  343. package/dist/ui/status-line.js +0 -53
  344. package/dist/utils/__tests__/format-error.test.d.ts +0 -1
  345. package/dist/utils/__tests__/format-error.test.js +0 -43
  346. package/dist/utils/__tests__/help-json.test.d.ts +0 -1
  347. package/dist/utils/__tests__/help-json.test.js +0 -39
  348. package/dist/utils/__tests__/http.test.d.ts +0 -1
  349. package/dist/utils/__tests__/http.test.js +0 -381
  350. package/dist/utils/__tests__/ignore-matcher.test.d.ts +0 -1
  351. package/dist/utils/__tests__/ignore-matcher.test.js +0 -205
  352. package/dist/utils/__tests__/output.test.d.ts +0 -1
  353. package/dist/utils/__tests__/output.test.js +0 -38
  354. package/dist/utils/__tests__/prompt.test.d.ts +0 -1
  355. package/dist/utils/__tests__/prompt.test.js +0 -27
  356. package/dist/utils/__tests__/resolve.test.d.ts +0 -1
  357. package/dist/utils/__tests__/resolve.test.js +0 -95
  358. package/dist/utils/__tests__/sqlite.test.d.ts +0 -1
  359. package/dist/utils/__tests__/sqlite.test.js +0 -89
  360. package/dist/utils/agent-guidance.d.ts +0 -47
  361. package/dist/utils/agent-guidance.js +0 -128
  362. package/dist/utils/app-info.d.ts +0 -70
  363. package/dist/utils/app-info.js +0 -167
  364. package/dist/utils/curl-parser.d.ts +0 -12
  365. package/dist/utils/curl-parser.js +0 -162
  366. package/dist/utils/data-input.d.ts +0 -1
  367. package/dist/utils/data-input.js +0 -38
  368. package/dist/utils/format-error.d.ts +0 -10
  369. package/dist/utils/format-error.js +0 -38
  370. package/dist/utils/fs.d.ts +0 -5
  371. package/dist/utils/fs.js +0 -23
  372. package/dist/utils/help-json.d.ts +0 -70
  373. package/dist/utils/help-json.js +0 -88
  374. package/dist/utils/http.d.ts +0 -46
  375. package/dist/utils/http.js +0 -421
  376. package/dist/utils/ignore-matcher.d.ts +0 -47
  377. package/dist/utils/ignore-matcher.js +0 -132
  378. package/dist/utils/output.d.ts +0 -17
  379. package/dist/utils/output.js +0 -27
  380. package/dist/utils/prompt.d.ts +0 -5
  381. package/dist/utils/prompt.js +0 -42
  382. package/dist/utils/resolve.d.ts +0 -12
  383. package/dist/utils/resolve.js +0 -52
  384. package/dist/utils/setup-state.d.ts +0 -3
  385. package/dist/utils/setup-state.js +0 -19
  386. package/dist/utils/sqlite-adapter.d.ts +0 -48
  387. package/dist/utils/sqlite-adapter.js +0 -147
  388. package/dist/utils/sqlite.d.ts +0 -14
  389. package/dist/utils/sqlite.js +0 -14
  390. package/dist/utils/subprocess.d.ts +0 -19
  391. package/dist/utils/subprocess.js +0 -27
  392. package/dist/utils/which.d.ts +0 -14
  393. package/dist/utils/which.js +0 -47
  394. package/dist/utils/zip.d.ts +0 -7
  395. package/dist/utils/zip.js +0 -42
  396. package/dist/validate/__tests__/validate.test.d.ts +0 -1
  397. package/dist/validate/__tests__/validate.test.js +0 -42
  398. package/dist/validate/freshness.d.ts +0 -28
  399. package/dist/validate/freshness.js +0 -48
  400. package/dist/validate/preview.d.ts +0 -21
  401. package/dist/validate/preview.js +0 -39
  402. package/dist/workspace/resolve.d.ts +0 -19
  403. 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 {};