@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,730 @@
1
+ /**
2
+ * Playbook Commands - Collection command interface for playbooks
3
+ *
4
+ * Provides CLI commands for playbook operations:
5
+ * - playbook list: List playbooks with filtering
6
+ * - playbook show: Show playbook details
7
+ * - playbook validate: Validate playbook structure
8
+ * - playbook create: Create a new playbook
9
+ */
10
+ import { success, failure, ExitCode } from '../types.js';
11
+ import { getFormatter, getOutputMode } from '../formatter.js';
12
+ import { createPlaybook, validatePlaybook, validateSteps, validateVariables, validateNoCircularInheritance, resolveInheritanceChain, VariableType, } from '@stoneforge/core';
13
+ import { validateCreateWorkflow } from '@stoneforge/core';
14
+ import { suggestCommands } from '../suggest.js';
15
+ import { resolveActor, createAPI } from '../db.js';
16
+ const playbookListOptions = [
17
+ {
18
+ name: 'limit',
19
+ short: 'l',
20
+ description: 'Maximum number of results',
21
+ hasValue: true,
22
+ },
23
+ ];
24
+ async function playbookListHandler(_args, options) {
25
+ const { api, error } = createAPI(options);
26
+ if (error) {
27
+ return failure(error, ExitCode.GENERAL_ERROR);
28
+ }
29
+ try {
30
+ // Build filter
31
+ const filter = {
32
+ type: 'playbook',
33
+ };
34
+ // Limit
35
+ if (options.limit) {
36
+ const limit = parseInt(options.limit, 10);
37
+ if (isNaN(limit) || limit < 1) {
38
+ return failure('Limit must be a positive number', ExitCode.VALIDATION);
39
+ }
40
+ filter.limit = limit;
41
+ }
42
+ const result = await api.listPaginated(filter);
43
+ const items = result.items;
44
+ const mode = getOutputMode(options);
45
+ const formatter = getFormatter(mode);
46
+ if (mode === 'json') {
47
+ return success(items);
48
+ }
49
+ if (mode === 'quiet') {
50
+ return success(items.map((p) => p.id).join('\n'));
51
+ }
52
+ if (items.length === 0) {
53
+ return success(null, 'No playbooks found');
54
+ }
55
+ // Build table
56
+ const headers = ['ID', 'NAME', 'TITLE', 'VERSION', 'STEPS', 'CREATED'];
57
+ const rows = items.map((p) => [
58
+ p.id,
59
+ p.name,
60
+ p.title.length > 30 ? p.title.substring(0, 27) + '...' : p.title,
61
+ `v${p.version}`,
62
+ String(p.steps.length),
63
+ p.createdAt.split('T')[0],
64
+ ]);
65
+ const table = formatter.table(headers, rows);
66
+ const summary = `\nShowing ${items.length} of ${result.total} playbooks`;
67
+ return success(items, table + summary);
68
+ }
69
+ catch (err) {
70
+ const message = err instanceof Error ? err.message : String(err);
71
+ return failure(`Failed to list playbooks: ${message}`, ExitCode.GENERAL_ERROR);
72
+ }
73
+ }
74
+ const playbookListCommand = {
75
+ name: 'list',
76
+ description: 'List playbooks',
77
+ usage: 'sf playbook list [options]',
78
+ help: `List playbooks with optional filtering.
79
+
80
+ Options:
81
+ -l, --limit <n> Maximum results
82
+
83
+ Examples:
84
+ sf playbook list
85
+ sf playbook list --limit 10`,
86
+ options: playbookListOptions,
87
+ handler: playbookListHandler,
88
+ };
89
+ const playbookShowOptions = [
90
+ {
91
+ name: 'steps',
92
+ short: 's',
93
+ description: 'Include step definitions',
94
+ hasValue: false,
95
+ },
96
+ {
97
+ name: 'variables',
98
+ short: 'v',
99
+ description: 'Include variable definitions',
100
+ hasValue: false,
101
+ },
102
+ ];
103
+ async function playbookShowHandler(args, options) {
104
+ const [nameOrId] = args;
105
+ if (!nameOrId) {
106
+ return failure('Usage: sf playbook show <name|id>', ExitCode.INVALID_ARGUMENTS);
107
+ }
108
+ const { api, error } = createAPI(options);
109
+ if (error) {
110
+ return failure(error, ExitCode.GENERAL_ERROR);
111
+ }
112
+ try {
113
+ let playbook = null;
114
+ // First try as ID
115
+ if (nameOrId.startsWith('el-')) {
116
+ playbook = await api.get(nameOrId);
117
+ }
118
+ // If not found by ID, search by name
119
+ if (!playbook) {
120
+ const allPlaybooks = await api.list({ type: 'playbook' });
121
+ playbook = allPlaybooks.find((p) => p.name === nameOrId) || null;
122
+ }
123
+ if (!playbook) {
124
+ return failure(`Playbook not found: ${nameOrId}`, ExitCode.NOT_FOUND);
125
+ }
126
+ if (playbook.type !== 'playbook') {
127
+ return failure(`Element ${nameOrId} is not a playbook (type: ${playbook.type})`, ExitCode.VALIDATION);
128
+ }
129
+ const mode = getOutputMode(options);
130
+ const formatter = getFormatter(mode);
131
+ if (mode === 'json') {
132
+ return success(playbook);
133
+ }
134
+ if (mode === 'quiet') {
135
+ return success(playbook.id);
136
+ }
137
+ // Human-readable output
138
+ let output = formatter.element(playbook);
139
+ // Add playbook-specific info
140
+ output += '\n\n--- Playbook Info ---\n';
141
+ output += `Name: ${playbook.name}\n`;
142
+ output += `Version: ${playbook.version}\n`;
143
+ output += `Steps: ${playbook.steps.length}\n`;
144
+ output += `Variables: ${playbook.variables.length}\n`;
145
+ if (playbook.extends && playbook.extends.length > 0) {
146
+ output += `Extends: ${playbook.extends.join(', ')}\n`;
147
+ }
148
+ // Show steps if requested
149
+ if (options.steps && playbook.steps.length > 0) {
150
+ output += '\n--- Steps ---\n';
151
+ const stepHeaders = ['ID', 'TITLE', 'DEPENDS ON'];
152
+ const stepRows = playbook.steps.map((s) => [
153
+ s.id,
154
+ s.title.length > 40 ? s.title.substring(0, 37) + '...' : s.title,
155
+ s.dependsOn?.join(', ') || '-',
156
+ ]);
157
+ output += formatter.table(stepHeaders, stepRows);
158
+ }
159
+ // Show variables if requested
160
+ if (options.variables && playbook.variables.length > 0) {
161
+ output += '\n--- Variables ---\n';
162
+ const varHeaders = ['NAME', 'TYPE', 'REQUIRED', 'DEFAULT'];
163
+ const varRows = playbook.variables.map((v) => [
164
+ v.name,
165
+ v.type,
166
+ v.required ? 'yes' : 'no',
167
+ v.default !== undefined ? String(v.default) : '-',
168
+ ]);
169
+ output += formatter.table(varHeaders, varRows);
170
+ }
171
+ return success(playbook, output);
172
+ }
173
+ catch (err) {
174
+ const message = err instanceof Error ? err.message : String(err);
175
+ return failure(`Failed to show playbook: ${message}`, ExitCode.GENERAL_ERROR);
176
+ }
177
+ }
178
+ const playbookShowCommand = {
179
+ name: 'show',
180
+ description: 'Show playbook details',
181
+ usage: 'sf playbook show <name|id> [options]',
182
+ help: `Display detailed information about a playbook.
183
+
184
+ Arguments:
185
+ name|id Playbook name or identifier
186
+
187
+ Options:
188
+ -s, --steps Include step definitions
189
+ -v, --variables Include variable definitions
190
+
191
+ Examples:
192
+ sf playbook show deploy
193
+ sf playbook show el-abc123 --steps --variables
194
+ sf playbook show deploy --json`,
195
+ options: playbookShowOptions,
196
+ handler: playbookShowHandler,
197
+ };
198
+ const playbookValidateOptions = [
199
+ {
200
+ name: 'var',
201
+ description: 'Set variable for create-time validation (name=value, can be repeated)',
202
+ hasValue: true,
203
+ array: true,
204
+ },
205
+ {
206
+ name: 'create',
207
+ short: 'c',
208
+ description: 'Perform create-time validation (validates variables can be resolved)',
209
+ hasValue: false,
210
+ },
211
+ ];
212
+ /**
213
+ * Parses variable arguments in name=value format
214
+ */
215
+ function parseVariableArgs(varArgs) {
216
+ const variables = {};
217
+ if (!varArgs)
218
+ return variables;
219
+ const args = Array.isArray(varArgs) ? varArgs : [varArgs];
220
+ for (const varArg of args) {
221
+ const eqIndex = varArg.indexOf('=');
222
+ if (eqIndex === -1) {
223
+ throw new Error(`Invalid variable format: ${varArg}. Use name=value`);
224
+ }
225
+ const name = varArg.slice(0, eqIndex);
226
+ const value = varArg.slice(eqIndex + 1);
227
+ // Try to parse as JSON for boolean/number types, otherwise use as string
228
+ try {
229
+ if (value === 'true') {
230
+ variables[name] = true;
231
+ }
232
+ else if (value === 'false') {
233
+ variables[name] = false;
234
+ }
235
+ else if (!isNaN(Number(value)) && value.trim() !== '') {
236
+ variables[name] = Number(value);
237
+ }
238
+ else {
239
+ variables[name] = value;
240
+ }
241
+ }
242
+ catch {
243
+ variables[name] = value;
244
+ }
245
+ }
246
+ return variables;
247
+ }
248
+ async function playbookValidateHandler(args, options) {
249
+ const [nameOrId] = args;
250
+ if (!nameOrId) {
251
+ return failure('Usage: sf playbook validate <name|id> [--var name=value] [--create]', ExitCode.INVALID_ARGUMENTS);
252
+ }
253
+ const { api, error } = createAPI(options);
254
+ if (error) {
255
+ return failure(error, ExitCode.GENERAL_ERROR);
256
+ }
257
+ try {
258
+ let playbook = null;
259
+ // First try as ID
260
+ if (nameOrId.startsWith('el-')) {
261
+ playbook = await api.get(nameOrId);
262
+ }
263
+ // If not found by ID, search by name
264
+ if (!playbook) {
265
+ const allPlaybooks = await api.list({ type: 'playbook' });
266
+ playbook = allPlaybooks.find((p) => p.name === nameOrId) || null;
267
+ }
268
+ if (!playbook) {
269
+ return failure(`Playbook not found: ${nameOrId}`, ExitCode.NOT_FOUND);
270
+ }
271
+ if (playbook.type !== 'playbook') {
272
+ return failure(`Element ${nameOrId} is not a playbook (type: ${playbook.type})`, ExitCode.VALIDATION);
273
+ }
274
+ const issues = [];
275
+ // Validate playbook structure
276
+ try {
277
+ validatePlaybook(playbook);
278
+ }
279
+ catch (err) {
280
+ const message = err instanceof Error ? err.message : String(err);
281
+ issues.push(`Structure: ${message}`);
282
+ }
283
+ // Validate steps
284
+ try {
285
+ validateSteps(playbook.steps);
286
+ }
287
+ catch (err) {
288
+ const message = err instanceof Error ? err.message : String(err);
289
+ issues.push(`Steps: ${message}`);
290
+ }
291
+ // Validate variables
292
+ try {
293
+ validateVariables(playbook.variables);
294
+ }
295
+ catch (err) {
296
+ const message = err instanceof Error ? err.message : String(err);
297
+ issues.push(`Variables: ${message}`);
298
+ }
299
+ // Check for circular dependencies in steps
300
+ const stepIds = new Set(playbook.steps.map((s) => s.id));
301
+ for (const step of playbook.steps) {
302
+ if (step.dependsOn) {
303
+ for (const depId of step.dependsOn) {
304
+ if (!stepIds.has(depId)) {
305
+ issues.push(`Step '${step.id}' depends on unknown step '${depId}'`);
306
+ }
307
+ }
308
+ }
309
+ }
310
+ // Check for unused variables in templates
311
+ const definedVars = new Set(playbook.variables.map((v) => v.name));
312
+ const varPattern = /\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g;
313
+ for (const step of playbook.steps) {
314
+ let match;
315
+ const pattern = new RegExp(varPattern.source, 'g');
316
+ while ((match = pattern.exec(step.title)) !== null) {
317
+ if (!definedVars.has(match[1])) {
318
+ issues.push(`Step '${step.id}' uses undefined variable '${match[1]}' in title`);
319
+ }
320
+ }
321
+ if (step.description) {
322
+ const descPattern = new RegExp(varPattern.source, 'g');
323
+ while ((match = descPattern.exec(step.description)) !== null) {
324
+ if (!definedVars.has(match[1])) {
325
+ issues.push(`Step '${step.id}' uses undefined variable '${match[1]}' in description`);
326
+ }
327
+ }
328
+ }
329
+ if (step.condition) {
330
+ const condPattern = new RegExp(varPattern.source, 'g');
331
+ while ((match = condPattern.exec(step.condition)) !== null) {
332
+ if (!definedVars.has(match[1])) {
333
+ issues.push(`Step '${step.id}' uses undefined variable '${match[1]}' in condition`);
334
+ }
335
+ }
336
+ }
337
+ }
338
+ // Check for circular inheritance (always, not just create-time)
339
+ if (playbook.extends && playbook.extends.length > 0) {
340
+ const allPlaybooks = await api.list({ type: 'playbook' });
341
+ const playbookLoader = (name) => {
342
+ return allPlaybooks.find((p) => p.name.toLowerCase() === name.toLowerCase());
343
+ };
344
+ try {
345
+ await resolveInheritanceChain(playbook, playbookLoader);
346
+ }
347
+ catch (err) {
348
+ const message = err instanceof Error ? err.message : String(err);
349
+ issues.push(`Inheritance: ${message}`);
350
+ }
351
+ }
352
+ // Create-time validation - run if --create flag is set or if --var is provided
353
+ const shouldDoCreateValidation = options.create || options.var;
354
+ let createValidationResult;
355
+ if (shouldDoCreateValidation) {
356
+ // Parse provided variables
357
+ let providedVars = {};
358
+ try {
359
+ providedVars = parseVariableArgs(options.var);
360
+ }
361
+ catch (err) {
362
+ const message = err instanceof Error ? err.message : String(err);
363
+ return failure(message, ExitCode.VALIDATION);
364
+ }
365
+ // Create a playbook loader that fetches from the database
366
+ const allPlaybooks = await api.list({ type: 'playbook' });
367
+ const playbookLoader = (name) => {
368
+ return allPlaybooks.find((p) => p.name.toLowerCase() === name.toLowerCase());
369
+ };
370
+ // Validate create-time variables
371
+ createValidationResult = await validateCreateWorkflow(playbook, providedVars, playbookLoader);
372
+ if (!createValidationResult.valid) {
373
+ issues.push(`Create-time: ${createValidationResult.error}`);
374
+ }
375
+ }
376
+ const mode = getOutputMode(options);
377
+ // Build JSON result
378
+ const jsonResult = {
379
+ valid: issues.length === 0,
380
+ issues,
381
+ playbook: { id: playbook.id, name: playbook.name },
382
+ };
383
+ // Add create-time validation details if performed
384
+ if (shouldDoCreateValidation && createValidationResult) {
385
+ jsonResult.createValidation = {
386
+ performed: true,
387
+ valid: createValidationResult.valid,
388
+ ...(createValidationResult.valid && {
389
+ resolvedVariables: createValidationResult.resolvedVariables,
390
+ includedSteps: createValidationResult.includedSteps?.map((s) => s.id),
391
+ skippedSteps: createValidationResult.skippedSteps,
392
+ }),
393
+ ...(createValidationResult.error && { error: createValidationResult.error }),
394
+ };
395
+ }
396
+ if (mode === 'json') {
397
+ return success(jsonResult);
398
+ }
399
+ if (mode === 'quiet') {
400
+ return success(issues.length === 0 ? 'valid' : 'invalid');
401
+ }
402
+ // Human-readable output
403
+ let output = '';
404
+ if (issues.length === 0) {
405
+ output = `Playbook '${playbook.name}' is valid`;
406
+ // Add create-time details if validation was performed
407
+ if (shouldDoCreateValidation && createValidationResult?.valid) {
408
+ output += '\n\n--- Create-time Validation ---';
409
+ output += '\nVariables resolved successfully';
410
+ if (createValidationResult.includedSteps && createValidationResult.includedSteps.length > 0) {
411
+ output += `\nIncluded steps: ${createValidationResult.includedSteps.map((s) => s.id).join(', ')}`;
412
+ }
413
+ if (createValidationResult.skippedSteps && createValidationResult.skippedSteps.length > 0) {
414
+ output += `\nSkipped steps: ${createValidationResult.skippedSteps.join(', ')}`;
415
+ }
416
+ }
417
+ }
418
+ else {
419
+ const issueList = issues.map((i, idx) => ` ${idx + 1}. ${i}`).join('\n');
420
+ output = `Playbook '${playbook.name}' has ${issues.length} issue(s):\n${issueList}`;
421
+ }
422
+ return success(jsonResult, output);
423
+ }
424
+ catch (err) {
425
+ const message = err instanceof Error ? err.message : String(err);
426
+ return failure(`Failed to validate playbook: ${message}`, ExitCode.GENERAL_ERROR);
427
+ }
428
+ }
429
+ const playbookValidateCommand = {
430
+ name: 'validate',
431
+ description: 'Validate playbook structure and create-time variables',
432
+ usage: 'sf playbook validate <name|id> [--var name=value] [--create]',
433
+ help: `Validate a playbook's structure and optionally test create-time variable resolution.
434
+
435
+ Structure Checks:
436
+ - Required fields are present
437
+ - Step IDs are unique
438
+ - Step dependencies reference existing steps
439
+ - Variables used in templates are defined
440
+ - No circular dependencies
441
+
442
+ Create-time Checks (with --create or --var):
443
+ - All required variables are provided
444
+ - Variable values match their declared types
445
+ - Enum values are within allowed list
446
+ - Variable substitution completes without errors
447
+ - Condition evaluation succeeds
448
+
449
+ Arguments:
450
+ name|id Playbook name or identifier
451
+
452
+ Options:
453
+ --var <name=value> Set variable for create-time validation (can be repeated)
454
+ -c, --create Perform create-time validation
455
+
456
+ Examples:
457
+ sf playbook validate deploy
458
+ sf playbook validate deploy --create
459
+ sf playbook validate deploy --var env=production --var debug=true
460
+ sf playbook validate el-abc123 --var version=1.0.0`,
461
+ options: playbookValidateOptions,
462
+ handler: playbookValidateHandler,
463
+ };
464
+ const playbookCreateOptions = [
465
+ {
466
+ name: 'name',
467
+ short: 'n',
468
+ description: 'Playbook name (unique identifier, required)',
469
+ hasValue: true,
470
+ required: true,
471
+ },
472
+ {
473
+ name: 'title',
474
+ short: 't',
475
+ description: 'Playbook title (display name, required)',
476
+ hasValue: true,
477
+ required: true,
478
+ },
479
+ {
480
+ name: 'step',
481
+ short: 's',
482
+ description: 'Add step (format: id:title[:dependsOn,...], can be repeated)',
483
+ hasValue: true,
484
+ array: true,
485
+ },
486
+ {
487
+ name: 'variable',
488
+ short: 'v',
489
+ description: 'Add variable (format: name:type[:default][:required], can be repeated)',
490
+ hasValue: true,
491
+ array: true,
492
+ },
493
+ {
494
+ name: 'extends',
495
+ short: 'e',
496
+ description: 'Extend playbook (can be repeated)',
497
+ hasValue: true,
498
+ array: true,
499
+ },
500
+ {
501
+ name: 'tag',
502
+ description: 'Add tag (can be repeated)',
503
+ hasValue: true,
504
+ array: true,
505
+ },
506
+ ];
507
+ function parseStep(stepArg) {
508
+ const parts = stepArg.split(':');
509
+ if (parts.length < 2) {
510
+ throw new Error(`Invalid step format: ${stepArg}. Expected id:title[:dependsOn,...]`);
511
+ }
512
+ const id = parts[0].trim();
513
+ const title = parts[1].trim();
514
+ const dependsOn = parts.length > 2 ? parts[2].split(',').map((d) => d.trim()).filter(Boolean) : undefined;
515
+ return {
516
+ id,
517
+ title,
518
+ ...(dependsOn && dependsOn.length > 0 && { dependsOn }),
519
+ };
520
+ }
521
+ function parseVariable(varArg) {
522
+ const parts = varArg.split(':');
523
+ if (parts.length < 2) {
524
+ throw new Error(`Invalid variable format: ${varArg}. Expected name:type[:default][:required]`);
525
+ }
526
+ const name = parts[0].trim();
527
+ const type = parts[1].trim();
528
+ if (!Object.values(VariableType).includes(type)) {
529
+ throw new Error(`Invalid variable type: ${type}. Must be one of: ${Object.values(VariableType).join(', ')}`);
530
+ }
531
+ let defaultValue;
532
+ let required = true;
533
+ if (parts.length > 2) {
534
+ const defaultStr = parts[2].trim();
535
+ if (defaultStr) {
536
+ switch (type) {
537
+ case VariableType.STRING:
538
+ defaultValue = defaultStr;
539
+ break;
540
+ case VariableType.NUMBER:
541
+ defaultValue = parseFloat(defaultStr);
542
+ if (isNaN(defaultValue)) {
543
+ throw new Error(`Invalid number default for variable '${name}': ${defaultStr}`);
544
+ }
545
+ break;
546
+ case VariableType.BOOLEAN:
547
+ if (defaultStr !== 'true' && defaultStr !== 'false') {
548
+ throw new Error(`Invalid boolean default for variable '${name}': ${defaultStr}. Must be 'true' or 'false'`);
549
+ }
550
+ defaultValue = defaultStr === 'true';
551
+ break;
552
+ }
553
+ }
554
+ }
555
+ if (parts.length > 3) {
556
+ required = parts[3].trim().toLowerCase() !== 'false';
557
+ }
558
+ return {
559
+ name,
560
+ type,
561
+ required,
562
+ ...(defaultValue !== undefined && { default: defaultValue }),
563
+ };
564
+ }
565
+ async function playbookCreateHandler(_args, options) {
566
+ if (!options.name) {
567
+ return failure('--name is required for creating a playbook', ExitCode.INVALID_ARGUMENTS);
568
+ }
569
+ if (!options.title) {
570
+ return failure('--title is required for creating a playbook', ExitCode.INVALID_ARGUMENTS);
571
+ }
572
+ const { api, error } = createAPI(options, true);
573
+ if (error) {
574
+ return failure(error, ExitCode.GENERAL_ERROR);
575
+ }
576
+ try {
577
+ const actor = resolveActor(options);
578
+ // Parse steps
579
+ const steps = [];
580
+ if (options.step) {
581
+ const stepArgs = Array.isArray(options.step) ? options.step : [options.step];
582
+ for (const stepArg of stepArgs) {
583
+ try {
584
+ steps.push(parseStep(stepArg));
585
+ }
586
+ catch (err) {
587
+ const message = err instanceof Error ? err.message : String(err);
588
+ return failure(message, ExitCode.VALIDATION);
589
+ }
590
+ }
591
+ }
592
+ // Parse variables
593
+ const variables = [];
594
+ if (options.variable) {
595
+ const varArgs = Array.isArray(options.variable) ? options.variable : [options.variable];
596
+ for (const varArg of varArgs) {
597
+ try {
598
+ variables.push(parseVariable(varArg));
599
+ }
600
+ catch (err) {
601
+ const message = err instanceof Error ? err.message : String(err);
602
+ return failure(message, ExitCode.VALIDATION);
603
+ }
604
+ }
605
+ }
606
+ // Parse extends
607
+ let extendsPlaybooks;
608
+ if (options.extends) {
609
+ extendsPlaybooks = Array.isArray(options.extends) ? options.extends : [options.extends];
610
+ }
611
+ // Handle tags
612
+ let tags;
613
+ if (options.tag) {
614
+ tags = Array.isArray(options.tag) ? options.tag : [options.tag];
615
+ }
616
+ // Validate that creating this playbook won't create circular inheritance
617
+ if (extendsPlaybooks && extendsPlaybooks.length > 0) {
618
+ const allPlaybooks = await api.list({ type: 'playbook' });
619
+ const playbookLoader = (name) => {
620
+ return allPlaybooks.find((p) => p.name.toLowerCase() === name.toLowerCase());
621
+ };
622
+ const cycleCheck = await validateNoCircularInheritance(options.name, extendsPlaybooks, playbookLoader);
623
+ if (!cycleCheck.valid) {
624
+ return failure(cycleCheck.error, ExitCode.VALIDATION);
625
+ }
626
+ }
627
+ const input = {
628
+ name: options.name,
629
+ title: options.title,
630
+ createdBy: actor,
631
+ steps,
632
+ variables,
633
+ ...(extendsPlaybooks && { extends: extendsPlaybooks }),
634
+ ...(tags && { tags }),
635
+ };
636
+ const playbook = await createPlaybook(input);
637
+ const created = await api.create(playbook);
638
+ const mode = getOutputMode(options);
639
+ if (mode === 'quiet') {
640
+ return success(created.id);
641
+ }
642
+ return success(created, `Created playbook ${created.id} (${options.name})`);
643
+ }
644
+ catch (err) {
645
+ const message = err instanceof Error ? err.message : String(err);
646
+ return failure(`Failed to create playbook: ${message}`, ExitCode.GENERAL_ERROR);
647
+ }
648
+ }
649
+ const playbookCreateCommand = {
650
+ name: 'create',
651
+ description: 'Create a new playbook',
652
+ usage: 'sf playbook create --name <name> --title <title> [options]',
653
+ help: `Create a new playbook template.
654
+
655
+ Options:
656
+ -n, --name <name> Playbook name (unique identifier, required)
657
+ -t, --title <title> Playbook title (display name, required)
658
+ -s, --step <spec> Add step (format: id:title[:dependsOn,...])
659
+ -v, --variable <spec> Add variable (format: name:type[:default][:required])
660
+ -e, --extends <name> Extend playbook (can be repeated)
661
+ --tag <tag> Add tag (can be repeated)
662
+
663
+ Step format:
664
+ id:title Basic step
665
+ id:title:dep1,dep2 Step with dependencies
666
+
667
+ Variable format:
668
+ name:type Required variable
669
+ name:type:default Variable with default
670
+ name:type:default:false Optional variable with default
671
+
672
+ Examples:
673
+ sf playbook create --name deploy --title "Deployment Process"
674
+ sf playbook create -n deploy -t "Deploy" -s "build:Build app" -s "test:Run tests:build"
675
+ sf playbook create -n deploy -t "Deploy" -v "env:string" -v "debug:boolean:false:false"`,
676
+ options: playbookCreateOptions,
677
+ handler: playbookCreateHandler,
678
+ };
679
+ // ============================================================================
680
+ // Playbook Root Command
681
+ // ============================================================================
682
+ export const playbookCommand = {
683
+ name: 'playbook',
684
+ description: 'Manage playbooks (workflow templates)',
685
+ usage: 'sf playbook <subcommand> [options]',
686
+ help: `Manage playbooks - templates for creating workflows.
687
+
688
+ Playbooks define reusable sequences of tasks with variables, conditions,
689
+ and dependencies. They can be instantiated as workflows using 'sf workflow create'.
690
+
691
+ Subcommands:
692
+ list List playbooks
693
+ show Show playbook details
694
+ validate Validate playbook structure
695
+ create Create a new playbook
696
+
697
+ Examples:
698
+ sf playbook list
699
+ sf playbook show deploy --steps --variables
700
+ sf playbook validate deploy
701
+ sf playbook create --name deploy --title "Deployment"`,
702
+ subcommands: {
703
+ list: playbookListCommand,
704
+ show: playbookShowCommand,
705
+ validate: playbookValidateCommand,
706
+ create: playbookCreateCommand,
707
+ // Aliases (hidden from --help via dedup in getCommandHelp)
708
+ new: playbookCreateCommand,
709
+ add: playbookCreateCommand,
710
+ ls: playbookListCommand,
711
+ get: playbookShowCommand,
712
+ view: playbookShowCommand,
713
+ },
714
+ handler: async (args, options) => {
715
+ // Default to list if no subcommand
716
+ if (args.length === 0) {
717
+ return playbookListHandler(args, options);
718
+ }
719
+ // Show "did you mean?" for unknown subcommands
720
+ const subNames = Object.keys(playbookCommand.subcommands);
721
+ const suggestions = suggestCommands(args[0], subNames);
722
+ let msg = `Unknown subcommand: ${args[0]}`;
723
+ if (suggestions.length > 0) {
724
+ msg += `\n\nDid you mean?\n${suggestions.map(s => ` ${s}`).join('\n')}`;
725
+ }
726
+ msg += '\n\nRun "sf playbook --help" to see available subcommands.';
727
+ return failure(msg, ExitCode.INVALID_ARGUMENTS);
728
+ },
729
+ };
730
+ //# sourceMappingURL=playbook.js.map