@stacksjs/ts-cloud-core 0.1.1

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 (251) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +321 -0
  3. package/package.json +31 -0
  4. package/src/advanced-features.test.ts +465 -0
  5. package/src/aws/cloudformation.ts +421 -0
  6. package/src/aws/cloudfront.ts +158 -0
  7. package/src/aws/credentials.test.ts +132 -0
  8. package/src/aws/credentials.ts +545 -0
  9. package/src/aws/index.ts +87 -0
  10. package/src/aws/s3.test.ts +188 -0
  11. package/src/aws/s3.ts +1088 -0
  12. package/src/aws/signature.test.ts +670 -0
  13. package/src/aws/signature.ts +1155 -0
  14. package/src/backup/disaster-recovery.test.ts +726 -0
  15. package/src/backup/disaster-recovery.ts +500 -0
  16. package/src/backup/index.ts +34 -0
  17. package/src/backup/manager.test.ts +498 -0
  18. package/src/backup/manager.ts +432 -0
  19. package/src/cicd/circleci.ts +430 -0
  20. package/src/cicd/github-actions.ts +424 -0
  21. package/src/cicd/gitlab-ci.ts +255 -0
  22. package/src/cicd/index.ts +8 -0
  23. package/src/cli/history.ts +396 -0
  24. package/src/cli/index.ts +10 -0
  25. package/src/cli/progress.ts +458 -0
  26. package/src/cli/repl.ts +454 -0
  27. package/src/cli/suggestions.ts +327 -0
  28. package/src/cli/table.test.ts +319 -0
  29. package/src/cli/table.ts +332 -0
  30. package/src/cloudformation/builder.test.ts +327 -0
  31. package/src/cloudformation/builder.ts +378 -0
  32. package/src/cloudformation/builders/api-gateway.ts +449 -0
  33. package/src/cloudformation/builders/cache.ts +334 -0
  34. package/src/cloudformation/builders/cdn.ts +278 -0
  35. package/src/cloudformation/builders/compute.ts +485 -0
  36. package/src/cloudformation/builders/database.ts +392 -0
  37. package/src/cloudformation/builders/functions.ts +343 -0
  38. package/src/cloudformation/builders/messaging.ts +140 -0
  39. package/src/cloudformation/builders/monitoring.ts +300 -0
  40. package/src/cloudformation/builders/network.ts +264 -0
  41. package/src/cloudformation/builders/queue.ts +147 -0
  42. package/src/cloudformation/builders/security.ts +399 -0
  43. package/src/cloudformation/builders/storage.ts +285 -0
  44. package/src/cloudformation/index.ts +30 -0
  45. package/src/cloudformation/types.ts +173 -0
  46. package/src/compliance/aws-config.ts +543 -0
  47. package/src/compliance/cloudtrail.ts +376 -0
  48. package/src/compliance/compliance.test.ts +423 -0
  49. package/src/compliance/guardduty.ts +446 -0
  50. package/src/compliance/index.ts +66 -0
  51. package/src/compliance/security-hub.ts +456 -0
  52. package/src/containers/build-optimization.ts +416 -0
  53. package/src/containers/containers.test.ts +508 -0
  54. package/src/containers/image-scanning.ts +360 -0
  55. package/src/containers/index.ts +9 -0
  56. package/src/containers/registry.ts +293 -0
  57. package/src/containers/service-mesh.ts +520 -0
  58. package/src/database/database.test.ts +762 -0
  59. package/src/database/index.ts +9 -0
  60. package/src/database/migrations.ts +444 -0
  61. package/src/database/performance.ts +528 -0
  62. package/src/database/replicas.ts +534 -0
  63. package/src/database/users.ts +494 -0
  64. package/src/dependency-graph.ts +143 -0
  65. package/src/deployment/ab-testing.ts +582 -0
  66. package/src/deployment/blue-green.ts +452 -0
  67. package/src/deployment/canary.ts +500 -0
  68. package/src/deployment/deployment.test.ts +526 -0
  69. package/src/deployment/index.ts +61 -0
  70. package/src/deployment/progressive.ts +62 -0
  71. package/src/dns/dns.test.ts +641 -0
  72. package/src/dns/dnssec.ts +315 -0
  73. package/src/dns/index.ts +8 -0
  74. package/src/dns/resolver.ts +496 -0
  75. package/src/dns/routing.ts +593 -0
  76. package/src/email/advanced/analytics.ts +445 -0
  77. package/src/email/advanced/index.ts +11 -0
  78. package/src/email/advanced/rules.ts +465 -0
  79. package/src/email/advanced/scheduling.ts +352 -0
  80. package/src/email/advanced/search.ts +412 -0
  81. package/src/email/advanced/shared-mailboxes.ts +404 -0
  82. package/src/email/advanced/templates.ts +455 -0
  83. package/src/email/advanced/threading.ts +281 -0
  84. package/src/email/analytics.ts +467 -0
  85. package/src/email/bounce-handling.ts +425 -0
  86. package/src/email/email.test.ts +431 -0
  87. package/src/email/handlers/__tests__/inbound.test.ts +38 -0
  88. package/src/email/handlers/__tests__/outbound.test.ts +37 -0
  89. package/src/email/handlers/converter.ts +227 -0
  90. package/src/email/handlers/feedback.ts +228 -0
  91. package/src/email/handlers/inbound.ts +169 -0
  92. package/src/email/handlers/outbound.ts +178 -0
  93. package/src/email/index.ts +15 -0
  94. package/src/email/reputation.ts +303 -0
  95. package/src/email/templates.ts +352 -0
  96. package/src/errors/index.test.ts +434 -0
  97. package/src/errors/index.ts +416 -0
  98. package/src/health-checks/index.ts +40 -0
  99. package/src/index.ts +360 -0
  100. package/src/intrinsic-functions.ts +118 -0
  101. package/src/lambda/concurrency.ts +330 -0
  102. package/src/lambda/destinations.ts +345 -0
  103. package/src/lambda/dlq.ts +425 -0
  104. package/src/lambda/index.ts +11 -0
  105. package/src/lambda/lambda.test.ts +840 -0
  106. package/src/lambda/layers.ts +263 -0
  107. package/src/lambda/versions.ts +376 -0
  108. package/src/lambda/vpc.ts +399 -0
  109. package/src/local/config.ts +114 -0
  110. package/src/local/index.ts +6 -0
  111. package/src/local/mock-aws.ts +351 -0
  112. package/src/modules/ai.ts +340 -0
  113. package/src/modules/api.ts +478 -0
  114. package/src/modules/auth.ts +805 -0
  115. package/src/modules/cache.ts +417 -0
  116. package/src/modules/cdn.ts +1062 -0
  117. package/src/modules/communication.ts +1094 -0
  118. package/src/modules/compute.ts +3348 -0
  119. package/src/modules/database.ts +554 -0
  120. package/src/modules/deployment.ts +1079 -0
  121. package/src/modules/dns.ts +337 -0
  122. package/src/modules/email.ts +1538 -0
  123. package/src/modules/filesystem.ts +515 -0
  124. package/src/modules/index.ts +32 -0
  125. package/src/modules/messaging.ts +486 -0
  126. package/src/modules/monitoring.ts +2086 -0
  127. package/src/modules/network.ts +664 -0
  128. package/src/modules/parameter-store.ts +325 -0
  129. package/src/modules/permissions.ts +1081 -0
  130. package/src/modules/phone.ts +494 -0
  131. package/src/modules/queue.ts +1260 -0
  132. package/src/modules/redirects.ts +464 -0
  133. package/src/modules/registry.ts +699 -0
  134. package/src/modules/search.ts +401 -0
  135. package/src/modules/secrets.ts +416 -0
  136. package/src/modules/security.ts +731 -0
  137. package/src/modules/sms.ts +389 -0
  138. package/src/modules/storage.ts +1120 -0
  139. package/src/modules/workflow.ts +680 -0
  140. package/src/multi-account/config.ts +521 -0
  141. package/src/multi-account/index.ts +7 -0
  142. package/src/multi-account/manager.ts +427 -0
  143. package/src/multi-region/cross-region.ts +410 -0
  144. package/src/multi-region/index.ts +8 -0
  145. package/src/multi-region/manager.ts +483 -0
  146. package/src/multi-region/regions.ts +435 -0
  147. package/src/network-security/index.ts +48 -0
  148. package/src/observability/index.ts +9 -0
  149. package/src/observability/logs.ts +522 -0
  150. package/src/observability/metrics.ts +460 -0
  151. package/src/observability/observability.test.ts +782 -0
  152. package/src/observability/synthetics.ts +568 -0
  153. package/src/observability/xray.ts +358 -0
  154. package/src/phone/advanced/analytics.ts +349 -0
  155. package/src/phone/advanced/callbacks.ts +428 -0
  156. package/src/phone/advanced/index.ts +8 -0
  157. package/src/phone/advanced/ivr-builder.ts +504 -0
  158. package/src/phone/advanced/recording.ts +310 -0
  159. package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
  160. package/src/phone/handlers/incoming-call.ts +117 -0
  161. package/src/phone/handlers/missed-call.ts +116 -0
  162. package/src/phone/handlers/voicemail.ts +179 -0
  163. package/src/phone/index.ts +9 -0
  164. package/src/presets/api-backend.ts +134 -0
  165. package/src/presets/data-pipeline.ts +204 -0
  166. package/src/presets/extend.test.ts +295 -0
  167. package/src/presets/extend.ts +297 -0
  168. package/src/presets/fullstack-app.ts +144 -0
  169. package/src/presets/index.ts +27 -0
  170. package/src/presets/jamstack.ts +135 -0
  171. package/src/presets/microservices.ts +167 -0
  172. package/src/presets/ml-api.ts +208 -0
  173. package/src/presets/nodejs-server.ts +104 -0
  174. package/src/presets/nodejs-serverless.ts +114 -0
  175. package/src/presets/realtime-app.ts +184 -0
  176. package/src/presets/static-site.ts +64 -0
  177. package/src/presets/traditional-web-app.ts +339 -0
  178. package/src/presets/wordpress.ts +138 -0
  179. package/src/preview/github.test.ts +249 -0
  180. package/src/preview/github.ts +297 -0
  181. package/src/preview/index.ts +37 -0
  182. package/src/preview/manager.test.ts +440 -0
  183. package/src/preview/manager.ts +326 -0
  184. package/src/preview/notifications.test.ts +582 -0
  185. package/src/preview/notifications.ts +341 -0
  186. package/src/queue/batch-processing.ts +402 -0
  187. package/src/queue/dlq-monitoring.ts +402 -0
  188. package/src/queue/fifo.ts +342 -0
  189. package/src/queue/index.ts +9 -0
  190. package/src/queue/management.ts +428 -0
  191. package/src/queue/queue.test.ts +429 -0
  192. package/src/resource-mgmt/index.ts +39 -0
  193. package/src/resource-naming.ts +62 -0
  194. package/src/s3/index.ts +523 -0
  195. package/src/schema/cloud-config.schema.json +554 -0
  196. package/src/schema/index.ts +68 -0
  197. package/src/security/certificate-manager.ts +492 -0
  198. package/src/security/index.ts +9 -0
  199. package/src/security/scanning.ts +545 -0
  200. package/src/security/secrets-manager.ts +476 -0
  201. package/src/security/secrets-rotation.ts +456 -0
  202. package/src/security/security.test.ts +738 -0
  203. package/src/sms/advanced/ab-testing.ts +389 -0
  204. package/src/sms/advanced/analytics.ts +336 -0
  205. package/src/sms/advanced/campaigns.ts +523 -0
  206. package/src/sms/advanced/chatbot.ts +224 -0
  207. package/src/sms/advanced/index.ts +10 -0
  208. package/src/sms/advanced/link-tracking.ts +248 -0
  209. package/src/sms/advanced/mms.ts +308 -0
  210. package/src/sms/handlers/__tests__/send.test.ts +40 -0
  211. package/src/sms/handlers/delivery-status.ts +133 -0
  212. package/src/sms/handlers/receive.ts +162 -0
  213. package/src/sms/handlers/send.ts +174 -0
  214. package/src/sms/index.ts +9 -0
  215. package/src/stack-diff.ts +389 -0
  216. package/src/static-site/index.ts +85 -0
  217. package/src/template-builder.ts +110 -0
  218. package/src/template-validator.ts +574 -0
  219. package/src/utils/cache.ts +291 -0
  220. package/src/utils/diff.ts +269 -0
  221. package/src/utils/hash.ts +227 -0
  222. package/src/utils/index.ts +8 -0
  223. package/src/utils/parallel.ts +294 -0
  224. package/src/validators/credentials.test.ts +274 -0
  225. package/src/validators/credentials.ts +233 -0
  226. package/src/validators/quotas.test.ts +434 -0
  227. package/src/validators/quotas.ts +217 -0
  228. package/test/ai.test.ts +327 -0
  229. package/test/api.test.ts +511 -0
  230. package/test/auth.test.ts +632 -0
  231. package/test/cache.test.ts +406 -0
  232. package/test/cdn.test.ts +247 -0
  233. package/test/compute.test.ts +861 -0
  234. package/test/database.test.ts +523 -0
  235. package/test/deployment.test.ts +499 -0
  236. package/test/dns.test.ts +270 -0
  237. package/test/email.test.ts +439 -0
  238. package/test/filesystem.test.ts +382 -0
  239. package/test/integration.test.ts +350 -0
  240. package/test/messaging.test.ts +514 -0
  241. package/test/monitoring.test.ts +634 -0
  242. package/test/network.test.ts +425 -0
  243. package/test/permissions.test.ts +488 -0
  244. package/test/queue.test.ts +484 -0
  245. package/test/registry.test.ts +306 -0
  246. package/test/security.test.ts +462 -0
  247. package/test/storage.test.ts +463 -0
  248. package/test/template-validator.test.ts +559 -0
  249. package/test/workflow.test.ts +592 -0
  250. package/tsconfig.json +16 -0
  251. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,458 @@
