t3code-cli 0.10.0 → 0.12.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 (328) hide show
  1. package/README.md +75 -11
  2. package/dist/application.js +2 -2
  3. package/dist/auth.js +1 -1
  4. package/dist/bin.js +86754 -2675
  5. package/dist/cli.js +15 -2
  6. package/dist/config.js +120 -2
  7. package/dist/connection.js +2 -12
  8. package/dist/index.js +1 -1
  9. package/dist/node.js +6 -0
  10. package/dist/orchestration.js +2 -2
  11. package/dist/rpc.js +2 -2
  12. package/dist/runtime.js +2 -2
  13. package/dist/shared.js +43850 -38040
  14. package/dist/src/application/actions.d.ts +25 -0
  15. package/dist/src/application/index.d.ts +3 -2
  16. package/dist/src/application/layer.d.ts +99 -842
  17. package/dist/src/application/model-selection.d.ts +17 -2
  18. package/dist/src/application/models.d.ts +1 -87
  19. package/dist/src/application/project-commands.d.ts +31 -2
  20. package/dist/src/application/projects.d.ts +15 -137
  21. package/dist/src/application/service.d.ts +147 -5
  22. package/dist/src/application/shell-sequence.d.ts +5 -4
  23. package/dist/src/application/terminals.d.ts +66 -0
  24. package/dist/src/application/thread-commands.d.ts +48 -1
  25. package/dist/src/application/thread-update.d.ts +8 -0
  26. package/dist/src/application/thread-wait.d.ts +4 -6
  27. package/dist/src/application/threads.d.ts +36 -14
  28. package/dist/src/auth/error.d.ts +2 -1
  29. package/dist/src/auth/index.d.ts +1 -1
  30. package/dist/src/auth/layer.d.ts +34 -6
  31. package/dist/src/auth/local-base-dir.d.ts +5 -2
  32. package/dist/src/auth/local-origin.d.ts +3 -3
  33. package/dist/src/auth/local-token.d.ts +3 -3
  34. package/dist/src/auth/service.d.ts +15 -3
  35. package/dist/src/auth/type.d.ts +40 -1
  36. package/dist/src/cli/env/flag.d.ts +2 -0
  37. package/dist/src/cli/env/index.d.ts +2 -0
  38. package/dist/src/cli/env/selection-layer.d.ts +3 -0
  39. package/dist/src/cli/flags.d.ts +5 -0
  40. package/dist/src/cli/format/index.d.ts +1 -0
  41. package/dist/src/cli/format/output.d.ts +12 -0
  42. package/dist/src/cli/index.d.ts +4 -0
  43. package/dist/src/cli/runtime/index.d.ts +1 -0
  44. package/dist/src/cli/runtime/service.d.ts +16 -0
  45. package/dist/src/{scope → cli/scope}/resolve.d.ts +5 -4
  46. package/dist/src/config/config.d.ts +49 -0
  47. package/dist/src/config/credential/cipher-node.d.ts +3 -0
  48. package/dist/src/config/credential/cipher-web.d.ts +3 -0
  49. package/dist/src/config/credential/cipher.d.ts +29 -0
  50. package/dist/src/config/credential/env.d.ts +3 -0
  51. package/dist/src/config/credential/error.d.ts +8 -0
  52. package/dist/src/config/credential/index.d.ts +6 -0
  53. package/dist/src/config/credential/service.d.ts +34 -0
  54. package/dist/src/config/env/env.d.ts +55 -0
  55. package/dist/src/config/env/index.d.ts +2 -0
  56. package/dist/src/{layout/base-dir.d.ts → config/env/layout.d.ts} +0 -1
  57. package/dist/src/config/environment-name/index.d.ts +1 -0
  58. package/dist/src/config/environment-name/name.d.ts +10 -0
  59. package/dist/src/config/error.d.ts +4 -11
  60. package/dist/src/config/index.d.ts +10 -5
  61. package/dist/src/config/keystore/error.d.ts +8 -0
  62. package/dist/src/config/keystore/file.d.ts +9 -0
  63. package/dist/src/config/keystore/index.d.ts +4 -0
  64. package/dist/src/config/keystore/keyring-node.d.ts +4 -0
  65. package/dist/src/config/keystore/service.d.ts +33 -0
  66. package/dist/src/config/paths/index.d.ts +1 -0
  67. package/dist/src/config/paths/paths.d.ts +6 -0
  68. package/dist/src/config/persist/file-mode.d.ts +4 -0
  69. package/dist/src/config/persist/migration.d.ts +68 -0
  70. package/dist/src/config/persist/persist.d.ts +40 -0
  71. package/dist/src/config/persist/schema.d.ts +68 -0
  72. package/dist/src/config/resolve/resolve.d.ts +40 -0
  73. package/dist/src/config/selection/index.d.ts +1 -0
  74. package/dist/src/config/selection/resolve.d.ts +4 -0
  75. package/dist/src/config/selection/service.d.ts +10 -0
  76. package/dist/src/config/types.d.ts +36 -0
  77. package/dist/src/config/url/error.d.ts +10 -0
  78. package/dist/src/config/url/index.d.ts +2 -0
  79. package/dist/src/connection/index.d.ts +0 -1
  80. package/dist/src/contracts/index.d.ts +1 -1
  81. package/dist/src/domain/error.d.ts +27 -1
  82. package/dist/src/domain/helpers.d.ts +8 -2
  83. package/dist/src/domain/model-config.d.ts +30 -1
  84. package/dist/src/domain/thread-activities.d.ts +1 -1
  85. package/dist/src/domain/thread-lifecycle.d.ts +3 -2
  86. package/dist/src/index.d.ts +1 -2
  87. package/dist/src/{connection/node.d.ts → node/connection.d.ts} +1 -1
  88. package/dist/src/node/index.d.ts +1 -0
  89. package/dist/src/orchestration/index.d.ts +1 -1
  90. package/dist/src/orchestration/layer.d.ts +120 -829
  91. package/dist/src/orchestration/service.d.ts +2 -1
  92. package/dist/src/rpc/error.d.ts +5 -3
  93. package/dist/src/rpc/index.d.ts +5 -1
  94. package/dist/src/rpc/layer.d.ts +1166 -40
  95. package/dist/src/rpc/operation.d.ts +6280 -0
  96. package/dist/src/rpc/ws-group.d.ts +372 -27
  97. package/dist/src/runtime/index.d.ts +1 -2
  98. package/dist/src/runtime/layer.d.ts +12 -8
  99. package/dist/src/t3tools/index.d.ts +1 -1
  100. package/dist/t3tools.js +2 -2
  101. package/package.json +27 -19
  102. package/src/application/actions.test.ts +328 -0
  103. package/src/application/actions.ts +475 -0
  104. package/src/application/index.ts +32 -2
  105. package/src/application/layer.ts +43 -5
  106. package/src/application/model-selection.ts +70 -4
  107. package/src/application/models.ts +14 -14
  108. package/src/application/project-commands.ts +38 -5
  109. package/src/application/projects.ts +49 -20
  110. package/src/application/service.ts +275 -78
  111. package/src/application/shell-sequence.ts +6 -8
  112. package/src/application/terminals.ts +208 -0
  113. package/src/application/thread-commands.test.ts +43 -0
  114. package/src/application/thread-commands.ts +82 -1
  115. package/src/application/thread-update.ts +72 -0
  116. package/src/application/thread-wait.ts +47 -48
  117. package/src/application/threads.test.ts +172 -0
  118. package/src/application/threads.ts +128 -45
  119. package/src/auth/error.ts +2 -1
  120. package/src/auth/index.ts +7 -0
  121. package/src/auth/layer.test-utils.ts +34 -0
  122. package/src/auth/layer.ts +116 -34
  123. package/src/auth/local-base-dir.ts +13 -6
  124. package/src/auth/local-origin.ts +10 -7
  125. package/src/auth/local-token.ts +28 -23
  126. package/src/auth/service.test.ts +200 -0
  127. package/src/auth/service.ts +27 -3
  128. package/src/auth/transport.ts +2 -2
  129. package/src/auth/type.ts +47 -1
  130. package/src/bin.ts +26 -6
  131. package/src/cli/action-format.ts +110 -0
  132. package/src/cli/action.ts +398 -0
  133. package/src/cli/app.ts +8 -0
  134. package/src/cli/auth.ts +76 -23
  135. package/src/cli/env/flag.ts +8 -0
  136. package/src/cli/env/index.ts +2 -0
  137. package/src/cli/env/selection-layer.test.ts +49 -0
  138. package/src/cli/env/selection-layer.ts +27 -0
  139. package/src/cli/env.ts +107 -0
  140. package/src/cli/error.ts +26 -3
  141. package/src/cli/extra-args.test.ts +32 -0
  142. package/src/cli/extra-args.ts +22 -0
  143. package/src/cli/flags.ts +26 -1
  144. package/src/cli/format/auth.ts +92 -0
  145. package/src/cli/format/human.ts +131 -0
  146. package/src/cli/format/index.ts +12 -0
  147. package/src/cli/format/model.ts +29 -0
  148. package/src/cli/format/output.ts +51 -0
  149. package/src/cli/format/project.ts +32 -0
  150. package/src/cli/format/terminal.ts +87 -0
  151. package/src/cli/format/thread.test.ts +34 -0
  152. package/src/cli/format/thread.ts +185 -0
  153. package/src/cli/index.ts +20 -0
  154. package/src/cli/input/layer.ts +20 -0
  155. package/src/cli/input/service.ts +1 -0
  156. package/src/cli/interaction/confirm.ts +64 -0
  157. package/src/cli/interaction/self-action.ts +36 -0
  158. package/src/cli/message-input.ts +1 -1
  159. package/src/cli/model.ts +9 -5
  160. package/src/cli/project.ts +15 -8
  161. package/src/cli/projects/delete.ts +51 -0
  162. package/src/cli/require.ts +9 -7
  163. package/src/cli/runtime/index.ts +1 -0
  164. package/src/cli/runtime/service.ts +28 -0
  165. package/src/{scope → cli/scope}/resolve.ts +11 -9
  166. package/src/cli/terminal/attach.ts +29 -0
  167. package/src/cli/terminal/create.ts +55 -0
  168. package/src/cli/terminal/destroy.ts +62 -0
  169. package/src/cli/terminal/encoding.test.ts +63 -0
  170. package/src/cli/terminal/encoding.ts +23 -0
  171. package/src/cli/terminal/error.ts +14 -0
  172. package/src/cli/terminal/io-node-layer.ts +82 -0
  173. package/src/cli/terminal/io-service.ts +25 -0
  174. package/src/cli/terminal/list.ts +36 -0
  175. package/src/cli/terminal/read.test-utils.ts +48 -0
  176. package/src/cli/terminal/read.test.ts +35 -0
  177. package/src/cli/terminal/read.ts +99 -0
  178. package/src/cli/terminal/scope.ts +35 -0
  179. package/src/cli/terminal/shared.ts +250 -0
  180. package/src/cli/terminal/stream.ts +61 -0
  181. package/src/cli/terminal/wait.test.ts +148 -0
  182. package/src/cli/terminal/wait.ts +223 -0
  183. package/src/cli/terminal/write.test.ts +34 -0
  184. package/src/cli/terminal/write.ts +133 -0
  185. package/src/cli/terminal.ts +26 -0
  186. package/src/cli/thread.ts +8 -0
  187. package/src/cli/threads/approve.ts +11 -7
  188. package/src/cli/threads/archive.ts +23 -25
  189. package/src/cli/threads/callback.ts +6 -4
  190. package/src/cli/threads/delete.ts +67 -0
  191. package/src/cli/threads/interrupt.ts +57 -0
  192. package/src/cli/threads/list.test.ts +71 -0
  193. package/src/cli/threads/list.ts +25 -13
  194. package/src/cli/threads/messages.ts +11 -7
  195. package/src/cli/threads/respond.ts +11 -7
  196. package/src/cli/threads/send.ts +39 -9
  197. package/src/cli/threads/show.ts +11 -7
  198. package/src/cli/threads/start.ts +18 -13
  199. package/src/cli/threads/unarchive.ts +48 -0
  200. package/src/cli/threads/update.ts +155 -0
  201. package/src/cli/threads/wait.ts +11 -7
  202. package/src/cli/wait-events.ts +1 -1
  203. package/src/config/config.ts +260 -0
  204. package/src/config/credential/cipher-node.ts +40 -0
  205. package/src/config/credential/cipher-web.ts +82 -0
  206. package/src/config/credential/cipher.ts +39 -0
  207. package/src/config/credential/env.ts +13 -0
  208. package/src/config/credential/error.ts +9 -0
  209. package/src/config/credential/index.ts +6 -0
  210. package/src/config/credential/service.test-utils.ts +25 -0
  211. package/src/config/credential/service.test.ts +116 -0
  212. package/src/config/credential/service.ts +205 -0
  213. package/src/config/env/env.test-utils.ts +14 -0
  214. package/src/config/env/env.ts +81 -0
  215. package/src/config/env/index.ts +2 -0
  216. package/src/{layout/base-dir.ts → config/env/layout.ts} +0 -9
  217. package/src/config/environment-name/index.ts +7 -0
  218. package/src/config/environment-name/name.test.ts +66 -0
  219. package/src/config/environment-name/name.ts +49 -0
  220. package/src/config/error.ts +6 -8
  221. package/src/config/index.ts +16 -5
  222. package/src/config/keystore/error.ts +9 -0
  223. package/src/config/keystore/file.ts +96 -0
  224. package/src/config/keystore/index.ts +4 -0
  225. package/src/config/keystore/keyring-node.test.ts +12 -0
  226. package/src/config/keystore/keyring-node.ts +100 -0
  227. package/src/config/keystore/service.test-utils.ts +15 -0
  228. package/src/config/keystore/service.test.ts +18 -0
  229. package/src/config/keystore/service.ts +29 -0
  230. package/src/config/layer.test-utils.ts +45 -0
  231. package/src/config/layer.test.ts +209 -0
  232. package/src/config/paths/index.ts +1 -0
  233. package/src/config/paths/paths.ts +36 -0
  234. package/src/config/persist/file-mode.ts +21 -0
  235. package/src/config/persist/migration.test.ts +71 -0
  236. package/src/config/persist/migration.ts +82 -0
  237. package/src/config/persist/persist.test.ts +113 -0
  238. package/src/config/persist/persist.ts +65 -0
  239. package/src/config/persist/schema.ts +37 -0
  240. package/src/config/platform.test-utils.ts +20 -0
  241. package/src/config/resolve/resolve.test-utils.ts +20 -0
  242. package/src/config/resolve/resolve.test.ts +137 -0
  243. package/src/config/resolve/resolve.ts +104 -0
  244. package/src/config/selection/index.ts +1 -0
  245. package/src/config/selection/resolve.ts +14 -0
  246. package/src/config/selection/service.ts +29 -0
  247. package/src/config/temp-home.test-utils.ts +7 -0
  248. package/src/config/types.ts +49 -0
  249. package/src/config/url/error.ts +8 -0
  250. package/src/config/url/index.ts +7 -0
  251. package/src/config/url/url.test.ts +31 -0
  252. package/src/connection/index.ts +0 -1
  253. package/src/contracts/index.ts +1 -1
  254. package/src/domain/error.ts +39 -1
  255. package/src/domain/helpers.test.ts +1 -1
  256. package/src/domain/helpers.ts +1 -1
  257. package/src/domain/model-config.ts +5 -1
  258. package/src/domain/thread-activities.test.ts +1 -1
  259. package/src/domain/thread-activities.ts +1 -1
  260. package/src/domain/thread-lifecycle.test.ts +1 -1
  261. package/src/domain/thread-lifecycle.ts +15 -1
  262. package/src/effect.test-utils.ts +25 -0
  263. package/src/index.ts +1 -1
  264. package/src/{connection/node.ts → node/connection.ts} +1 -1
  265. package/src/node/index.ts +1 -0
  266. package/src/orchestration/index.ts +1 -1
  267. package/src/orchestration/layer.ts +34 -86
  268. package/src/orchestration/service.ts +5 -1
  269. package/src/rpc/error.ts +12 -2
  270. package/src/rpc/index.ts +5 -1
  271. package/src/rpc/layer.ts +1 -1
  272. package/src/rpc/operation.ts +83 -0
  273. package/src/rpc/ws-group.ts +25 -1
  274. package/src/runtime/index.ts +2 -1
  275. package/src/runtime/layer.test-utils.ts +32 -0
  276. package/src/runtime/layer.test.ts +66 -0
  277. package/src/runtime/layer.ts +22 -11
  278. package/src/t3tools/index.ts +1 -1
  279. package/src/types/marked-terminal.d.ts +11 -0
  280. package/dist/layout.js +0 -2
  281. package/dist/scope.js +0 -2
  282. package/dist/src/cli/output-format.d.ts +0 -9
  283. package/dist/src/config/layer.d.ts +0 -22
  284. package/dist/src/config/service.d.ts +0 -22
  285. package/dist/src/environment/layer.d.ts +0 -3
  286. package/dist/src/environment/service.d.ts +0 -12
  287. package/dist/src/layout/index.d.ts +0 -1
  288. package/dist/upstream-t3code/packages/contracts/src/auth.d.ts +0 -441
  289. package/dist/upstream-t3code/packages/contracts/src/baseSchemas.d.ts +0 -38
  290. package/dist/upstream-t3code/packages/contracts/src/desktopBootstrap.d.ts +0 -14
  291. package/dist/upstream-t3code/packages/contracts/src/editor.d.ts +0 -124
  292. package/dist/upstream-t3code/packages/contracts/src/environment.d.ts +0 -64
  293. package/dist/upstream-t3code/packages/contracts/src/environmentHttp.d.ts +0 -785
  294. package/dist/upstream-t3code/packages/contracts/src/filesystem.d.ts +0 -26
  295. package/dist/upstream-t3code/packages/contracts/src/git.d.ts +0 -500
  296. package/dist/upstream-t3code/packages/contracts/src/index.d.ts +0 -25
  297. package/dist/upstream-t3code/packages/contracts/src/ipc.d.ts +0 -496
  298. package/dist/upstream-t3code/packages/contracts/src/keybindings.d.ts +0 -133
  299. package/dist/upstream-t3code/packages/contracts/src/model.d.ts +0 -112
  300. package/dist/upstream-t3code/packages/contracts/src/orchestration.d.ts +0 -7338
  301. package/dist/upstream-t3code/packages/contracts/src/project.d.ts +0 -45
  302. package/dist/upstream-t3code/packages/contracts/src/provider.d.ts +0 -130
  303. package/dist/upstream-t3code/packages/contracts/src/providerInstance.d.ts +0 -99
  304. package/dist/upstream-t3code/packages/contracts/src/providerRuntime.d.ts +0 -4450
  305. package/dist/upstream-t3code/packages/contracts/src/relay.d.ts +0 -1262
  306. package/dist/upstream-t3code/packages/contracts/src/relayClient.d.ts +0 -48
  307. package/dist/upstream-t3code/packages/contracts/src/remoteAccess.d.ts +0 -45
  308. package/dist/upstream-t3code/packages/contracts/src/review.d.ts +0 -37
  309. package/dist/upstream-t3code/packages/contracts/src/rpc.d.ts +0 -8710
  310. package/dist/upstream-t3code/packages/contracts/src/server.d.ts +0 -2336
  311. package/dist/upstream-t3code/packages/contracts/src/settings.d.ts +0 -292
  312. package/dist/upstream-t3code/packages/contracts/src/sourceControl.d.ts +0 -177
  313. package/dist/upstream-t3code/packages/contracts/src/terminal.d.ts +0 -333
  314. package/dist/upstream-t3code/packages/contracts/src/vcs.d.ts +0 -148
  315. package/src/cli/auth-format.ts +0 -45
  316. package/src/cli/model-format.ts +0 -18
  317. package/src/cli/output-format.ts +0 -41
  318. package/src/cli/project-format.ts +0 -11
  319. package/src/cli/thread-format.ts +0 -121
  320. package/src/config/layer.ts +0 -105
  321. package/src/config/service.ts +0 -26
  322. package/src/environment/layer.ts +0 -12
  323. package/src/environment/service.ts +0 -13
  324. package/src/layout/index.ts +0 -1
  325. /package/dist/src/{scope → cli/scope}/index.d.ts +0 -0
  326. /package/dist/src/config/{url.d.ts → url/url.d.ts} +0 -0
  327. /package/src/{scope → cli/scope}/index.ts +0 -0
  328. /package/src/config/{url.ts → url/url.ts} +0 -0
