@method-ai-hq/sdk 0.0.0-stage → 0.14.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 (262) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -2
  3. package/dist/packages/contracts/src/identity.d.ts +16 -0
  4. package/dist/packages/contracts/src/identity.js +41 -0
  5. package/dist/packages/contracts/src/method-package.d.ts +71 -0
  6. package/dist/packages/contracts/src/method-package.js +22 -0
  7. package/dist/packages/method-document/src/conversion-report.d.ts +138 -0
  8. package/dist/packages/method-document/src/conversion-report.js +23 -0
  9. package/dist/packages/sdk/examples/daily-briefing/README.md +31 -0
  10. package/dist/packages/sdk/examples/daily-briefing/TASK.md +3 -0
  11. package/dist/packages/sdk/examples/daily-briefing/approved-report.md +138 -0
  12. package/dist/packages/sdk/examples/daily-briefing/briefing_artifacts.py +39 -0
  13. package/dist/packages/sdk/examples/daily-briefing/briefing_check_inputs.py +47 -0
  14. package/dist/packages/sdk/examples/daily-briefing/briefing_files.py +31 -0
  15. package/dist/packages/sdk/examples/daily-briefing/briefing_manifest.py +23 -0
  16. package/dist/packages/sdk/examples/daily-briefing/briefing_markdown.py +67 -0
  17. package/dist/packages/sdk/examples/daily-briefing/briefing_progress.py +18 -0
  18. package/dist/packages/sdk/examples/daily-briefing/briefing_render.py +71 -0
  19. package/dist/packages/sdk/examples/daily-briefing/briefing_sessions.py +37 -0
  20. package/dist/packages/sdk/examples/daily-briefing/briefing_times.py +158 -0
  21. package/dist/packages/sdk/examples/daily-briefing/briefing_validation.py +101 -0
  22. package/dist/packages/sdk/examples/daily-briefing/briefing_website.py +215 -0
  23. package/dist/packages/sdk/examples/daily-briefing/checks.json +19 -0
  24. package/dist/packages/sdk/examples/daily-briefing/daily-briefing.method +158 -0
  25. package/dist/packages/sdk/examples/daily-briefing/inputs.json +1 -0
  26. package/dist/packages/sdk/examples/daily-briefing/package-lock.json +10 -0
  27. package/dist/packages/sdk/examples/daily-briefing/package.json +4 -0
  28. package/dist/packages/sdk/examples/daily-briefing/pyproject.toml +8 -0
  29. package/dist/packages/sdk/examples/daily-briefing/reader/reader.css +35 -0
  30. package/dist/packages/sdk/examples/daily-briefing/reader/reader.js +42 -0
  31. package/dist/packages/sdk/examples/daily-briefing/sample/context.json +1 -0
  32. package/dist/packages/sdk/examples/daily-briefing/sample/documents/document-001.md +219 -0
  33. package/dist/packages/sdk/examples/daily-briefing/sample/media.json +1 -0
  34. package/dist/packages/sdk/examples/daily-briefing/sample/metadata/documents.json +39 -0
  35. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0001.json +49 -0
  36. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0002.json +49 -0
  37. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0003.json +49 -0
  38. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0004.json +49 -0
  39. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0005.json +49 -0
  40. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0006.json +49 -0
  41. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0007.json +49 -0
  42. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0008.json +49 -0
  43. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0009.json +49 -0
  44. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0010.json +49 -0
  45. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0011.json +49 -0
  46. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0012.json +49 -0
  47. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0013.json +49 -0
  48. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0014.json +49 -0
  49. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0015.json +49 -0
  50. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0016.json +49 -0
  51. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0017.json +49 -0
  52. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0018.json +49 -0
  53. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0019.json +49 -0
  54. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0020.json +49 -0
  55. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0021.json +49 -0
  56. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0022.json +49 -0
  57. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0023.json +49 -0
  58. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0024.json +49 -0
  59. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0025.json +49 -0
  60. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0026.json +49 -0
  61. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0027.json +49 -0
  62. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0028.json +49 -0
  63. package/dist/packages/sdk/examples/daily-briefing/sample/sessions.json +1 -0
  64. package/dist/packages/sdk/examples/daily-briefing/sample/timeline.jsonl +28 -0
  65. package/dist/packages/sdk/examples/daily-briefing/sample/untimed.jsonl +0 -0
  66. package/dist/packages/sdk/examples/daily-briefing/sample-output.zip +0 -0
  67. package/dist/packages/sdk/examples/daily-briefing/sample-report.md +51 -0
  68. package/dist/packages/sdk/examples/daily-briefing/uv.lock +128 -0
  69. package/dist/packages/sdk/examples/daily-briefing/vendor/marked-LICENSE.md +44 -0
  70. package/dist/packages/sdk/examples/daily-briefing/vendor/marked.mjs +77 -0
  71. package/dist/packages/sdk/examples/files.json +126 -0
  72. package/dist/packages/sdk/examples/message-routing/README.md +23 -0
  73. package/dist/packages/sdk/examples/message-routing/TASK.md +5 -0
  74. package/dist/packages/sdk/examples/message-routing/choose-destination.mjs +10 -0
  75. package/dist/packages/sdk/examples/message-routing/create-ticket.mjs +15 -0
  76. package/dist/packages/sdk/examples/message-routing/inputs.json +1 -0
  77. package/dist/packages/sdk/examples/message-routing/message-routing.method +45 -0
  78. package/dist/packages/sdk/examples/message-routing/result.fixture.json +6 -0
  79. package/dist/packages/sdk/examples/message-routing/routing.mjs +29 -0
  80. package/dist/packages/sdk/examples/message-routing/routing.test.mjs +27 -0
  81. package/dist/packages/sdk/examples/message-routing/ticket.method +88 -0
  82. package/dist/packages/sdk/examples/message-routing/ticket.test.mjs +64 -0
  83. package/dist/packages/sdk/examples/notes-summary/README.md +23 -0
  84. package/dist/packages/sdk/examples/notes-summary/TASK.md +3 -0
  85. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/case.json +36 -0
  86. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/examples.json +16 -0
  87. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/files/0 +10 -0
  88. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/recording.json +195 -0
  89. package/dist/packages/sdk/examples/notes-summary/notes/2026-10-05-standup.md +4 -0
  90. package/dist/packages/sdk/examples/notes-summary/notes/2026-10-07-planning.md +3 -0
  91. package/dist/packages/sdk/examples/notes-summary/notes-summary.method +54 -0
  92. package/dist/packages/sdk/examples/notes-summary/out/.keep +0 -0
  93. package/dist/packages/sdk/examples/notes-summary/read-notes.mjs +4 -0
  94. package/dist/packages/sdk/examples/notes-summary/save.mjs +7 -0
  95. package/dist/packages/sdk/examples/outbound-management/README.md +119 -0
  96. package/dist/packages/sdk/examples/outbound-management/TASK.md +5 -0
  97. package/dist/packages/sdk/examples/outbound-management/check_plan.py +16 -0
  98. package/dist/packages/sdk/examples/outbound-management/check_sources.py +14 -0
  99. package/dist/packages/sdk/examples/outbound-management/crm.py +153 -0
  100. package/dist/packages/sdk/examples/outbound-management/crm_test.py +141 -0
  101. package/dist/packages/sdk/examples/outbound-management/fixtures/README.md +15 -0
  102. package/dist/packages/sdk/examples/outbound-management/fixtures/crm.json +54 -0
  103. package/dist/packages/sdk/examples/outbound-management/fixtures/day-1/messages.json +29 -0
  104. package/dist/packages/sdk/examples/outbound-management/fixtures/day-2/messages.json +38 -0
  105. package/dist/packages/sdk/examples/outbound-management/fixtures/observations.json +12 -0
  106. package/dist/packages/sdk/examples/outbound-management/fixtures/plan.json +69 -0
  107. package/dist/packages/sdk/examples/outbound-management/fixtures/receipt.json +1 -0
  108. package/dist/packages/sdk/examples/outbound-management/fixtures/state.json +99 -0
  109. package/dist/packages/sdk/examples/outbound-management/fixtures/tasks.md +16 -0
  110. package/dist/packages/sdk/examples/outbound-management/inputs.json +4 -0
  111. package/dist/packages/sdk/examples/outbound-management/outbound.method +277 -0
  112. package/dist/packages/sdk/examples/outbound-management/read_crm.py +4 -0
  113. package/dist/packages/sdk/examples/outbound-management/save_day.py +4 -0
  114. package/dist/packages/sdk/examples/outbound-management/starter/crm.json +11 -0
  115. package/dist/packages/sdk/examples/social-briefing/README.md +41 -0
  116. package/dist/packages/sdk/examples/social-briefing/TASK.md +5 -0
  117. package/dist/packages/sdk/examples/social-briefing/inputs.json +5 -0
  118. package/dist/packages/sdk/examples/social-briefing/result.fixture.json +14 -0
  119. package/dist/packages/sdk/examples/social-briefing/social-briefing.method +110 -0
  120. package/dist/packages/sdk/examples/support-triage/README.md +77 -0
  121. package/dist/packages/sdk/examples/support-triage/TASK.md +3 -0
  122. package/dist/packages/sdk/examples/support-triage/cases/double-charge-to-billing/case.json +40 -0
  123. package/dist/packages/sdk/examples/support-triage/cases/double-charge-to-billing/recording.json +460 -0
  124. package/dist/packages/sdk/examples/support-triage/fake-helpdesk.mjs +38 -0
  125. package/dist/packages/sdk/examples/support-triage/helpdesk.mjs +33 -0
  126. package/dist/packages/sdk/examples/support-triage/inputs.json +1 -0
  127. package/dist/packages/sdk/examples/support-triage/read-ticket.mjs +8 -0
  128. package/dist/packages/sdk/examples/support-triage/route.mjs +4 -0
  129. package/dist/packages/sdk/examples/support-triage/sample-tickets.json +5 -0
  130. package/dist/packages/sdk/examples/support-triage/set-ticket.mjs +9 -0
  131. package/dist/packages/sdk/examples/support-triage/support-triage.method +165 -0
  132. package/dist/packages/sdk/examples/support-triage/triage.mjs +38 -0
  133. package/dist/packages/sdk/examples/support-triage/triage.test.mjs +68 -0
  134. package/dist/packages/sdk/observers/mail.delivery/fetch.mjs +87 -0
  135. package/dist/packages/sdk/observers/mail.delivery/fixtures/bounce-by-recipient.json +1 -0
  136. package/dist/packages/sdk/observers/mail.delivery/fixtures/bounce.json +1 -0
  137. package/dist/packages/sdk/observers/mail.delivery/fixtures/delayed.json +1 -0
  138. package/dist/packages/sdk/observers/mail.delivery/fixtures/delivered.json +1 -0
  139. package/dist/packages/sdk/observers/mail.delivery/fixtures/empty.json +1 -0
  140. package/dist/packages/sdk/observers/mail.delivery/fixtures/other-message.json +1 -0
  141. package/dist/packages/sdk/observers/mail.delivery/judge.mjs +13 -0
  142. package/dist/packages/sdk/observers/mail.delivery/observer.json +14 -0
  143. package/dist/packages/sdk/package.json +232 -0
  144. package/dist/packages/sdk/src/authoring-example.d.ts +91 -0
  145. package/dist/packages/sdk/src/authoring-example.js +50 -0
  146. package/dist/packages/sdk/src/authoring-instructions.d.ts +20 -0
  147. package/dist/packages/sdk/src/authoring-instructions.js +151 -0
  148. package/dist/packages/sdk/src/authoring-prompt.d.ts +2 -0
  149. package/dist/packages/sdk/src/authoring-prompt.js +8 -0
  150. package/dist/packages/sdk/src/authoring.d.ts +10 -0
  151. package/dist/packages/sdk/src/authoring.js +287 -0
  152. package/dist/packages/sdk/src/bindings.d.ts +4 -0
  153. package/dist/packages/sdk/src/bindings.js +124 -0
  154. package/dist/packages/sdk/src/bridge.d.ts +2 -0
  155. package/dist/packages/sdk/src/bridge.js +23 -0
  156. package/dist/packages/sdk/src/browser-runtime/pyproject.toml +5 -0
  157. package/dist/packages/sdk/src/browser-runtime/tools.json +189 -0
  158. package/dist/packages/sdk/src/browser-runtime/uv.lock +3894 -0
  159. package/dist/packages/sdk/src/browser-service.py +127 -0
  160. package/dist/packages/sdk/src/browser.d.ts +38 -0
  161. package/dist/packages/sdk/src/browser.js +203 -0
  162. package/dist/packages/sdk/src/capabilities.d.ts +13 -0
  163. package/dist/packages/sdk/src/capabilities.js +68 -0
  164. package/dist/packages/sdk/src/classification-client.d.ts +4 -0
  165. package/dist/packages/sdk/src/classification-client.js +27 -0
  166. package/dist/packages/sdk/src/cloud.d.ts +23 -0
  167. package/dist/packages/sdk/src/cloud.js +43 -0
  168. package/dist/packages/sdk/src/computer-settings.d.ts +28 -0
  169. package/dist/packages/sdk/src/computer-settings.js +57 -0
  170. package/dist/packages/sdk/src/connect.d.ts +47 -0
  171. package/dist/packages/sdk/src/connect.js +203 -0
  172. package/dist/packages/sdk/src/current-inspection.d.ts +3 -0
  173. package/dist/packages/sdk/src/current-inspection.js +146 -0
  174. package/dist/packages/sdk/src/current-runtime.d.ts +14 -0
  175. package/dist/packages/sdk/src/current-runtime.js +167 -0
  176. package/dist/packages/sdk/src/doctor.d.ts +17 -0
  177. package/dist/packages/sdk/src/doctor.js +68 -0
  178. package/dist/packages/sdk/src/executable.d.ts +2 -0
  179. package/dist/packages/sdk/src/executable.js +22 -0
  180. package/dist/packages/sdk/src/explain-effects.d.ts +32 -0
  181. package/dist/packages/sdk/src/explain-effects.js +192 -0
  182. package/dist/packages/sdk/src/explain.d.ts +115 -0
  183. package/dist/packages/sdk/src/explain.js +441 -0
  184. package/dist/packages/sdk/src/files.d.ts +9 -0
  185. package/dist/packages/sdk/src/files.js +50 -0
  186. package/dist/packages/sdk/src/guard-keys.d.ts +5 -0
  187. package/dist/packages/sdk/src/guard-keys.js +42 -0
  188. package/dist/packages/sdk/src/hosted-models.d.ts +49 -0
  189. package/dist/packages/sdk/src/hosted-models.js +81 -0
  190. package/dist/packages/sdk/src/improve.d.ts +55 -0
  191. package/dist/packages/sdk/src/improve.js +159 -0
  192. package/dist/packages/sdk/src/index.d.ts +13 -0
  193. package/dist/packages/sdk/src/index.js +13 -0
  194. package/dist/packages/sdk/src/inspect.d.ts +6 -0
  195. package/dist/packages/sdk/src/inspect.js +17 -0
  196. package/dist/packages/sdk/src/link.d.ts +12 -0
  197. package/dist/packages/sdk/src/link.js +53 -0
  198. package/dist/packages/sdk/src/local-cli.d.ts +24 -0
  199. package/dist/packages/sdk/src/local-cli.js +146 -0
  200. package/dist/packages/sdk/src/local-setup.d.ts +15 -0
  201. package/dist/packages/sdk/src/local-setup.js +42 -0
  202. package/dist/packages/sdk/src/method-client.d.ts +30 -0
  203. package/dist/packages/sdk/src/method-client.js +250 -0
  204. package/dist/packages/sdk/src/method-files.d.ts +29 -0
  205. package/dist/packages/sdk/src/method-files.js +93 -0
  206. package/dist/packages/sdk/src/method-help.d.ts +19 -0
  207. package/dist/packages/sdk/src/method-help.js +404 -0
  208. package/dist/packages/sdk/src/method-issues.d.ts +86 -0
  209. package/dist/packages/sdk/src/method-issues.js +192 -0
  210. package/dist/packages/sdk/src/method-sync.d.ts +46 -0
  211. package/dist/packages/sdk/src/method-sync.js +293 -0
  212. package/dist/packages/sdk/src/method.d.ts +5 -0
  213. package/dist/packages/sdk/src/method.js +359 -0
  214. package/dist/packages/sdk/src/model-checks.d.ts +96 -0
  215. package/dist/packages/sdk/src/model-checks.js +181 -0
  216. package/dist/packages/sdk/src/outbox.d.ts +120 -0
  217. package/dist/packages/sdk/src/outbox.js +297 -0
  218. package/dist/packages/sdk/src/own-model-key.d.ts +22 -0
  219. package/dist/packages/sdk/src/own-model-key.js +76 -0
  220. package/dist/packages/sdk/src/prepare.d.ts +24 -0
  221. package/dist/packages/sdk/src/prepare.js +245 -0
  222. package/dist/packages/sdk/src/process.d.ts +15 -0
  223. package/dist/packages/sdk/src/process.js +107 -0
  224. package/dist/packages/sdk/src/production.d.ts +182 -0
  225. package/dist/packages/sdk/src/production.js +284 -0
  226. package/dist/packages/sdk/src/progress.d.ts +12 -0
  227. package/dist/packages/sdk/src/progress.js +48 -0
  228. package/dist/packages/sdk/src/proposals.d.ts +73 -0
  229. package/dist/packages/sdk/src/proposals.js +284 -0
  230. package/dist/packages/sdk/src/quality.d.ts +82 -0
  231. package/dist/packages/sdk/src/quality.js +294 -0
  232. package/dist/packages/sdk/src/result-files.d.ts +5 -0
  233. package/dist/packages/sdk/src/result-files.js +149 -0
  234. package/dist/packages/sdk/src/result-transfer.d.ts +21 -0
  235. package/dist/packages/sdk/src/result-transfer.js +37 -0
  236. package/dist/packages/sdk/src/run-data.d.ts +11 -0
  237. package/dist/packages/sdk/src/run-data.js +80 -0
  238. package/dist/packages/sdk/src/run-method.d.ts +2 -0
  239. package/dist/packages/sdk/src/run-method.js +2 -0
  240. package/dist/packages/sdk/src/run-saved.d.ts +5 -0
  241. package/dist/packages/sdk/src/run-saved.js +138 -0
  242. package/dist/packages/sdk/src/run-worker.d.ts +6 -0
  243. package/dist/packages/sdk/src/run-worker.js +131 -0
  244. package/dist/packages/sdk/src/secrets.d.ts +37 -0
  245. package/dist/packages/sdk/src/secrets.js +196 -0
  246. package/dist/packages/sdk/src/update.d.ts +5 -0
  247. package/dist/packages/sdk/src/update.js +39 -0
  248. package/dist/packages/sdk/src/versions.d.ts +58 -0
  249. package/dist/packages/sdk/src/versions.js +144 -0
  250. package/dist/packages/sdk/src/worker.d.ts +120 -0
  251. package/dist/packages/sdk/src/worker.js +443 -0
  252. package/dist/packages/workflow-language/src/execution.d.ts +1 -0
  253. package/dist/packages/workflow-language/src/execution.js +1 -0
  254. package/dist/packages/workflow-language/src/inspection.d.ts +990 -0
  255. package/dist/packages/workflow-language/src/inspection.js +130 -0
  256. package/dist/packages/workflow-language/src/result-files.d.ts +160 -0
  257. package/dist/packages/workflow-language/src/result-files.js +67 -0
  258. package/dist/packages/workflow-language/src/schema.d.ts +207 -0
  259. package/dist/packages/workflow-language/src/schema.js +56 -0
  260. package/dist/packages/workflow-language/src/validate.d.ts +11 -0
  261. package/dist/packages/workflow-language/src/validate.js +77 -0
  262. package/package.json +230 -4
