primitive-admin 1.1.0-alpha.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (591) hide show
  1. package/README.md +523 -109
  2. package/assets/skill/skills/primitive-platform/SKILL.md +289 -0
  3. package/dist/bin/primitive.d.ts +2 -0
  4. package/dist/bin/primitive.js +372 -21
  5. package/dist/bin/primitive.js.map +1 -1
  6. package/dist/src/commands/admins.d.ts +2 -0
  7. package/dist/src/commands/admins.js +136 -30
  8. package/dist/src/commands/admins.js.map +1 -1
  9. package/dist/src/commands/analytics.d.ts +2 -0
  10. package/dist/src/commands/analytics.js +713 -55
  11. package/dist/src/commands/analytics.js.map +1 -1
  12. package/dist/src/commands/apps.d.ts +2 -0
  13. package/dist/src/commands/apps.js +65 -100
  14. package/dist/src/commands/apps.js.map +1 -1
  15. package/dist/src/commands/auth-sessions.d.ts +7 -0
  16. package/dist/src/commands/auth-sessions.js +144 -0
  17. package/dist/src/commands/auth-sessions.js.map +1 -0
  18. package/dist/src/commands/auth.d.ts +2 -0
  19. package/dist/src/commands/auth.js +238 -108
  20. package/dist/src/commands/auth.js.map +1 -1
  21. package/dist/src/commands/blob-buckets.d.ts +2 -0
  22. package/dist/src/commands/blob-buckets.js +331 -0
  23. package/dist/src/commands/blob-buckets.js.map +1 -0
  24. package/dist/src/commands/catalog.d.ts +2 -0
  25. package/dist/src/commands/catalog.js +63 -48
  26. package/dist/src/commands/catalog.js.map +1 -1
  27. package/dist/src/commands/collection-type-configs.d.ts +2 -0
  28. package/dist/src/commands/collection-type-configs.js +85 -0
  29. package/dist/src/commands/collection-type-configs.js.map +1 -0
  30. package/dist/src/commands/collections.d.ts +2 -0
  31. package/dist/src/commands/collections.js +1282 -0
  32. package/dist/src/commands/collections.js.map +1 -0
  33. package/dist/src/commands/comparisons.d.ts +2 -0
  34. package/dist/src/commands/comparisons.js +6 -6
  35. package/dist/src/commands/comparisons.js.map +1 -1
  36. package/dist/src/commands/config.d.ts +46 -0
  37. package/dist/src/commands/config.js +465 -0
  38. package/dist/src/commands/config.js.map +1 -0
  39. package/dist/src/commands/connections.d.ts +2 -0
  40. package/dist/src/commands/connections.js +99 -0
  41. package/dist/src/commands/connections.js.map +1 -0
  42. package/dist/src/commands/cron-triggers.d.ts +2 -0
  43. package/dist/src/commands/cron-triggers.js +266 -0
  44. package/dist/src/commands/cron-triggers.js.map +1 -0
  45. package/dist/src/commands/database-type-configs.d.ts +2 -0
  46. package/dist/src/commands/database-type-configs.js +164 -0
  47. package/dist/src/commands/database-type-configs.js.map +1 -0
  48. package/dist/src/commands/database-types.d.ts +2 -0
  49. package/dist/src/commands/database-types.js +471 -0
  50. package/dist/src/commands/database-types.js.map +1 -0
  51. package/dist/src/commands/databases.d.ts +65 -0
  52. package/dist/src/commands/databases.js +1887 -243
  53. package/dist/src/commands/databases.js.map +1 -1
  54. package/dist/src/commands/documents.d.ts +60 -0
  55. package/dist/src/commands/documents.js +2888 -22
  56. package/dist/src/commands/documents.js.map +1 -1
  57. package/dist/src/commands/email-templates.d.ts +2 -0
  58. package/dist/src/commands/email-templates.js +175 -0
  59. package/dist/src/commands/email-templates.js.map +1 -0
  60. package/dist/src/commands/env.d.ts +23 -0
  61. package/dist/src/commands/env.js +395 -0
  62. package/dist/src/commands/env.js.map +1 -0
  63. package/dist/src/commands/feature-flags.d.ts +14 -0
  64. package/dist/src/commands/feature-flags.js +117 -0
  65. package/dist/src/commands/feature-flags.js.map +1 -0
  66. package/dist/src/commands/functions.d.ts +20 -0
  67. package/dist/src/commands/functions.js +1630 -0
  68. package/dist/src/commands/functions.js.map +1 -0
  69. package/dist/src/commands/group-type-configs.d.ts +2 -0
  70. package/dist/src/commands/group-type-configs.js +87 -0
  71. package/dist/src/commands/group-type-configs.js.map +1 -0
  72. package/dist/src/commands/groups.d.ts +2 -0
  73. package/dist/src/commands/groups.js +65 -113
  74. package/dist/src/commands/groups.js.map +1 -1
  75. package/dist/src/commands/guides.d.ts +223 -0
  76. package/dist/src/commands/guides.js +627 -69
  77. package/dist/src/commands/guides.js.map +1 -1
  78. package/dist/src/commands/init.d.ts +37 -0
  79. package/dist/src/commands/init.js +1693 -208
  80. package/dist/src/commands/init.js.map +1 -1
  81. package/dist/src/commands/integrations.d.ts +2 -0
  82. package/dist/src/commands/integrations.js +428 -187
  83. package/dist/src/commands/integrations.js.map +1 -1
  84. package/dist/src/commands/llm.d.ts +2 -0
  85. package/dist/src/commands/llm.js +4 -2
  86. package/dist/src/commands/llm.js.map +1 -1
  87. package/dist/src/commands/locks.d.ts +8 -0
  88. package/dist/src/commands/locks.js +175 -0
  89. package/dist/src/commands/locks.js.map +1 -0
  90. package/dist/src/commands/metadata-category-configs.d.ts +12 -0
  91. package/dist/src/commands/metadata-category-configs.js +113 -0
  92. package/dist/src/commands/metadata-category-configs.js.map +1 -0
  93. package/dist/src/commands/metadata.d.ts +2 -0
  94. package/dist/src/commands/metadata.js +288 -0
  95. package/dist/src/commands/metadata.js.map +1 -0
  96. package/dist/src/commands/prompts.d.ts +2 -0
  97. package/dist/src/commands/prompts.js +322 -634
  98. package/dist/src/commands/prompts.js.map +1 -1
  99. package/dist/src/commands/rule-sets.d.ts +3 -0
  100. package/dist/src/commands/rule-sets.js +139 -148
  101. package/dist/src/commands/rule-sets.js.map +1 -1
  102. package/dist/src/commands/scripts.d.ts +30 -0
  103. package/dist/src/commands/scripts.js +688 -0
  104. package/dist/src/commands/scripts.js.map +1 -0
  105. package/dist/src/commands/secrets.d.ts +2 -0
  106. package/dist/src/commands/secrets.js +109 -0
  107. package/dist/src/commands/secrets.js.map +1 -0
  108. package/dist/src/commands/sessions.d.ts +2 -0
  109. package/dist/src/commands/sessions.js +76 -0
  110. package/dist/src/commands/sessions.js.map +1 -0
  111. package/dist/src/commands/skill.d.ts +2 -0
  112. package/dist/src/commands/skill.js +29 -0
  113. package/dist/src/commands/skill.js.map +1 -0
  114. package/dist/src/commands/sync-app-settings.d.ts +158 -0
  115. package/dist/src/commands/sync-app-settings.js +328 -0
  116. package/dist/src/commands/sync-app-settings.js.map +1 -0
  117. package/dist/src/commands/sync.d.ts +2670 -0
  118. package/dist/src/commands/sync.js +17144 -834
  119. package/dist/src/commands/sync.js.map +1 -1
  120. package/dist/src/commands/tokens.d.ts +2 -0
  121. package/dist/src/commands/tokens.js +132 -22
  122. package/dist/src/commands/tokens.js.map +1 -1
  123. package/dist/src/commands/users.d.ts +2 -0
  124. package/dist/src/commands/users.js +542 -24
  125. package/dist/src/commands/users.js.map +1 -1
  126. package/dist/src/commands/vars.d.ts +8 -0
  127. package/dist/src/commands/vars.js +97 -0
  128. package/dist/src/commands/vars.js.map +1 -0
  129. package/dist/src/commands/waitlist.d.ts +2 -0
  130. package/dist/src/commands/waitlist.js +12 -11
  131. package/dist/src/commands/waitlist.js.map +1 -1
  132. package/dist/src/commands/webhooks.d.ts +31 -0
  133. package/dist/src/commands/webhooks.js +633 -0
  134. package/dist/src/commands/webhooks.js.map +1 -0
  135. package/dist/src/commands/workflows.d.ts +88 -0
  136. package/dist/src/commands/workflows.js +1568 -742
  137. package/dist/src/commands/workflows.js.map +1 -1
  138. package/dist/src/lib/access-rule-display.d.ts +21 -0
  139. package/dist/src/lib/access-rule-display.js +34 -0
  140. package/dist/src/lib/access-rule-display.js.map +1 -0
  141. package/dist/src/lib/api-client.d.ts +2550 -0
  142. package/dist/src/lib/api-client.js +2503 -160
  143. package/dist/src/lib/api-client.js.map +1 -1
  144. package/dist/src/lib/app-settings-descriptor.d.ts +263 -0
  145. package/dist/src/lib/app-settings-descriptor.js +583 -0
  146. package/dist/src/lib/app-settings-descriptor.js.map +1 -0
  147. package/dist/src/lib/auth-flow.d.ts +8 -0
  148. package/dist/src/lib/batch.d.ts +26 -0
  149. package/dist/src/lib/batch.js +32 -0
  150. package/dist/src/lib/batch.js.map +1 -0
  151. package/dist/src/lib/block-layout.d.ts +160 -0
  152. package/dist/src/lib/block-layout.js +451 -0
  153. package/dist/src/lib/block-layout.js.map +1 -0
  154. package/dist/src/lib/block-selector.d.ts +58 -0
  155. package/dist/src/lib/block-selector.js +92 -0
  156. package/dist/src/lib/block-selector.js.map +1 -0
  157. package/dist/src/lib/canonical-json.d.ts +12 -0
  158. package/dist/src/lib/canonical-json.js +35 -0
  159. package/dist/src/lib/canonical-json.js.map +1 -0
  160. package/dist/src/lib/channel.d.ts +30 -0
  161. package/dist/src/lib/channel.js +68 -0
  162. package/dist/src/lib/channel.js.map +1 -0
  163. package/dist/src/lib/cli-manifest.d.ts +68 -0
  164. package/dist/src/lib/cli-manifest.js +71 -0
  165. package/dist/src/lib/cli-manifest.js.map +1 -0
  166. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +101 -0
  167. package/dist/src/lib/codegen-shared/generatedFiles.js +191 -0
  168. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -0
  169. package/dist/src/lib/codegen-shared/prettierStable.d.ts +262 -0
  170. package/dist/src/lib/codegen-shared/prettierStable.js +610 -0
  171. package/dist/src/lib/codegen-shared/prettierStable.js.map +1 -0
  172. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +38 -0
  173. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +46 -0
  174. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -0
  175. package/dist/src/lib/collection-export.d.ts +184 -0
  176. package/dist/src/lib/collection-export.js +252 -0
  177. package/dist/src/lib/collection-export.js.map +1 -0
  178. package/dist/src/lib/config-json-field.d.ts +28 -0
  179. package/dist/src/lib/config-json-field.js +56 -0
  180. package/dist/src/lib/config-json-field.js.map +1 -0
  181. package/dist/src/lib/config-object-descriptor.d.ts +127 -0
  182. package/dist/src/lib/config-object-descriptor.js +740 -0
  183. package/dist/src/lib/config-object-descriptor.js.map +1 -0
  184. package/dist/src/lib/config-payload.d.ts +92 -0
  185. package/dist/src/lib/config-payload.js +161 -0
  186. package/dist/src/lib/config-payload.js.map +1 -0
  187. package/dist/src/lib/config-surface.d.ts +141 -0
  188. package/dist/src/lib/config-surface.js +368 -0
  189. package/dist/src/lib/config-surface.js.map +1 -0
  190. package/dist/src/lib/config-toml.d.ts +10 -0
  191. package/dist/src/lib/config-toml.js +42 -0
  192. package/dist/src/lib/config-toml.js.map +1 -0
  193. package/dist/src/lib/config.d.ts +71 -0
  194. package/dist/src/lib/config.js +71 -68
  195. package/dist/src/lib/config.js.map +1 -1
  196. package/dist/src/lib/confirm-prompt.d.ts +83 -0
  197. package/dist/src/lib/confirm-prompt.js +110 -0
  198. package/dist/src/lib/confirm-prompt.js.map +1 -0
  199. package/dist/src/lib/constants.d.ts +11 -0
  200. package/dist/src/lib/constants.js +12 -0
  201. package/dist/src/lib/constants.js.map +1 -0
  202. package/dist/src/lib/crash-handlers.d.ts +20 -0
  203. package/dist/src/lib/crash-handlers.js +49 -0
  204. package/dist/src/lib/crash-handlers.js.map +1 -0
  205. package/dist/src/lib/credentials-store.d.ts +104 -0
  206. package/dist/src/lib/credentials-store.js +336 -0
  207. package/dist/src/lib/credentials-store.js.map +1 -0
  208. package/dist/src/lib/csv.d.ts +47 -0
  209. package/dist/src/lib/csv.js +172 -0
  210. package/dist/src/lib/csv.js.map +1 -0
  211. package/dist/src/lib/data-input.d.ts +23 -0
  212. package/dist/src/lib/data-input.js +50 -0
  213. package/dist/src/lib/data-input.js.map +1 -0
  214. package/dist/src/lib/db-codegen/dbFingerprint.d.ts +10 -0
  215. package/dist/src/lib/db-codegen/dbFingerprint.js +17 -0
  216. package/dist/src/lib/db-codegen/dbFingerprint.js.map +1 -0
  217. package/dist/src/lib/db-codegen/dbGenerator.d.ts +67 -0
  218. package/dist/src/lib/db-codegen/dbGenerator.js +170 -0
  219. package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -0
  220. package/dist/src/lib/db-codegen/dbNaming.d.ts +87 -0
  221. package/dist/src/lib/db-codegen/dbNaming.js +180 -0
  222. package/dist/src/lib/db-codegen/dbNaming.js.map +1 -0
  223. package/dist/src/lib/db-codegen/dbTemplates.d.ts +272 -0
  224. package/dist/src/lib/db-codegen/dbTemplates.js +480 -0
  225. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -0
  226. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +73 -0
  227. package/dist/src/lib/db-codegen/dbTsTypes.js +139 -0
  228. package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -0
  229. package/dist/src/lib/db-codegen/dbTypeIR.d.ts +146 -0
  230. package/dist/src/lib/db-codegen/dbTypeIR.js +525 -0
  231. package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -0
  232. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.d.ts +112 -0
  233. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js +211 -0
  234. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js.map +1 -0
  235. package/dist/src/lib/deprecation.d.ts +22 -0
  236. package/dist/src/lib/deprecation.js +43 -0
  237. package/dist/src/lib/deprecation.js.map +1 -0
  238. package/dist/src/lib/document-export-permissions.d.ts +30 -0
  239. package/dist/src/lib/document-export-permissions.js +54 -0
  240. package/dist/src/lib/document-export-permissions.js.map +1 -0
  241. package/dist/src/lib/document-ingest-artifact.d.ts +120 -0
  242. package/dist/src/lib/document-ingest-artifact.js +505 -0
  243. package/dist/src/lib/document-ingest-artifact.js.map +1 -0
  244. package/dist/src/lib/document-ingest-input.d.ts +51 -0
  245. package/dist/src/lib/document-ingest-input.js +132 -0
  246. package/dist/src/lib/document-ingest-input.js.map +1 -0
  247. package/dist/src/lib/document-ingest-rows.d.ts +137 -0
  248. package/dist/src/lib/document-ingest-rows.js +181 -0
  249. package/dist/src/lib/document-ingest-rows.js.map +1 -0
  250. package/dist/src/lib/document-ingest.d.ts +101 -0
  251. package/dist/src/lib/document-ingest.js +384 -0
  252. package/dist/src/lib/document-ingest.js.map +1 -0
  253. package/dist/src/lib/env-resolver-core.d.ts +258 -0
  254. package/dist/src/lib/env-resolver-core.js +447 -0
  255. package/dist/src/lib/env-resolver-core.js.map +1 -0
  256. package/dist/src/lib/env-resolver.d.ts +99 -0
  257. package/dist/src/lib/env-resolver.js +153 -0
  258. package/dist/src/lib/env-resolver.js.map +1 -0
  259. package/dist/src/lib/fetch.d.ts +5 -0
  260. package/dist/src/lib/function-bundle.d.ts +147 -0
  261. package/dist/src/lib/function-bundle.js +341 -0
  262. package/dist/src/lib/function-bundle.js.map +1 -0
  263. package/dist/src/lib/function-collect.d.ts +123 -0
  264. package/dist/src/lib/function-collect.js +610 -0
  265. package/dist/src/lib/function-collect.js.map +1 -0
  266. package/dist/src/lib/function-db-types.d.ts +202 -0
  267. package/dist/src/lib/function-db-types.js +870 -0
  268. package/dist/src/lib/function-db-types.js.map +1 -0
  269. package/dist/src/lib/function-document-types.d.ts +144 -0
  270. package/dist/src/lib/function-document-types.js +370 -0
  271. package/dist/src/lib/function-document-types.js.map +1 -0
  272. package/dist/src/lib/function-grants-preflight.d.ts +64 -0
  273. package/dist/src/lib/function-grants-preflight.js +105 -0
  274. package/dist/src/lib/function-grants-preflight.js.map +1 -0
  275. package/dist/src/lib/function-log-lines.d.ts +76 -0
  276. package/dist/src/lib/function-log-lines.js +160 -0
  277. package/dist/src/lib/function-log-lines.js.map +1 -0
  278. package/dist/src/lib/function-log-row.d.ts +29 -0
  279. package/dist/src/lib/function-log-row.js +73 -0
  280. package/dist/src/lib/function-log-row.js.map +1 -0
  281. package/dist/src/lib/function-log-tail.d.ts +132 -0
  282. package/dist/src/lib/function-log-tail.js +262 -0
  283. package/dist/src/lib/function-log-tail.js.map +1 -0
  284. package/dist/src/lib/function-run.d.ts +289 -0
  285. package/dist/src/lib/function-run.js +389 -0
  286. package/dist/src/lib/function-run.js.map +1 -0
  287. package/dist/src/lib/function-schema-codegen.d.ts +143 -0
  288. package/dist/src/lib/function-schema-codegen.js +420 -0
  289. package/dist/src/lib/function-schema-codegen.js.map +1 -0
  290. package/dist/src/lib/function-sync.d.ts +459 -0
  291. package/dist/src/lib/function-sync.js +1258 -0
  292. package/dist/src/lib/function-sync.js.map +1 -0
  293. package/dist/src/lib/function-trigger-listing.d.ts +27 -0
  294. package/dist/src/lib/function-trigger-listing.js +70 -0
  295. package/dist/src/lib/function-trigger-listing.js.map +1 -0
  296. package/dist/src/lib/function-typecheck.d.ts +86 -0
  297. package/dist/src/lib/function-typecheck.js +370 -0
  298. package/dist/src/lib/function-typecheck.js.map +1 -0
  299. package/dist/src/lib/function-versions.d.ts +122 -0
  300. package/dist/src/lib/function-versions.js +182 -0
  301. package/dist/src/lib/function-versions.js.map +1 -0
  302. package/dist/src/lib/generated-allowlist.d.ts +28 -0
  303. package/dist/src/lib/generated-allowlist.js +281 -0
  304. package/dist/src/lib/generated-allowlist.js.map +1 -0
  305. package/dist/src/lib/generated-config-surfaces.d.ts +2936 -0
  306. package/dist/src/lib/generated-config-surfaces.js +10569 -0
  307. package/dist/src/lib/generated-config-surfaces.js.map +1 -0
  308. package/dist/src/lib/generated-sdk-types.d.ts +12 -0
  309. package/dist/src/lib/generated-sdk-types.js +13 -0
  310. package/dist/src/lib/generated-sdk-types.js.map +1 -0
  311. package/dist/src/lib/generated-template-lint.d.ts +212 -0
  312. package/dist/src/lib/generated-template-lint.js +624 -0
  313. package/dist/src/lib/generated-template-lint.js.map +1 -0
  314. package/dist/src/lib/init-adopt.d.ts +16 -0
  315. package/dist/src/lib/init-adopt.js +34 -0
  316. package/dist/src/lib/init-adopt.js.map +1 -0
  317. package/dist/src/lib/init-assets.d.ts +39 -0
  318. package/dist/src/lib/init-assets.js +97 -0
  319. package/dist/src/lib/init-assets.js.map +1 -0
  320. package/dist/src/lib/init-client-platforms.d.ts +14 -0
  321. package/dist/src/lib/init-client-platforms.js +71 -0
  322. package/dist/src/lib/init-client-platforms.js.map +1 -0
  323. package/dist/src/lib/init-config.d.ts +98 -0
  324. package/dist/src/lib/init-config.js +186 -0
  325. package/dist/src/lib/init-config.js.map +1 -0
  326. package/dist/src/lib/init-email-redirect-uris.d.ts +37 -0
  327. package/dist/src/lib/init-email-redirect-uris.js +46 -0
  328. package/dist/src/lib/init-email-redirect-uris.js.map +1 -0
  329. package/dist/src/lib/init-ios-links.d.ts +91 -0
  330. package/dist/src/lib/init-ios-links.js +219 -0
  331. package/dist/src/lib/init-ios-links.js.map +1 -0
  332. package/dist/src/lib/init-plan.d.ts +80 -0
  333. package/dist/src/lib/init-plan.js +95 -0
  334. package/dist/src/lib/init-plan.js.map +1 -0
  335. package/dist/src/lib/init-production-env.d.ts +48 -0
  336. package/dist/src/lib/init-production-env.js +59 -0
  337. package/dist/src/lib/init-production-env.js.map +1 -0
  338. package/dist/src/lib/init-schema.d.ts +74 -0
  339. package/dist/src/lib/init-schema.js +358 -0
  340. package/dist/src/lib/init-schema.js.map +1 -0
  341. package/dist/src/lib/init-xcode.d.ts +34 -0
  342. package/dist/src/lib/init-xcode.js +138 -0
  343. package/dist/src/lib/init-xcode.js.map +1 -0
  344. package/dist/src/lib/integration-request-config.d.ts +30 -0
  345. package/dist/src/lib/integration-request-config.js +145 -0
  346. package/dist/src/lib/integration-request-config.js.map +1 -0
  347. package/dist/src/lib/integration-selector.d.ts +42 -0
  348. package/dist/src/lib/integration-selector.js +46 -0
  349. package/dist/src/lib/integration-selector.js.map +1 -0
  350. package/dist/src/lib/ios-app-id.d.ts +34 -0
  351. package/dist/src/lib/ios-app-id.js +69 -0
  352. package/dist/src/lib/ios-app-id.js.map +1 -0
  353. package/dist/src/lib/list-options.d.ts +68 -0
  354. package/dist/src/lib/list-options.js +89 -0
  355. package/dist/src/lib/list-options.js.map +1 -0
  356. package/dist/src/lib/local-state.d.ts +55 -0
  357. package/dist/src/lib/local-state.js +167 -0
  358. package/dist/src/lib/local-state.js.map +1 -0
  359. package/dist/src/lib/local-test-cases.d.ts +63 -0
  360. package/dist/src/lib/local-test-cases.js +136 -0
  361. package/dist/src/lib/local-test-cases.js.map +1 -0
  362. package/dist/src/lib/log-inspection.d.ts +715 -0
  363. package/dist/src/lib/log-inspection.js +816 -0
  364. package/dist/src/lib/log-inspection.js.map +1 -0
  365. package/dist/src/lib/logout-admin-session.d.ts +33 -0
  366. package/dist/src/lib/logout-admin-session.js +70 -0
  367. package/dist/src/lib/logout-admin-session.js.map +1 -0
  368. package/dist/src/lib/migration-nag.d.ts +49 -0
  369. package/dist/src/lib/migration-nag.js +163 -0
  370. package/dist/src/lib/migration-nag.js.map +1 -0
  371. package/dist/src/lib/object-status-filter.d.ts +22 -0
  372. package/dist/src/lib/object-status-filter.js +45 -0
  373. package/dist/src/lib/object-status-filter.js.map +1 -0
  374. package/dist/src/lib/output.d.ts +124 -0
  375. package/dist/src/lib/output.js +219 -8
  376. package/dist/src/lib/output.js.map +1 -1
  377. package/dist/src/lib/package-manager.d.ts +140 -0
  378. package/dist/src/lib/package-manager.js +305 -0
  379. package/dist/src/lib/package-manager.js.map +1 -0
  380. package/dist/src/lib/paginate.d.ts +98 -0
  381. package/dist/src/lib/paginate.js +112 -0
  382. package/dist/src/lib/paginate.js.map +1 -0
  383. package/dist/src/lib/platform-owned.d.ts +63 -0
  384. package/dist/src/lib/platform-owned.js +85 -0
  385. package/dist/src/lib/platform-owned.js.map +1 -0
  386. package/dist/src/lib/project-config.d.ts +122 -0
  387. package/dist/src/lib/project-config.js +244 -0
  388. package/dist/src/lib/project-config.js.map +1 -0
  389. package/dist/src/lib/prompt-cost-format.d.ts +11 -0
  390. package/dist/src/lib/prompt-cost-format.js +41 -0
  391. package/dist/src/lib/prompt-cost-format.js.map +1 -0
  392. package/dist/src/lib/prompt-schema-codegen.d.ts +147 -0
  393. package/dist/src/lib/prompt-schema-codegen.js +462 -0
  394. package/dist/src/lib/prompt-schema-codegen.js.map +1 -0
  395. package/dist/src/lib/query-operators.d.ts +43 -0
  396. package/dist/src/lib/query-operators.js +80 -0
  397. package/dist/src/lib/query-operators.js.map +1 -0
  398. package/dist/src/lib/record-filter.d.ts +18 -0
  399. package/dist/src/lib/record-filter.js +55 -0
  400. package/dist/src/lib/record-filter.js.map +1 -0
  401. package/dist/src/lib/refresh-admin-credentials.d.ts +73 -0
  402. package/dist/src/lib/refresh-admin-credentials.js +123 -0
  403. package/dist/src/lib/refresh-admin-credentials.js.map +1 -0
  404. package/dist/src/lib/resolve-init-dev-port.d.ts +56 -0
  405. package/dist/src/lib/resolve-init-dev-port.js +55 -0
  406. package/dist/src/lib/resolve-init-dev-port.js.map +1 -0
  407. package/dist/src/lib/resolve-init-server.d.ts +64 -0
  408. package/dist/src/lib/resolve-init-server.js +77 -0
  409. package/dist/src/lib/resolve-init-server.js.map +1 -0
  410. package/dist/src/lib/resolve-owner.d.ts +19 -0
  411. package/dist/src/lib/resolve-owner.js +20 -0
  412. package/dist/src/lib/resolve-owner.js.map +1 -0
  413. package/dist/src/lib/resolve-platform.d.ts +74 -0
  414. package/dist/src/lib/resolve-platform.js +105 -0
  415. package/dist/src/lib/resolve-platform.js.map +1 -0
  416. package/dist/src/lib/run-status.d.ts +19 -0
  417. package/dist/src/lib/run-status.generated.d.ts +39 -0
  418. package/dist/src/lib/run-status.generated.js +66 -0
  419. package/dist/src/lib/run-status.generated.js.map +1 -0
  420. package/dist/src/lib/run-status.js +19 -0
  421. package/dist/src/lib/run-status.js.map +1 -0
  422. package/dist/src/lib/server-text-normalization.d.ts +51 -0
  423. package/dist/src/lib/server-text-normalization.js +90 -0
  424. package/dist/src/lib/server-text-normalization.js.map +1 -0
  425. package/dist/src/lib/server-url.d.ts +22 -0
  426. package/dist/src/lib/server-url.js +33 -0
  427. package/dist/src/lib/server-url.js.map +1 -0
  428. package/dist/src/lib/signing-secret-status.d.ts +81 -0
  429. package/dist/src/lib/signing-secret-status.js +116 -0
  430. package/dist/src/lib/signing-secret-status.js.map +1 -0
  431. package/dist/src/lib/skill-installer.d.ts +71 -0
  432. package/dist/src/lib/skill-installer.js +441 -0
  433. package/dist/src/lib/skill-installer.js.map +1 -0
  434. package/dist/src/lib/snapshot-audit-source.d.ts +45 -0
  435. package/dist/src/lib/snapshot-audit-source.js +58 -0
  436. package/dist/src/lib/snapshot-audit-source.js.map +1 -0
  437. package/dist/src/lib/snapshot-audit-store.d.ts +52 -0
  438. package/dist/src/lib/snapshot-audit-store.js +196 -0
  439. package/dist/src/lib/snapshot-audit-store.js.map +1 -0
  440. package/dist/src/lib/snapshot-audit.d.ts +207 -0
  441. package/dist/src/lib/snapshot-audit.js +431 -0
  442. package/dist/src/lib/snapshot-audit.js.map +1 -0
  443. package/dist/src/lib/snapshot-build-rows.d.ts +60 -0
  444. package/dist/src/lib/snapshot-build-rows.js +87 -0
  445. package/dist/src/lib/snapshot-build-rows.js.map +1 -0
  446. package/dist/src/lib/snapshot-build.d.ts +50 -0
  447. package/dist/src/lib/snapshot-build.js +111 -0
  448. package/dist/src/lib/snapshot-build.js.map +1 -0
  449. package/dist/src/lib/snapshot-manifest-layout.d.ts +61 -0
  450. package/dist/src/lib/snapshot-manifest-layout.js +70 -0
  451. package/dist/src/lib/snapshot-manifest-layout.js.map +1 -0
  452. package/dist/src/lib/snapshots.d.ts +98 -0
  453. package/dist/src/lib/snapshots.js +294 -0
  454. package/dist/src/lib/snapshots.js.map +1 -0
  455. package/dist/src/lib/step-run-table.d.ts +43 -0
  456. package/dist/src/lib/step-run-table.js +129 -0
  457. package/dist/src/lib/step-run-table.js.map +1 -0
  458. package/dist/src/lib/storage-pending-retry.d.ts +26 -0
  459. package/dist/src/lib/storage-pending-retry.js +42 -0
  460. package/dist/src/lib/storage-pending-retry.js.map +1 -0
  461. package/dist/src/lib/swift-codegen/agentGenerator.d.ts +42 -0
  462. package/dist/src/lib/swift-codegen/agentGenerator.js +118 -0
  463. package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -0
  464. package/dist/src/lib/swift-codegen/banners.d.ts +24 -0
  465. package/dist/src/lib/swift-codegen/banners.js +25 -0
  466. package/dist/src/lib/swift-codegen/banners.js.map +1 -0
  467. package/dist/src/lib/swift-codegen/dbGenerator.d.ts +113 -0
  468. package/dist/src/lib/swift-codegen/dbGenerator.js +926 -0
  469. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -0
  470. package/dist/src/lib/swift-codegen/dbSwiftTypes.d.ts +42 -0
  471. package/dist/src/lib/swift-codegen/dbSwiftTypes.js +100 -0
  472. package/dist/src/lib/swift-codegen/dbSwiftTypes.js.map +1 -0
  473. package/dist/src/lib/swift-codegen/functionGenerator.d.ts +139 -0
  474. package/dist/src/lib/swift-codegen/functionGenerator.js +462 -0
  475. package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -0
  476. package/dist/src/lib/swift-codegen/generator.d.ts +100 -0
  477. package/dist/src/lib/swift-codegen/generator.js +457 -0
  478. package/dist/src/lib/swift-codegen/generator.js.map +1 -0
  479. package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +87 -0
  480. package/dist/src/lib/swift-codegen/schemaToSwift.js +661 -0
  481. package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -0
  482. package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +94 -0
  483. package/dist/src/lib/swift-codegen/siblingSymbols.js +155 -0
  484. package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -0
  485. package/dist/src/lib/swift-codegen/swiftNaming.d.ts +85 -0
  486. package/dist/src/lib/swift-codegen/swiftNaming.js +198 -0
  487. package/dist/src/lib/swift-codegen/swiftNaming.js.map +1 -0
  488. package/dist/src/lib/sync-dir-selector.d.ts +21 -0
  489. package/dist/src/lib/sync-dir-selector.js +30 -0
  490. package/dist/src/lib/sync-dir-selector.js.map +1 -0
  491. package/dist/src/lib/sync-paths.d.ts +128 -0
  492. package/dist/src/lib/sync-paths.js +195 -0
  493. package/dist/src/lib/sync-paths.js.map +1 -0
  494. package/dist/src/lib/sync-resource-types.d.ts +563 -0
  495. package/dist/src/lib/sync-resource-types.js +1073 -0
  496. package/dist/src/lib/sync-resource-types.js.map +1 -0
  497. package/dist/src/lib/sync-selectors.d.ts +138 -0
  498. package/dist/src/lib/sync-selectors.js +289 -0
  499. package/dist/src/lib/sync-selectors.js.map +1 -0
  500. package/dist/src/lib/template.d.ts +170 -0
  501. package/dist/src/lib/template.js +484 -68
  502. package/dist/src/lib/template.js.map +1 -1
  503. package/dist/src/lib/test-case-file-names.d.ts +40 -0
  504. package/dist/src/lib/test-case-file-names.js +91 -0
  505. package/dist/src/lib/test-case-file-names.js.map +1 -0
  506. package/dist/src/lib/test-case-keys.d.ts +29 -0
  507. package/dist/src/lib/test-case-keys.js +55 -0
  508. package/dist/src/lib/test-case-keys.js.map +1 -0
  509. package/dist/src/lib/test-case-variables.d.ts +29 -0
  510. package/dist/src/lib/test-case-variables.js +71 -0
  511. package/dist/src/lib/test-case-variables.js.map +1 -0
  512. package/dist/src/lib/token-inject.d.ts +56 -0
  513. package/dist/src/lib/token-inject.js +204 -0
  514. package/dist/src/lib/token-inject.js.map +1 -0
  515. package/dist/src/lib/toml-database-config.d.ts +123 -0
  516. package/dist/src/lib/toml-database-config.js +544 -0
  517. package/dist/src/lib/toml-database-config.js.map +1 -0
  518. package/dist/src/lib/toml-metadata-config.d.ts +151 -0
  519. package/dist/src/lib/toml-metadata-config.js +476 -0
  520. package/dist/src/lib/toml-metadata-config.js.map +1 -0
  521. package/dist/src/lib/toml-native-form.d.ts +46 -0
  522. package/dist/src/lib/toml-native-form.js +78 -0
  523. package/dist/src/lib/toml-native-form.js.map +1 -0
  524. package/dist/src/lib/toml-params-validator.d.ts +129 -0
  525. package/dist/src/lib/toml-params-validator.js +298 -0
  526. package/dist/src/lib/toml-params-validator.js.map +1 -0
  527. package/dist/src/lib/toml-scalar-edit.d.ts +43 -0
  528. package/dist/src/lib/toml-scalar-edit.js +283 -0
  529. package/dist/src/lib/toml-scalar-edit.js.map +1 -0
  530. package/dist/src/lib/user-selector.d.ts +24 -0
  531. package/dist/src/lib/user-selector.js +33 -0
  532. package/dist/src/lib/user-selector.js.map +1 -0
  533. package/dist/src/lib/version-check.d.ts +35 -0
  534. package/dist/src/lib/version-check.js +241 -0
  535. package/dist/src/lib/version-check.js.map +1 -0
  536. package/dist/src/lib/watch.d.ts +121 -0
  537. package/dist/src/lib/watch.js +169 -0
  538. package/dist/src/lib/watch.js.map +1 -0
  539. package/dist/src/lib/web-url.d.ts +40 -0
  540. package/dist/src/lib/web-url.js +76 -0
  541. package/dist/src/lib/web-url.js.map +1 -0
  542. package/dist/src/lib/webhook-deliver.d.ts +209 -0
  543. package/dist/src/lib/webhook-deliver.js +519 -0
  544. package/dist/src/lib/webhook-deliver.js.map +1 -0
  545. package/dist/src/lib/workflow-apply.d.ts +110 -0
  546. package/dist/src/lib/workflow-apply.js +164 -0
  547. package/dist/src/lib/workflow-apply.js.map +1 -0
  548. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.d.ts +129 -0
  549. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js +269 -0
  550. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js.map +1 -0
  551. package/dist/src/lib/workflow-codegen/generator.d.ts +96 -0
  552. package/dist/src/lib/workflow-codegen/generator.js +361 -0
  553. package/dist/src/lib/workflow-codegen/generator.js.map +1 -0
  554. package/dist/src/lib/workflow-codegen/invokerIR.d.ts +94 -0
  555. package/dist/src/lib/workflow-codegen/invokerIR.js +76 -0
  556. package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -0
  557. package/dist/src/lib/workflow-codegen/naming.d.ts +33 -0
  558. package/dist/src/lib/workflow-codegen/naming.js +81 -0
  559. package/dist/src/lib/workflow-codegen/naming.js.map +1 -0
  560. package/dist/src/lib/workflow-codegen/schemaToTs.d.ts +80 -0
  561. package/dist/src/lib/workflow-codegen/schemaToTs.js +303 -0
  562. package/dist/src/lib/workflow-codegen/schemaToTs.js.map +1 -0
  563. package/dist/src/lib/workflow-config-apply.d.ts +70 -0
  564. package/dist/src/lib/workflow-config-apply.js +137 -0
  565. package/dist/src/lib/workflow-config-apply.js.map +1 -0
  566. package/dist/src/lib/workflow-config-sidecar.d.ts +63 -0
  567. package/dist/src/lib/workflow-config-sidecar.js +96 -0
  568. package/dist/src/lib/workflow-config-sidecar.js.map +1 -0
  569. package/dist/src/lib/workflow-defaults.d.ts +29 -0
  570. package/dist/src/lib/workflow-defaults.js +41 -0
  571. package/dist/src/lib/workflow-defaults.js.map +1 -0
  572. package/dist/src/lib/workflow-fragments.d.ts +64 -0
  573. package/dist/src/lib/workflow-fragments.js +342 -0
  574. package/dist/src/lib/workflow-fragments.js.map +1 -0
  575. package/dist/src/lib/workflow-include-preserve.d.ts +76 -0
  576. package/dist/src/lib/workflow-include-preserve.js +286 -0
  577. package/dist/src/lib/workflow-include-preserve.js.map +1 -0
  578. package/dist/src/lib/workflow-payload.d.ts +98 -0
  579. package/dist/src/lib/workflow-payload.js +178 -0
  580. package/dist/src/lib/workflow-payload.js.map +1 -0
  581. package/dist/src/lib/workflow-toml-validator.d.ts +211 -0
  582. package/dist/src/lib/workflow-toml-validator.js +770 -0
  583. package/dist/src/lib/workflow-toml-validator.js.map +1 -0
  584. package/dist/src/lib/workflow-usage.d.ts +196 -0
  585. package/dist/src/lib/workflow-usage.js +310 -0
  586. package/dist/src/lib/workflow-usage.js.map +1 -0
  587. package/dist/src/types/index.d.ts +591 -0
  588. package/dist/src/validators.d.ts +65 -0
  589. package/dist/src/validators.js +64 -0
  590. package/dist/src/validators.js.map +1 -0
  591. package/package.json +34 -9
