kaddidlehopper 0.5.0 → 0.7.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 (296) hide show
  1. package/dist/cli.js +123 -206
  2. package/dist/index.js +1 -31
  3. package/dist/types/cli.d.ts +1 -8
  4. package/dist/types/types.d.ts +7 -12
  5. package/package.json +1 -2
  6. package/src/cli.ts +152 -269
  7. package/src/index.ts +1 -47
  8. package/src/types.ts +7 -13
  9. package/CONTEXT.md +0 -139
  10. package/add-ons/ai/README.md +0 -34
  11. package/add-ons/ai/assets/AGENTS.md.append +0 -24
  12. package/add-ons/ai/assets/_dot_env.local.append +0 -13
  13. package/add-ons/ai/assets/src/components/AIAssistant.tsx +0 -149
  14. package/add-ons/ai/assets/src/lib/ai-hook.ts +0 -21
  15. package/add-ons/ai/assets/src/lib/weather-tools.ts +0 -30
  16. package/add-ons/ai/assets/src/routes/api.chat.ts +0 -94
  17. package/add-ons/ai/assets/src/routes/chat.css +0 -175
  18. package/add-ons/ai/assets/src/routes/chat.tsx +0 -141
  19. package/add-ons/ai/info.json +0 -22
  20. package/add-ons/ai/package.json +0 -17
  21. package/add-ons/ai/small-logo.svg +0 -8
  22. package/add-ons/db/assets/AGENTS.md.append +0 -22
  23. package/add-ons/db/assets/DB-SETUP.md +0 -65
  24. package/add-ons/db/assets/_dot_env.local.append +0 -2
  25. package/add-ons/db/assets/drizzle.config.ts +0 -10
  26. package/add-ons/db/assets/src/db/index.ts +0 -8
  27. package/add-ons/db/assets/src/db/schema.ts +0 -8
  28. package/add-ons/db/assets/src/routes/db-example.tsx +0 -118
  29. package/add-ons/db/assets/src/server/guestbook.functions.ts +0 -23
  30. package/add-ons/db/info.json +0 -22
  31. package/add-ons/db/package.json +0 -10
  32. package/add-ons/forms/assets/AGENTS.md.append +0 -13
  33. package/add-ons/forms/assets/public/form-example.html +0 -14
  34. package/add-ons/forms/assets/src/routes/form-example.tsx +0 -76
  35. package/add-ons/forms/info.json +0 -18
  36. package/add-ons/forms/package.json +0 -3
  37. package/examples/ai-chat/README.md +0 -36
  38. package/examples/ai-chat/assets/AGENTS.md.append +0 -24
  39. package/examples/ai-chat/assets/src/lib/ai-hook.ts +0 -21
  40. package/examples/ai-chat/assets/src/lib/weather-tools.ts +0 -30
  41. package/examples/ai-chat/assets/src/routes/__root.tsx +0 -57
  42. package/examples/ai-chat/assets/src/routes/api.chat.ts +0 -94
  43. package/examples/ai-chat/assets/src/routes/index.tsx +0 -141
  44. package/examples/ai-chat/assets/src/styles.css +0 -165
  45. package/examples/ai-chat/info.json +0 -25
  46. package/examples/ai-chat/package.json +0 -14
  47. package/examples/blog/README.md +0 -60
  48. package/examples/blog/assets/AGENTS.md.append +0 -18
  49. package/examples/blog/assets/content/posts/beach.md +0 -12
  50. package/examples/blog/assets/content/posts/jungle.md.ejs +0 -12
  51. package/examples/blog/assets/content/posts/mountains.md.ejs +0 -12
  52. package/examples/blog/assets/content/posts/snorkeling.md.ejs +0 -12
  53. package/examples/blog/assets/content/posts/waterfall.md.ejs +0 -12
  54. package/examples/blog/assets/content-collections.ts +0 -30
  55. package/examples/blog/assets/public/beach.jpg +0 -0
  56. package/examples/blog/assets/public/jungle.jpg +0 -0
  57. package/examples/blog/assets/public/mountains.jpg +0 -0
  58. package/examples/blog/assets/public/snorkeling.jpg +0 -0
  59. package/examples/blog/assets/public/waterfall.jpg +0 -0
  60. package/examples/blog/assets/src/components/Header.tsx +0 -52
  61. package/examples/blog/assets/src/components/VacayAssistant.tsx +0 -205
  62. package/examples/blog/assets/src/components/blog-posts.tsx +0 -78
  63. package/examples/blog/assets/src/components/ui/card.tsx +0 -92
  64. package/examples/blog/assets/src/lib/blog-ai-hook.ts +0 -25
  65. package/examples/blog/assets/src/lib/blog-tools.ts +0 -111
  66. package/examples/blog/assets/src/lib/utils.ts +0 -6
  67. package/examples/blog/assets/src/routes/__root.tsx +0 -57
  68. package/examples/blog/assets/src/routes/api.blog-chat.ts +0 -117
  69. package/examples/blog/assets/src/routes/category.$category.tsx +0 -19
  70. package/examples/blog/assets/src/routes/index.tsx +0 -19
  71. package/examples/blog/assets/src/routes/posts.$slug.tsx +0 -63
  72. package/examples/blog/assets/src/styles.css +0 -138
  73. package/examples/blog/info.json +0 -40
  74. package/examples/blog/package.json +0 -23
  75. package/examples/calculator/README.md +0 -16
  76. package/examples/calculator/assets/AGENTS.md.append +0 -9
  77. package/examples/calculator/assets/src/components/Calculator.tsx +0 -238
  78. package/examples/calculator/assets/src/components/Header.tsx +0 -17
  79. package/examples/calculator/assets/src/routes/__root.tsx +0 -57
  80. package/examples/calculator/assets/src/routes/index.tsx +0 -14
  81. package/examples/calculator/info.json +0 -20
  82. package/examples/calculator/package.json +0 -1
  83. package/examples/dashboard/README.md +0 -17
  84. package/examples/dashboard/assets/AGENTS.md.append +0 -12
  85. package/examples/dashboard/assets/src/components/Header.tsx +0 -17
  86. package/examples/dashboard/assets/src/routes/__root.tsx +0 -57
  87. package/examples/dashboard/assets/src/routes/index.tsx +0 -158
  88. package/examples/dashboard/info.json +0 -19
  89. package/examples/dashboard/package.json +0 -6
  90. package/examples/ecommerce/README.md +0 -48
  91. package/examples/ecommerce/assets/AGENTS.md.append +0 -22
  92. package/examples/ecommerce/assets/public/logo.png +0 -0
  93. package/examples/ecommerce/assets/public/motorcycle-scooter.jpg +0 -0
  94. package/examples/ecommerce/assets/src/components/BuyButton.tsx +0 -35
  95. package/examples/ecommerce/assets/src/components/Header.tsx +0 -36
  96. package/examples/ecommerce/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  97. package/examples/ecommerce/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  98. package/examples/ecommerce/assets/src/data/motorcycles.ts +0 -27
  99. package/examples/ecommerce/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  100. package/examples/ecommerce/assets/src/lib/motorcycle-tools.ts +0 -42
  101. package/examples/ecommerce/assets/src/lib/stripe.server.ts +0 -39
  102. package/examples/ecommerce/assets/src/routes/__root.tsx +0 -57
  103. package/examples/ecommerce/assets/src/routes/api.motorcycle-chat.ts +0 -78
  104. package/examples/ecommerce/assets/src/routes/checkout/cancel.tsx +0 -25
  105. package/examples/ecommerce/assets/src/routes/checkout/success.tsx +0 -25
  106. package/examples/ecommerce/assets/src/routes/index.tsx +0 -76
  107. package/examples/ecommerce/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -55
  108. package/examples/ecommerce/assets/src/store/motorcycle-assistant.ts +0 -3
  109. package/examples/ecommerce/assets/src/styles.css +0 -212
  110. package/examples/ecommerce/info.json +0 -38
  111. package/examples/ecommerce/package.json +0 -13
  112. package/examples/events/README.md +0 -110
  113. package/examples/events/assets/AGENTS.md.append +0 -21
  114. package/examples/events/assets/content/speakers/andre-costa.md +0 -22
  115. package/examples/events/assets/content/speakers/hans-mueller.md.ejs +0 -22
  116. package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +0 -22
  117. package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +0 -22
  118. package/examples/events/assets/content/speakers/marie-dubois.md.ejs +0 -20
  119. package/examples/events/assets/content/speakers/priya-sharma.md.ejs +0 -22
  120. package/examples/events/assets/content/talks/croissant-lamination-secrets.md +0 -39
  121. package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +0 -39
  122. package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +0 -39
  123. package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +0 -39
  124. package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +0 -36
  125. package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +0 -32
  126. package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +0 -39
  127. package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +0 -39
  128. package/examples/events/assets/content-collections.ts +0 -56
  129. package/examples/events/assets/public/background-1.jpg +0 -0
  130. package/examples/events/assets/public/background-2.jpg +0 -0
  131. package/examples/events/assets/public/background-3.jpg +0 -0
  132. package/examples/events/assets/public/background-4.jpg +0 -0
  133. package/examples/events/assets/public/conference-logo.png +0 -0
  134. package/examples/events/assets/public/favicon.ico +0 -0
  135. package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
  136. package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
  137. package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
  138. package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
  139. package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
  140. package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
  141. package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
  142. package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
  143. package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
  144. package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
  145. package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
  146. package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
  147. package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
  148. package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
  149. package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
  150. package/examples/events/assets/public/tanstack-word-logo-white.svg +0 -1
  151. package/examples/events/assets/src/components/Header.tsx +0 -59
  152. package/examples/events/assets/src/components/HeaderNav.tsx +0 -67
  153. package/examples/events/assets/src/components/HeroCarousel.tsx +0 -61
  154. package/examples/events/assets/src/components/RemyAssistant.tsx +0 -207
  155. package/examples/events/assets/src/components/SpeakerCard.tsx +0 -67
  156. package/examples/events/assets/src/components/TalkCard.tsx +0 -77
  157. package/examples/events/assets/src/components/ui/card.tsx +0 -92
  158. package/examples/events/assets/src/lib/conference-ai-hook.ts +0 -26
  159. package/examples/events/assets/src/lib/conference-tools.ts +0 -210
  160. package/examples/events/assets/src/lib/model-selection.ts +0 -1
  161. package/examples/events/assets/src/lib/utils.ts +0 -6
  162. package/examples/events/assets/src/routes/__root.tsx +0 -70
  163. package/examples/events/assets/src/routes/api.remy-chat.ts +0 -119
  164. package/examples/events/assets/src/routes/index.tsx +0 -192
  165. package/examples/events/assets/src/routes/schedule.index.tsx +0 -274
  166. package/examples/events/assets/src/routes/speakers.$slug.tsx +0 -122
  167. package/examples/events/assets/src/routes/speakers.index.tsx +0 -40
  168. package/examples/events/assets/src/routes/talks.$slug.tsx +0 -116
  169. package/examples/events/assets/src/routes/talks.index.tsx +0 -40
  170. package/examples/events/assets/src/styles.css +0 -182
  171. package/examples/events/info.json +0 -61
  172. package/examples/events/package.json +0 -23
  173. package/examples/marketing/README.md +0 -60
  174. package/examples/marketing/assets/AGENTS.md.append +0 -15
  175. package/examples/marketing/assets/public/logo.png +0 -0
  176. package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
  177. package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
  178. package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
  179. package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
  180. package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
  181. package/examples/marketing/assets/src/components/Header.tsx +0 -36
  182. package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  183. package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  184. package/examples/marketing/assets/src/data/motorcycles.ts.ejs +0 -77
  185. package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  186. package/examples/marketing/assets/src/lib/motorcycle-tools.ts +0 -42
  187. package/examples/marketing/assets/src/routes/__root.tsx +0 -57
  188. package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +0 -78
  189. package/examples/marketing/assets/src/routes/index.tsx +0 -72
  190. package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -56
  191. package/examples/marketing/assets/src/store/motorcycle-assistant.ts +0 -3
  192. package/examples/marketing/assets/src/styles.css +0 -212
  193. package/examples/marketing/info.json +0 -33
  194. package/examples/marketing/package.json +0 -14
  195. package/examples/portfolio/README.md +0 -49
  196. package/examples/portfolio/assets/AGENTS.md.append +0 -21
  197. package/examples/portfolio/assets/content/blog/getting-started-with-tanstack.md +0 -53
  198. package/examples/portfolio/assets/content/blog/react-19-features.md +0 -78
  199. package/examples/portfolio/assets/content/blog/tailwind-css-v4-guide.md +0 -60
  200. package/examples/portfolio/assets/content/education/code-school.md +0 -17
  201. package/examples/portfolio/assets/content/jobs/initech-junior.md +0 -20
  202. package/examples/portfolio/assets/content/projects/portfolio-site.md +0 -15
  203. package/examples/portfolio/assets/content/projects/task-manager.md +0 -15
  204. package/examples/portfolio/assets/content-collections.ts +0 -65
  205. package/examples/portfolio/assets/public/contact.html +0 -6
  206. package/examples/portfolio/assets/public/headshot-on-white.jpg +0 -0
  207. package/examples/portfolio/assets/src/components/Header.tsx +0 -33
  208. package/examples/portfolio/assets/src/components/ResumeAssistant.tsx +0 -193
  209. package/examples/portfolio/assets/src/components/ui/badge.tsx +0 -46
  210. package/examples/portfolio/assets/src/components/ui/card.tsx +0 -92
  211. package/examples/portfolio/assets/src/components/ui/checkbox.tsx +0 -30
  212. package/examples/portfolio/assets/src/components/ui/hover-card.tsx +0 -44
  213. package/examples/portfolio/assets/src/components/ui/separator.tsx +0 -26
  214. package/examples/portfolio/assets/src/lib/resume-ai-hook.ts +0 -21
  215. package/examples/portfolio/assets/src/lib/resume-tools.ts +0 -165
  216. package/examples/portfolio/assets/src/lib/utils.ts +0 -6
  217. package/examples/portfolio/assets/src/routes/__root.tsx +0 -57
  218. package/examples/portfolio/assets/src/routes/api.resume-chat.ts +0 -116
  219. package/examples/portfolio/assets/src/routes/blog/$slug.tsx +0 -73
  220. package/examples/portfolio/assets/src/routes/contact.tsx +0 -121
  221. package/examples/portfolio/assets/src/routes/index.tsx +0 -55
  222. package/examples/portfolio/assets/src/routes/projects.tsx +0 -62
  223. package/examples/portfolio/assets/src/routes/resume.tsx +0 -220
  224. package/examples/portfolio/assets/src/styles.css +0 -138
  225. package/examples/portfolio/info.json +0 -50
  226. package/examples/portfolio/package.json +0 -26
  227. package/examples/resume/README.md +0 -82
  228. package/examples/resume/assets/AGENTS.md.append +0 -19
  229. package/examples/resume/assets/content/education/code-school.md +0 -17
  230. package/examples/resume/assets/content/jobs/freelance.md.ejs +0 -13
  231. package/examples/resume/assets/content/jobs/initech-junior.md +0 -20
  232. package/examples/resume/assets/content/jobs/initech-lead.md.ejs +0 -29
  233. package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +0 -28
  234. package/examples/resume/assets/content-collections.ts +0 -36
  235. package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
  236. package/examples/resume/assets/src/components/Header.tsx +0 -33
  237. package/examples/resume/assets/src/components/ResumeAssistant.tsx +0 -193
  238. package/examples/resume/assets/src/components/ui/badge.tsx +0 -46
  239. package/examples/resume/assets/src/components/ui/card.tsx +0 -92
  240. package/examples/resume/assets/src/components/ui/checkbox.tsx +0 -30
  241. package/examples/resume/assets/src/components/ui/hover-card.tsx +0 -44
  242. package/examples/resume/assets/src/components/ui/separator.tsx +0 -26
  243. package/examples/resume/assets/src/lib/resume-ai-hook.ts +0 -21
  244. package/examples/resume/assets/src/lib/resume-tools.ts +0 -165
  245. package/examples/resume/assets/src/lib/utils.ts +0 -6
  246. package/examples/resume/assets/src/routes/api.resume-chat.ts +0 -110
  247. package/examples/resume/assets/src/routes/index.tsx +0 -220
  248. package/examples/resume/assets/src/styles.css +0 -138
  249. package/examples/resume/info.json +0 -35
  250. package/examples/resume/package.json +0 -26
  251. package/examples/saas/README.md +0 -16
  252. package/examples/saas/assets/AGENTS.md.append +0 -9
  253. package/examples/saas/assets/src/components/Header.tsx +0 -17
  254. package/examples/saas/assets/src/routes/__root.tsx +0 -57
  255. package/examples/saas/assets/src/routes/faq.tsx +0 -94
  256. package/examples/saas/assets/src/routes/index.tsx +0 -197
  257. package/examples/saas/info.json +0 -23
  258. package/examples/saas/package.json +0 -1
  259. package/examples/survey/README.md +0 -20
  260. package/examples/survey/assets/AGENTS.md.append +0 -9
  261. package/examples/survey/assets/public/form-survey.html +0 -15
  262. package/examples/survey/assets/src/components/SurveyForm.tsx +0 -128
  263. package/examples/survey/assets/src/routes/index.tsx +0 -14
  264. package/examples/survey/info.json +0 -19
  265. package/examples/survey/package.json +0 -1
  266. package/project/base/AGENTS.md +0 -86
  267. package/project/base/_dot_claude/skills/content-collections/SKILL.md +0 -505
  268. package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +0 -410
  269. package/project/base/_dot_claude/skills/netlify-db/SKILL.md +0 -424
  270. package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +0 -419
  271. package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +0 -243
  272. package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +0 -372
  273. package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +0 -421
  274. package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +0 -426
  275. package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +0 -493
  276. package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +0 -430
  277. package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +0 -445
  278. package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +0 -494
  279. package/project/base/_dot_gitignore +0 -8
  280. package/project/base/netlify.toml +0 -7
  281. package/project/base/package.json +0 -33
  282. package/project/base/public/favicon.ico +0 -0
  283. package/project/base/public/tanstack-circle-logo.png +0 -0
  284. package/project/base/public/tanstack-word-logo-white.svg +0 -1
  285. package/project/base/src/components/Header.tsx +0 -17
  286. package/project/base/src/components/HeaderNav.tsx.ejs +0 -179
  287. package/project/base/src/router.tsx +0 -15
  288. package/project/base/src/routes/__root.tsx +0 -57
  289. package/project/base/src/routes/index.tsx +0 -48
  290. package/project/base/src/styles.css +0 -15
  291. package/project/base/tsconfig.json +0 -28
  292. package/project/base/vite.config.ts.ejs +0 -25
  293. package/project/info.json +0 -17
  294. package/project/packages.json +0 -22
  295. package/scripts/check-outdated-packages.js +0 -421
  296. package/src/agents-md.ts +0 -139
