@cursor/july 0.1.96 → 0.1.97

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 (321) hide show
  1. package/README.md +3 -2
  2. package/dist/bin/agent-serve.js +14 -3
  3. package/dist/channels/origin/api.d.ts +15 -4
  4. package/dist/channels/origin/api.d.ts.map +1 -1
  5. package/dist/channels/origin/api.js +19 -3
  6. package/dist/channels/origin/binding.d.ts +11 -0
  7. package/dist/channels/origin/binding.d.ts.map +1 -1
  8. package/dist/channels/origin/binding.js +41 -25
  9. package/dist/channels/origin/index.d.ts +1 -0
  10. package/dist/channels/origin/index.d.ts.map +1 -1
  11. package/dist/channels/origin/index.js +1 -0
  12. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  13. package/dist/channels/origin/origin-channel.js +3 -0
  14. package/dist/channels/origin/reactions.d.ts +56 -0
  15. package/dist/channels/origin/reactions.d.ts.map +1 -0
  16. package/dist/channels/origin/reactions.js +67 -0
  17. package/dist/channels/origin/types.d.ts +11 -0
  18. package/dist/channels/origin/types.d.ts.map +1 -1
  19. package/dist/channels/slack/approvals.d.ts +16 -0
  20. package/dist/channels/slack/approvals.d.ts.map +1 -1
  21. package/dist/channels/slack/approvals.js +86 -0
  22. package/dist/channels/slack/cli-project.d.ts +19 -0
  23. package/dist/channels/slack/cli-project.d.ts.map +1 -0
  24. package/dist/channels/slack/cli-project.js +234 -0
  25. package/dist/channels/slack/defaults.d.ts.map +1 -1
  26. package/dist/channels/slack/defaults.js +97 -39
  27. package/dist/channels/slack/init.d.ts +3 -2
  28. package/dist/channels/slack/init.d.ts.map +1 -1
  29. package/dist/channels/slack/init.js +5 -5
  30. package/dist/channels/slack/interactive.d.ts +2 -1
  31. package/dist/channels/slack/interactive.d.ts.map +1 -1
  32. package/dist/channels/slack/interactive.js +15 -4
  33. package/dist/channels/slack/manifest.d.ts +1 -1
  34. package/dist/channels/slack/manifest.js +1 -1
  35. package/dist/channels/slack/setup.d.ts +1 -1
  36. package/dist/channels/slack/setup.d.ts.map +1 -1
  37. package/dist/channels/slack/setup.js +10 -6
  38. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  39. package/dist/channels/slack/slack-channel.js +1 -0
  40. package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
  41. package/dist/channels/slack/tool-approval-events.js +91 -1
  42. package/dist/docs/404.html +2 -2
  43. package/dist/docs/ab.html +4 -4
  44. package/dist/docs/assets/{app.jXleCmOc.js → app.B3rWNYE1.js} +1 -1
  45. package/dist/docs/assets/chunks/@localSearchIndexroot.BU9nHdnQ.js +1 -0
  46. package/dist/docs/assets/chunks/{VPLocalSearchBox.8LhyglIT.js → VPLocalSearchBox.C1lhqJJR.js} +1 -1
  47. package/dist/docs/assets/chunks/{theme.9ged-pr2.js → theme.B40_SXuv.js} +2 -2
  48. package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
  49. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
  50. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
  51. package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
  52. package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
  53. package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
  54. package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
  55. package/dist/docs/assets/{reference_cli.md.CCnG-nJx.js → reference_cli.md.RyZf5OTE.js} +9 -6
  56. package/dist/docs/assets/{reference_cli.md.CCnG-nJx.lean.js → reference_cli.md.RyZf5OTE.lean.js} +1 -1
  57. package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
  58. package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.5zOAbV86.js} +1 -1
  59. package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
  60. package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.B84gw9Ii.js} +1 -1
  61. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
  62. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
  63. package/dist/docs/assets/troubleshooting.md.DCiPBhYs.js +1 -0
  64. package/dist/docs/building-with-agents.html +4 -4
  65. package/dist/docs/concepts.html +4 -4
  66. package/dist/docs/deployment.html +7 -7
  67. package/dist/docs/deployment.md +5 -4
  68. package/dist/docs/evals.html +4 -4
  69. package/dist/docs/guides/agent-to-agent.html +4 -4
  70. package/dist/docs/guides/cloud-runtime.html +4 -4
  71. package/dist/docs/guides/convert-automation.html +4 -4
  72. package/dist/docs/guides/github.html +4 -4
  73. package/dist/docs/guides/human-in-the-loop.html +4 -4
  74. package/dist/docs/guides/mcp-oauth.html +8 -8
  75. package/dist/docs/guides/mcp-oauth.md +26 -8
  76. package/dist/docs/guides/opentelemetry.html +4 -4
  77. package/dist/docs/guides/slack.html +17 -33
  78. package/dist/docs/guides/slack.md +92 -201
  79. package/dist/docs/guides/webhooks.html +4 -4
  80. package/dist/docs/hashmap.json +1 -1
  81. package/dist/docs/hillclimbing.html +4 -4
  82. package/dist/docs/index.html +6 -6
  83. package/dist/docs/index.md +5 -3
  84. package/dist/docs/llms-full.txt +299 -653
  85. package/dist/docs/llms.txt +2 -2
  86. package/dist/docs/quickstart.html +4 -4
  87. package/dist/docs/reference/agent-config.html +6 -6
  88. package/dist/docs/reference/agent-config.md +2 -2
  89. package/dist/docs/reference/artifacts.html +4 -4
  90. package/dist/docs/reference/channels.html +4 -4
  91. package/dist/docs/reference/cli.html +13 -10
  92. package/dist/docs/reference/cli.md +38 -16
  93. package/dist/docs/reference/connections.html +9 -8
  94. package/dist/docs/reference/connections.md +6 -3
  95. package/dist/docs/reference/hooks.html +4 -4
  96. package/dist/docs/reference/http-api.html +6 -6
  97. package/dist/docs/reference/http-api.md +2 -1
  98. package/dist/docs/reference/instructions.html +4 -4
  99. package/dist/docs/reference/playground.html +4 -4
  100. package/dist/docs/reference/project-layout.html +4 -4
  101. package/dist/docs/reference/prompt.html +4 -4
  102. package/dist/docs/reference/schedules.html +4 -4
  103. package/dist/docs/reference/sessions.html +5 -5
  104. package/dist/docs/reference/sessions.md +4 -2
  105. package/dist/docs/reference/skills.html +4 -4
  106. package/dist/docs/reference/subagents.html +4 -4
  107. package/dist/docs/reference/tools.html +6 -6
  108. package/dist/docs/reference/tools.md +9 -2
  109. package/dist/docs/scaffolding-agents.html +4 -4
  110. package/dist/docs/storage.html +4 -4
  111. package/dist/docs/templates/agentic-owners.html +4 -4
  112. package/dist/docs/templates/agents-md.html +28 -0
  113. package/dist/docs/templates/agents-md.md +105 -0
  114. package/dist/docs/templates/code-wiki.html +5 -5
  115. package/dist/docs/templates/demo.html +4 -4
  116. package/dist/docs/templates/pr-autofixer.html +4 -4
  117. package/dist/docs/templates/security-help.html +4 -4
  118. package/dist/docs/templates/security-reviewer.html +5 -5
  119. package/dist/docs/templates/triage.html +4 -4
  120. package/dist/docs/troubleshooting.html +5 -5
  121. package/dist/docs/troubleshooting.md +2 -2
  122. package/dist/evals/run-facts.d.ts.map +1 -1
  123. package/dist/evals/run-facts.js +27 -0
  124. package/dist/internal/advertise-tools.d.ts.map +1 -1
  125. package/dist/internal/advertise-tools.js +15 -9
  126. package/dist/internal/approval-gate.d.ts +12 -13
  127. package/dist/internal/approval-gate.d.ts.map +1 -1
  128. package/dist/internal/approval-gate.js +16 -21
  129. package/dist/internal/chat-client.js +10 -0
  130. package/dist/internal/cli-deploy.d.ts +5 -0
  131. package/dist/internal/cli-deploy.d.ts.map +1 -1
  132. package/dist/internal/cli-deploy.js +50 -17
  133. package/dist/internal/cli-mcp-oauth.js +8 -18
  134. package/dist/internal/cli-slack-manual.d.ts +24 -2
  135. package/dist/internal/cli-slack-manual.d.ts.map +1 -1
  136. package/dist/internal/cli-slack-manual.js +251 -20
  137. package/dist/internal/cli-slack.d.ts +9 -2
  138. package/dist/internal/cli-slack.d.ts.map +1 -1
  139. package/dist/internal/cli-slack.js +29 -82
  140. package/dist/internal/cursor/account-mcp.d.ts +1 -0
  141. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  142. package/dist/internal/cursor/account-mcp.js +17 -0
  143. package/dist/internal/cursor-agent-template.d.ts +1 -1
  144. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  145. package/dist/internal/cursor-agent-template.js +1 -0
  146. package/dist/internal/deploy-client.d.ts +17 -0
  147. package/dist/internal/deploy-client.d.ts.map +1 -1
  148. package/dist/internal/deploy-client.js +31 -10
  149. package/dist/internal/env-file.d.ts +19 -0
  150. package/dist/internal/env-file.d.ts.map +1 -0
  151. package/dist/internal/env-file.js +90 -0
  152. package/dist/internal/framework-file-storage.d.ts +1 -1
  153. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  154. package/dist/internal/framework-file-storage.js +6 -0
  155. package/dist/internal/hosted-delivery-protocol.d.ts +7 -0
  156. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  157. package/dist/internal/hosted-delivery-protocol.js +7 -0
  158. package/dist/internal/hosted-delivery.d.ts +6 -0
  159. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  160. package/dist/internal/hosted-delivery.js +63 -22
  161. package/dist/internal/hosted-execution-diag.d.ts +48 -0
  162. package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
  163. package/dist/internal/hosted-execution-diag.js +150 -0
  164. package/dist/internal/http-channel.d.ts.map +1 -1
  165. package/dist/internal/http-channel.js +81 -1
  166. package/dist/internal/init-project.d.ts.map +1 -1
  167. package/dist/internal/init-project.js +2 -0
  168. package/dist/internal/logs-client.d.ts +5 -0
  169. package/dist/internal/logs-client.d.ts.map +1 -1
  170. package/dist/internal/logs-client.js +5 -0
  171. package/dist/internal/mcp-endpoint.js +2 -3
  172. package/dist/internal/mcp-host.d.ts +10 -0
  173. package/dist/internal/mcp-host.d.ts.map +1 -1
  174. package/dist/internal/mcp-host.js +57 -8
  175. package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
  176. package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
  177. package/dist/internal/mcp-oauth-on-demand.js +287 -0
  178. package/dist/internal/mcp-oauth-persist.d.ts +42 -0
  179. package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
  180. package/dist/internal/mcp-oauth-persist.js +92 -0
  181. package/dist/internal/mcp-oauth.d.ts +1 -0
  182. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  183. package/dist/internal/mcp-oauth.js +10 -2
  184. package/dist/internal/otel/collector.d.ts.map +1 -1
  185. package/dist/internal/otel/collector.js +22 -9
  186. package/dist/internal/server.d.ts +3 -0
  187. package/dist/internal/server.d.ts.map +1 -1
  188. package/dist/internal/server.js +49 -22
  189. package/dist/internal/session-engine.d.ts +88 -3
  190. package/dist/internal/session-engine.d.ts.map +1 -1
  191. package/dist/internal/session-engine.js +515 -43
  192. package/dist/internal/sessions-client.d.ts +9 -7
  193. package/dist/internal/sessions-client.d.ts.map +1 -1
  194. package/dist/internal/sessions-client.js +3 -1
  195. package/dist/internal/storage-coordinator.d.ts +7 -0
  196. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  197. package/dist/internal/storage-coordinator.js +28 -5
  198. package/dist/internal/storage-roles.d.ts +5 -0
  199. package/dist/internal/storage-roles.d.ts.map +1 -1
  200. package/dist/internal/stream-progress.d.ts.map +1 -1
  201. package/dist/internal/stream-progress.js +10 -0
  202. package/dist/internal/tool-policy.d.ts +9 -1
  203. package/dist/internal/tool-policy.d.ts.map +1 -1
  204. package/dist/internal/tool-policy.js +13 -0
  205. package/dist/internal/trajectory.d.ts.map +1 -1
  206. package/dist/internal/trajectory.js +10 -0
  207. package/dist/internal/turn-outbox.d.ts +24 -0
  208. package/dist/internal/turn-outbox.d.ts.map +1 -0
  209. package/dist/internal/turn-outbox.js +80 -0
  210. package/dist/playground/assets/{index-Cx9CclK_.js → index-DrkI6y5O.js} +45 -45
  211. package/dist/playground/assets/index-DzNGwm7q.css +1 -0
  212. package/dist/playground/index.html +2 -2
  213. package/dist/storage-backends/cursor-hosted.d.ts +1 -1
  214. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  215. package/dist/storage-backends/cursor-hosted.js +12 -0
  216. package/dist/storage.d.ts +8 -0
  217. package/dist/storage.d.ts.map +1 -1
  218. package/dist/types.d.ts +63 -7
  219. package/dist/types.d.ts.map +1 -1
  220. package/docs/README.md +5 -3
  221. package/docs/deployment.md +5 -4
  222. package/docs/guides/mcp-oauth.md +26 -8
  223. package/docs/guides/slack.md +93 -202
  224. package/docs/reference/agent-config.md +2 -2
  225. package/docs/reference/cli.md +38 -16
  226. package/docs/reference/connections.md +6 -3
  227. package/docs/reference/http-api.md +2 -1
  228. package/docs/reference/sessions.md +4 -2
  229. package/docs/reference/tools.md +9 -2
  230. package/docs/templates/agents-md.md +110 -0
  231. package/docs/troubleshooting.md +2 -2
  232. package/package.json +1 -1
  233. package/skills/create-agent/SKILL.md +2 -1
  234. package/skills/mcp-auth/SKILL.md +6 -3
  235. package/skills/setup-slack/SKILL.md +23 -13
  236. package/src/bin/agent-serve.ts +13 -1
  237. package/src/channels/origin/api.ts +24 -5
  238. package/src/channels/origin/binding.ts +55 -26
  239. package/src/channels/origin/index.ts +1 -0
  240. package/src/channels/origin/origin-channel.ts +4 -0
  241. package/src/channels/origin/reactions.ts +82 -0
  242. package/src/channels/origin/types.ts +11 -0
  243. package/src/channels/slack/approvals.ts +105 -0
  244. package/src/channels/slack/cli-project.ts +267 -0
  245. package/src/channels/slack/defaults.ts +109 -46
  246. package/src/channels/slack/init.ts +10 -5
  247. package/src/channels/slack/interactive.ts +35 -9
  248. package/src/channels/slack/manifest.ts +1 -1
  249. package/src/channels/slack/setup.ts +10 -6
  250. package/src/channels/slack/slack-channel.ts +1 -0
  251. package/src/channels/slack/tool-approval-events.ts +93 -0
  252. package/src/evals/run-facts.ts +32 -0
  253. package/src/internal/advertise-tools.ts +31 -10
  254. package/src/internal/approval-gate.ts +20 -29
  255. package/src/internal/chat-client.ts +19 -0
  256. package/src/internal/cli-deploy.ts +79 -9
  257. package/src/internal/cli-mcp-oauth.ts +7 -21
  258. package/src/internal/cli-slack-manual.ts +350 -18
  259. package/src/internal/cli-slack.ts +40 -84
  260. package/src/internal/cursor/account-mcp.ts +21 -0
  261. package/src/internal/cursor-agent-template.ts +1 -0
  262. package/src/internal/deploy-client.ts +43 -0
  263. package/src/internal/env-file.ts +84 -0
  264. package/src/internal/framework-file-storage.ts +7 -1
  265. package/src/internal/hosted-delivery-protocol.ts +7 -0
  266. package/src/internal/hosted-delivery.ts +100 -27
  267. package/src/internal/hosted-execution-diag.ts +207 -0
  268. package/src/internal/http-channel.ts +99 -0
  269. package/src/internal/init-project.ts +2 -0
  270. package/src/internal/logs-client.ts +5 -0
  271. package/src/internal/mcp-endpoint.ts +13 -4
  272. package/src/internal/mcp-host.ts +77 -11
  273. package/src/internal/mcp-oauth-on-demand.ts +352 -0
  274. package/src/internal/mcp-oauth-persist.ts +127 -0
  275. package/src/internal/mcp-oauth.ts +17 -1
  276. package/src/internal/otel/collector.ts +30 -0
  277. package/src/internal/server.ts +59 -9
  278. package/src/internal/session-engine.ts +659 -56
  279. package/src/internal/sessions-client.ts +17 -8
  280. package/src/internal/storage-coordinator.ts +38 -8
  281. package/src/internal/storage-roles.ts +7 -0
  282. package/src/internal/stream-progress.ts +19 -0
  283. package/src/internal/tool-policy.ts +18 -0
  284. package/src/internal/trajectory.ts +10 -0
  285. package/src/internal/turn-outbox.ts +85 -0
  286. package/src/storage-backends/cursor-hosted.ts +13 -1
  287. package/src/storage.ts +12 -0
  288. package/src/types.ts +68 -8
  289. package/templates/agents-md/README.md +39 -0
  290. package/templates/agents-md/agent/agent.ts +8 -0
  291. package/templates/agents-md/agent/channels/github.ts +10 -0
  292. package/templates/agents-md/agent/channels/queue.ts +28 -0
  293. package/templates/agents-md/agent/channels/slack.ts +9 -0
  294. package/templates/agents-md/agent/instructions.md +39 -0
  295. package/templates/agents-md/agent/lib/collect.ts +439 -0
  296. package/templates/agents-md/agent/lib/config.ts +8 -0
  297. package/templates/agents-md/agent/lib/paths.ts +51 -0
  298. package/templates/agents-md/agent/schedules/weekly.ts +17 -0
  299. package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
  300. package/templates/agents-md/evals/agents.eval.ts +135 -0
  301. package/templates/agents-md/evals/evals.config.ts +5 -0
  302. package/templates/agents-md/init.json +38 -0
  303. package/templates/agents-md/package.json +18 -0
  304. package/templates/agents-md/tsconfig.json +12 -0
  305. package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +0 -1
  306. package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
  307. package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
  308. package/dist/docs/assets/index.md.CI93Ltlb.js +0 -5
  309. package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
  310. package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
  311. package/dist/docs/design/agsh.md +0 -406
  312. package/dist/playground/assets/index-B1DDvBeC.css +0 -1
  313. package/docs/design/agsh.md +0 -406
  314. /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
  315. /package/dist/docs/assets/{index.md.CI93Ltlb.lean.js → index.md.CVeRUOeZ.lean.js} +0 -0
  316. /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
  317. /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
  318. /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.5zOAbV86.lean.js} +0 -0
  319. /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
  320. /package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.B84gw9Ii.lean.js} +0 -0
  321. /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
