@stoneforge/quarry 0.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 (330) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +160 -0
  3. package/dist/api/index.d.ts +8 -0
  4. package/dist/api/index.d.ts.map +1 -0
  5. package/dist/api/index.js +8 -0
  6. package/dist/api/index.js.map +1 -0
  7. package/dist/api/quarry-api.d.ts +268 -0
  8. package/dist/api/quarry-api.d.ts.map +1 -0
  9. package/dist/api/quarry-api.js +3905 -0
  10. package/dist/api/quarry-api.js.map +1 -0
  11. package/dist/api/types.d.ts +1359 -0
  12. package/dist/api/types.d.ts.map +1 -0
  13. package/dist/api/types.js +204 -0
  14. package/dist/api/types.js.map +1 -0
  15. package/dist/bin/sf.d.ts +3 -0
  16. package/dist/bin/sf.d.ts.map +1 -0
  17. package/dist/bin/sf.js +9 -0
  18. package/dist/bin/sf.js.map +1 -0
  19. package/dist/cli/commands/admin.d.ts +11 -0
  20. package/dist/cli/commands/admin.d.ts.map +1 -0
  21. package/dist/cli/commands/admin.js +465 -0
  22. package/dist/cli/commands/admin.js.map +1 -0
  23. package/dist/cli/commands/alias.d.ts +8 -0
  24. package/dist/cli/commands/alias.d.ts.map +1 -0
  25. package/dist/cli/commands/alias.js +70 -0
  26. package/dist/cli/commands/alias.js.map +1 -0
  27. package/dist/cli/commands/channel.d.ts +13 -0
  28. package/dist/cli/commands/channel.d.ts.map +1 -0
  29. package/dist/cli/commands/channel.js +680 -0
  30. package/dist/cli/commands/channel.js.map +1 -0
  31. package/dist/cli/commands/completion.d.ts +8 -0
  32. package/dist/cli/commands/completion.d.ts.map +1 -0
  33. package/dist/cli/commands/completion.js +87 -0
  34. package/dist/cli/commands/completion.js.map +1 -0
  35. package/dist/cli/commands/config.d.ts +12 -0
  36. package/dist/cli/commands/config.d.ts.map +1 -0
  37. package/dist/cli/commands/config.js +242 -0
  38. package/dist/cli/commands/config.js.map +1 -0
  39. package/dist/cli/commands/crud.d.ts +64 -0
  40. package/dist/cli/commands/crud.d.ts.map +1 -0
  41. package/dist/cli/commands/crud.js +805 -0
  42. package/dist/cli/commands/crud.js.map +1 -0
  43. package/dist/cli/commands/dep.d.ts +16 -0
  44. package/dist/cli/commands/dep.d.ts.map +1 -0
  45. package/dist/cli/commands/dep.js +499 -0
  46. package/dist/cli/commands/dep.js.map +1 -0
  47. package/dist/cli/commands/document.d.ts +12 -0
  48. package/dist/cli/commands/document.d.ts.map +1 -0
  49. package/dist/cli/commands/document.js +1039 -0
  50. package/dist/cli/commands/document.js.map +1 -0
  51. package/dist/cli/commands/embeddings.d.ts +12 -0
  52. package/dist/cli/commands/embeddings.d.ts.map +1 -0
  53. package/dist/cli/commands/embeddings.js +273 -0
  54. package/dist/cli/commands/embeddings.js.map +1 -0
  55. package/dist/cli/commands/entity.d.ts +16 -0
  56. package/dist/cli/commands/entity.d.ts.map +1 -0
  57. package/dist/cli/commands/entity.js +522 -0
  58. package/dist/cli/commands/entity.js.map +1 -0
  59. package/dist/cli/commands/gc.d.ts +10 -0
  60. package/dist/cli/commands/gc.d.ts.map +1 -0
  61. package/dist/cli/commands/gc.js +257 -0
  62. package/dist/cli/commands/gc.js.map +1 -0
  63. package/dist/cli/commands/help.d.ts +11 -0
  64. package/dist/cli/commands/help.d.ts.map +1 -0
  65. package/dist/cli/commands/help.js +169 -0
  66. package/dist/cli/commands/help.js.map +1 -0
  67. package/dist/cli/commands/history.d.ts +9 -0
  68. package/dist/cli/commands/history.d.ts.map +1 -0
  69. package/dist/cli/commands/history.js +160 -0
  70. package/dist/cli/commands/history.js.map +1 -0
  71. package/dist/cli/commands/identity.d.ts +18 -0
  72. package/dist/cli/commands/identity.d.ts.map +1 -0
  73. package/dist/cli/commands/identity.js +698 -0
  74. package/dist/cli/commands/identity.js.map +1 -0
  75. package/dist/cli/commands/inbox.d.ts +20 -0
  76. package/dist/cli/commands/inbox.d.ts.map +1 -0
  77. package/dist/cli/commands/inbox.js +493 -0
  78. package/dist/cli/commands/inbox.js.map +1 -0
  79. package/dist/cli/commands/init.d.ts +20 -0
  80. package/dist/cli/commands/init.d.ts.map +1 -0
  81. package/dist/cli/commands/init.js +144 -0
  82. package/dist/cli/commands/init.js.map +1 -0
  83. package/dist/cli/commands/install.d.ts +9 -0
  84. package/dist/cli/commands/install.d.ts.map +1 -0
  85. package/dist/cli/commands/install.js +200 -0
  86. package/dist/cli/commands/install.js.map +1 -0
  87. package/dist/cli/commands/library.d.ts +12 -0
  88. package/dist/cli/commands/library.d.ts.map +1 -0
  89. package/dist/cli/commands/library.js +665 -0
  90. package/dist/cli/commands/library.js.map +1 -0
  91. package/dist/cli/commands/message.d.ts +11 -0
  92. package/dist/cli/commands/message.d.ts.map +1 -0
  93. package/dist/cli/commands/message.js +608 -0
  94. package/dist/cli/commands/message.js.map +1 -0
  95. package/dist/cli/commands/plan.d.ts +17 -0
  96. package/dist/cli/commands/plan.d.ts.map +1 -0
  97. package/dist/cli/commands/plan.js +698 -0
  98. package/dist/cli/commands/plan.js.map +1 -0
  99. package/dist/cli/commands/playbook.d.ts +12 -0
  100. package/dist/cli/commands/playbook.d.ts.map +1 -0
  101. package/dist/cli/commands/playbook.js +730 -0
  102. package/dist/cli/commands/playbook.js.map +1 -0
  103. package/dist/cli/commands/reset.d.ts +12 -0
  104. package/dist/cli/commands/reset.d.ts.map +1 -0
  105. package/dist/cli/commands/reset.js +306 -0
  106. package/dist/cli/commands/reset.js.map +1 -0
  107. package/dist/cli/commands/serve.d.ts +11 -0
  108. package/dist/cli/commands/serve.d.ts.map +1 -0
  109. package/dist/cli/commands/serve.js +106 -0
  110. package/dist/cli/commands/serve.js.map +1 -0
  111. package/dist/cli/commands/stats.d.ts +8 -0
  112. package/dist/cli/commands/stats.d.ts.map +1 -0
  113. package/dist/cli/commands/stats.js +82 -0
  114. package/dist/cli/commands/stats.js.map +1 -0
  115. package/dist/cli/commands/sync.d.ts +14 -0
  116. package/dist/cli/commands/sync.d.ts.map +1 -0
  117. package/dist/cli/commands/sync.js +370 -0
  118. package/dist/cli/commands/sync.js.map +1 -0
  119. package/dist/cli/commands/task.d.ts +25 -0
  120. package/dist/cli/commands/task.d.ts.map +1 -0
  121. package/dist/cli/commands/task.js +1153 -0
  122. package/dist/cli/commands/task.js.map +1 -0
  123. package/dist/cli/commands/team.d.ts +13 -0
  124. package/dist/cli/commands/team.d.ts.map +1 -0
  125. package/dist/cli/commands/team.js +471 -0
  126. package/dist/cli/commands/team.js.map +1 -0
  127. package/dist/cli/commands/workflow.d.ts +16 -0
  128. package/dist/cli/commands/workflow.d.ts.map +1 -0
  129. package/dist/cli/commands/workflow.js +753 -0
  130. package/dist/cli/commands/workflow.js.map +1 -0
  131. package/dist/cli/completion.d.ts +28 -0
  132. package/dist/cli/completion.d.ts.map +1 -0
  133. package/dist/cli/completion.js +295 -0
  134. package/dist/cli/completion.js.map +1 -0
  135. package/dist/cli/db.d.ts +38 -0
  136. package/dist/cli/db.d.ts.map +1 -0
  137. package/dist/cli/db.js +90 -0
  138. package/dist/cli/db.js.map +1 -0
  139. package/dist/cli/formatter.d.ts +87 -0
  140. package/dist/cli/formatter.d.ts.map +1 -0
  141. package/dist/cli/formatter.js +464 -0
  142. package/dist/cli/formatter.js.map +1 -0
  143. package/dist/cli/index.d.ts +33 -0
  144. package/dist/cli/index.d.ts.map +1 -0
  145. package/dist/cli/index.js +38 -0
  146. package/dist/cli/index.js.map +1 -0
  147. package/dist/cli/parser.d.ts +45 -0
  148. package/dist/cli/parser.d.ts.map +1 -0
  149. package/dist/cli/parser.js +256 -0
  150. package/dist/cli/parser.js.map +1 -0
  151. package/dist/cli/plugin-loader.d.ts +39 -0
  152. package/dist/cli/plugin-loader.d.ts.map +1 -0
  153. package/dist/cli/plugin-loader.js +165 -0
  154. package/dist/cli/plugin-loader.js.map +1 -0
  155. package/dist/cli/plugin-registry.d.ts +50 -0
  156. package/dist/cli/plugin-registry.d.ts.map +1 -0
  157. package/dist/cli/plugin-registry.js +206 -0
  158. package/dist/cli/plugin-registry.js.map +1 -0
  159. package/dist/cli/plugin-types.d.ts +106 -0
  160. package/dist/cli/plugin-types.d.ts.map +1 -0
  161. package/dist/cli/plugin-types.js +103 -0
  162. package/dist/cli/plugin-types.js.map +1 -0
  163. package/dist/cli/runner.d.ts +35 -0
  164. package/dist/cli/runner.d.ts.map +1 -0
  165. package/dist/cli/runner.js +340 -0
  166. package/dist/cli/runner.js.map +1 -0
  167. package/dist/cli/suggest.d.ts +15 -0
  168. package/dist/cli/suggest.d.ts.map +1 -0
  169. package/dist/cli/suggest.js +49 -0
  170. package/dist/cli/suggest.js.map +1 -0
  171. package/dist/cli/types.d.ts +138 -0
  172. package/dist/cli/types.d.ts.map +1 -0
  173. package/dist/cli/types.js +63 -0
  174. package/dist/cli/types.js.map +1 -0
  175. package/dist/config/config.d.ts +86 -0
  176. package/dist/config/config.d.ts.map +1 -0
  177. package/dist/config/config.js +348 -0
  178. package/dist/config/config.js.map +1 -0
  179. package/dist/config/defaults.d.ts +66 -0
  180. package/dist/config/defaults.d.ts.map +1 -0
  181. package/dist/config/defaults.js +114 -0
  182. package/dist/config/defaults.js.map +1 -0
  183. package/dist/config/duration.d.ts +75 -0
  184. package/dist/config/duration.d.ts.map +1 -0
  185. package/dist/config/duration.js +190 -0
  186. package/dist/config/duration.js.map +1 -0
  187. package/dist/config/env.d.ts +67 -0
  188. package/dist/config/env.d.ts.map +1 -0
  189. package/dist/config/env.js +207 -0
  190. package/dist/config/env.js.map +1 -0
  191. package/dist/config/file.d.ts +97 -0
  192. package/dist/config/file.d.ts.map +1 -0
  193. package/dist/config/file.js +365 -0
  194. package/dist/config/file.js.map +1 -0
  195. package/dist/config/index.d.ts +35 -0
  196. package/dist/config/index.d.ts.map +1 -0
  197. package/dist/config/index.js +41 -0
  198. package/dist/config/index.js.map +1 -0
  199. package/dist/config/merge.d.ts +53 -0
  200. package/dist/config/merge.d.ts.map +1 -0
  201. package/dist/config/merge.js +226 -0
  202. package/dist/config/merge.js.map +1 -0
  203. package/dist/config/types.d.ts +257 -0
  204. package/dist/config/types.d.ts.map +1 -0
  205. package/dist/config/types.js +72 -0
  206. package/dist/config/types.js.map +1 -0
  207. package/dist/config/validation.d.ts +55 -0
  208. package/dist/config/validation.d.ts.map +1 -0
  209. package/dist/config/validation.js +251 -0
  210. package/dist/config/validation.js.map +1 -0
  211. package/dist/http/index.d.ts +8 -0
  212. package/dist/http/index.d.ts.map +1 -0
  213. package/dist/http/index.js +12 -0
  214. package/dist/http/index.js.map +1 -0
  215. package/dist/http/sync-handlers.d.ts +162 -0
  216. package/dist/http/sync-handlers.d.ts.map +1 -0
  217. package/dist/http/sync-handlers.js +271 -0
  218. package/dist/http/sync-handlers.js.map +1 -0
  219. package/dist/index.d.ts +25 -0
  220. package/dist/index.d.ts.map +1 -0
  221. package/dist/index.js +69 -0
  222. package/dist/index.js.map +1 -0
  223. package/dist/server/index.d.ts +34 -0
  224. package/dist/server/index.d.ts.map +1 -0
  225. package/dist/server/index.js +3329 -0
  226. package/dist/server/index.js.map +1 -0
  227. package/dist/server/static.d.ts +18 -0
  228. package/dist/server/static.d.ts.map +1 -0
  229. package/dist/server/static.js +71 -0
  230. package/dist/server/static.js.map +1 -0
  231. package/dist/server/ws/broadcaster.d.ts +8 -0
  232. package/dist/server/ws/broadcaster.d.ts.map +1 -0
  233. package/dist/server/ws/broadcaster.js +7 -0
  234. package/dist/server/ws/broadcaster.js.map +1 -0
  235. package/dist/server/ws/handler.d.ts +55 -0
  236. package/dist/server/ws/handler.d.ts.map +1 -0
  237. package/dist/server/ws/handler.js +160 -0
  238. package/dist/server/ws/handler.js.map +1 -0
  239. package/dist/services/blocked-cache.d.ts +297 -0
  240. package/dist/services/blocked-cache.d.ts.map +1 -0
  241. package/dist/services/blocked-cache.js +755 -0
  242. package/dist/services/blocked-cache.js.map +1 -0
  243. package/dist/services/dependency.d.ts +205 -0
  244. package/dist/services/dependency.d.ts.map +1 -0
  245. package/dist/services/dependency.js +566 -0
  246. package/dist/services/dependency.js.map +1 -0
  247. package/dist/services/embeddings/fusion.d.ts +33 -0
  248. package/dist/services/embeddings/fusion.d.ts.map +1 -0
  249. package/dist/services/embeddings/fusion.js +34 -0
  250. package/dist/services/embeddings/fusion.js.map +1 -0
  251. package/dist/services/embeddings/index.d.ts +12 -0
  252. package/dist/services/embeddings/index.d.ts.map +1 -0
  253. package/dist/services/embeddings/index.js +10 -0
  254. package/dist/services/embeddings/index.js.map +1 -0
  255. package/dist/services/embeddings/local-provider.d.ts +31 -0
  256. package/dist/services/embeddings/local-provider.d.ts.map +1 -0
  257. package/dist/services/embeddings/local-provider.js +80 -0
  258. package/dist/services/embeddings/local-provider.js.map +1 -0
  259. package/dist/services/embeddings/service.d.ts +76 -0
  260. package/dist/services/embeddings/service.d.ts.map +1 -0
  261. package/dist/services/embeddings/service.js +153 -0
  262. package/dist/services/embeddings/service.js.map +1 -0
  263. package/dist/services/embeddings/types.d.ts +70 -0
  264. package/dist/services/embeddings/types.d.ts.map +1 -0
  265. package/dist/services/embeddings/types.js +8 -0
  266. package/dist/services/embeddings/types.js.map +1 -0
  267. package/dist/services/id-length-cache.d.ts +156 -0
  268. package/dist/services/id-length-cache.d.ts.map +1 -0
  269. package/dist/services/id-length-cache.js +197 -0
  270. package/dist/services/id-length-cache.js.map +1 -0
  271. package/dist/services/inbox.d.ts +147 -0
  272. package/dist/services/inbox.d.ts.map +1 -0
  273. package/dist/services/inbox.js +428 -0
  274. package/dist/services/inbox.js.map +1 -0
  275. package/dist/services/index.d.ts +10 -0
  276. package/dist/services/index.d.ts.map +1 -0
  277. package/dist/services/index.js +10 -0
  278. package/dist/services/index.js.map +1 -0
  279. package/dist/services/priority-service.d.ts +145 -0
  280. package/dist/services/priority-service.d.ts.map +1 -0
  281. package/dist/services/priority-service.js +272 -0
  282. package/dist/services/priority-service.js.map +1 -0
  283. package/dist/services/search-utils.d.ts +47 -0
  284. package/dist/services/search-utils.d.ts.map +1 -0
  285. package/dist/services/search-utils.js +83 -0
  286. package/dist/services/search-utils.js.map +1 -0
  287. package/dist/sync/hash.d.ts +48 -0
  288. package/dist/sync/hash.d.ts.map +1 -0
  289. package/dist/sync/hash.js +136 -0
  290. package/dist/sync/hash.js.map +1 -0
  291. package/dist/sync/index.d.ts +11 -0
  292. package/dist/sync/index.d.ts.map +1 -0
  293. package/dist/sync/index.js +16 -0
  294. package/dist/sync/index.js.map +1 -0
  295. package/dist/sync/merge.d.ts +80 -0
  296. package/dist/sync/merge.d.ts.map +1 -0
  297. package/dist/sync/merge.js +310 -0
  298. package/dist/sync/merge.js.map +1 -0
  299. package/dist/sync/serialization.d.ts +132 -0
  300. package/dist/sync/serialization.d.ts.map +1 -0
  301. package/dist/sync/serialization.js +306 -0
  302. package/dist/sync/serialization.js.map +1 -0
  303. package/dist/sync/service.d.ts +102 -0
  304. package/dist/sync/service.d.ts.map +1 -0
  305. package/dist/sync/service.js +493 -0
  306. package/dist/sync/service.js.map +1 -0
  307. package/dist/sync/types.d.ts +275 -0
  308. package/dist/sync/types.d.ts.map +1 -0
  309. package/dist/sync/types.js +76 -0
  310. package/dist/sync/types.js.map +1 -0
  311. package/dist/systems/identity.d.ts +479 -0
  312. package/dist/systems/identity.d.ts.map +1 -0
  313. package/dist/systems/identity.js +817 -0
  314. package/dist/systems/identity.js.map +1 -0
  315. package/dist/systems/index.d.ts +8 -0
  316. package/dist/systems/index.d.ts.map +1 -0
  317. package/dist/systems/index.js +29 -0
  318. package/dist/systems/index.js.map +1 -0
  319. package/package.json +121 -0
  320. package/web/assets/charts-vendor-D1YcbGux.js +55 -0
  321. package/web/assets/dnd-vendor-DmxE-_ZH.js +5 -0
  322. package/web/assets/editor-vendor-BxraAWts.js +279 -0
  323. package/web/assets/index-B77vv208.js +341 -0
  324. package/web/assets/index-CF_XnVLh.css +1 -0
  325. package/web/assets/router-vendor-BCKpRBrB.js +41 -0
  326. package/web/assets/ui-vendor-DUahGnbT.js +45 -0
  327. package/web/assets/utils-vendor-CfYKiENT.js +813 -0
  328. package/web/favicon.ico +0 -0
  329. package/web/index.html +23 -0
  330. package/web/logo.png +0 -0
