@take-out/cli 0.0.39

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 (331) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +274 -0
  3. package/cli.mjs +3 -0
  4. package/dist/cjs/cli.cjs +71 -0
  5. package/dist/cjs/cli.js +70 -0
  6. package/dist/cjs/cli.js.map +6 -0
  7. package/dist/cjs/cli.native.js +79 -0
  8. package/dist/cjs/cli.native.js.map +6 -0
  9. package/dist/cjs/commands/changed.cjs +212 -0
  10. package/dist/cjs/commands/changed.js +214 -0
  11. package/dist/cjs/commands/changed.js.map +6 -0
  12. package/dist/cjs/commands/changed.native.js +289 -0
  13. package/dist/cjs/commands/changed.native.js.map +6 -0
  14. package/dist/cjs/commands/docs.cjs +388 -0
  15. package/dist/cjs/commands/docs.js +313 -0
  16. package/dist/cjs/commands/docs.js.map +6 -0
  17. package/dist/cjs/commands/docs.native.js +476 -0
  18. package/dist/cjs/commands/docs.native.js.map +6 -0
  19. package/dist/cjs/commands/env-setup.cjs +90 -0
  20. package/dist/cjs/commands/env-setup.js +78 -0
  21. package/dist/cjs/commands/env-setup.js.map +6 -0
  22. package/dist/cjs/commands/env-setup.native.js +85 -0
  23. package/dist/cjs/commands/env-setup.native.js.map +6 -0
  24. package/dist/cjs/commands/onboard.cjs +479 -0
  25. package/dist/cjs/commands/onboard.js +631 -0
  26. package/dist/cjs/commands/onboard.js.map +6 -0
  27. package/dist/cjs/commands/onboard.native.js +608 -0
  28. package/dist/cjs/commands/onboard.native.js.map +6 -0
  29. package/dist/cjs/commands/run.cjs +148 -0
  30. package/dist/cjs/commands/run.js +116 -0
  31. package/dist/cjs/commands/run.js.map +6 -0
  32. package/dist/cjs/commands/run.native.js +140 -0
  33. package/dist/cjs/commands/run.native.js.map +6 -0
  34. package/dist/cjs/commands/script.cjs +379 -0
  35. package/dist/cjs/commands/script.js +339 -0
  36. package/dist/cjs/commands/script.js.map +6 -0
  37. package/dist/cjs/commands/script.native.js +449 -0
  38. package/dist/cjs/commands/script.native.js.map +6 -0
  39. package/dist/cjs/commands/sync.cjs +190 -0
  40. package/dist/cjs/commands/sync.js +168 -0
  41. package/dist/cjs/commands/sync.js.map +6 -0
  42. package/dist/cjs/commands/sync.native.js +211 -0
  43. package/dist/cjs/commands/sync.native.js.map +6 -0
  44. package/dist/cjs/constants/ascii.cjs +36 -0
  45. package/dist/cjs/constants/ascii.js +30 -0
  46. package/dist/cjs/constants/ascii.js.map +6 -0
  47. package/dist/cjs/constants/ascii.native.js +36 -0
  48. package/dist/cjs/constants/ascii.native.js.map +6 -0
  49. package/dist/cjs/index.cjs +64 -0
  50. package/dist/cjs/index.js +55 -0
  51. package/dist/cjs/index.js.map +6 -0
  52. package/dist/cjs/index.native.js +94 -0
  53. package/dist/cjs/index.native.js.map +6 -0
  54. package/dist/cjs/types.cjs +16 -0
  55. package/dist/cjs/types.js +14 -0
  56. package/dist/cjs/types.js.map +6 -0
  57. package/dist/cjs/types.native.js +15 -0
  58. package/dist/cjs/types.native.js.map +6 -0
  59. package/dist/cjs/utils/env-categories.cjs +272 -0
  60. package/dist/cjs/utils/env-categories.js +296 -0
  61. package/dist/cjs/utils/env-categories.js.map +6 -0
  62. package/dist/cjs/utils/env-categories.native.js +317 -0
  63. package/dist/cjs/utils/env-categories.native.js.map +6 -0
  64. package/dist/cjs/utils/env-setup.cjs +181 -0
  65. package/dist/cjs/utils/env-setup.js +190 -0
  66. package/dist/cjs/utils/env-setup.js.map +6 -0
  67. package/dist/cjs/utils/env-setup.native.js +264 -0
  68. package/dist/cjs/utils/env-setup.native.js.map +6 -0
  69. package/dist/cjs/utils/env.cjs +118 -0
  70. package/dist/cjs/utils/env.js +97 -0
  71. package/dist/cjs/utils/env.js.map +6 -0
  72. package/dist/cjs/utils/env.native.js +128 -0
  73. package/dist/cjs/utils/env.native.js.map +6 -0
  74. package/dist/cjs/utils/files.cjs +215 -0
  75. package/dist/cjs/utils/files.js +164 -0
  76. package/dist/cjs/utils/files.js.map +6 -0
  77. package/dist/cjs/utils/files.native.js +266 -0
  78. package/dist/cjs/utils/files.native.js.map +6 -0
  79. package/dist/cjs/utils/parallel-runner.cjs +99 -0
  80. package/dist/cjs/utils/parallel-runner.js +84 -0
  81. package/dist/cjs/utils/parallel-runner.js.map +6 -0
  82. package/dist/cjs/utils/parallel-runner.native.js +123 -0
  83. package/dist/cjs/utils/parallel-runner.native.js.map +6 -0
  84. package/dist/cjs/utils/ports.cjs +101 -0
  85. package/dist/cjs/utils/ports.js +81 -0
  86. package/dist/cjs/utils/ports.js.map +6 -0
  87. package/dist/cjs/utils/ports.native.js +130 -0
  88. package/dist/cjs/utils/ports.native.js.map +6 -0
  89. package/dist/cjs/utils/prerequisites.cjs +119 -0
  90. package/dist/cjs/utils/prerequisites.js +107 -0
  91. package/dist/cjs/utils/prerequisites.js.map +6 -0
  92. package/dist/cjs/utils/prerequisites.native.js +127 -0
  93. package/dist/cjs/utils/prerequisites.native.js.map +6 -0
  94. package/dist/cjs/utils/prompts.cjs +161 -0
  95. package/dist/cjs/utils/prompts.js +162 -0
  96. package/dist/cjs/utils/prompts.js.map +6 -0
  97. package/dist/cjs/utils/prompts.native.js +179 -0
  98. package/dist/cjs/utils/prompts.native.js.map +6 -0
  99. package/dist/cjs/utils/script-listing.cjs +113 -0
  100. package/dist/cjs/utils/script-listing.js +108 -0
  101. package/dist/cjs/utils/script-listing.js.map +6 -0
  102. package/dist/cjs/utils/script-listing.native.js +174 -0
  103. package/dist/cjs/utils/script-listing.native.js.map +6 -0
  104. package/dist/cjs/utils/sync.cjs +85 -0
  105. package/dist/cjs/utils/sync.js +70 -0
  106. package/dist/cjs/utils/sync.js.map +6 -0
  107. package/dist/cjs/utils/sync.native.js +84 -0
  108. package/dist/cjs/utils/sync.native.js.map +6 -0
  109. package/dist/cjs/utils/welcome.cjs +50 -0
  110. package/dist/cjs/utils/welcome.js +42 -0
  111. package/dist/cjs/utils/welcome.js.map +6 -0
  112. package/dist/cjs/utils/welcome.native.js +47 -0
  113. package/dist/cjs/utils/welcome.native.js.map +6 -0
  114. package/dist/esm/cli.js +79 -0
  115. package/dist/esm/cli.js.map +6 -0
  116. package/dist/esm/cli.mjs +71 -0
  117. package/dist/esm/cli.mjs.map +1 -0
  118. package/dist/esm/cli.native.js +69 -0
  119. package/dist/esm/cli.native.js.map +1 -0
  120. package/dist/esm/commands/changed.js +194 -0
  121. package/dist/esm/commands/changed.js.map +6 -0
  122. package/dist/esm/commands/changed.mjs +178 -0
  123. package/dist/esm/commands/changed.mjs.map +1 -0
  124. package/dist/esm/commands/changed.native.js +273 -0
  125. package/dist/esm/commands/changed.native.js.map +1 -0
  126. package/dist/esm/commands/docs.js +306 -0
  127. package/dist/esm/commands/docs.js.map +6 -0
  128. package/dist/esm/commands/docs.mjs +353 -0
  129. package/dist/esm/commands/docs.mjs.map +1 -0
  130. package/dist/esm/commands/docs.native.js +516 -0
  131. package/dist/esm/commands/docs.native.js.map +1 -0
  132. package/dist/esm/commands/env-setup.js +56 -0
  133. package/dist/esm/commands/env-setup.js.map +6 -0
  134. package/dist/esm/commands/env-setup.mjs +56 -0
  135. package/dist/esm/commands/env-setup.mjs.map +1 -0
  136. package/dist/esm/commands/env-setup.native.js +59 -0
  137. package/dist/esm/commands/env-setup.native.js.map +1 -0
  138. package/dist/esm/commands/onboard.js +645 -0
  139. package/dist/esm/commands/onboard.js.map +6 -0
  140. package/dist/esm/commands/onboard.mjs +445 -0
  141. package/dist/esm/commands/onboard.mjs.map +1 -0
  142. package/dist/esm/commands/onboard.native.js +584 -0
  143. package/dist/esm/commands/onboard.native.js.map +1 -0
  144. package/dist/esm/commands/run.js +95 -0
  145. package/dist/esm/commands/run.js.map +6 -0
  146. package/dist/esm/commands/run.mjs +114 -0
  147. package/dist/esm/commands/run.mjs.map +1 -0
  148. package/dist/esm/commands/run.native.js +133 -0
  149. package/dist/esm/commands/run.native.js.map +1 -0
  150. package/dist/esm/commands/script.js +338 -0
  151. package/dist/esm/commands/script.js.map +6 -0
  152. package/dist/esm/commands/script.mjs +336 -0
  153. package/dist/esm/commands/script.mjs.map +1 -0
  154. package/dist/esm/commands/script.native.js +445 -0
  155. package/dist/esm/commands/script.native.js.map +1 -0
  156. package/dist/esm/commands/sync.js +158 -0
  157. package/dist/esm/commands/sync.js.map +6 -0
  158. package/dist/esm/commands/sync.mjs +155 -0
  159. package/dist/esm/commands/sync.mjs.map +1 -0
  160. package/dist/esm/commands/sync.native.js +173 -0
  161. package/dist/esm/commands/sync.native.js.map +1 -0
  162. package/dist/esm/constants/ascii.js +14 -0
  163. package/dist/esm/constants/ascii.js.map +6 -0
  164. package/dist/esm/constants/ascii.mjs +12 -0
  165. package/dist/esm/constants/ascii.mjs.map +1 -0
  166. package/dist/esm/constants/ascii.native.js +12 -0
  167. package/dist/esm/constants/ascii.native.js.map +1 -0
  168. package/dist/esm/index.js +83 -0
  169. package/dist/esm/index.js.map +6 -0
  170. package/dist/esm/index.mjs +7 -0
  171. package/dist/esm/index.mjs.map +1 -0
  172. package/dist/esm/index.native.js +7 -0
  173. package/dist/esm/index.native.js.map +1 -0
  174. package/dist/esm/types.js +1 -0
  175. package/dist/esm/types.js.map +6 -0
  176. package/dist/esm/types.mjs +2 -0
  177. package/dist/esm/types.mjs.map +1 -0
  178. package/dist/esm/types.native.js +2 -0
  179. package/dist/esm/types.native.js.map +1 -0
  180. package/dist/esm/utils/env-categories.js +272 -0
  181. package/dist/esm/utils/env-categories.js.map +6 -0
  182. package/dist/esm/utils/env-categories.mjs +233 -0
  183. package/dist/esm/utils/env-categories.mjs.map +1 -0
  184. package/dist/esm/utils/env-categories.native.js +246 -0
  185. package/dist/esm/utils/env-categories.native.js.map +1 -0
  186. package/dist/esm/utils/env-setup.js +173 -0
  187. package/dist/esm/utils/env-setup.js.map +6 -0
  188. package/dist/esm/utils/env-setup.mjs +146 -0
  189. package/dist/esm/utils/env-setup.mjs.map +1 -0
  190. package/dist/esm/utils/env-setup.native.js +243 -0
  191. package/dist/esm/utils/env-setup.native.js.map +1 -0
  192. package/dist/esm/utils/env.js +83 -0
  193. package/dist/esm/utils/env.js.map +6 -0
  194. package/dist/esm/utils/env.mjs +90 -0
  195. package/dist/esm/utils/env.mjs.map +1 -0
  196. package/dist/esm/utils/env.native.js +99 -0
  197. package/dist/esm/utils/env.native.js.map +1 -0
  198. package/dist/esm/utils/files.js +150 -0
  199. package/dist/esm/utils/files.js.map +6 -0
  200. package/dist/esm/utils/files.mjs +187 -0
  201. package/dist/esm/utils/files.mjs.map +1 -0
  202. package/dist/esm/utils/files.native.js +247 -0
  203. package/dist/esm/utils/files.native.js.map +1 -0
  204. package/dist/esm/utils/parallel-runner.js +69 -0
  205. package/dist/esm/utils/parallel-runner.js.map +6 -0
  206. package/dist/esm/utils/parallel-runner.mjs +76 -0
  207. package/dist/esm/utils/parallel-runner.mjs.map +1 -0
  208. package/dist/esm/utils/parallel-runner.native.js +109 -0
  209. package/dist/esm/utils/parallel-runner.native.js.map +1 -0
  210. package/dist/esm/utils/ports.js +65 -0
  211. package/dist/esm/utils/ports.js.map +6 -0
  212. package/dist/esm/utils/ports.mjs +74 -0
  213. package/dist/esm/utils/ports.mjs.map +1 -0
  214. package/dist/esm/utils/ports.native.js +93 -0
  215. package/dist/esm/utils/ports.native.js.map +1 -0
  216. package/dist/esm/utils/prerequisites.js +91 -0
  217. package/dist/esm/utils/prerequisites.js.map +6 -0
  218. package/dist/esm/utils/prerequisites.mjs +91 -0
  219. package/dist/esm/utils/prerequisites.mjs.map +1 -0
  220. package/dist/esm/utils/prerequisites.native.js +97 -0
  221. package/dist/esm/utils/prerequisites.native.js.map +1 -0
  222. package/dist/esm/utils/prompts.js +139 -0
  223. package/dist/esm/utils/prompts.js.map +6 -0
  224. package/dist/esm/utils/prompts.mjs +112 -0
  225. package/dist/esm/utils/prompts.mjs.map +1 -0
  226. package/dist/esm/utils/prompts.native.js +115 -0
  227. package/dist/esm/utils/prompts.native.js.map +1 -0
  228. package/dist/esm/utils/script-listing.js +91 -0
  229. package/dist/esm/utils/script-listing.js.map +6 -0
  230. package/dist/esm/utils/script-listing.mjs +76 -0
  231. package/dist/esm/utils/script-listing.mjs.map +1 -0
  232. package/dist/esm/utils/script-listing.native.js +151 -0
  233. package/dist/esm/utils/script-listing.native.js.map +1 -0
  234. package/dist/esm/utils/sync.js +50 -0
  235. package/dist/esm/utils/sync.js.map +6 -0
  236. package/dist/esm/utils/sync.mjs +48 -0
  237. package/dist/esm/utils/sync.mjs.map +1 -0
  238. package/dist/esm/utils/sync.native.js +53 -0
  239. package/dist/esm/utils/sync.native.js.map +1 -0
  240. package/dist/esm/utils/welcome.js +21 -0
  241. package/dist/esm/utils/welcome.js.map +6 -0
  242. package/dist/esm/utils/welcome.mjs +15 -0
  243. package/dist/esm/utils/welcome.mjs.map +1 -0
  244. package/dist/esm/utils/welcome.native.js +18 -0
  245. package/dist/esm/utils/welcome.native.js.map +1 -0
  246. package/docs/aggregates.md +579 -0
  247. package/docs/cloudflare-dev-tunnel.md +41 -0
  248. package/docs/database.md +203 -0
  249. package/docs/docs.md +8 -0
  250. package/docs/emitters.md +562 -0
  251. package/docs/hot-updater.md +223 -0
  252. package/docs/native-hot-update.md +252 -0
  253. package/docs/one-components.md +234 -0
  254. package/docs/one-hooks.md +570 -0
  255. package/docs/one-routes.md +660 -0
  256. package/docs/package-json.md +115 -0
  257. package/docs/react-native-navigation-flow.md +184 -0
  258. package/docs/scripts.md +147 -0
  259. package/docs/sync-prompt.md +208 -0
  260. package/docs/tamagui.md +478 -0
  261. package/docs/testing-integration.md +564 -0
  262. package/docs/triggers.md +450 -0
  263. package/docs/zero.md +719 -0
  264. package/package.json +76 -0
  265. package/scripts/seed.ts +209 -0
  266. package/src/cli.ts +147 -0
  267. package/src/commands/changed.ts +313 -0
  268. package/src/commands/docs.ts +582 -0
  269. package/src/commands/env-setup.ts +69 -0
  270. package/src/commands/onboard.ts +1391 -0
  271. package/src/commands/run.ts +173 -0
  272. package/src/commands/script.ts +587 -0
  273. package/src/commands/sync.ts +305 -0
  274. package/src/constants/ascii.ts +17 -0
  275. package/src/index.ts +63 -0
  276. package/src/types.ts +59 -0
  277. package/src/utils/env-categories.ts +245 -0
  278. package/src/utils/env-setup.ts +338 -0
  279. package/src/utils/env.ts +127 -0
  280. package/src/utils/files.ts +302 -0
  281. package/src/utils/parallel-runner.ts +129 -0
  282. package/src/utils/ports.ts +77 -0
  283. package/src/utils/prerequisites.ts +137 -0
  284. package/src/utils/prompts.ts +197 -0
  285. package/src/utils/script-listing.ts +214 -0
  286. package/src/utils/sync.ts +101 -0
  287. package/src/withOpSqliteStatic.cjs +51 -0
  288. package/types/cli.d.ts +7 -0
  289. package/types/cli.d.ts.map +1 -0
  290. package/types/commands/changed.d.ts +14 -0
  291. package/types/commands/changed.d.ts.map +1 -0
  292. package/types/commands/docs.d.ts +5 -0
  293. package/types/commands/docs.d.ts.map +1 -0
  294. package/types/commands/env-setup.d.ts +25 -0
  295. package/types/commands/env-setup.d.ts.map +1 -0
  296. package/types/commands/onboard.d.ts +16 -0
  297. package/types/commands/onboard.d.ts.map +1 -0
  298. package/types/commands/run.d.ts +8 -0
  299. package/types/commands/run.d.ts.map +1 -0
  300. package/types/commands/script.d.ts +28 -0
  301. package/types/commands/script.d.ts.map +1 -0
  302. package/types/commands/sync.d.ts +5 -0
  303. package/types/commands/sync.d.ts.map +1 -0
  304. package/types/constants/ascii.d.ts +6 -0
  305. package/types/constants/ascii.d.ts.map +1 -0
  306. package/types/index.d.ts +12 -0
  307. package/types/index.d.ts.map +1 -0
  308. package/types/types.d.ts +54 -0
  309. package/types/types.d.ts.map +1 -0
  310. package/types/utils/env-categories.d.ts +8 -0
  311. package/types/utils/env-categories.d.ts.map +1 -0
  312. package/types/utils/env-setup.d.ts +10 -0
  313. package/types/utils/env-setup.d.ts.map +1 -0
  314. package/types/utils/env.d.ts +19 -0
  315. package/types/utils/env.d.ts.map +1 -0
  316. package/types/utils/files.d.ts +47 -0
  317. package/types/utils/files.d.ts.map +1 -0
  318. package/types/utils/parallel-runner.d.ts +15 -0
  319. package/types/utils/parallel-runner.d.ts.map +1 -0
  320. package/types/utils/ports.d.ts +16 -0
  321. package/types/utils/ports.d.ts.map +1 -0
  322. package/types/utils/prerequisites.d.ts +11 -0
  323. package/types/utils/prerequisites.d.ts.map +1 -0
  324. package/types/utils/prompts.d.ts +30 -0
  325. package/types/utils/prompts.d.ts.map +1 -0
  326. package/types/utils/script-listing.d.ts +7 -0
  327. package/types/utils/script-listing.d.ts.map +1 -0
  328. package/types/utils/sync.d.ts +16 -0
  329. package/types/utils/sync.d.ts.map +1 -0
  330. package/types/utils/welcome.d.ts +6 -0
  331. package/types/utils/welcome.d.ts.map +1 -0
