anhedral 0.1.1 → 0.1.3

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.
@@ -1,37 +1,53 @@
1
+ import { rmSync } from 'node:fs';
1
2
  import path from 'node:path';
2
3
  import { writeFile, exec } from '../util.js';
4
+ import { anhedralPrint } from '../print.js';
3
5
  import { resolveToolchainChannel, resolveToolchain, toolPackageRef } from '../toolchain.js';
4
6
  export async function scaffoldExtension(root, { projectName, displayName }) {
5
- const dir = path.join(root, 'extension');
7
+ const appsRoot = path.join(root, 'apps');
8
+ const dir = path.join(appsRoot, 'extension');
6
9
  const toolchain = resolveToolchain(resolveToolchainChannel(process.env.ANHEDRAL_TOOLCHAIN));
7
- // ── 1. Scaffold via WXT CLI ───────────────────────────────────────────
8
- console.log(' Scaffolding WXT extension...');
9
- exec(`pnpm dlx ${toolPackageRef('wxt', toolchain.wxt)} init extension -t react --pm pnpm`, root);
10
- // ── 2. Overwrite package.json with proper scripts ──────────────────────
10
+ anhedralPrint.section('Chrome extension (WXT)');
11
+ anhedralPrint.step('Scaffolding WXT extension');
12
+ writeFile(path.join(appsRoot, '.gitkeep'), '');
13
+ exec(`pnpm dlx ${toolPackageRef('wxt', toolchain.wxt)} init extension -t react --pm pnpm`, appsRoot);
11
14
  writePackageJson(dir, projectName);
12
- // ── 3. Install dependencies ───────────────────────────────────────────
13
- console.log(' Installing Clerk + React + UI dependencies...');
14
- exec('pnpm add @clerk/chrome-extension react react-dom clsx tailwind-merge class-variance-authority lucide-react', dir);
15
- exec(`pnpm add -D @types/chrome @types/react @types/react-dom @wxt-dev/module-react autoprefixer postcss tailwindcss typescript ${toolPackageRef('wxt', toolchain.wxt)}`, dir);
16
- // ── 4. Write config files ─────────────────────────────────────────────
15
+ anhedralPrint.done('WXT extension scaffolded');
16
+ anhedralPrint.step('Installing Clerk + React + Tailwind dependencies');
17
+ exec(`pnpm add -D @types/chrome @types/react @types/react-dom @wxt-dev/module-react autoprefixer postcss tailwindcss@3.4.19 typescript@5.9.3 ${toolPackageRef('wxt', toolchain.wxt)}`, dir);
18
+ exec('pnpm add @clerk/chrome-extension react react-dom clsx tailwind-merge class-variance-authority lucide-react@0.468.0', dir);
19
+ anhedralPrint.done('Extension dependencies installed');
20
+ cleanWxtStarterFiles(dir);
17
21
  writeWxtConfig(dir, displayName);
18
22
  writeTsConfig(dir);
19
23
  writeEnvExample(dir);
24
+ writeEnvFile(dir);
20
25
  writePostcssConfig(dir);
21
26
  writeTailwindConfig(dir);
22
27
  writeShadcnConfig(dir);
28
+ writeReadme(dir, displayName);
23
29
  writeCnUtil(dir);
24
- // ── 5. Write source files ─────────────────────────────────────────────
25
30
  writeAuthContext(dir);
26
31
  writeApiClient(dir);
27
32
  writeBackground(dir);
33
+ writeContentScript(dir);
28
34
  writeSidepanelEntry(dir);
29
35
  writeSidepanelHtml(dir, displayName);
30
36
  writeSidepanelApp(dir);
31
37
  writeStyles(dir);
32
- // ── 6. Add shadcn button component (sets up CSS variables + component) ─
33
- console.log(' Adding shadcn button component...');
38
+ anhedralPrint.step('Adding shadcn button component');
34
39
  exec(`pnpm dlx ${toolPackageRef('shadcn', toolchain.shadcn)} add button`, dir);
40
+ writeStyles(dir);
41
+ anhedralPrint.done('shadcn button installed');
42
+ }
43
+ function cleanWxtStarterFiles(dir) {
44
+ for (const relativePath of [
45
+ 'entrypoints',
46
+ 'assets/react.svg',
47
+ 'public/wxt.svg',
48
+ ]) {
49
+ rmSync(path.join(dir, relativePath), { recursive: true, force: true });
50
+ }
35
51
  }