@@ -0,0 +1,805 @@
1
+ /**
2
+ * CRUD Commands - Create, List, Show, Update, Delete operations
3
+ *
4
+ * Provides CLI commands for basic element operations:
5
+ * - create: Create new elements (tasks, etc.)
6
+ * - list: List elements with filtering
7
+ * - show: Show detailed element information
8
+ */
9
+ import { success, failure, ExitCode } from '../types.js';
10
+ import { getFormatter, getOutputMode, getStatusIcon, formatEventsTable } from '../formatter.js';
11
+ import { createTask, createDocument, ContentType, TaskStatus, TaskTypeValue, PlanStatus } from '@stoneforge/core';
12
+ import { createInboxService } from '../../services/inbox.js';
13
+ import { resolveActor, createAPI } from '../db.js';
14
+ export const createOptions = [
15
+ {
16
+ name: 'title',
17
+ short: 't',
18
+ description: 'Title for the element (required for tasks)',
19
+ hasValue: true,
20
+ },
21
+ {
22
+ name: 'name',
23
+ short: 'n',
24
+ description: 'Alias for --title',
25
+ hasValue: true,
26
+ },
27
+ {
28
+ name: 'priority',
29
+ short: 'p',
30
+ description: 'Priority level (1-5, 1=critical)',
31
+ hasValue: true,
32
+ },
33
+ {
34
+ name: 'complexity',
35
+ short: 'c',
36
+ description: 'Complexity level (1-5, 1=trivial)',
37
+ hasValue: true,
38
+ },
39
+ {
40
+ name: 'type',
41
+ description: 'Task type (bug, feature, task, chore)',
42
+ hasValue: true,
43
+ },
44
+ {
45
+ name: 'assignee',
46
+ short: 'a',
47
+ description: 'Assignee entity ID',
48
+ hasValue: true,
49
+ },
50
+ {
51
+ name: 'tag',
52
+ description: 'Add a tag (can be repeated)',
53
+ hasValue: true,
54
+ array: true,
55
+ },
56
+ {
57
+ name: 'plan',
58
+ description: 'Plan ID or name to attach this task to',
59
+ hasValue: true,
60
+ },
61
+ {
62
+ name: 'description',
63
+ short: 'd',
64
+ description: 'Task description (creates a linked document)',
65
+ hasValue: true,
66
+ },
67
+ ];
68
+ export async function createHandler(args, options) {
69
+ // First argument is the element type
70
+ const [elementType] = args;
71
+ if (!elementType) {
72
+ return failure('Usage: sf task create [options]\n\nUse "sf task create --help" for options.', ExitCode.INVALID_ARGUMENTS);
73
+ }
74
+ // Currently only support task creation
75
+ if (elementType !== 'task') {
76
+ return failure(`Unsupported element type: ${elementType}. Currently supported: task`, ExitCode.INVALID_ARGUMENTS);
77
+ }
78
+ // Use --name as alias for --title
79
+ const title = options.title ?? options.name;
80
+ // Validate required options for task
81
+ if (!title) {
82
+ return failure('--title (or --name) is required for creating a task', ExitCode.INVALID_ARGUMENTS);
83
+ }
84
+ // Create command should create the database if it doesn't exist
85
+ const { api, error } = createAPI(options, true);
86
+ if (error) {
87
+ return failure(error, ExitCode.GENERAL_ERROR);
88
+ }
89
+ try {
90
+ const actor = resolveActor(options);
91
+ // Parse priority
92
+ let priority;
93
+ if (options.priority) {
94
+ const p = parseInt(options.priority, 10);
95
+ if (isNaN(p) || p < 1 || p > 5) {
96
+ return failure('Priority must be a number from 1 to 5', ExitCode.VALIDATION);
97
+ }
98
+ priority = p;
99
+ }
100
+ // Parse complexity
101
+ let complexity;
102
+ if (options.complexity) {
103
+ const c = parseInt(options.complexity, 10);
104
+ if (isNaN(c) || c < 1 || c > 5) {
105
+ return failure('Complexity must be a number from 1 to 5', ExitCode.VALIDATION);
106
+ }
107
+ complexity = c;
108
+ }
109
+ let taskType;
110
+ if (options.type) {
111
+ const validTypes = Object.values(TaskTypeValue);
112
+ if (!validTypes.includes(options.type)) {
113
+ return failure(`Invalid task type: ${options.type}. Must be one of: ${validTypes.join(', ')}`, ExitCode.VALIDATION);
114
+ }
115
+ taskType = options.type;
116
+ }
117
+ // Handle tags (may come as array if --tag is specified multiple times)
118
+ let tags;
119
+ if (options.tag) {
120
+ tags = Array.isArray(options.tag) ? options.tag : [options.tag];
121
+ }
122
+ // Handle description: create a document and link it to the task
123
+ let descriptionRef;
124
+ if (options.description) {
125
+ const docInput = {
126
+ content: options.description,
127
+ contentType: ContentType.MARKDOWN,
128
+ createdBy: actor,
129
+ };
130
+ const newDoc = await createDocument(docInput);
131
+ const createdDoc = await api.create(newDoc);
132
+ descriptionRef = createdDoc.id;
133
+ }
134
+ // Create task input (title is guaranteed non-null from validation above)
135
+ const input = {
136
+ title: title,
137
+ createdBy: actor,
138
+ ...(priority !== undefined && { priority }),
139
+ ...(complexity !== undefined && { complexity }),
140
+ ...(taskType !== undefined && { taskType }),
141
+ ...(options.assignee && { assignee: options.assignee }),
142
+ ...(tags && { tags }),
143
+ ...(descriptionRef !== undefined && { descriptionRef }),
144
+ };
145
+ // Create the task
146
+ const task = await createTask(input);
147
+ // The API's create method expects ElementInput which Task satisfies
148
+ const created = await api.create(task);
149
+ // If --plan is provided, attach the task to the plan
150
+ let planWarning;
151
+ if (options.plan) {
152
+ try {
153
+ // First try to find by ID (if it looks like an element ID)
154
+ let plan = null;
155
+ if (options.plan.startsWith('el-') || options.plan.match(/^el[a-z0-9]+$/i)) {
156
+ plan = await api.get(options.plan);
157
+ }
158
+ // If not found by ID, search by title
159
+ if (!plan) {
160
+ const plans = await api.list({ type: 'plan' });
161
+ plan = plans.find((p) => p.title === options.plan) ?? null;
162
+ }
163
+ if (!plan) {
164
+ planWarning = `Warning: Plan not found: ${options.plan}. Task was created but not attached to a plan.`;
165
+ }
166
+ else if (plan.type !== 'plan') {
167
+ planWarning = `Warning: ${options.plan} is not a plan (type: ${plan.type}). Task was created but not attached.`;
168
+ }
169
+ else if (plan.status === PlanStatus.CANCELLED) {
170
+ planWarning = `Warning: Plan ${plan.id} is cancelled. Task was created but not attached.`;
171
+ }
172
+ else {
173
+ await api.addTaskToPlan(created.id, plan.id, { actor });
174
+ }
175
+ }
176
+ catch (attachErr) {
177
+ const attachMessage = attachErr instanceof Error ? attachErr.message : String(attachErr);
178
+ planWarning = `Warning: Failed to attach task to plan: ${attachMessage}. Task was created successfully.`;
179
+ }
180
+ }
181
+ const message = planWarning
182
+ ? `Created task ${created.id}\n${planWarning}`
183
+ : `Created task ${created.id}`;
184
+ return success(created, message);
185
+ }
186
+ catch (err) {
187
+ const message = err instanceof Error ? err.message : String(err);
188
+ return failure(`Failed to create task: ${message}`, ExitCode.GENERAL_ERROR);
189
+ }
190
+ }
191
+ export const createCommand = {
192
+ name: 'create',
193
+ description: 'Create a new element',
194
+ usage: 'sf create <type> [options]',
195
+ help: `Create a new element of the specified type.
196
+
197
+ Supported types:
198
+ task Work item with status, priority, and assignment
199
+
200
+ Task options:
201
+ -t, --title <text> Task title (required)
202
+ -d, --description <text> Task description (creates a linked document)
203
+ -p, --priority <1-5> Priority (1=critical, 5=minimal, default=3)
204
+ -c, --complexity <1-5> Complexity (1=trivial, 5=very complex, default=3)
205
+ --type <type> Task type: bug, feature, task, chore
206
+ -a, --assignee <id> Assignee entity ID
207
+ --tag <tag> Add a tag (can be repeated)
208
+ --plan <id|name> Plan ID or name to attach this task to
209
+
210
+ Examples:
211
+ sf create task --title "Fix login bug" --priority 1 --type bug
212
+ sf create task -t "Add dark mode" --tag ui --tag feature
213
+ sf create task -t "Implement feature X" --plan el-plan123
214
+ sf create task -t "Implement feature X" --plan "My Plan Name"
215
+ sf create task -t "New feature" -d "Detailed description here"`,
216
+ options: createOptions,
217
+ handler: createHandler,
218
+ };
219
+ export const listOptions = [
220
+ {
221
+ name: 'type',
222
+ short: 't',
223
+ description: 'Filter by element type',
224
+ hasValue: true,
225
+ },
226
+ {
227
+ name: 'status',
228
+ short: 's',
229
+ description: 'Filter by status (for tasks)',
230
+ hasValue: true,
231
+ },
232
+ {
233
+ name: 'priority',
234
+ short: 'p',
235
+ description: 'Filter by priority (for tasks)',
236
+ hasValue: true,
237
+ },
238
+ {
239
+ name: 'assignee',
240
+ short: 'a',
241
+ description: 'Filter by assignee (for tasks)',
242
+ hasValue: true,
243
+ },
244
+ {
245
+ name: 'tag',
246
+ description: 'Filter by tag (can be repeated for AND)',
247
+ hasValue: true,
248
+ array: true,
249
+ },
250
+ {
251
+ name: 'limit',
252
+ short: 'l',
253
+ description: 'Maximum number of results',
254
+ hasValue: true,
255
+ },
256
+ {
257
+ name: 'offset',
258
+ short: 'o',
259
+ description: 'Number of results to skip',
260
+ hasValue: true,
261
+ },
262
+ ];
263
+ export async function listHandler(args, options) {
264
+ const { api, error } = createAPI(options);
265
+ if (error) {
266
+ return failure(error, ExitCode.GENERAL_ERROR);
267
+ }
268
+ try {
269
+ // Build filter from options
270
+ const filter = {};
271
+ // Type filter (can also be first positional arg)
272
+ const typeArg = args[0] ?? options.type;
273
+ if (typeArg) {
274
+ filter.type = typeArg;
275
+ }
276
+ // Status filter
277
+ if (options.status) {
278
+ const validStatuses = Object.values(TaskStatus);
279
+ if (!validStatuses.includes(options.status)) {
280
+ return failure(`Invalid status: ${options.status}. Must be one of: ${validStatuses.join(', ')}`, ExitCode.VALIDATION);
281
+ }
282
+ filter.status = options.status;
283
+ }
284
+ // Priority filter
285
+ if (options.priority) {
286
+ const priority = parseInt(options.priority, 10);
287
+ if (isNaN(priority) || priority < 1 || priority > 5) {
288
+ return failure('Priority must be a number from 1 to 5', ExitCode.VALIDATION);
289
+ }
290
+ filter.priority = priority;
291
+ }
292
+ // Assignee filter
293
+ if (options.assignee) {
294
+ filter.assignee = options.assignee;
295
+ }
296
+ // Tag filter
297
+ if (options.tag) {
298
+ filter.tags = Array.isArray(options.tag) ? options.tag : [options.tag];
299
+ }
300
+ // Pagination
301
+ if (options.limit) {
302
+ const limit = parseInt(options.limit, 10);
303
+ if (isNaN(limit) || limit < 1) {
304
+ return failure('Limit must be a positive number', ExitCode.VALIDATION);
305
+ }
306
+ filter.limit = limit;
307
+ }
308
+ if (options.offset) {
309
+ const offset = parseInt(options.offset, 10);
310
+ if (isNaN(offset) || offset < 0) {
311
+ return failure('Offset must be a non-negative number', ExitCode.VALIDATION);
312
+ }
313
+ filter.offset = offset;
314
+ }
315
+ // Query elements
316
+ const result = await api.listPaginated(filter);
317
+ // Format output based on mode
318
+ const mode = getOutputMode(options);
319
+ const formatter = getFormatter(mode);
320
+ if (mode === 'json') {
321
+ return success(result.items);
322
+ }
323
+ if (mode === 'quiet') {
324
+ return success(result.items.map((e) => e.id).join('\n'));
325
+ }
326
+ // Human-readable output
327
+ if (result.items.length === 0) {
328
+ return success(null, 'No elements found');
329
+ }
330
+ // Sort by priority ASC for tasks (P1 is highest priority, comes first)
331
+ const sortedItems = [...result.items].sort((a, b) => {
332
+ const dataA = a;
333
+ const dataB = b;
334
+ const priorityA = typeof dataA.priority === 'number' ? dataA.priority : 999;
335
+ const priorityB = typeof dataB.priority === 'number' ? dataB.priority : 999;
336
+ return priorityA - priorityB;
337
+ });
338
+ // Build table data with priority and assignee columns
339
+ const headers = ['ID', 'TYPE', 'TITLE/NAME', 'STATUS', 'PRIORITY', 'ASSIGNEE', 'CREATED'];
340
+ const rows = sortedItems.map((item) => {
341
+ const data = item;
342
+ const title = data.title ?? data.name ?? '-';
343
+ const status = data.status ? `${getStatusIcon(data.status)} ${data.status}` : '-';
344
+ const priority = typeof data.priority === 'number' ? `P${data.priority}` : '-';
345
+ const assignee = typeof data.assignee === 'string' ? data.assignee : '-';
346
+ const created = item.createdAt.split('T')[0];
347
+ return [item.id, item.type, title, status, priority, assignee, created];
348
+ });
349
+ const table = formatter.table(headers, rows);
350
+ const summary = `\nShowing ${result.items.length} of ${result.total} elements`;
351
+ return success(result.items, table + summary);
352
+ }
353
+ catch (err) {
354
+ const message = err instanceof Error ? err.message : String(err);
355
+ return failure(`Failed to list elements: ${message}`, ExitCode.GENERAL_ERROR);
356
+ }
357
+ }
358
+ export const listCommand = {
359
+ name: 'list',
360
+ description: 'List elements',
361
+ usage: 'sf list [type] [options]',
362
+ help: `List elements with optional filtering.
363
+
364
+ Arguments:
365
+ type Element type to list (task, document, etc.)
366
+
367
+ Options:
368
+ -t, --type <type> Filter by element type
369
+ -s, --status <status> Filter by status (for tasks)
370
+ -p, --priority <1-5> Filter by priority (for tasks)
371
+ -a, --assignee <id> Filter by assignee (for tasks)
372
+ --tag <tag> Filter by tag (can be repeated)
373
+ -l, --limit <n> Maximum results (default: 50)
374
+ -o, --offset <n> Skip first n results
375
+
376
+ Examples:
377
+ sf list task
378
+ sf list task --status open
379
+ sf list --type task --priority 1 --status in_progress
380
+ sf list --tag urgent`,
381
+ options: listOptions,
382
+ handler: listHandler,
383
+ };
384
+ export const showOptions = [
385
+ {
386
+ name: 'events',
387
+ short: 'e',
388
+ description: 'Include recent events/history',
389
+ hasValue: false,
390
+ },
391
+ {
392
+ name: 'events-limit',
393
+ description: 'Maximum number of events to show (default: 10)',
394
+ hasValue: true,
395
+ },
396
+ ];
397
+ export async function showHandler(args, options) {
398
+ const [id] = args;
399
+ if (!id) {
400
+ return failure('Usage: sf show <id>', ExitCode.INVALID_ARGUMENTS);
401
+ }
402
+ const { api, backend, error } = createAPI(options);
403
+ if (error) {
404
+ return failure(error, ExitCode.GENERAL_ERROR);
405
+ }
406
+ try {
407
+ // Handle inbox item IDs (e.g., inbox-abc123)
408
+ if (id.startsWith('inbox-')) {
409
+ const inboxService = createInboxService(backend);
410
+ const inboxItem = inboxService.getInboxItem(id);
411
+ if (!inboxItem) {
412
+ return failure(`Inbox item not found: ${id}`, ExitCode.NOT_FOUND);
413
+ }
414
+ // Fetch the associated message with hydrated content
415
+ const message = await api.get(inboxItem.messageId, {
416
+ hydrate: { content: true }
417
+ });
418
+ // Build a combined result with inbox item info and message content
419
+ const result = {
420
+ ...inboxItem,
421
+ messageContent: message?.content ?? null,
422
+ messageSender: message?.sender ?? null,
423
+ };
424
+ const mode = getOutputMode(options);
425
+ const formatter = getFormatter(mode);
426
+ if (mode === 'json') {
427
+ return success(result);
428
+ }
429
+ if (mode === 'quiet') {
430
+ return success(inboxItem.id);
431
+ }
432
+ // Human-readable output
433
+ const output = formatter.element(result);
434
+ return success(result, output);
435
+ }
436
+ // Get the element
437
+ const element = await api.get(id);
438
+ if (!element) {
439
+ return failure(`Element not found: ${id}`, ExitCode.NOT_FOUND);
440
+ }
441
+ // Check if element is deleted (tombstone)
442
+ const data = element;
443
+ if (data.status === 'tombstone' || data.deletedAt) {
444
+ return failure(`Element not found: ${id}`, ExitCode.NOT_FOUND);
445
+ }
446
+ // Format output based on mode
447
+ const mode = getOutputMode(options);
448
+ const formatter = getFormatter(mode);
449
+ // Get events if requested
450
+ let events;
451
+ if (options.events) {
452
+ const eventsLimit = options['events-limit'] ? parseInt(options['events-limit'], 10) : 10;
453
+ events = await api.getEvents(id, { limit: eventsLimit });
454
+ }
455
+ // Get plan progress if element is a plan
456
+ let planProgress;
457
+ if (element.type === 'plan') {
458
+ try {
459
+ planProgress = await api.getPlanProgress(id);
460
+ }
461
+ catch {
462
+ // Ignore errors fetching progress
463
+ }
464
+ }
465
+ if (mode === 'json') {
466
+ if (events || planProgress) {
467
+ return success({ element, ...(planProgress && { progress: planProgress }), ...(events && { events }) });
468
+ }
469
+ return success(element);
470
+ }
471
+ if (mode === 'quiet') {
472
+ return success(element.id);
473
+ }
474
+ // Human-readable output - format as key-value pairs
475
+ let output = formatter.element(element);
476
+ // Add plan progress if available
477
+ if (planProgress) {
478
+ output += '\n\n--- Task Progress ---\n';
479
+ output += `Total: ${planProgress.totalTasks}\n`;
480
+ output += `Completed: ${planProgress.completedTasks}\n`;
481
+ output += `In Progress: ${planProgress.inProgressTasks}\n`;
482
+ output += `Blocked: ${planProgress.blockedTasks}\n`;
483
+ output += `Ready: ${planProgress.remainingTasks}\n`;
484
+ output += `Progress: ${planProgress.completionPercentage}%`;
485
+ }
486
+ // Add events if requested
487
+ if (events && events.length > 0) {
488
+ output += '\n\n--- Recent Events ---\n';
489
+ output += formatEventsTable(events);
490
+ }
491
+ else if (options.events) {
492
+ output += '\n\n--- Recent Events ---\nNo events';
493
+ }
494
+ return success(element, output);
495
+ }
496
+ catch (err) {
497
+ const message = err instanceof Error ? err.message : String(err);
498
+ return failure(`Failed to get element: ${message}`, ExitCode.GENERAL_ERROR);
499
+ }
500
+ }
501
+ export const showCommand = {
502
+ name: 'show',
503
+ description: 'Show element details',
504
+ usage: 'sf show <id> [options]',
505
+ help: `Display detailed information about an element.
506
+
507
+ Arguments:
508
+ id Element identifier (e.g., el-abc123) or inbox item ID (e.g., inbox-abc123)
509
+
510
+ Options:
511
+ -e, --events Include recent events/history
512
+ --events-limit <n> Maximum events to show (default: 10)
513
+
514
+ Examples:
515
+ sf show el-abc123
516
+ sf show el-abc123 --events
517
+ sf show el-abc123 --events --events-limit 20
518
+ sf show el-abc123 --json
519
+ sf show inbox-abc123 # Show inbox item with message content`,
520
+ options: showOptions,
521
+ handler: showHandler,
522
+ };
523
+ export const updateOptions = [
524
+ {
525
+ name: 'title',
526
+ short: 't',
527
+ description: 'New title',
528
+ hasValue: true,
529
+ },
530
+ {
531
+ name: 'priority',
532
+ short: 'p',
533
+ description: 'New priority level (1-5)',
534
+ hasValue: true,
535
+ },
536
+ {
537
+ name: 'complexity',
538
+ short: 'c',
539
+ description: 'New complexity level (1-5)',
540
+ hasValue: true,
541
+ },
542
+ {
543
+ name: 'status',
544
+ short: 's',
545
+ description: 'New status (for tasks: open, in_progress, closed, deferred)',
546
+ hasValue: true,
547
+ },
548
+ {
549
+ name: 'assignee',
550
+ short: 'a',
551
+ description: 'New assignee (use empty string to unassign)',
552
+ hasValue: true,
553
+ },
554
+ {
555
+ name: 'tag',
556
+ description: 'Replace all tags (can be repeated)',
557
+ hasValue: true,
558
+ array: true,
559
+ },
560
+ {
561
+ name: 'add-tag',
562
+ description: 'Add a tag (can be repeated)',
563
+ hasValue: true,
564
+ array: true,
565
+ },
566
+ {
567
+ name: 'remove-tag',
568
+ description: 'Remove a tag (can be repeated)',
569
+ hasValue: true,
570
+ array: true,
571
+ },
572
+ ];
573
+ export async function updateHandler(args, options) {
574
+ const [id] = args;
575
+ if (!id) {
576
+ return failure('Usage: sf update <id> [options]', ExitCode.INVALID_ARGUMENTS);
577
+ }
578
+ const { api, error } = createAPI(options);
579
+ if (error) {
580
+ return failure(error, ExitCode.GENERAL_ERROR);
581
+ }
582
+ try {
583
+ // Get existing element
584
+ const element = await api.get(id);
585
+ if (!element) {
586
+ return failure(`Element not found: ${id}`, ExitCode.NOT_FOUND);
587
+ }
588
+ // Check if element is deleted (tombstone)
589
+ const elemData = element;
590
+ if (elemData.status === 'tombstone' || elemData.deletedAt) {
591
+ return failure(`Element not found: ${id}`, ExitCode.NOT_FOUND);
592
+ }
593
+ // Build updates object
594
+ const updates = {};
595
+ // Handle title
596
+ if (options.title !== undefined) {
597
+ updates.title = options.title;
598
+ }
599
+ // Handle priority (for tasks)
600
+ if (options.priority !== undefined) {
601
+ if (element.type !== 'task') {
602
+ return failure('Priority can only be set on tasks', ExitCode.VALIDATION);
603
+ }
604
+ const p = parseInt(options.priority, 10);
605
+ if (isNaN(p) || p < 1 || p > 5) {
606
+ return failure('Priority must be a number from 1 to 5', ExitCode.VALIDATION);
607
+ }
608
+ updates.priority = p;
609
+ }
610
+ // Handle complexity (for tasks)
611
+ if (options.complexity !== undefined) {
612
+ if (element.type !== 'task') {
613
+ return failure('Complexity can only be set on tasks', ExitCode.VALIDATION);
614
+ }
615
+ const c = parseInt(options.complexity, 10);
616
+ if (isNaN(c) || c < 1 || c > 5) {
617
+ return failure('Complexity must be a number from 1 to 5', ExitCode.VALIDATION);
618
+ }
619
+ updates.complexity = c;
620
+ }
621
+ // Handle status (for tasks)
622
+ if (options.status !== undefined) {
623
+ if (element.type !== 'task') {
624
+ return failure('Status can only be set on tasks', ExitCode.VALIDATION);
625
+ }
626
+ const validStatuses = Object.values(TaskStatus);
627
+ if (!validStatuses.includes(options.status)) {
628
+ return failure(`Invalid status: ${options.status}. Must be one of: ${validStatuses.join(', ')}`, ExitCode.VALIDATION);
629
+ }
630
+ updates.status = options.status;
631
+ }
632
+ // Handle assignee (for tasks)
633
+ if (options.assignee !== undefined) {
634
+ if (element.type !== 'task') {
635
+ return failure('Assignee can only be set on tasks', ExitCode.VALIDATION);
636
+ }
637
+ // Empty string means unassign
638
+ updates.assignee = options.assignee === '' ? undefined : options.assignee;
639
+ }
640
+ // Handle tag operations
641
+ let currentTags = element.tags ?? [];
642
+ // Complete replacement with --tag
643
+ if (options.tag !== undefined) {
644
+ const tags = Array.isArray(options.tag) ? options.tag : [options.tag];
645
+ currentTags = tags;
646
+ }
647
+ // Add tags with --add-tag
648
+ if (options['add-tag'] !== undefined) {
649
+ const tagsToAdd = Array.isArray(options['add-tag']) ? options['add-tag'] : [options['add-tag']];
650
+ const tagSet = new Set(currentTags);
651
+ for (const tag of tagsToAdd) {
652
+ tagSet.add(tag);
653
+ }
654
+ currentTags = Array.from(tagSet);
655
+ }
656
+ // Remove tags with --remove-tag
657
+ if (options['remove-tag'] !== undefined) {
658
+ const tagsToRemove = Array.isArray(options['remove-tag']) ? options['remove-tag'] : [options['remove-tag']];
659
+ const removeSet = new Set(tagsToRemove);
660
+ currentTags = currentTags.filter(tag => !removeSet.has(tag));
661
+ }
662
+ // Only update tags if any tag option was used
663
+ if (options.tag !== undefined || options['add-tag'] !== undefined || options['remove-tag'] !== undefined) {
664
+ updates.tags = currentTags;
665
+ }
666
+ // Check if there are any updates to apply
667
+ if (Object.keys(updates).length === 0) {
668
+ return failure('No updates specified. Use --help for available options.', ExitCode.INVALID_ARGUMENTS);
669
+ }
670
+ // Resolve actor for audit trail
671
+ const actor = resolveActor(options);
672
+ // Apply the update with optimistic concurrency control
673
+ const updated = await api.update(id, updates, {
674
+ actor,
675
+ expectedUpdatedAt: element.updatedAt,
676
+ });
677
+ // Format output based on mode
678
+ const mode = getOutputMode(options);
679
+ const formatter = getFormatter(mode);
680
+ if (mode === 'json') {
681
+ return success(updated);
682
+ }
683
+ if (mode === 'quiet') {
684
+ return success(updated.id);
685
+ }
686
+ // Human-readable output
687
+ const output = formatter.element(updated);
688
+ return success(updated, `Updated ${element.type} ${id}\n\n${output}`);
689
+ }
690
+ catch (err) {
691
+ const message = err instanceof Error ? err.message : String(err);
692
+ return failure(`Failed to update element: ${message}`, ExitCode.GENERAL_ERROR);
693
+ }
694
+ }
695
+ export const updateCommand = {
696
+ name: 'update',
697
+ description: 'Update an element',
698
+ usage: 'sf update <id> [options]',
699
+ help: `Update fields on an existing element.
700
+
701
+ Arguments:
702
+ id Element identifier (e.g., el-abc123)
703
+
704
+ Options:
705
+ -t, --title <text> New title
706
+ -p, --priority <1-5> New priority (tasks only)
707
+ -c, --complexity <1-5> New complexity (tasks only)
708
+ -s, --status <status> New status (tasks only: open, in_progress, closed, deferred)
709
+ -a, --assignee <id> New assignee (tasks only, empty string to unassign)
710
+ --tag <tag> Replace all tags (can be repeated)
711
+ --add-tag <tag> Add a tag (can be repeated)
712
+ --remove-tag <tag> Remove a tag (can be repeated)
713
+
714
+ Examples:
715
+ sf update el-abc123 --title "New Title"
716
+ sf update el-abc123 --priority 1 --status in_progress
717
+ sf update el-abc123 --add-tag urgent --add-tag frontend
718
+ sf update el-abc123 --remove-tag old-tag
719
+ sf update el-abc123 --assignee "" # Unassign`,
720
+ options: updateOptions,
721
+ handler: updateHandler,
722
+ };
723
+ export const deleteOptions = [
724
+ {
725
+ name: 'reason',
726
+ short: 'r',
727
+ description: 'Deletion reason',
728
+ hasValue: true,
729
+ },
730
+ {
731
+ name: 'force',
732
+ short: 'f',
733
+ description: 'Skip confirmation (for scripts)',
734
+ },
735
+ ];
736
+ export async function deleteHandler(args, options) {
737
+ const [id] = args;
738
+ if (!id) {
739
+ return failure('Usage: sf delete <id> [options]', ExitCode.INVALID_ARGUMENTS);
740
+ }
741
+ const { api, error } = createAPI(options);
742
+ if (error) {
743
+ return failure(error, ExitCode.GENERAL_ERROR);
744
+ }
745
+ try {
746
+ // Get existing element to verify it exists and get its type
747
+ const element = await api.get(id);
748
+ if (!element) {
749
+ return failure(`Element not found: ${id}`, ExitCode.NOT_FOUND);
750
+ }
751
+ // Check if element is already deleted (tombstone)
752
+ const elemData = element;
753
+ if (elemData.status === 'tombstone' || elemData.deletedAt) {
754
+ return failure(`Element not found: ${id}`, ExitCode.NOT_FOUND);
755
+ }
756
+ // Check if element type supports deletion
757
+ if (element.type === 'message') {
758
+ return failure('Messages cannot be deleted (immutable)', ExitCode.VALIDATION);
759
+ }
760
+ // Resolve actor for audit trail
761
+ const actor = resolveActor(options);
762
+ // Perform the soft delete
763
+ await api.delete(id, { actor, reason: options.reason });
764
+ // Format output based on mode
765
+ const mode = getOutputMode(options);
766
+ if (mode === 'json') {
767
+ return success({ id, deleted: true, type: element.type });
768
+ }
769
+ if (mode === 'quiet') {
770
+ return success(id);
771
+ }
772
+ return success({ id, deleted: true }, `Deleted ${element.type} ${id}`);
773
+ }
774
+ catch (err) {
775
+ const message = err instanceof Error ? err.message : String(err);
776
+ return failure(`Failed to delete element: ${message}`, ExitCode.GENERAL_ERROR);
777
+ }
778
+ }
779
+ export const deleteCommand = {
780
+ name: 'delete',
781
+ description: 'Delete an element',
782
+ usage: 'sf delete <id> [options]',
783
+ help: `Soft-delete an element.
784
+
785
+ The element will be marked as deleted (tombstone) but not immediately removed.
786
+ Tombstones are retained for a configurable period (default: 30 days) to
787
+ allow sync operations to propagate deletions.
788
+
789
+ Note: Messages cannot be deleted as they are immutable.
790
+
791
+ Arguments:
792
+ id Element identifier (e.g., el-abc123)
793
+
794
+ Options:
795
+ -r, --reason <text> Deletion reason (recorded in audit trail)
796
+ -f, --force Skip confirmation (for scripts)
797
+
798
+ Examples:
799
+ sf delete el-abc123
800
+ sf delete el-abc123 --reason "Duplicate entry"
801
+ sf delete el-abc123 -f`,
802
+ options: deleteOptions,
803
+ handler: deleteHandler,
804
+ };
805
+ //# sourceMappingURL=crud.js.map