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.
- package/dist/cli.js +104 -208
- package/dist/index.js +1 -31
- package/dist/types/cli.d.ts +1 -8
- package/dist/types/types.d.ts +7 -12
- package/package.json +1 -2
- package/src/cli.ts +129 -271
- package/src/index.ts +1 -47
- package/src/types.ts +7 -13
- package/CONTEXT.md +0 -139
- package/add-ons/ai/README.md +0 -34
- package/add-ons/ai/assets/AGENTS.md.append +0 -24
- package/add-ons/ai/assets/_dot_env.local.append +0 -13
- package/add-ons/ai/assets/src/components/AIAssistant.tsx +0 -149
- package/add-ons/ai/assets/src/lib/ai-hook.ts +0 -21
- package/add-ons/ai/assets/src/lib/weather-tools.ts +0 -30
- package/add-ons/ai/assets/src/routes/api.chat.ts +0 -94
- package/add-ons/ai/assets/src/routes/chat.css +0 -175
- package/add-ons/ai/assets/src/routes/chat.tsx +0 -141
- package/add-ons/ai/info.json +0 -22
- package/add-ons/ai/package.json +0 -17
- package/add-ons/ai/small-logo.svg +0 -8
- package/add-ons/db/assets/AGENTS.md.append +0 -22
- package/add-ons/db/assets/DB-SETUP.md +0 -65
- package/add-ons/db/assets/_dot_env.local.append +0 -2
- package/add-ons/db/assets/drizzle.config.ts +0 -10
- package/add-ons/db/assets/src/db/index.ts +0 -8
- package/add-ons/db/assets/src/db/schema.ts +0 -8
- package/add-ons/db/assets/src/routes/db-example.tsx +0 -118
- package/add-ons/db/assets/src/server/guestbook.functions.ts +0 -23
- package/add-ons/db/info.json +0 -22
- package/add-ons/db/package.json +0 -10
- package/add-ons/forms/assets/AGENTS.md.append +0 -13
- package/add-ons/forms/assets/public/form-example.html +0 -14
- package/add-ons/forms/assets/src/routes/form-example.tsx +0 -76
- package/add-ons/forms/info.json +0 -18
- package/add-ons/forms/package.json +0 -3
- package/examples/ai-chat/README.md +0 -36
- package/examples/ai-chat/assets/AGENTS.md.append +0 -24
- package/examples/ai-chat/assets/src/lib/ai-hook.ts +0 -21
- package/examples/ai-chat/assets/src/lib/weather-tools.ts +0 -30
- package/examples/ai-chat/assets/src/routes/__root.tsx +0 -57
- package/examples/ai-chat/assets/src/routes/api.chat.ts +0 -94
- package/examples/ai-chat/assets/src/routes/index.tsx +0 -141
- package/examples/ai-chat/assets/src/styles.css +0 -165
- package/examples/ai-chat/info.json +0 -25
- package/examples/ai-chat/package.json +0 -14
- package/examples/blog/README.md +0 -60
- package/examples/blog/assets/AGENTS.md.append +0 -18
- package/examples/blog/assets/content/posts/beach.md +0 -12
- package/examples/blog/assets/content/posts/jungle.md.ejs +0 -12
- package/examples/blog/assets/content/posts/mountains.md.ejs +0 -12
- package/examples/blog/assets/content/posts/snorkeling.md.ejs +0 -12
- package/examples/blog/assets/content/posts/waterfall.md.ejs +0 -12
- package/examples/blog/assets/content-collections.ts +0 -30
- package/examples/blog/assets/public/beach.jpg +0 -0
- package/examples/blog/assets/public/jungle.jpg +0 -0
- package/examples/blog/assets/public/mountains.jpg +0 -0
- package/examples/blog/assets/public/snorkeling.jpg +0 -0
- package/examples/blog/assets/public/waterfall.jpg +0 -0
- package/examples/blog/assets/src/components/Header.tsx +0 -52
- package/examples/blog/assets/src/components/VacayAssistant.tsx +0 -205
- package/examples/blog/assets/src/components/blog-posts.tsx +0 -78
- package/examples/blog/assets/src/components/ui/card.tsx +0 -92
- package/examples/blog/assets/src/lib/blog-ai-hook.ts +0 -25
- package/examples/blog/assets/src/lib/blog-tools.ts +0 -111
- package/examples/blog/assets/src/lib/utils.ts +0 -6
- package/examples/blog/assets/src/routes/__root.tsx +0 -57
- package/examples/blog/assets/src/routes/api.blog-chat.ts +0 -117
- package/examples/blog/assets/src/routes/category.$category.tsx +0 -19
- package/examples/blog/assets/src/routes/index.tsx +0 -19
- package/examples/blog/assets/src/routes/posts.$slug.tsx +0 -63
- package/examples/blog/assets/src/styles.css +0 -138
- package/examples/blog/info.json +0 -40
- package/examples/blog/package.json +0 -23
- package/examples/calculator/README.md +0 -16
- package/examples/calculator/assets/AGENTS.md.append +0 -9
- package/examples/calculator/assets/src/components/Calculator.tsx +0 -238
- package/examples/calculator/assets/src/components/Header.tsx +0 -17
- package/examples/calculator/assets/src/routes/__root.tsx +0 -57
- package/examples/calculator/assets/src/routes/index.tsx +0 -14
- package/examples/calculator/info.json +0 -20
- package/examples/calculator/package.json +0 -1
- package/examples/dashboard/README.md +0 -17
- package/examples/dashboard/assets/AGENTS.md.append +0 -12
- package/examples/dashboard/assets/src/components/Header.tsx +0 -17
- package/examples/dashboard/assets/src/routes/__root.tsx +0 -57
- package/examples/dashboard/assets/src/routes/index.tsx +0 -158
- package/examples/dashboard/info.json +0 -19
- package/examples/dashboard/package.json +0 -6
- package/examples/ecommerce/README.md +0 -48
- package/examples/ecommerce/assets/AGENTS.md.append +0 -22
- package/examples/ecommerce/assets/public/logo.png +0 -0
- package/examples/ecommerce/assets/public/motorcycle-scooter.jpg +0 -0
- package/examples/ecommerce/assets/src/components/BuyButton.tsx +0 -35
- package/examples/ecommerce/assets/src/components/Header.tsx +0 -36
- package/examples/ecommerce/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
- package/examples/ecommerce/assets/src/components/MotorcycleRecommendation.tsx +0 -53
- package/examples/ecommerce/assets/src/data/motorcycles.ts +0 -27
- package/examples/ecommerce/assets/src/lib/motorcycle-ai-hook.ts +0 -24
- package/examples/ecommerce/assets/src/lib/motorcycle-tools.ts +0 -42
- package/examples/ecommerce/assets/src/lib/stripe.server.ts +0 -39
- package/examples/ecommerce/assets/src/routes/__root.tsx +0 -57
- package/examples/ecommerce/assets/src/routes/api.motorcycle-chat.ts +0 -78
- package/examples/ecommerce/assets/src/routes/checkout/cancel.tsx +0 -25
- package/examples/ecommerce/assets/src/routes/checkout/success.tsx +0 -25
- package/examples/ecommerce/assets/src/routes/index.tsx +0 -76
- package/examples/ecommerce/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -55
- package/examples/ecommerce/assets/src/store/motorcycle-assistant.ts +0 -3
- package/examples/ecommerce/assets/src/styles.css +0 -212
- package/examples/ecommerce/info.json +0 -38
- package/examples/ecommerce/package.json +0 -13
- package/examples/events/README.md +0 -110
- package/examples/events/assets/AGENTS.md.append +0 -21
- package/examples/events/assets/content/speakers/andre-costa.md +0 -22
- package/examples/events/assets/content/speakers/hans-mueller.md.ejs +0 -22
- package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +0 -22
- package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +0 -22
- package/examples/events/assets/content/speakers/marie-dubois.md.ejs +0 -20
- package/examples/events/assets/content/speakers/priya-sharma.md.ejs +0 -22
- package/examples/events/assets/content/talks/croissant-lamination-secrets.md +0 -39
- package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +0 -39
- package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +0 -39
- package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +0 -39
- package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +0 -36
- package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +0 -32
- package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +0 -39
- package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +0 -39
- package/examples/events/assets/content-collections.ts +0 -56
- package/examples/events/assets/public/background-1.jpg +0 -0
- package/examples/events/assets/public/background-2.jpg +0 -0
- package/examples/events/assets/public/background-3.jpg +0 -0
- package/examples/events/assets/public/background-4.jpg +0 -0
- package/examples/events/assets/public/conference-logo.png +0 -0
- package/examples/events/assets/public/favicon.ico +0 -0
- package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
- package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
- package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
- package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
- package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
- package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
- package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
- package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
- package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
- package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
- package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
- package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
- package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
- package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
- package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
- package/examples/events/assets/public/tanstack-word-logo-white.svg +0 -1
- package/examples/events/assets/src/components/Header.tsx +0 -59
- package/examples/events/assets/src/components/HeaderNav.tsx +0 -67
- package/examples/events/assets/src/components/HeroCarousel.tsx +0 -61
- package/examples/events/assets/src/components/RemyAssistant.tsx +0 -207
- package/examples/events/assets/src/components/SpeakerCard.tsx +0 -67
- package/examples/events/assets/src/components/TalkCard.tsx +0 -77
- package/examples/events/assets/src/components/ui/card.tsx +0 -92
- package/examples/events/assets/src/lib/conference-ai-hook.ts +0 -26
- package/examples/events/assets/src/lib/conference-tools.ts +0 -210
- package/examples/events/assets/src/lib/model-selection.ts +0 -1
- package/examples/events/assets/src/lib/utils.ts +0 -6
- package/examples/events/assets/src/routes/__root.tsx +0 -70
- package/examples/events/assets/src/routes/api.remy-chat.ts +0 -119
- package/examples/events/assets/src/routes/index.tsx +0 -192
- package/examples/events/assets/src/routes/schedule.index.tsx +0 -274
- package/examples/events/assets/src/routes/speakers.$slug.tsx +0 -122
- package/examples/events/assets/src/routes/speakers.index.tsx +0 -40
- package/examples/events/assets/src/routes/talks.$slug.tsx +0 -116
- package/examples/events/assets/src/routes/talks.index.tsx +0 -40
- package/examples/events/assets/src/styles.css +0 -182
- package/examples/events/info.json +0 -61
- package/examples/events/package.json +0 -23
- package/examples/marketing/README.md +0 -60
- package/examples/marketing/assets/AGENTS.md.append +0 -15
- package/examples/marketing/assets/public/logo.png +0 -0
- package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
- package/examples/marketing/assets/src/components/Header.tsx +0 -36
- package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
- package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +0 -53
- package/examples/marketing/assets/src/data/motorcycles.ts.ejs +0 -77
- package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +0 -24
- package/examples/marketing/assets/src/lib/motorcycle-tools.ts +0 -42
- package/examples/marketing/assets/src/routes/__root.tsx +0 -57
- package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +0 -78
- package/examples/marketing/assets/src/routes/index.tsx +0 -72
- package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -56
- package/examples/marketing/assets/src/store/motorcycle-assistant.ts +0 -3
- package/examples/marketing/assets/src/styles.css +0 -212
- package/examples/marketing/info.json +0 -33
- package/examples/marketing/package.json +0 -14
- package/examples/portfolio/README.md +0 -49
- package/examples/portfolio/assets/AGENTS.md.append +0 -21
- package/examples/portfolio/assets/content/blog/getting-started-with-tanstack.md +0 -53
- package/examples/portfolio/assets/content/blog/react-19-features.md +0 -78
- package/examples/portfolio/assets/content/blog/tailwind-css-v4-guide.md +0 -60
- package/examples/portfolio/assets/content/education/code-school.md +0 -17
- package/examples/portfolio/assets/content/jobs/initech-junior.md +0 -20
- package/examples/portfolio/assets/content/projects/portfolio-site.md +0 -15
- package/examples/portfolio/assets/content/projects/task-manager.md +0 -15
- package/examples/portfolio/assets/content-collections.ts +0 -65
- package/examples/portfolio/assets/public/contact.html +0 -6
- package/examples/portfolio/assets/public/headshot-on-white.jpg +0 -0
- package/examples/portfolio/assets/src/components/Header.tsx +0 -33
- package/examples/portfolio/assets/src/components/ResumeAssistant.tsx +0 -193
- package/examples/portfolio/assets/src/components/ui/badge.tsx +0 -46
- package/examples/portfolio/assets/src/components/ui/card.tsx +0 -92
- package/examples/portfolio/assets/src/components/ui/checkbox.tsx +0 -30
- package/examples/portfolio/assets/src/components/ui/hover-card.tsx +0 -44
- package/examples/portfolio/assets/src/components/ui/separator.tsx +0 -26
- package/examples/portfolio/assets/src/lib/resume-ai-hook.ts +0 -21
- package/examples/portfolio/assets/src/lib/resume-tools.ts +0 -165
- package/examples/portfolio/assets/src/lib/utils.ts +0 -6
- package/examples/portfolio/assets/src/routes/__root.tsx +0 -57
- package/examples/portfolio/assets/src/routes/api.resume-chat.ts +0 -116
- package/examples/portfolio/assets/src/routes/blog/$slug.tsx +0 -73
- package/examples/portfolio/assets/src/routes/contact.tsx +0 -121
- package/examples/portfolio/assets/src/routes/index.tsx +0 -55
- package/examples/portfolio/assets/src/routes/projects.tsx +0 -62
- package/examples/portfolio/assets/src/routes/resume.tsx +0 -220
- package/examples/portfolio/assets/src/styles.css +0 -138
- package/examples/portfolio/info.json +0 -50
- package/examples/portfolio/package.json +0 -26
- package/examples/resume/README.md +0 -82
- package/examples/resume/assets/AGENTS.md.append +0 -19
- package/examples/resume/assets/content/education/code-school.md +0 -17
- package/examples/resume/assets/content/jobs/freelance.md.ejs +0 -13
- package/examples/resume/assets/content/jobs/initech-junior.md +0 -20
- package/examples/resume/assets/content/jobs/initech-lead.md.ejs +0 -29
- package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +0 -28
- package/examples/resume/assets/content-collections.ts +0 -36
- package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
- package/examples/resume/assets/src/components/Header.tsx +0 -33
- package/examples/resume/assets/src/components/ResumeAssistant.tsx +0 -193
- package/examples/resume/assets/src/components/ui/badge.tsx +0 -46
- package/examples/resume/assets/src/components/ui/card.tsx +0 -92
- package/examples/resume/assets/src/components/ui/checkbox.tsx +0 -30
- package/examples/resume/assets/src/components/ui/hover-card.tsx +0 -44
- package/examples/resume/assets/src/components/ui/separator.tsx +0 -26
- package/examples/resume/assets/src/lib/resume-ai-hook.ts +0 -21
- package/examples/resume/assets/src/lib/resume-tools.ts +0 -165
- package/examples/resume/assets/src/lib/utils.ts +0 -6
- package/examples/resume/assets/src/routes/api.resume-chat.ts +0 -110
- package/examples/resume/assets/src/routes/index.tsx +0 -220
- package/examples/resume/assets/src/styles.css +0 -138
- package/examples/resume/info.json +0 -35
- package/examples/resume/package.json +0 -26
- package/examples/saas/README.md +0 -16
- package/examples/saas/assets/AGENTS.md.append +0 -9
- package/examples/saas/assets/src/components/Header.tsx +0 -17
- package/examples/saas/assets/src/routes/__root.tsx +0 -57
- package/examples/saas/assets/src/routes/faq.tsx +0 -94
- package/examples/saas/assets/src/routes/index.tsx +0 -197
- package/examples/saas/info.json +0 -23
- package/examples/saas/package.json +0 -1
- package/examples/survey/README.md +0 -20
- package/examples/survey/assets/AGENTS.md.append +0 -9
- package/examples/survey/assets/public/form-survey.html +0 -15
- package/examples/survey/assets/src/components/SurveyForm.tsx +0 -128
- package/examples/survey/assets/src/routes/index.tsx +0 -14
- package/examples/survey/info.json +0 -19
- package/examples/survey/package.json +0 -1
- package/project/base/AGENTS.md +0 -86
- package/project/base/_dot_claude/skills/content-collections/SKILL.md +0 -505
- package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +0 -410
- package/project/base/_dot_claude/skills/netlify-db/SKILL.md +0 -424
- package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +0 -419
- package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +0 -243
- package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +0 -372
- package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +0 -421
- package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +0 -426
- package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +0 -493
- package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +0 -430
- package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +0 -445
- package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +0 -494
- package/project/base/_dot_gitignore +0 -8
- package/project/base/netlify.toml +0 -7
- package/project/base/package.json +0 -33
- package/project/base/public/favicon.ico +0 -0
- package/project/base/public/tanstack-circle-logo.png +0 -0
- package/project/base/public/tanstack-word-logo-white.svg +0 -1
- package/project/base/src/components/Header.tsx +0 -17
- package/project/base/src/components/HeaderNav.tsx.ejs +0 -179
- package/project/base/src/router.tsx +0 -15
- package/project/base/src/routes/__root.tsx +0 -57
- package/project/base/src/routes/index.tsx +0 -48
- package/project/base/src/styles.css +0 -15
- package/project/base/tsconfig.json +0 -28
- package/project/base/vite.config.ts.ejs +0 -25
- package/project/info.json +0 -17
- package/project/packages.json +0 -22
- package/scripts/check-outdated-packages.js +0 -421
- package/src/agents-md.ts +0 -139
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { resolve, basename
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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(
|
|
68
|
-
const environment = createEnvironment(appName);
|
|
42
|
+
export function cli() {
|
|
69
43
|
const program = new Command();
|
|
70
|
-
|
|
71
|
-
|
|
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(
|
|
76
|
-
|
|
77
|
-
|
|
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'
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -1,8 +1 @@
|
|
|
1
|
-
export
|
|
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;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import type { PackageManager } from '@tanstack/cta-engine';
|
|
2
1
|
export interface CliOptions {
|
|
3
|
-
|
|
4
|
-
packageManager
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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.
|
|
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"
|