kaddidlehopper 0.4.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 (297) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/dist/cli.js +104 -207
  3. package/dist/index.js +1 -31
  4. package/dist/types/cli.d.ts +1 -8
  5. package/dist/types/types.d.ts +7 -12
  6. package/package.json +1 -2
  7. package/src/cli.ts +129 -270
  8. package/src/index.ts +1 -47
  9. package/src/types.ts +7 -13
  10. package/CONTEXT.md +0 -139
  11. package/add-ons/ai/README.md +0 -34
  12. package/add-ons/ai/assets/AGENTS.md.append +0 -24
  13. package/add-ons/ai/assets/_dot_env.local.append +0 -13
  14. package/add-ons/ai/assets/src/components/AIAssistant.tsx +0 -149
  15. package/add-ons/ai/assets/src/lib/ai-hook.ts +0 -21
  16. package/add-ons/ai/assets/src/lib/weather-tools.ts +0 -30
  17. package/add-ons/ai/assets/src/routes/api.chat.ts +0 -94
  18. package/add-ons/ai/assets/src/routes/chat.css +0 -175
  19. package/add-ons/ai/assets/src/routes/chat.tsx +0 -141
  20. package/add-ons/ai/info.json +0 -21
  21. package/add-ons/ai/package.json +0 -17
  22. package/add-ons/ai/small-logo.svg +0 -8
  23. package/add-ons/db/assets/AGENTS.md.append +0 -22
  24. package/add-ons/db/assets/DB-SETUP.md +0 -65
  25. package/add-ons/db/assets/_dot_env.local.append +0 -2
  26. package/add-ons/db/assets/drizzle.config.ts +0 -10
  27. package/add-ons/db/assets/src/db/index.ts +0 -8
  28. package/add-ons/db/assets/src/db/schema.ts +0 -8
  29. package/add-ons/db/assets/src/routes/db-example.tsx +0 -118
  30. package/add-ons/db/assets/src/server/guestbook.functions.ts +0 -23
  31. package/add-ons/db/info.json +0 -21
  32. package/add-ons/db/package.json +0 -10
  33. package/add-ons/forms/assets/AGENTS.md.append +0 -13
  34. package/add-ons/forms/assets/public/form-example.html +0 -14
  35. package/add-ons/forms/assets/src/routes/form-example.tsx +0 -76
  36. package/add-ons/forms/info.json +0 -17
  37. package/add-ons/forms/package.json +0 -3
  38. package/examples/ai-chat/README.md +0 -36
  39. package/examples/ai-chat/assets/AGENTS.md.append +0 -24
  40. package/examples/ai-chat/assets/src/lib/ai-hook.ts +0 -21
  41. package/examples/ai-chat/assets/src/lib/weather-tools.ts +0 -30
  42. package/examples/ai-chat/assets/src/routes/__root.tsx +0 -57
  43. package/examples/ai-chat/assets/src/routes/api.chat.ts +0 -94
  44. package/examples/ai-chat/assets/src/routes/index.tsx +0 -141
  45. package/examples/ai-chat/assets/src/styles.css +0 -165
  46. package/examples/ai-chat/info.json +0 -24
  47. package/examples/ai-chat/package.json +0 -14
  48. package/examples/blog/README.md +0 -60
  49. package/examples/blog/assets/AGENTS.md.append +0 -18
  50. package/examples/blog/assets/content/posts/beach.md +0 -12
  51. package/examples/blog/assets/content/posts/jungle.md.ejs +0 -12
  52. package/examples/blog/assets/content/posts/mountains.md.ejs +0 -12
  53. package/examples/blog/assets/content/posts/snorkeling.md.ejs +0 -12
  54. package/examples/blog/assets/content/posts/waterfall.md.ejs +0 -12
  55. package/examples/blog/assets/content-collections.ts +0 -30
  56. package/examples/blog/assets/public/beach.jpg +0 -0
  57. package/examples/blog/assets/public/jungle.jpg +0 -0
  58. package/examples/blog/assets/public/mountains.jpg +0 -0
  59. package/examples/blog/assets/public/snorkeling.jpg +0 -0
  60. package/examples/blog/assets/public/waterfall.jpg +0 -0
  61. package/examples/blog/assets/src/components/Header.tsx +0 -52
  62. package/examples/blog/assets/src/components/VacayAssistant.tsx +0 -205
  63. package/examples/blog/assets/src/components/blog-posts.tsx +0 -78
  64. package/examples/blog/assets/src/components/ui/card.tsx +0 -92
  65. package/examples/blog/assets/src/lib/blog-ai-hook.ts +0 -25
  66. package/examples/blog/assets/src/lib/blog-tools.ts +0 -111
  67. package/examples/blog/assets/src/lib/utils.ts +0 -6
  68. package/examples/blog/assets/src/routes/__root.tsx +0 -57
  69. package/examples/blog/assets/src/routes/api.blog-chat.ts +0 -117
  70. package/examples/blog/assets/src/routes/category.$category.tsx +0 -19
  71. package/examples/blog/assets/src/routes/index.tsx +0 -19
  72. package/examples/blog/assets/src/routes/posts.$slug.tsx +0 -63
  73. package/examples/blog/assets/src/styles.css +0 -138
  74. package/examples/blog/info.json +0 -39
  75. package/examples/blog/package.json +0 -23
  76. package/examples/calculator/README.md +0 -16
  77. package/examples/calculator/assets/AGENTS.md.append +0 -9
  78. package/examples/calculator/assets/src/components/Calculator.tsx +0 -238
  79. package/examples/calculator/assets/src/components/Header.tsx +0 -17
  80. package/examples/calculator/assets/src/routes/__root.tsx +0 -57
  81. package/examples/calculator/assets/src/routes/index.tsx +0 -14
  82. package/examples/calculator/info.json +0 -19
  83. package/examples/calculator/package.json +0 -1
  84. package/examples/dashboard/README.md +0 -17
  85. package/examples/dashboard/assets/AGENTS.md.append +0 -12
  86. package/examples/dashboard/assets/src/components/Header.tsx +0 -17
  87. package/examples/dashboard/assets/src/routes/__root.tsx +0 -57
  88. package/examples/dashboard/assets/src/routes/index.tsx +0 -158
  89. package/examples/dashboard/info.json +0 -18
  90. package/examples/dashboard/package.json +0 -6
  91. package/examples/ecommerce/README.md +0 -48
  92. package/examples/ecommerce/assets/AGENTS.md.append +0 -22
  93. package/examples/ecommerce/assets/public/logo.png +0 -0
  94. package/examples/ecommerce/assets/public/motorcycle-scooter.jpg +0 -0
  95. package/examples/ecommerce/assets/src/components/BuyButton.tsx +0 -35
  96. package/examples/ecommerce/assets/src/components/Header.tsx +0 -36
  97. package/examples/ecommerce/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  98. package/examples/ecommerce/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  99. package/examples/ecommerce/assets/src/data/motorcycles.ts +0 -27
  100. package/examples/ecommerce/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  101. package/examples/ecommerce/assets/src/lib/motorcycle-tools.ts +0 -42
  102. package/examples/ecommerce/assets/src/lib/stripe.server.ts +0 -39
  103. package/examples/ecommerce/assets/src/routes/__root.tsx +0 -57
  104. package/examples/ecommerce/assets/src/routes/api.motorcycle-chat.ts +0 -78
  105. package/examples/ecommerce/assets/src/routes/checkout/cancel.tsx +0 -25
  106. package/examples/ecommerce/assets/src/routes/checkout/success.tsx +0 -25
  107. package/examples/ecommerce/assets/src/routes/index.tsx +0 -76
  108. package/examples/ecommerce/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -55
  109. package/examples/ecommerce/assets/src/store/motorcycle-assistant.ts +0 -3
  110. package/examples/ecommerce/assets/src/styles.css +0 -212
  111. package/examples/ecommerce/info.json +0 -37
  112. package/examples/ecommerce/package.json +0 -13
  113. package/examples/events/README.md +0 -110
  114. package/examples/events/assets/AGENTS.md.append +0 -21
  115. package/examples/events/assets/content/speakers/andre-costa.md +0 -22
  116. package/examples/events/assets/content/speakers/hans-mueller.md.ejs +0 -22
  117. package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +0 -22
  118. package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +0 -22
  119. package/examples/events/assets/content/speakers/marie-dubois.md.ejs +0 -20
  120. package/examples/events/assets/content/speakers/priya-sharma.md.ejs +0 -22
  121. package/examples/events/assets/content/talks/croissant-lamination-secrets.md +0 -39
  122. package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +0 -39
  123. package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +0 -39
  124. package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +0 -39
  125. package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +0 -36
  126. package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +0 -32
  127. package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +0 -39
  128. package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +0 -39
  129. package/examples/events/assets/content-collections.ts +0 -56
  130. package/examples/events/assets/public/background-1.jpg +0 -0
  131. package/examples/events/assets/public/background-2.jpg +0 -0
  132. package/examples/events/assets/public/background-3.jpg +0 -0
  133. package/examples/events/assets/public/background-4.jpg +0 -0
  134. package/examples/events/assets/public/conference-logo.png +0 -0
  135. package/examples/events/assets/public/favicon.ico +0 -0
  136. package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
  137. package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
  138. package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
  139. package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
  140. package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
  141. package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
  142. package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
  143. package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
  144. package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
  145. package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
  146. package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
  147. package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
  148. package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
  149. package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
  150. package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
  151. package/examples/events/assets/public/tanstack-word-logo-white.svg +0 -1
  152. package/examples/events/assets/src/components/Header.tsx +0 -59
  153. package/examples/events/assets/src/components/HeaderNav.tsx +0 -67
  154. package/examples/events/assets/src/components/HeroCarousel.tsx +0 -61
  155. package/examples/events/assets/src/components/RemyAssistant.tsx +0 -207
  156. package/examples/events/assets/src/components/SpeakerCard.tsx +0 -67
  157. package/examples/events/assets/src/components/TalkCard.tsx +0 -77
  158. package/examples/events/assets/src/components/ui/card.tsx +0 -92
  159. package/examples/events/assets/src/lib/conference-ai-hook.ts +0 -26
  160. package/examples/events/assets/src/lib/conference-tools.ts +0 -210
  161. package/examples/events/assets/src/lib/model-selection.ts +0 -1
  162. package/examples/events/assets/src/lib/utils.ts +0 -6
  163. package/examples/events/assets/src/routes/__root.tsx +0 -70
  164. package/examples/events/assets/src/routes/api.remy-chat.ts +0 -119
  165. package/examples/events/assets/src/routes/index.tsx +0 -192
  166. package/examples/events/assets/src/routes/schedule.index.tsx +0 -274
  167. package/examples/events/assets/src/routes/speakers.$slug.tsx +0 -122
  168. package/examples/events/assets/src/routes/speakers.index.tsx +0 -40
  169. package/examples/events/assets/src/routes/talks.$slug.tsx +0 -116
  170. package/examples/events/assets/src/routes/talks.index.tsx +0 -40
  171. package/examples/events/assets/src/styles.css +0 -182
  172. package/examples/events/info.json +0 -60
  173. package/examples/events/package.json +0 -23
  174. package/examples/marketing/README.md +0 -60
  175. package/examples/marketing/assets/AGENTS.md.append +0 -15
  176. package/examples/marketing/assets/public/logo.png +0 -0
  177. package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
  178. package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
  179. package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
  180. package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
  181. package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
  182. package/examples/marketing/assets/src/components/Header.tsx +0 -36
  183. package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  184. package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  185. package/examples/marketing/assets/src/data/motorcycles.ts.ejs +0 -77
  186. package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  187. package/examples/marketing/assets/src/lib/motorcycle-tools.ts +0 -42
  188. package/examples/marketing/assets/src/routes/__root.tsx +0 -57
  189. package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +0 -78
  190. package/examples/marketing/assets/src/routes/index.tsx +0 -72
  191. package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -56
  192. package/examples/marketing/assets/src/store/motorcycle-assistant.ts +0 -3
  193. package/examples/marketing/assets/src/styles.css +0 -212
  194. package/examples/marketing/info.json +0 -32
  195. package/examples/marketing/package.json +0 -14
  196. package/examples/portfolio/README.md +0 -49
  197. package/examples/portfolio/assets/AGENTS.md.append +0 -21
  198. package/examples/portfolio/assets/content/blog/getting-started-with-tanstack.md +0 -53
  199. package/examples/portfolio/assets/content/blog/react-19-features.md +0 -78
  200. package/examples/portfolio/assets/content/blog/tailwind-css-v4-guide.md +0 -60
  201. package/examples/portfolio/assets/content/education/code-school.md +0 -17
  202. package/examples/portfolio/assets/content/jobs/initech-junior.md +0 -20
  203. package/examples/portfolio/assets/content/projects/portfolio-site.md +0 -15
  204. package/examples/portfolio/assets/content/projects/task-manager.md +0 -15
  205. package/examples/portfolio/assets/content-collections.ts +0 -65
  206. package/examples/portfolio/assets/public/contact.html +0 -6
  207. package/examples/portfolio/assets/public/headshot-on-white.jpg +0 -0
  208. package/examples/portfolio/assets/src/components/Header.tsx +0 -33
  209. package/examples/portfolio/assets/src/components/ResumeAssistant.tsx +0 -193
  210. package/examples/portfolio/assets/src/components/ui/badge.tsx +0 -46
  211. package/examples/portfolio/assets/src/components/ui/card.tsx +0 -92
  212. package/examples/portfolio/assets/src/components/ui/checkbox.tsx +0 -30
  213. package/examples/portfolio/assets/src/components/ui/hover-card.tsx +0 -44
  214. package/examples/portfolio/assets/src/components/ui/separator.tsx +0 -26
  215. package/examples/portfolio/assets/src/lib/resume-ai-hook.ts +0 -21
  216. package/examples/portfolio/assets/src/lib/resume-tools.ts +0 -165
  217. package/examples/portfolio/assets/src/lib/utils.ts +0 -6
  218. package/examples/portfolio/assets/src/routes/__root.tsx +0 -57
  219. package/examples/portfolio/assets/src/routes/api.resume-chat.ts +0 -116
  220. package/examples/portfolio/assets/src/routes/blog/$slug.tsx +0 -73
  221. package/examples/portfolio/assets/src/routes/contact.tsx +0 -121
  222. package/examples/portfolio/assets/src/routes/index.tsx +0 -55
  223. package/examples/portfolio/assets/src/routes/projects.tsx +0 -62
  224. package/examples/portfolio/assets/src/routes/resume.tsx +0 -220
  225. package/examples/portfolio/assets/src/styles.css +0 -138
  226. package/examples/portfolio/info.json +0 -49
  227. package/examples/portfolio/package.json +0 -26
  228. package/examples/resume/README.md +0 -82
  229. package/examples/resume/assets/AGENTS.md.append +0 -19
  230. package/examples/resume/assets/content/education/code-school.md +0 -17
  231. package/examples/resume/assets/content/jobs/freelance.md.ejs +0 -13
  232. package/examples/resume/assets/content/jobs/initech-junior.md +0 -20
  233. package/examples/resume/assets/content/jobs/initech-lead.md.ejs +0 -29
  234. package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +0 -28
  235. package/examples/resume/assets/content-collections.ts +0 -36
  236. package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
  237. package/examples/resume/assets/src/components/Header.tsx +0 -33
  238. package/examples/resume/assets/src/components/ResumeAssistant.tsx +0 -193
  239. package/examples/resume/assets/src/components/ui/badge.tsx +0 -46
  240. package/examples/resume/assets/src/components/ui/card.tsx +0 -92
  241. package/examples/resume/assets/src/components/ui/checkbox.tsx +0 -30
  242. package/examples/resume/assets/src/components/ui/hover-card.tsx +0 -44
  243. package/examples/resume/assets/src/components/ui/separator.tsx +0 -26
  244. package/examples/resume/assets/src/lib/resume-ai-hook.ts +0 -21
  245. package/examples/resume/assets/src/lib/resume-tools.ts +0 -165
  246. package/examples/resume/assets/src/lib/utils.ts +0 -6
  247. package/examples/resume/assets/src/routes/api.resume-chat.ts +0 -110
  248. package/examples/resume/assets/src/routes/index.tsx +0 -220
  249. package/examples/resume/assets/src/styles.css +0 -138
  250. package/examples/resume/info.json +0 -34
  251. package/examples/resume/package.json +0 -26
  252. package/examples/saas/README.md +0 -16
  253. package/examples/saas/assets/AGENTS.md.append +0 -9
  254. package/examples/saas/assets/src/components/Header.tsx +0 -17
  255. package/examples/saas/assets/src/routes/__root.tsx +0 -57
  256. package/examples/saas/assets/src/routes/faq.tsx +0 -94
  257. package/examples/saas/assets/src/routes/index.tsx +0 -197
  258. package/examples/saas/info.json +0 -22
  259. package/examples/saas/package.json +0 -1
  260. package/examples/survey/README.md +0 -20
  261. package/examples/survey/assets/AGENTS.md.append +0 -9
  262. package/examples/survey/assets/public/form-survey.html +0 -15
  263. package/examples/survey/assets/src/components/SurveyForm.tsx +0 -128
  264. package/examples/survey/assets/src/routes/index.tsx +0 -14
  265. package/examples/survey/info.json +0 -18
  266. package/examples/survey/package.json +0 -1
  267. package/project/base/AGENTS.md +0 -86
  268. package/project/base/_dot_claude/skills/content-collections/SKILL.md +0 -505
  269. package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +0 -410
  270. package/project/base/_dot_claude/skills/netlify-db/SKILL.md +0 -424
  271. package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +0 -419
  272. package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +0 -243
  273. package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +0 -372
  274. package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +0 -421
  275. package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +0 -426
  276. package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +0 -493
  277. package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +0 -430
  278. package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +0 -445
  279. package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +0 -494
  280. package/project/base/_dot_gitignore +0 -8
  281. package/project/base/netlify.toml +0 -7
  282. package/project/base/package.json +0 -33
  283. package/project/base/public/favicon.ico +0 -0
  284. package/project/base/public/tanstack-circle-logo.png +0 -0
  285. package/project/base/public/tanstack-word-logo-white.svg +0 -1
  286. package/project/base/src/components/Header.tsx +0 -17
  287. package/project/base/src/components/HeaderNav.tsx.ejs +0 -179
  288. package/project/base/src/router.tsx +0 -15
  289. package/project/base/src/routes/__root.tsx +0 -57
  290. package/project/base/src/routes/index.tsx +0 -48
  291. package/project/base/src/styles.css +0 -15
  292. package/project/base/tsconfig.json +0 -28
  293. package/project/base/vite.config.ts.ejs +0 -25
  294. package/project/info.json +0 -17
  295. package/project/packages.json +0 -22
  296. package/scripts/check-outdated-packages.js +0 -421
  297. package/src/agents-md.ts +0 -139
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(grep:*)"
5
+ ]
6
+ }
7
+ }
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,229 +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
- options: addon.options,
114
- }));
115
- console.log(JSON.stringify(serialized, null, 2));
116
- return;
117
- }
118
- // Handle --list-add-ons (text format)
119
- if (options.listAddOns) {
120
- const addOns = await getAllAddOns(framework, defaultMode);
121
- let hasConfigurableAddOns = false;
122
- for (const addOn of addOns.filter((a) => !forcedAddOns.includes(a.id))) {
123
- const hasOptions = addOn.options && Object.keys(addOn.options).length > 0;
124
- const optionMarker = hasOptions ? '*' : ' ';
125
- if (hasOptions)
126
- hasConfigurableAddOns = true;
127
- console.log(`${optionMarker} ${chalk.bold(addOn.id)}: ${addOn.description}`);
128
- }
129
- if (hasConfigurableAddOns) {
130
- console.log('\n* = has configuration options');
131
- }
132
- return;
133
- }
134
- // Handle --addon-details
135
- if (options.addonDetails) {
136
- const addOns = await getAllAddOns(framework, defaultMode);
137
- const addOn = addOns.find((a) => a.id === options.addonDetails);
138
- if (!addOn) {
139
- console.error(`Add-on '${options.addonDetails}' not found`);
140
- process.exit(1);
141
- }
142
- console.log(`${chalk.bold.cyan('Add-on Details:')} ${chalk.bold(addOn.name)}`);
143
- console.log(`${chalk.bold('ID:')} ${addOn.id}`);
144
- console.log(`${chalk.bold('Description:')} ${addOn.description}`);
145
- console.log(`${chalk.bold('Type:')} ${addOn.type}`);
146
- console.log(`${chalk.bold('Phase:')} ${addOn.phase}`);
147
- console.log(`${chalk.bold('Supported Modes:')} ${addOn.modes.join(', ')}`);
148
- if (addOn.dependsOn && addOn.dependsOn.length > 0) {
149
- console.log(`${chalk.bold('Dependencies:')} ${addOn.dependsOn.join(', ')}`);
150
- }
151
- if (addOn.options && Object.keys(addOn.options).length > 0) {
152
- console.log(`\n${chalk.bold.yellow('Configuration Options:')}`);
153
- for (const [optionName, option] of Object.entries(addOn.options)) {
154
- if (option && typeof option === 'object' && 'type' in option) {
155
- const opt = option;
156
- console.log(` ${chalk.bold(optionName)}:`);
157
- console.log(` Label: ${opt.label}`);
158
- if (opt.description) {
159
- console.log(` Description: ${opt.description}`);
160
- }
161
- console.log(` Type: ${opt.type}`);
162
- console.log(` Default: ${opt.default}`);
163
- if (opt.type === 'select' && opt.options) {
164
- console.log(` Available values:`);
165
- for (const choice of opt.options) {
166
- console.log(` - ${choice.value}: ${choice.label}`);
167
- }
168
- }
169
- }
170
- }
171
- }
172
- else {
173
- console.log(`\n${chalk.gray('No configuration options available')}`);
174
- }
175
- if (addOn.routes && addOn.routes.length > 0) {
176
- console.log(`\n${chalk.bold.green('Routes:')}`);
177
- for (const route of addOn.routes) {
178
- console.log(` ${chalk.bold(route.url)} (${route.name})`);
179
- console.log(` File: ${route.path}`);
180
- }
181
- }
182
- return;
183
- }
184
- // Handle project creation: target-dir defaults to current directory
185
- const targetDir = resolve(options.targetDir ?? '.');
186
- 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
187
68
  let resolvedProjectName;