@@ -0,0 +1,587 @@
1
+ /**
2
+ * Enhanced Script command - hybrid script runner with filesystem discovery
3
+ */
4
+
5
+ import {
6
+ existsSync,
7
+ readdirSync,
8
+ statSync,
9
+ readFileSync,
10
+ writeFileSync,
11
+ mkdirSync,
12
+ } from 'node:fs'
13
+ import { homedir } from 'node:os'
14
+ import { join, relative, dirname, basename, extname } from 'node:path'
15
+ import { fileURLToPath } from 'node:url'
16
+
17
+ import { defineCommand } from 'citty'
18
+ import pc from 'picocolors'
19
+
20
+ import { listAllScripts, listCategoryScripts } from '../utils/script-listing'
21
+ import {
22
+ type FileToSync,
23
+ compareFiles,
24
+ getFileSize,
25
+ syncFileWithConfirmation,
26
+ } from '../utils/sync'
27
+
28
+ // cache configuration
29
+ const CACHE_DIR = join(homedir(), '.takeout')
30
+ const CACHE_FILE = join(CACHE_DIR, 'script-cache.json')
31
+
32
+ interface ScriptMetadata {
33
+ description?: string
34
+ args?: string[]
35
+ mtime?: number
36
+ }
37
+
38
+ interface ScriptCache {
39
+ [path: string]: ScriptMetadata
40
+ }
41
+
42
+ // find scripts package root using import.meta.resolve
43
+ function findScriptsPackageRoot(): string | null {
44
+ try {
45
+ const resolved = import.meta.resolve('@take-out/scripts/package.json')
46
+ // use fileURLToPath for proper cross-platform handling
47
+ const packageJsonPath = fileURLToPath(new URL(resolved))
48
+ const packageRoot = join(packageJsonPath, '..')
49
+ const srcPath = join(packageRoot, 'src')
50
+
51
+ if (existsSync(srcPath)) {
52
+ return srcPath
53
+ }
54
+ } catch (err) {
55
+ // scripts package not found, that's ok for hybrid mode
56
+ }
57
+
58
+ return null
59
+ }
60
+
61
+ // get local scripts directory
62
+ export function getLocalScriptsDir(): string {
63
+ return join(process.cwd(), 'scripts')
64
+ }
65
+
66
+ // extract metadata from script file
67
+ function extractMetadata(filePath: string): ScriptMetadata {
68
+ try {
69
+ const content = readFileSync(filePath, 'utf-8')
70
+ const lines = content.split('\n').slice(0, 20) // check first 20 lines
71
+
72
+ const metadata: ScriptMetadata = {}
73
+
74
+ for (const line of lines) {
75
+ // look for @description comment
76
+ const descMatch = line.match(/@description\s+(.+)/)
77
+ if (descMatch && descMatch[1]) {
78
+ metadata.description = descMatch[1].trim()
79
+ }
80
+
81
+ // look for @args comment
82
+ const argsMatch = line.match(/@args\s+(.+)/)
83
+ if (argsMatch && argsMatch[1]) {
84
+ metadata.args = argsMatch[1].split(',').map((a) => a.trim())
85
+ }
86
+ }
87
+
88
+ // add file modification time
89
+ const stat = statSync(filePath)
90
+ metadata.mtime = stat.mtimeMs
91
+
92
+ return metadata
93
+ } catch (err) {
94
+ return {}
95
+ }
96
+ }
97
+
98
+ // load or create cache
99
+ function loadCache(): ScriptCache {
100
+ try {
101
+ if (existsSync(CACHE_FILE)) {
102
+ return JSON.parse(readFileSync(CACHE_FILE, 'utf-8'))
103
+ }
104
+ } catch (err) {
105
+ // ignore cache errors
106
+ }
107
+ return {}
108
+ }
109
+
110
+ // save cache
111
+ function saveCache(cache: ScriptCache) {
112
+ try {
113
+ if (!existsSync(CACHE_DIR)) {
114
+ mkdirSync(CACHE_DIR, { recursive: true })
115
+ }
116
+ writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2))
117
+ } catch (err) {
118
+ // ignore cache write errors
119
+ }
120
+ }
121
+
122
+ // get metadata for a script (with caching)
123
+ export function getScriptMetadata(filePath: string): ScriptMetadata {
124
+ const cache = loadCache()
125
+ const stat = statSync(filePath)
126
+
127
+ // check if cache is valid
128
+ if (cache[filePath] && cache[filePath].mtime === stat.mtimeMs) {
129
+ return cache[filePath]
130
+ }
131
+
132
+ // extract fresh metadata
133
+ const metadata = extractMetadata(filePath)
134
+
135
+ // update cache
136
+ cache[filePath] = metadata
137
+ saveCache(cache)
138
+
139
+ return metadata
140
+ }
141
+
142
+ // discover scripts in a directory
143
+ export function discoverScripts(
144
+ dir: string,
145
+ baseDir: string = dir,
146
+ depth: number = 0,
147
+ maxDepth: number = 1
148
+ ): Map<string, string> {
149
+ const scripts = new Map<string, string>()
150
+
151
+ if (!existsSync(dir)) {
152
+ return scripts
153
+ }
154
+
155
+ try {
156
+ const entries = readdirSync(dir)
157
+
158
+ for (const entry of entries) {
159
+ const fullPath = join(dir, entry)
160
+ const stat = statSync(fullPath)
161
+
162
+ if (stat.isDirectory()) {
163
+ // skip helpers directories and limit depth to maxDepth
164
+ if (entry === 'helpers' || depth >= maxDepth) {
165
+ continue
166
+ }
167
+ // recursively discover in subdirectories
168
+ const subScripts = discoverScripts(fullPath, baseDir, depth + 1, maxDepth)
169
+ for (const [name, path] of subScripts) {
170
+ scripts.set(name, path)
171
+ }
172
+ } else if (entry.endsWith('.ts') || entry.endsWith('.js')) {
173
+ // create script name relative to base, normalize to forward slashes
174
+ const relativePath = relative(baseDir, fullPath).split('\\').join('/')
175
+ const scriptName = relativePath.replace(/\.(ts|js)$/, '')
176
+ scripts.set(scriptName, fullPath)
177
+ }
178
+ }
179
+ } catch (err) {
180
+ // ignore directory read errors
181
+ }
182
+
183
+ return scripts
184
+ }
185
+
186
+ // find a script by name (checks both built-in and local)
187
+ export function findScript(name: string): string | null {
188
+ // normalize name: convert colons to slashes for consistency
189
+ const normalizedName = name.replace(/:/g, '/')
190
+
191
+ // 1. check local scripts first (they override built-ins)
192
+ const localDir = getLocalScriptsDir()
193
+
194
+ // try direct file path with normalized name
195
+ for (const ext of ['.ts', '.js', '']) {
196
+ const localPath = join(localDir, `${normalizedName}${ext}`)
197
+ if (existsSync(localPath)) {
198
+ return localPath
199
+ }
200
+ }
201
+
202
+ // 2. check built-in scripts
203
+ const builtInDir = findScriptsPackageRoot()
204
+ if (builtInDir) {
205
+ for (const ext of ['.ts', '.js', '']) {
206
+ const builtInPath = join(builtInDir, `${normalizedName}${ext}`)
207
+ if (existsSync(builtInPath)) {
208
+ return builtInPath
209
+ }
210
+ }
211
+ }
212
+
213
+ return null
214
+ }
215
+
216
+ export {
217
+ formatScriptList,
218
+ listAllScripts,
219
+ listCategoryScripts,
220
+ } from '../utils/script-listing'
221
+
222
+ // run a script
223
+ async function runScript(scriptPath: string, args: string[]): Promise<void> {
224
+ console.info(pc.dim(`Running: ${relative(process.cwd(), scriptPath)}`))
225
+ console.info()
226
+
227
+ try {
228
+ const { $ } = await import('bun')
229
+ await $`bun ${scriptPath} ${args}`
230
+ } catch (err) {
231
+ console.error(pc.red(`✗ Error running script: ${err}`))
232
+ process.exit(1)
233
+ }
234
+ }
235
+
236
+ // new command for creating scripts
237
+ const newCommand = defineCommand({
238
+ meta: {
239
+ name: 'new',
240
+ description: 'Create a new script from template',
241
+ },
242
+ args: {
243
+ path: {
244
+ type: 'positional',
245
+ description: 'Path for the new script (e.g., ci/test)',
246
+ required: true,
247
+ },
248
+ },
249
+ async run({ args }) {
250
+ const scriptPath = args.path
251
+ const localDir = getLocalScriptsDir()
252
+
253
+ // determine full path
254
+ const fullPath = join(localDir, `${scriptPath}.ts`)
255
+ const dir = dirname(fullPath)
256
+
257
+ // create directory if needed
258
+ if (!existsSync(dir)) {
259
+ mkdirSync(dir, { recursive: true })
260
+ }
261
+
262
+ // check if file exists
263
+ if (existsSync(fullPath)) {
264
+ console.error(
265
+ pc.red(`✗ Script already exists: ${relative(process.cwd(), fullPath)}`)
266
+ )
267
+ process.exit(1)
268
+ }
269
+
270
+ // create template
271
+ const template = `#!/usr/bin/env bun
272
+
273
+ /**
274
+ * @description TODO: Add description
275
+ * @args --verbose, --dry-run
276
+ */
277
+
278
+ import { parseArgs } from 'node:util'
279
+
280
+ const { values } = parseArgs({
281
+ args: process.argv.slice(2),
282
+ options: {
283
+ verbose: {
284
+ type: 'boolean',
285
+ short: 'v',
286
+ },
287
+ 'dry-run': {
288
+ type: 'boolean',
289
+ },
290
+ },
291
+ })
292
+
293
+ async function main() {
294
+ console.info('Running ${basename(scriptPath)}...')
295
+
296
+ if (values.verbose) {
297
+ console.info('Verbose mode enabled')
298
+ }
299
+
300
+ if (values['dry-run']) {
301
+ console.info('Dry run mode - no changes will be made')
302
+ }
303
+
304
+ // TODO: Implement your script logic here
305
+ }
306
+
307
+ main().catch(console.error)
308
+ `
309
+
310
+ writeFileSync(fullPath, template)
311
+ console.info(pc.green(`✓ Created new script: ${relative(process.cwd(), fullPath)}`))
312
+ console.info()
313
+ console.info(pc.dim(`Edit the script and update the TODO sections`))
314
+ },
315
+ })
316
+
317
+ // get all script files recursively
318
+ function getAllScriptFiles(dir: string, baseDir: string = dir): string[] {
319
+ const files: string[] = []
320
+
321
+ if (!existsSync(dir)) {
322
+ return files
323
+ }
324
+
325
+ try {
326
+ const entries = readdirSync(dir)
327
+
328
+ for (const entry of entries) {
329
+ const fullPath = join(dir, entry)
330
+ const stat = statSync(fullPath)
331
+
332
+ if (stat.isDirectory()) {
333
+ files.push(...getAllScriptFiles(fullPath, baseDir))
334
+ } else if (
335
+ entry.endsWith('.ts') ||
336
+ entry.endsWith('.js') ||
337
+ entry.endsWith('.cjs')
338
+ ) {
339
+ // normalize to forward slashes for consistent naming
340
+ files.push(relative(baseDir, fullPath).split('\\').join('/'))
341
+ }
342
+ }
343
+ } catch (err) {
344
+ // ignore directory read errors
345
+ }
346
+
347
+ return files
348
+ }
349
+
350
+ // eject command to copy built-in scripts to local directory
351
+ const ejectCommand = defineCommand({
352
+ meta: {
353
+ name: 'eject',
354
+ description: 'Eject built-in scripts into your project',
355
+ },
356
+ args: {
357
+ yes: {
358
+ type: 'boolean',
359
+ description: 'Skip confirmations and eject all files',
360
+ default: false,
361
+ },
362
+ },
363
+ async run({ args }) {
364
+ const cwd = process.cwd()
365
+ const targetScriptsDir = join(cwd, 'scripts')
366
+ const sourceScriptsDir = findScriptsPackageRoot()
367
+
368
+ console.info()
369
+ console.info(pc.bold(pc.cyan('⚙️ Eject Scripts')))
370
+ console.info()
371
+
372
+ if (!sourceScriptsDir) {
373
+ console.error(
374
+ pc.red(
375
+ '✗ Built-in scripts package (@take-out/scripts) not found or not installed'
376
+ )
377
+ )
378
+ console.info()
379
+ console.info(pc.dim('Install with: bun add -d @take-out/scripts'))
380
+ process.exit(1)
381
+ }
382
+
383
+ console.info(pc.dim(`Source: ${sourceScriptsDir}`))
384
+ console.info(pc.dim(`Target: ${targetScriptsDir}`))
385
+ console.info()
386
+
387
+ // ensure target scripts directory exists
388
+ if (!existsSync(targetScriptsDir)) {
389
+ console.info(pc.yellow('⚠ Target scripts directory does not exist, will create it'))
390
+ mkdirSync(targetScriptsDir, { recursive: true })
391
+ }
392
+
393
+ // get all script files from source (including subdirectories)
394
+ const sourceFiles = getAllScriptFiles(sourceScriptsDir)
395
+
396
+ if (sourceFiles.length === 0) {
397
+ console.info(pc.yellow('No script files found in built-in scripts package'))
398
+ return
399
+ }
400
+
401
+ // analyze all files
402
+ const filesToSync: FileToSync[] = []
403
+ const stats = {
404
+ new: 0,
405
+ modified: 0,
406
+ identical: 0,
407
+ }
408
+
409
+ for (const file of sourceFiles) {
410
+ const sourcePath = join(sourceScriptsDir, file)
411
+ const targetPath = join(targetScriptsDir, file)
412
+ const status = compareFiles(sourcePath, targetPath)
413
+
414
+ stats[status]++
415
+
416
+ filesToSync.push({
417
+ name: file,
418
+ sourcePath,
419
+ targetPath,
420
+ status,
421
+ sourceSize: getFileSize(sourcePath),
422
+ targetSize: getFileSize(targetPath),
423
+ })
424
+ }
425
+
426
+ console.info(pc.bold('Summary:'))
427
+ console.info(` ${pc.green(`${stats.new} new`)}`)
428
+ console.info(` ${pc.yellow(`${stats.modified} modified`)}`)
429
+ console.info(` ${pc.dim(`${stats.identical} identical`)}`)
430
+ console.info()
431
+
432
+ if (stats.new === 0 && stats.modified === 0) {
433
+ console.info(pc.green('✓ All scripts are already up to date!'))
434
+ return
435
+ }
436
+
437
+ // sort files: new first, then modified, then identical
438
+ const sortOrder = { new: 0, modified: 1, identical: 2 }
439
+ filesToSync.sort((a, b) => sortOrder[a.status] - sortOrder[b.status])
440
+
441
+ // sync files
442
+ let syncedCount = 0
443
+
444
+ for (const file of filesToSync) {
445
+ if (args.yes && file.status !== 'identical') {
446
+ // auto-sync without confirmation
447
+ const targetDir = dirname(file.targetPath)
448
+ if (!existsSync(targetDir)) {
449
+ mkdirSync(targetDir, { recursive: true })
450
+ }
451
+ const content = readFileSync(file.sourcePath)
452
+ writeFileSync(file.targetPath, content)
453
+ console.info(pc.green(` ✓ ${file.name}`))
454
+ syncedCount++
455
+ } else {
456
+ const wasSynced = await syncFileWithConfirmation(file)
457
+ if (wasSynced) {
458
+ syncedCount++
459
+ }
460
+ }
461
+ }
462
+
463
+ console.info()
464
+ console.info(pc.bold(pc.green(`✓ Complete: ${syncedCount} file(s) ejected`)))
465
+ console.info()
466
+ },
467
+ })
468
+
469
+ // run subcommand for backwards compatibility
470
+ const runSubCommand = defineCommand({
471
+ meta: {
472
+ name: 'run',
473
+ description: 'Run a script (for backwards compatibility)',
474
+ },
475
+ args: {
476
+ name: {
477
+ type: 'positional',
478
+ description: 'Script name',
479
+ required: true,
480
+ },
481
+ },
482
+ async run({ args, rawArgs }) {
483
+ // Find and run the script
484
+ const scriptPath = findScript(args.name)
485
+
486
+ if (!scriptPath) {
487
+ console.error(pc.red(`✗ Script not found: ${args.name}`))
488
+ console.info()
489
+ console.info(pc.dim(`Run 'tko script' to see available scripts`))
490
+ process.exit(1)
491
+ }
492
+
493
+ // Extract args after the script name
494
+ const scriptArgs = rawArgs.slice(rawArgs.indexOf(args.name) + 1)
495
+
496
+ await runScript(scriptPath, scriptArgs)
497
+ },
498
+ })
499
+
500
+ // main script command (handles both list and run)
501
+ export const scriptCommand = defineCommand({
502
+ meta: {
503
+ name: 'script',
504
+ description: 'Hybrid script runner with filesystem discovery',
505
+ },
506
+ args: {
507
+ name: {
508
+ type: 'positional',
509
+ description: 'Script name or category to run/list',
510
+ required: false,
511
+ },
512
+ },
513
+ subCommands: {
514
+ new: newCommand,
515
+ run: runSubCommand,
516
+ eject: ejectCommand,
517
+ },
518
+ async run({ args, rawArgs }) {
519
+ // citty doesn't handle args with hyphens well, so use rawArgs directly
520
+ // rawArgs[0] is 'script', rawArgs[1] is the actual script name
521
+ const scriptName = args.name || rawArgs[1]
522
+
523
+ // if no name provided, list all available scripts
524
+ if (!scriptName) {
525
+ listAllScripts(false) // don't show built-in commands in script listing
526
+ return
527
+ }
528
+
529
+ // check if this is a category listing (e.g., "takeout script ci")
530
+ if (listCategoryScripts(scriptName)) {
531
+ return
532
+ }
533
+
534
+ // find and run the script
535
+ const scriptPath = findScript(scriptName)
536
+
537
+ if (!scriptPath) {
538
+ console.error(pc.red(`✗ Script not found: ${scriptName}`))
539
+ console.info()
540
+ console.info(pc.dim(`Run 'tko script' to see available scripts`))
541
+ process.exit(1)
542
+ }
543
+
544
+ // extract args after the script name
545
+ const scriptArgs = rawArgs.slice(rawArgs.indexOf(scriptName) + 1)
546
+
547
+ await runScript(scriptPath, scriptArgs)
548
+ },
549
+ })
550
+
551
+ // shorthand command that skips "script" subcommand
552
+ export function createShorthandCommand(name: string) {
553
+ return defineCommand({
554
+ meta: {
555
+ name,
556
+ description: 'Run script directly',
557
+ },
558
+ args: {
559
+ args: {
560
+ type: 'positional',
561
+ description: 'Script and arguments',
562
+ required: false,
563
+ },
564
+ },
565
+ async run({ rawArgs }) {
566
+ // if no args provided, list scripts in this category
567
+ const scriptName = rawArgs.length > 0 ? `${name}/${rawArgs[0]}` : name
568
+ const scriptArgs =
569
+ rawArgs.length > 0
570
+ ? ['script', scriptName, ...rawArgs.slice(1)]
571
+ : ['script', name]
572
+
573
+ // delegate to script command
574
+ const { run } = scriptCommand
575
+ if (run) {
576
+ await run({
577
+ args: {
578
+ name: scriptName,
579
+ _: rawArgs.slice(1),
580
+ } as any,
581
+ rawArgs: scriptArgs,
582
+ cmd: scriptCommand,
583
+ } as any)
584
+ }
585
+ },
586
+ })
587
+ }