tackl 1.0.10 → 2.0.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 (3) hide show
  1. package/README.md +15 -61
  2. package/bin/tackl.js +234 -169
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -1,75 +1,29 @@
1
1
  # Tackl CLI
2
2
 
3
- The ultimate Next.js starter kit with Waffl Grid system.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install -g tackl
9
- ```
3
+ Scaffolds [Tackl](https://github.com/12-studio/tackl) — the animation-first Next.js starter kit — **into the current directory**. Made for the "empty git repo" workflow: create your repo, clone it, run one command.
10
4
 
11
5
  ## Usage
12
6
 
13
7
  ```bash
14
- tackl my-app
8
+ mkdir my-app && cd my-app # or clone your empty repo and cd in
9
+ bunx tackl # or: bun create tackl / npm create tackl@latest
15
10
  ```
16
11
 
17
- ### Options
12
+ You'll be asked which CMS to use — **DatoCMS**, **Sanity**, or **None** — and the scaffold is pruned to match: only the chosen adapter, its dependencies, its docs, and its `.env.example` block survive.
18
13
 
19
- - `--ref <branch>` - Template ref (branch or tag) (default: main)
20
- - `--no-install` - Skip dependency installation
21
- - `--no-git` - Skip git initialization
22
- - `--help` - Show help message
14
+ ## What it does
23
15
 
24
- ### Examples
16
+ 1. Verifies the directory is empty (an existing `.git`, `.gitignore`, `README.md` or `LICENSE` is fine — your repo's `.git` and remote are kept)
17
+ 2. Downloads the latest tagged release of the template (falls back to `main` if none)
18
+ 3. Prunes repo-only cargo (the CLI itself, `.github`) and the unused CMS adapter
19
+ 4. Sets your package name, initialises git if needed, and runs `bun install`
25
20
 
26
- ```bash
27
- # Create a new project
28
- tackl my-app
21
+ ## Options
29
22
 
