promptarc 0.0.2 → 0.0.4

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 (246) hide show
  1. package/README.md +33 -25
  2. package/dist/bin.js +886 -4
  3. package/dist/share-direct.js +360 -0
  4. package/dist/web/.next/BUILD_ID +1 -1
  5. package/dist/web/.next/app-build-manifest.json +318 -226
  6. package/dist/web/.next/app-path-routes-manifest.json +24 -13
  7. package/dist/web/.next/build-manifest.json +14 -14
  8. package/dist/web/.next/prerender-manifest.json +89 -1
  9. package/dist/web/.next/routes-manifest.json +36 -0
  10. package/dist/web/.next/server/app/_not-found/page.js +1 -1
  11. package/dist/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/dist/web/.next/server/app/api/admin/backfill-ai/route.js +8 -0
  13. package/dist/web/.next/server/app/api/admin/backfill-ai/route.js.nft.json +1 -0
  14. package/dist/web/.next/server/app/api/admin/backfill-ai/route_client-reference-manifest.js +1 -0
  15. package/dist/web/.next/server/app/api/admin/backfill-search/route.js +1 -1
  16. package/dist/web/.next/server/app/api/admin/backfill-search/route.js.nft.json +1 -1
  17. package/dist/web/.next/server/app/api/admin/backfill-search/route_client-reference-manifest.js +1 -1
  18. package/dist/web/.next/server/app/api/ai/context/route.js +31 -0
  19. package/dist/web/.next/server/app/api/ai/context/route.js.nft.json +1 -0
  20. package/dist/web/.next/server/app/api/ai/context/route_client-reference-manifest.js +1 -0
  21. package/dist/web/.next/server/app/api/ai/pr-summary/route.js +28 -0
  22. package/dist/web/.next/server/app/api/ai/pr-summary/route.js.nft.json +1 -0
  23. package/dist/web/.next/server/app/api/ai/pr-summary/route_client-reference-manifest.js +1 -0
  24. package/dist/web/.next/server/app/api/ai/pr-summary-local/route.js +28 -0
  25. package/dist/web/.next/server/app/api/ai/pr-summary-local/route.js.nft.json +1 -0
  26. package/dist/web/.next/server/app/api/ai/pr-summary-local/route_client-reference-manifest.js +1 -0
  27. package/dist/web/.next/server/app/api/ai/process/route.js +8 -0
  28. package/dist/web/.next/server/app/api/ai/process/route.js.nft.json +1 -0
  29. package/dist/web/.next/server/app/api/ai/process/route_client-reference-manifest.js +1 -0
  30. package/dist/web/.next/server/app/api/cli/download/route.js +1 -1
  31. package/dist/web/.next/server/app/api/cli/download/route.js.nft.json +1 -1
  32. package/dist/web/.next/server/app/api/cli/download/route_client-reference-manifest.js +1 -1
  33. package/dist/web/.next/server/app/api/cli/feedback/route_client-reference-manifest.js +1 -1
  34. package/dist/web/.next/server/app/api/cli/init/route_client-reference-manifest.js +1 -1
  35. package/dist/web/.next/server/app/api/cli/my-shares/route.js +1 -1
  36. package/dist/web/.next/server/app/api/cli/my-shares/route_client-reference-manifest.js +1 -1
  37. package/dist/web/.next/server/app/api/cli/poll/route_client-reference-manifest.js +1 -1
  38. package/dist/web/.next/server/app/api/cli/project-settings/route_client-reference-manifest.js +1 -1
  39. package/dist/web/.next/server/app/api/cli/share-mutate/route_client-reference-manifest.js +1 -1
  40. package/dist/web/.next/server/app/api/cli/sync-list/route_client-reference-manifest.js +1 -1
  41. package/dist/web/.next/server/app/api/local/project-sessions/route.js +1 -1
  42. package/dist/web/.next/server/app/api/local/project-sessions/route.js.nft.json +1 -1
  43. package/dist/web/.next/server/app/api/local/project-sessions/route_client-reference-manifest.js +1 -1
  44. package/dist/web/.next/server/app/api/local/watch/route.js +2 -2
  45. package/dist/web/.next/server/app/api/local/watch/route.js.nft.json +1 -1
  46. package/dist/web/.next/server/app/api/local/watch/route_client-reference-manifest.js +1 -1
  47. package/dist/web/.next/server/app/api/search/route.js +1 -1
  48. package/dist/web/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  49. package/dist/web/.next/server/app/api/search-index/route.js +1 -1
  50. package/dist/web/.next/server/app/api/search-index/route.js.nft.json +1 -1
  51. package/dist/web/.next/server/app/api/search-index/route_client-reference-manifest.js +1 -1
  52. package/dist/web/.next/server/app/api/share/download/[slug]/route.js +1 -1
  53. package/dist/web/.next/server/app/api/share/download/[slug]/route.js.nft.json +1 -1
  54. package/dist/web/.next/server/app/api/share/download/[slug]/route_client-reference-manifest.js +1 -1
  55. package/dist/web/.next/server/app/api/share/route.js +1 -1
  56. package/dist/web/.next/server/app/api/share/route.js.nft.json +1 -1
  57. package/dist/web/.next/server/app/api/share/route_client-reference-manifest.js +1 -1
  58. package/dist/web/.next/server/app/api/upload/route.js +2 -2
  59. package/dist/web/.next/server/app/api/upload/route.js.nft.json +1 -1
  60. package/dist/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -1
  61. package/dist/web/.next/server/app/auth/callback/route_client-reference-manifest.js +1 -1
  62. package/dist/web/.next/server/app/auth/signout/route_client-reference-manifest.js +1 -1
  63. package/dist/web/.next/server/app/blog/ai-coding-agents-2026/page.js +2 -0
  64. package/dist/web/.next/server/app/blog/ai-coding-agents-2026/page.js.nft.json +1 -0
  65. package/dist/web/.next/server/app/blog/ai-coding-agents-2026/page_client-reference-manifest.js +1 -0
  66. package/dist/web/.next/server/app/blog/page.js +2 -0
  67. package/dist/web/.next/server/app/blog/page.js.nft.json +1 -0
  68. package/dist/web/.next/server/app/blog/page_client-reference-manifest.js +1 -0
  69. package/dist/web/.next/server/app/cli/login/page.js +1 -1
  70. package/dist/web/.next/server/app/cli/login/page_client-reference-manifest.js +1 -1
  71. package/dist/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  72. package/dist/web/.next/server/app/feedback/page_client-reference-manifest.js +1 -1
  73. package/dist/web/.next/server/app/my-projects/[projectKey]/page_client-reference-manifest.js +1 -1
  74. package/dist/web/.next/server/app/my-projects/page_client-reference-manifest.js +1 -1
  75. package/dist/web/.next/server/app/my-shares/page.js +1 -1
  76. package/dist/web/.next/server/app/my-shares/page_client-reference-manifest.js +1 -1
  77. package/dist/web/.next/server/app/opengraph-image/route.js +1 -71
  78. package/dist/web/.next/server/app/opengraph-image/route.js.nft.json +1 -0
  79. package/dist/web/.next/server/app/opengraph-image/route_client-reference-manifest.js +1 -1
  80. package/dist/web/.next/server/app/opengraph-image.body +0 -0
  81. package/dist/web/.next/server/app/opengraph-image.meta +1 -0
  82. package/dist/web/.next/server/app/page.js +2 -2
  83. package/dist/web/.next/server/app/page.js.nft.json +1 -1
  84. package/dist/web/.next/server/app/page_client-reference-manifest.js +1 -1
  85. package/dist/web/.next/server/app/privacy/page.js +2 -0
  86. package/dist/web/.next/server/app/privacy/page.js.nft.json +1 -0
  87. package/dist/web/.next/server/app/privacy/page_client-reference-manifest.js +1 -0
  88. package/dist/web/.next/server/app/projects/[id]/page.js +1 -1
  89. package/dist/web/.next/server/app/projects/[id]/page.js.nft.json +1 -1
  90. package/dist/web/.next/server/app/projects/[id]/page_client-reference-manifest.js +1 -1
  91. package/dist/web/.next/server/app/r/[slug]/opengraph-image/route.js +1 -1
  92. package/dist/web/.next/server/app/r/[slug]/opengraph-image/route_client-reference-manifest.js +1 -1
  93. package/dist/web/.next/server/app/r/[slug]/page.js +2 -2
  94. package/dist/web/.next/server/app/r/[slug]/page.js.nft.json +1 -1
  95. package/dist/web/.next/server/app/r/[slug]/page_client-reference-manifest.js +1 -1
  96. package/dist/web/.next/server/app/robots.txt/route.js +16 -0
  97. package/dist/web/.next/server/app/robots.txt/route.js.nft.json +1 -0
  98. package/dist/web/.next/server/app/robots.txt/route_client-reference-manifest.js +1 -0
  99. package/dist/web/.next/server/app/robots.txt.body +9 -0
  100. package/dist/web/.next/server/app/robots.txt.meta +1 -0
  101. package/dist/web/.next/server/app/search/page_client-reference-manifest.js +1 -1
  102. package/dist/web/.next/server/app/sessions/[projectId]/[sessionId]/page.js +1 -1
  103. package/dist/web/.next/server/app/sessions/[projectId]/[sessionId]/page.js.nft.json +1 -1
  104. package/dist/web/.next/server/app/sessions/[projectId]/[sessionId]/page_client-reference-manifest.js +1 -1
  105. package/dist/web/.next/server/app/sign-in/page.js +1 -1
  106. package/dist/web/.next/server/app/sign-in/page_client-reference-manifest.js +1 -1
  107. package/dist/web/.next/server/app/sitemap.xml/route.js +16 -0
  108. package/dist/web/.next/server/app/sitemap.xml/route.js.nft.json +1 -0
  109. package/dist/web/.next/server/app/sitemap.xml/route_client-reference-manifest.js +1 -0
  110. package/dist/web/.next/server/app/sitemap.xml.body +51 -0
  111. package/dist/web/.next/server/app/sitemap.xml.meta +1 -0
  112. package/dist/web/.next/server/app/terms/page.js +2 -0
  113. package/dist/web/.next/server/app/terms/page.js.nft.json +1 -0
  114. package/dist/web/.next/server/app/terms/page_client-reference-manifest.js +1 -0
  115. package/dist/web/.next/server/app-paths-manifest.json +24 -13
  116. package/dist/web/.next/server/chunks/1017.js +2 -0
  117. package/dist/web/.next/server/chunks/1134.js +8 -0
  118. package/dist/web/.next/server/chunks/1825.js +1 -1
  119. package/dist/web/.next/server/chunks/8334.js +1 -1
  120. package/dist/web/.next/server/functions-config-manifest.json +17 -2
  121. package/dist/web/.next/server/middleware-build-manifest.js +1 -1
  122. package/dist/web/.next/server/middleware-manifest.json +1 -43
  123. package/dist/web/.next/server/pages/500.html +1 -1
  124. package/dist/web/.next/server/server-reference-manifest.js +1 -1
  125. package/dist/web/.next/server/server-reference-manifest.json +1 -1
  126. package/dist/web/.next/static/LF69v3WA6LbEAkvRHNqfG/_buildManifest.js +1 -0
  127. package/dist/web/.next/static/chunks/1029-12dc6d00537d43fd.js +1 -0
  128. package/dist/web/.next/static/chunks/4695-b52fc3d81451c3c3.js +1 -0
  129. package/dist/web/.next/static/chunks/552926ff-c5d04ae0c2d9f826.js +1 -0
  130. package/dist/web/.next/static/chunks/7200-4560b15b9cad37b3.js +1 -0
  131. package/dist/web/.next/static/chunks/app/_not-found/page-0f1123e35eb8f046.js +1 -0
  132. package/dist/web/.next/static/chunks/app/api/admin/backfill-ai/route-0f1123e35eb8f046.js +1 -0
  133. package/dist/web/.next/static/chunks/app/api/admin/backfill-search/route-0f1123e35eb8f046.js +1 -0
  134. package/dist/web/.next/static/chunks/app/api/ai/context/route-0f1123e35eb8f046.js +1 -0
  135. package/dist/web/.next/static/chunks/app/api/ai/pr-summary/route-0f1123e35eb8f046.js +1 -0
  136. package/dist/web/.next/static/chunks/app/api/ai/pr-summary-local/route-0f1123e35eb8f046.js +1 -0
  137. package/dist/web/.next/static/chunks/app/api/ai/process/route-0f1123e35eb8f046.js +1 -0
  138. package/dist/web/.next/static/chunks/app/api/cli/download/route-0f1123e35eb8f046.js +1 -0
  139. package/dist/web/.next/static/chunks/app/api/cli/feedback/route-0f1123e35eb8f046.js +1 -0
  140. package/dist/web/.next/static/chunks/app/api/cli/init/route-0f1123e35eb8f046.js +1 -0
  141. package/dist/web/.next/static/chunks/app/api/cli/my-shares/route-0f1123e35eb8f046.js +1 -0
  142. package/dist/web/.next/static/chunks/app/api/cli/poll/route-0f1123e35eb8f046.js +1 -0
  143. package/dist/web/.next/static/chunks/app/api/cli/project-settings/route-0f1123e35eb8f046.js +1 -0
  144. package/dist/web/.next/static/chunks/app/api/cli/share-mutate/route-0f1123e35eb8f046.js +1 -0
  145. package/dist/web/.next/static/chunks/app/api/cli/sync-list/route-0f1123e35eb8f046.js +1 -0
  146. package/dist/web/.next/static/chunks/app/api/local/project-sessions/route-0f1123e35eb8f046.js +1 -0
  147. package/dist/web/.next/static/chunks/app/api/local/watch/route-0f1123e35eb8f046.js +1 -0
  148. package/dist/web/.next/static/chunks/app/api/search/route-0f1123e35eb8f046.js +1 -0
  149. package/dist/web/.next/static/chunks/app/api/search-index/route-0f1123e35eb8f046.js +1 -0
  150. package/dist/web/.next/static/chunks/app/api/share/download/[slug]/route-0f1123e35eb8f046.js +1 -0
  151. package/dist/web/.next/static/chunks/app/api/share/route-0f1123e35eb8f046.js +1 -0
  152. package/dist/web/.next/static/chunks/app/api/upload/route-0f1123e35eb8f046.js +1 -0
  153. package/dist/web/.next/static/chunks/app/auth/callback/route-0f1123e35eb8f046.js +1 -0
  154. package/dist/web/.next/static/chunks/app/auth/signout/route-0f1123e35eb8f046.js +1 -0
  155. package/dist/web/.next/static/chunks/app/blog/ai-coding-agents-2026/page-0f1123e35eb8f046.js +1 -0
  156. package/dist/web/.next/static/chunks/app/blog/layout-9b51270472318488.js +1 -0
  157. package/dist/web/.next/static/chunks/app/blog/page-8400ea4ac5a7eb44.js +1 -0
  158. package/dist/web/.next/static/chunks/app/cli/login/page-26287cfd64c0fb49.js +1 -0
  159. package/dist/web/.next/static/chunks/app/digest/page-8400ea4ac5a7eb44.js +1 -0
  160. package/dist/web/.next/static/chunks/app/feedback/page-55ef0005807f1cef.js +1 -0
  161. package/dist/web/.next/static/chunks/app/layout-aa56caf2bf5af86f.js +1 -0
  162. package/dist/web/.next/static/chunks/app/my-projects/[projectKey]/page-7c5f2badf689d2e8.js +1 -0
  163. package/dist/web/.next/static/chunks/app/my-projects/page-c0390151fd6332a0.js +1 -0
  164. package/dist/web/.next/static/chunks/app/my-shares/{page-697d61e07a7546b2.js → page-49c0112973401c14.js} +1 -1
  165. package/dist/web/.next/static/chunks/app/not-found-8400ea4ac5a7eb44.js +1 -0
  166. package/dist/web/.next/static/chunks/app/opengraph-image/route-0f1123e35eb8f046.js +1 -0
  167. package/dist/web/.next/static/chunks/app/page-95394e3c6370084f.js +1 -0
  168. package/dist/web/.next/static/chunks/app/privacy/page-9b51270472318488.js +1 -0
  169. package/dist/web/.next/static/chunks/app/projects/[id]/page-7dd46a825d03a7d9.js +1 -0
  170. package/dist/web/.next/static/chunks/app/r/[slug]/opengraph-image/route-0f1123e35eb8f046.js +1 -0
  171. package/dist/web/.next/static/chunks/app/r/[slug]/page-6a0983805e048ac3.js +1 -0
  172. package/dist/web/.next/static/chunks/app/robots.txt/route-0f1123e35eb8f046.js +1 -0
  173. package/dist/web/.next/static/chunks/app/search/page-c29f37dc2462b676.js +1 -0
  174. package/dist/web/.next/static/chunks/app/sessions/[projectId]/[sessionId]/page-87b4505dd32edb9d.js +1 -0
  175. package/dist/web/.next/static/chunks/app/sign-in/page-fea3971ff9237b72.js +1 -0
  176. package/dist/web/.next/static/chunks/app/sitemap.xml/route-0f1123e35eb8f046.js +1 -0
  177. package/dist/web/.next/static/chunks/app/terms/page-9b51270472318488.js +1 -0
  178. package/dist/web/.next/static/chunks/d99d8e6a-55da88efbe15e3e1.js +1 -0
  179. package/dist/web/.next/static/chunks/framework-c1efa271b5a96d9d.js +1 -0
  180. package/dist/web/.next/static/chunks/main-app-70de904c7caade50.js +1 -0
  181. package/dist/web/.next/static/chunks/main-d211cddc79aac1cb.js +1 -0
  182. package/dist/web/.next/static/chunks/pages/{_app-42b849402a54b9e6.js → _app-701b0752a0d4c3a9.js} +1 -1
  183. package/dist/web/.next/static/chunks/pages/_error-327385abc873a736.js +1 -0
  184. package/dist/web/.next/static/chunks/{webpack-3a8b65d12c8eb987.js → webpack-3a446128dfa39f85.js} +1 -1
  185. package/dist/web/.next/static/css/c76bb7513858c50c.css +3 -0
  186. package/dist/web/package.json +1 -0
  187. package/dist/web/public/apple-touch-icon.png +0 -0
  188. package/dist/web/public/favicon-16.png +0 -0
  189. package/dist/web/public/favicon-32.png +0 -0
  190. package/dist/web/public/icon-192.png +0 -0
  191. package/dist/web/public/icon-512.png +0 -0
  192. package/dist/web/public/logo.png +0 -0
  193. package/package.json +1 -1
  194. package/dist/web/.next/server/app/opengraph-image/route.js.map +0 -1
  195. package/dist/web/.next/server/chunks/6019.js +0 -2
  196. package/dist/web/.next/server/edge-chunks/asset_noto-sans-v27-latin-regular.5dda3fca77107598.ttf +0 -0
  197. package/dist/web/.next/server/edge-chunks/wasm_77d9faebf7af9e421806970ce10a58e9d83116d7.wasm +0 -0
  198. package/dist/web/.next/server/edge-chunks/wasm_ef4866ecae192fd87727067cf2c0c0cf9fb8b020.wasm +0 -0
  199. package/dist/web/.next/server/edge-runtime-webpack.js +0 -2
  200. package/dist/web/.next/server/interception-route-rewrite-manifest.js +0 -1
  201. package/dist/web/.next/static/ZxFskpyUoxefJOzSxSb6s/_buildManifest.js +0 -1
  202. package/dist/web/.next/static/chunks/200-619918ea0fdbd6ff.js +0 -1
  203. package/dist/web/.next/static/chunks/29-7904810aeffd75a0.js +0 -1
  204. package/dist/web/.next/static/chunks/552926ff-e8a371ec0133d0dc.js +0 -1
  205. package/dist/web/.next/static/chunks/695-00bca043953d4170.js +0 -1
  206. package/dist/web/.next/static/chunks/app/_not-found/page-bd7ebde8ab75cb95.js +0 -1
  207. package/dist/web/.next/static/chunks/app/api/admin/backfill-search/route-bd7ebde8ab75cb95.js +0 -1
  208. package/dist/web/.next/static/chunks/app/api/cli/download/route-bd7ebde8ab75cb95.js +0 -1
  209. package/dist/web/.next/static/chunks/app/api/cli/feedback/route-bd7ebde8ab75cb95.js +0 -1
  210. package/dist/web/.next/static/chunks/app/api/cli/init/route-bd7ebde8ab75cb95.js +0 -1
  211. package/dist/web/.next/static/chunks/app/api/cli/my-shares/route-bd7ebde8ab75cb95.js +0 -1
  212. package/dist/web/.next/static/chunks/app/api/cli/poll/route-bd7ebde8ab75cb95.js +0 -1
  213. package/dist/web/.next/static/chunks/app/api/cli/project-settings/route-bd7ebde8ab75cb95.js +0 -1
  214. package/dist/web/.next/static/chunks/app/api/cli/share-mutate/route-bd7ebde8ab75cb95.js +0 -1
  215. package/dist/web/.next/static/chunks/app/api/cli/sync-list/route-bd7ebde8ab75cb95.js +0 -1
  216. package/dist/web/.next/static/chunks/app/api/local/project-sessions/route-bd7ebde8ab75cb95.js +0 -1
  217. package/dist/web/.next/static/chunks/app/api/local/watch/route-bd7ebde8ab75cb95.js +0 -1
  218. package/dist/web/.next/static/chunks/app/api/search/route-bd7ebde8ab75cb95.js +0 -1
  219. package/dist/web/.next/static/chunks/app/api/search-index/route-bd7ebde8ab75cb95.js +0 -1
  220. package/dist/web/.next/static/chunks/app/api/share/download/[slug]/route-bd7ebde8ab75cb95.js +0 -1
  221. package/dist/web/.next/static/chunks/app/api/share/route-bd7ebde8ab75cb95.js +0 -1
  222. package/dist/web/.next/static/chunks/app/api/upload/route-bd7ebde8ab75cb95.js +0 -1
  223. package/dist/web/.next/static/chunks/app/auth/callback/route-bd7ebde8ab75cb95.js +0 -1
  224. package/dist/web/.next/static/chunks/app/auth/signout/route-bd7ebde8ab75cb95.js +0 -1
  225. package/dist/web/.next/static/chunks/app/cli/login/page-647fd605d0fe2472.js +0 -1
  226. package/dist/web/.next/static/chunks/app/digest/page-41ec674edc998616.js +0 -1
  227. package/dist/web/.next/static/chunks/app/feedback/page-60f4d489fd598c9c.js +0 -1
  228. package/dist/web/.next/static/chunks/app/layout-f5666b9135264807.js +0 -1
  229. package/dist/web/.next/static/chunks/app/my-projects/[projectKey]/page-3a816489e5e8534b.js +0 -1
  230. package/dist/web/.next/static/chunks/app/my-projects/page-c88eba56cb5d0b8d.js +0 -1
  231. package/dist/web/.next/static/chunks/app/not-found-41ec674edc998616.js +0 -1
  232. package/dist/web/.next/static/chunks/app/opengraph-image/route-bd7ebde8ab75cb95.js +0 -1
  233. package/dist/web/.next/static/chunks/app/page-e6e80157848514e3.js +0 -1
  234. package/dist/web/.next/static/chunks/app/projects/[id]/page-faec0dc5b09bd35f.js +0 -1
  235. package/dist/web/.next/static/chunks/app/r/[slug]/opengraph-image/route-bd7ebde8ab75cb95.js +0 -1
  236. package/dist/web/.next/static/chunks/app/r/[slug]/page-65707eb198f673a1.js +0 -1
  237. package/dist/web/.next/static/chunks/app/search/page-fc0b629efa815c64.js +0 -1
  238. package/dist/web/.next/static/chunks/app/sessions/[projectId]/[sessionId]/page-5e70eaa0a429d94a.js +0 -1
  239. package/dist/web/.next/static/chunks/app/sign-in/page-792d0d79889186cf.js +0 -1
  240. package/dist/web/.next/static/chunks/d99d8e6a-80443bd3c9d31f50.js +0 -1
  241. package/dist/web/.next/static/chunks/framework-39000e34f38d10f6.js +0 -1
  242. package/dist/web/.next/static/chunks/main-app-21e9441fa7ac15ef.js +0 -1
  243. package/dist/web/.next/static/chunks/main-f824d48fd7ff2e3b.js +0 -1
  244. package/dist/web/.next/static/chunks/pages/_error-412ff705bc5b0636.js +0 -1
  245. package/dist/web/.next/static/css/3c4744a7426424ee.css +0 -3
  246. /package/dist/web/.next/static/{ZxFskpyUoxefJOzSxSb6s → LF69v3WA6LbEAkvRHNqfG}/_ssgManifest.js +0 -0