188
- if (projectName) {
189
- resolvedProjectName =
190
- projectName === '.'
191
- ? sanitizePackageName(getCurrentDirectoryName())
192
- : sanitizePackageName(projectName);
193
- }
194
- else if (isCurrentDir) {
195
- resolvedProjectName = sanitizePackageName(getCurrentDirectoryName());
69
+ if (projectName && projectName !== '.') {
70
+ resolvedProjectName = sanitizePackageName(projectName);
196
71
  }
197
72
  else {
198
- console.error('Project name is required when --target-dir is not the current directory');
199
- program.help();
200
- return;
73
+ resolvedProjectName = sanitizePackageName(getCurrentDirectoryName());
201
74
  }
202
75
  const { valid, error } = validateProjectName(resolvedProjectName);
203
76
  if (!valid) {
204
- console.error(error);
77
+ console.error(chalk.red(error));
205
78
  process.exit(1);
206
79
  }
207
- // Determine selected add-ons
208
- const selectedAddOns = new Set([...forcedAddOns]);
209
- if (options.addOns && Array.isArray(options.addOns)) {
210
- for (const a of options.addOns) {
211
- 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);
212
86
  }
213
87
  }
214
- const chosenAddOns = await finalizeAddOns(framework, defaultMode, Array.from(selectedAddOns));
215
- const finalOptions = {
216
- projectName: resolvedProjectName,
217
- targetDir,
218
- framework,
219
- mode: defaultMode,
220
- typescript: true,
221
- tailwind: true,
222
- packageManager: options.packageManager ||
223
- getPackageManager() ||
224
- DEFAULT_PACKAGE_MANAGER,
225
- git: options.git !== false,
226
- install: options.install !== false,
227
- chosenAddOns,
228
- addOnOptions: {
229
- ...populateAddOnOptionsDefaults(chosenAddOns),
230
- project: { bareBones: options.bareBones ?? true },
231
- },
232
- };
233
- environment.intro(`Creating a new ${appName} app in ${resolvedProjectName}...`);
234
- await createApp(environment, finalOptions);
235
- // Delete files specified in bareBones.deleteFiles for each add-on when in bare-bones mode
236
- if (options.bareBones !== false) {
237
- for (const addOn of chosenAddOns) {
238
- const addOnWithBareBones = addOn;
239
- if (addOnWithBareBones.bareBones?.deleteFiles) {
240
- for (const file of addOnWithBareBones.bareBones.deleteFiles) {
241
- const filePath = join(targetDir, file);
242
- try {
243
- await unlink(filePath);
244
- }
245
- catch {
246
- // File may not exist, ignore errors
247
- }
248
- }
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');
249
110
  }
250
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`));
251
149
  }
252
- // Generate an AGENTS.md file in the root of the project
253
- await generateAgentsMd(targetDir, resolvedProjectName, chosenAddOns);
150
+ console.log(chalk.cyan(' pnpm dev'));
254
151
  });
255
152
  program.parse();
256
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.4.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"