@@ -0,0 +1,45 @@
1
+ format: method/3.4
2
+ name: Classify and route a customer message
3
+ goal: Choose a support destination and show the rule used.
4
+ inputs:
5
+ message:
6
+ type: text
7
+ description: The complete customer message to classify.
8
+ steps:
9
+ classify_message:
10
+ name: Classify the customer message
11
+ in:
12
+ message: inputs.message
13
+ do:
14
+ kind: classify
15
+ question: Which team should handle the main request in this message?
16
+ options:
17
+ billing: Billing — invoices, charges, refunds, and payments.
18
+ technical: Technical support — errors and problems using the product.
19
+ unclear: Unclear — the message does not say enough to choose, or no listed team fits.
20
+ out: message_category
21
+ choose_destination:
22
+ name: Choose the destination
23
+ purpose: |
24
+ Chooses Billing or Technical support when that category is selected
25
+ and its probability is at least 90%. Otherwise chooses Manual review.
26
+ Returns the destination, the rule used, and the probability and threshold.
27
+ in:
28
+ category: message_category
29
+ do:
30
+ kind: run
31
+ runtime: node
32
+ entrypoint: choose-destination.mjs
33
+ out:
34
+ routing:
35
+ type: record
36
+ description: The selected destination and the values used to choose it.
37
+ fields:
38
+ destination: text
39
+ rule_applied: text
40
+ selected_probability: number
41
+ required_probability: number
42
+ result: routing
43
+ files:
44
+ - routing.mjs
45
+ run_prompt: Run this Method with the customer message, then show the destination and the rule used.
@@ -0,0 +1,6 @@
1
+ {
2
+ "destination": "billing",
3
+ "rule_applied": "selected_category_meets_threshold",
4
+ "selected_probability": 0.94,
5
+ "required_probability": 0.9
6
+ }
@@ -0,0 +1,29 @@
1
+ export function chooseDestination(category) {
2
+ const ids = ['billing', 'technical', 'unclear'];
3
+ if (!category || !ids.includes(category.choice)) {
4
+ throw new Error('Expected a declared category.');
5
+ }
6
+ const probabilities = category.probabilities;
7
+ if (!probabilities || Object.keys(probabilities).length !== ids.length ||
8
+ ids.some(id => !Object.hasOwn(probabilities, id) ||
9
+ !Number.isFinite(probabilities[id]) ||
10
+ probabilities[id] < 0 || probabilities[id] > 1)) {
11
+ throw new Error('Expected one valid probability per category.');
12
+ }
13
+ if (Math.abs(ids.reduce((sum, id) => sum + probabilities[id], 0) - 1) > 1e-6 ||
14
+ probabilities[category.choice] + 1e-6 < Math.max(...ids.map(id => probabilities[id]))) {
15
+ throw new Error('Expected a valid distribution and a maximum-probability choice.');
16
+ }
17
+ const required = 0.90;
18
+ const observed = probabilities[category.choice];
19
+ const automatic = category.choice !== 'unclear' && observed >= required;
20
+ return {
21
+ routing: {
22
+ destination: automatic ? category.choice : 'manual_review',
23
+ rule_applied: category.choice === 'unclear' ? 'unclear_requires_review' :
24
+ automatic ? 'selected_category_meets_threshold' : 'below_threshold',
25
+ selected_probability: observed,
26
+ required_probability: required
27
+ }
28
+ };
29
+ }
@@ -0,0 +1,27 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { chooseDestination } from './routing.mjs';
4
+ const classify = (choice, billing, technical, unclear) =>
5
+ ({choice, probabilities: {billing, technical, unclear}});
6
+ test('accepts the exact threshold', () => {
7
+ const r = chooseDestination(classify('billing', .90, .08, .02)).routing;
8
+ assert.equal(r.destination, 'billing');
9
+ assert.equal(r.rule_applied, 'selected_category_meets_threshold');
10
+ });
11
+ test('routes below the threshold to manual review', () => {
12
+ const r = chooseDestination(classify('billing', .899, .081, .02)).routing;
13
+ assert.equal(r.destination, 'manual_review');
14
+ assert.equal(r.rule_applied, 'below_threshold');
15
+ });
16
+ test('unclear always requires review', () => {
17
+ const r = chooseDestination(classify('unclear', 0, 0, 1)).routing;
18
+ assert.equal(r.destination, 'manual_review');
19
+ assert.equal(r.rule_applied, 'unclear_requires_review');
20
+ });
21
+ test('routes technical support independently', () => {
22
+ assert.equal(chooseDestination(classify('technical', .03, .95, .02)).routing.destination, 'technical');
23
+ });
24
+ test('rejects missing or invalid input', () => {
25
+ assert.throws(() => chooseDestination(null));
26
+ assert.throws(() => chooseDestination(classify('billing', .9, .9, 0)));
27
+ });
@@ -0,0 +1,88 @@
1
+ format: method/3.4
2
+ name: Route a message and create one ticket
3
+ goal: Create a ticket and recover an interrupted response without a duplicate write.
4
+ inputs:
5
+ message:
6
+ type: text
7
+ description: The complete customer message to classify.
8
+ steps:
9
+ classify_message:
10
+ name: Classify the customer message
11
+ in:
12
+ message: inputs.message
13
+ do:
14
+ kind: classify
15
+ question: Which team should handle the main request in this message?
16
+ options:
17
+ billing: Billing — invoices, charges, refunds, and payments.
18
+ technical: Technical support — errors and problems using the product.
19
+ unclear: Unclear — the message does not say enough to choose, or no listed team fits.
20
+ out: message_category
21
+ choose_destination:
22
+ name: Choose the destination
23
+ purpose: |
24
+ Chooses Billing or Technical support when that category is selected
25
+ and its probability is at least 90%. Otherwise chooses Manual review.
26
+ Returns the destination, the rule used, and the probability and threshold.
27
+ in:
28
+ category: message_category
29
+ do:
30
+ kind: run
31
+ runtime: node
32
+ entrypoint: choose-destination.mjs
33
+ out:
34
+ routing:
35
+ type: record
36
+ description: The selected destination and the values used to choose it.
37
+ fields:
38
+ destination: text
39
+ rule_applied: text
40
+ selected_probability: number
41
+ required_probability: number
42
+ create_ticket:
43
+ name: Create the support ticket
44
+ purpose: Creates a ticket for the selected destination and returns its receipt.
45
+ Sends the operation ID as the Idempotency-Key, so a retry returns the same
46
+ ticket. The saved effect reads the ticket back by the operation ID.
47
+ in:
48
+ message: inputs.message
49
+ routing: routing
50
+ service: environment.ticket_service
51
+ do:
52
+ kind: run
53
+ runtime: node
54
+ entrypoint: create-ticket.mjs
55
+ changes:
56
+ - environment.ticket_service
57
+ out:
58
+ ticket:
59
+ type: record
60
+ description: The service receipt for this operation.
61
+ fields:
62
+ ticket_id: text
63
+ operation_id: text
64
+ effects:
65
+ saved:
66
+ intent: The ticket service stores one ticket for this operation with the
67
+ submitted message and destination.
68
+ in:
69
+ message: inputs.message
70
+ destination: routing.destination
71
+ observe:
72
+ kind: http
73
+ path: /tickets/by-operation/{token}
74
+ expect:
75
+ fields:
76
+ operation_id: "{token}"
77
+ message: "{inputs.message}"
78
+ destination: "{inputs.destination}"
79
+ blocking: true
80
+ result: ticket
81
+ files:
82
+ - routing.mjs
83
+ run_prompt: Run this Method with the customer message, then show the destination
84
+ and the rule used.
85
+ environment:
86
+ ticket_service:
87
+ type: service
88
+ description: Ticket service with idempotent writes and operation lookup.
@@ -0,0 +1,64 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {createServer} from 'node:http';
4
+ import {mkdtemp,cp,readFile,writeFile,rm} from 'node:fs/promises';
5
+ import {tmpdir} from 'node:os';
6
+ import {join} from 'node:path';
7
+ import {fileURLToPath} from 'node:url';
8
+ import {parse,stringify} from 'yaml';
9
+ import {runMethod} from '@withmethod/runtime';
10
+
11
+ test('retry reuses the operation ID after the service commits and before stdout returns', async t => {
12
+ const root=await mkdtemp(join(tmpdir(),'method-ticket-retry-'));
13
+ t.after(()=>rm(root,{recursive:true,force:true}));
14
+ const records=new Map(), writes=[];
15
+ let committed;
16
+ const saved=new Promise(resolve=>committed=resolve);
17
+ let first=true;
18
+ const server=createServer(async(req,res)=>{
19
+ let body='';for await(const chunk of req)body+=chunk;
20
+ res.setHeader('content-type','application/json');
21
+ if(req.url==='/committed') {
22
+ if(first){first=false;committed();return;}
23
+ res.end('{}');return;
24
+ }
25
+ if(req.method==='POST'&&req.url==='/tickets') {
26
+ const key=req.headers['idempotency-key'],payload=JSON.parse(body),prior=records.get(key);
27
+ writes.push(key);
28
+ if(prior&&(prior.message!==payload.message||prior.destination!==payload.destination)){res.statusCode=409;res.end('{}');return;}
29
+ const record=prior??{ticket_id:'ticket-'+(records.size+1),operation_id:key,...payload};
30
+ records.set(key,record);res.end(JSON.stringify({ticket_id:record.ticket_id,operation_id:key}));return;
31
+ }
32
+ const record=records.get(decodeURIComponent(req.url.split('/').at(-1)));
33
+ res.statusCode=record?200:404;res.end(JSON.stringify(record??{}));
34
+ });
35
+ await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));
36
+ t.after(()=>{server.closeAllConnections();server.close();});
37
+ const url=`http://127.0.0.1:${server.address().port}`;
38
+ await cp(fileURLToPath(new URL('./',import.meta.url)),root,{recursive:true});
39
+ const method=parse(await readFile(join(root,'ticket.method'),'utf8'));
40
+ // The handshake exists only in this test wrapper. The real action has no test hooks.
41
+ method.steps.create_ticket.do.entrypoint='fixture-action.mjs';
42
+ method.files.push('create-ticket.mjs');
43
+ await writeFile(join(root,'fixture-action.mjs'),`const write=process.stdout.write.bind(process.stdout);process.stdout.write=data=>{void fetch(${JSON.stringify(url+'/committed')},{method:'POST'}).then(()=>write(data));return true;};await import('./create-ticket.mjs');`);
44
+ const file=join(root,'task.method');await writeFile(file,stringify(method));
45
+ const config={allow_local_processes:true,runtimes:{node:{command:process.execPath,version:process.version}},environment:{ticket_service:url},classification:{provider:'typesafe',model:'jev-fixture'}};
46
+ let classifications=0;
47
+ const classification={resolve:async()=>config.classification,evaluate:async()=>{classifications++;return {...config.classification,choice:'billing',probabilities:{billing:.94,technical:.04,unclear:.02},confidence:.8,usage:null};}};
48
+ const runDir=join(root,'run'),controller=new AbortController();
49
+ const running=runMethod(file,config,{runDir,classification,inputs:{message:'Please send my invoice.'},signal:controller.signal});
50
+ await saved;
51
+ controller.abort(Object.assign(Error('Test interruption after service commit'),{code:'interrupted'}));
52
+ assert.notEqual((await running).status,'completed');
53
+ assert.equal(records.size,1);
54
+ const resumed=await runMethod(file,config,{runDir,classification,resume:true,retry:['create_ticket:0']});
55
+ assert.equal(resumed.status,'completed');assert.equal(records.size,1);assert.equal(classifications,1);
56
+ assert.equal(writes.length,2);assert.equal(writes[0],writes[1]);
57
+ assert.equal(resumed.result.operation_id,writes[0]);
58
+ // The effect read the ticket back through the lookup connection; the receipt did not confirm it.
59
+ assert.equal(resumed.effects.confirmed,1);
60
+ const ledger=(await readFile(join(runDir,'effects.jsonl'),'utf8')).trim().split('\n').map(line=>JSON.parse(line));
61
+ assert.equal(ledger.at(-1).verdict,'confirmed');assert.equal(ledger.at(-1).token,writes[0]);
62
+ const conflict=await fetch(url+'/tickets',{method:'POST',headers:{'Idempotency-Key':writes[0]},body:JSON.stringify({message:'Different',destination:'billing'})});
63
+ assert.equal(conflict.status,409);
64
+ });
@@ -0,0 +1,23 @@
1
+ # Weekly summary from meeting notes
2
+
3
+ The smallest complete Method: a script reads a folder, one model call finds the items, and a script saves a file.
4
+
5
+ - The folders `notes/` and `out/` beside the Method are its two files connections, so they need no binding. `out/` is empty until the first run.
6
+ - The save step declares `changes: [environment.out]`. Nothing else is needed: the runtime reads the folder before and after the step, and the run fails if the step returns `out/weekly.md` but that file did not change.
7
+ - The prompt shows two items written the way they should be. They come from other notes, so the model copies their form, not their content.
8
+ - Run it: `method run notes-summary.method`.
9
+
10
+ ## The case in cases/
11
+
12
+ The first version of the prompt asked for due dates as YYYY-MM-DD but said nothing about other dates. Its run wrote the decision "Move the client demo to 14 Oct." The correction was: write dates in decisions as YYYY-MM-DD too. The prompt now says so, and it shows an example decision with a date. The run after the change wrote "Move the client demo to 2026-10-14."
13
+
14
+ That correction is kept as a case. It was made from the two runs:
15
+
16
+ ```sh
17
+ method case new notes-summary.method --id dates-in-decisions \
18
+ --run BAD_RUN --passing-run FIXED_RUN \
19
+ --note "The demo date in the decision says 14 Oct. Write dates in decisions as YYYY-MM-DD too, like the due dates." \
20
+ --rubric "Every date in a decision is written as YYYY-MM-DD."
21
+ ```
22
+
23
+ `method test notes-summary.method` runs it again: the summarize step changed since the bad run, so it runs live, and a model judges the saved summary against the rubric. `method publish` runs the cases first. Add a case in the same way for each correction that a later version must keep.
@@ -0,0 +1,3 @@
1
+ # Request
2
+
3
+ Every Friday, read this week's meeting notes and save a summary with the decisions and the action items. Each item names its owner, its due date when the notes give one, and the note it came from.
@@ -0,0 +1,36 @@
1
+ {
2
+ "format": "method-case/1",
3
+ "id": "dates-in-decisions",
4
+ "status": "active",
5
+ "method_file": "notes-summary.method",
6
+ "note": "The demo date in the decision says 14 Oct. Write dates in decisions as YYYY-MM-DD too, like the due dates.",
7
+ "author": null,
8
+ "created": "2026-10-10T02:18:15.490Z",
9
+ "source": {
10
+ "run_dir": "/work/notes-summary/run2",
11
+ "execution_id": "e8348baa-6be6-421e-b50d-0f86e4df765f",
12
+ "method_sha256": "a444edf4f50b1263e1a0b1037a26ba2db91a95f3508e2e6f11747f44d468fa7d",
13
+ "passing_run_dir": "/work/notes-summary/run3"
14
+ },
15
+ "expect": [
16
+ {
17
+ "kind": "rubric",
18
+ "ref": "outputs.path",
19
+ "criteria": [
20
+ {
21
+ "id": "c1",
22
+ "text": "Every date in a decision is written as YYYY-MM-DD."
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "runs": null,
28
+ "min_pass": null,
29
+ "supersedes": [],
30
+ "superseded_by": null,
31
+ "files": {
32
+ "/work/notes-summary/out/weekly.md": "files/0"
33
+ },
34
+ "retention_until": "2027-10-10",
35
+ "redacted": true
36
+ }
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "run": "failing",
4
+ "labels": {
5
+ "c1": false
6
+ },
7
+ "value": "/work/notes-summary/out/weekly.md"
8
+ },
9
+ {
10
+ "run": "passing",
11
+ "labels": {
12
+ "c1": true
13
+ },
14
+ "value": "/work/notes-summary/out/weekly.md"
15
+ }
16
+ ]
@@ -0,0 +1,10 @@
1
+ # Weekly summary
2
+
3
+ ## Decisions
4
+ - Move the client demo to 14 Oct. (2026-10-05-standup.md)
5
+ - Drop the PDF export from this release. (2026-10-07-planning.md)
6
+
7
+ ## Action items
8
+ - Finish the invoice export. — Dana, due 2026-10-08 (2026-10-05-standup.md)
9
+ - Ask legal about the new NDA template. — Sam (2026-10-05-standup.md)
10
+ - Rewrite the onboarding email. — Priya, due 2026-10-10 (2026-10-07-planning.md)
@@ -0,0 +1,195 @@
1
+ {
2
+ "format": "method-recording/1",
3
+ "method_sha256": "a444edf4f50b1263e1a0b1037a26ba2db91a95f3508e2e6f11747f44d468fa7d",
4
+ "execution_id": "e8348baa-6be6-421e-b50d-0f86e4df765f",
5
+ "inputs": {},
6
+ "initial_state": {},
7
+ "environment": {
8
+ "notes": "/work/notes-summary/notes",
9
+ "out": "/work/notes-summary/out"
10
+ },
11
+ "keys": {
12
+ "read": "1fd58db074013c4e48e962c51903c64713d8ec03deee94801a84b5efe34297d9",
13
+ "summarize": "0639fb3e5ebc18181b7b7dc63d11cea0cc23238540c3b7953ec4b53ff16e51d7",
14
+ "save": "92f29d344b66c6e90a7af14ead1f3942b3a2bb93e2d7553b863c27e8e419307c"
15
+ },
16
+ "iterations": {
17
+ "summarize": [
18
+ {
19
+ "inputs": {
20
+ "material": "## 2026-10-05-standup.md\n# Standup 5 Oct\n- Dana: the invoice export is late; she will finish it by 8 Oct.\n- Decision: move the client demo to 14 Oct.\n- Sam will ask legal about the new NDA template.\n\n\n## 2026-10-07-planning.md\n# Planning 7 Oct\n- Decision: drop the PDF export from this release.\n- Priya owns the onboarding email rewrite, due 10 Oct.\n"
21
+ },
22
+ "candidate": {
23
+ "summary": {
24
+ "decisions": [
25
+ {
26
+ "text": "Move the client demo to 14 Oct.",
27
+ "source": "2026-10-05-standup.md"
28
+ },
29
+ {
30
+ "text": "Drop the PDF export from this release.",
31
+ "source": "2026-10-07-planning.md"
32
+ }
33
+ ],
34
+ "actions": [
35
+ {
36
+ "action": "Finish the invoice export.",
37
+ "owner": "Dana",
38
+ "due": "2026-10-08",
39
+ "source": "2026-10-05-standup.md"
40
+ },
41
+ {
42
+ "action": "Ask legal about the new NDA template.",
43
+ "owner": "Sam",
44
+ "due": "",
45
+ "source": "2026-10-05-standup.md"
46
+ },
47
+ {
48
+ "action": "Rewrite the onboarding email.",
49
+ "owner": "Priya",
50
+ "due": "2026-10-10",
51
+ "source": "2026-10-07-planning.md"
52
+ }
53
+ ]
54
+ }
55
+ }
56
+ }
57
+ ],
58
+ "save": [
59
+ {
60
+ "inputs": {
61
+ "summary": {
62
+ "decisions": [
63
+ {
64
+ "text": "Move the client demo to 14 Oct.",
65
+ "source": "2026-10-05-standup.md"
66
+ },
67
+ {
68
+ "text": "Drop the PDF export from this release.",
69
+ "source": "2026-10-07-planning.md"
70
+ }
71
+ ],
72
+ "actions": [
73
+ {
74
+ "action": "Finish the invoice export.",
75
+ "owner": "Dana",
76
+ "due": "2026-10-08",
77
+ "source": "2026-10-05-standup.md"
78
+ },
79
+ {
80
+ "action": "Ask legal about the new NDA template.",
81
+ "owner": "Sam",
82
+ "due": "",
83
+ "source": "2026-10-05-standup.md"
84
+ },
85
+ {
86
+ "action": "Rewrite the onboarding email.",
87
+ "owner": "Priya",
88
+ "due": "2026-10-10",
89
+ "source": "2026-10-07-planning.md"
90
+ }
91
+ ]
92
+ },
93
+ "out": "/work/notes-summary/out"
94
+ },
95
+ "candidate": {
96
+ "path": "/work/notes-summary/out/weekly.md"
97
+ }
98
+ }
99
+ ]
100
+ },
101
+ "observations": {},
102
+ "method": {
103
+ "format": "method/3.4",
104
+ "id": "wf_e1a98901bb45007eba9d8653bf08c98f",
105
+ "name": "Weekly summary from meeting notes",
106
+ "goal": "Read this week's meeting notes and save a summary with the decisions and the action items, each with its owner, due date and source note.",
107
+ "environment": {
108
+ "notes": {
109
+ "type": "files",
110
+ "description": "Folder of Markdown meeting notes."
111
+ },
112
+ "out": {
113
+ "type": "files",
114
+ "description": "Folder where the summary is saved."
115
+ }
116
+ },
117
+ "steps": {
118
+ "read": {
119
+ "name": "Read the notes",
120
+ "purpose": "Reads every Markdown file in the notes folder, oldest name first, and returns their text headed by the file name.",
121
+ "in": {
122
+ "notes": "environment.notes"
123
+ },
124
+ "do": {
125
+ "kind": "run",
126
+ "runtime": "node",
127
+ "entrypoint": "read-notes.mjs"
128
+ },
129
+ "out": {
130
+ "material": {
131
+ "type": "text",
132
+ "description": "All notes, each headed by its file name."
133
+ }
134
+ }
135
+ },
136
+ "summarize": {
137
+ "name": "Find decisions and action items",
138
+ "in": {
139
+ "material": "material"
140
+ },
141
+ "do": {
142
+ "kind": "call",
143
+ "model": "default",
144
+ "prompt": "From these meeting notes, list every decision and every action item.\nFor each item, give the note file it comes from. For an action item,\ngive the owner, and the due date as YYYY-MM-DD when the notes give one;\notherwise use an empty due. Do not add anything that the notes do not say.\n\n{{material}}\n"
145
+ },
146
+ "out": {
147
+ "summary": {
148
+ "type": "record",
149
+ "fields": {
150
+ "decisions": {
151
+ "type": "list",
152
+ "fields": {
153
+ "text": "text",
154
+ "source": "text"
155
+ }
156
+ },
157
+ "actions": {
158
+ "type": "list",
159
+ "fields": {
160
+ "action": "text",
161
+ "owner": "text",
162
+ "due": "text",
163
+ "source": "text"
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+ },
170
+ "save": {
171
+ "name": "Save the summary",
172
+ "purpose": "Writes the decisions and action items to out/weekly.md as Markdown and returns the file path.",
173
+ "in": {
174
+ "summary": "summary",
175
+ "out": "environment.out"
176
+ },
177
+ "do": {
178
+ "kind": "run",
179
+ "runtime": "node",
180
+ "entrypoint": "save.mjs"
181
+ },
182
+ "changes": [
183
+ "environment.out"
184
+ ],
185
+ "out": {
186
+ "path": {
187
+ "type": "text",
188
+ "description": "Path of the saved summary."
189
+ }
190
+ }
191
+ }
192
+ },
193
+ "result": "path"
194
+ }
195
+ }
@@ -0,0 +1,4 @@
1
+ # Standup 5 Oct
2
+ - Dana: the invoice export is late; she will finish it by 8 Oct.
3
+ - Decision: move the client demo to 14 Oct.
4
+ - Sam will ask legal about the new NDA template.
@@ -0,0 +1,3 @@
1
+ # Planning 7 Oct
2
+ - Decision: drop the PDF export from this release.
3
+ - Priya owns the onboarding email rewrite, due 10 Oct.
@@ -0,0 +1,54 @@
1
+ format: method/3.4
2
+ name: Weekly summary from meeting notes
3
+ goal: Read this week's meeting notes and save a summary with the decisions and the action items, each with its owner, due date and source note.
4
+ environment:
5
+ notes:
6
+ type: files
7
+ description: Folder of Markdown meeting notes.
8
+ out:
9
+ type: files
10
+ description: Folder where the summary is saved.
11
+ steps:
12
+ read:
13
+ name: Read the notes
14
+ purpose: Reads every Markdown file in the notes folder, oldest name first, and returns their text headed by the file name.
15
+ in: {notes: environment.notes}
16
+ do: {kind: run, runtime: node, entrypoint: read-notes.mjs}
17
+ out:
18
+ material: {type: text, description: "All notes, each headed by its file name."}
19
+ summarize:
20
+ name: Find decisions and action items
21
+ in: {material: material}
22
+ do:
23
+ kind: call
24
+ model: default
25
+ prompt: |
26
+ From these meeting notes, list every decision and every action item.
27
+ For each item, give the note file it comes from. For an action item,
28
+ give the owner and the due date. Write every date as YYYY-MM-DD, also
29
+ inside a decision; take the year from the note's file name. When the
30
+ notes give no due date, leave due empty. Do not add anything that the
31
+ notes do not say.
32
+
33
+ Two items from other notes, written the way they should be:
34
+ - "Decision: ship the beta on 3 Nov." in 2026-10-28-review.md is the decision
35
+ text "Ship the beta on 2026-11-03.", source "2026-10-28-review.md".
36
+ - "Lee will book the venue." in 2026-10-28-review.md is the action
37
+ "Book the venue.", owner "Lee", due "", source "2026-10-28-review.md".
38
+
39
+ {{material}}
40
+ out:
41
+ summary:
42
+ type: record
43
+ fields:
44
+ decisions: {type: list, fields: {text: text, source: text}}
45
+ actions: {type: list, fields: {action: text, owner: text, due: text, source: text}}
46
+ save:
47
+ name: Save the summary
48
+ purpose: Writes the decisions and action items to out/weekly.md as Markdown and returns the file path.
49
+ in: {summary: summary, out: environment.out}
50
+ do: {kind: run, runtime: node, entrypoint: save.mjs}
51
+ changes: [environment.out]
52
+ out:
53
+ path: {type: text, description: Path of the saved summary.}
54
+ result: path
@@ -0,0 +1,4 @@
1
+ import {readFileSync, readdirSync} from 'node:fs';
2
+ const {notes} = JSON.parse(readFileSync(0, 'utf8'));
3
+ const files = readdirSync(notes).filter(name => name.endsWith('.md')).sort();
4
+ console.log(JSON.stringify({material: files.map(name => `## ${name}\n${readFileSync(`${notes}/${name}`, 'utf8')}`).join('\n\n')}));
@@ -0,0 +1,7 @@
1
+ import {readFileSync, writeFileSync} from 'node:fs';
2
+ const {summary, out} = JSON.parse(readFileSync(0, 'utf8'));
3
+ const lines = ['# Weekly summary', '', '## Decisions', ...summary.decisions.map(d => `- ${d.text} (${d.source})`), '', '## Action items',
4
+ ...summary.actions.map(a => `- ${a.action} — ${a.owner}${a.due ? `, due ${a.due}` : ''} (${a.source})`), ''];
5
+ const path = `${out}/weekly.md`;
6
+ writeFileSync(path, lines.join('\n'));
7
+ console.log(JSON.stringify({path}));