@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,582 @@
1
+ /**
2
+ * Docs command - list and retrieve documentation files from the package
3
+ */
4
+
5
+ import {
6
+ existsSync,
7
+ lstatSync,
8
+ mkdirSync,
9
+ readdirSync,
10
+ readFileSync,
11
+ rmSync,
12
+ symlinkSync,
13
+ unlinkSync,
14
+ writeFileSync,
15
+ } from 'node:fs'
16
+ import { join, parse, relative } from 'node:path'
17
+
18
+ import { defineCommand } from 'citty'
19
+ import pc from 'picocolors'
20
+
21
+ import {
22
+ type FileToSync,
23
+ compareFiles,
24
+ getFileSize,
25
+ syncFileWithConfirmation,
26
+ } from '../utils/sync'
27
+
28
+ // Find package root by looking for package.json
29
+ function findPackageRoot(startDir: string): string {
30
+ let dir = startDir
31
+ while (dir !== parse(dir).root) {
32
+ if (existsSync(join(dir, 'package.json'))) {
33
+ const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'))
34
+ if (pkg.name === '@take-out/cli') {
35
+ return dir
36
+ }
37
+ }
38
+ dir = join(dir, '..')
39
+ }
40
+ throw new Error('Could not find @take-out/cli package root')
41
+ }
42
+
43
+ const PACKAGE_ROOT = findPackageRoot(import.meta.dirname)
44
+ const DOCS_DIR = join(PACKAGE_ROOT, 'docs')
45
+
46
+ const listCommand = defineCommand({
47
+ meta: {
48
+ name: 'list',
49
+ description: 'List all available documentation files',
50
+ },
51
+ async run() {
52
+ if (!existsSync(DOCS_DIR)) {
53
+ console.error(pc.red('āœ— Docs directory not found'))
54
+ process.exit(1)
55
+ }
56
+
57
+ const files = readdirSync(DOCS_DIR)
58
+ .filter((f) => f.endsWith('.md'))
59
+ .sort()
60
+
61
+ console.info()
62
+ console.info(pc.bold(pc.cyan('šŸ“š Available Documentation')))
63
+ console.info()
64
+
65
+ for (const file of files) {
66
+ const name = file.replace(/\.md$/, '')
67
+ const path = join(DOCS_DIR, file)
68
+
69
+ // try to extract first heading as description
70
+ let description = ''
71
+ try {
72
+ const content = readFileSync(path, 'utf-8')
73
+ const match = content.match(/^#\s+(.+)$/m)
74
+ if (match?.[1]) {
75
+ description = match[1]
76
+ }
77
+ } catch {
78
+ // ignore read errors
79
+ }
80
+
81
+ if (description) {
82
+ console.info(` ${pc.green(name)}`)
83
+ console.info(` ${pc.dim(description)}`)
84
+ } else {
85
+ console.info(` ${pc.green(name)}`)
86
+ }
87
+ }
88
+
89
+ console.info()
90
+ console.info(pc.dim(`Use 'takeout docs get <name>' to view a document`))
91
+ console.info()
92
+ },
93
+ })
94
+
95
+ const getCommand = defineCommand({
96
+ meta: {
97
+ name: 'get',
98
+ description: 'Get the content of one or more documentation files',
99
+ },
100
+ args: {
101
+ name: {
102
+ type: 'positional',
103
+ description: 'Name(s) of the doc files (without .md extension)',
104
+ required: true,
105
+ valueHint: 'name...',
106
+ },
107
+ },
108
+ async run({ args }) {
109
+ // args._ contains all positional arguments
110
+ // if multiple names are provided, they'll be in args._
111
+ // if only one is provided, it will be in both args.name and args._[0]
112
+ const names = args._.length > 0 ? args._ : [args.name]
113
+
114
+ const results: Array<{ name: string; content: string }> = []
115
+ const errors: Array<{ name: string; error: string }> = []
116
+
117
+ // check local ./docs folder first
118
+ const cwd = process.cwd()
119
+ const localDocsDir = join(cwd, 'docs')
120
+
121
+ // collect all docs
122
+ for (const name of names) {
123
+ const fileName = name.endsWith('.md') ? name : `${name}.md`
124
+
125
+ // try local docs first
126
+ const localFilePath = join(localDocsDir, fileName)
127
+ const packageFilePath = join(DOCS_DIR, fileName)
128
+
129
+ let filePath: string | null = null
130
+
131
+ if (existsSync(localFilePath)) {
132
+ filePath = localFilePath
133
+ } else if (existsSync(packageFilePath)) {
134
+ filePath = packageFilePath
135
+ }
136
+
137
+ if (!filePath) {
138
+ errors.push({ name, error: 'File not found' })
139
+ continue
140
+ }
141
+
142
+ try {
143
+ const content = readFileSync(filePath, 'utf-8')
144
+ results.push({ name, content })
145
+ } catch (err) {
146
+ errors.push({ name, error: String(err) })
147
+ }
148
+ }
149
+
150
+ // print errors if any
151
+ if (errors.length > 0) {
152
+ for (const { name, error } of errors) {
153
+ console.error(pc.red(`āœ— Doc file not found: ${name}`))
154
+ }
155
+ console.info()
156
+ console.info(pc.dim(`Use 'takeout docs list' to see available docs`))
157
+
158
+ // if no successful results, exit
159
+ if (results.length === 0) {
160
+ process.exit(1)
161
+ }
162
+ console.info()
163
+ }
164
+
165
+ // print results
166
+ for (let i = 0; i < results.length; i++) {
167
+ const result = results[i]
168
+ if (!result) continue
169
+
170
+ // print title
171
+ console.info(`# ${result.name}`)
172
+ console.info()
173
+
174
+ // print content
175
+ console.info(result.content)
176
+
177
+ // print separator if not last item
178
+ if (i < results.length - 1) {
179
+ console.info()
180
+ console.info('---')
181
+ console.info()
182
+ }
183
+ }
184
+ },
185
+ })
186
+
187
+ const pathCommand = defineCommand({
188
+ meta: {
189
+ name: 'path',
190
+ description: 'Get the absolute path to a documentation file',
191
+ },
192
+ args: {
193
+ name: {
194
+ type: 'positional',
195
+ description: 'Name of the doc file (without .md extension)',
196
+ required: false,
197
+ },
198
+ },
199
+ async run({ args }) {
200
+ if (!args.name) {
201
+ // return docs directory path
202
+ console.info(DOCS_DIR)
203
+ return
204
+ }
205
+
206
+ const fileName = args.name.endsWith('.md') ? args.name : `${args.name}.md`
207
+ const filePath = join(DOCS_DIR, fileName)
208
+
209
+ if (!existsSync(filePath)) {
210
+ console.error(pc.red(`āœ— Doc file not found: ${args.name}`))
211
+ process.exit(1)
212
+ }
213
+
214
+ console.info(filePath)
215
+ },
216
+ })
217
+
218
+ const ejectCommand = defineCommand({
219
+ meta: {
220
+ name: 'eject',
221
+ description: 'Eject Takeout documentation files into your project',
222
+ },
223
+ args: {
224
+ yes: {
225
+ type: 'boolean',
226
+ description: 'Skip confirmations and eject all files',
227
+ default: false,
228
+ },
229
+ },
230
+ async run({ args }) {
231
+ const cwd = process.cwd()
232
+ const targetDocsDir = join(cwd, 'docs')
233
+ const sourceDocsDir = DOCS_DIR
234
+
235
+ console.info()
236
+ console.info(pc.bold(pc.cyan('šŸ“š Eject Docs')))
237
+ console.info()
238
+ console.info(pc.dim(`Source: ${sourceDocsDir}`))
239
+ console.info(pc.dim(`Target: ${targetDocsDir}`))
240
+ console.info()
241
+
242
+ if (!existsSync(sourceDocsDir)) {
243
+ console.error(pc.red('āœ— Source docs directory not found in Takeout package'))
244
+ process.exit(1)
245
+ }
246
+
247
+ // ensure target docs directory exists
248
+ if (!existsSync(targetDocsDir)) {
249
+ console.info(pc.yellow('⚠ Target docs directory does not exist, will create it'))
250
+ mkdirSync(targetDocsDir, { recursive: true })
251
+ }
252
+
253
+ // get all markdown files from source
254
+ const sourceFiles = readdirSync(sourceDocsDir).filter((f) => f.endsWith('.md'))
255
+
256
+ if (sourceFiles.length === 0) {
257
+ console.info(pc.yellow('No markdown files found in Takeout docs'))
258
+ return
259
+ }
260
+
261
+ // analyze all files
262
+ const filesToSync: FileToSync[] = []
263
+ const stats = {
264
+ new: 0,
265
+ modified: 0,
266
+ identical: 0,
267
+ }
268
+
269
+ for (const file of sourceFiles) {
270
+ const sourcePath = join(sourceDocsDir, file)
271
+ const targetPath = join(targetDocsDir, file)
272
+ const status = compareFiles(sourcePath, targetPath)
273
+
274
+ stats[status]++
275
+
276
+ filesToSync.push({
277
+ name: file,
278
+ sourcePath,
279
+ targetPath,
280
+ status,
281
+ sourceSize: getFileSize(sourcePath),
282
+ targetSize: getFileSize(targetPath),
283
+ })
284
+ }
285
+
286
+ console.info(pc.bold('Summary:'))
287
+ console.info(` ${pc.green(`${stats.new} new`)}`)
288
+ console.info(` ${pc.yellow(`${stats.modified} modified`)}`)
289
+ console.info(` ${pc.dim(`${stats.identical} identical`)}`)
290
+ console.info()
291
+
292
+ if (stats.new === 0 && stats.modified === 0) {
293
+ console.info(pc.green('āœ“ All docs are already up to date!'))
294
+ return
295
+ }
296
+
297
+ // sort files: new first, then modified, then identical
298
+ const sortOrder = { new: 0, modified: 1, identical: 2 }
299
+ filesToSync.sort((a, b) => sortOrder[a.status] - sortOrder[b.status])
300
+
301
+ // sync files
302
+ let syncedCount = 0
303
+
304
+ for (const file of filesToSync) {
305
+ if (args.yes && file.status !== 'identical') {
306
+ // auto-sync without confirmation
307
+ const targetDir = join(targetDocsDir)
308
+ if (!existsSync(targetDir)) {
309
+ mkdirSync(targetDir, { recursive: true })
310
+ }
311
+ const content = readFileSync(file.sourcePath)
312
+ writeFileSync(file.targetPath, content)
313
+ console.info(pc.green(` āœ“ ${file.name}`))
314
+ syncedCount++
315
+ } else {
316
+ const wasSynced = await syncFileWithConfirmation(file)
317
+ if (wasSynced) {
318
+ syncedCount++
319
+ }
320
+ }
321
+ }
322
+
323
+ console.info()
324
+ console.info(pc.bold(pc.green(`āœ“ Complete: ${syncedCount} file(s) ejected`)))
325
+ console.info()
326
+ },
327
+ })
328
+
329
+ // check if content has yaml frontmatter with required skill fields
330
+ function hasSkillFrontmatter(content: string): boolean {
331
+ if (!content.startsWith('---')) return false
332
+ const endIndex = content.indexOf('---', 3)
333
+ if (endIndex === -1) return false
334
+
335
+ const frontmatter = content.slice(3, endIndex)
336
+ return frontmatter.includes('name:') && frontmatter.includes('description:')
337
+ }
338
+
339
+ // extract title and description from markdown content (for docs without frontmatter)
340
+ function extractDocMeta(content: string): { title: string; description: string } {
341
+ const lines = content.split('\n')
342
+ let title = ''
343
+ let description = ''
344
+
345
+ // skip frontmatter if present
346
+ let startLine = 0
347
+ if (lines[0]?.trim() === '---') {
348
+ for (let i = 1; i < lines.length; i++) {
349
+ if (lines[i]?.trim() === '---') {
350
+ startLine = i + 1
351
+ break
352
+ }
353
+ }
354
+ }
355
+
356
+ for (let i = startLine; i < lines.length; i++) {
357
+ const trimmed = lines[i]?.trim() || ''
358
+ // first h1 heading is the title
359
+ if (!title && trimmed.startsWith('# ')) {
360
+ title = trimmed.slice(2).trim()
361
+ continue
362
+ }
363
+ // first non-empty, non-heading line after title is description
364
+ if (title && trimmed && !trimmed.startsWith('#')) {
365
+ description = trimmed
366
+ break
367
+ }
368
+ }
369
+
370
+ return { title, description }
371
+ }
372
+
373
+ // convert doc name to skill-friendly name (lowercase, hyphens)
374
+ function toSkillName(name: string): string {
375
+ return name
376
+ .toLowerCase()
377
+ .replace(/[^a-z0-9-]/g, '-')
378
+ .replace(/-+/g, '-')
379
+ .replace(/^-|-$/g, '')
380
+ .slice(0, 64)
381
+ }
382
+
383
+ // collect all docs from both package and local directories
384
+ function collectAllDocs(
385
+ cwd: string
386
+ ): Array<{ name: string; path: string; source: 'package' | 'local' }> {
387
+ const docs: Array<{ name: string; path: string; source: 'package' | 'local' }> = []
388
+ const seen = new Set<string>()
389
+
390
+ // local docs override package docs
391
+ const localDocsDir = join(cwd, 'docs')
392
+ if (existsSync(localDocsDir)) {
393
+ const files = readdirSync(localDocsDir).filter((f) => f.endsWith('.md'))
394
+ for (const file of files) {
395
+ const name = file.replace(/\.md$/, '')
396
+ docs.push({ name, path: join(localDocsDir, file), source: 'local' })
397
+ seen.add(name)
398
+ }
399
+ }
400
+
401
+ // package docs (only if not overridden by local)
402
+ if (existsSync(DOCS_DIR)) {
403
+ const files = readdirSync(DOCS_DIR).filter((f) => f.endsWith('.md'))
404
+ for (const file of files) {
405
+ const name = file.replace(/\.md$/, '')
406
+ if (!seen.has(name)) {
407
+ docs.push({ name, path: join(DOCS_DIR, file), source: 'package' })
408
+ }
409
+ }
410
+ }
411
+
412
+ return docs
413
+ }
414
+
415
+ const SKILL_PREFIX = 'takeout-'
416
+
417
+ const skillsCommand = defineCommand({
418
+ meta: {
419
+ name: 'skills',
420
+ description: 'Generate Claude Code skills from documentation',
421
+ },
422
+ args: {
423
+ clean: {
424
+ type: 'boolean',
425
+ description: 'Remove existing skills before generating',
426
+ default: false,
427
+ },
428
+ },
429
+ async run({ args }) {
430
+ const cwd = process.cwd()
431
+ const skillsDir = join(cwd, '.claude', 'skills')
432
+
433
+ console.info()
434
+ console.info(pc.bold(pc.cyan('🧠 Generate Claude Code Skills')))
435
+ console.info()
436
+
437
+ // collect all docs
438
+ const docs = collectAllDocs(cwd)
439
+
440
+ if (docs.length === 0) {
441
+ console.info(pc.yellow('No documentation files found'))
442
+ return
443
+ }
444
+
445
+ console.info(pc.dim(`Found ${docs.length} documentation files`))
446
+ console.info()
447
+
448
+ // clean existing takeout skills if requested
449
+ if (args.clean && existsSync(skillsDir)) {
450
+ console.info(pc.yellow('Cleaning existing takeout skills...'))
451
+ const existing = readdirSync(skillsDir)
452
+ for (const dir of existing) {
453
+ if (dir.startsWith(SKILL_PREFIX)) {
454
+ rmSync(join(skillsDir, dir), { recursive: true })
455
+ }
456
+ }
457
+ }
458
+
459
+ // ensure skills directory exists
460
+ if (!existsSync(skillsDir)) {
461
+ mkdirSync(skillsDir, { recursive: true })
462
+ }
463
+
464
+ let symlinked = 0
465
+ let generated = 0
466
+ let unchanged = 0
467
+
468
+ for (const doc of docs) {
469
+ const baseName = toSkillName(doc.name)
470
+ const skillName = `${SKILL_PREFIX}${baseName}`
471
+ const skillDir = join(skillsDir, skillName)
472
+ const skillFile = join(skillDir, 'SKILL.md')
473
+
474
+ // read doc content
475
+ const content = readFileSync(doc.path, 'utf-8')
476
+ const hasFrontmatter = hasSkillFrontmatter(content)
477
+
478
+ // ensure skill directory exists
479
+ if (!existsSync(skillDir)) {
480
+ mkdirSync(skillDir, { recursive: true })
481
+ }
482
+
483
+ if (hasFrontmatter) {
484
+ // doc has frontmatter - symlink it directly
485
+ const relativePath = relative(skillDir, doc.path)
486
+
487
+ // check if symlink already exists and points to same target
488
+ if (existsSync(skillFile)) {
489
+ try {
490
+ const stat = lstatSync(skillFile)
491
+ if (stat.isSymbolicLink()) {
492
+ const existingTarget = readFileSync(skillFile, 'utf-8')
493
+ // symlink exists, check if content matches (via symlink)
494
+ if (existingTarget === content) {
495
+ unchanged++
496
+ continue
497
+ }
498
+ }
499
+ // remove existing file/symlink to recreate
500
+ unlinkSync(skillFile)
501
+ } catch {
502
+ // ignore errors, will recreate
503
+ }
504
+ }
505
+
506
+ symlinkSync(relativePath, skillFile)
507
+ symlinked++
508
+
509
+ const sourceLabel = doc.source === 'local' ? pc.blue('local') : pc.dim('package')
510
+ console.info(
511
+ ` ${pc.green('⟷')} ${skillName} ${sourceLabel} ${pc.dim('(symlink)')}`
512
+ )
513
+ } else {
514
+ // doc lacks frontmatter - generate skill file with frontmatter
515
+ const { title, description } = extractDocMeta(content)
516
+ const skillDescription = description
517
+ ? `${title}. ${description}`.slice(0, 1024)
518
+ : title.slice(0, 1024)
519
+
520
+ const skillContent = `---
521
+ name: ${skillName}
522
+ description: ${skillDescription}
523
+ ---
524
+
525
+ ${content}
526
+ `
527
+
528
+ // check if skill already exists and is identical
529
+ if (existsSync(skillFile)) {
530
+ try {
531
+ const stat = lstatSync(skillFile)
532
+ if (stat.isSymbolicLink()) {
533
+ unlinkSync(skillFile)
534
+ } else {
535
+ const existing = readFileSync(skillFile, 'utf-8')
536
+ if (existing === skillContent) {
537
+ unchanged++
538
+ continue
539
+ }
540
+ }
541
+ } catch {
542
+ // ignore errors
543
+ }
544
+ }
545
+
546
+ writeFileSync(skillFile, skillContent)
547
+ generated++
548
+
549
+ const sourceLabel = doc.source === 'local' ? pc.blue('local') : pc.dim('package')
550
+ console.info(
551
+ ` ${pc.green('āœ“')} ${skillName} ${sourceLabel} ${pc.dim('(generated)')}`
552
+ )
553
+ }
554
+ }
555
+
556
+ console.info()
557
+ console.info(pc.bold('Summary:'))
558
+ if (symlinked > 0) console.info(` ${pc.green(`${symlinked} symlinked`)}`)
559
+ if (generated > 0)
560
+ console.info(
561
+ ` ${pc.yellow(`${generated} generated`)} ${pc.dim('(add frontmatter to source to enable symlink)')}`
562
+ )
563
+ if (unchanged > 0) console.info(` ${pc.dim(`${unchanged} unchanged`)}`)
564
+ console.info()
565
+ console.info(pc.dim(`Skills in ${skillsDir}`))
566
+ console.info()
567
+ },
568
+ })
569
+
570
+ export const docsCommand = defineCommand({
571
+ meta: {
572
+ name: 'docs',
573
+ description: 'List and retrieve Takeout documentation',
574
+ },
575
+ subCommands: {
576
+ list: listCommand,
577
+ get: getCommand,
578
+ path: pathCommand,
579
+ eject: ejectCommand,
580
+ skills: skillsCommand,
581
+ },
582
+ })
@@ -0,0 +1,69 @@
1
+ import { defineCommand } from 'citty'
2
+ import pc from 'picocolors'
3
+
4
+ import { setupProductionEnv, listCategories } from '../utils/env-setup'
5
+
6
+ export const envSetupCommand = defineCommand({
7
+ meta: {
8
+ name: 'env:setup',
9
+ description: 'Configure production environment variables',
10
+ },
11
+ args: {
12
+ category: {
13
+ type: 'string',
14
+ description: 'Setup only a specific category (e.g., aws, apple, storage)',
15
+ alias: 'c',
16
+ },
17
+ file: {
18
+ type: 'string',
19
+ description: 'Environment file to update',
20
+ default: '.env.production',
21
+ alias: 'f',
22
+ },
23
+ list: {
24
+ type: 'boolean',
25
+ description: 'List all available categories',
26
+ alias: 'l',
27
+ default: false,
28
+ },
29
+ 'skip-optional': {
30
+ type: 'boolean',
31
+ description: 'Skip optional categories',
32
+ default: false,
33
+ },
34
+ },
35
+ async run({ args }) {
36
+ try {
37
+ const cwd = process.cwd()
38
+
39
+ // list categories if requested
40
+ if (args.list) {
41
+ listCategories()
42
+ return
43
+ }
44
+
45
+ console.info('')
46
+ console.info(pc.bold('šŸš€ Takeout Production Environment Setup'))
47
+ console.info(pc.gray('Configure your production deployment settings'))
48
+ console.info('')
49
+
50
+ const success = await setupProductionEnv(cwd, {
51
+ skipOptional: args['skip-optional'],
52
+ envFile: args.file,
53
+ onlyCategory: args.category,
54
+ interactive: true,
55
+ })
56
+
57
+ if (!success) {
58
+ console.info(pc.yellow('\n⚠ Environment setup was cancelled'))
59
+ console.info(pc.gray('You can resume anytime with:'))
60
+ console.info(pc.cyan(' bun takeout env:setup'))
61
+ process.exit(1)
62
+ }
63
+ } catch (error) {
64
+ console.error(pc.red('\nāœ– Environment setup failed:'))
65
+ console.error(error)
66
+ process.exit(1)
67
+ }
68
+ },
69
+ })