@@ -1,11 +1,46 @@
1
- import { readFileSync, writeFileSync, statSync } from "fs";
2
- import { basename } from "path";
3
- import * as TOML from "@iarna/toml";
4
- import { lookup as mimeLookup } from "mime-types";
1
+ import { writeFileSync } from "fs";
5
2
  import { ApiClient } from "../lib/api-client.js";
6
3
  import { resolveAppId } from "../lib/config.js";
7
4
  import chalk from "chalk";
8
- import { success, error, info, warn, keyValue, formatTable, formatId, formatDate, formatStatus, formatDuration, json, divider, } from "../lib/output.js";
5
+ import { success, error, info, warn, keyValue, result as printResult, formatTable, formatId, formatDate, formatStatus, formatDuration, json, divider, progress, progressEnd, } from "../lib/output.js";
6
+ import { pollUntil } from "../lib/watch.js";
7
+ import { confirmPrompt } from "../lib/confirm-prompt.js";
8
+ import { parseStatusFilter } from "../lib/object-status-filter.js";
9
+ import { formatAccessRuleForDisplay } from "../lib/access-rule-display.js";
10
+ import { requireTestCaseInputVariables } from "../lib/test-case-variables.js";
11
+ import { resolvePromptSelector } from "../lib/block-selector.js";
12
+ import { normalizeCliListEnvelope, wholeListEnvelope, } from "../lib/paginate.js";
13
+ import { pageCursorOption, pageLimitOption, parsePageLimit, printEmptyPage, printPageHint, } from "../lib/list-options.js";
14
+ /**
15
+ * The one-line token accounting an execution prints.
16
+ *
17
+ * #3358 — `reasoningTokens` is appended only when the provider reported some,
18
+ * so a prompt that does no thinking prints exactly what it always did. It is
19
+ * shown beside the output count rather than folded into it because the two
20
+ * providers disagree about whether reasoning is already inside that number,
21
+ * and the whole point of the metric is to say how much of the decode was
22
+ * deliberation rather than answer.
23
+ */
24
+ function formatTokens(metrics) {
25
+ const base = `${metrics?.inputTokens || 0} in / ${metrics?.outputTokens || 0} out`;
26
+ const reasoning = metrics?.reasoningTokens;
27
+ return typeof reasoning === "number"
28
+ ? `${base} (${reasoning} reasoning)`
29
+ : base;
30
+ }
31
+ /**
32
+ * Resolve a prompt reference — its server id, or the author-chosen key its
33
+ * TOML file is named for.
34
+ *
35
+ * Under TOML-only authoring the key is what an operator has: `prompts/<key>.toml`
36
+ * is the object, and the id is a server detail they never wrote down. Mirrors
37
+ * `resolveScriptId` in `scripts.ts`. The resolution itself lives in
38
+ * `block-selector.ts` (#3249) so the `tests` verbs and `configs get` agree on
39
+ * it — and drain every page of the list, where this used to read one.
40
+ */
41
+ async function resolvePromptId(client, appId, ref) {
42
+ return (await resolvePromptSelector(client, appId, ref)).blockId;
43
+ }
9
44
  export function registerPromptsCommands(program) {
10
45
  const prompts = program
11
46
  .command("prompts")
@@ -13,9 +48,19 @@ export function registerPromptsCommands(program) {
13
48
  .addHelpText("after", `
14
49
  Examples:
15
50
  $ primitive prompts list
16
- $ primitive prompts create --from-file summarizer.toml
51
+ $ primitive config create prompt summarizer # scaffold prompts/summarizer.toml
52
+ $ primitive config push --only prompt/summarizer # apply it
17
53
  $ primitive prompts execute 01HXY... --vars '{"text":"Hello world"}'
18
- $ primitive prompts configs activate 01HXY... 01ABC...
54
+ $ primitive prompts configs list summarizer # what the server runs
55
+ `);
56
+ // Server-side configuration is authored in TOML and applied with `config push`
57
+ // (issue #2645) — this group no longer carries config-setting flags.
58
+ prompts.addHelpText("after", `
59
+ Configuration lives in prompts/<key>.toml:
60
+ $ primitive config fields prompt # the file's keys, types and defaults
61
+ $ primitive config create prompt <key> # scaffold one locally
62
+ $ primitive config set prompt/<key> <path>=<value>
63
+ $ primitive config push --only prompt/<key>
19
64
  `);
20
65
  // List prompts
21
66
  prompts
@@ -23,21 +68,35 @@ Examples:
23
68
  .description("List prompts")
24
69
  .argument("[app-id]", "App ID (uses current app if not specified)")
25
70
  .option("--app <app-id>", "App ID")
26
- .option("--status <status>", "Filter by status: draft, active, archived")
71
+ .option("--status <status>", "Filter by status: active, inactive, archived")
72
+ .addOption(pageLimitOption())
73
+ .addOption(pageCursorOption())
27
74
  .option("--json", "Output as JSON")
28
75
  .action(async (appId, options) => {
76
+ const limit = parsePageLimit(options.limit);
29
77
  const resolvedAppId = resolveAppId(appId, options);
30
78
  const client = new ApiClient();
31
79
  try {
32
- const { items } = await client.listPrompts(resolvedAppId, {
33
- status: options.status,
34
- });
80
+ // #2803 — `draft` is gone; refuse it by name rather than returning the
81
+ // empty list a canonical filter would produce. #2887 added `archived`:
82
+ // the delete flow writes one now, so asking for the retired rows is a
83
+ // question with an answer rather than a 400.
84
+ const page = normalizeCliListEnvelope(await client.listPrompts(resolvedAppId, {
85
+ status: parseStatusFilter("prompts", options.status, [
86
+ "active",
87
+ "inactive",
88
+ "archived",
89
+ ]),
90
+ limit,
91
+ cursor: options.cursor,
92
+ }));
93
+ const items = page.items;
35
94
  if (options.json) {
36
- json(items);
95
+ json(page);
37
96
  return;
38
97
  }
39
- if (!items || items.length === 0) {
40
- info("No prompts found.");
98
+ if (items.length === 0) {
99
+ printEmptyPage(page, "prompts");
41
100
  return;
42
101
  }
43
102
  console.log(formatTable(items, [
@@ -47,215 +106,161 @@ Examples:
47
106
  { header: "STATUS", key: "status", format: formatStatus },
48
107
  { header: "MODIFIED", key: "modifiedAt", format: formatDate },
49
108
  ]));
109
+ printPageHint(page);
50
110
  }
51
111
  catch (err) {
52
112
  error(err.message);
53
113
  process.exit(1);
54
114
  }
55
115
  });
56
- // Create prompt
116
+ // #2645 criterion 9 / #2803 — the operational verbs. They are the only
117
+ // writers of `status`, which is server-owned and no longer a TOML key.
57
118
  prompts
58
- .command("create")
59
- .description("Create a new prompt")
60
- .argument("[app-id]", "App ID (uses current app if not specified)")
61
- .option("--app <app-id>", "App ID")
62
- .option("--key <key>", "Prompt key (unique identifier)")
63
- .option("--name <name>", "Display name")
64
- .option("--description <desc>", "Description")
65
- .option("--provider <provider>", "LLM provider: openrouter, gemini", "openrouter")
66
- .option("--model <model>", "Model name")
67
- .option("--system-prompt <prompt>", "System prompt")
68
- .option("--user-template <template>", "User prompt template")
69
- .option("--temperature <temp>", "Temperature (0-1)")
70
- .option("--max-tokens <n>", "Max output tokens")
71
- .option("--output-format <format>", "Output format: text, json")
72
- .option("--input-schema <json>", "Input schema as JSON")
73
- .option("--output-schema <json>", "Output schema as JSON")
74
- .option("--from-file <path>", "Load prompt from TOML file")
119
+ .command("disable")
120
+ .description("Take prompt out of service now. Operational, not configuration: `status` is server-owned and 'config push' can neither set nor clear it.")
121
+ .argument("<prompt-id>", "Prompt ID")
122
+ .option("--app <app-id>", "App ID (uses current app if not specified)")
75
123
  .option("--json", "Output as JSON")
76
- .action(async (appId, options) => {
77
- const resolvedAppId = resolveAppId(appId, options);
124
+ .action(async (id, options) => {
125
+ const resolvedAppId = resolveAppId(undefined, options);
78
126
  const client = new ApiClient();
79
- let payload;
80
- if (options.fromFile) {
81
- try {
82
- const content = readFileSync(options.fromFile, "utf-8");
83
- const tomlData = TOML.parse(content);
84
- const prompt = tomlData.prompt || tomlData;
85
- const config = tomlData.configs?.[0] || {};
86
- payload = {
87
- promptKey: prompt.key || prompt.promptKey,
88
- displayName: prompt.displayName || prompt.name,
89
- description: prompt.description,
90
- provider: config.provider || prompt.provider || "openrouter",
91
- model: config.model || prompt.model,
92
- systemPrompt: config.systemPrompt || prompt.systemPrompt,
93
- userPromptTemplate: config.userPromptTemplate || prompt.userPromptTemplate,
94
- temperature: config.temperature || prompt.temperature,
95
- maxTokens: config.maxTokens || prompt.maxTokens,
96
- outputFormat: config.outputFormat || prompt.outputFormat,
97
- inputSchema: prompt.inputSchema,
98
- outputSchema: prompt.outputSchema,
99
- };
100
- }
101
- catch (err) {
102
- error(`Failed to read TOML file: ${err.message}`);
103
- process.exit(1);
104
- }
105
- }
106
- else {
107
- if (!options.key || !options.name || !options.model || !options.userTemplate) {
108
- error("Required: --key, --name, --model, --user-template (or use --from-file)");
109
- process.exit(1);
110
- }
111
- payload = {
112
- promptKey: options.key,
113
- displayName: options.name,
114
- description: options.description,
115
- provider: options.provider,
116
- model: options.model,
117
- systemPrompt: options.systemPrompt,
118
- userPromptTemplate: options.userTemplate,
119
- temperature: options.temperature,
120
- maxTokens: options.maxTokens ? parseInt(options.maxTokens) : undefined,
121
- outputFormat: options.outputFormat,
122
- inputSchema: options.inputSchema,
123
- outputSchema: options.outputSchema,
124
- };
125
- }
126
127
  try {
127
- const result = await client.createPrompt(resolvedAppId, payload);
128
+ const result = await client.setPromptAvailability(resolvedAppId, id, "disable");
128
129
  if (options.json) {
129
130
  json(result);
130
131
  return;
131
132
  }
132
- success(`Prompt created: ${result.displayName}`);
133
- keyValue("Prompt ID", result.promptId);
134
- keyValue("Key", result.promptKey);
133
+ success(`Prompt disabled: ${result.promptKey || id}`);
135
134
  }
136
135
  catch (err) {
137
136
  error(err.message);
138
137
  process.exit(1);
139
138
  }
140
139
  });
141
- // Get prompt
142
140
  prompts
143
- .command("get")
144
- .description("Get prompt details")
141
+ .command("enable")
142
+ .description("Put prompt back in service.")
145
143
  .argument("<prompt-id>", "Prompt ID")
146
144
  .option("--app <app-id>", "App ID (uses current app if not specified)")
147
145
  .option("--json", "Output as JSON")
148
- .action(async (promptId, options) => {
146
+ .action(async (id, options) => {
149
147
  const resolvedAppId = resolveAppId(undefined, options);
150
148
  const client = new ApiClient();
151
149
  try {
152
- const result = await client.getPrompt(resolvedAppId, promptId);
150
+ const result = await client.setPromptAvailability(resolvedAppId, id, "enable");
153
151
  if (options.json) {
154
152
  json(result);
155
153
  return;
156
154
  }
157
- keyValue("Prompt ID", result.promptId);
158
- keyValue("Key", result.promptKey);
159
- keyValue("Name", result.displayName);
160
- keyValue("Description", result.description);
161
- keyValue("Status", formatStatus(result.status));
162
- keyValue("Active Config", result.activeConfigId || "-");
163
- if (result.configs && result.configs.length > 0) {
164
- divider();
165
- info(`Configs (${result.configs.length}):`);
166
- console.log(formatTable(result.configs, [
167
- { header: "ID", key: "configId", format: formatId },
168
- { header: "NAME", key: "configName" },
169
- { header: "MODEL", key: "model" },
170
- { header: "STATUS", key: "status", format: formatStatus },
171
- ]));
172
- }
155
+ success(`Prompt enabled: ${result.promptKey || id}`);
173
156
  }
174
157
  catch (err) {
175
158
  error(err.message);
176
159
  process.exit(1);
177
160
  }
178
161
  });
179
- // Update prompt
162
+ // #2907 — the delete flow's CLI spelling. Not a second writer of `status`:
163
+ // this is the plain `DELETE` #2887 made retire-with-history, which until now
164
+ // only the admin API and the console could reach. Named `archive`, not
165
+ // `delete`, because a configuration noun gets no `delete` verb
166
+ // (docs/cli-design.md) and prune-by-push stays the only CLI hard-delete path.
180
167
  prompts
181
- .command("update")
182
- .description("Update a prompt")
168
+ .command("archive")
169
+ .description("Retire a prompt (soft delete). Delete-lifecycle, not `disable`: the row and its configs are kept so executions still resolve, it goes on holding its key, and there is no un-archive.")
183
170
  .argument("<prompt-id>", "Prompt ID")
184
171
  .option("--app <app-id>", "App ID (uses current app if not specified)")
185
- .option("--name <name>", "Display name")
186
- .option("--description <desc>", "Description")
187
- .option("--status <status>", "Status: draft, active, archived")
188
- .option("--input-schema <json>", "Input schema as JSON")
189
- .option("--output-schema <json>", "Output schema as JSON")
172
+ .option("-y, --yes", "Skip confirmation prompt")
190
173
  .option("--json", "Output as JSON")
191
- .action(async (promptId, options) => {
174
+ .addHelpText("after", `
175
+ What archiving a prompt costs (retire-with-history):
176
+ The member endpoint refuses it, and so does admin execution — the diagnostic
177
+ runs an INACTIVE prompt on purpose, but not an archived one.
178
+ Its configs, executions and analytics keep resolving what they point at.
179
+ The archived row still holds its promptKey.
180
+ \`primitive prompts enable\` refuses an archived prompt; there is no
181
+ un-archive.
182
+ The per-version \`[[configs]] status\` is a different key and is untouched.
183
+
184
+ Reclaiming the key means hard-deleting the row:
185
+ $ rm prompts/<key>.toml
186
+ $ primitive config push --prune # confirmed; hard-deletes the row
187
+ # or hard-delete it in the console, then re-add the file and push.
188
+ `)
189
+ .action(async (id, options) => {
192
190
  const resolvedAppId = resolveAppId(undefined, options);
193
- const payload = {};
194
- if (options.name)
195
- payload.displayName = options.name;
196
- if (options.description)
197
- payload.description = options.description;
198
- if (options.status)
199
- payload.status = options.status;
200
- if (options.inputSchema)
201
- payload.inputSchema = options.inputSchema;
202
- if (options.outputSchema)
203
- payload.outputSchema = options.outputSchema;
204
- if (Object.keys(payload).length === 0) {
205
- error("No update options specified.");
206
- process.exit(1);
191
+ if (!options.yes) {
192
+ let confirmed;
193
+ try {
194
+ confirmed = await confirmPrompt(`Archive prompt ${id}? It is retired, its configs and executions are ` +
195
+ `kept, it goes on holding its key, and there is no un-archive.`);
196
+ }
197
+ catch (err) {
198
+ error(err.message);
199
+ process.exit(1);
200
+ }
201
+ if (!confirmed) {
202
+ info("Cancelled.");
203
+ return;
204
+ }
207
205
  }
208
206
  const client = new ApiClient();
209
207
  try {
210
- const result = await client.updatePrompt(resolvedAppId, promptId, payload);
208
+ // The soft delete: no `hard`, so the configs and stored prompt bodies
209
+ // survive and every execution row keeps resolving.
210
+ const result = await client.deletePrompt(resolvedAppId, id);
211
211
  if (options.json) {
212
212
  json(result);
213
213
  return;
214
214
  }
215
- success("Prompt updated.");
215
+ success(`Prompt archived: ${id}`);
216
+ info("Its configs and executions keep resolving. The row still holds its key — reclaim it by removing its file and running a confirmed `primitive config push --prune`.");
216
217
  }
217
218
  catch (err) {
218
219
  error(err.message);
219
220
  process.exit(1);
220
221
  }
221
222
  });
222
- // Delete prompt
223
223
  prompts
224
- .command("delete")
225
- .description("Delete or archive a prompt")
224
+ .command("get")
225
+ .description("Get prompt details")
226
226
  .argument("<prompt-id>", "Prompt ID")
227
227
  .option("--app <app-id>", "App ID (uses current app if not specified)")
228
- .option("--hard", "Permanently delete instead of archive")
229
- .option("-y, --yes", "Skip confirmation prompt")
228
+ .option("--json", "Output as JSON")
230
229
  .action(async (promptId, options) => {
231
230
  const resolvedAppId = resolveAppId(undefined, options);
232
- if (!options.yes) {
233
- const action = options.hard ? "permanently delete" : "archive";
234
- const inquirer = await import("inquirer");
235
- const { confirm } = await inquirer.default.prompt([
236
- {
237
- type: "confirm",
238
- name: "confirm",
239
- message: `Are you sure you want to ${action} prompt ${promptId}?`,
240
- default: false,
241
- },
242
- ]);
243
- if (!confirm) {
244
- info("Cancelled.");
245
- return;
246
- }
247
- }
248
231
  const client = new ApiClient();
249
232
  try {
250
- await client.deletePrompt(resolvedAppId, promptId, options.hard);
251
- success(`Prompt ${options.hard ? "deleted" : "archived"}.`);
233
+ const result = await client.getPrompt(resolvedAppId, promptId);
234
+ if (options.json) {
235
+ json(result);
236
+ return;
237
+ }
238
+ printResult("Prompt ID", result.promptId);
239
+ printResult("Key", result.promptKey);
240
+ printResult("Name", result.displayName);
241
+ printResult("Description", result.description);
242
+ printResult("Status", formatStatus(result.status));
243
+ // #2652: no rule means every non-admin execution is denied. Print that
244
+ // in words — a blank or "-" would read as "unrestricted" and hide a
245
+ // prompt that was never given a rule.
246
+ printResult("Access Rule", formatAccessRuleForDisplay(result.accessRule, "prompt"));
247
+ printResult("Active Config", result.activeConfigId || "-");
248
+ if (result.configs && result.configs.length > 0) {
249
+ divider();
250
+ info(`Configs (${result.configs.length}):`);
251
+ console.log(formatTable(result.configs, [
252
+ { header: "ID", key: "configId", format: formatId },
253
+ { header: "NAME", key: "configName" },
254
+ { header: "MODEL", key: "model" },
255
+ { header: "STATUS", key: "status", format: formatStatus },
256
+ ]));
257
+ }
252
258
  }
253
259
  catch (err) {
254
260
  error(err.message);
255
261
  process.exit(1);
256
262
  }
257
263
  });
258
- // Execute prompt
259
264
  prompts
260
265
  .command("execute")
261
266
  .description("Execute a prompt with variables")
@@ -289,7 +294,7 @@ Examples:
289
294
  if (result.success) {
290
295
  success("Execution successful");
291
296
  keyValue("Duration", formatDuration(result.metrics?.durationMs));
292
- keyValue("Tokens", `${result.metrics?.inputTokens || 0} in / ${result.metrics?.outputTokens || 0} out`);
297
+ keyValue("Tokens", formatTokens(result.metrics));
293
298
  console.log("\nOutput:");
294
299
  console.log(result.output);
295
300
  }
@@ -397,8 +402,25 @@ Examples:
397
402
  process.exit(1);
398
403
  }
399
404
  });
400
- // Configs subcommand
401
- const configs = prompts.command("configs").description("Manage prompt configs");
405
+ // Configs subcommand — reads only. A prompt's configs are `[[configs]]`
406
+ // entries in its own TOML file (#2645), so the verbs that used to write them
407
+ // were a second writer for state the file already owns.
408
+ const configs = prompts
409
+ .command("configs")
410
+ .description("Inspect a prompt's configs (authored in prompts/<key>.toml)")
411
+ .addHelpText("after", `
412
+ A prompt's configs are the \`[[configs]]\` entries of prompts/<key>.toml. Add,
413
+ edit, retire (\`status = "archived"\`) or mark one live (\`active = true\`) in that
414
+ file, then apply it:
415
+
416
+ $ primitive config push --only prompt/<key>
417
+
418
+ That per-config \`status\` says which VERSION is retired and is yours to author.
419
+ The PROMPT's own availability is server-owned and has no TOML key: use
420
+ 'primitive prompts disable' / 'enable'.
421
+
422
+ Duplicating a config is copying its \`[[configs]]\` block and renaming it.
423
+ `);
402
424
  // List configs
403
425
  configs
404
426
  .command("list")
@@ -412,7 +434,7 @@ Examples:
412
434
  try {
413
435
  const { items } = await client.listPromptConfigs(resolvedAppId, promptId);
414
436
  if (options.json) {
415
- json(items);
437
+ json(wholeListEnvelope(items));
416
438
  return;
417
439
  }
418
440
  if (!items || items.length === 0) {
@@ -432,154 +454,51 @@ Examples:
432
454
  process.exit(1);
433
455
  }
434
456
  });
435
- // Create config
457
+ // Get one config. `list` says which configs exist; this says what one of
458
+ // them actually runs — the body, provider and model the server would use.
459
+ // With `prompts configs create/update` gone, this is where that content is
460
+ // read back from.
436
461
  configs
437
- .command("create")
438
- .description("Create a new config for a prompt")
439
- .argument("<prompt-id>", "Prompt ID")
462
+ .command("get")
463
+ .description("Show one config of a prompt as the server has it")
464
+ .argument("<prompt>", "Prompt ID or key")
465
+ .argument("<config>", "Config ID or name")
440
466
  .option("--app <app-id>", "App ID (uses current app if not specified)")
441
- .option("--name <name>", "Config name")
442
- .option("--description <desc>", "Description")
443
- .option("--provider <provider>", "LLM provider: openrouter, gemini", "openrouter")
444
- .option("--model <model>", "Model name")
445
- .option("--system-prompt <prompt>", "System prompt")
446
- .option("--user-template <template>", "User prompt template")
447
- .option("--temperature <temp>", "Temperature (0-1)")
448
- .option("--max-tokens <n>", "Max output tokens")
449
- .option("--output-format <format>", "Output format: text, json")
450
467
  .option("--json", "Output as JSON")
451
- .action(async (promptId, options) => {
468
+ .action(async (prompt, config, options) => {
452
469
  const resolvedAppId = resolveAppId(undefined, options);
453
- if (!options.name || !options.model || !options.userTemplate) {
454
- error("Required: --name, --model, --user-template");
455
- process.exit(1);
456
- }
457
- const payload = {
458
- configName: options.name,
459
- description: options.description,
460
- provider: options.provider,
461
- model: options.model,
462
- systemPrompt: options.systemPrompt,
463
- userPromptTemplate: options.userTemplate,
464
- temperature: options.temperature,
465
- maxTokens: options.maxTokens ? parseInt(options.maxTokens) : undefined,
466
- outputFormat: options.outputFormat,
467
- };
468
470
  const client = new ApiClient();
469
471
  try {
470
- const result = await client.createPromptConfig(resolvedAppId, promptId, payload);
471
- if (options.json) {
472
- json(result);
473
- return;
472
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
473
+ const { items } = await client.listPromptConfigs(resolvedAppId, promptId);
474
+ const summary = (items ?? []).find((c) => c.configId === config || c.configName === config);
475
+ if (!summary) {
476
+ error(`Prompt '${prompt}' has no config '${config}'. Run 'primitive prompts configs list ${prompt}' to see them.`);
477
+ process.exit(1);
474
478
  }
475
- success(`Config created: ${result.configName}`);
476
- keyValue("Config ID", result.configId);
477
- }
478
- catch (err) {
479
- error(err.message);
480
- process.exit(1);
481
- }
482
- });
483
- // Activate config
484
- configs
485
- .command("activate")
486
- .description("Set a config as the active config for a prompt")
487
- .argument("<prompt-id>", "Prompt ID")
488
- .argument("<config-id>", "Config ID")
489
- .option("--app <app-id>", "App ID (uses current app if not specified)")
490
- .action(async (promptId, configId, options) => {
491
- const resolvedAppId = resolveAppId(undefined, options);
492
- const client = new ApiClient();
493
- try {
494
- await client.activatePromptConfig(resolvedAppId, promptId, configId);
495
- success(`Config ${configId} activated.`);
496
- }
497
- catch (err) {
498
- error(err.message);
499
- process.exit(1);
500
- }
501
- });
502
- // Duplicate config
503
- configs
504
- .command("duplicate")
505
- .description("Duplicate a config")
506
- .argument("<prompt-id>", "Prompt ID")
507
- .argument("<config-id>", "Config ID to duplicate")
508
- .option("--app <app-id>", "App ID (uses current app if not specified)")
509
- .option("--name <name>", "New config name")
510
- .option("--json", "Output as JSON")
511
- .action(async (promptId, configId, options) => {
512
- const resolvedAppId = resolveAppId(undefined, options);
513
- const client = new ApiClient();
514
- try {
515
- const result = await client.duplicatePromptConfig(resolvedAppId, promptId, configId, {
516
- configName: options.name,
517
- });
479
+ const full = (await client.getPromptConfig(resolvedAppId, promptId, summary.configId)) ??
480
+ summary;
518
481
  if (options.json) {
519
- json(result);
482
+ json(full);
520
483
  return;
521
484
  }
522
- success(`Config duplicated: ${result.configName}`);
523
- keyValue("New Config ID", result.configId);
524
- }
525
- catch (err) {
526
- error(err.message);
527
- process.exit(1);
528
- }
529
- });
530
- // Update config
531
- configs
532
- .command("update")
533
- .description("Update a config")
534
- .argument("<prompt-id>", "Prompt ID")
535
- .argument("<config-id>", "Config ID")
536
- .option("--app <app-id>", "App ID (uses current app if not specified)")
537
- .option("--name <name>", "Config name")
538
- .option("--description <desc>", "Description")
539
- .option("--provider <provider>", "LLM provider: openrouter, gemini")
540
- .option("--model <model>", "Model name")
541
- .option("--system-prompt <prompt>", "System prompt")
542
- .option("--user-template <template>", "User prompt template")
543
- .option("--temperature <temp>", "Temperature (0-1)")
544
- .option("--max-tokens <n>", "Max output tokens")
545
- .option("--output-format <format>", "Output format: text, json")
546
- .option("--status <status>", "Status: active, archived")
547
- .option("--json", "Output as JSON")
548
- .action(async (promptId, configId, options) => {
549
- const resolvedAppId = resolveAppId(undefined, options);
550
- const payload = {};
551
- if (options.name)
552
- payload.configName = options.name;
553
- if (options.description)
554
- payload.description = options.description;
555
- if (options.provider)
556
- payload.provider = options.provider;
557
- if (options.model)
558
- payload.model = options.model;
559
- if (options.systemPrompt)
560
- payload.systemPrompt = options.systemPrompt;
561
- if (options.userTemplate)
562
- payload.userPromptTemplate = options.userTemplate;
563
- if (options.temperature)
564
- payload.temperature = options.temperature;
565
- if (options.maxTokens)
566
- payload.maxTokens = parseInt(options.maxTokens);
567
- if (options.outputFormat)
568
- payload.outputFormat = options.outputFormat;
569
- if (options.status)
570
- payload.status = options.status;
571
- if (Object.keys(payload).length === 0) {
572
- error("No update options specified.");
573
- process.exit(1);
574
- }
575
- const client = new ApiClient();
576
- try {
577
- const result = await client.updatePromptConfig(resolvedAppId, promptId, configId, payload);
578
- if (options.json) {
579
- json(result);
580
- return;
485
+ printResult("Config ID", full.configId);
486
+ printResult("Name", full.configName);
487
+ printResult("Status", formatStatus(full.status));
488
+ printResult("Provider", full.provider || "-");
489
+ printResult("Model", full.model || "-");
490
+ if (full.description)
491
+ printResult("Description", full.description);
492
+ if (full.systemPrompt) {
493
+ divider();
494
+ info("System prompt:");
495
+ console.log(full.systemPrompt);
496
+ }
497
+ if (full.userPrompt) {
498
+ divider();
499
+ info("User prompt:");
500
+ console.log(full.userPrompt);
581
501
  }
582
- success("Config updated.");
583
502
  }
584
503
  catch (err) {
585
504
  error(err.message);
@@ -591,33 +510,51 @@ Examples:
591
510
  // ============================================
592
511
  const tests = prompts
593
512
  .command("tests")
594
- .description("Manage and run prompt test cases")
513
+ .description("Run and inspect prompt test cases")
595
514
  .addHelpText("after", `
515
+ Test cases are authored in TOML at prompts/<key>.tests/<name>.toml and applied
516
+ with \`primitive config push\` (issue #2769); \`config fields prompt\` lists the
517
+ fields. Deleting a case is removing its file and running \`config push --prune\`.
518
+
519
+ Every verb here takes the prompt's KEY or its ID (#3249). An identifier that
520
+ names no prompt in the app fails; "No test cases found." only ever means the
521
+ prompt exists and has no cases.
522
+
596
523
  Examples:
597
- $ primitive prompts tests list <prompt-id>
598
- $ primitive prompts tests create <prompt-id> --name "Basic test" --vars '{"input":"hello"}'
599
- $ primitive prompts tests run <prompt-id> <test-case-id>
600
- $ primitive prompts tests run-all <prompt-id>
601
- $ primitive prompts tests run-all <prompt-id> --test-cases 01ABC,01DEF,01GHI
524
+ $ primitive prompts tests list <prompt>
525
+ $ primitive prompts tests run <prompt> <test-case-id>
526
+ $ primitive prompts tests run-all <prompt>
527
+ $ primitive prompts tests run-all <prompt> --test-cases 01ABC,01DEF,01GHI
602
528
  `);
603
529
  // List test cases
604
530
  tests
605
531
  .command("list")
606
532
  .description("List test cases for a prompt")
607
- .argument("<prompt-id>", "Prompt ID")
533
+ .argument("<prompt>", "Prompt key or ID")
608
534
  .option("--app <app-id>", "App ID (uses current app if not specified)")
535
+ .addOption(pageLimitOption())
536
+ .addOption(pageCursorOption())
609
537
  .option("--json", "Output as JSON")
610
- .action(async (promptId, options) => {
538
+ .action(async (prompt, options) => {
539
+ const limit = parsePageLimit(options.limit);
611
540
  const resolvedAppId = resolveAppId(undefined, options);
612
541
  const client = new ApiClient();
613
542
  try {
614
- const { items } = await client.listTestCases(resolvedAppId, "prompt", promptId);
543
+ // #3249 — resolve BEFORE listing: the test-case endpoint answers an
544
+ // unknown block with an empty page, so an unresolved identifier would
545
+ // print "No test cases found." and exit 0.
546
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
547
+ const page = normalizeCliListEnvelope(await client.listTestCases(resolvedAppId, "prompt", promptId, {
548
+ limit,
549
+ cursor: options.cursor,
550
+ }));
551
+ const items = page.items;
615
552
  if (options.json) {
616
- json(items);
553
+ json(page);
617
554
  return;
618
555
  }
619
- if (!items || items.length === 0) {
620
- info("No test cases found.");
556
+ if (items.length === 0) {
557
+ printEmptyPage(page, "test cases");
621
558
  return;
622
559
  }
623
560
  console.log(formatTable(items, [
@@ -626,79 +563,7 @@ Examples:
626
563
  { header: "CONFIG", key: "configId", format: (v) => v ? formatId(v) : "-" },
627
564
  { header: "CREATED", key: "createdAt", format: formatDate },
628
565
  ]));
629
- }
630
- catch (err) {
631
- error(err.message);
632
- process.exit(1);
633
- }
634
- });
635
- // Create test case
636
- tests
637
- .command("create")
638
- .description("Create a test case for a prompt")
639
- .argument("<prompt-id>", "Prompt ID")
640
- .option("--app <app-id>", "App ID (uses current app if not specified)")
641
- .option("--name <name>", "Test case name (required)")
642
- .option("--vars <json>", "Input variables as JSON (required)")
643
- .option("--pattern <regex>", "Expected output pattern (regex)")
644
- .option("--contains <json>", "Expected strings to contain (JSON array)")
645
- .option("--json-subset <json>", "Expected JSON subset to match")
646
- .option("--config <config-id>", "Config ID to use for this test")
647
- .option("--evaluator-prompt <prompt-id>", "Evaluator prompt ID")
648
- .option("--evaluator-config <config-id>", "Evaluator config ID")
649
- .option("--json", "Output as JSON")
650
- .action(async (promptId, options) => {
651
- const resolvedAppId = resolveAppId(undefined, options);
652
- if (!options.name || !options.vars) {
653
- error("Required: --name and --vars");
654
- process.exit(1);
655
- }
656
- let inputVariables;
657
- try {
658
- inputVariables = JSON.parse(options.vars);
659
- }
660
- catch {
661
- error("Invalid JSON in --vars");
662
- process.exit(1);
663
- }
664
- let expectedOutputContains;
665
- if (options.contains) {
666
- try {
667
- expectedOutputContains = JSON.parse(options.contains);
668
- }
669
- catch {
670
- error("Invalid JSON in --contains");
671
- process.exit(1);
672
- }
673
- }
674
- let expectedJsonSubset;
675
- if (options.jsonSubset) {
676
- try {
677
- expectedJsonSubset = JSON.parse(options.jsonSubset);
678
- }
679
- catch {
680
- error("Invalid JSON in --json-subset");
681
- process.exit(1);
682
- }
683
- }
684
- const client = new ApiClient();
685
- try {
686
- const result = await client.createTestCase(resolvedAppId, "prompt", promptId, {
687
- name: options.name,
688
- inputVariables,
689
- expectedOutputPattern: options.pattern,
690
- expectedOutputContains,
691
- expectedJsonSubset,
692
- configId: options.config,
693
- evaluatorPromptId: options.evaluatorPrompt,
694
- evaluatorConfigId: options.evaluatorConfig,
695
- });
696
- if (options.json) {
697
- json(result);
698
- return;
699
- }
700
- success(`Test case created: ${result.name}`);
701
- keyValue("Test Case ID", result.testCaseId);
566
+ printPageHint(page);
702
567
  }
703
568
  catch (err) {
704
569
  error(err.message);
@@ -709,23 +574,24 @@ Examples:
709
574
  tests
710
575
  .command("get")
711
576
  .description("Get test case details")
712
- .argument("<prompt-id>", "Prompt ID")
577
+ .argument("<prompt>", "Prompt key or ID")
713
578
  .argument("<test-case-id>", "Test Case ID")
714
579
  .option("--app <app-id>", "App ID (uses current app if not specified)")
715
580
  .option("--json", "Output as JSON")
716
- .action(async (promptId, testCaseId, options) => {
581
+ .action(async (prompt, testCaseId, options) => {
717
582
  const resolvedAppId = resolveAppId(undefined, options);
718
583
  const client = new ApiClient();
719
584
  try {
585
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
720
586
  const result = await client.getTestCase(resolvedAppId, "prompt", promptId, testCaseId);
721
587
  if (options.json) {
722
588
  json(result);
723
589
  return;
724
590
  }
725
- keyValue("Test Case ID", result.testCaseId);
726
- keyValue("Name", result.name);
727
- keyValue("Config ID", result.configId || "-");
728
- keyValue("Created", formatDate(result.createdAt));
591
+ printResult("Test Case ID", result.testCaseId);
592
+ printResult("Name", result.name);
593
+ printResult("Config ID", result.configId || "-");
594
+ printResult("Created", formatDate(result.createdAt));
729
595
  divider();
730
596
  info("Input Variables:");
731
597
  try {
@@ -737,7 +603,7 @@ Examples:
737
603
  }
738
604
  if (result.expectedOutputPattern) {
739
605
  divider();
740
- keyValue("Expected Pattern", result.expectedOutputPattern);
606
+ printResult("Expected Pattern", result.expectedOutputPattern);
741
607
  }
742
608
  if (result.expectedOutputContains) {
743
609
  divider();
@@ -761,126 +627,10 @@ Examples:
761
627
  }
762
628
  if (result.evaluatorPromptId) {
763
629
  divider();
764
- keyValue("Evaluator Prompt", result.evaluatorPromptId);
765
- keyValue("Evaluator Config", result.evaluatorConfigId || "-");
766
- }
767
- }
768
- catch (err) {
769
- error(err.message);
770
- process.exit(1);
771
- }
772
- });
773
- // Update test case
774
- tests
775
- .command("update")
776
- .description("Update a test case")
777
- .argument("<prompt-id>", "Prompt ID")
778
- .argument("<test-case-id>", "Test Case ID")
779
- .option("--app <app-id>", "App ID (uses current app if not specified)")
780
- .option("--name <name>", "Test case name")
781
- .option("--vars <json>", "Input variables as JSON")
782
- .option("--pattern <regex>", "Expected output pattern (regex)")
783
- .option("--contains <json>", "Expected strings to contain (JSON array)")
784
- .option("--json-subset <json>", "Expected JSON subset to match")
785
- .option("--config <config-id>", "Config ID to use for this test")
786
- .option("--clear-pattern", "Clear expected pattern")
787
- .option("--clear-contains", "Clear expected contains")
788
- .option("--clear-json-subset", "Clear expected JSON subset")
789
- .option("--json", "Output as JSON")
790
- .action(async (promptId, testCaseId, options) => {
791
- const resolvedAppId = resolveAppId(undefined, options);
792
- const payload = {};
793
- if (options.name)
794
- payload.name = options.name;
795
- if (options.vars) {
796
- try {
797
- payload.inputVariables = JSON.parse(options.vars);
798
- }
799
- catch {
800
- error("Invalid JSON in --vars");
801
- process.exit(1);
802
- }
803
- }
804
- if (options.clearPattern) {
805
- payload.expectedOutputPattern = null;
806
- }
807
- else if (options.pattern) {
808
- payload.expectedOutputPattern = options.pattern;
809
- }
810
- if (options.clearContains) {
811
- payload.expectedOutputContains = null;
812
- }
813
- else if (options.contains) {
814
- try {
815
- payload.expectedOutputContains = JSON.parse(options.contains);
816
- }
817
- catch {
818
- error("Invalid JSON in --contains");
819
- process.exit(1);
820
- }
821
- }
822
- if (options.clearJsonSubset) {
823
- payload.expectedJsonSubset = null;
824
- }
825
- else if (options.jsonSubset) {
826
- try {
827
- payload.expectedJsonSubset = JSON.parse(options.jsonSubset);
828
- }
829
- catch {
830
- error("Invalid JSON in --json-subset");
831
- process.exit(1);
832
- }
833
- }
834
- if (options.config)
835
- payload.configId = options.config;
836
- if (Object.keys(payload).length === 0) {
837
- error("No update options specified.");
838
- process.exit(1);
839
- }
840
- const client = new ApiClient();
841
- try {
842
- const result = await client.updateTestCase(resolvedAppId, "prompt", promptId, testCaseId, payload);
843
- if (options.json) {
844
- json(result);
845
- return;
846
- }
847
- success("Test case updated.");
848
- }
849
- catch (err) {
850
- error(err.message);
851
- process.exit(1);
852
- }
853
- });
854
- // Delete test case
855
- tests
856
- .command("delete")
857
- .description("Delete a test case")
858
- .argument("<prompt-id>", "Prompt ID")
859
- .argument("<test-case-id>", "Test Case ID")
860
- .option("--app <app-id>", "App ID (uses current app if not specified)")
861
- .option("-y, --yes", "Skip confirmation prompt")
862
- .action(async (promptId, testCaseId, options) => {
863
- const resolvedAppId = resolveAppId(undefined, options);
864
- if (!options.yes) {
865
- const inquirer = await import("inquirer");
866
- const { confirm } = await inquirer.default.prompt([
867
- {
868
- type: "confirm",
869
- name: "confirm",
870
- message: `Are you sure you want to delete test case ${testCaseId}?`,
871
- default: false,
872
- },
873
- ]);
874
- if (!confirm) {
875
- info("Cancelled.");
876
- return;
630
+ printResult("Evaluator Prompt", result.evaluatorPromptId);
631
+ printResult("Evaluator Config", result.evaluatorConfigId || "-");
877
632
  }
878
633
  }
879
- const client = new ApiClient();
880
- try {
881
- await client.deleteTestCase(resolvedAppId, "prompt", promptId, testCaseId);
882
- success("Test case deleted.");
883
- }
884
634
  catch (err) {
885
635
  error(err.message);
886
636
  process.exit(1);
@@ -890,24 +640,22 @@ Examples:
890
640
  tests
891
641
  .command("run")
892
642
  .description("Run a single test case")
893
- .argument("<prompt-id>", "Prompt ID")
643
+ .argument("<prompt>", "Prompt key or ID")
894
644
  .argument("<test-case-id>", "Test Case ID")
895
645
  .option("--app <app-id>", "App ID (uses current app if not specified)")
896
646
  .option("--config <config-id>", "Override config ID")
897
647
  .option("--json", "Output as JSON")
898
- .action(async (promptId, testCaseId, options) => {
648
+ .action(async (prompt, testCaseId, options) => {
899
649
  const resolvedAppId = resolveAppId(undefined, options);
900
650
  const client = new ApiClient();
901
651
  try {
652
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
902
653
  // Get test case to retrieve its variables
903
654
  const testCase = await client.getTestCase(resolvedAppId, "prompt", promptId, testCaseId);
904
- let variables = {};
905
- try {
906
- variables = JSON.parse(testCase.inputVariables || "{}");
907
- }
908
- catch {
909
- // Use empty object if parsing fails
910
- }
655
+ // #3158 — the case's stored input IS the run's input, and the server
656
+ // hands it back parsed; `JSON.parse` on that object threw and the run
657
+ // went ahead with `{}`, disagreeing with `run-all` over the same case.
658
+ const variables = requireTestCaseInputVariables(testCase.inputVariables, testCaseId);
911
659
  const result = await client.executeBlockTest(resolvedAppId, "prompt", promptId, {
912
660
  variables,
913
661
  testCaseId,
@@ -925,7 +673,7 @@ Examples:
925
673
  error("Test FAILED");
926
674
  }
927
675
  keyValue("Duration", formatDuration(result.metrics?.durationMs));
928
- keyValue("Tokens", `${result.metrics?.inputTokens || 0} in / ${result.metrics?.outputTokens || 0} out`);
676
+ keyValue("Tokens", formatTokens(result.metrics));
929
677
  if (result.verification) {
930
678
  divider();
931
679
  info("Verification:");
@@ -962,15 +710,18 @@ Examples:
962
710
  tests
963
711
  .command("run-all")
964
712
  .description("Run all test cases for a prompt, or a specific subset")
965
- .argument("<prompt-id>", "Prompt ID")
713
+ .argument("<prompt>", "Prompt key or ID")
966
714
  .option("--app <app-id>", "App ID (uses current app if not specified)")
967
715
  .option("--config <config-id>", "Override config ID for all tests")
968
716
  .option("--test-cases <ids>", "Comma-separated list of test case IDs to run (runs only these)")
969
717
  .option("--json", "Output as JSON")
970
- .action(async (promptId, options) => {
718
+ .action(async (prompt, options) => {
971
719
  const resolvedAppId = resolveAppId(undefined, options);
972
720
  const client = new ApiClient();
973
721
  try {
722
+ // #3249 — resolve first, so `Total Tests: 0` can only ever mean the
723
+ // prompt exists and has no cases, never that the identifier was wrong.
724
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
974
725
  // Parse test case IDs if provided
975
726
  let testCaseIds;
976
727
  if (options.testCases) {
@@ -1028,15 +779,16 @@ Examples:
1028
779
  tests
1029
780
  .command("runs")
1030
781
  .description("List recent test runs for a prompt")
1031
- .argument("<prompt-id>", "Prompt ID")
782
+ .argument("<prompt>", "Prompt key or ID")
1032
783
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1033
784
  .option("--limit <n>", "Maximum number of runs to show", "20")
1034
785
  .option("--group <group>", "Filter by comparison group")
1035
786
  .option("--json", "Output as JSON")
1036
- .action(async (promptId, options) => {
787
+ .action(async (prompt, options) => {
1037
788
  const resolvedAppId = resolveAppId(undefined, options);
1038
789
  const client = new ApiClient();
1039
790
  try {
791
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
1040
792
  const { items } = await client.listTestRuns(resolvedAppId, "prompt", promptId, {
1041
793
  limit: parseInt(options.limit),
1042
794
  comparisonGroup: options.group,
@@ -1078,24 +830,25 @@ Examples:
1078
830
  .description("Run tests in parallel using workflow-based execution")
1079
831
  .addHelpText("after", `
1080
832
  Examples:
1081
- $ primitive prompts tests batch start <prompt-id>
1082
- $ primitive prompts tests batch start <prompt-id> --test-cases 01ABC,01DEF
1083
- $ primitive prompts tests batch status <prompt-id> <batch-id>
1084
- $ primitive prompts tests batch cancel <prompt-id> <batch-id>
833
+ $ primitive prompts tests batch start <prompt>
834
+ $ primitive prompts tests batch start <prompt> --test-cases 01ABC,01DEF
835
+ $ primitive prompts tests batch status <prompt> <batch-id>
836
+ $ primitive prompts tests batch cancel <prompt> <batch-id>
1085
837
  `);
1086
838
  // Start batch tests
1087
839
  batch
1088
840
  .command("start")
1089
841
  .description("Start parallel batch test execution")
1090
- .argument("<prompt-id>", "Prompt ID")
842
+ .argument("<prompt>", "Prompt key or ID")
1091
843
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1092
844
  .option("--config <config-id>", "Override config ID for all tests")
1093
845
  .option("--test-cases <ids>", "Comma-separated list of test case IDs to run")
1094
846
  .option("--json", "Output as JSON")
1095
- .action(async (promptId, options) => {
847
+ .action(async (prompt, options) => {
1096
848
  const resolvedAppId = resolveAppId(undefined, options);
1097
849
  const client = new ApiClient();
1098
850
  try {
851
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
1099
852
  let testCaseIds;
1100
853
  if (options.testCases) {
1101
854
  testCaseIds = options.testCases.split(",").map((id) => id.trim()).filter(Boolean);
@@ -1132,27 +885,37 @@ Examples:
1132
885
  batch
1133
886
  .command("status")
1134
887
  .description("Get status of a batch test execution")
1135
- .argument("<prompt-id>", "Prompt ID")
888
+ .argument("<prompt>", "Prompt key or ID")
1136
889
  .argument("<batch-id>", "Batch ID")
1137
890
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1138
891
  .option("--wait", "Wait for completion (polls every 2 seconds)")
1139
892
  .option("--json", "Output as JSON")
1140
- .action(async (promptId, batchId, options) => {
893
+ .action(async (prompt, batchId, options) => {
1141
894
  const resolvedAppId = resolveAppId(undefined, options);
1142
895
  const client = new ApiClient();
1143
896
  try {
897
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
1144
898
  const fetchStatus = async () => {
1145
899
  return client.getBatchTestStatus(resolvedAppId, "prompt", promptId, batchId);
1146
900
  };
1147
901
  let result = await fetchStatus();
1148
902
  if (options.wait && result.status === "running") {
1149
903
  info("Waiting for batch completion...");
1150
- while (result.status === "running") {
1151
- await new Promise((r) => setTimeout(r, 2000));
1152
- result = await fetchStatus();
1153
- process.stdout.write(`\r Completed: ${result.completed}/${result.results?.length || 0} `);
1154
- }
1155
- console.log();
904
+ // #1969 — shared poll primitive (delay-then-probe every 2s, honoring
905
+ // an AbortSignal). `onError: "throw"` preserves the prior loop's
906
+ // fail-fast behavior: a throwing fetchStatus (expired token, deleted
907
+ // batch → 401/404) propagates to the command-level catch below and
908
+ // exits non-zero, rather than spinning forever with a frozen progress
909
+ // line. (The follow/watch tail loops legitimately keep "continue".)
910
+ const controller = new AbortController();
911
+ result = await pollUntil(fetchStatus, (r) => r.status !== "running", {
912
+ intervalMs: 2000,
913
+ signal: controller.signal,
914
+ onError: "throw",
915
+ // Progress goes to stderr so it can't corrupt JSON on stdout under --json.
916
+ onValue: (r) => progress(` Completed: ${r.completed}/${r.results?.length || 0} `),
917
+ });
918
+ progressEnd();
1156
919
  }
1157
920
  if (options.json) {
1158
921
  json(result);
@@ -1188,23 +951,22 @@ Examples:
1188
951
  batch
1189
952
  .command("cancel")
1190
953
  .description("Cancel a running batch test execution")
1191
- .argument("<prompt-id>", "Prompt ID")
954
+ .argument("<prompt>", "Prompt key or ID")
1192
955
  .argument("<batch-id>", "Batch ID")
1193
956
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1194
957
  .option("-y, --yes", "Skip confirmation prompt")
1195
958
  .option("--json", "Output as JSON")
1196
- .action(async (promptId, batchId, options) => {
959
+ .action(async (prompt, batchId, options) => {
1197
960
  const resolvedAppId = resolveAppId(undefined, options);
1198
961
  if (!options.yes) {
1199
- const inquirer = await import("inquirer");
1200
- const { confirm } = await inquirer.default.prompt([
1201
- {
1202
- type: "confirm",
1203
- name: "confirm",
1204
- message: `Are you sure you want to cancel batch ${batchId}?`,
1205
- default: false,
1206
- },
1207
- ]);
962
+ let confirm;
963
+ try {
964
+ confirm = await confirmPrompt(`Are you sure you want to cancel batch ${batchId}?`);
965
+ }
966
+ catch (err) {
967
+ error(err.message);
968
+ process.exit(1);
969
+ }
1208
970
  if (!confirm) {
1209
971
  info("Cancelled.");
1210
972
  return;
@@ -1212,6 +974,7 @@ Examples:
1212
974
  }
1213
975
  const client = new ApiClient();
1214
976
  try {
977
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
1215
978
  // First get current status to get instance IDs
1216
979
  const status = await client.getBatchTestStatus(resolvedAppId, "prompt", promptId, batchId);
1217
980
  // Get instance IDs that are still running
@@ -1244,29 +1007,32 @@ Examples:
1244
1007
  // ============================================
1245
1008
  const attachments = tests
1246
1009
  .command("attachments")
1247
- .description("Manage test case file attachments")
1010
+ .description("Inspect test case file attachments")
1248
1011
  .addHelpText("after", `
1012
+ Attachments are authored as files in prompts/<key>.tests/<name>/ and applied
1013
+ with \`primitive config push\` (#2769); removing one and running
1014
+ \`config push --prune\` deletes it server-side. These verbs read server state.
1015
+
1249
1016
  Examples:
1250
- $ primitive prompts tests attachments list <prompt-id> <test-case-id>
1251
- $ primitive prompts tests attachments upload <prompt-id> <test-case-id> ./document.pdf
1252
- $ primitive prompts tests attachments download <prompt-id> <test-case-id> document.pdf
1253
- $ primitive prompts tests attachments delete <prompt-id> <test-case-id> document.pdf
1017
+ $ primitive prompts tests attachments list <prompt> <test-case-id>
1018
+ $ primitive prompts tests attachments download <prompt> <test-case-id> document.pdf
1254
1019
  `);
1255
1020
  // List attachments
1256
1021
  attachments
1257
1022
  .command("list")
1258
1023
  .description("List attachments for a test case")
1259
- .argument("<prompt-id>", "Prompt ID")
1024
+ .argument("<prompt>", "Prompt key or ID")
1260
1025
  .argument("<test-case-id>", "Test Case ID")
1261
1026
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1262
1027
  .option("--json", "Output as JSON")
1263
- .action(async (promptId, testCaseId, options) => {
1028
+ .action(async (prompt, testCaseId, options) => {
1264
1029
  const resolvedAppId = resolveAppId(undefined, options);
1265
1030
  const client = new ApiClient();
1266
1031
  try {
1032
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
1267
1033
  const result = await client.listTestCaseAttachments(resolvedAppId, "prompt", promptId, testCaseId);
1268
1034
  if (options.json) {
1269
- json(result.attachments);
1035
+ json(wholeListEnvelope(result.attachments));
1270
1036
  return;
1271
1037
  }
1272
1038
  if (!result.attachments || result.attachments.length === 0) {
@@ -1288,62 +1054,20 @@ Examples:
1288
1054
  process.exit(1);
1289
1055
  }
1290
1056
  });
1291
- // Upload attachment
1292
- attachments
1293
- .command("upload")
1294
- .description("Upload a file attachment to a test case")
1295
- .argument("<prompt-id>", "Prompt ID")
1296
- .argument("<test-case-id>", "Test Case ID")
1297
- .argument("<file-path>", "Path to the file to upload")
1298
- .option("--app <app-id>", "App ID (uses current app if not specified)")
1299
- .option("--name <name>", "Override the filename")
1300
- .option("--json", "Output as JSON")
1301
- .action(async (promptId, testCaseId, filePath, options) => {
1302
- const resolvedAppId = resolveAppId(undefined, options);
1303
- const client = new ApiClient();
1304
- try {
1305
- // Check file exists and get stats
1306
- const stats = statSync(filePath);
1307
- if (!stats.isFile()) {
1308
- error(`Not a file: ${filePath}`);
1309
- process.exit(1);
1310
- }
1311
- // Check file size (10 MB limit)
1312
- const TEN_MB = 10 * 1024 * 1024;
1313
- if (stats.size > TEN_MB) {
1314
- error(`File too large (max 10MB): ${formatFileSize(stats.size)}`);
1315
- process.exit(1);
1316
- }
1317
- const filename = options.name || basename(filePath);
1318
- const data = readFileSync(filePath);
1319
- const contentType = mimeLookup(filePath) || "application/octet-stream";
1320
- const result = await client.uploadTestCaseAttachment(resolvedAppId, "prompt", promptId, testCaseId, filename, data, contentType);
1321
- if (options.json) {
1322
- json(result);
1323
- return;
1324
- }
1325
- success(`Uploaded: ${filename}`);
1326
- keyValue("Size", formatFileSize(result.attachment.size));
1327
- keyValue("Type", result.attachment.contentType);
1328
- }
1329
- catch (err) {
1330
- error(err.message);
1331
- process.exit(1);
1332
- }
1333
- });
1334
1057
  // Download attachment
1335
1058
  attachments
1336
1059
  .command("download")
1337
1060
  .description("Download a file attachment from a test case")
1338
- .argument("<prompt-id>", "Prompt ID")
1061
+ .argument("<prompt>", "Prompt key or ID")
1339
1062
  .argument("<test-case-id>", "Test Case ID")
1340
1063
  .argument("<filename>", "Name of the file to download")
1341
1064
  .argument("[output-path]", "Output path (defaults to current directory)")
1342
1065
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1343
- .action(async (promptId, testCaseId, filename, outputPath, options) => {
1066
+ .action(async (prompt, testCaseId, filename, outputPath, options) => {
1344
1067
  const resolvedAppId = resolveAppId(undefined, options);
1345
1068
  const client = new ApiClient();
1346
1069
  try {
1070
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
1347
1071
  const result = await client.downloadTestCaseAttachment(resolvedAppId, "prompt", promptId, testCaseId, filename);
1348
1072
  const finalPath = outputPath || filename;
1349
1073
  writeFileSync(finalPath, result.data);
@@ -1356,42 +1080,6 @@ Examples:
1356
1080
  process.exit(1);
1357
1081
  }
1358
1082
  });
1359
- // Delete attachment
1360
- attachments
1361
- .command("delete")
1362
- .description("Delete a file attachment from a test case")
1363
- .argument("<prompt-id>", "Prompt ID")
1364
- .argument("<test-case-id>", "Test Case ID")
1365
- .argument("<filename>", "Name of the file to delete")
1366
- .option("--app <app-id>", "App ID (uses current app if not specified)")
1367
- .option("-y, --yes", "Skip confirmation prompt")
1368
- .action(async (promptId, testCaseId, filename, options) => {
1369
- const resolvedAppId = resolveAppId(undefined, options);
1370
- if (!options.yes) {
1371
- const inquirer = await import("inquirer");
1372
- const { confirm } = await inquirer.default.prompt([
1373
- {
1374
- type: "confirm",
1375
- name: "confirm",
1376
- message: `Are you sure you want to delete attachment "${filename}"?`,
1377
- default: false,
1378
- },
1379
- ]);
1380
- if (!confirm) {
1381
- info("Cancelled.");
1382
- return;
1383
- }
1384
- }
1385
- const client = new ApiClient();
1386
- try {
1387
- await client.deleteTestCaseAttachment(resolvedAppId, "prompt", promptId, testCaseId, filename);
1388
- success(`Deleted: ${filename}`);
1389
- }
1390
- catch (err) {
1391
- error(err.message);
1392
- process.exit(1);
1393
- }
1394
- });
1395
1083
  }
1396
1084
  function formatFileSize(bytes) {
1397
1085
  if (bytes < 1024)