36
52
  function writePackageJson(dir, projectName) {
37
53
  writeFile(path.join(dir, 'package.json'), JSON.stringify({
@@ -64,7 +80,7 @@ export default defineConfig({
64
80
  description: '${displayName} Chrome Extension',
65
81
  version: '0.1.0',
66
82
  ...(crxPublicKey ? { key: crxPublicKey } : {}),
67
- permissions: ['cookies', 'storage'],
83
+ permissions: ['activeTab', 'cookies', 'storage'],
68
84
  host_permissions: [],
69
85
  action: {
70
86
  default_title: 'Open ${displayName}',
@@ -87,6 +103,7 @@ function writeTsConfig(dir) {
87
103
  forceConsistentCasingInFileNames: true,
88
104
  resolveJsonModule: true,
89
105
  isolatedModules: true,
106
+ allowImportingTsExtensions: true,
90
107
  noEmit: true,
91
108
  jsx: 'react-jsx',
92
109
  lib: ['DOM', 'DOM.Iterable', 'ESNext'],
@@ -111,12 +128,29 @@ VITE_WEBSITE_URL=http://localhost:8081
111
128
  # Chrome Extension CRX public key (optional, for stable extension ID)
112
129
  VITE_CRX_PUBLIC_KEY=
113
130
 
131
+ # RevenueCat Web Billing URL (optional, for subscription management)
132
+ VITE_RC_BILLING_URL=
133
+ `);
134
+ }
135
+ function writeEnvFile(dir) {
136
+ writeFile(path.join(dir, '.env'), `# Clerk Authentication
137
+ VITE_CLERK_PUBLISHABLE_KEY=
138
+
139
+ # Backend API URL
140
+ VITE_API_URL=http://localhost:8787
141
+
142
+ # Website URL (for sign-up and subscription links)
143
+ VITE_WEBSITE_URL=http://localhost:8081
144
+
145
+ # Chrome Extension CRX public key (optional, for stable extension ID)
146
+ VITE_CRX_PUBLIC_KEY=
147
+
114
148
  # RevenueCat Web Billing URL (optional, for subscription management)
115
149
  VITE_RC_BILLING_URL=
116
150
  `);
117
151
  }
118
152
  function writePostcssConfig(dir) {
119
- writeFile(path.join(dir, 'postcss.config.js'), `module.exports = {
153
+ writeFile(path.join(dir, 'postcss.config.cjs'), `module.exports = {
120
154
  plugins: {
121
155
  tailwindcss: {},
122
156
  autoprefixer: {},
@@ -157,14 +191,79 @@ export function cn(...inputs: ClassValue[]) {
157
191
  `);
158
192
  }
159
193
  function writeTailwindConfig(dir) {
160
- writeFile(path.join(dir, 'tailwind.config.js'), `/** @type {import('tailwindcss').Config} */
194
+ writeFile(path.join(dir, 'tailwind.config.cjs'), `/** @type {import('tailwindcss').Config} */
161
195
  module.exports = {
162
196
  content: ['./src/**/*.{ts,tsx,html}'],
163
- theme: { extend: {} },
197
+ theme: {
198
+ extend: {
199
+ colors: {
200
+ border: 'hsl(var(--border))',
201
+ input: 'hsl(var(--input))',
202
+ ring: 'hsl(var(--ring))',
203
+ background: 'hsl(var(--background))',
204
+ foreground: 'hsl(var(--foreground))',
205
+ primary: {
206
+ DEFAULT: 'hsl(var(--primary))',
207
+ foreground: 'hsl(var(--primary-foreground))',
208
+ },
209
+ secondary: {
210
+ DEFAULT: 'hsl(var(--secondary))',
211
+ foreground: 'hsl(var(--secondary-foreground))',
212
+ },
213
+ destructive: {
214
+ DEFAULT: 'hsl(var(--destructive))',
215
+ foreground: 'hsl(var(--destructive-foreground))',
216
+ },
217
+ muted: {
218
+ DEFAULT: 'hsl(var(--muted))',
219
+ foreground: 'hsl(var(--muted-foreground))',
220
+ },
221
+ accent: {
222
+ DEFAULT: 'hsl(var(--accent))',
223
+ foreground: 'hsl(var(--accent-foreground))',
224
+ },
225
+ popover: {
226
+ DEFAULT: 'hsl(var(--popover))',
227
+ foreground: 'hsl(var(--popover-foreground))',
228
+ },
229
+ card: {
230
+ DEFAULT: 'hsl(var(--card))',
231
+ foreground: 'hsl(var(--card-foreground))',
232
+ },
233
+ },
234
+ borderRadius: {
235
+ lg: 'var(--radius)',
236
+ md: 'calc(var(--radius) - 2px)',
237
+ sm: 'calc(var(--radius) - 4px)',
238
+ },
239
+ },
240
+ },
164
241
  plugins: [],
165
242
  };
166
243
  `);
167
244
  }
245
+ function writeReadme(dir, displayName) {
246
+ writeFile(path.join(dir, 'README.md'), `# ${displayName} Chrome Extension
247
+
248
+ WXT side-panel extension generated by anhedral.
249
+
250
+ ## Development
251
+
252
+ \`\`\`bash
253
+ pnpm dev
254
+ pnpm build
255
+ pnpm zip
256
+ \`\`\`
257
+
258
+ Set \`VITE_CLERK_PUBLISHABLE_KEY\` and \`VITE_API_URL\` in \`.env\` before using auth-backed routes. Set \`VITE_CRX_PUBLIC_KEY\` only when you need a stable Chrome extension ID.
259
+
260
+ ## Chrome
261
+
262
+ Run \`pnpm build\`, then load \`.output/chrome-mv3\` as an unpacked extension from \`chrome://extensions\`.
263
+
264
+ The browser action opens the side panel. The background script initializes Clerk, the side panel handles sign-in/subscription state, and the content script is ready for page-to-extension messages.
265
+ `);
266
+ }
168
267
  function writeAuthContext(dir) {
169
268
  writeFile(path.join(dir, 'src/contexts/auth-context.tsx'), `import * as React from 'react';
170
269
  import { ClerkProvider, useAuth as useClerkAuth, useUser } from '@clerk/chrome-extension';
@@ -379,6 +478,23 @@ export default defineBackground(() => {
379
478
  });
380
479
  `);
381
480
  }
481
+ function writeContentScript(dir) {
482
+ writeFile(path.join(dir, 'src/entrypoints/content.ts'), `export default defineContentScript({
483
+ matches: ['<all_urls>'],
484
+ main() {
485
+ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
486
+ if (message?.type !== 'ANHEDRAL_PAGE_SNAPSHOT') return false;
487
+
488
+ sendResponse({
489
+ title: document.title,
490
+ location: window.location.href,
491
+ });
492
+ return true;
493
+ });
494
+ },
495
+ });
496
+ `);
497
+ }
382
498
  function writeSidepanelEntry(dir) {
383
499
  writeFile(path.join(dir, 'src/entrypoints/sidepanel/main.tsx'), `import * as React from 'react';
384
500
  import { createRoot } from 'react-dom/client';
@@ -418,9 +534,33 @@ function writeSidepanelApp(dir) {
418
534
  writeFile(path.join(dir, 'src/entrypoints/sidepanel/app.tsx'), `import * as React from 'react';
419
535
  import { useAuth } from '../../contexts/auth-context';
420
536
  import { SignIn } from '@clerk/chrome-extension';
537
+ import { Button } from '../../components/ui/button';
538
+
539
+ type PageSnapshot = {
540
+ title: string;
541
+ location: string;
542
+ };
421
543
 
422
544
  export function SidePanelApp() {
423
545
  const { isSignedIn, isLoading, signOut, subscription } = useAuth();
546
+ const [page, setPage] = React.useState<PageSnapshot | null>(null);
547
+ const [pageError, setPageError] = React.useState<string | null>(null);
548
+
549
+ const readActivePage = React.useCallback(async () => {
550
+ setPageError(null);
551
+ const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
552
+ if (!tab.id) {
553
+ setPageError('No active tab is available.');
554
+ return;
555
+ }
556
+
557
+ try {
558
+ const response = await chrome.tabs.sendMessage(tab.id, { type: 'ANHEDRAL_PAGE_SNAPSHOT' });
559
+ setPage(response as PageSnapshot);
560
+ } catch {
561
+ setPageError('Refresh the active page, then try again.');
562
+ }
563
+ }, []);
424
564
 
425
565
  if (isLoading) {
426
566
  return <div style={{ padding: 24, textAlign: 'center' }}>Loading...</div>;
@@ -438,7 +578,15 @@ export function SidePanelApp() {
438
578
  <div style={{ padding: 24 }}>
439
579
  <h2>Welcome!</h2>
440
580
  <p>Subscription: {subscription.status}</p>
441
- <button onClick={signOut}>Sign Out</button>
581
+ <Button type="button" onClick={() => void readActivePage()}>Read active page</Button>
582
+ {page ? (
583
+ <div style={{ marginTop: 16 }}>
584
+ <strong>{page.title || 'Untitled page'}</strong>
585
+ <p style={{ overflowWrap: 'anywhere' }}>{page.location}</p>
586
+ </div>
587
+ ) : null}
588
+ {pageError ? <p style={{ color: 'hsl(var(--destructive))' }}>{pageError}</p> : null}
589
+ <Button type="button" variant="outline" onClick={signOut}>Sign Out</Button>
442
590
  </div>
443
591
  );
444
592
  }
@@ -449,6 +597,40 @@ function writeStyles(dir) {
449
597
  @tailwind components;
450
598
  @tailwind utilities;
451
599
 
600
+ @layer base {
601
+ :root {
602
+ --background: 0 0% 100%;
603
+ --foreground: 0 0% 9%;
604
+ --card: 0 0% 100%;
605
+ --card-foreground: 0 0% 9%;
606
+ --popover: 0 0% 100%;
607
+ --popover-foreground: 0 0% 9%;
608
+ --primary: 0 0% 9%;
609
+ --primary-foreground: 0 0% 98%;
610
+ --secondary: 0 0% 96.1%;
611
+ --secondary-foreground: 0 0% 9%;
612
+ --muted: 0 0% 96.1%;
613
+ --muted-foreground: 0 0% 45.1%;
614
+ --accent: 0 0% 96.1%;
615
+ --accent-foreground: 0 0% 9%;
616
+ --destructive: 0 84.2% 60.2%;
617
+ --destructive-foreground: 0 0% 98%;
618
+ --border: 0 0% 89.8%;
619
+ --input: 0 0% 89.8%;
620
+ --ring: 0 0% 63.9%;
621
+ --radius: 0.5rem;
622
+ }
623
+
624
+ * {
625
+ border-color: hsl(var(--border));
626
+ }
627
+
628
+ body {
629
+ background: hsl(var(--background));
630
+ color: hsl(var(--foreground));
631
+ }
632
+ }
633
+
452
634
  body {
453
635
  margin: 0;
454
636
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
@@ -1,26 +1,27 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { writeAnhedralLogo } from '../branding.js';
4
+ import { anhedralPrint } from '../print.js';
4
5
  import { appendGitignore, writeFile, exec, execExpect, liftNestedProject } from '../util.js';
5
6
  import { resolveToolchainChannel, resolveToolchain, toolPackageRef } from '../toolchain.js';
6
7
  export async function scaffoldFrontend(root, { projectName, displayName }) {
7
- const dir = path.join(root, 'frontend');
8
+ const dir = path.join(root, 'apps/mobile');
8
9
  const toolchain = resolveToolchain(resolveToolchainChannel(process.env.ANHEDRAL_TOOLCHAIN));
9
- // ── 1. Scaffold via react-native-reusables CLI ────────────────────────
10
- console.log(' Scaffolding Expo app with react-native-reusables...');
10
+ anhedralPrint.section('Mobile (Expo)');
11
+ anhedralPrint.step('Scaffolding Expo app with react-native-reusables');
11
12
  fs.mkdirSync(dir, { recursive: true });
12
13
  await execExpect(`pnpm dlx ${toolPackageRef('@react-native-reusables/cli', toolchain.reactNativeReusables)} init -t clerk-auth`, dir, [
13
14
  ['What is the name of your project?', projectName],
14
15
  ['Would you like to install dependencies?', 'n'],
15
16
  ['Would you like to initialize a Git repository?', 'n'],
16
17
  ]);
17
- // The CLI scaffolds into a subdir named after the project — move contents up
18
18
  liftNestedProject(dir, projectName);
19
- console.log(' Installing frontend dependencies...');
20
- exec('pnpm install', dir);
19
+ anhedralPrint.done('Expo app scaffolded');
20
+ anhedralPrint.step('Installing mobile dependencies');
21
+ exec('pnpm install --no-frozen-lockfile', dir);
21
22
  patchFrontendPackageJson(dir);
22
23
  appendGitignore(dir, ['.env', '.env.*', '!.env.example']);
23
- // ── 2. Patch in API client ────────────────────────────────────────────
24
+ anhedralPrint.done('Mobile dependencies installed');
24
25
  writeApiClient(dir);
25
26
  writeConfigFile(dir);
26
27
  writeApiHook(dir);
@@ -30,19 +31,19 @@ export async function scaffoldFrontend(root, { projectName, displayName }) {
30
31
  writeEnvExample(dir);
31
32
  writeEnvFile(dir);
32
33
  writeAnhedralLogo(dir);
33
- // ── 2b. Write subscription files ─────────────────────────────────────
34
34
  writeSubscriptionProvider(dir);
35
35
  writeUseSubscription(dir);
36
- // ── 3. Install extra dependencies ─────────────────────────────────────
37
- console.log(' Installing additional dependencies...');
36
+ anhedralPrint.step('Installing image picker + RevenueCat SDKs');
38
37
  exec('pnpm exec expo install expo-image-picker', dir);
39
38
  exec('pnpm add react-native-purchases react-native-purchases-ui @revenuecat/purchases-js', dir);
39
+ anhedralPrint.done('Additional mobile SDKs installed');
40
40
  }
41
41
  function patchFrontendPackageJson(dir) {
42
42
  const filePath = path.join(dir, 'package.json');
43
43
  const packageJson = JSON.parse(fs.readFileSync(filePath, 'utf8'));
44
44
  packageJson.scripts = {
45
45
  ...(packageJson.scripts ?? {}),
46
+ build: 'pnpm typecheck',
46
47
  typecheck: 'tsc --noEmit',
47
48
  };
48
49
  writeFile(filePath, JSON.stringify(packageJson, null, 2) + '\n');
@@ -2,12 +2,9 @@ export declare const TOOLCHAIN_CHANNELS: readonly ["latest", "stable"];
2
2
  export type ToolchainChannel = (typeof TOOLCHAIN_CHANNELS)[number];
3
3
  export type ToolchainSpec = {
4
4
  verifiedAt: string | null;
5
- viteCreate: string;
6
5
  shadcn: string;
7
6
  reactNativeReusables: string;
8
7
  wxt: string;
9
- tauriCli: string;
10
- tauriApi: string;
11
8
  };
12
9
  export declare function resolveToolchainChannel(value: string | undefined): ToolchainChannel;
13
10
  export declare function resolveToolchain(channel: ToolchainChannel): ToolchainSpec;
package/dist/toolchain.js CHANGED
@@ -2,27 +2,18 @@ export const TOOLCHAIN_CHANNELS = ['latest', 'stable'];
2
2
  const TOOLCHAIN_SPECS = {
3
3
  latest: {
4
4
  verifiedAt: null,
5
- viteCreate: 'latest',
6
5
  shadcn: 'latest',
7
6
  reactNativeReusables: 'latest',
8
7
  wxt: 'latest',
9
- tauriCli: 'latest',
10
- tauriApi: 'latest',
11
8
  },
12
9
  stable: {
13
10
  verifiedAt: '2026-03-22',
14
- // renovate: datasource=npm depName=create-vite
15
- viteCreate: '7.1.4',
16
11
  // renovate: datasource=npm depName=shadcn
17
12
  shadcn: '4.1.0',
18
13
  // renovate: datasource=npm depName=@react-native-reusables/cli
19
14
  reactNativeReusables: '0.5.0',
20
15
  // renovate: datasource=npm depName=wxt
21
16
  wxt: '0.20.20',
22
- // renovate: datasource=npm depName=@tauri-apps/cli
23
- tauriCli: '2.8.4',
24
- // renovate: datasource=npm depName=@tauri-apps/api
25
- tauriApi: '2.8.0',
26
17
  },
27
18
  };
28
19
  export function resolveToolchainChannel(value) {
package/dist/util.js CHANGED
@@ -13,13 +13,42 @@ export function appendGitignore(root, lines) {
13
13
  const merged = [...new Set([...existing, ...lines])];
14
14
  writeFile(filePath, merged.join('\n') + '\n');
15
15
  }
16
+ const VERBOSE = process.env.ANHEDRAL_VERBOSE === '1';
17
+ function dumpFailure(error, cmd) {
18
+ const err = error;
19
+ const stdoutText = err.stdout ? err.stdout.toString() : '';
20
+ const stderrText = err.stderr ? err.stderr.toString() : '';
21
+ if (stdoutText)
22
+ process.stderr.write(stdoutText);
23
+ if (stderrText)
24
+ process.stderr.write(stderrText);
25
+ throw new Error(`Command failed (exit ${err.status ?? '?'}): ${cmd}`);
26
+ }
16
27
  export function exec(cmd, cwd) {
17
- console.log(` $ ${cmd}`);
18
- execSync(cmd, { cwd, stdio: 'inherit' });
28
+ if (VERBOSE) {
29
+ console.log(` $ ${cmd}`);
30
+ execSync(cmd, { cwd, stdio: 'inherit' });
31
+ return;
32
+ }
33
+ try {
34
+ execSync(cmd, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
35
+ }
36
+ catch (error) {
37
+ dumpFailure(error, cmd);
38
+ }
19
39
  }
20
40
  export function execWithInput(cmd, cwd, stdinInput) {
21
- console.log(` $ ${cmd}`);
22
- execSync(cmd, { cwd, stdio: ['pipe', 'inherit', 'inherit'], input: stdinInput });
41
+ if (VERBOSE) {
42
+ console.log(` $ ${cmd}`);
43
+ execSync(cmd, { cwd, stdio: ['pipe', 'inherit', 'inherit'], input: stdinInput });
44
+ return;
45
+ }
46
+ try {
47
+ execSync(cmd, { cwd, stdio: ['pipe', 'pipe', 'pipe'], input: stdinInput });
48
+ }
49
+ catch (error) {
50
+ dumpFailure(error, cmd);
51
+ }
23
52
  }
24
53
  function moveDirectoryContents(sourceDir, targetDir) {
25
54
  for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
@@ -63,11 +92,14 @@ function trimOutputTail(output, maxLength = 4000) {
63
92
  return output.slice(output.length - maxLength);
64
93
  }
65
94
  export async function execExpect(cmd, cwd, prompts, timeoutMs = 120_000) {
66
- console.log(` $ ${cmd}`);
95
+ if (VERBOSE) {
96
+ console.log(` $ ${cmd}`);
97
+ }
67
98
  await new Promise((resolve, reject) => {
68
- const child = spawn('bash', ['-lc', cmd], {
99
+ const child = spawn(cmd, {
69
100
  cwd,
70
101
  env: process.env,
102
+ shell: true,
71
103
  stdio: ['pipe', 'pipe', 'pipe'],
72
104
  });
73
105
  let combinedOutput = '';
@@ -90,7 +122,8 @@ export async function execExpect(cmd, cwd, prompts, timeoutMs = 120_000) {
90
122
  const handleChunk = (chunk, target) => {
91
123
  const text = chunk.toString();
92
124
  combinedOutput += text;
93
- target.write(text);
125
+ if (VERBOSE)
126
+ target.write(text);
94
127
  maybeAnswerPrompt();
95
128
  };
96
129
  const timeoutId = setTimeout(() => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "anhedral",
3
- "version": "0.1.1",
4
- "description": "Opinionated init CLI for Next, Next + Fastify, Expo + Fastify, and backend starters with Neon, Drizzle, R2, auth, and billing integrations.",
3
+ "version": "0.1.3",
4
+ "description": "Opinionated init CLI for Expo or Next.js fullstack workspaces with Fastify, WXT, shared packages, Neon, Drizzle, Clerk, Stripe, RevenueCat, and R2.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "anhedral": "bin/anhedral.js"
@@ -23,8 +23,7 @@
23
23
  "url": "https://github.com/anhedral/anhedral-init/issues"
24
24
  },
25
25
  "publishConfig": {
26
- "access": "public",
27
- "provenance": true
26
+ "access": "public"
28
27
  },
29
28
  "engines": {
30
29
  "node": ">=20.0.0"
@@ -37,14 +36,13 @@
37
36
  "test": "pnpm test:cli",
38
37
  "pretest": "pnpm build",
39
38
  "prepublishOnly": "pnpm test:all",
40
- "test:cli": "node scripts/test-cli.js",
41
- "test:cli:validation": "node scripts/test-cli-validation.js",
42
- "test:cli:scaffold": "node scripts/test-cli-scaffold-headless.js",
43
- "test:e2e": "node scripts/test-e2e-public-stacks.js",
44
- "test:e2e:latest": "node scripts/test-e2e-public-stacks.js --toolchain latest",
39
+ "test:cli": "node tests/test-cli.js",
40
+ "test:cli:validation": "node tests/test-cli-validation.js",
41
+ "test:e2e": "node tests/test-e2e-public-stacks.js",
42
+ "test:e2e:latest": "node tests/test-e2e-public-stacks.js --toolchain latest",
45
43
  "test:all": "pnpm test && pnpm test:e2e",
46
44
  "release:check": "pnpm test:all && npm pack --dry-run",
47
- "demo:refresh": "pnpm build && node scripts/refresh-demo.js"
45
+ "demo:refresh": "pnpm build && node tests/refresh-demo.js"
48
46
  },
49
47
  "keywords": [
50
48
  "cli",
@@ -55,7 +53,7 @@
55
53
  "drizzle",
56
54
  "neondb",
57
55
  "revenuecat",
58
- "better-auth",
56
+ "wxt",
59
57
  "shadcn"
60
58
  ],
61
59
  "license": "MIT",