package/dist/bin.js CHANGED
@@ -11,16 +11,19 @@
11
11
  * promptarc login Run the auth flow without booting the server
12
12
  * promptarc logout Delete the saved auth token
13
13
  * promptarc whoami Print the signed-in account
14
+ * promptarc pr Generate a PR description from the current session
15
+ * promptarc pull Download a shared session for resume
14
16
  * promptarc --version Print the package version
15
17
  */
16
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
18
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
17
19
  import { homedir } from 'node:os';
18
- import { dirname, join, resolve } from 'node:path';
20
+ import { basename, dirname, join, resolve } from 'node:path';
19
21
  import { fileURLToPath } from 'node:url';
20
22
  import { spawn } from 'node:child_process';
21
23
  import { createServer } from 'node:net';
22
24
  import { runLogin, loadAuth, deleteAuth } from './auth.js';
23
25
  import { startSyncLoop } from './sync.js';
26
+ import { buildReplayFromJsonl } from './share-direct.js';
24
27
  const __filename = fileURLToPath(import.meta.url);
25
28
  const __dirname = dirname(__filename);
26
29
  const VERSION = readJson(join(__dirname, '..', 'package.json')).version ?? '0.0.0';
@@ -54,6 +57,25 @@ async function main() {
54
57
  console.log(`Signed in as ${auth.email ?? auth.userId}${auth.login ? ` (@${auth.login})` : ''}`);
55
58
  return;
56
59
  }