@@ -0,0 +1,36 @@
1
+ import * as Effect from "effect/Effect";
2
+
3
+ import { SelfActionError } from "../error.ts";
4
+ import { isAgentEnvironment } from "../format/output.ts";
5
+ import type { T3CliEnvShape } from "../../config/env/env.ts";
6
+ import type { CliRuntime } from "../runtime/service.ts";
7
+
8
+ export const requireSelfActionConfirmation = Effect.fn("requireSelfActionConfirmation")(
9
+ function* (input: {
10
+ readonly threadId: string;
11
+ readonly force: boolean;
12
+ readonly cliRuntime: CliRuntime["Service"];
13
+ readonly t3CliEnv: T3CliEnvShape;
14
+ readonly action: string;
15
+ }) {
16
+ if (input.force) {
17
+ return;
18
+ }
19
+ if (!isAgentEnvironment(input.t3CliEnv)) {
20
+ return;
21
+ }
22
+ const callerThreadId = input.t3CliEnv.scope.t3codeThreadId;
23
+ if (callerThreadId === undefined || callerThreadId.length === 0) {
24
+ return;
25
+ }
26
+ if (input.threadId !== callerThreadId) {
27
+ return;
28
+ }
29
+ yield* Effect.fail(
30
+ new SelfActionError({
31
+ threadId: input.threadId,
32
+ message: `cannot ${input.action} thread ${input.threadId}: command targets the calling thread. Pass --force to confirm.`,
33
+ }),
34
+ );
35
+ },
36
+ );
@@ -1,6 +1,6 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import * as Schema from "effect/Schema";
3
- import { ProviderUserInputAnswers } from "#t3tools/contracts";
3
+ import { ProviderUserInputAnswers } from "@t3tools/contracts";
4
4
 