package/dist/cli.js CHANGED
@@ -1,11 +1,14 @@
1
- import { resolve, basename, join } from 'node:path';
2
- import { unlink } from 'node:fs/promises';
3
- import { Command, InvalidArgumentError } from 'commander';
1
+ import { resolve, basename } from 'node:path';
2
+ import { existsSync, readdirSync } from 'node:fs';
3
+ import { cp, rm, mkdtemp, readFile, writeFile } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { execSync } from 'node:child_process';
7
+ import { Command } from 'commander';
4
8
  import chalk from 'chalk';
5
9
  import validatePackageName from 'validate-npm-package-name';
6
- import { SUPPORTED_PACKAGE_MANAGERS, createApp, createDefaultEnvironment, finalizeAddOns, getAllAddOns, getFrameworkByName, getPackageManager, DEFAULT_PACKAGE_MANAGER, populateAddOnOptionsDefaults, } from '@tanstack/cta-engine';
7
- import { generateAgentsMd } from './agents-md.js';
8
- // Utility functions
10
+ const GITHUB_REPO = 'https://github.com/netlify/swar-templates.git';
11
+ const MANIFEST_URL = 'https://raw.githubusercontent.com/netlify/swar-templates/main/manifest.json';
9
12
  function sanitizePackageName(name) {
10
13
  return name
11
14
  .toLowerCase()
@@ -28,230 +31,144 @@ function validateProjectName(name) {
28
31
  'Project name does not meet npm package naming requirements',
29
32
  };
30
33
  }