60
+ if (cmd === 'context') {
61
+ await runContext(args.slice(1));
62
+ return;
63
+ }
64
+ if (cmd === 'pr') {
65
+ await runPR(args.slice(1));
66
+ return;
67
+ }
68
+ if (cmd === 'share') {
69
+ const sessionArg = args[1];
70
+ if (!sessionArg) {
71
+ console.error('Usage: promptarc share <session-id>');
72
+ console.error(' Share a local session to the cloud as private.');
73
+ process.exitCode = 1;
74
+ return;
75
+ }
76
+ await runShare(sessionArg, args.slice(2));
77
+ return;
78
+ }
57
79
  if (cmd === 'pull') {
58
80
  const slug = args[1];
59
81
  if (!slug) {
@@ -81,6 +103,10 @@ COMMANDS
81
103
  login Sign in to your promptarc account via GitHub
82
104
  logout Delete the saved auth token
83
105
  whoami Print the currently signed-in account
106
+ context What do I need to know? Past sessions for this project
107
+ pr Generate a PR description from the current session
108
+ pr --share Generate + share session as private (includes link)
109
+ share <id> Share a local session to the cloud (private by default)
84
110
  pull <slug> Download a shared session into ~/.claude/ for resume
85
111
  --version Print the CLI version
86
112
  --help Show this message
@@ -89,13 +115,869 @@ LEARN MORE
89
115
  https://promptarc.dev
90
116
  `);
91
117
  }
92
- async function runPull(slug) {
118
+ async function runContext(args) {
119
+ const cwd = process.cwd();
120
+ const query = args.filter((a) => !a.startsWith('--')).join(' ');
121
+ const filesFlag = args.includes('--files');
122
+ const cwdParts = cwd.split('/').filter(Boolean);
123
+ const projectName = cwdParts[cwdParts.length - 1] ?? '';
124
+ console.log(`promptarc context`);
125
+ console.log(` ▸ project: ${projectName}`);
126
+ // 1. Detect recently changed files (git + find fallback).
127
+ let files = [];
128
+ if (filesFlag) {
129
+ try {
130
+ const { execSync } = await import('node:child_process');
131
+ // Try git first: staged + last 3 commits.
132
+ const gitFiles = execSync('git diff --name-only HEAD~3 HEAD 2>/dev/null; git diff --name-only --cached 2>/dev/null; git diff --name-only 2>/dev/null', { cwd, encoding: 'utf8' });
133
+ files = [...new Set(gitFiles.split('\n').filter(Boolean))].slice(0, 15);
134
+ }
135
+ catch { /* not a git repo */ }
136
+ // Fallback: recently modified files.
137
+ if (files.length === 0) {
138
+ try {
139
+ const { execSync } = await import('node:child_process');
140
+ const found = execSync('find . -maxdepth 3 -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.py" -o -name "*.rs" -o -name "*.go" | head -15', { cwd, encoding: 'utf8' });
141
+ files = found.split('\n').filter(Boolean).map((f) => f.replace(/^\.\//, ''));
142
+ }
143
+ catch { /* no find */ }
144
+ }
145
+ }
146
+ if (files.length > 0) {
147
+ console.log(` ▸ files: ${files.slice(0, 5).join(', ')}${files.length > 5 ? ` +${files.length - 5} more` : ''}`);
148
+ }
149
+ // 2. Local scan: find sessions across all three agents for this project.
150
+ console.log(` ▸ scanning local sessions...`);
151
+ const localSessions = scanLocalSessions(cwd, projectName);
152
+ if (localSessions.length > 0) {
153
+ const lastActive = fmtRelativeTime(localSessions[0].mtime);
154
+ console.log(`\n ${projectName} · ${localSessions.length} session${localSessions.length === 1 ? '' : 's'} · last active ${lastActive}`);
155
+ }
156
+ // 3. Cloud search + AI briefing (requires auth).
93
157
  const auth = loadAuth();
158
+ let cloudSessionsFound = false;
159
+ if (auth) {
160
+ console.log(`\n ▸ searching cloud archive...`);
161
+ const siteUrl = process.env.PROMPTARC_SITE_URL ?? 'https://promptarc.dev';
162
+ // Build local context to send to the API for AI briefing.
163
+ const localContext = localSessions.slice(0, 3).map((s) => {
164
+ const prompt = s.firstPrompt
165
+ ? s.firstPrompt.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 300)
166
+ : '';
167
+ return `[${s.agent}] ${prompt} (${s.messageCount} messages)`;
168
+ }).join('\n');
169
+ try {
170
+ const res = await fetch(`${siteUrl}/api/ai/context`, {
171
+ method: 'POST',
172
+ headers: {
173
+ 'content-type': 'application/json',
174
+ authorization: `Bearer ${auth.token}`,
175
+ },
176
+ body: JSON.stringify({
177
+ cwd,
178
+ files,
179
+ query,
180
+ localContext,
181
+ }),
182
+ });
183
+ if (res.ok) {
184
+ const json = (await res.json());
185
+ if (json.count > 0) {
186
+ cloudSessionsFound = true;
187
+ console.log(` ${json.count} cloud session${json.count === 1 ? '' : 's'} found.\n`);
188
+ }
189
+ else {
190
+ console.log(` No cloud sessions found.`);
191
+ if (localSessions.length > 0) {
192
+ console.log(` Share sessions to unlock AI briefing from any machine.`);
193
+ }
194
+ }
195
+ // Resume command — pick up the exact session.
196
+ if (localSessions.length > 0) {
197
+ const latest = localSessions[0];
198
+ const agentCmd = latest.agent === 'codex'
199
+ ? 'codex'
200
+ : latest.agent === 'cursor'
201
+ ? 'cursor'
202
+ : 'claude';
203
+ const shortId = latest.sessionId ?? basename(latest.path, '.jsonl');
204
+ console.log(`\n Resume last session:`);
205
+ console.log(` ${agentCmd} --resume ${shortId}`);
206
+ }
207
+ // AI briefing — the actionable part.
208
+ if (json.aiBriefing) {
209
+ if (json.aiBriefing.unfinished) {
210
+ console.log(`\n What's unfinished:`);
211
+ for (const line of json.aiBriefing.unfinished.split('\n')) {
212
+ const trimmed = line.trim();
213
+ if (trimmed)
214
+ console.log(` ${trimmed}`);
215
+ }
216
+ }
217
+ if (json.aiBriefing.resumePrompt) {
218
+ console.log(`\n Resume prompt (paste into your agent):`);
219
+ console.log(` ┌─`);
220
+ for (const line of json.aiBriefing.resumePrompt.split('\n')) {
221
+ console.log(` │ ${line}`);
222
+ }
223
+ console.log(` └─`);
224
+ }
225
+ if (json.aiBriefing.warnings) {
226
+ console.log(`\n Heads up:`);
227
+ for (const line of json.aiBriefing.warnings.split('\n')) {
228
+ const trimmed = line.trim();
229
+ if (trimmed)
230
+ console.log(` ${trimmed}`);
231
+ }
232
+ }
233
+ }
234
+ else if (localSessions.length > 0 && json.count === 0) {
235
+ console.log(`\n Share sessions to get AI briefings with resume prompts.`);
236
+ }
237
+ }
238
+ else {
239
+ console.log(` Cloud search failed (HTTP ${res.status}).`);
240
+ }
241
+ }
242
+ catch {
243
+ console.log(` Could not reach promptarc.dev (offline?).`);
244
+ if (localSessions.length === 0) {
245
+ console.log(` No local sessions found either.`);
246
+ }
247
+ }
248
+ }
249
+ else if (localSessions.length === 0) {
250
+ console.log(`\n No local sessions found. Run \`promptarc login\` to search cloud archive.`);
251
+ }
252
+ else {
253
+ console.log(`\n Run \`promptarc login\` to also search your cloud archive.`);
254
+ }
255
+ // Session list at the bottom (least important).
256
+ if (localSessions.length > 0) {
257
+ console.log('');
258
+ for (const s of localSessions.slice(0, 6)) {
259
+ const when = fmtRelativeTime(s.mtime);
260
+ const agent = s.agent === 'claude' ? 'Claude' : s.agent === 'codex' ? 'Codex' : 'Cursor';
261
+ const cleanPrompt = s.firstPrompt
262
+ ? s.firstPrompt.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim()
263
+ : '';
264
+ let title = cleanPrompt || `Session ${basename(s.path, '.jsonl').slice(0, 8)}`;
265
+ if (title.length > 50) {
266
+ title = title.slice(0, 50).replace(/\s\S*$/, '') + '...';
267
+ }
268
+ console.log(` ${when.padEnd(13)} [${agent}] ${title} · ${fmtNumber(s.messageCount)} msgs`);
269
+ }
270
+ // Gentle share nudge — only when no cloud sessions exist.
271
+ if (!auth || !cloudSessionsFound) {
272
+ const latest = localSessions[0];
273
+ console.log(`\n ${localSessions.length} local session${localSessions.length === 1 ? '' : 's'} · 0 cloud sessions`);
274
+ console.log(` Share to search from any machine: promptarc share ${latest.sessionId.slice(0, 8)}`);
275
+ }
276
+ }
277
+ console.log('');
278
+ }
279
+ /**
280
+ * Scan ~/.claude/, ~/.codex/, ~/.cursor/ for sessions matching the
281
+ * current project. Returns session summaries sorted by recency.
282
+ */
283
+ function scanLocalSessions(cwd, projectName) {
284
+ const results = [];
285
+ const cwdEncoded = cwd.replace(/\//g, '-');
286
+ // Claude Code: ~/.claude/projects/<encoded-cwd>/*.jsonl
287
+ const claudeRoot = join(homedir(), '.claude', 'projects');
288
+ try {
289
+ for (const dir of readdirSync(claudeRoot)) {
290
+ if (dir === cwdEncoded || dir.endsWith(projectName)) {
291
+ const projectDir = join(claudeRoot, dir);
292
+ if (!statSync(projectDir).isDirectory())
293
+ continue;
294
+ for (const file of readdirSync(projectDir)) {
295
+ if (!file.endsWith('.jsonl'))
296
+ continue;
297
+ const fp = join(projectDir, file);
298
+ const info = quickParseSession(fp, 'claude');
299
+ if (info)
300
+ results.push(info);
301
+ }
302
+ }
303
+ }
304
+ }
305
+ catch { /* no Claude sessions */ }
306
+ // Codex: ~/.codex/sessions/**/*.jsonl (match cwd in first line)
307
+ const codexRoot = join(homedir(), '.codex', 'sessions');
308
+ try {
309
+ const codexFiles = findJsonlRecursive(codexRoot);
310
+ for (const fp of codexFiles) {
311
+ try {
312
+ const firstLine = readFileSync(fp, 'utf8').split('\n')[0] ?? '';
313
+ if (firstLine.includes(cwd) || firstLine.includes(projectName)) {
314
+ const info = quickParseSession(fp, 'codex');
315
+ if (info)
316
+ results.push(info);
317
+ }
318
+ }
319
+ catch { /* skip */ }
320
+ }
321
+ }
322
+ catch { /* no Codex sessions */ }
323
+ // Cursor: ~/.cursor/projects/<encoded>/agent-transcripts/<uuid>/<uuid>.jsonl
324
+ const cursorEncoded = cwd.replace(/\//g, '-').replace(/^-/, '');
325
+ const cursorRoot = join(homedir(), '.cursor', 'projects');
326
+ try {
327
+ for (const dir of readdirSync(cursorRoot)) {
328
+ if (dir === cursorEncoded || dir.endsWith(projectName)) {
329
+ const transcriptsDir = join(cursorRoot, dir, 'agent-transcripts');
330
+ try {
331
+ for (const sd of readdirSync(transcriptsDir)) {
332
+ const fp = join(transcriptsDir, sd, `${sd}.jsonl`);
333
+ try {
334
+ statSync(fp);
335
+ const info = quickParseSession(fp, 'cursor');
336
+ if (info)
337
+ results.push(info);
338
+ }
339
+ catch { /* skip */ }
340
+ }
341
+ }
342
+ catch { /* no transcripts */ }
343
+ }
344
+ }
345
+ }
346
+ catch { /* no Cursor sessions */ }
347
+ // Sort by most recent first.
348
+ results.sort((a, b) => b.mtime - a.mtime);
349
+ return results;
350
+ }
351
+ /**
352
+ * Quick parse a JSONL session to extract: mtime, message count,
353
+ * and the first user prompt. Reads only enough to get the data
354
+ * without loading the entire file.
355
+ */
356
+ function quickParseSession(filePath, agent) {
357
+ try {
358
+ const stat = statSync(filePath);
359
+ // For large files, read only the first 100KB to find the first prompt.
360
+ // Estimate total message count from line count of the full file.
361
+ const fullRaw = readFileSync(filePath, 'utf8');
362
+ const totalLines = fullRaw.split('\n').filter(Boolean).length;
363
+ const scanRaw = fullRaw.length > 100_000 ? fullRaw.slice(0, 100_000) : fullRaw;
364
+ const lines = scanRaw.split('\n').filter(Boolean);
365
+ if (totalLines === 0)
366
+ return null;
367
+ let messageCount = totalLines; // approximate: 1 line ≈ 1 entry
368
+ let firstPrompt = '';
369
+ for (const line of lines) {
370
+ let parsed;
371
+ try {
372
+ parsed = JSON.parse(line);
373
+ }
374
+ catch {
375
+ continue;
376
+ }
377
+ // Claude Code: {type: "user", message: "text"}
378
+ if (agent === 'claude') {
379
+ if (!firstPrompt && parsed.type === 'user') {
380
+ let text = '';
381
+ if (typeof parsed.message === 'string')
382
+ text = parsed.message;
383
+ else if (typeof parsed.message === 'object' && parsed.message) {
384
+ const content = parsed.message.content;
385
+ // content can be a string or an array of blocks.
386
+ if (typeof content === 'string')
387
+ text = content;
388
+ else if (Array.isArray(content)) {
389
+ const textBlock = content.find((b) => b.type === 'text');
390
+ if (textBlock)
391
+ text = textBlock.text;
392
+ }
393
+ }
394
+ // Skip system messages and interrupted markers.
395
+ if (text && !isSystemMessage(text))
396
+ firstPrompt = text;
397
+ }
398
+ }
399
+ // Codex: {type: "event_msg", payload: {type: "user_message", message: "..."}}
400
+ if (agent === 'codex') {
401
+ if (!firstPrompt && parsed.type === 'event_msg') {
402
+ const payload = parsed.payload;
403
+ if (payload?.type === 'user_message' && typeof payload.message === 'string') {
404
+ firstPrompt = payload.message;
405
+ }
406
+ }
407
+ }
408
+ // Cursor: {role: "user", message: {content: [{type: "text", text: "..."}]}}
409
+ if (agent === 'cursor') {
410
+ if (!firstPrompt && parsed.role === 'user' && typeof parsed.message === 'object' && parsed.message) {
411
+ const content = parsed.message.content;
412
+ if (Array.isArray(content)) {
413
+ const textBlock = content.find((b) => b.type === 'text');
414
+ if (textBlock) {
415
+ firstPrompt = textBlock.text
416
+ .replace(/<\/?user_query>/g, '')
417
+ .trim();
418
+ }
419
+ }
420
+ }
421
+ }
422
+ // Stop as soon as we find a real user prompt.
423
+ if (firstPrompt)
424
+ break;
425
+ }
426
+ if (totalLines < 2)
427
+ return null;
428
+ // Fallback title: use session ID from filename.
429
+ if (!firstPrompt) {
430
+ const name = basename(filePath, '.jsonl');
431
+ firstPrompt = `Session ${name.slice(0, 8)}... (${messageCount} messages)`;
432
+ }
433
+ // Extract session ID from filename or path.
434
+ const fileName = basename(filePath, '.jsonl');
435
+ const uuidMatch = fileName.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
436
+ const sessionId = uuidMatch?.[1] ?? fileName;
437
+ return {
438
+ path: filePath,
439
+ sessionId,
440
+ agent,
441
+ mtime: stat.mtimeMs,
442
+ messageCount,
443
+ firstPrompt,
444
+ };
445
+ }
446
+ catch {
447
+ return null;
448
+ }
449
+ }
450
+ /** Filter out system-generated messages that aren't real user prompts. */
451
+ function isSystemMessage(text) {
452
+ const t = text.trim().toLowerCase();
453
+ return (t.startsWith('[request interrupted') ||
454
+ t.startsWith('[tool use') ||
455
+ t.startsWith('<system') ||
456
+ t.startsWith('<environment') ||
457
+ t.startsWith('<permissions') ||
458
+ t.startsWith('<app-context') ||
459
+ t === '' ||
460
+ t.length < 5);
461
+ }
462
+ async function runShare(sessionArg, extraArgs) {
463
+ let auth = loadAuth();
464
+ if (!auth) {
465
+ console.log(' ▸ not signed in, starting login...');
466
+ auth = await runLogin();
467
+ }
468
+ // Find the session file.
469
+ const session = sessionArg.length >= 8
470
+ ? findSessionById(sessionArg)
471
+ : null;
472
+ if (!session) {
473
+ // Try partial match — the user might pass just the first 8 chars.
474
+ const partialMatch = findSessionByPrefix(sessionArg);
475
+ if (!partialMatch) {
476
+ console.error(`Session not found: ${sessionArg}`);
477
+ console.error('Run `promptarc context` to see available sessions.');
478
+ process.exitCode = 1;
479
+ return;
480
+ }
481
+ return doShare(partialMatch, auth);
482
+ }
483
+ return doShare(session, auth);
484
+ }
485
+ async function doShare(session, auth) {
486
+ console.log(`promptarc share`);
487
+ console.log(` ▸ session: ${session.sessionId.slice(0, 8)}...`);
488
+ console.log(` ▸ building artifact locally...`);
489
+ // Extract project name from projectId.
490
+ const projectId = session.projectId ?? '';
491
+ const parts = projectId.replace(/^(codex:|cursor:)/, '').split('-').filter(Boolean);
492
+ const projectName = parts[parts.length - 1] ?? 'unknown';
493
+ // Build the SharedReplay artifact locally (reads JSONL, redacts secrets).
494
+ let replay;
495
+ try {
496
+ replay = buildReplayFromJsonl(session.path, projectName);
497
+ }
498
+ catch (err) {
499
+ console.error(` ✗ Failed to build artifact: ${String(err)}`);
500
+ process.exitCode = 1;
501
+ return;
502
+ }
503
+ console.log(` ▸ ${replay.meta.messageCount} messages, ${replay.redactionStats.secretsRedacted} secrets redacted`);
504
+ console.log(` ▸ uploading to promptarc.dev...`);
505
+ // POST directly to hosted /api/upload with the bearer token.
506
+ const siteUrl = process.env.PROMPTARC_UPLOAD_ENDPOINT ?? 'https://promptarc.dev/api/upload';
507
+ const payload = JSON.stringify({
508
+ replay,
509
+ sourceSessionId: session.sessionId,
510
+ projectKey: projectId,
511
+ agentSource: projectId.startsWith('codex:') ? 'codex' : projectId.startsWith('cursor:') ? 'cursor' : 'claude-code',
512
+ visibility: 'private',
513
+ autoSync: false,
514
+ _h: '',
515
+ });
516
+ if (payload.length > 5 * 1024 * 1024) {
517
+ console.error(` ✗ Session too large (${(payload.length / 1024 / 1024).toFixed(1)} MB). Max 5 MB.`);
518
+ process.exitCode = 1;
519
+ return;
520
+ }
521
+ try {
522
+ const res = await fetch(siteUrl, {
523
+ method: 'POST',
524
+ headers: {
525
+ 'content-type': 'application/json',
526
+ authorization: `Bearer ${auth.token}`,
527
+ },
528
+ body: payload,
529
+ });
530
+ if (!res.ok) {
531
+ const text = await res.text();
532
+ console.error(` ✗ Upload failed: HTTP ${res.status}`);
533
+ console.error(` ${text.slice(0, 200)}`);
534
+ process.exitCode = 1;
535
+ return;
536
+ }
537
+ const json = (await res.json());
538
+ if (json.status === 'unchanged') {
539
+ console.log(` ✓ Already shared (unchanged).`);
540
+ }
541
+ else {
542
+ console.log(` ✓ Shared.`);
543
+ }
544
+ if (json.url) {
545
+ console.log(` ${json.url}`);
546
+ }
547
+ }
548
+ catch (err) {
549
+ console.error(` ✗ Upload failed: ${String(err)}`);
550
+ process.exitCode = 1;
551
+ return;
552
+ }
553
+ console.log('');
554
+ }
555
+ function findSessionByPrefix(prefix) {
556
+ const claudeRoot = join(homedir(), '.claude', 'projects');
557
+ try {
558
+ for (const dir of readdirSync(claudeRoot)) {
559
+ const projectDir = join(claudeRoot, dir);
560
+ if (!statSync(projectDir).isDirectory())
561
+ continue;
562
+ for (const file of readdirSync(projectDir)) {
563
+ if (file.startsWith(prefix) && file.endsWith('.jsonl')) {
564
+ return {
565
+ path: join(projectDir, file),
566
+ sessionId: basename(file, '.jsonl'),
567
+ projectId: dir,
568
+ };
569
+ }
570
+ }
571
+ }
572
+ }
573
+ catch { /* no Claude projects */ }
574
+ // Cursor.
575
+ const cursorRoot = join(homedir(), '.cursor', 'projects');
576
+ try {
577
+ for (const dir of readdirSync(cursorRoot)) {
578
+ const transcriptsDir = join(cursorRoot, dir, 'agent-transcripts');
579
+ try {
580
+ for (const sd of readdirSync(transcriptsDir)) {
581
+ if (sd.startsWith(prefix)) {
582
+ const fp = join(transcriptsDir, sd, `${sd}.jsonl`);
583
+ try {
584
+ statSync(fp);
585
+ return { path: fp, sessionId: sd, projectId: `cursor:${dir}` };
586
+ }
587
+ catch { /* skip */ }
588
+ }
589
+ }
590
+ }
591
+ catch { /* no transcripts */ }
592
+ }
593
+ }
594
+ catch { /* no Cursor */ }
595
+ return null;
596
+ }
597
+ function fmtRelativeTime(ms) {
598
+ const diff = Date.now() - ms;
599
+ const mins = Math.floor(diff / 60_000);
600
+ if (mins < 1)
601
+ return 'just now';
602
+ if (mins < 60)
603
+ return `${mins}m ago`;
604
+ const hours = Math.floor(mins / 60);
605
+ if (hours < 24)
606
+ return `${hours}h ago`;
607
+ const days = Math.floor(hours / 24);
608
+ if (days === 1)
609
+ return 'yesterday';
610
+ if (days < 7)
611
+ return `${days}d ago`;
612
+ const weeks = Math.floor(days / 7);
613
+ if (weeks < 5)
614
+ return `${weeks}w ago`;
615
+ const months = Math.floor(days / 30);
616
+ return `${months}mo ago`;
617
+ }
618
+ function fmtNumber(n) {
619
+ return n.toLocaleString('en-US');
620
+ }
621
+ async function runPR(args) {
622
+ let auth = loadAuth();
94
623
  if (!auth) {
95
- console.error('Not signed in. Run `promptarc login` first.');
624
+ console.log(' not signed in, starting login...');
625
+ auth = await runLogin();
626
+ }
627
+ const shouldShare = args.includes('--share');
628
+ const sessionIdArg = args.find((a) => !a.startsWith('--'));
629
+ const copyFlag = args.includes('--copy');
630
+ // 1. Find the session.
631
+ const cwd = process.cwd();
632
+ const sessionFile = sessionIdArg
633
+ ? findSessionById(sessionIdArg)
634
+ : findLatestSession(cwd);
635
+ if (!sessionFile) {
636
+ console.error(sessionIdArg
637
+ ? `Session ${sessionIdArg} not found.`
638
+ : `No sessions found for ${cwd}. Run this from a project directory with Claude Code/Codex/Cursor sessions.`);
639
+ process.exitCode = 1;
640
+ return;
641
+ }
642
+ console.log(` ▸ reading ${sessionFile.path}`);
643
+ // 2. Extract summary input from the local JSONL.
644
+ const input = extractLocalSummaryInput(sessionFile.path);
645
+ if (input.length < 20) {
646
+ console.error('Session is too short to generate a PR description.');
647
+ process.exitCode = 1;
648
+ return;
649
+ }
650
+ // 3. If --share, share the session first via the local dashboard.
651
+ let shareUrl = '';
652
+ if (shouldShare) {
653
+ console.log(' ▸ sharing session as private...');
654
+ const localOrigin = 'http://localhost:3030';
655
+ try {
656
+ const shareRes = await fetch(`${localOrigin}/api/share`, {
657
+ method: 'POST',
658
+ headers: { 'content-type': 'application/json' },
659
+ body: JSON.stringify({
660
+ sessionId: sessionFile.sessionId,
661
+ projectId: sessionFile.projectId,
662
+ visibility: 'private',
663
+ _h: '',
664
+ }),
665
+ });
666
+ if (shareRes.ok) {
667
+ const json = (await shareRes.json());
668
+ shareUrl = json.url ?? '';
669
+ console.log(` ▸ shared: ${shareUrl}`);
670
+ }
671
+ else {
672
+ console.error(' ▸ share failed (dashboard may not be running). Continuing without link.');
673
+ }
674
+ }
675
+ catch {
676
+ console.error(' ▸ could not reach local dashboard. Run `promptarc` first, or skip --share.');
677
+ }
678
+ }
679
+ // 4. Generate PR description via hosted API.
680
+ console.log(' ▸ generating PR description...');
681
+ const siteUrl = process.env.PROMPTARC_SITE_URL ?? 'https://promptarc.dev';
682
+ let markdown;
683
+ try {
684
+ const res = await fetch(`${siteUrl}/api/ai/pr-summary-local`, {
685
+ method: 'POST',
686
+ headers: {
687
+ 'content-type': 'application/json',
688
+ authorization: `Bearer ${auth.token}`,
689
+ },
690
+ body: JSON.stringify({ input }),
691
+ });
692
+ if (!res.ok) {
693
+ const json = (await res.json());
694
+ console.error(` ✗ ${json.error ?? `HTTP ${res.status}`}`);
695
+ process.exitCode = 1;
696
+ return;
697
+ }
698
+ const json = (await res.json());
699
+ markdown = json.markdown;
700
+ }
701
+ catch (err) {
702
+ console.error(` ✗ Failed to reach ${siteUrl}: ${String(err)}`);
96
703
  process.exitCode = 1;
97
704
  return;
98
705
  }
706
+ // 5. Append session link if shared.
707
+ if (shareUrl) {
708
+ markdown += `\n\n---\n*Generated by [promptarc](https://promptarc.dev) from [session →](${shareUrl})*`;
709
+ }
710
+ // 6. Output.
711
+ if (copyFlag) {
712
+ try {
713
+ const cmd = process.platform === 'darwin'
714
+ ? 'pbcopy'
715
+ : process.platform === 'win32'
716
+ ? 'clip'
717
+ : 'xclip -selection clipboard';
718
+ const { execSync } = await import('node:child_process');
719
+ execSync(cmd, { input: markdown });
720
+ console.log(' ✓ Copied to clipboard.\n');
721
+ }
722
+ catch {
723
+ // Fall through to stdout.
724
+ }
725
+ }
726
+ console.log('');
727
+ console.log(markdown);
728
+ console.log('');
729
+ }
730
+ function findLatestSession(cwd) {
731
+ const candidates = [];
732
+ const cwdParts = cwd.split('/').filter(Boolean);
733
+ const lastPart = cwdParts[cwdParts.length - 1] ?? '';
734
+ const encoded = cwd.replace(/\//g, '-');
735
+ // 1. Claude Code: ~/.claude/projects/<encoded-cwd>/*.jsonl
736
+ const claudeRoot = join(homedir(), '.claude', 'projects');
737
+ try {
738
+ const dirs = readdirSync(claudeRoot);
739
+ for (const dir of dirs) {
740
+ if (dir === encoded || dir.endsWith(lastPart)) {
741
+ const projectDir = join(claudeRoot, dir);
742
+ if (!statSync(projectDir).isDirectory())
743
+ continue;
744
+ const files = readdirSync(projectDir).filter((f) => f.endsWith('.jsonl'));
745
+ for (const f of files) {
746
+ const fp = join(projectDir, f);
747
+ candidates.push({
748
+ path: fp,
749
+ sessionId: basename(f, '.jsonl'),
750
+ projectId: dir,
751
+ });
752
+ }
753
+ }
754
+ }
755
+ }
756
+ catch { /* no Claude sessions */ }
757
+ // 2. Codex: ~/.codex/sessions/**/*.jsonl (scan all, match cwd from session_meta)
758
+ const codexRoot = join(homedir(), '.codex', 'sessions');
759
+ try {
760
+ const codexFiles = findJsonlRecursive(codexRoot);
761
+ for (const fp of codexFiles) {
762
+ // Quick check: read first line for cwd match.
763
+ try {
764
+ const firstLine = readFileSync(fp, 'utf8').split('\n')[0] ?? '';
765
+ if (firstLine.includes(cwd) || firstLine.includes(lastPart)) {
766
+ const name = basename(fp, '.jsonl');
767
+ const uuidMatch = name.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
768
+ candidates.push({
769
+ path: fp,
770
+ sessionId: uuidMatch?.[1] ?? name,
771
+ projectId: `codex:${cwd.replace(/\//g, '-')}`,
772
+ });
773
+ }
774
+ }
775
+ catch { /* skip */ }
776
+ }
777
+ }
778
+ catch { /* no Codex sessions */ }
779
+ // 3. Cursor: ~/.cursor/projects/<encoded>/agent-transcripts/<uuid>/<uuid>.jsonl
780
+ const cursorEncoded = cwd.replace(/\//g, '-').replace(/^-/, '');
781
+ const cursorRoot = join(homedir(), '.cursor', 'projects');
782
+ try {
783
+ const dirs = readdirSync(cursorRoot);
784
+ for (const dir of dirs) {
785
+ if (dir === cursorEncoded || dir.endsWith(lastPart)) {
786
+ const transcriptsDir = join(cursorRoot, dir, 'agent-transcripts');
787
+ try {
788
+ const sessionDirs = readdirSync(transcriptsDir);
789
+ for (const sd of sessionDirs) {
790
+ const fp = join(transcriptsDir, sd, `${sd}.jsonl`);
791
+ try {
792
+ statSync(fp);
793
+ candidates.push({
794
+ path: fp,
795
+ sessionId: sd,
796
+ projectId: `cursor:${dir}`,
797
+ });
798
+ }
799
+ catch { /* skip */ }
800
+ }
801
+ }
802
+ catch { /* no transcripts */ }
803
+ }
804
+ }
805
+ }
806
+ catch { /* no Cursor sessions */ }
807
+ if (candidates.length === 0)
808
+ return null;
809
+ // Return the most recently modified file.
810
+ candidates.sort((a, b) => {
811
+ try {
812
+ return statSync(b.path).mtimeMs - statSync(a.path).mtimeMs;
813
+ }
814
+ catch {
815
+ return 0;
816
+ }
817
+ });
818
+ return candidates[0];
819
+ }
820
+ function findJsonlRecursive(dir) {
821
+ const results = [];
822
+ try {
823
+ const entries = readdirSync(dir, { withFileTypes: true });
824
+ for (const entry of entries) {
825
+ const full = join(dir, entry.name);
826
+ if (entry.isDirectory())
827
+ results.push(...findJsonlRecursive(full));
828
+ else if (entry.name.endsWith('.jsonl'))
829
+ results.push(full);
830
+ }
831
+ }
832
+ catch { /* skip */ }
833
+ return results;
834
+ }
835
+ function findSessionById(id) {
836
+ // Search Claude Code.
837
+ const claudeRoot = join(homedir(), '.claude', 'projects');
838
+ try {
839
+ for (const dir of readdirSync(claudeRoot)) {
840
+ const fp = join(claudeRoot, dir, `${id}.jsonl`);
841
+ try {
842
+ statSync(fp);
843
+ return { path: fp, sessionId: id, projectId: dir };
844
+ }
845
+ catch { /* skip */ }
846
+ }
847
+ }
848
+ catch { /* no Claude */ }
849
+ // Search Cursor.
850
+ const cursorRoot = join(homedir(), '.cursor', 'projects');
851
+ try {
852
+ for (const dir of readdirSync(cursorRoot)) {
853
+ const fp = join(cursorRoot, dir, 'agent-transcripts', id, `${id}.jsonl`);
854
+ try {
855
+ statSync(fp);
856
+ return { path: fp, sessionId: id, projectId: `cursor:${dir}` };
857
+ }
858
+ catch { /* skip */ }
859
+ }
860
+ }
861
+ catch { /* no Cursor */ }
862
+ // Search Codex (scan all, match by UUID in filename).
863
+ const codexRoot = join(homedir(), '.codex', 'sessions');
864
+ const codexFiles = findJsonlRecursive(codexRoot);
865
+ for (const fp of codexFiles) {
866
+ if (fp.includes(id)) {
867
+ return { path: fp, sessionId: id, projectId: null };
868
+ }
869
+ }
870
+ return null;
871
+ }
872
+ /**
873
+ * Lightweight summary input extraction for the CLI. Reads the local
874
+ * JSONL and pulls: first user prompt, last user prompt, tool call
875
+ * names + file paths, and error outputs. Capped at 4K chars.
876
+ */
877
+ function extractLocalSummaryInput(filePath) {
878
+ const raw = readFileSync(filePath, 'utf8');
879
+ const lines = raw.split('\n').filter(Boolean);
880
+ const parts = [];
881
+ const userPrompts = [];
882
+ const toolCalls = [];
883
+ const errors = [];
884
+ const filePaths = new Set();
885
+ for (const line of lines) {
886
+ let parsed;
887
+ try {
888
+ parsed = JSON.parse(line);
889
+ }
890
+ catch {
891
+ continue;
892
+ }
893
+ // Claude Code format.
894
+ const type = parsed.type;
895
+ const msg = parsed.message;
896
+ if (type === 'user' && typeof msg === 'string') {
897
+ userPrompts.push(msg);
898
+ }
899
+ if (typeof msg === 'object' && msg && Array.isArray(msg.content)) {
900
+ for (const block of msg.content) {
901
+ if (block.type === 'text' && typeof block.text === 'string') {
902
+ if (type === 'user')
903
+ userPrompts.push(block.text);
904
+ }
905
+ if (block.type === 'tool_use') {
906
+ const name = block.name;
907
+ const input = block.input;
908
+ const fp = input?.file_path ?? input?.path ?? '';
909
+ if (fp)
910
+ filePaths.add(fp);
911
+ if (input?.command)
912
+ toolCalls.push(`${name}: ${String(input.command).slice(0, 100)}`);
913
+ else if (fp)
914
+ toolCalls.push(`${name}: ${fp}`);
915
+ else
916
+ toolCalls.push(name);
917
+ }
918
+ if (block.type === 'tool_result' && block.is_error) {
919
+ const output = typeof block.content === 'string' ? block.content : String(block.output ?? '');
920
+ if (output)
921
+ errors.push(output.slice(0, 200));
922
+ }
923
+ }
924
+ }
925
+ // Codex format: {timestamp, type, payload}.
926
+ if (parsed.payload && typeof parsed.payload === 'object') {
927
+ const payload = parsed.payload;
928
+ if (payload.type === 'message' && payload.role === 'user') {
929
+ const content = payload.content;
930
+ if (Array.isArray(content)) {
931
+ for (const block of content) {
932
+ if ((block.type === 'input_text' || block.type === 'text') && typeof block.text === 'string') {
933
+ userPrompts.push(block.text);
934
+ }
935
+ }
936
+ }
937
+ }
938
+ }
939
+ // Cursor format: {role, message: {content}}.
940
+ if (parsed.role === 'user' && typeof parsed.message === 'object' && parsed.message) {
941
+ const content = parsed.message.content;
942
+ if (Array.isArray(content)) {
943
+ for (const block of content) {
944
+ if (block.type === 'text' && typeof block.text === 'string') {
945
+ const text = block.text.replace(/<\/?user_query>/g, '').trim();
946
+ if (text)
947
+ userPrompts.push(text);
948
+ }
949
+ }
950
+ }
951
+ }
952
+ }
953
+ if (userPrompts.length > 0) {
954
+ parts.push('--- First prompt ---');
955
+ parts.push(userPrompts[0].slice(0, 500));
956
+ }
957
+ if (userPrompts.length > 1) {
958
+ parts.push('--- Last prompt ---');
959
+ parts.push(userPrompts[userPrompts.length - 1].slice(0, 300));
960
+ }
961
+ if (toolCalls.length > 0) {
962
+ parts.push('--- Tool calls ---');
963
+ parts.push([...new Set(toolCalls)].slice(0, 15).join('\n'));
964
+ }
965
+ if (filePaths.size > 0) {
966
+ parts.push(`--- Files (${filePaths.size}) ---`);
967
+ parts.push([...filePaths].slice(0, 20).join(', '));
968
+ }
969
+ if (errors.length > 0) {
970
+ parts.push('--- Errors ---');
971
+ parts.push(errors.slice(0, 3).join('\n'));
972
+ }
973
+ return parts.join('\n').slice(0, 4000);
974
+ }
975
+ async function runPull(slug) {
976
+ let auth = loadAuth();
977
+ if (!auth) {
978
+ console.log(' ▸ not signed in, starting login...');
979
+ auth = await runLogin();
980
+ }
99
981
  console.log(`promptarc pull ${slug}`);
100
982
  console.log(' ▸ downloading session...');
101
983
  const siteUrl = process.env.PROMPTARC_SITE_URL ?? 'https://promptarc.dev';