5
5
  import { MessageInputError } from "./error.ts";
6
6
  import type { InputError } from "./input/error.ts";
package/src/cli/model.ts CHANGED
@@ -2,11 +2,13 @@ import * as Effect from "effect/Effect";
2
2
  import * as Option from "effect/Option";
3
3
  import { Command, Flag } from "effect/unstable/cli";
4
4
 
5
+ import { extraArgsConfig } from "./extra-args.ts";
5
6
  import { formatFlag } from "./flags.ts";
6
7
  import { T3Application } from "../application/service.ts";
7
- import { Environment } from "../environment/service.ts";
8
- import { formatModelsHuman } from "./model-format.ts";
9
- import { resolveOutputFormat } from "./output-format.ts";
8
+ import { CliRuntime } from "../cli/runtime/service.ts";
9
+ import { loadT3CliEnv } from "../config/env/env.ts";
10
+ import { formatModelsHuman } from "./format/model.ts";
11
+ import { resolveOutputFormat } from "./format/output.ts";
10
12
  import { T3Output } from "./output/service.ts";
11
13
 
12
14
  export function createModelCommand() {
@@ -22,14 +24,16 @@ const listCommand = Command.make(
22
24
  all: Flag.boolean("all"),
23
25
  provider: Flag.string("provider").pipe(Flag.optional),
24
26
  format: formatFlag,
27
+ ...extraArgsConfig,
25
28
  },
26
29
  ({ all, provider, format }) =>
27
30
  Effect.gen(function* () {
28
31
  const application = yield* T3Application;
29
- const environment = yield* Environment;
32
+ const cliRuntime = yield* CliRuntime;
33
+ const t3CliEnv = yield* loadT3CliEnv;
30
34
  const output = yield* T3Output;
31
35
  const providerValue = Option.getOrUndefined(provider);
32
- const resolvedFormat = resolveOutputFormat(format, environment, "json");
36
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
33
37
  const providers = yield* application.listModels({
34
38
  all,
35
39
  ...(providerValue !== undefined && providerValue.length > 0
@@ -2,17 +2,20 @@ import * as Effect from "effect/Effect";
2
2
  import * as Option from "effect/Option";
3
3
  import { Command, Flag } from "effect/unstable/cli";
4
4
 
5
+ import { extraArgsConfig } from "./extra-args.ts";
5
6
  import { formatFlag, projectPathFlag } from "./flags.ts";
6
- import { formatProjectAddedHuman, formatProjectsHuman } from "./project-format.ts";
7
+ import { formatProjectAddedHuman, formatProjectsHuman } from "./format/project.ts";
8
+ import { deleteProjectCommand } from "./projects/delete.ts";
7
9
  import { T3Application } from "../application/service.ts";
8
- import { Environment } from "../environment/service.ts";
9
- import { resolveOutputFormat } from "./output-format.ts";
10
+ import { CliRuntime } from "../cli/runtime/service.ts";
11
+ import { loadT3CliEnv } from "../config/env/env.ts";
12
+ import { resolveOutputFormat } from "./format/output.ts";
10
13
  import { T3Output } from "./output/service.ts";
11
14
 
12
15
  export function createProjectCommand() {
13
16
  return Command.make("project").pipe(
14
17
  Command.withDescription("project commands"),
15
- Command.withSubcommands([listCommand, addCommand]),
18
+ Command.withSubcommands([listCommand, addCommand, deleteProjectCommand]),
16
19
  );
17
20
  }
18
21
 
@@ -20,13 +23,15 @@ const listCommand = Command.make(
20
23
  "list",
21
24
  {
22
25
  format: formatFlag,
26
+ ...extraArgsConfig,
23
27
  },
24
28
  ({ format }) =>
25
29
  Effect.gen(function* () {
26
30
  const application = yield* T3Application;
27
- const environment = yield* Environment;
31
+ const cliRuntime = yield* CliRuntime;
32
+ const t3CliEnv = yield* loadT3CliEnv;
28
33
  const output = yield* T3Output;
29
- const resolvedFormat = resolveOutputFormat(format, environment, "json");
34
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
30
35
  const snapshot = yield* application.loadShell();
31
36
  if (resolvedFormat === "json") {
32
37
  yield* output.printJson(snapshot.projects);
@@ -42,13 +47,15 @@ const addCommand = Command.make(
42
47
  path: projectPathFlag,
43
48
  title: Flag.string("title").pipe(Flag.optional),
44
49
  format: formatFlag,
50
+ ...extraArgsConfig,
45
51
  },
46
52
  ({ path, title, format }) =>
47
53
  Effect.gen(function* () {
48
54
  const application = yield* T3Application;
49
- const environment = yield* Environment;
55
+ const cliRuntime = yield* CliRuntime;
56
+ const t3CliEnv = yield* loadT3CliEnv;
50
57
  const output = yield* T3Output;
51
- const resolvedFormat = resolveOutputFormat(format, environment, "json");
58
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
52
59
  const titleValue = Option.getOrUndefined(title);
53
60
  const result = yield* application.addProject({
54
61
  path,
@@ -0,0 +1,51 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { Command } from "effect/unstable/cli";
3
+
4
+ import { extraArgsConfig } from "../extra-args.ts";
5
+ import { requireDestructiveConfirmation } from "../interaction/confirm.ts";
6
+ import { forceFlag, formatFlag, projectFlag, yesFlag } from "../flags.ts";
7
+ import { formatProjectDeletedHuman } from "../format/project.ts";
8
+ import { requireCommandProjectRef } from "../require.ts";
9
+ import { T3Application } from "../../application/service.ts";
10
+ import { CliRuntime } from "../../cli/runtime/service.ts";
11
+ import { loadT3CliEnv } from "../../config/env/env.ts";
12
+ import { resolveOutputFormat } from "../format/output.ts";
13
+ import { T3Output } from "../output/service.ts";
14
+
15
+ export const deleteProjectCommand = Command.make(
16
+ "delete",
17
+ {
18
+ project: projectFlag,
19
+ force: forceFlag,
20
+ yes: yesFlag,
21
+ format: formatFlag,
22
+ ...extraArgsConfig,
23
+ },
24
+ ({ project, force, yes, format }) =>
25
+ Effect.gen(function* () {
26
+ const application = yield* T3Application;
27
+ const cliRuntime = yield* CliRuntime;
28
+ const t3CliEnv = yield* loadT3CliEnv;
29
+ const output = yield* T3Output;
30
+ const projectRef = yield* requireCommandProjectRef({ project });
31
+ const resolvedProject = yield* application.resolveProject(projectRef);
32
+ yield* requireDestructiveConfirmation({
33
+ message: `Delete project ${resolvedProject.title} (${resolvedProject.id})?`,
34
+ yes,
35
+ cliRuntime,
36
+ t3CliEnv,
37
+ });
38
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
39
+ const result = yield* application.deleteProject({
40
+ projectId: resolvedProject.id,
41
+ ...(force ? { force: true } : {}),
42
+ });
43
+ if (resolvedFormat === "json") {
44
+ return yield* output.printJson({
45
+ projectId: result.projectId,
46
+ dispatch: result.dispatch,
47
+ });
48
+ }
49
+ return yield* output.printInfo(formatProjectDeletedHuman(result));
50
+ }),
51
+ ).pipe(Command.withDescription("delete project"));
@@ -1,21 +1,23 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import * as Option from "effect/Option";
3
3
 
4
- import { T3Config } from "../config/service.ts";
4
+ import { T3Config } from "../config/config.ts";
5
+ import { loadT3CliEnv } from "../config/env/env.ts";
6
+ import { CliRuntime } from "../cli/runtime/service.ts";
5
7
  import { ProjectLookupError } from "../domain/error.ts";
6
- import { resolveCommandProjectRef } from "../scope/index.ts";
8
+ import { resolveCommandProjectRef } from "./scope/index.ts";
7
9
 
8
10
  export const requireCommandProjectRef = Effect.fn("requireCommandProjectRef")(function* (input: {
9
11
  readonly project: Option.Option<string>;
10
- readonly env: Readonly<Record<string, string | undefined>>;
11
- readonly cwd: string;
12
12
  }) {
13
+ const cliRuntime = yield* CliRuntime;
14
+ const t3CliEnv = yield* loadT3CliEnv;
13
15
  const config = yield* T3Config;
14
16
  const resolved = yield* config.resolve();
15
17
  const ref = resolveCommandProjectRef({
16
18
  value: Option.getOrUndefined(input.project),
17
- env: input.env,
18
- cwd: input.cwd,
19
+ scope: t3CliEnv.scope,
20
+ cwd: cliRuntime.cwd,
19
21
  isLocal: resolved.local,
20
22
  });
21
23
  if (ref === undefined) {
@@ -23,7 +25,7 @@ export const requireCommandProjectRef = Effect.fn("requireCommandProjectRef")(fu
23
25
  new ProjectLookupError({
24
26
  message:
25
27
  "project is required: pass --project, set T3CODE_PROJECT_ROOT / T3CODE_PROJECT_ID, or use local auth",
26
- ref: input.cwd,
28
+ ref: cliRuntime.cwd,
27
29
  }),
28
30
  );
29
31
  }
@@ -0,0 +1 @@
1
+ export { CliRuntime, layer } from "./service.ts";
@@ -0,0 +1,28 @@
1
+ import * as Context from "effect/Context";
2
+ import * as Layer from "effect/Layer";
3
+
4
+ export class CliRuntime extends Context.Service<
5
+ CliRuntime,
6
+ {
7
+ readonly cwd: string;
8
+ readonly stdoutIsTTY: boolean;
9
+ readonly stderrIsTTY: boolean;
10
+ }
11
+ >()("t3cli/CliRuntime") {
12
+ static layerTest = (input?: {
13
+ readonly cwd?: string;
14
+ readonly stdoutIsTTY?: boolean;
15
+ readonly stderrIsTTY?: boolean;
16
+ }) =>
17
+ Layer.succeed(CliRuntime, {
18
+ cwd: input?.cwd ?? process.cwd(),
19
+ stdoutIsTTY: input?.stdoutIsTTY ?? false,
20
+ stderrIsTTY: input?.stderrIsTTY ?? false,
21
+ });
22
+ }
23
+
24
+ export const layer = Layer.succeed(CliRuntime, {
25
+ cwd: process.cwd(),
26
+ stdoutIsTTY: process.stdout.isTTY ?? false,
27
+ stderrIsTTY: process.stderr.isTTY ?? false,
28
+ });
@@ -1,15 +1,17 @@
1
+ import type { T3CliEnvScope } from "../../config/env/env.ts";
2
+
1
3
  export function resolveProjectRef(input: {
2
4
  readonly value: string | undefined;
3
- readonly env: Readonly<Record<string, string | undefined>>;
5
+ readonly scope: T3CliEnvScope;
4
6
  }): string | undefined {
5
7
  if (input.value !== undefined && input.value.length > 0) {
6
8
  return input.value;
7
9
  }
8
- const fromRoot = input.env.T3CODE_PROJECT_ROOT;
10
+ const fromRoot = input.scope.t3codeProjectRoot;
9
11
  if (fromRoot !== undefined && fromRoot.length > 0) {
10
12
  return fromRoot;
11
13
  }
12
- const fromId = input.env.T3CODE_PROJECT_ID;
14
+ const fromId = input.scope.t3codeProjectId;
13
15
  if (fromId !== undefined && fromId.length > 0) {
14
16
  return fromId;
15
17
  }
@@ -18,11 +20,11 @@ export function resolveProjectRef(input: {
18
20
 
19
21
  export function resolveCommandProjectRef(input: {
20
22
  readonly value: string | undefined;
21
- readonly env: Readonly<Record<string, string | undefined>>;
23
+ readonly scope: T3CliEnvScope;
22
24
  readonly cwd: string;
23
25
  readonly isLocal?: boolean;
24
26
  }): string | undefined {
25
- const explicit = resolveProjectRef({ value: input.value, env: input.env });
27
+ const explicit = resolveProjectRef({ value: input.value, scope: input.scope });
26
28
  if (explicit !== undefined) {
27
29
  return explicit;
28
30
  }
@@ -34,13 +36,13 @@ export function resolveCommandProjectRef(input: {
34
36
 
35
37
  export function resolveWorktreePath(input: {
36
38
  readonly value: string | undefined;
37
- readonly env: Readonly<Record<string, string | undefined>>;
39
+ readonly scope: T3CliEnvScope;
38
40
  readonly inferred?: string;
39
41
  }): string | undefined {
40
42
  if (input.value !== undefined && input.value.length > 0) {
41
43
  return input.value;
42
44
  }
43
- const fromEnv = input.env.T3CODE_WORKTREE_PATH;
45
+ const fromEnv = input.scope.t3codeWorktreePath;
44
46
  if (fromEnv !== undefined && fromEnv.length > 0) {
45
47
  return fromEnv;
46
48
  }
@@ -49,12 +51,12 @@ export function resolveWorktreePath(input: {
49
51
 
50
52
  export function resolveThreadId(input: {
51
53
  readonly value: string | undefined;
52
- readonly env: Readonly<Record<string, string | undefined>>;
54
+ readonly scope: T3CliEnvScope;
53
55
  }): string | undefined {
54
56
  if (input.value !== undefined && input.value.length > 0) {
55
57
  return input.value;
56
58
  }
57
- const fromEnv = input.env.T3CODE_THREAD_ID;
59
+ const fromEnv = input.scope.t3codeThreadId;
58
60
  if (fromEnv !== undefined && fromEnv.length > 0) {
59
61
  return fromEnv;
60
62
  }
@@ -0,0 +1,29 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { Argument, Command } from "effect/unstable/cli";
3
+
4
+ import { T3Application } from "../../application/service.ts";
5
+ import { extraArgsConfig } from "../extra-args.ts";
6
+ import { threadFlag } from "../flags.ts";
7
+ import { requireCommandThreadId } from "./scope.ts";
8
+ import { runAttachedTerminalSession, toTerminalAttachTarget } from "./shared.ts";
9
+
10
+ export const attachTerminalCommand = Command.make(
11
+ "attach",
12
+ {
13
+ thread: threadFlag,
14
+ terminalId: Argument.string("terminal-id"),
15
+ ...extraArgsConfig,
16
+ },
17
+ ({ thread, terminalId }) =>
18
+ Effect.gen(function* () {
19
+ const application = yield* T3Application;
20
+ const threadId = yield* requireCommandThreadId({ thread });
21
+ const terminal = yield* application.getTerminal({
22
+ threadId,
23
+ terminalId,
24
+ });
25
+ yield* runAttachedTerminalSession({
26
+ terminal: toTerminalAttachTarget(terminal),
27
+ });
28
+ }),
29
+ ).pipe(Command.withDescription("attach to a terminal"));
@@ -0,0 +1,55 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Option from "effect/Option";
3
+ import { Argument, Command, Flag } from "effect/unstable/cli";
4
+
5
+ import { extraArgsConfig } from "../extra-args.ts";
6
+ import { formatTerminalCreatedHuman } from "../format/terminal.ts";
7
+ import { T3Application } from "../../application/service.ts";
8
+ import { CliRuntime } from "../../cli/runtime/service.ts";
9
+ import { loadT3CliEnv } from "../../config/env/env.ts";
10
+ import { formatFlag, threadFlag } from "../flags.ts";
11
+ import { resolveOutputFormat } from "../format/output.ts";
12
+ import { T3Output } from "../output/service.ts";
13
+ import { requireCommandThreadId } from "./scope.ts";
14
+ import { runAttachedTerminalSession, snapshotToTerminalAttachTarget } from "./shared.ts";
15
+
16
+ export const createTerminalCommand = Command.make(
17
+ "create",
18
+ {
19
+ thread: threadFlag,
20
+ command: Argument.string("command").pipe(Argument.optional),
21
+ id: Flag.string("id").pipe(Flag.optional),
22
+ attach: Flag.boolean("attach"),
23
+ format: formatFlag,
24
+ ...extraArgsConfig,
25
+ },
26
+ ({ thread, command, id, attach, format }) =>
27
+ Effect.gen(function* () {
28
+ const application = yield* T3Application;
29
+ const cliRuntime = yield* CliRuntime;
30
+ const t3CliEnv = yield* loadT3CliEnv;
31
+ const output = yield* T3Output;
32
+ const threadId = yield* requireCommandThreadId({ thread });
33
+ const terminalId = Option.getOrUndefined(id);
34
+ const commandValue = Option.getOrUndefined(command);
35
+ const snapshot = yield* application.createTerminal({
36
+ threadId,
37
+ ...(terminalId !== undefined ? { terminalId } : {}),
38
+ ...(commandValue !== undefined ? { command: commandValue } : {}),
39
+ });
40
+
41
+ if (attach) {
42
+ yield* runAttachedTerminalSession({
43
+ terminal: snapshotToTerminalAttachTarget(snapshot),
44
+ });
45
+ return;
46
+ }
47
+
48
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
49
+ if (resolvedFormat === "json") {
50
+ yield* output.printJson(snapshot);
51
+ } else {
52
+ yield* output.printInfo(formatTerminalCreatedHuman(snapshot));
53
+ }
54
+ }),
55
+ ).pipe(Command.withDescription("create a terminal for a thread"));
@@ -0,0 +1,62 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { Argument, Command, Flag } from "effect/unstable/cli";
3
+
4
+ import { extraArgsConfig } from "../extra-args.ts";
5
+ import { requireDestructiveConfirmation } from "../interaction/confirm.ts";
6
+ import { formatTerminalDestroyedHuman } from "../format/terminal.ts";
7
+ import { T3Application } from "../../application/service.ts";
8
+ import { CliRuntime } from "../../cli/runtime/service.ts";
9
+ import { loadT3CliEnv } from "../../config/env/env.ts";
10
+ import { formatFlag, threadFlag, yesFlag } from "../flags.ts";
11
+ import { resolveOutputFormat } from "../format/output.ts";
12
+ import { T3Output } from "../output/service.ts";
13
+ import { requireCommandThreadId } from "./scope.ts";
14
+
15
+ export const destroyTerminalCommand = Command.make(
16
+ "destroy",
17
+ {
18
+ thread: threadFlag,
19
+ terminalId: Argument.string("terminal-id"),
20
+ quiet: Flag.boolean("quiet"),
21
+ yes: yesFlag,
22
+ format: formatFlag,
23
+ ...extraArgsConfig,
24
+ },
25
+ ({ thread, terminalId, quiet, yes, format }) =>
26
+ Effect.gen(function* () {
27
+ const application = yield* T3Application;
28
+ const cliRuntime = yield* CliRuntime;
29
+ const t3CliEnv = yield* loadT3CliEnv;
30
+ const output = yield* T3Output;
31
+ const threadId = yield* requireCommandThreadId({ thread });
32
+ yield* requireDestructiveConfirmation({
33
+ message: `Destroy terminal ${terminalId} in thread ${threadId} and delete its history?`,
34
+ yes,
35
+ cliRuntime,
36
+ t3CliEnv,
37
+ });
38
+ const terminal = {
39
+ threadId,
40
+ terminalId,
41
+ };
42
+ yield* application.destroyTerminal(terminal);
43
+ if (quiet) {
44
+ return;
45
+ }
46
+ const resolvedFormat = resolveOutputFormat(format, cliRuntime, t3CliEnv, "json");
47
+ if (resolvedFormat === "json") {
48
+ yield* output.printJson({
49
+ threadId: terminal.threadId,
50
+ terminalId: terminal.terminalId,
51
+ destroyed: true,
52
+ });
53
+ return;
54
+ }
55
+ yield* output.printInfo(
56
+ formatTerminalDestroyedHuman({
57
+ terminalId,
58
+ threadId,
59
+ }),
60
+ );
61
+ }),
62
+ ).pipe(Command.withDescription("destroy a terminal and its history"));
@@ -0,0 +1,63 @@
1
+ import "vite-plus/test/config";
2
+
3
+ import { describe, expect, it } from "vite-plus/test";
4
+
5
+ import { decodeBase64Payload, decodeHexPayload } from "./encoding.ts";
6
+ import { filterAttachStreamEvent } from "./shared.ts";
7
+
8
+ describe("terminal encoding", () => {
9
+ it("preserves non-utf8 bytes in hex payloads", () => {
10
+ expect(decodeHexPayload("ff")).toBe("\xff");
11
+ });
12
+
13
+ it("preserves non-utf8 bytes in base64 payloads", () => {
14
+ expect(decodeBase64Payload("/w==")).toBe("\xff");
15
+ });
16
+ });
17
+
18
+ describe("filterAttachStreamEvent", () => {
19
+ const snapshotEvent = {
20
+ type: "snapshot" as const,
21
+ snapshot: {
22
+ threadId: "thread-1",
23
+ terminalId: "term-1",
24
+ cwd: "/tmp",
25
+ worktreePath: null,
26
+ status: "running" as const,
27
+ pid: 1,
28
+ history: "hello",
29
+ exitCode: null,
30
+ exitSignal: null,
31
+ label: "shell",
32
+ updatedAt: "2026-01-01T00:00:00.000Z",
33
+ sequence: 3,
34
+ },
35
+ };
36
+
37
+ it("keeps snapshot history when --history is set with --from-sequence", () => {
38
+ const filtered = filterAttachStreamEvent(snapshotEvent, {
39
+ includeHistory: true,
40
+ fromSequence: 5,
41
+ });
42
+ expect(filtered?.type).toBe("snapshot");
43
+ if (filtered?.type === "snapshot") {
44
+ expect(filtered.snapshot.history).toBe("hello");
45
+ }
46
+ });
47
+
48
+ it("filters sequenced events inclusively from --from-sequence", () => {
49
+ const outputEvent = {
50
+ type: "output" as const,
51
+ threadId: "thread-1",
52
+ terminalId: "term-1",
53
+ sequence: 5,
54
+ data: "x",
55
+ };
56
+ expect(
57
+ filterAttachStreamEvent(outputEvent, { includeHistory: false, fromSequence: 5 }),
58
+ ).toEqual(outputEvent);
59
+ expect(
60
+ filterAttachStreamEvent(outputEvent, { includeHistory: false, fromSequence: 6 }),
61
+ ).toBeNull();
62
+ });
63
+ });
@@ -0,0 +1,23 @@
1
+ export function bytesToLatin1String(bytes: Uint8Array): string {
2
+ return Buffer.from(bytes).toString("latin1");
3
+ }
4
+
5
+ export function decodeHexPayload(value: string): string | undefined {
6
+ const normalized = value.trim();
7
+ if (
8
+ normalized.length === 0 ||
9
+ normalized.length % 2 !== 0 ||
10
+ !/^[0-9a-fA-F]+$/.test(normalized)
11
+ ) {
12
+ return undefined;
13
+ }
14
+ return Buffer.from(normalized, "hex").toString("latin1");
15
+ }
16
+
17
+ export function decodeBase64Payload(value: string): string | undefined {
18
+ const normalized = value.trim();
19
+ if (normalized.length === 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(normalized)) {
20
+ return undefined;
21
+ }
22
+ return Buffer.from(normalized, "base64").toString("latin1");
23
+ }
@@ -0,0 +1,14 @@
1
+ import * as Schema from "effect/Schema";
2
+
3
+ export class TerminalCliError extends Schema.TaggedErrorClass<TerminalCliError>()(
4
+ "TerminalCliError",
5
+ {
6
+ message: Schema.String,
7
+ threadId: Schema.optionalKey(Schema.String),
8
+ terminalId: Schema.optionalKey(Schema.String),
9
+ },
10
+ ) {}
11
+
12
+ export class TerminalIoError extends Schema.TaggedErrorClass<TerminalIoError>()("TerminalIoError", {
13
+ message: Schema.String,
14
+ }) {}
@@ -0,0 +1,82 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+ import * as Queue from "effect/Queue";
4
+ import * as Stream from "effect/Stream";
5
+
6
+ import { TerminalIoError } from "./error.ts";
7
+ import { TerminalIo, type RawTerminalIoSession, type TerminalWindowSize } from "./io-service.ts";
8
+
9
+ const DEFAULT_COLS = 80;
10
+ const DEFAULT_ROWS = 24;
11
+ const INTERACTIVE_TTY_MESSAGE = "terminal attach requires an interactive TTY on stdin and stdout";
12
+
13
+ const requireInteractiveTty = Effect.gen(function* () {
14
+ if (!process.stdin.isTTY || !process.stdout.isTTY || process.stdin.setRawMode === undefined) {
15
+ return yield* Effect.fail(new TerminalIoError({ message: INTERACTIVE_TTY_MESSAGE }));
16
+ }
17
+
18
+ return {
19
+ stdin: process.stdin,
20
+ stdout: process.stdout,
21
+ };
22
+ });
23
+
24
+ export const NodeTerminalIoLive = Layer.succeed(TerminalIo, {
25
+ getWindowSize: requireInteractiveTty.pipe(Effect.map(readWindowSize)),
26
+ writeOutput: (text: string) =>
27
+ Effect.sync(() => {
28
+ process.stdout.write(text);
29
+ }),
30
+ openRawSession: Effect.acquireRelease(
31
+ Effect.gen(function* () {
32
+ const tty = yield* requireInteractiveTty;
33
+ const input = yield* Queue.unbounded<Uint8Array>();
34
+ const resize = yield* Queue.unbounded<TerminalWindowSize>();
35
+
36
+ const onResize = () => {
37
+ Effect.runFork(Queue.offer(resize, readWindowSize(tty)).pipe(Effect.ignore));
38
+ };
39
+
40
+ const onData = (chunk: Buffer | string) => {
41
+ Effect.runFork(
42
+ Queue.offer(input, typeof chunk === "string" ? Buffer.from(chunk) : chunk).pipe(
43
+ Effect.ignore,
44
+ ),
45
+ );
46
+ };
47
+
48
+ tty.stdin.resume();
49
+ tty.stdin.setRawMode(true);
50
+ process.on("SIGWINCH", onResize);
51
+ tty.stdin.on("data", onData);
52
+
53
+ return {
54
+ tty,
55
+ input,
56
+ resize,
57
+ onData,
58
+ onResize,
59
+ session: {
60
+ input: Stream.fromQueue(input),
61
+ resize: Stream.fromQueue(resize),
62
+ } satisfies RawTerminalIoSession,
63
+ };
64
+ }),
65
+ ({ tty, input, resize, onData, onResize }) =>
66
+ Effect.gen(function* () {
67
+ tty.stdin.off("data", onData);
68
+ process.off("SIGWINCH", onResize);
69
+ tty.stdin.setRawMode(false);
70
+ tty.stdin.pause();
71
+ yield* Queue.shutdown(input);
72
+ yield* Queue.shutdown(resize);
73
+ }),
74
+ ).pipe(Effect.map(({ session }) => session)),
75
+ });
76
+
77
+ function readWindowSize(tty: { readonly stdout: NodeJS.WriteStream }): TerminalWindowSize {
78
+ return {
79
+ cols: tty.stdout.columns ?? DEFAULT_COLS,
80
+ rows: tty.stdout.rows ?? DEFAULT_ROWS,
81
+ };
82
+ }