30
- # Create with specific branch
31
- tackl my-app --ref develop
32
-
33
- # Skip installation and git
34
- tackl my-app --no-install --no-git
35
23
  ```
36
-
37
- ## What's Included
38
-
39
- - ⚡ **Next.js 15** with App Router
40
- - 🎨 **Waffl Grid System** - Responsive grid with smart breakpoint inheritance
41
- - 🎭 **Styled Components** - CSS-in-JS with TypeScript
42
- - 🎬 **GSAP** - Professional animations
43
- - 📱 **Responsive Design** - Mobile-first approach
44
- - 🔧 **TypeScript** - Full type safety
45
- - 🎯 **Performance** - Optimized for speed
46
- - 📦 **Modern Tooling** - ESLint, Prettier, Husky
47
-
48
- ## Features
49
-
50
- ### Waffl Grid System
51
-
52
- - Smart breakpoint inheritance (only specify what changes!)
53
- - Responsive grid columns
54
- - Semantic components
55
- - CSS custom properties
56
-
57
- ### Example Usage
58
-
59
- ```jsx
60
- <Waffl>
61
- <Section $s='1/-1' $l='3/9'>
62
- // Mobile: full width, Desktop: 3/9 // Tablet inherits mobile (full
63
- width) Content
64
- </Section>
65
- </Waffl>
24
+ --cms <dato|sanity|none> Skip the CMS prompt
25
+ --name <name> Package name (default: directory name)
26
+ --ref <tag|branch> Template ref (default: latest release)
27
+ --no-install Skip dependency installation
28
+ --no-git Skip git init (an existing .git is always kept)
66
29
  ```
67
-
68
- ## Repository
69
-
70
- - **GitHub**: https://github.com/12-studio/tackl
71
- - **Issues**: https://github.com/12-studio/tackl/issues
72
-
73
- ## License
74
-
75
- MIT
package/bin/tackl.js CHANGED
@@ -6,14 +6,10 @@ import path from 'path';
6
6
  import pc from 'picocolors';
7
7
  import prompts from 'prompts';
8
8
  import * as tar from 'tar';
9
- import { fileURLToPath } from 'url';
10
-
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = path.dirname(__filename);
13
9
 
14
10
  const OWNER = '12-studio';
15
11
  const REPO = 'tackl';
16
- const DEFAULT_REF = 'main';
12
+ const FALLBACK_REF = 'main';
17
13
 
18
14
  const log = {
19
15
  info: m => console.log(pc.cyan('•'), m),
@@ -21,11 +17,10 @@ const log = {
21
17
  warn: m => console.log(pc.yellow('▲'), m),
22
18
  error: m => console.log(pc.red('✖'), m),
23
19
  step: m => console.log(pc.blue('→'), m),
24
- success: m => console.log(pc.green('🎉'), m),
25
20
  };
26
21
 
27
22
  const showBanner = () => {
28
- const purple = str => `\x1b[38;5;99m${str}\x1b[0m`; // Deep vibrant purple
23
+ const purple = str => `\x1b[38;5;99m${str}\x1b[0m`;
29
24
  console.log(
30
25
  pc.bold(
31
26
  purple(`
@@ -40,221 +35,294 @@ const showBanner = () => {
40
35
  ║ ║
41
36
  ║ The Ultimate Next.js Starter Kit ║
42
37
  ║ ║
43
- ╚═════════════════════════════════════════════════╝
38
+ ╚═════════════════════════════════════════════════╝
44
39
  `)
45
40
  )
46
41
  );
47
42
  };
48
43
 
44
+ // SECTION • Directory checks
45
+ // NOTE • The CLI scaffolds INTO the current directory — run it inside an
46
+ // empty (or freshly-initialised) git repo. These files are allowed to
47
+ // already exist without counting as "not empty".
48
+ const ALLOWED_EXISTING = new Set(['.git', '.gitignore', 'README.md', 'LICENSE', '.DS_Store']);
49
+
50
+ const dirIsScaffoldable = dir => {
51
+ if (!fs.existsSync(dir)) return true;
52
+ return fs.readdirSync(dir).every(entry => ALLOWED_EXISTING.has(entry));
53
+ };
54
+
55
+ // SECTION • Template ref resolution
56
+ // NOTE • Prefer the latest GitHub release so every scaffold is a tested,
57
+ // reproducible snapshot; fall back to main (with a warning) when no
58
+ // release exists. --ref overrides both.
59
+ const resolveRef = async explicitRef => {
60
+ if (explicitRef) return { ref: explicitRef, source: 'flag' };
61
+ try {
62
+ const res = await fetch(`https://api.github.com/repos/${OWNER}/${REPO}/releases/latest`, {
63
+ headers: { accept: 'application/vnd.github+json' },
64
+ });
65
+ if (res.ok) {
66
+ const json = await res.json();
67
+ if (json?.tag_name) return { ref: json.tag_name, source: 'release' };
68
+ }
69
+ } catch {}
70
+ return { ref: FALLBACK_REF, source: 'fallback' };
71
+ };
72
+
73
+ // SECTION • Pruning
74
+ const rmrf = (root, rel) => {
75
+ const p = path.join(root, rel);
76
+ if (fs.existsSync(p)) fs.rmSync(p, { recursive: true, force: true });
77
+ };
78
+
79
+ const stripEnvBlock = (root, marker) => {
80
+ const p = path.join(root, '.env.example');
81
+ if (!fs.existsSync(p)) return;
82
+ const lines = fs.readFileSync(p, 'utf8').split('\n');
83
+ const out = [];
84
+ let skipping = false;
85
+ for (const line of lines) {
86
+ if (line.startsWith(marker)) {
87
+ skipping = true;
88
+ continue;
89
+ }
90
+ if (skipping && line.trim() === '') {
91
+ skipping = false;
92
+ continue;
93
+ }
94
+ if (!skipping) out.push(line);
95
+ }
96
+ fs.writeFileSync(p, out.join('\n'));
97
+ };
98
+
99
+ const stripDeps = (root, deps) => {
100
+ const p = path.join(root, 'package.json');
101
+ if (!fs.existsSync(p)) return;
102
+ const json = JSON.parse(fs.readFileSync(p, 'utf8'));
103
+ for (const d of deps) {
104
+ delete json.dependencies?.[d];
105
+ delete json.devDependencies?.[d];
106
+ }
107
+ fs.writeFileSync(p, JSON.stringify(json, null, '\t') + '\n');
108
+ };
109
+
110
+ const CMS = {
111
+ dato: {
112
+ title: 'DatoCMS',
113
+ prune: root => {
114
+ rmrf(root, 'src/cms/sanity');
115
+ rmrf(root, 'docs/Sanity');
116
+ stripDeps(root, ['next-sanity']);
117
+ stripEnvBlock(root, '# CMS — Sanity');
118
+ },
119
+ },
120
+ sanity: {
121
+ title: 'Sanity',
122
+ prune: root => {
123
+ rmrf(root, 'src/cms/dato');
124
+ rmrf(root, 'docs/DatoCMS');
125
+ stripDeps(root, ['@datocms/cda-client', 'react-datocms', 'graphql', 'graphql-tag']);
126
+ stripEnvBlock(root, '# CMS — DatoCMS');
127
+ // Rewire the adapter entry to the kept adapter
128
+ const entry = path.join(root, 'src/cms/index.ts');
129
+ if (fs.existsSync(entry)) {
130
+ fs.writeFileSync(entry, fs.readFileSync(entry, 'utf8').replace(`'./dato'`, `'./sanity'`));
131
+ }
132
+ },
133
+ },
134
+ none: {
135
+ title: 'No CMS',
136
+ prune: root => {
137
+ rmrf(root, 'src/cms');
138
+ rmrf(root, 'docs/Sanity');
139
+ rmrf(root, 'docs/DatoCMS');
140
+ rmrf(root, 'docs/CMS.md');
141
+ stripDeps(root, ['next-sanity', '@datocms/cda-client', 'react-datocms', 'graphql', 'graphql-tag']);
142
+ stripEnvBlock(root, '# CMS — Sanity');
143
+ stripEnvBlock(root, '# CMS — DatoCMS');
144
+ },
145
+ },
146
+ };
147
+
148
+ // NOTE • Repo-only cargo that should never land in a scaffolded project
149
+ const REPO_CARGO = ['tackl', '.github', 'bun.lock'];
150
+
49
151
  async function main() {
50
152
  const args = process.argv.slice(2);
51
153
 
52
- // Handle help flag
53
154
  if (args.includes('--help') || args.includes('-h')) {
54
155
  console.log(`
55
156
  ${pc.bold('Tackl CLI')} - The ultimate Next.js starter kit
56
157
 
57
158
  ${pc.bold('Usage:')}
58
- tackl [project-name] [options]
59
-
60
- ${pc.bold('Options:')}
61
- --no-install Skip dependency installation (default: install)
62
- --no-git Skip git initialization (default: init)
63
- -h, --help Show this help message
159
+ Run inside an empty directory (or a freshly-initialised git repo):
64
160
 
65
- ${pc.bold('Examples:')}
66
- tackl my-app # Creates project with npm install + git init
67
- tackl my-app --no-install # Creates project with git init only
68
- tackl my-app --no-git # Creates project with npm install only
69
- tackl my-app --no-install --no-git # Creates project only
161
+ mkdir my-app && cd my-app
162
+ bunx tackl
70
163
 
71
- ${pc.bold('Repository:')} https://github.com/12-studio/tackl
164
+ ${pc.bold('Options:')}
165
+ --cms <dato|sanity|none> Skip the CMS prompt
166
+ --name <name> Package name (default: the directory name)
167
+ --ref <tag|branch> Template ref (default: latest release)
168
+ --no-install Skip dependency installation
169
+ --no-git Skip git init (existing .git is always kept)
170
+ -h, --help Show this help message
171
+
172
+ ${pc.bold('Repository:')} https://github.com/${OWNER}/${REPO}
72
173
  `);
73
174
  process.exit(0);
74
175
  }
75
176
 
76
- const argName = args.find(a => !a.startsWith('-'));
77
-
78
- const initial = {
79
- name: argName,
80
- ref: DEFAULT_REF, // Always use main branch
81
- install: !args.includes('--no-install'), // Default: true (install)
82
- git: !args.includes('--no-git'), // Default: true (init git)
177
+ const flagValue = name => {
178
+ const i = args.indexOf(name);
179
+ return i !== -1 ? args[i + 1] : undefined;
83
180
  };
84
181
 
85
- // Filter out null prompts
86
- const promptQuestions = [
87
- {
88
- type: initial.name ? null : 'text',
89
- name: 'name',
90
- message: 'Project directory',
91
- initial: 'my-tackl-app',
92
- validate: v =>
93
- /^[a-z0-9\-._]+$/.test(v)
94
- ? true
95
- : 'Use lowercase letters, numbers, - . _',
96
- },
97
- ].filter(q => q.type !== null);
98
-
99
- const answers = await prompts(promptQuestions, {
100
- onCancel: () => {
101
- log.warn('Cancelled.');
102
- process.exit(1);
103
- },
104
- onSubmit: () => {
105
- // Clean up terminal
106
- process.stdout.write('\x1b[?25h');
107
- },
108
- });
109
-
110
- const projectName = (answers.name || initial.name).trim();
111
- const ref = initial.ref; // Always use main branch
112
- const doInstall = initial.install; // Default: true
113
- const doGit = initial.git; // Default: true
182
+ const targetDir = process.cwd();
114
183
 
115
- // Validate project name
116
- if (!projectName) {
117
- log.error('Project name is required.');
184
+ if (!dirIsScaffoldable(targetDir)) {
185
+ log.error('This directory is not empty. Run tackl inside an empty directory (a .git folder, .gitignore, README.md and LICENSE are fine).');
118
186
  process.exit(1);
119
187
  }
120
188
 
121
- const targetDir = path.resolve(process.cwd(), projectName);
122
- if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
123
- log.error(
124
- `Directory "${projectName}" already exists and is not empty.`
125
- );
189
+ showBanner();
190
+
191
+ // SECTION • Gather choices
192
+ const cmsFlag = flagValue('--cms');
193
+ if (cmsFlag && !CMS[cmsFlag]) {
194
+ log.error(`Unknown --cms value "${cmsFlag}". Use dato, sanity or none.`);
126
195
  process.exit(1);
127
196
  }
128
- fs.mkdirSync(targetDir, { recursive: true });
129
197
 
130
- showBanner();
131
- console.log(pc.bold(pc.white('🚀 Starting project creation...\n')));
198
+ const answers = await prompts(
199
+ [
200
+ {
201
+ type: cmsFlag ? null : 'select',
202
+ name: 'cms',
203
+ message: 'Which CMS should this project use?',
204
+ choices: [
205
+ { title: 'DatoCMS', value: 'dato' },
206
+ { title: 'Sanity', value: 'sanity' },
207
+ { title: 'None', value: 'none' },
208
+ ],
209
+ initial: 0,
210
+ },
211
+ ].filter(q => q.type !== null),
212
+ {
213
+ onCancel: () => {
214
+ log.warn('Cancelled.');
215
+ process.exit(1);
216
+ },
217
+ }
218
+ );
132
219
 
133
- log.step(`Creating project: ${pc.bold(pc.cyan(projectName))}`);
134
- log.step(`Template: ${pc.bold(pc.yellow(`${OWNER}/${REPO}@${ref}`))}`);
135
- console.log();
220
+ const cms = cmsFlag || answers.cms;
221
+ const rawName = flagValue('--name') || path.basename(targetDir);
222
+ const projectName = rawName
223
+ .toLowerCase()
224
+ .replace(/[^a-z0-9\-._]/g, '-')
225
+ .replace(/^[-.]+|[-.]+$/g, '') || 'tackl-app';
226
+ const doInstall = !args.includes('--no-install');
227
+ const doGit = !args.includes('--no-git');
228
+ const hadGit = fs.existsSync(path.join(targetDir, '.git'));
136
229
 
137
- const tarUrl = `https://codeload.github.com/${OWNER}/${REPO}/tar.gz/${encodeURIComponent(ref)}`;
138
- const tmpTar = path.join(os.tmpdir(), `tackl-${Date.now()}.tgz`);
230
+ const { ref, source } = await resolveRef(flagValue('--ref'));
231
+ if (source === 'fallback') log.warn(`No release found — using ${FALLBACK_REF} (untagged).`);
139
232
 
140
- try {
141
- log.step('📥 Downloading template...');
142
- const res = await fetch(tarUrl);
143
- if (!res.ok)
144
- throw new Error(`Download failed: ${res.status} ${res.statusText}`);
233
+ console.log();
234
+ log.step(`Scaffolding ${pc.bold(pc.cyan(projectName))} in ${pc.bold(targetDir)}`);
235
+ log.step(`Template: ${pc.bold(pc.yellow(`${OWNER}/${REPO}@${ref}`))} · CMS: ${pc.bold(CMS[cms].title)}`);
236
+ console.log();
145
237
 
146
- // Convert response to buffer and write to file
147
- const arrayBuffer = await res.arrayBuffer();
148
- const buffer = Buffer.from(arrayBuffer);
149
- fs.writeFileSync(tmpTar, buffer);
238
+ // NOTE • If the repo already has its own LICENSE, that choice wins —
239
+ // stash it and restore it after extraction (the template ships MIT)
240
+ const licensePath = path.join(targetDir, 'LICENSE');
241
+ const existingLicense = fs.existsSync(licensePath) ? fs.readFileSync(licensePath, 'utf8') : null;
150
242
 
151
- log.ok('Template downloaded successfully!');
152
- } catch (e) {
153
- log.error(`Download failed: ${e.message}`);
154
- log.error('Please check your internet connection and try again.');
155
- process.exit(1);
243
+ // SECTION • Download + extract
244
+ // NOTE • TACKL_TEMPLATE_TAR points at a local tarball — used by tests/CI
245
+ const tmpTar = process.env.TACKL_TEMPLATE_TAR || path.join(os.tmpdir(), `tackl-${Date.now()}.tgz`);
246
+
247
+ if (!process.env.TACKL_TEMPLATE_TAR) {
248
+ const tarUrl = `https://codeload.github.com/${OWNER}/${REPO}/tar.gz/${encodeURIComponent(ref)}`;
249
+ try {
250
+ log.step('📥 Downloading template...');
251
+ const res = await fetch(tarUrl);
252
+ if (!res.ok) throw new Error(`Download failed: ${res.status} ${res.statusText}`);
253
+ fs.writeFileSync(tmpTar, Buffer.from(await res.arrayBuffer()));
254
+ log.ok('Template downloaded!');
255
+ } catch (e) {
256
+ log.error(`Download failed: ${e.message}`);
257
+ log.error('Please check your internet connection and try again.');
258
+ process.exit(1);
259
+ }
156
260
  }
157
261
 
158
262
  try {
159
263
  log.step('📦 Extracting files...');
160
264
  await tar.x({ file: tmpTar, cwd: targetDir, strip: 1 });
161
- log.ok('Files extracted successfully!');
265
+ log.ok('Files extracted!');
162
266
  } catch (e) {
163
267
  log.error(`Extraction failed: ${e.message}`);
164
- log.error('Please try again or check if the template is valid.');
165
268
  process.exit(1);
166
269
  } finally {
167
- try {
168
- fs.unlinkSync(tmpTar);
169
- } catch {}
270
+ if (!process.env.TACKL_TEMPLATE_TAR) {
271
+ try {
272
+ fs.unlinkSync(tmpTar);
273
+ } catch {}
274
+ }
170
275
  }
171
276
 
172
- // Remove template .git if present
173
- try {
174
- const gitPath = path.join(targetDir, '.git');
175
- if (fs.existsSync(gitPath))
176
- fs.rmSync(gitPath, { recursive: true, force: true });
177
- } catch {}
178
-
179
- // Remove any existing lock files to ensure npm is used
180
- try {
181
- const yarnLock = path.join(targetDir, 'yarn.lock');
182
- const pnpmLock = path.join(targetDir, 'pnpm-lock.yaml');
183
- if (fs.existsSync(yarnLock)) fs.unlinkSync(yarnLock);
184
- if (fs.existsSync(pnpmLock)) fs.unlinkSync(pnpmLock);
185
- log.step('🧹 Cleaned up lock files to ensure npm usage');
186
- } catch {}
187
-
188
- // Patch package name
189
- log.step('📝 Updating project configuration...');
190
- const pkg = path.join(targetDir, 'package.json');
191
- if (fs.existsSync(pkg)) {
277
+ // SECTION • Prune
278
+ log.step('🧹 Pruning template...');
279
+ if (existingLicense !== null) {
280
+ fs.writeFileSync(licensePath, existingLicense);
281
+ log.info('Kept your existing LICENSE.');
282
+ }
283
+ for (const cargo of REPO_CARGO) rmrf(targetDir, cargo);
284
+ for (const lock of ['yarn.lock', 'package-lock.json', 'pnpm-lock.yaml']) rmrf(targetDir, lock);
285
+ CMS[cms].prune(targetDir);
286
+ log.ok(`Template pruned for ${CMS[cms].title}.`);
287
+
288
+ // SECTION • Patch package.json
289
+ const pkgPath = path.join(targetDir, 'package.json');
290
+ if (fs.existsSync(pkgPath)) {
192
291
  try {
193
- const json = JSON.parse(fs.readFileSync(pkg, 'utf8'));
194
- json.name = projectName.toLowerCase();
195
- fs.writeFileSync(pkg, JSON.stringify(json, null, 2));
196
- log.ok('Project configuration updated!');
292
+ const json = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
293
+ json.name = projectName;
294
+ json.version = '0.1.0';
295
+ delete json.repository;
296
+ delete json.author;
297
+ fs.writeFileSync(pkgPath, JSON.stringify(json, null, '\t') + '\n');
298
+ log.ok('package.json updated.');
197
299
  } catch (e) {
198
300
  log.warn(`Could not update package.json: ${e.message}`);
199
301
  }
200
302
  }
201
303
 
202
- // Create .npmrc to ensure npm is used
203
- try {
204
- const npmrcPath = path.join(targetDir, '.npmrc');
205
- fs.writeFileSync(npmrcPath, 'package-lock=true\n');
206
- log.step('📝 Created .npmrc to ensure npm usage');
207
- } catch (e) {
208
- log.warn(`Could not create .npmrc: ${e.message}`);
209
- }
210
-
211
- if (doGit) {
304
+ // SECTION • Git
305
+ if (doGit && !hadGit) {
212
306
  try {
213
- log.step('🔧 Initializing git repository...');
307
+ log.step('🔧 Initialising git repository...');
214
308
  execSync('git init', { cwd: targetDir, stdio: 'ignore' });
215
-
216
- // Set default branch to main (not master)
217
309
  execSync('git branch -M main', { cwd: targetDir, stdio: 'ignore' });
218
-
219
310
  execSync('git add -A', { cwd: targetDir, stdio: 'ignore' });
220
- execSync('git commit -m "chore: scaffold with tackl"', {
221
- cwd: targetDir,
222
- stdio: 'ignore',
223
- });
224
- log.ok('Git repository initialized!');
311
+ execSync('git commit -m "chore: scaffold with tackl" --no-verify', { cwd: targetDir, stdio: 'ignore' });
312
+ log.ok('Git repository initialised!');
225
313
  } catch (e) {
226
314
  log.warn(`Git init failed: ${e.message}`);
227
315
  }
316
+ } else if (hadGit) {
317
+ log.info('Existing .git kept — review and commit the scaffold yourself.');
228
318
  }
229
319
 
320
+ // SECTION • Install
230
321
  if (doInstall) {
231
322
  try {
232
- log.step('📦 Installing dependencies with npm...');
233
-
234
- // Disable Husky during installation to prevent Git issues
235
- const originalEnv = { ...process.env };
236
- process.env.HUSKY = '0';
237
-
238
- execSync('npm install', { cwd: targetDir, stdio: 'inherit' });
239
-
240
- // Restore environment
241
- process.env = originalEnv;
242
-
243
- // Try to fix audit issues automatically
244
- try {
245
- log.step('🔧 Fixing security vulnerabilities...');
246
- execSync('npm audit fix', { cwd: targetDir, stdio: 'inherit' });
247
- log.ok('Security vulnerabilities fixed!');
248
- } catch (e) {
249
- log.warn(
250
- 'Some vulnerabilities could not be automatically fixed.'
251
- );
252
- log.info(
253
- 'You can run "npm audit fix --force" manually if needed.'
254
- );
255
- }
256
-
257
- log.ok('Dependencies installed successfully!');
323
+ log.step('📦 Installing dependencies with bun...');
324
+ execSync('bun install', { cwd: targetDir, stdio: 'inherit', env: { ...process.env, HUSKY: '0' } });
325
+ log.ok('Dependencies installed!');
258
326
  } catch (e) {
259
327
  log.warn(`Install failed: ${e.message}`);
260
328
  }
@@ -262,14 +330,11 @@ ${pc.bold('Repository:')} https://github.com/12-studio/tackl
262
330
  log.info('Skipped dependency install.');
263
331
  }
264
332
 
265
- const rel = path.relative(process.cwd(), targetDir) || '.';
266
- console.log(
267
- '\n' + pc.bold(pc.green('Success!')) + ' Project scaffolded.\n'
268
- );
269
- console.log(` Next steps:`);
270
- console.log(` 1) cd ${rel}`);
271
- if (!doInstall) console.log(` 2) npm install`);
272
- console.log(` ${doInstall ? '2' : '3'}) npm run dev`);
333
+ console.log('\n' + pc.bold(pc.green('Success!')) + ' Project scaffolded.\n');
334
+ console.log(' Next steps:');
335
+ console.log(' 1) cp .env.example .env # then fill in your values');
336
+ if (!doInstall) console.log(' 2) bun install');
337
+ console.log(` ${doInstall ? '2' : '3'}) bun run dev`);
273
338
  }
274
339
 
275
340
  main().catch(e => {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "tackl",
3
- "version": "1.0.10",
4
- "description": "The ultimate Next.js starter kit with Waffl Grid system",
3
+ "version": "2.0.0",
4
+ "description": "Scaffold a Tackl project into the current directory — Next.js, styled-components, Waffl grid, DatoCMS/Sanity adapters",
5
5
  "type": "module",
6
6
  "bin": {
7
- "tackl": "./bin/tackl.js"
7
+ "tackl": "bin/tackl.js"
8
8
  },
9
9
  "main": "index.js",
10
10
  "scripts": {
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",
30
- "url": "https://github.com/12-studio/tackl"
30
+ "url": "git+https://github.com/12-studio/tackl.git"
31
31
  },
32
32
  "homepage": "https://github.com/12-studio/tackl",
33
33
  "bugs": {