turtlecode 0.1.0 → 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 (127) hide show
  1. package/assets/assets/{_basePickBy-B_7ShcHh.js → _basePickBy-BcOvpC5b.js} +1 -1
  2. package/assets/assets/{_baseUniq-BsW7XEuT.js → _baseUniq-D9AKNI-S.js} +1 -1
  3. package/assets/assets/{arc-BbW57NUb.js → arc-ClcBVsDq.js} +1 -1
  4. package/assets/assets/architectureDiagram-2XIMDMQ5-CRV28Qi0.js +36 -0
  5. package/assets/assets/{auth-callback-C2n64Qcj.js → auth-callback-D7CK9pHp.js} +1 -1
  6. package/assets/assets/{blockDiagram-WCTKOSBZ-C1Ni5oSK.js → blockDiagram-WCTKOSBZ-CL-sI8NU.js} +2 -2
  7. package/assets/assets/{c4Diagram-IC4MRINW-g8EmnUUA.js → c4Diagram-IC4MRINW-AFG5p1yt.js} +2 -2
  8. package/assets/assets/channel-BXqEQVGi.js +1 -0
  9. package/assets/assets/{chunk-4BX2VUAB-CGR6NDgJ.js → chunk-4BX2VUAB-CgNy2Qqu.js} +1 -1
  10. package/assets/assets/chunk-55IACEB6-Bsz0SHtO.js +1 -0
  11. package/assets/assets/{chunk-FMBD7UC4-CBSUaebz.js → chunk-FMBD7UC4-BRuwu34q.js} +1 -1
  12. package/assets/assets/chunk-JSJVCQXG-Bt0GkrjS.js +1 -0
  13. package/assets/assets/{chunk-KX2RTZJC-D_r6FHlr.js → chunk-KX2RTZJC-BiUvn-D7.js} +1 -1
  14. package/assets/assets/{chunk-NQ4KR5QH-_-NyPl-E.js → chunk-NQ4KR5QH-DH4YUQE9.js} +1 -1
  15. package/assets/assets/{chunk-QZHKN3VN-CMDZeFvi.js → chunk-QZHKN3VN-CrLB363X.js} +1 -1
  16. package/assets/assets/chunk-VVM5DH6Z-D2vG_ZPx.js +1 -0
  17. package/assets/assets/{chunk-WL4C6EOR-p1mj0O6S.js → chunk-WL4C6EOR-913dgDB-.js} +1 -1
  18. package/assets/assets/classDiagram-VBA2DB6C-gcpKg8SN.js +1 -0
  19. package/assets/assets/classDiagram-v2-RAHNMMFH-gcpKg8SN.js +1 -0
  20. package/assets/assets/clone-oMsF326O.js +1 -0
  21. package/assets/assets/{connection-popover-Sip4k8lc.js → connection-popover-D2MEtT6z.js} +1 -1
  22. package/assets/assets/{context-popover-jX8NoD9w.js → context-popover-C1ZDT4aK.js} +1 -1
  23. package/assets/assets/cose-bilkent-S5V4N54A-DRWnwV5H.js +1 -0
  24. package/assets/assets/{cssMode-xHiM5mcb.js → cssMode-DX-s2EC8.js} +2 -2
  25. package/assets/assets/dagre-KLK3FWXG-CN4ule0l.js +4 -0
  26. package/assets/assets/diagram-E7M64L7V-docJyJu4.js +24 -0
  27. package/assets/assets/diagram-IFDJBPK2-ChVpZldk.js +43 -0
  28. package/assets/assets/diagram-P4PSJMXO-Bf1-kRnd.js +24 -0
  29. package/assets/assets/dialog-clone-project-B1wF7jYQ.js +1 -0
  30. package/assets/assets/{dialog-connect-provider-BEqcXIot.js → dialog-connect-provider-Ot6Qr5dL.js} +2 -2
  31. package/assets/assets/dialog-create-project-BUc4DxX6.js +1 -0
  32. package/assets/assets/{dialog-edit-project-BfeaBlLZ.js → dialog-edit-project-BrkjzrGe.js} +1 -1
  33. package/assets/assets/{dialog-fork-D0oj4IQ5.js → dialog-fork-CyeKjf0e.js} +1 -1
  34. package/assets/assets/{dialog-manage-models-mDJUOPob.js → dialog-manage-models-CoiwPdu-.js} +1 -1
  35. package/assets/assets/{dialog-select-directory-BocFiQ9d.js → dialog-select-directory-K0nOY5Ky.js} +3 -3
  36. package/assets/assets/{dialog-select-file-CifFpv3x.js → dialog-select-file-Dx0Bny2E.js} +1 -1
  37. package/assets/assets/{dialog-select-mcp-Bz0DoDof.js → dialog-select-mcp-ehJzhdpz.js} +1 -1
  38. package/assets/assets/{dialog-select-model-unpaid-Da_CQzO4.js → dialog-select-model-unpaid-IVl97Sa_.js} +2 -2
  39. package/assets/assets/{dialog-select-provider-2iOfyWxy.js → dialog-select-provider-CBq6hNDx.js} +1 -1
  40. package/assets/assets/{dialog-select-server-DBhmJ2f8.js → dialog-select-server-DA5hATxx.js} +1 -1
  41. package/assets/assets/{dialog-settings-gGxknIbS.js → dialog-settings-BoOPbXZp.js} +1 -1
  42. package/assets/assets/{erDiagram-INFDFZHY-7L6v8ikj.js → erDiagram-INFDFZHY-kDj6gvOH.js} +2 -2
  43. package/assets/assets/{file-icon-CgbvTpIc.js → file-icon-DVEMs7Sx.js} +1 -1
  44. package/assets/assets/{flowDiagram-PKNHOUZH-aJT2T3cx.js → flowDiagram-PKNHOUZH-CsQeJUub.js} +2 -2
  45. package/assets/assets/{freemarker2-BgcPHQt-.js → freemarker2-B_-gEFnj.js} +2 -2
  46. package/assets/assets/ganttDiagram-A5KZAMGK-D1W7NciY.js +292 -0
  47. package/assets/assets/{ghostty-web-CyXMoQwm.js → ghostty-web-BQl0ls6m.js} +1 -1
  48. package/assets/assets/gitGraphDiagram-K3NZZRJ6-D4eeLV-l.js +65 -0
  49. package/assets/assets/{graph-CFcaDaoV.js → graph-KGuLNbW2.js} +1 -1
  50. package/assets/assets/{handlebars-DSpGDIPh.js → handlebars-DlnQtQmF.js} +2 -2
  51. package/assets/assets/{home-6Va-8zmR.js → home-BnHPimua.js} +1 -1
  52. package/assets/assets/{html-CNWh6Ln_.js → html-DrjCwvQg.js} +2 -2
  53. package/assets/assets/{htmlMode-LcU-TZaJ.js → htmlMode-C1xX0YwN.js} +3 -3
  54. package/assets/assets/index-DKwABhnu.js +2584 -0
  55. package/assets/assets/{index-DpJDxStT.css → index-DTxAHLat.css} +1 -1
  56. package/assets/assets/infoDiagram-LFFYTUFH-DsQ9EVx0.js +2 -0
  57. package/assets/assets/{ishikawaDiagram-PHBUUO56-y62OJXdI.js → ishikawaDiagram-PHBUUO56-MzetUfFK.js} +6 -6
  58. package/assets/assets/{javascript-CTmFPESg.js → javascript-CBQQIA5q.js} +2 -2
  59. package/assets/assets/{jobs-popover-DRbIcR2O.js → jobs-popover-DQmVz-e0.js} +1 -1
  60. package/assets/assets/{journeyDiagram-4ABVD52K-Bi9XcKs5.js → journeyDiagram-4ABVD52K-BkKgSN_9.js} +2 -2
  61. package/assets/assets/{jsonMode-CW1XLtuQ.js → jsonMode-B2Ye6w7y.js} +3 -3
  62. package/assets/assets/{kanban-definition-K7BYSVSG-DE01LQ5I.js → kanban-definition-K7BYSVSG-DIJz6AsI.js} +4 -4
  63. package/assets/assets/{layout-C6rurAvx.js → layout-BZKpv2Gr.js} +1 -1
  64. package/assets/assets/linear-COY9pyF4.js +1 -0
  65. package/assets/assets/{liquid-DB5uFXiY.js → liquid-CQXVmj19.js} +2 -2
  66. package/assets/assets/{list-BPk4NMWt.js → list-DPJi0fzr.js} +1 -1
  67. package/assets/assets/{mdx-Q7_EfF7H.js → mdx-QuCI1OJC.js} +2 -2
  68. package/assets/assets/{mermaid.core-DdXWHS-1.js → mermaid.core-bhqKFRxj.js} +10 -10
  69. package/assets/assets/{mindmap-definition-YRQLILUH-BajbwusW.js → mindmap-definition-YRQLILUH-CVv44i-B.js} +2 -2
  70. package/assets/assets/pieDiagram-SKSYHLDU-2ekrtn6z.js +30 -0
  71. package/assets/assets/{python-B3qjyNJj.js → python-Bx2b-xP4.js} +2 -2
  72. package/assets/assets/{quadrantDiagram-337W2JSQ-CZMB9UKV.js → quadrantDiagram-337W2JSQ-BoTofVsV.js} +2 -2
  73. package/assets/assets/{razor-CJkx1qh1.js → razor-2zgukQRj.js} +2 -2
  74. package/assets/assets/{requirementDiagram-Z7DCOOCP-BlyteLn_.js → requirementDiagram-Z7DCOOCP-LSodooxy.js} +2 -2
  75. package/assets/assets/sankeyDiagram-WA2Y5GQK-DUXSSMar.js +10 -0
  76. package/assets/assets/{select-CEKfrTm2.js → select-BbwOgh1Q.js} +1 -1
  77. package/assets/assets/{sequenceDiagram-2WXFIKYE-B2cOVhRx.js → sequenceDiagram-2WXFIKYE-C01qZdHR.js} +11 -11
  78. package/assets/assets/{server-row-BhbnbNJy.js → server-row-mRVGCgvL.js} +1 -1
  79. package/assets/assets/session-BXKBDkSI.js +34 -0
  80. package/assets/assets/stateDiagram-RAJIS63D-BuPj1Py4.js +1 -0
  81. package/assets/assets/stateDiagram-v2-FVOUBMTO-CjYACBI3.js +1 -0
  82. package/assets/assets/{status-popover-body-DkLpfJ2k.js → status-popover-body-9YRn8z8W.js} +2 -2
  83. package/assets/assets/{switch-DPq0vsit.js → switch-CHySmW8J.js} +1 -1
  84. package/assets/assets/{tabs-dF68ik4J.js → tabs-CKNlwTKd.js} +1 -1
  85. package/assets/assets/{tag--BCZ5q7_.js → tag-C4xikCZp.js} +1 -1
  86. package/assets/assets/{timeline-definition-YZTLITO2-C18tfizI.js → timeline-definition-YZTLITO2-CPcDqVYw.js} +2 -2
  87. package/assets/assets/transform-BwXaE9hv.js +1 -0
  88. package/assets/assets/{treemap-KZPCXAKY-C7fWdgj-.js → treemap-KZPCXAKY-Bin819KW.js} +1 -1
  89. package/assets/assets/{trellis-6YKi26t8.js → trellis-Csm7WAqm.js} +217 -231
  90. package/assets/assets/{trellis-badge-demo-BDa3ILpo.js → trellis-badge-demo-nxtlIRoh.js} +1 -1
  91. package/assets/assets/{trellis-op-icon-5x1lMwNH.js → trellis-op-icon-CzIuo5X7.js} +1 -1
  92. package/assets/assets/{tsMode-Bg19hQiu.js → tsMode-qOM514sL.js} +2 -2
  93. package/assets/assets/{typescript-8CtqEj8t.js → typescript-XOJMwr_m.js} +2 -2
  94. package/assets/assets/{vennDiagram-LZ73GAT5-DWmAgqdE.js → vennDiagram-LZ73GAT5-BDAdfrV5.js} +2 -2
  95. package/assets/assets/{xml-BKjz9euG.js → xml-BIwomT_0.js} +2 -2
  96. package/assets/assets/{xychartDiagram-JWTSCODW-Ckcyhr_q.js → xychartDiagram-JWTSCODW-Be2x0vdX.js} +2 -2
  97. package/assets/assets/{yaml-jJWTWPtR.js → yaml-BZMZa-4Z.js} +2 -2
  98. package/assets/index.html +2 -4
  99. package/assets/oc-theme-preload.js +0 -2
  100. package/bin/cli.mjs +166 -24
  101. package/package.json +1 -1
  102. package/assets/assets/architectureDiagram-2XIMDMQ5-DVePwAIo.js +0 -36
  103. package/assets/assets/channel-CPcPUiyc.js +0 -1
  104. package/assets/assets/chunk-55IACEB6-CuagpIkW.js +0 -1
  105. package/assets/assets/chunk-JSJVCQXG-DK5RSVJJ.js +0 -1
  106. package/assets/assets/chunk-VVM5DH6Z-COaOSPgd.js +0 -1
  107. package/assets/assets/classDiagram-VBA2DB6C-BAnp4Zaa.js +0 -1
  108. package/assets/assets/classDiagram-v2-RAHNMMFH-BAnp4Zaa.js +0 -1
  109. package/assets/assets/clone-Cf5BTVxd.js +0 -1
  110. package/assets/assets/cose-bilkent-S5V4N54A-C5LXU9pd.js +0 -1
  111. package/assets/assets/dagre-KLK3FWXG-m-9cmD-w.js +0 -4
  112. package/assets/assets/diagram-E7M64L7V-Cn-5LVpt.js +0 -24
  113. package/assets/assets/diagram-IFDJBPK2-CD8BK0Gp.js +0 -43
  114. package/assets/assets/diagram-P4PSJMXO-JVUlmg7w.js +0 -24
  115. package/assets/assets/dialog-clone-project-BgrtjUcd.js +0 -1
  116. package/assets/assets/dialog-create-project-TcSOut-3.js +0 -1
  117. package/assets/assets/ganttDiagram-A5KZAMGK-C5vwClxA.js +0 -292
  118. package/assets/assets/gitGraphDiagram-K3NZZRJ6-CI0SeLE3.js +0 -65
  119. package/assets/assets/index-bSvs-Qh3.js +0 -2544
  120. package/assets/assets/infoDiagram-LFFYTUFH-H6835-cD.js +0 -2
  121. package/assets/assets/linear-ilW0Y4uJ.js +0 -1
  122. package/assets/assets/pieDiagram-SKSYHLDU-DxK57epR.js +0 -30
  123. package/assets/assets/purify.es-A66Cw1IH.js +0 -2
  124. package/assets/assets/sankeyDiagram-WA2Y5GQK-DrdtRBZz.js +0 -10
  125. package/assets/assets/session-CdcUAq9-.js +0 -60
  126. package/assets/assets/stateDiagram-RAJIS63D-C4udRYnl.js +0 -1
  127. package/assets/assets/stateDiagram-v2-FVOUBMTO-CECUON5A.js +0 -1