31
- // Create environment with UI functions for console output
32
- function createEnvironment(appName) {
33
- const env = createDefaultEnvironment();
34
- env.appName = appName;
35
- env.intro = (message) => console.log(chalk.bold.cyan(message));
36
- env.outro = (message) => console.log(chalk.bold.green(message));
37
- env.info = (title, message) => {
38
- if (title && message) {
39
- console.log(chalk.blue(`${title}: ${message}`));
40
- }
41
- else if (title) {
42
- console.log(chalk.blue(title));
43
- }
44
- };
45
- env.error = (title, message) => {
46
- if (title && message) {
47
- console.error(chalk.red(`${title}: ${message}`));
48
- }
49
- else if (title) {
50
- console.error(chalk.red(title));
51
- }
52
- };
53
- env.warn = (title, message) => {
54
- if (title && message) {
55
- console.warn(chalk.yellow(`${title}: ${message}`));
56
- }
57
- else if (title) {
58
- console.warn(chalk.yellow(title));
59
- }
60
- };
61
- env.spinner = () => ({
62
- start: (message) => console.log(chalk.gray(`⟳ ${message}`)),
63
- stop: (message) => console.log(chalk.green(`✓ ${message}`)),
64
- });
65
- return env;
34
+ function getPackageManagerFromUserAgent() {
35
+ const userAgent = process.env.npm_config_user_agent;
36
+ if (!userAgent)
37
+ return undefined;
38
+ const pmSpec = userAgent.split(' ')[0];
39
+ const separatorPos = pmSpec.lastIndexOf('/');
40
+ return separatorPos !== -1 ? pmSpec.substring(0, separatorPos) : pmSpec;
66
41
  }
67
- export function cli({ name, appName, defaultFramework, forcedMode, forcedAddOns = [], }) {
68
- const environment = createEnvironment(appName);
42
+ export function cli() {
69
43
  const program = new Command();
70
- const defaultMode = forcedMode || 'file-router';
71
- program.name(name).description(`CLI to create a new ${appName} application`);
44
+ program
45
+ .name('netlify-cta')
46
+ .description('CLI to create a new Netlify TanStack Start application');
72
47
  program.argument('[project-name]', 'name of the project');
73
48
  program
74
49
  .option('--no-install', 'skip installing dependencies')
75
- .option(`--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`, `Explicitly tell the CLI to use this package manager`, (value) => {
76
- if (!SUPPORTED_PACKAGE_MANAGERS.includes(value)) {
77
- throw new InvalidArgumentError(`Invalid package manager: ${value}. The following are allowed: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`);
78
- }
79
- return value;
80
- })
81
- .option('--add-ons [...add-ons]', 'pick from a list of available add-ons (comma separated list)', (value) => {
82
- let addOns = !!value;
83
- if (typeof value === 'string') {
84
- addOns = value.split(',').map((addon) => addon.trim());
85
- }
86
- return addOns;
87
- })
88
- .option('--list-add-ons', 'list all available add-ons', false)
89
- .option('--list-addons-json', 'list all available add-ons as JSON', false)
90
- .option('--addon-details <addon-id>', 'show detailed information about a specific add-on')
50
+ .option('--package-manager <pm>', 'explicitly tell the CLI to use this package manager')
51
+ .option('--add-ons [id]', 'starter ID to use from the template repo')
52
+ .option('--list-addons-json', 'list all available starters as JSON', false)
91
53
  .option('--no-git', 'do not create a git repository')
92
- .option('--target-dir <path>', 'the target directory for the application root', '.')
93
- .option('-f, --force', 'force project creation even if the target directory is not empty', true)
94
- .option('--no-force', 'refuse to create if the target directory is not empty')
95
- .option('--bare-bones', 'create minimal scaffolding for LLM modification', true)
96
- .option('--no-bare-bones', 'create full scaffolding (not minimal)');
54
+ .option('--target-dir <path>', 'the target directory for the application root')
55
+ .option('-f, --force', 'force project creation even if the target directory is not empty');
97
56
  program.action(async (projectName, options) => {
98
- const framework = getFrameworkByName(defaultFramework);
99
- if (!framework) {
100
- console.error(`Framework '${defaultFramework}' not found`);
101
- process.exit(1);
102
- }
103
57
  // Handle --list-addons-json
104
58
  if (options.listAddonsJson) {
105
- const addOns = await getAllAddOns(framework, defaultMode);
106
- const serialized = addOns
107
- .filter((a) => !forcedAddOns.includes(a.id))
108
- .map((addon) => ({
109
- id: addon.id,
110
- name: addon.name,
111
- description: addon.description,
112
- type: addon.type,
113
- features: addon.features ?? [],
114
- options: addon.options,
115
- }));
116
- console.log(JSON.stringify(serialized, null, 2));
117
- return;
59
+ const manifest = await fetch(MANIFEST_URL).then((r) => r.json());
60
+ console.log(JSON.stringify(manifest.starters, null, 2));
61
+ process.exit(0);
62
+ }
63
+ // Resolve starter ID
64
+ const starterId = typeof options.addOns === 'string' ? options.addOns : 'basic';
65
+ // Resolve target directory — default to CWD
66
+ const targetDir = options.targetDir ? resolve(options.targetDir) : resolve(process.cwd());
67
+ // Resolve project name for package.json
68
+ let resolvedProjectName;
69
+ if (projectName && projectName !== '.') {
70
+ resolvedProjectName = sanitizePackageName(projectName);
118
71
  }
119
- // Handle --list-add-ons (text format)
120
- if (options.listAddOns) {
121
- const addOns = await getAllAddOns(framework, defaultMode);
122
- let hasConfigurableAddOns = false;
123
- for (const addOn of addOns.filter((a) => !forcedAddOns.includes(a.id))) {
124
- const hasOptions = addOn.options && Object.keys(addOn.options).length > 0;
125
- const optionMarker = hasOptions ? '*' : ' ';
126
- if (hasOptions)
127
- hasConfigurableAddOns = true;
128
- console.log(`${optionMarker} ${chalk.bold(addOn.id)}: ${addOn.description}`);
129
- }
130
- if (hasConfigurableAddOns) {
131
- console.log('\n* = has configuration options');
132
- }
133
- return;
72
+ else {
73
+ resolvedProjectName = sanitizePackageName(getCurrentDirectoryName());
134
74
  }
135
- // Handle --addon-details
136
- if (options.addonDetails) {
137
- const addOns = await getAllAddOns(framework, defaultMode);
138
- const addOn = addOns.find((a) => a.id === options.addonDetails);
139
- if (!addOn) {
140
- console.error(`Add-on '${options.addonDetails}' not found`);
75
+ const { valid, error } = validateProjectName(resolvedProjectName);
76
+ if (!valid) {
77
+ console.error(chalk.red(error));
78
+ process.exit(1);
79
+ }
80
+ // Check if target directory exists and is non-empty
81
+ if (existsSync(targetDir)) {
82
+ const contents = readdirSync(targetDir);
83
+ if (contents.length > 0 && !options.force) {
84
+ console.error(chalk.red(`Target directory "${targetDir}" is not empty. Use --force to overwrite.`));
141
85
  process.exit(1);
142
86
  }
143
- console.log(`${chalk.bold.cyan('Add-on Details:')} ${chalk.bold(addOn.name)}`);
144
- console.log(`${chalk.bold('ID:')} ${addOn.id}`);
145
- console.log(`${chalk.bold('Description:')} ${addOn.description}`);
146
- console.log(`${chalk.bold('Type:')} ${addOn.type}`);
147
- console.log(`${chalk.bold('Phase:')} ${addOn.phase}`);
148
- console.log(`${chalk.bold('Supported Modes:')} ${addOn.modes.join(', ')}`);
149
- if (addOn.dependsOn && addOn.dependsOn.length > 0) {
150
- console.log(`${chalk.bold('Dependencies:')} ${addOn.dependsOn.join(', ')}`);
87
+ }
88
+ // Delete index.html if it exists in the target directory
89
+ const indexHtmlPath = join(targetDir, 'index.html');
90
+ if (existsSync(indexHtmlPath)) {
91
+ await rm(indexHtmlPath);
92
+ }
93
+ console.log(chalk.bold.cyan(`Creating a new Netlify TanStack Start app in ${chalk.white(targetDir)}...`));
94
+ console.log(chalk.gray(`Using starter: ${starterId}`));
95
+ const manifest = await fetch(MANIFEST_URL).then((r) => r.json());
96
+ const starterEntry = manifest.starters.find((s) => s.id === starterId);
97
+ const frameworkId = starterEntry?.framework;
98
+ // Sparse clone the template repo into a temp directory
99
+ const tmpDir = await mkdtemp(join(tmpdir(), 'netlify-cta-'));
100
+ try {
101
+ console.log(chalk.gray('⟳ Fetching template...'));
102
+ const sparsePaths = [`starters/${starterId}`, ...(frameworkId ? [`frameworks/${frameworkId}`] : [])];
103
+ execSync(`git clone --depth=1 --filter=blob:none --sparse ${GITHUB_REPO} ${tmpDir}`, { stdio: 'pipe' });
104
+ execSync(`git -C ${tmpDir} sparse-checkout set ${sparsePaths.join(' ')}`, { stdio: 'pipe' });
105
+ const starterPath = join(tmpDir, 'starters', starterId);
106
+ if (!existsSync(starterPath)) {
107
+ console.error(chalk.red(`Starter "${starterId}" not found in the template repo. Run --list-addons-json to see available starters.`));
108
+ process.exit(1);
151
109
  }
152
- if (addOn.options && Object.keys(addOn.options).length > 0) {
153
- console.log(`\n${chalk.bold.yellow('Configuration Options:')}`);
154
- for (const [optionName, option] of Object.entries(addOn.options)) {
155
- if (option && typeof option === 'object' && 'type' in option) {
156
- const opt = option;
157
- console.log(` ${chalk.bold(optionName)}:`);
158
- console.log(` Label: ${opt.label}`);
159
- if (opt.description) {
160
- console.log(` Description: ${opt.description}`);
161
- }
162
- console.log(` Type: ${opt.type}`);
163
- console.log(` Default: ${opt.default}`);
164
- if (opt.type === 'select' && opt.options) {
165
- console.log(` Available values:`);
166
- for (const choice of opt.options) {
167
- console.log(` - ${choice.value}: ${choice.label}`);
168
- }
169
- }
170
- }
110
+ // Copy starter files to target directory
111
+ await cp(starterPath, targetDir, { recursive: true });
112
+ // Copy framework overlay files if they exist
113
+ if (frameworkId) {
114
+ const frameworkPath = join(tmpDir, 'frameworks', frameworkId);
115
+ if (existsSync(frameworkPath)) {
116
+ console.log(chalk.gray(`⟳ Applying framework overlay (${frameworkId})...`));
117
+ await cp(frameworkPath, targetDir, { recursive: true });
118
+ console.log(chalk.green(`✓ Framework overlay applied (${frameworkId})`));
119
+ }
120
+ else {
121
+ console.log(chalk.yellow(`⚠ Framework overlay "${frameworkId}" not found in repo, skipping`));
171
122
  }
172
123
  }
173
- else {
174
- console.log(`\n${chalk.gray('No configuration options available')}`);
175
- }
176
- if (addOn.routes && addOn.routes.length > 0) {
177
- console.log(`\n${chalk.bold.green('Routes:')}`);
178
- for (const route of addOn.routes) {
179
- console.log(` ${chalk.bold(route.url)} (${route.name})`);
180
- console.log(` File: ${route.path}`);
124
+ // Update package.json name if a project name was provided
125
+ if (projectName && projectName !== '.') {
126
+ const pkgPath = join(targetDir, 'package.json');
127
+ if (existsSync(pkgPath)) {
128
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
129
+ pkg.name = resolvedProjectName;
130
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
181
131
  }
182
132
  }
183
- return;
184
- }
185
- // Handle project creation: target-dir defaults to current directory
186
- const targetDir = resolve(options.targetDir ?? '.');
187
- const isCurrentDir = targetDir === resolve(process.cwd());
188
- let resolvedProjectName;
189
- if (projectName) {
190
- resolvedProjectName =
191
- projectName === '.'
192
- ? sanitizePackageName(getCurrentDirectoryName())
193
- : sanitizePackageName(projectName);
194
- }
195
- else if (isCurrentDir) {
196
- resolvedProjectName = sanitizePackageName(getCurrentDirectoryName());
197
- }
198
- else {
199
- console.error('Project name is required when --target-dir is not the current directory');
200
- program.help();
201
- return;
133
+ console.log(chalk.green(`✓ Template copied`));
202
134
  }
203
- const { valid, error } = validateProjectName(resolvedProjectName);
204
- if (!valid) {
205
- console.error(error);
206
- process.exit(1);
135
+ finally {
136
+ await rm(tmpDir, { recursive: true, force: true });
207
137
  }
208
- // Determine selected add-ons
209
- const selectedAddOns = new Set([...forcedAddOns]);
210
- if (options.addOns && Array.isArray(options.addOns)) {
211
- for (const a of options.addOns) {
212
- selectedAddOns.add(a);
138
+ // Initialize git repository
139
+ if (options.git !== false) {
140
+ try {
141
+ execSync('git init', { cwd: targetDir, stdio: 'pipe' });
142
+ console.log(chalk.green('✓ Initialized git repository'));
143
+ }
144
+ catch {
145
+ console.warn(chalk.yellow('⚠ Could not initialize git repository'));
213
146
  }
214
147
  }
215
- const chosenAddOns = await finalizeAddOns(framework, defaultMode, Array.from(selectedAddOns));
216
- const finalOptions = {
217
- projectName: resolvedProjectName,
218
- targetDir,
219
- framework,
220
- mode: defaultMode,
221
- typescript: true,
222
- tailwind: true,
223
- packageManager: options.packageManager ||
224
- getPackageManager() ||
225
- DEFAULT_PACKAGE_MANAGER,
226
- git: options.git !== false,
227
- install: options.install !== false,
228
- chosenAddOns,
229
- addOnOptions: {
230
- ...populateAddOnOptionsDefaults(chosenAddOns),
231
- project: { bareBones: options.bareBones ?? true },
232
- },
233
- };
234
- environment.intro(`Creating a new ${appName} app in ${resolvedProjectName}...`);
235
- await createApp(environment, finalOptions);
236
- // Delete files specified in bareBones.deleteFiles for each add-on when in bare-bones mode
237
- if (options.bareBones !== false) {
238
- for (const addOn of chosenAddOns) {
239
- const addOnWithBareBones = addOn;
240
- if (addOnWithBareBones.bareBones?.deleteFiles) {
241
- for (const file of addOnWithBareBones.bareBones.deleteFiles) {
242
- const filePath = join(targetDir, file);
243
- try {
244
- await unlink(filePath);
245
- }
246
- catch {
247
- // File may not exist, ignore errors
248
- }
249
- }
250
- }
148
+ // Install dependencies
149
+ if (options.install !== false) {
150
+ const pm = options.packageManager ??
151
+ getPackageManagerFromUserAgent() ??
152
+ 'pnpm';
153
+ console.log(chalk.gray(`⟳ Installing dependencies with ${pm}...`));
154
+ try {
155
+ execSync(`${pm} install`, { cwd: targetDir, stdio: 'inherit' });
156
+ console.log(chalk.green('✓ Dependencies installed'));
251
157
  }
158
+ catch {
159
+ console.error(chalk.red(`Failed to install dependencies. Run \`${pm} install\` manually.`));
160
+ }
161
+ }
162
+ console.log(chalk.bold.green('\nDone! Your project is ready.'));
163
+ console.log(chalk.white('\nNext steps:'));
164
+ console.log(chalk.cyan(` cd ${basename(targetDir)}`));
165
+ if (options.install === false) {
166
+ const pm = options.packageManager ??
167
+ getPackageManagerFromUserAgent() ??
168
+ 'pnpm';
169
+ console.log(chalk.cyan(` ${pm} install`));
252
170
  }
253
- // Generate an AGENTS.md file in the root of the project
254
- await generateAgentsMd(targetDir, resolvedProjectName, chosenAddOns);
171
+ console.log(chalk.cyan(' pnpm dev'));
255
172
  });
256
173
  program.parse();
257
174
  }
package/dist/index.js CHANGED
@@ -1,33 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { dirname, join } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { registerFramework, scanAddOnDirectories, scanProjectDirectory, } from '@tanstack/cta-engine';
5
2
  import { cli } from './cli.js';
6
- const projectDirectory = join(dirname(dirname(fileURLToPath(import.meta.url))), 'project');
7
- const addOns = scanAddOnDirectories([
8
- join(dirname(dirname(fileURLToPath(import.meta.url))), 'add-ons'),
9
- join(dirname(dirname(fileURLToPath(import.meta.url))), 'examples'),
10
- ]);
11
- const { files, basePackageJSON, optionalPackages } = scanProjectDirectory(projectDirectory, join(dirname(dirname(fileURLToPath(import.meta.url))), 'project', 'base'));
12
- registerFramework({
13
- id: 'netlify-start',
14
- name: 'Netlify TanStack Start',
15
- description: 'TanStack Start applications for Netlify deployment',
16
- version: '0.1.0',
17
- base: files,
18
- addOns,
19
- basePackageJSON,
20
- optionalPackages,
21
- supportedModes: {
22
- 'file-router': {
23
- displayName: 'File Router',
24
- description: 'File-based routing with TanStack Start',
25
- forceTypescript: true,
26
- },
27
- },
28
- });
29
- cli({
30
- name: 'netlify-cta',
31
- appName: 'Netlify TanStack Start',
32
- defaultFramework: 'Netlify TanStack Start',
33
- });
3
+ cli();
@@ -1,8 +1 @@
1
- export interface CliConfig {
2
- name: string;
3
- appName: string;
4
- defaultFramework: string;
5
- forcedMode?: string;
6
- forcedAddOns?: Array<string>;
7
- }
8
- export declare function cli({ name, appName, defaultFramework, forcedMode, forcedAddOns, }: CliConfig): void;
1
+ export declare function cli(): void;
@@ -1,14 +1,9 @@
1
- import type { PackageManager } from '@tanstack/cta-engine';
2
1
  export interface CliOptions {
3
- projectName?: string;
4
- packageManager?: PackageManager;
5
- git?: boolean;
6
- addOns?: Array<string> | boolean;
7
- listAddOns?: boolean;
8
- listAddonsJson?: boolean;
9
- addonDetails?: string;
10
- targetDir?: string;
11
- install?: boolean;
12
- force?: boolean;
13
- bareBones?: boolean;
2
+ install: boolean;
3
+ packageManager: string | undefined;
4
+ addOns: string | boolean | undefined;
5
+ listAddonsJson: boolean;
6
+ git: boolean;
7
+ force: boolean | undefined;
8
+ targetDir: string | undefined;
14
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaddidlehopper",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Create TanStack Start applications for Netlify",
5
5
  "bin": "./dist/index.js",
6
6
  "type": "module",
@@ -18,7 +18,6 @@
18
18
  "author": "Jack Herrington <jherr@pobox.com>",
19
19
  "license": "MIT",
20
20
  "dependencies": {
21
- "@tanstack/cta-engine": "^0.48.0",
22
21
  "chalk": "^5.4.1",
23
22
  "commander": "^13.1.0",
24
23
  "validate-npm-package-name": "^7.0.0"