@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,698 @@
1
+ /**
2
+ * Plan Commands - Collection command interface for plans
3
+ *
4
+ * Provides CLI commands for plan operations:
5
+ * - plan create: Create a new plan
6
+ * - plan list: List plans with filtering
7
+ * - plan show: Show plan details with progress
8
+ * - plan activate: Activate a plan (draft -> active)
9
+ * - plan complete: Complete a plan (active -> completed)
10
+ * - plan cancel: Cancel a plan
11
+ * - plan add-task: Add a task to a plan
12
+ * - plan remove-task: Remove a task from a plan
13
+ * - plan tasks: List tasks in a plan
14
+ */
15
+ import { success, failure, ExitCode } from '../types.js';
16
+ import { getFormatter, getOutputMode, getStatusIcon } from '../formatter.js';
17
+ import { createPlan, PlanStatus } from '@stoneforge/core';
18
+ import { suggestCommands } from '../suggest.js';
19
+ import { resolveActor, createAPI } from '../db.js';
20
+ const planCreateOptions = [
21
+ {
22
+ name: 'title',
23
+ short: 't',
24
+ description: 'Plan title (required)',
25
+ hasValue: true,
26
+ required: true,
27
+ },
28
+ {
29
+ name: 'status',
30
+ short: 's',
31
+ description: 'Initial status (draft, active). Default: draft',
32
+ hasValue: true,
33
+ },
34
+ {
35
+ name: 'tag',
36
+ description: 'Add a tag (can be repeated)',
37
+ hasValue: true,
38
+ array: true,
39
+ },
40
+ ];
41
+ async function planCreateHandler(_args, options) {
42
+ if (!options.title) {
43
+ return failure('--title is required for creating a plan', ExitCode.INVALID_ARGUMENTS);
44
+ }
45
+ // Validate status if provided
46
+ if (options.status) {
47
+ const validStatuses = [PlanStatus.DRAFT, PlanStatus.ACTIVE];
48
+ if (!validStatuses.includes(options.status)) {
49
+ return failure(`Invalid initial status: ${options.status}. Must be one of: draft, active`, ExitCode.VALIDATION);
50
+ }
51
+ }
52
+ const { api, error } = createAPI(options, true);
53
+ if (error) {
54
+ return failure(error, ExitCode.GENERAL_ERROR);
55
+ }
56
+ try {
57
+ const actor = resolveActor(options);
58
+ // Handle tags
59
+ let tags;
60
+ if (options.tag) {
61
+ tags = Array.isArray(options.tag) ? options.tag : [options.tag];
62
+ }
63
+ const input = {
64
+ title: options.title,
65
+ createdBy: actor,
66
+ status: options.status ?? PlanStatus.DRAFT,
67
+ ...(tags && { tags }),
68
+ };
69
+ const plan = await createPlan(input);
70
+ const created = await api.create(plan);
71
+ return success(created, `Created plan ${created.id}`);
72
+ }
73
+ catch (err) {
74
+ const message = err instanceof Error ? err.message : String(err);
75
+ return failure(`Failed to create plan: ${message}`, ExitCode.GENERAL_ERROR);
76
+ }
77
+ }
78
+ const planCreateCommand = {
79
+ name: 'create',
80
+ description: 'Create a new plan',
81
+ usage: 'sf plan create --title <title> [options]',
82
+ help: `Create a new plan to organize related tasks.
83
+
84
+ Options:
85
+ -t, --title <text> Plan title (required)
86
+ -s, --status <status> Initial status: draft (default) or active
87
+ --tag <tag> Add a tag (can be repeated)
88
+
89
+ Examples:
90
+ sf plan create --title "Q1 Feature Roadmap"
91
+ sf plan create -t "Sprint 3" --status active
92
+ sf plan create --title "Bug Backlog" --tag urgent --tag backend`,
93
+ options: planCreateOptions,
94
+ handler: planCreateHandler,
95
+ };
96
+ const planListOptions = [
97
+ {
98
+ name: 'status',
99
+ short: 's',
100
+ description: 'Filter by status (draft, active, completed, cancelled)',
101
+ hasValue: true,
102
+ },
103
+ {
104
+ name: 'tag',
105
+ description: 'Filter by tag (can be repeated for AND)',
106
+ hasValue: true,
107
+ array: true,
108
+ },
109
+ {
110
+ name: 'limit',
111
+ short: 'l',
112
+ description: 'Maximum number of results',
113
+ hasValue: true,
114
+ },
115
+ ];
116
+ async function planListHandler(_args, options) {
117
+ const { api, error } = createAPI(options);
118
+ if (error) {
119
+ return failure(error, ExitCode.GENERAL_ERROR);
120
+ }
121
+ try {
122
+ // Build filter
123
+ const filter = {
124
+ type: 'plan',
125
+ };
126
+ // Status filter
127
+ if (options.status) {
128
+ const validStatuses = Object.values(PlanStatus);
129
+ if (!validStatuses.includes(options.status)) {
130
+ return failure(`Invalid status: ${options.status}. Must be one of: ${validStatuses.join(', ')}`, ExitCode.VALIDATION);
131
+ }
132
+ filter.status = options.status;
133
+ }
134
+ // Tag filter
135
+ if (options.tag) {
136
+ filter.tags = Array.isArray(options.tag) ? options.tag : [options.tag];
137
+ }
138
+ // Limit
139
+ if (options.limit) {
140
+ const limit = parseInt(options.limit, 10);
141
+ if (isNaN(limit) || limit < 1) {
142
+ return failure('Limit must be a positive number', ExitCode.VALIDATION);
143
+ }
144
+ filter.limit = limit;
145
+ }
146
+ const result = await api.listPaginated(filter);
147
+ // Post-filter by status since the API doesn't filter by status for plans
148
+ let items = result.items;
149
+ if (options.status) {
150
+ items = items.filter((p) => p.status === options.status);
151
+ }
152
+ const mode = getOutputMode(options);
153
+ const formatter = getFormatter(mode);
154
+ if (mode === 'json') {
155
+ return success(items);
156
+ }
157
+ if (mode === 'quiet') {
158
+ return success(items.map((p) => p.id).join('\n'));
159
+ }
160
+ if (items.length === 0) {
161
+ return success(null, 'No plans found');
162
+ }
163
+ // Build table with progress info
164
+ const headers = ['ID', 'TITLE', 'STATUS', 'PROGRESS', 'CREATED'];
165
+ const rows = [];
166
+ for (const plan of items) {
167
+ // Get progress for each plan
168
+ let progressStr = '-';
169
+ try {
170
+ const progress = await api.getPlanProgress(plan.id);
171
+ progressStr = `${progress.completionPercentage}% (${progress.completedTasks}/${progress.totalTasks})`;
172
+ }
173
+ catch {
174
+ // Ignore progress fetch errors
175
+ }
176
+ const status = `${getStatusIcon(plan.status)} ${plan.status}`;
177
+ const created = plan.createdAt.split('T')[0];
178
+ rows.push([plan.id, plan.title, status, progressStr, created]);
179
+ }
180
+ const table = formatter.table(headers, rows);
181
+ const summary = `\nShowing ${items.length} of ${result.total} plans`;
182
+ return success(items, table + summary);
183
+ }
184
+ catch (err) {
185
+ const message = err instanceof Error ? err.message : String(err);
186
+ return failure(`Failed to list plans: ${message}`, ExitCode.GENERAL_ERROR);
187
+ }
188
+ }
189
+ const planListCommand = {
190
+ name: 'list',
191
+ description: 'List plans',
192
+ usage: 'sf plan list [options]',
193
+ help: `List plans with optional filtering.
194
+
195
+ Options:
196
+ -s, --status <status> Filter by status: draft, active, completed, cancelled
197
+ --tag <tag> Filter by tag (can be repeated)
198
+ -l, --limit <n> Maximum results
199
+
200
+ Examples:
201
+ sf plan list
202
+ sf plan list --status active
203
+ sf plan list --tag sprint --tag q1`,
204
+ options: planListOptions,
205
+ handler: planListHandler,
206
+ };
207
+ const planShowOptions = [
208
+ {
209
+ name: 'tasks',
210
+ short: 't',
211
+ description: 'Include task list',
212
+ hasValue: false,
213
+ },
214
+ ];
215
+ async function planShowHandler(args, options) {
216
+ const [id] = args;
217
+ if (!id) {
218
+ return failure('Usage: sf plan show <id>\nExample: sf plan show el-abc123', ExitCode.INVALID_ARGUMENTS);
219
+ }
220
+ const { api, error } = createAPI(options);
221
+ if (error) {
222
+ return failure(error, ExitCode.GENERAL_ERROR);
223
+ }
224
+ try {
225
+ const plan = await api.get(id);
226
+ if (!plan) {
227
+ return failure(`Plan not found: ${id}`, ExitCode.NOT_FOUND);
228
+ }
229
+ if (plan.type !== 'plan') {
230
+ return failure(`Element ${id} is not a plan (type: ${plan.type})`, ExitCode.VALIDATION);
231
+ }
232
+ // Get progress
233
+ const progress = await api.getPlanProgress(id);
234
+ // Get tasks if requested
235
+ let tasks;
236
+ if (options.tasks) {
237
+ tasks = await api.getTasksInPlan(id);
238
+ }
239
+ const mode = getOutputMode(options);
240
+ const formatter = getFormatter(mode);
241
+ if (mode === 'json') {
242
+ return success({ plan, progress, ...(tasks && { tasks }) });
243
+ }
244
+ if (mode === 'quiet') {
245
+ return success(plan.id);
246
+ }
247
+ // Human-readable output
248
+ let output = formatter.element(plan);
249
+ // Add progress section
250
+ output += '\n\n--- Task Progress ---\n';
251
+ output += `Total: ${progress.totalTasks}\n`;
252
+ output += `Completed: ${progress.completedTasks}\n`;
253
+ output += `In Progress: ${progress.inProgressTasks}\n`;
254
+ output += `Blocked: ${progress.blockedTasks}\n`;
255
+ output += `Ready: ${progress.remainingTasks}\n`;
256
+ output += `Progress: ${progress.completionPercentage}%`;
257
+ // Add tasks section if requested
258
+ if (tasks && tasks.length > 0) {
259
+ output += '\n\n--- Tasks ---\n';
260
+ const taskHeaders = ['ID', 'TITLE', 'STATUS', 'PRIORITY'];
261
+ const taskRows = tasks.map((t) => [
262
+ t.id,
263
+ t.title.length > 40 ? t.title.substring(0, 37) + '...' : t.title,
264
+ `${getStatusIcon(t.status)} ${t.status}`,
265
+ `P${t.priority ?? 3}`,
266
+ ]);
267
+ output += formatter.table(taskHeaders, taskRows);
268
+ }
269
+ else if (options.tasks) {
270
+ output += '\n\n--- Tasks ---\nNo tasks';
271
+ }
272
+ return success({ plan, progress }, output);
273
+ }
274
+ catch (err) {
275
+ const message = err instanceof Error ? err.message : String(err);
276
+ return failure(`Failed to show plan: ${message}`, ExitCode.GENERAL_ERROR);
277
+ }
278
+ }
279
+ const planShowCommand = {
280
+ name: 'show',
281
+ description: 'Show plan details',
282
+ usage: 'sf plan show <id> [options]',
283
+ help: `Display detailed information about a plan including progress.
284
+
285
+ Arguments:
286
+ id Plan identifier (e.g., el-abc123)
287
+
288
+ Options:
289
+ -t, --tasks Include list of tasks in the plan
290
+
291
+ Examples:
292
+ sf plan show el-abc123
293
+ sf plan show el-abc123 --tasks
294
+ sf plan show el-abc123 --json`,
295
+ options: planShowOptions,
296
+ handler: planShowHandler,
297
+ };
298
+ // ============================================================================
299
+ // Plan Activate Command
300
+ // ============================================================================
301
+ async function planActivateHandler(args, options) {
302
+ const [id] = args;
303
+ if (!id) {
304
+ return failure('Usage: sf plan activate <id>\nExample: sf plan activate el-abc123', ExitCode.INVALID_ARGUMENTS);
305
+ }
306
+ const { api, error } = createAPI(options);
307
+ if (error) {
308
+ return failure(error, ExitCode.GENERAL_ERROR);
309
+ }
310
+ try {
311
+ const plan = await api.get(id);
312
+ if (!plan) {
313
+ return failure(`Plan not found: ${id}`, ExitCode.NOT_FOUND);
314
+ }
315
+ if (plan.type !== 'plan') {
316
+ return failure(`Element ${id} is not a plan (type: ${plan.type})`, ExitCode.VALIDATION);
317
+ }
318
+ if (plan.status === PlanStatus.ACTIVE) {
319
+ return success(plan, `Plan ${id} is already active`);
320
+ }
321
+ if (plan.status !== PlanStatus.DRAFT) {
322
+ return failure(`Cannot activate plan: current status is '${plan.status}'. Only draft plans can be activated.`, ExitCode.VALIDATION);
323
+ }
324
+ const actor = resolveActor(options);
325
+ const updated = await api.update(id, { status: PlanStatus.ACTIVE }, { actor });
326
+ return success(updated, `Activated plan ${id}`);
327
+ }
328
+ catch (err) {
329
+ const message = err instanceof Error ? err.message : String(err);
330
+ return failure(`Failed to activate plan: ${message}`, ExitCode.GENERAL_ERROR);
331
+ }
332
+ }
333
+ const planActivateCommand = {
334
+ name: 'activate',
335
+ description: 'Activate a draft plan',
336
+ usage: 'sf plan activate <id>',
337
+ help: `Transition a plan from draft to active status.
338
+
339
+ Arguments:
340
+ id Plan identifier
341
+
342
+ Examples:
343
+ sf plan activate el-abc123`,
344
+ handler: planActivateHandler,
345
+ };
346
+ // ============================================================================
347
+ // Plan Complete Command
348
+ // ============================================================================
349
+ async function planCompleteHandler(args, options) {
350
+ const [id] = args;
351
+ if (!id) {
352
+ return failure('Usage: sf plan complete <id>\nExample: sf plan complete el-abc123', ExitCode.INVALID_ARGUMENTS);
353
+ }
354
+ const { api, error } = createAPI(options);
355
+ if (error) {
356
+ return failure(error, ExitCode.GENERAL_ERROR);
357
+ }
358
+ try {
359
+ const plan = await api.get(id);
360
+ if (!plan) {
361
+ return failure(`Plan not found: ${id}`, ExitCode.NOT_FOUND);
362
+ }
363
+ if (plan.type !== 'plan') {
364
+ return failure(`Element ${id} is not a plan (type: ${plan.type})`, ExitCode.VALIDATION);
365
+ }
366
+ if (plan.status === PlanStatus.COMPLETED) {
367
+ return success(plan, `Plan ${id} is already completed`);
368
+ }
369
+ if (plan.status !== PlanStatus.ACTIVE) {
370
+ return failure(`Cannot complete plan: current status is '${plan.status}'. Only active plans can be completed.`, ExitCode.VALIDATION);
371
+ }
372
+ const actor = resolveActor(options);
373
+ const now = new Date().toISOString();
374
+ const updated = await api.update(id, { status: PlanStatus.COMPLETED, completedAt: now }, { actor });
375
+ return success(updated, `Completed plan ${id}`);
376
+ }
377
+ catch (err) {
378
+ const message = err instanceof Error ? err.message : String(err);
379
+ return failure(`Failed to complete plan: ${message}`, ExitCode.GENERAL_ERROR);
380
+ }
381
+ }
382
+ const planCompleteCommand = {
383
+ name: 'complete',
384
+ description: 'Complete an active plan',
385
+ usage: 'sf plan complete <id>',
386
+ help: `Transition a plan from active to completed status.
387
+
388
+ Arguments:
389
+ id Plan identifier
390
+
391
+ Examples:
392
+ sf plan complete el-abc123`,
393
+ handler: planCompleteHandler,
394
+ };
395
+ const planCancelOptions = [
396
+ {
397
+ name: 'reason',
398
+ short: 'r',
399
+ description: 'Cancellation reason',
400
+ hasValue: true,
401
+ },
402
+ ];
403
+ async function planCancelHandler(args, options) {
404
+ const [id] = args;
405
+ if (!id) {
406
+ return failure('Usage: sf plan cancel <id>\nExample: sf plan cancel el-abc123', ExitCode.INVALID_ARGUMENTS);
407
+ }
408
+ const { api, error } = createAPI(options);
409
+ if (error) {
410
+ return failure(error, ExitCode.GENERAL_ERROR);
411
+ }
412
+ try {
413
+ const plan = await api.get(id);
414
+ if (!plan) {
415
+ return failure(`Plan not found: ${id}`, ExitCode.NOT_FOUND);
416
+ }
417
+ if (plan.type !== 'plan') {
418
+ return failure(`Element ${id} is not a plan (type: ${plan.type})`, ExitCode.VALIDATION);
419
+ }
420
+ if (plan.status === PlanStatus.CANCELLED) {
421
+ return success(plan, `Plan ${id} is already cancelled`);
422
+ }
423
+ if (plan.status === PlanStatus.COMPLETED) {
424
+ return failure(`Cannot cancel plan: current status is '${plan.status}'. Completed plans cannot be cancelled.`, ExitCode.VALIDATION);
425
+ }
426
+ const actor = resolveActor(options);
427
+ const now = new Date().toISOString();
428
+ const updates = {
429
+ status: PlanStatus.CANCELLED,
430
+ cancelledAt: now,
431
+ };
432
+ if (options.reason) {
433
+ updates.cancelReason = options.reason;
434
+ }
435
+ const updated = await api.update(id, updates, { actor });
436
+ return success(updated, `Cancelled plan ${id}`);
437
+ }
438
+ catch (err) {
439
+ const message = err instanceof Error ? err.message : String(err);
440
+ return failure(`Failed to cancel plan: ${message}`, ExitCode.GENERAL_ERROR);
441
+ }
442
+ }
443
+ const planCancelCommand = {
444
+ name: 'cancel',
445
+ description: 'Cancel a plan',
446
+ usage: 'sf plan cancel <id> [options]',
447
+ help: `Cancel a draft or active plan.
448
+
449
+ Arguments:
450
+ id Plan identifier
451
+
452
+ Options:
453
+ -r, --reason <text> Cancellation reason
454
+
455
+ Examples:
456
+ sf plan cancel el-abc123
457
+ sf plan cancel el-abc123 --reason "Requirements changed"`,
458
+ options: planCancelOptions,
459
+ handler: planCancelHandler,
460
+ };
461
+ // ============================================================================
462
+ // Plan Add Task Command
463
+ // ============================================================================
464
+ async function planAddTaskHandler(args, options) {
465
+ const [planId, taskId] = args;
466
+ if (!planId || !taskId) {
467
+ return failure('Usage: sf plan add-task <plan-id> <task-id>\nExample: sf plan add-task el-plan123 el-task456', ExitCode.INVALID_ARGUMENTS);
468
+ }
469
+ const { api, error } = createAPI(options);
470
+ if (error) {
471
+ return failure(error, ExitCode.GENERAL_ERROR);
472
+ }
473
+ try {
474
+ // Verify plan exists
475
+ const plan = await api.get(planId);
476
+ if (!plan) {
477
+ return failure(`Plan not found: ${planId}`, ExitCode.NOT_FOUND);
478
+ }
479
+ if (plan.type !== 'plan') {
480
+ return failure(`Element ${planId} is not a plan (type: ${plan.type})`, ExitCode.VALIDATION);
481
+ }
482
+ if (plan.status === PlanStatus.CANCELLED) {
483
+ return failure(`Cannot add task to plan with status 'cancelled'`, ExitCode.VALIDATION);
484
+ }
485
+ // Verify task exists
486
+ const task = await api.get(taskId);
487
+ if (!task) {
488
+ return failure(`Task not found: ${taskId}`, ExitCode.NOT_FOUND);
489
+ }
490
+ if (task.type !== 'task') {
491
+ return failure(`Element ${taskId} is not a task (type: ${task.type})`, ExitCode.VALIDATION);
492
+ }
493
+ const actor = resolveActor(options);
494
+ await api.addTaskToPlan(taskId, planId, { actor });
495
+ return success({ planId, taskId }, `Added task ${taskId} to plan ${planId}`);
496
+ }
497
+ catch (err) {
498
+ const message = err instanceof Error ? err.message : String(err);
499
+ return failure(`Failed to add task to plan: ${message}`, ExitCode.GENERAL_ERROR);
500
+ }
501
+ }
502
+ const planAddTaskCommand = {
503
+ name: 'add-task',
504
+ description: 'Add a task to a plan',
505
+ usage: 'sf plan add-task <plan-id> <task-id>',
506
+ help: `Add an existing task to a plan.
507
+
508
+ Arguments:
509
+ plan-id Plan identifier
510
+ task-id Task identifier to add
511
+
512
+ Examples:
513
+ sf plan add-task el-plan123 el-task456`,
514
+ handler: planAddTaskHandler,
515
+ };
516
+ // ============================================================================
517
+ // Plan Remove Task Command
518
+ // ============================================================================
519
+ async function planRemoveTaskHandler(args, options) {
520
+ const [planId, taskId] = args;
521
+ if (!planId || !taskId) {
522
+ return failure('Usage: sf plan remove-task <plan-id> <task-id>\nExample: sf plan remove-task el-plan123 el-task456', ExitCode.INVALID_ARGUMENTS);
523
+ }
524
+ const { api, error } = createAPI(options);
525
+ if (error) {
526
+ return failure(error, ExitCode.GENERAL_ERROR);
527
+ }
528
+ try {
529
+ const actor = resolveActor(options);
530
+ await api.removeTaskFromPlan(taskId, planId, actor);
531
+ return success({ planId, taskId }, `Removed task ${taskId} from plan ${planId}`);
532
+ }
533
+ catch (err) {
534
+ const message = err instanceof Error ? err.message : String(err);
535
+ return failure(`Failed to remove task from plan: ${message}`, ExitCode.GENERAL_ERROR);
536
+ }
537
+ }
538
+ const planRemoveTaskCommand = {
539
+ name: 'remove-task',
540
+ description: 'Remove a task from a plan',
541
+ usage: 'sf plan remove-task <plan-id> <task-id>',
542
+ help: `Remove a task from a plan.
543
+
544
+ Arguments:
545
+ plan-id Plan identifier
546
+ task-id Task identifier to remove
547
+
548
+ Examples:
549
+ sf plan remove-task el-plan123 el-task456`,
550
+ handler: planRemoveTaskHandler,
551
+ };
552
+ const planTasksOptions = [
553
+ {
554
+ name: 'status',
555
+ short: 's',
556
+ description: 'Filter by task status',
557
+ hasValue: true,
558
+ },
559
+ {
560
+ name: 'limit',
561
+ short: 'l',
562
+ description: 'Maximum number of results',
563
+ hasValue: true,
564
+ },
565
+ ];
566
+ async function planTasksHandler(args, options) {
567
+ const [planId] = args;
568
+ if (!planId) {
569
+ return failure('Usage: sf plan tasks <plan-id>\nExample: sf plan tasks el-abc123', ExitCode.INVALID_ARGUMENTS);
570
+ }
571
+ const { api, error } = createAPI(options);
572
+ if (error) {
573
+ return failure(error, ExitCode.GENERAL_ERROR);
574
+ }
575
+ try {
576
+ // Build filter
577
+ const filter = {};
578
+ if (options.status) {
579
+ filter.status = options.status;
580
+ }
581
+ let limit;
582
+ if (options.limit) {
583
+ limit = parseInt(options.limit, 10);
584
+ if (isNaN(limit) || limit < 1) {
585
+ return failure('Limit must be a positive number', ExitCode.VALIDATION);
586
+ }
587
+ }
588
+ let tasks = await api.getTasksInPlan(planId, filter);
589
+ // Apply limit (since getTasksInPlan doesn't support pagination directly)
590
+ if (limit !== undefined && tasks.length > limit) {
591
+ tasks = tasks.slice(0, limit);
592
+ }
593
+ const mode = getOutputMode(options);
594
+ const formatter = getFormatter(mode);
595
+ if (mode === 'json') {
596
+ return success(tasks);
597
+ }
598
+ if (mode === 'quiet') {
599
+ return success(tasks.map((t) => t.id).join('\n'));
600
+ }
601
+ if (tasks.length === 0) {
602
+ return success(null, 'No tasks in plan');
603
+ }
604
+ const headers = ['ID', 'TITLE', 'STATUS', 'PRIORITY', 'ASSIGNEE'];
605
+ const rows = tasks.map((t) => [
606
+ t.id,
607
+ t.title.length > 40 ? t.title.substring(0, 37) + '...' : t.title,
608
+ `${getStatusIcon(t.status)} ${t.status}`,
609
+ `P${t.priority ?? 3}`,
610
+ t.assignee ?? '-',
611
+ ]);
612
+ const table = formatter.table(headers, rows);
613
+ return success(tasks, table + `\n${tasks.length} task(s)`);
614
+ }
615
+ catch (err) {
616
+ const message = err instanceof Error ? err.message : String(err);
617
+ return failure(`Failed to list plan tasks: ${message}`, ExitCode.GENERAL_ERROR);
618
+ }
619
+ }
620
+ const planTasksCommand = {
621
+ name: 'tasks',
622
+ description: 'List tasks in a plan',
623
+ usage: 'sf plan tasks <plan-id> [options]',
624
+ help: `List tasks belonging to a plan.
625
+
626
+ Arguments:
627
+ plan-id Plan identifier
628
+
629
+ Options:
630
+ -s, --status <status> Filter by status
631
+ -l, --limit <n> Maximum results
632
+
633
+ Examples:
634
+ sf plan tasks el-abc123
635
+ sf plan tasks el-abc123 --status open`,
636
+ options: planTasksOptions,
637
+ handler: planTasksHandler,
638
+ };
639
+ // ============================================================================
640
+ // Plan Root Command
641
+ // ============================================================================
642
+ export const planCommand = {
643
+ name: 'plan',
644
+ description: 'Manage plans (task collections)',
645
+ usage: 'sf plan <subcommand> [options]',
646
+ help: `Manage plans - collections of related tasks.
647
+
648
+ Subcommands:
649
+ create Create a new plan
650
+ list List plans
651
+ show Show plan details with progress
652
+ activate Activate a draft plan
653
+ complete Complete an active plan
654
+ cancel Cancel a plan
655
+ add-task Add a task to a plan
656
+ remove-task Remove a task from a plan
657
+ tasks List tasks in a plan
658
+
659
+ Examples:
660
+ sf plan create --title "Q1 Roadmap"
661
+ sf plan list --status active
662
+ sf plan show el-abc123 --tasks
663
+ sf plan activate el-abc123
664
+ sf plan add-task el-plan123 el-task456`,
665
+ subcommands: {
666
+ create: planCreateCommand,
667
+ list: planListCommand,
668
+ show: planShowCommand,
669
+ activate: planActivateCommand,
670
+ complete: planCompleteCommand,
671
+ cancel: planCancelCommand,
672
+ 'add-task': planAddTaskCommand,
673
+ 'remove-task': planRemoveTaskCommand,
674
+ tasks: planTasksCommand,
675
+ // Aliases (hidden from --help via dedup in getCommandHelp)
676
+ new: planCreateCommand,
677
+ add: planCreateCommand,
678
+ ls: planListCommand,
679
+ get: planShowCommand,
680
+ view: planShowCommand,
681
+ },
682
+ handler: async (args, options) => {
683
+ // Default to list if no subcommand
684
+ if (args.length === 0) {
685
+ return planListHandler(args, options);
686
+ }
687
+ // Show "did you mean?" for unknown subcommands
688
+ const subNames = Object.keys(planCommand.subcommands);
689
+ const suggestions = suggestCommands(args[0], subNames);
690
+ let msg = `Unknown subcommand: ${args[0]}`;
691
+ if (suggestions.length > 0) {
692
+ msg += `\n\nDid you mean?\n${suggestions.map(s => ` ${s}`).join('\n')}`;
693
+ }
694
+ msg += '\n\nRun "sf plan --help" to see available subcommands.';
695
+ return failure(msg, ExitCode.INVALID_ARGUMENTS);
696
+ },
697
+ };
698
+ //# sourceMappingURL=plan.js.map