@@ -0,0 +1,439 @@
1
+ import {
2
+ prompt,
3
+ type AuthContext,
4
+ type HostContext,
5
+ type SendMessageFn,
6
+ } from "@cursor/july";
7
+ import { REPOS, slackChannels } from "./config.js";
8
+ import { isAgentsPath, isUpdateRef, snapshotPath } from "./paths.js";
9
+
10
+ const LOOKBACK_DAYS = 7;
11
+ const MAX_PRS = 50;
12
+ const MAX_FILES = 40;
13
+ const MAX_COMMENTS = 8;
14
+ const MAX_BODY = 1_200;
15
+ const MAX_SLACK = 80;
16
+ const MAX_LINE = 400;
17
+ const MAX_AGENTS = 20;
18
+ const CHANNEL_ID = /^[CGD][A-Z0-9]{8,}$/i;
19
+
20
+ type Octokit = Awaited<ReturnType<HostContext["github"]["getOctokit"]>>;
21
+ type Slack = Awaited<ReturnType<HostContext["slack"]["getClient"]>>;
22
+
23
+ type Pull = {
24
+ number: number;
25
+ title: string;
26
+ body: string;
27
+ author: string;
28
+ merged: string;
29
+ url: string;
30
+ files: string[];
31
+ comments: string[];
32
+ };
33
+
34
+ export type Week = {
35
+ window: string;
36
+ prompt: string;
37
+ files: Record<string, string>;
38
+ };
39
+
40
+ export async function collectWeek(host: HostContext): Promise<Week> {
41
+ const now = new Date();
42
+ const start = new Date(now.getTime() - LOOKBACK_DAYS * 86_400_000);
43
+ const until = isoDate(now);
44
+ const since = isoDate(start);
45
+ const window = `${since}/${until}`;
46
+ const repo = REPOS[0];
47
+ if (repo === undefined) {
48
+ throw new Error("REPOS is empty");
49
+ }
50
+ const files: Record<string, string> = {
51
+ "week/window.txt": `${window}\n`,
52
+ "week/repo.txt": `${repo}\n`,
53
+ };
54
+
55
+ const octokit = await host.github.getOctokit();
56
+ const parsed = splitRepo(repo);
57
+ const repoInfo = await octokit.rest.repos.get(parsed);
58
+ const base = repoInfo.data.default_branch;
59
+ files["week/base.ref"] = `${base}\n`;
60
+ const pulls = await loadPulls(octokit, parsed, since);
61
+ files["week/prs.md"] = formatPulls(pulls);
62
+ const agents = await loadAgents(octokit, parsed, base, pulls);
63
+ Object.assign(files, agents.files);
64
+ files["week/files.txt"] = `${agents.paths.join("\n")}\n`;
65
+ files["week/slack.md"] = await loadSlack(host, Math.floor(start.getTime() / 1000));
66
+
67
+ return {
68
+ window,
69
+ files,
70
+ prompt: prompt`
71
+ Weekly AGENTS.md review for ${repo} (${window}).
72
+ Read week/ and week/tree/. Call apply_agents once.
73
+ `,
74
+ };
75
+ }
76
+
77
+ export function startWeek(
78
+ send: SendMessageFn,
79
+ week: Week,
80
+ auth: AuthContext | null
81
+ ) {
82
+ return send(week.prompt, {
83
+ auth,
84
+ title: `AGENTS.md ${week.window}`,
85
+ continuationToken: `agents-md:${week.window}`,
86
+ admission: "coalesce",
87
+ workspaceFiles: week.files,
88
+ });
89
+ }
90
+
91
+ export function weekFromTarget(target: unknown): Week | undefined {
92
+ if (!isRecord(target)) {
93
+ return undefined;
94
+ }
95
+ if (typeof target.window !== "string" || typeof target.prompt !== "string") {
96
+ return undefined;
97
+ }
98
+ if (!isRecord(target.files)) {
99
+ return undefined;
100
+ }
101
+ const files: Record<string, string> = {};
102
+ for (const [path, content] of Object.entries(target.files)) {
103
+ if (typeof content !== "string") {
104
+ return undefined;
105
+ }
106
+ files[path] = content;
107
+ }
108
+ return { window: target.window, prompt: target.prompt, files };
109
+ }
110
+
111
+ async function loadPulls(
112
+ octokit: Octokit,
113
+ repo: { owner: string; repo: string },
114
+ since: string
115
+ ): Promise<Pull[]> {
116
+ const sinceMs = Date.parse(`${since}T00:00:00Z`);
117
+ let count = 0;
118
+ const closed = await octokit.paginate(
119
+ octokit.rest.pulls.list,
120
+ {
121
+ ...repo,
122
+ state: "closed",
123
+ sort: "updated",
124
+ direction: "desc",
125
+ per_page: 50,
126
+ },
127
+ (response, done) => {
128
+ const keep = [];
129
+ for (const pull of response.data) {
130
+ if (count + keep.length >= MAX_PRS) {
131
+ done();
132
+ break;
133
+ }
134
+ if (Date.parse(pull.updated_at) < sinceMs) {
135
+ done();
136
+ break;
137
+ }
138
+ if (pull.merged_at === null || Date.parse(pull.merged_at) < sinceMs) {
139
+ continue;
140
+ }
141
+ if (isUpdateRef(pull.head.ref)) {
142
+ continue;
143
+ }
144
+ keep.push(pull);
145
+ }
146
+ count += keep.length;
147
+ return keep;
148
+ }
149
+ );
150
+ const pulls: Pull[] = [];
151
+ for (const pull of closed) {
152
+ pulls.push(await loadPull(octokit, repo, pull));
153
+ }
154
+ return pulls;
155
+ }
156
+
157
+ async function loadPull(
158
+ octokit: Octokit,
159
+ repo: { owner: string; repo: string },
160
+ pull: {
161
+ number: number;
162
+ title: string;
163
+ body?: string | null;
164
+ user?: { login?: string } | null;
165
+ merged_at?: string | null;
166
+ html_url: string;
167
+ }
168
+ ): Promise<Pull> {
169
+ const args = { ...repo, pull_number: pull.number };
170
+ const [listed, issue, review] = await Promise.all([
171
+ octokit.rest.pulls.listFiles({ ...args, per_page: MAX_FILES }),
172
+ octokit.rest.issues.listComments({
173
+ ...repo,
174
+ issue_number: pull.number,
175
+ per_page: MAX_COMMENTS,
176
+ }),
177
+ octokit.rest.pulls.listReviewComments({
178
+ ...args,
179
+ per_page: MAX_COMMENTS,
180
+ }),
181
+ ]);
182
+ const comments = [...issue.data, ...review.data]
183
+ .map((item) => {
184
+ const author = item.user?.login ?? "unknown";
185
+ return `@${author}: ${clip(item.body ?? "", MAX_LINE)}`;
186
+ })
187
+ .filter((line) => !line.endsWith(": "));
188
+ return {
189
+ number: pull.number,
190
+ title: pull.title,
191
+ body: clip(pull.body ?? "", MAX_BODY),
192
+ author: pull.user?.login ?? "unknown",
193
+ merged: pull.merged_at ?? "",
194
+ url: pull.html_url,
195
+ files: listed.data.map((file) => file.filename),
196
+ comments,
197
+ };
198
+ }
199
+
200
+ async function loadAgents(
201
+ octokit: Octokit,
202
+ repo: { owner: string; repo: string },
203
+ ref: string,
204
+ pulls: Pull[]
205
+ ): Promise<{ paths: string[]; files: Record<string, string> }> {
206
+ const paths = new Set<string>(["AGENTS.md"]);
207
+ for (const path of await listAgents(octokit, repo, ref)) {
208
+ paths.add(path);
209
+ }
210
+ for (const pull of pulls) {
211
+ for (const file of pull.files) {
212
+ if (isAgentsPath(file)) {
213
+ paths.add(file);
214
+ }
215
+ }
216
+ }
217
+ const chosen = [...paths].slice(0, MAX_AGENTS);
218
+ const files: Record<string, string> = {};
219
+ const found: string[] = [];
220
+ for (const path of chosen) {
221
+ const body = await readFile(octokit, { ...repo, path, ref });
222
+ if (body !== undefined) {
223
+ found.push(path);
224
+ files[snapshotPath(path)] = body;
225
+ }
226
+ }
227
+ return { paths: found.sort(), files };
228
+ }
229
+
230
+ async function listAgents(
231
+ octokit: Octokit,
232
+ repo: { owner: string; repo: string },
233
+ ref: string
234
+ ): Promise<string[]> {
235
+ try {
236
+ const { data: refData } = await octokit.rest.git.getRef({
237
+ ...repo,
238
+ ref: `heads/${ref}`,
239
+ });
240
+ const { data } = await octokit.rest.git.getTree({
241
+ ...repo,
242
+ tree_sha: refData.object.sha,
243
+ recursive: "1",
244
+ });
245
+ return (data.tree ?? [])
246
+ .map((entry) => entry.path)
247
+ .filter(
248
+ (path): path is string =>
249
+ typeof path === "string" && isAgentsPath(path)
250
+ );
251
+ } catch (error) {
252
+ if (isNotFound(error)) {
253
+ return [];
254
+ }
255
+ throw error;
256
+ }
257
+ }
258
+
259
+ async function loadSlack(host: HostContext, oldest: number): Promise<string> {
260
+ const names = slackChannels();
261
+ if (names.length === 0) {
262
+ return "No Slack channels configured.\n";
263
+ }
264
+ let client: Slack;
265
+ try {
266
+ client = await host.slack.getClient();
267
+ } catch (error) {
268
+ return `Slack unavailable: ${errorMessage(error)}\n`;
269
+ }
270
+ const sections: string[] = [];
271
+ for (const name of names) {
272
+ sections.push(await readChannel(client, name, oldest));
273
+ }
274
+ return `${sections.join("\n")}\n`;
275
+ }
276
+
277
+ async function readChannel(
278
+ client: Slack,
279
+ name: string,
280
+ oldest: number
281
+ ): Promise<string> {
282
+ const id = await resolveChannel(client, name);
283
+ if (id === undefined) {
284
+ return `## ${name}\nUnresolved. Invite the bot and check the name.\n`;
285
+ }
286
+ let history: Awaited<ReturnType<Slack["conversations"]["history"]>>;
287
+ try {
288
+ history = await client.conversations.history({
289
+ channel: id,
290
+ oldest: String(oldest),
291
+ limit: MAX_SLACK,
292
+ });
293
+ } catch (error) {
294
+ return `## ${name}\n${errorMessage(error)}\n`;
295
+ }
296
+ const lines = [`## ${name}`];
297
+ for (const msg of history.messages ?? []) {
298
+ if (skipSlack(msg)) {
299
+ continue;
300
+ }
301
+ lines.push(`- ${formatSlack(msg)}`);
302
+ if ((msg.reply_count ?? 0) === 0 || msg.ts === undefined) {
303
+ continue;
304
+ }
305
+ const replies = await client.conversations.replies({
306
+ channel: id,
307
+ ts: msg.ts,
308
+ limit: 6,
309
+ });
310
+ for (const reply of (replies.messages ?? []).slice(1, 6)) {
311
+ if (skipSlack(reply)) {
312
+ continue;
313
+ }
314
+ lines.push(` - ${formatSlack(reply)}`);
315
+ }
316
+ }
317
+ if (lines.length === 1) {
318
+ lines.push("No messages this week.");
319
+ }
320
+ return `${lines.join("\n")}\n`;
321
+ }
322
+
323
+ async function resolveChannel(
324
+ client: Slack,
325
+ name: string
326
+ ): Promise<string | undefined> {
327
+ const trimmed = name.replace(/^#/, "");
328
+ if (CHANNEL_ID.test(trimmed)) {
329
+ return trimmed;
330
+ }
331
+ let cursor: string | undefined;
332
+ for (let page = 0; page < 4; page++) {
333
+ const listed = await client.users.conversations({
334
+ types: "public_channel,private_channel",
335
+ exclude_archived: true,
336
+ limit: 200,
337
+ cursor,
338
+ });
339
+ const match = listed.channels?.find((channel) => channel.name === trimmed);
340
+ if (match?.id !== undefined) {
341
+ return match.id;
342
+ }
343
+ cursor = listed.response_metadata?.next_cursor;
344
+ if (cursor === undefined || cursor === "") {
345
+ return undefined;
346
+ }
347
+ }
348
+ return undefined;
349
+ }
350
+
351
+ async function readFile(
352
+ octokit: Octokit,
353
+ args: { owner: string; repo: string; path: string; ref: string }
354
+ ): Promise<string | undefined> {
355
+ try {
356
+ const { data } = await octokit.rest.repos.getContent(args);
357
+ if (Array.isArray(data) || data.encoding !== "base64") {
358
+ return undefined;
359
+ }
360
+ if (typeof data.content !== "string") {
361
+ return undefined;
362
+ }
363
+ return Buffer.from(data.content, "base64").toString("utf8");
364
+ } catch (error) {
365
+ if (isNotFound(error)) {
366
+ return undefined;
367
+ }
368
+ throw error;
369
+ }
370
+ }
371
+
372
+ function formatPulls(pulls: Pull[]): string {
373
+ if (pulls.length === 0) {
374
+ return "No merged pull requests this week.\n";
375
+ }
376
+ return `${pulls.map(formatPull).join("\n")}\n`;
377
+ }
378
+
379
+ function formatPull(pull: Pull): string {
380
+ const comments =
381
+ pull.comments.length === 0
382
+ ? ""
383
+ : `\n\n### comments\n${pull.comments.map((line) => `- ${line}`).join("\n")}`;
384
+ return `## #${pull.number} ${pull.title}
385
+ url: ${pull.url}
386
+ author: @${pull.author}
387
+ merged: ${pull.merged}
388
+ files: ${pull.files.join(", ")}
389
+
390
+ ${pull.body}${comments}`;
391
+ }
392
+
393
+ function formatSlack(msg: { text?: string; user?: string; ts?: string }): string {
394
+ const who = msg.user === undefined ? "unknown" : `<@${msg.user}>`;
395
+ return `${who}: ${clip(msg.text ?? "", MAX_LINE)}`;
396
+ }
397
+
398
+ function skipSlack(msg: { subtype?: string; text?: string }): boolean {
399
+ if (msg.subtype !== undefined && msg.subtype !== "thread_broadcast") {
400
+ return true;
401
+ }
402
+ return (msg.text ?? "").trim() === "";
403
+ }
404
+
405
+ function splitRepo(fullName: string): { owner: string; repo: string } {
406
+ const [owner, repo] = fullName.split("/");
407
+ if (owner === undefined || repo === undefined) {
408
+ throw new Error(`invalid repo: ${fullName}`);
409
+ }
410
+ return { owner, repo };
411
+ }
412
+
413
+ function clip(text: string, max: number): string {
414
+ if (text.length <= max) {
415
+ return text;
416
+ }
417
+ return `${text.slice(0, max)}\n…[truncated]`;
418
+ }
419
+
420
+ function isoDate(value: Date): string {
421
+ return value.toISOString().slice(0, 10);
422
+ }
423
+
424
+ function isRecord(value: unknown): value is Record<string, unknown> {
425
+ return typeof value === "object" && value !== null && !Array.isArray(value);
426
+ }
427
+
428
+ function isNotFound(error: unknown): boolean {
429
+ return (
430
+ typeof error === "object" &&
431
+ error !== null &&
432
+ "status" in error &&
433
+ error.status === 404
434
+ );
435
+ }
436
+
437
+ function errorMessage(error: unknown): string {
438
+ return error instanceof Error ? error.message : String(error);
439
+ }
@@ -0,0 +1,8 @@
1
+ export const REPOS: readonly string[] = ["owner/repo"];
2
+ export const SLACK_CHANNELS: readonly string[] = ["#eng"];
3
+
4
+ const SKIP = new Set(["", "none", "-"]);
5
+
6
+ export function slackChannels(): readonly string[] {
7
+ return SLACK_CHANNELS.filter((name) => !SKIP.has(name.toLowerCase()));
8
+ }
@@ -0,0 +1,51 @@
1
+ export const BRANCH_PREFIX = "agents-md";
2
+ export const TREE_PREFIX = "week/tree";
3
+
4
+ const PATH = /^[A-Za-z0-9._/-]+$/;
5
+ const REF = /^[A-Za-z0-9._/-]+$/;
6
+
7
+ export function isAgentsPath(path: string): boolean {
8
+ return path === "AGENTS.md" || path.endsWith("/AGENTS.md");
9
+ }
10
+
11
+ export function snapshotPath(path: string): string {
12
+ return `${TREE_PREFIX}/${path}.txt`;
13
+ }
14
+
15
+ export function agentsPath(raw: string): string | undefined {
16
+ let path = raw.replace(/^\/+/, "");
17
+ if (path.startsWith(`${TREE_PREFIX}/`)) {
18
+ path = path.slice(TREE_PREFIX.length + 1);
19
+ if (path.endsWith(".txt")) {
20
+ path = path.slice(0, -4);
21
+ }
22
+ }
23
+ if (path.includes("..") || path.includes("\\") || !PATH.test(path)) {
24
+ return undefined;
25
+ }
26
+ if (!isAgentsPath(path)) {
27
+ return undefined;
28
+ }
29
+ return path;
30
+ }
31
+
32
+ export function isUpdateRef(ref: string): boolean {
33
+ return ref === BRANCH_PREFIX || ref.startsWith(`${BRANCH_PREFIX}/`);
34
+ }
35
+
36
+ export function updateBranch(base: string): string | undefined {
37
+ if (base === "" || !REF.test(base)) {
38
+ return undefined;
39
+ }
40
+ return `${BRANCH_PREFIX}/${base.replaceAll("/", "-")}`;
41
+ }
42
+
43
+ export function parseRepo(
44
+ raw: string
45
+ ): { owner: string; repo: string } | undefined {
46
+ const match = /^([A-Za-z0-9._-]+)\/([A-Za-z0-9._-]+)$/.exec(raw.trim());
47
+ if (match === null || match[1] === undefined || match[2] === undefined) {
48
+ return undefined;
49
+ }
50
+ return { owner: match[1], repo: match[2] };
51
+ }
@@ -0,0 +1,17 @@
1
+ import { defineSchedule } from "@cursor/july/schedules";
2
+ import queue from "../channels/queue.js";
3
+ import { collectWeek } from "../lib/collect.js";
4
+
5
+ export default defineSchedule({
6
+ cron: "0 9 * * 1",
7
+ async run({ receive, waitUntil, appAuth, host }) {
8
+ const week = await collectWeek(host);
9
+ waitUntil(
10
+ receive(queue, {
11
+ message: week.prompt,
12
+ auth: appAuth,
13
+ target: week,
14
+ })
15
+ );
16
+ },
17
+ });