package/bin/cli.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { createServer } from "node:http"
3
+ import { createServer, request as httpRequest } from "node:http"
4
4
  import { readFileSync, existsSync, statSync } from "node:fs"
5
- import { join, extname, dirname } from "node:path"
5
+ import { join, extname, dirname, resolve } from "node:path"
6
6
  import { fileURLToPath } from "node:url"
7
- import { exec } from "node:child_process"
7
+ import { exec, spawn } from "node:child_process"
8
8
 
9
9
  const root = join(dirname(fileURLToPath(import.meta.url)), "..", "assets")
10
10
 
@@ -17,10 +17,14 @@ if (args.includes("--help") || args.includes("-h")) {
17
17
  npx turtlecode [options]
18
18
 
19
19
  Options:
20
- --port, -p <number> Port to serve on (default: 3333)
21
- --no-open Don't auto-open the browser
22
- --help, -h Show this help message
23
- --version, -v Show version
20
+ --port, -p <number> Port to serve on (default: 3333)
21
+ --backend, -b <url> OpenCode backend URL (default: http://localhost:4096)
22
+ --no-open Don't auto-open the browser
23
+ --help, -h Show this help message
24
+ --version, -v Show version
25
+
26
+ The backend server (opencode) is started automatically.
27
+ Install it with: npm i -g opencode-ai
24
28
  `)
25
29
  process.exit(0)
26
30
  }
@@ -31,9 +35,72 @@ if (args.includes("--version") || args.includes("-v")) {
31
35
  process.exit(0)
32
36
  }
33
37
 
34
- const idx = Math.max(args.indexOf("--port"), args.indexOf("-p"))
35
- const port = idx !== -1 && args[idx + 1] ? parseInt(args[idx + 1], 10) : 3333
38
+ function flag(long, short) {
39
+ const i = Math.max(args.indexOf(long), args.indexOf(short))
40
+ return i !== -1 && args[i + 1] ? args[i + 1] : undefined
41
+ }
42
+
43
+ const port = parseInt(flag("--port", "-p") || "3333", 10)
36
44
  const open = !args.includes("--no-open")
45
+ const backend = new URL(flag("--backend", "-b") || process.env.OPENCODE_URL || "http://localhost:4096")
46
+
47
+ // URL-safe base64 encode (matches @opencode-ai/util/encode)
48
+ function encode(str) {
49
+ return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
50
+ }
51
+
52
+ function probe(origin) {
53
+ return new Promise((ok) => {
54
+ const req = httpRequest(origin, { method: "HEAD", timeout: 1000 }, (res) => {
55
+ res.resume()
56
+ ok(true)
57
+ })
58
+ req.on("error", () => ok(false))
59
+ req.on("timeout", () => {
60
+ req.destroy()
61
+ ok(false)
62
+ })
63
+ req.end()
64
+ })
65
+ }
66
+
67
+ async function boot(origin) {
68
+ if (await probe(origin)) return null
69
+
70
+ const url = new URL(origin)
71
+ if (url.hostname !== "localhost" && url.hostname !== "127.0.0.1") {
72
+ console.error(`\n Error: Backend not reachable at ${origin}\n`)
73
+ process.exit(1)
74
+ }
75
+
76
+ console.log(` Starting backend on port ${url.port}...`)
77
+ const child = spawn("opencode", ["serve", "--port", url.port], {
78
+ cwd: process.cwd(),
79
+ stdio: ["ignore", "pipe", "pipe"],
80
+ env: { ...process.env },
81
+ })
82
+
83
+ let exited = false
84
+ child.on("exit", (code) => {
85
+ exited = true
86
+ if (code && code !== 0) console.error(`\n Backend exited with code ${code}`)
87
+ })
88
+
89
+ child.on("error", (err) => {
90
+ console.error(`\n Error: Could not start backend.\n ${err.message}\n\n Install: npm i -g opencode-ai\n`)
91
+ process.exit(1)
92
+ })
93
+
94
+ const deadline = Date.now() + 30_000
95
+ while (Date.now() < deadline && !exited) {
96
+ if (await probe(origin)) return child
97
+ await new Promise((r) => setTimeout(r, 300))
98
+ }
99
+
100
+ if (!exited) child.kill()
101
+ console.error("\n Error: Backend failed to start within 30s.\n")
102
+ process.exit(1)
103
+ }
37
104
 
38
105
  const mime = {
39
106
  ".html": "text/html; charset=utf-8",
@@ -61,19 +128,63 @@ if (!existsSync(join(root, "index.html"))) {
61
128
  process.exit(1)
62
129
  }
63
130
 
131
+ const spawned = await boot(backend.origin)
132
+
133
+ // Determine whether a request should be proxied to the backend.
134
+ // Static assets are served directly; browser navigation gets the SPA
135
+ // index.html; everything else (API calls, SSE, etc.) is proxied.
136
+ function shouldProxy(req, file) {
137
+ // Non-GET/HEAD methods are always API requests
138
+ if (req.method !== "GET" && req.method !== "HEAD") return true
139
+
140
+ // If the file exists as a static asset, serve it directly
141
+ if (existsSync(file) && !statSync(file).isDirectory()) return false
142
+
143
+ // GET requests that accept text/html are browser navigation → SPA fallback
144
+ const accept = req.headers.accept || ""
145
+ if (accept.includes("text/html")) return false
146
+
147
+ // Everything else (JSON, SSE, etc.) → proxy
148
+ return true
149
+ }
150
+
151
+ function proxy(req, res) {
152
+ const url = new URL(req.url, `http://localhost:${port}`)
153
+ const target = `${backend.origin}${url.pathname}${url.search}`
154
+
155
+ const headers = { ...req.headers, host: backend.host }
156
+
157
+ const proxyReq = httpRequest(target, { method: req.method, headers }, (proxyRes) => {
158
+ res.writeHead(proxyRes.statusCode, proxyRes.headers)
159
+ proxyRes.pipe(res)
160
+ })
161
+
162
+ proxyReq.on("error", () => {
163
+ if (res.headersSent) return res.end()
164
+ res.writeHead(502, { "Content-Type": "application/json" })
165
+ res.end(
166
+ JSON.stringify({
167
+ error: `Backend not reachable at ${backend.origin}. Start it with: opencode serve`,
168
+ }),
169
+ )
170
+ })
171
+
172
+ req.pipe(proxyReq)
173
+ }
174
+
64
175
  const server = createServer((req, res) => {
65
176
  const url = new URL(req.url, `http://localhost:${port}`)
66
- let file = join(root, url.pathname === "/" ? "index.html" : url.pathname)
177
+ const file = join(root, url.pathname === "/" ? "index.html" : url.pathname)
67
178
 
68
- if (!existsSync(file) || statSync(file).isDirectory()) {
69
- file = join(root, "index.html")
70
- }
179
+ if (shouldProxy(req, file)) return proxy(req, res)
71
180
 
72
- const ext = extname(file)
181
+ // Serve static file or SPA fallback
182
+ const resolved = existsSync(file) && !statSync(file).isDirectory() ? file : join(root, "index.html")
183
+ const ext = extname(resolved)
73
184
  const type = mime[ext] || "application/octet-stream"
74
185
 
75
186
  try {
76
- const body = readFileSync(file)
187
+ const body = readFileSync(resolved)
77
188
  res.writeHead(200, {
78
189
  "Content-Type": type,
79
190
  "Content-Length": body.length,
@@ -86,26 +197,57 @@ const server = createServer((req, res) => {
86
197
  }
87
198
  })
88
199
 
200
+ // Handle WebSocket upgrade (used by PTY terminal connections)
201
+ server.on("upgrade", (req, socket, head) => {
202
+ const url = new URL(req.url, `http://localhost:${port}`)
203
+ const target = `${backend.origin}${url.pathname}${url.search}`
204
+ const headers = { ...req.headers, host: backend.host }
205
+
206
+ const proxyReq = httpRequest(target, { method: "GET", headers })
207
+
208
+ proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
209
+ socket.write(
210
+ `HTTP/1.1 101 Switching Protocols\r\n` +
211
+ Object.entries(proxyRes.headers)
212
+ .map(([k, v]) => `${k}: ${v}`)
213
+ .join("\r\n") +
214
+ "\r\n\r\n",
215
+ )
216
+ if (proxyHead.length) socket.write(proxyHead)
217
+ proxySocket.pipe(socket)
218
+ socket.pipe(proxySocket)
219
+ })
220
+
221
+ proxyReq.on("error", () => {
222
+ socket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n")
223
+ })
224
+
225
+ proxyReq.end()
226
+ })
227
+
89
228
  server.listen(port, () => {
90
- const url = `http://localhost:${port}`
229
+ const dir = encode(resolve(process.cwd()))
230
+ const url = `http://localhost:${port}/${dir}`
91
231
  console.log(`
92
232
  🐢 turtlecode v${process.env.npm_package_version || "0.1.0"}
93
233
 
94
- Local: ${url}
234
+ Local: ${url}
235
+ Backend: ${backend.origin}
236
+ Project: ${process.cwd()}
95
237
  Press Ctrl+C to stop
96
238
  `)
97
239
 
98
240
  if (open) {
99
- const cmd = process.platform === "darwin"
100
- ? "open"
101
- : process.platform === "win32"
102
- ? "start"
103
- : "xdg-open"
241
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open"
104
242
  exec(`${cmd} ${url}`)
105
243
  }
106
244
  })
107
245
 
108
- process.on("SIGINT", () => {
246
+ function cleanup() {
247
+ if (spawned) spawned.kill()
109
248
  console.log("\n Stopped.\n")
110
249
  process.exit(0)
111
- })
250
+ }
251
+
252
+ process.on("SIGINT", cleanup)
253
+ process.on("SIGTERM", cleanup)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turtlecode",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "AI-powered creative workspace — launch with npx turtlecode",
5
5
  "type": "module",
6
6
  "bin": {