1
+ /**
2
+ * Progress tracking with ETA and status updates
3
+ * Provides interactive progress bars and status indicators
4
+ */
5
+
6
+ export interface ProgressOptions {
7
+ total: number
8
+ current?: number
9
+ width?: number
10
+ format?: string
11
+ message?: string
12
+ showETA?: boolean
13
+ showPercentage?: boolean
14
+ showCounter?: boolean
15
+ }
16
+
17
+ /**
18
+ * Progress bar with ETA calculation
19
+ */
20
+ export class ProgressBar {
21
+ private total: number
22
+ private current: number = 0
23
+ private width: number
24
+ private format: string
25
+ private message: string
26
+ private showETA: boolean
27
+ private showPercentage: boolean
28
+ private showCounter: boolean
29
+ private startTime: number = Date.now()
30
+ private lastUpdate: number = 0
31
+
32
+ constructor(options: ProgressOptions) {
33
+ this.total = options.total
34
+ this.current = options.current || 0
35
+ this.width = options.width || 40
36
+ this.format = options.format || ':message :bar :percent :eta'
37
+ this.message = options.message || 'Progress'
38
+ this.showETA = options.showETA !== false
39
+ this.showPercentage = options.showPercentage !== false
40
+ this.showCounter = options.showCounter !== false
41
+ }
42
+
43
+ /**
44
+ * Update progress
45
+ */
46
+ tick(amount: number = 1): void {
47
+ this.current = Math.min(this.total, this.current + amount)
48
+ this.lastUpdate = Date.now()
49
+ }
50
+
51
+ /**
52
+ * Set progress to specific value
53
+ */
54
+ update(current: number): void {
55
+ this.current = Math.min(this.total, Math.max(0, current))
56
+ this.lastUpdate = Date.now()
57
+ }
58
+
59
+ /**
60
+ * Set message
61
+ */
62
+ setMessage(message: string): void {
63
+ this.message = message
64
+ }
65
+
66
+ /**
67
+ * Get current progress percentage
68
+ */
69
+ getPercentage(): number {
70
+ return Math.min(100, (this.current / this.total) * 100)
71
+ }
72
+
73
+ /**
74
+ * Calculate ETA in milliseconds
75
+ */
76
+ getETA(): number {
77
+ if (this.current === 0) return 0
78
+
79
+ const elapsed = Date.now() - this.startTime
80
+ const rate = this.current / elapsed
81
+ const remaining = this.total - this.current
82
+
83
+ return Math.round(remaining / rate)
84
+ }
85
+
86
+ /**
87
+ * Format ETA as human-readable string
88
+ */
89
+ getETAFormatted(): string {
90
+ if (this.current === 0) return 'calculating...'
91
+ if (this.current === this.total) return 'complete'
92
+
93
+ const eta = this.getETA()
94
+
95
+ if (eta < 1000) return '< 1s'
96
+ if (eta < 60000) return `${Math.round(eta / 1000)}s`
97
+
98
+ const minutes = Math.floor(eta / 60000)
99
+ if (minutes < 60) return `${minutes}m`
100
+
101
+ const hours = Math.floor(minutes / 60)
102
+ const remainingMinutes = minutes % 60
103
+
104
+ if (hours < 24) {
105
+ return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`
106
+ }
107
+
108
+ const days = Math.floor(hours / 24)
109
+ const remainingHours = hours % 24
110
+
111
+ return remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`
112
+ }
113
+
114
+ /**
115
+ * Render progress bar
116
+ */
117
+ render(): string {
118
+ const percentage = this.getPercentage()
119
+ const completed = Math.floor((this.width * this.current) / this.total)
120
+ const remaining = this.width - completed
121
+
122
+ const bar = '█'.repeat(completed) + '░'.repeat(remaining)
123
+ const percentStr = `${percentage.toFixed(1)}%`
124
+ const counterStr = `${this.current}/${this.total}`
125
+ const etaStr = this.showETA ? `ETA: ${this.getETAFormatted()}` : ''
126
+
127
+ let output = this.format
128
+ .replace(':message', this.message)
129
+ .replace(':bar', bar)
130
+ .replace(':percent', this.showPercentage ? percentStr : '')
131
+ .replace(':counter', this.showCounter ? counterStr : '')
132
+ .replace(':eta', etaStr)
133
+ .replace(':current', String(this.current))
134
+ .replace(':total', String(this.total))
135
+
136
+ // Clean up extra spaces
137
+ output = output.replace(/\s+/g, ' ').trim()
138
+
139
+ return output
140
+ }
141
+
142
+ /**
143
+ * Check if complete
144
+ */
145
+ isComplete(): boolean {
146
+ return this.current >= this.total
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Multi-step progress tracker
152
+ */
153
+ export interface Step {
154
+ name: string
155
+ status: 'pending' | 'active' | 'completed' | 'failed' | 'skipped'
156
+ message?: string
157
+ error?: string
158
+ }
159
+
160
+ export class MultiStepProgress {
161
+ private steps: Step[]
162
+ private currentStepIndex: number = 0
163
+
164
+ constructor(stepNames: string[]) {
165
+ this.steps = stepNames.map(name => ({
166
+ name,
167
+ status: 'pending',
168
+ }))
169
+ }
170
+
171
+ /**
172
+ * Start a step
173
+ */
174
+ startStep(index: number, message?: string): void {
175
+ if (index >= 0 && index < this.steps.length) {
176
+ this.steps[index].status = 'active'
177
+ this.steps[index].message = message
178
+ this.currentStepIndex = index
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Complete current step
184
+ */
185
+ completeStep(message?: string): void {
186
+ if (this.currentStepIndex >= 0 && this.currentStepIndex < this.steps.length) {
187
+ this.steps[this.currentStepIndex].status = 'completed'
188
+ this.steps[this.currentStepIndex].message = message
189
+ this.currentStepIndex++
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Fail current step
195
+ */
196
+ failStep(error: string): void {
197
+ if (this.currentStepIndex >= 0 && this.currentStepIndex < this.steps.length) {
198
+ this.steps[this.currentStepIndex].status = 'failed'
199
+ this.steps[this.currentStepIndex].error = error
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Skip current step
205
+ */
206
+ skipStep(message?: string): void {
207
+ if (this.currentStepIndex >= 0 && this.currentStepIndex < this.steps.length) {
208
+ this.steps[this.currentStepIndex].status = 'skipped'
209
+ this.steps[this.currentStepIndex].message = message
210
+ this.currentStepIndex++
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Get step by index
216
+ */
217
+ getStep(index: number): Step | undefined {
218
+ return this.steps[index]
219
+ }
220
+
221
+ /**
222
+ * Get all steps
223
+ */
224
+ getSteps(): Step[] {
225
+ return this.steps
226
+ }
227
+
228
+ /**
229
+ * Render progress
230
+ */
231
+ render(): string {
232
+ const lines: string[] = []
233
+
234
+ for (const step of this.steps) {
235
+ const icon = this.getStatusIcon(step.status)
236
+ let line = `${icon} ${step.name}`
237
+
238
+ if (step.message) {
239
+ line += ` - ${step.message}`
240
+ }
241
+
242
+ if (step.error) {
243
+ line += `\n Error: ${step.error}`
244
+ }
245
+
246
+ lines.push(line)
247
+ }
248
+
249
+ return lines.join('\n')
250
+ }
251
+
252
+ /**
253
+ * Get status icon
254
+ */
255
+ private getStatusIcon(status: Step['status']): string {
256
+ switch (status) {
257
+ case 'pending':
258
+ return '○'
259
+ case 'active':
260
+ return '◐'
261
+ case 'completed':
262
+ return '✓'
263
+ case 'failed':
264
+ return '✗'
265
+ case 'skipped':
266
+ return '⊘'
267
+ default:
268
+ return '○'
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Check if all steps complete
274
+ */
275
+ isComplete(): boolean {
276
+ return this.steps.every(step => step.status === 'completed' || step.status === 'skipped')
277
+ }
278
+
279
+ /**
280
+ * Check if any step failed
281
+ */
282
+ hasFailed(): boolean {
283
+ return this.steps.some(step => step.status === 'failed')
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Spinner for indeterminate progress
289
+ */
290
+ export class Spinner {
291
+ private frames: string[] = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
292
+ private currentFrame: number = 0
293
+ private message: string
294
+ private interval: Timer | null = null
295
+
296
+ constructor(message: string = 'Loading...') {
297
+ this.message = message
298
+ }
299
+
300
+ /**
301
+ * Start spinner
302
+ */
303
+ start(): void {
304
+ if (this.interval) return
305
+
306
+ this.interval = setInterval(() => {
307
+ this.currentFrame = (this.currentFrame + 1) % this.frames.length
308
+ // In a real CLI, we'd use process.stdout.write here
309
+ }, 80)
310
+ }
311
+
312
+ /**
313
+ * Stop spinner
314
+ */
315
+ stop(): void {
316
+ if (this.interval) {
317
+ clearInterval(this.interval)
318
+ this.interval = null
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Set message
324
+ */
325
+ setMessage(message: string): void {
326
+ this.message = message
327
+ }
328
+
329
+ /**
330
+ * Render current frame
331
+ */
332
+ render(): string {
333
+ return `${this.frames[this.currentFrame]} ${this.message}`
334
+ }
335
+
336
+ /**
337
+ * Succeed with checkmark
338
+ */
339
+ succeed(message?: string): string {
340
+ this.stop()
341
+ return `✓ ${message || this.message}`
342
+ }
343
+
344
+ /**
345
+ * Fail with X
346
+ */
347
+ fail(message?: string): string {
348
+ this.stop()
349
+ return `✗ ${message || this.message}`
350
+ }
351
+
352
+ /**
353
+ * Warn with exclamation
354
+ */
355
+ warn(message?: string): string {
356
+ this.stop()
357
+ return `⚠ ${message || this.message}`
358
+ }
359
+
360
+ /**
361
+ * Info with i
362
+ */
363
+ info(message?: string): string {
364
+ this.stop()
365
+ return `ℹ ${message || this.message}`
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Task list with status tracking
371
+ */
372
+ export interface Task {
373
+ title: string
374
+ status: 'pending' | 'running' | 'completed' | 'failed'
375
+ output?: string
376
+ }
377
+
378
+ export class TaskList {
379
+ private tasks: Task[] = []
380
+
381
+ /**
382
+ * Add task
383
+ */
384
+ add(title: string): number {
385
+ this.tasks.push({
386
+ title,
387
+ status: 'pending',
388
+ })
389
+ return this.tasks.length - 1
390
+ }
391
+
392
+ /**
393
+ * Start task
394
+ */
395
+ start(index: number): void {
396
+ if (index >= 0 && index < this.tasks.length) {
397
+ this.tasks[index].status = 'running'
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Complete task
403
+ */
404
+ complete(index: number, output?: string): void {
405
+ if (index >= 0 && index < this.tasks.length) {
406
+ this.tasks[index].status = 'completed'
407
+ this.tasks[index].output = output
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Fail task
413
+ */
414
+ fail(index: number, output?: string): void {
415
+ if (index >= 0 && index < this.tasks.length) {
416
+ this.tasks[index].status = 'failed'
417
+ this.tasks[index].output = output
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Render task list
423
+ */
424
+ render(): string {
425
+ const lines: string[] = []
426
+
427
+ for (const task of this.tasks) {
428
+ const icon = this.getStatusIcon(task.status)
429
+ let line = `${icon} ${task.title}`
430
+
431
+ if (task.output) {
432
+ line += `\n ${task.output}`
433
+ }
434
+
435
+ lines.push(line)
436
+ }
437
+
438
+ return lines.join('\n')
439
+ }
440
+
441
+ /**
442
+ * Get status icon
443
+ */
444
+ private getStatusIcon(status: Task['status']): string {
445
+ switch (status) {
446
+ case 'pending':
447
+ return '○'
448
+ case 'running':
449
+ return '◐'
450
+ case 'completed':
451
+ return '✓'
452
+ case 'failed':
453
+ return '✗'
454
+ default:
455
+ return '○'
456
+ }
457
+ }
458
+ }