kaddidlehopper 0.5.0 → 0.6.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 +104 -208
  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 +129 -271
  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/jherr/kdh-templates.git';
11
+ const MANIFEST_URL = 'https://raw.githubusercontent.com/jherr/kdh-templates/main/manifest.json';
9
12
  function sanitizePackageName(name) {
10
13
  return name
11
14
  .toLowerCase()
@@ -28,230 +31,123 @@ 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;
118
- }
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;
134
- }
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`);
141
- process.exit(1);
142
- }
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(', ')}`);
151
- }
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
- }
171
- }
172
- }
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}`);
181
- }
182
- }
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());
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
188
68
  let resolvedProjectName;
189
- if (projectName) {
190
- resolvedProjectName =
191
- projectName === '.'
192
- ? sanitizePackageName(getCurrentDirectoryName())
193
- : sanitizePackageName(projectName);
194
- }
195
- else if (isCurrentDir) {
196
- resolvedProjectName = sanitizePackageName(getCurrentDirectoryName());
69
+ if (projectName && projectName !== '.') {
70
+ resolvedProjectName = sanitizePackageName(projectName);
197
71
  }
198
72
  else {
199
- console.error('Project name is required when --target-dir is not the current directory');
200
- program.help();
201
- return;
73
+ resolvedProjectName = sanitizePackageName(getCurrentDirectoryName());
202
74
  }
203
75
  const { valid, error } = validateProjectName(resolvedProjectName);
204
76
  if (!valid) {
205
- console.error(error);
77
+ console.error(chalk.red(error));
206
78
  process.exit(1);
207
79
  }
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);
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.`));
85
+ process.exit(1);
213
86
  }
214
87
  }
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
- }
88
+ console.log(chalk.bold.cyan(`Creating a new Netlify TanStack Start app in ${chalk.white(targetDir)}...`));
89
+ console.log(chalk.gray(`Using starter: ${starterId}`));
90
+ // Sparse clone the template repo into a temp directory
91
+ const tmpDir = await mkdtemp(join(tmpdir(), 'netlify-cta-'));
92
+ try {
93
+ console.log(chalk.gray('⟳ Fetching template...'));
94
+ execSync(`git clone --depth=1 --filter=blob:none --sparse ${GITHUB_REPO} ${tmpDir}`, { stdio: 'pipe' });
95
+ execSync(`git -C ${tmpDir} sparse-checkout set starters/${starterId}`, { stdio: 'pipe' });
96
+ const starterPath = join(tmpDir, 'starters', starterId);
97
+ if (!existsSync(starterPath)) {
98
+ console.error(chalk.red(`Starter "${starterId}" not found in the template repo. Run --list-addons-json to see available starters.`));
99
+ process.exit(1);
100
+ }
101
+ // Copy starter files to target directory
102
+ await cp(starterPath, targetDir, { recursive: true });
103
+ // Update package.json name if a project name was provided
104
+ if (projectName && projectName !== '.') {
105
+ const pkgPath = join(targetDir, 'package.json');
106
+ if (existsSync(pkgPath)) {
107
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
108
+ pkg.name = resolvedProjectName;
109
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
250
110
  }
251
111
  }
112
+ console.log(chalk.green(`✓ Template copied`));
113
+ }
114
+ finally {
115
+ await rm(tmpDir, { recursive: true, force: true });
116
+ }
117
+ // Initialize git repository
118
+ if (options.git !== false) {
119
+ try {
120
+ execSync('git init', { cwd: targetDir, stdio: 'pipe' });
121
+ console.log(chalk.green('✓ Initialized git repository'));
122
+ }
123
+ catch {
124
+ console.warn(chalk.yellow('⚠ Could not initialize git repository'));
125
+ }
126
+ }
127
+ // Install dependencies
128
+ if (options.install !== false) {
129
+ const pm = options.packageManager ??
130
+ getPackageManagerFromUserAgent() ??
131
+ 'pnpm';
132
+ console.log(chalk.gray(`⟳ Installing dependencies with ${pm}...`));
133
+ try {
134
+ execSync(`${pm} install`, { cwd: targetDir, stdio: 'inherit' });
135
+ console.log(chalk.green('✓ Dependencies installed'));
136
+ }
137
+ catch {
138
+ console.error(chalk.red(`Failed to install dependencies. Run \`${pm} install\` manually.`));
139
+ }
140
+ }
141
+ console.log(chalk.bold.green('\nDone! Your project is ready.'));
142
+ console.log(chalk.white('\nNext steps:'));
143
+ console.log(chalk.cyan(` cd ${basename(targetDir)}`));
144
+ if (options.install === false) {
145
+ const pm = options.packageManager ??
146
+ getPackageManagerFromUserAgent() ??
147
+ 'pnpm';
148
+ console.log(chalk.cyan(` ${pm} install`));
252
149
  }
253
- // Generate an AGENTS.md file in the root of the project
254
- await generateAgentsMd(targetDir, resolvedProjectName, chosenAddOns);
150
+ console.log(chalk.cyan(' pnpm dev'));
255
151
  });
256
152
  program.parse();
257
153
  }
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.6.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"