@creatorem/cli 0.0.1 → 1.0.5
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/README.md +148 -0
- package/dist/cli.js +74992 -0
- package/dist/cli.js.map +1 -0
- package/package.json +26 -5
- package/src/cli.tsx +141 -0
- package/src/commands/create-dashboard.tsx +455 -0
- package/src/commands/create-mobile.tsx +555 -0
- package/src/commands/create.tsx +1119 -0
- package/src/commands/generate-migration.mjs +17 -66
- package/src/commands/generate-migration.tsx +46 -0
- package/src/commands/generate-schemas.mjs +2 -2
- package/src/commands/generate-schemas.tsx +36 -0
- package/src/dashboard-features/ai/index.ts +102 -0
- package/src/dashboard-features/analytics/index.ts +31 -0
- package/src/dashboard-features/billing/index.ts +349 -0
- package/src/dashboard-features/content-type/index.ts +64 -0
- package/src/dashboard-features/email-templates/index.ts +17 -0
- package/src/dashboard-features/emailer/index.ts +27 -0
- package/src/dashboard-features/index.ts +28 -0
- package/src/dashboard-features/keybindings/index.ts +52 -0
- package/src/dashboard-features/manager.ts +349 -0
- package/src/dashboard-features/monitoring/index.ts +16 -0
- package/src/dashboard-features/notification/index.ts +40 -0
- package/src/dashboard-features/onboarding/index.ts +65 -0
- package/src/dashboard-features/organization/index.ts +38 -0
- package/src/dashboard-features/types.ts +41 -0
- package/src/mobile-features/index.ts +12 -0
- package/src/mobile-features/manager.ts +1 -0
- package/src/mobile-features/notification/index.ts +41 -0
- package/src/mobile-features/onboarding/index.ts +35 -0
- package/src/mobile-features/organization/index.ts +38 -0
- package/src/mobile-features/types.ts +1 -0
- package/src/shims/signal-exit.js +32 -0
- package/src/ui/app.tsx +68 -0
- package/src/ui/multi-select.tsx +106 -0
- package/src/utils/ast.ts +422 -0
- package/src/utils/env-template.ts +635 -0
- package/tests/test-cli-features.sh +81 -0
- package/tests/test-cli-mobile.sh +65 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +21 -0
- package/bin/cli.mjs +0 -40
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'fs';
|
|
2
2
|
import { basename, join } from 'path';
|
|
3
|
-
import { showHelp } from './help.mjs';
|
|
4
3
|
|
|
5
|
-
export function runGenerateMigration(args) {
|
|
6
|
-
// Parse arguments specific to this command
|
|
7
|
-
let includeEnvSchemas = '';
|
|
8
|
-
let listEnvSchemas = false;
|
|
9
|
-
|
|
10
|
-
for (let i = 0; i < args.length; i++) {
|
|
11
|
-
const arg = args[i];
|
|
12
|
-
switch (arg) {
|
|
13
|
-
case '--app-schemas': {
|
|
14
|
-
const nextArg = args[i + 1];
|
|
15
|
-
if (i + 1 >= args.length || !nextArg || nextArg.startsWith('--')) {
|
|
16
|
-
console.error('Error: --app-schemas requires a comma-separated list of schema names');
|
|
17
|
-
console.error('Example: --app-schemas booking,other-schema');
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
includeEnvSchemas = nextArg;
|
|
21
|
-
i++;
|
|
22
|
-
break;
|
|
23
|
-
}
|
|
24
|
-
case '--list-app-schemas':
|
|
25
|
-
listEnvSchemas = true;
|
|
26
|
-
break;
|
|
27
|
-
default:
|
|
28
|
-
console.error(`Unknown option: ${arg}`);
|
|
29
|
-
showHelp();
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
4
|
|
|
5
|
+
export function runGenerateMigration(args) {
|
|
34
6
|
// Default paths based on CWD
|
|
35
7
|
const baseDir = process.cwd();
|
|
36
8
|
const supabaseDir = join(baseDir, 'supabase');
|
|
@@ -38,26 +10,11 @@ export function runGenerateMigration(args) {
|
|
|
38
10
|
const schemasDir = join(supabaseDir, 'schemas');
|
|
39
11
|
const migrationsDir = join(supabaseDir, 'migrations');
|
|
40
12
|
|
|
41
|
-
if (listEnvSchemas) {
|
|
42
|
-
console.log('Available environment schemas:');
|
|
43
|
-
if (existsSync(envSchemasDir)) {
|
|
44
|
-
const files = readdirSync(envSchemasDir).filter((file) => file.endsWith('.sql'));
|
|
45
|
-
files.forEach((file) => {
|
|
46
|
-
console.log(` ${basename(file, '.sql')}`);
|
|
47
|
-
});
|
|
48
|
-
} else {
|
|
49
|
-
console.log('No app-schemas directory found at ' + envSchemasDir);
|
|
50
|
-
}
|
|
51
|
-
process.exit(0);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
13
|
const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\..+/, '').replace('T', '');
|
|
55
14
|
const migrationFile = join(migrationsDir, `${timestamp}_generated_from_schemas.sql`);
|
|
56
15
|
|
|
57
16
|
console.log('Generating migration file from schema files...');
|
|
58
|
-
|
|
59
|
-
console.log(`Environment schemas will be included: ${includeEnvSchemas}`);
|
|
60
|
-
}
|
|
17
|
+
console.log('Including all schemas from app-schemas directory...');
|
|
61
18
|
|
|
62
19
|
// Create migrations directory if it doesn't exist
|
|
63
20
|
if (!existsSync(migrationsDir)) {
|
|
@@ -85,27 +42,21 @@ export function runGenerateMigration(args) {
|
|
|
85
42
|
console.warn(`Warning: schemas directory not found at ${schemasDir}`);
|
|
86
43
|
}
|
|
87
44
|
|
|
88
|
-
// Add environment schemas
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (envFiles.length === 0) {
|
|
104
|
-
console.warn(`Warning: No environment schema files found for '${schema}'`);
|
|
105
|
-
} else {
|
|
106
|
-
schemaFiles.push(...envFiles);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
45
|
+
// Add all environment schemas
|
|
46
|
+
if (existsSync(envSchemasDir)) {
|
|
47
|
+
console.log(`Checking app-schemas directory: ${envSchemasDir}`);
|
|
48
|
+
const envFiles = readdirSync(envSchemasDir)
|
|
49
|
+
.filter((file) => file.endsWith('.sql'))
|
|
50
|
+
.sort()
|
|
51
|
+
.map((file) => join(envSchemasDir, file));
|
|
52
|
+
|
|
53
|
+
if (envFiles.length === 0) {
|
|
54
|
+
console.log('No schema files found in app-schemas directory');
|
|
55
|
+
} else {
|
|
56
|
+
schemaFiles.push(...envFiles);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
console.log(`No app-schemas directory found at ${envSchemasDir}, skipping...`);
|
|
109
60
|
}
|
|
110
61
|
|
|
111
62
|
// Check if any schema files were found
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Text, Box } from 'ink';
|
|
3
|
+
import { runGenerateMigration } from '../commands/generate-migration.mjs';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
args: string[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function GenerateMigration({ args }: Props) {
|
|
10
|
+
const [status, setStatus] = useState<'running' | 'success' | 'error'>('running');
|
|
11
|
+
const [error, setError] = useState<string | null>(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
try {
|
|
15
|
+
// We are reusing the logic from the mjs file for now.
|
|
16
|
+
// Ideally we would rewrite it to be more "reactive" or just emit events,
|
|
17
|
+
// but for now we just wrap the execution.
|
|
18
|
+
// Note: runGenerateMigration currently uses console.log which might interfere with Ink.
|
|
19
|
+
// Implementing a proper Ink version would require refactoring the logic to not use console.log directly
|
|
20
|
+
// or capturing it. For this first pass, we'll let it output to stdout/stderr
|
|
21
|
+
// and just show a completion message here.
|
|
22
|
+
// However, Ink captures stdout, so console.log might get eaten or rendered weirdly if not using Static.
|
|
23
|
+
// A better approach for the migration command (which is synchronous file ops) might be to just run it.
|
|
24
|
+
|
|
25
|
+
runGenerateMigration(args);
|
|
26
|
+
setStatus('success');
|
|
27
|
+
} catch (e: any) {
|
|
28
|
+
setError(e.message || String(e));
|
|
29
|
+
setStatus('error');
|
|
30
|
+
}
|
|
31
|
+
}, [args]);
|
|
32
|
+
|
|
33
|
+
if (status === 'running') {
|
|
34
|
+
return <Text>Generating migration...</Text>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (status === 'error') {
|
|
38
|
+
return <Text color="red">Error: {error}</Text>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Box flexDirection="column">
|
|
43
|
+
<Text color="green">Migration generated successfully!</Text>
|
|
44
|
+
</Box>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -17,7 +17,7 @@ export function runGenerateSchemas(args) {
|
|
|
17
17
|
let data;
|
|
18
18
|
let inputDir;
|
|
19
19
|
|
|
20
|
-
let arg0 = '
|
|
20
|
+
let arg0 = '.creatorem'; // Default to looking for .creatorem folder in cwd
|
|
21
21
|
if (args.length > 0) {
|
|
22
22
|
arg0 = args[0].trim();
|
|
23
23
|
}
|
|
@@ -42,7 +42,7 @@ export function runGenerateSchemas(args) {
|
|
|
42
42
|
// If directory, look for setup.json
|
|
43
43
|
inputPath = join(inputPath, 'setup.json');
|
|
44
44
|
if (!existsSync(inputPath)) {
|
|
45
|
-
console.error(`Error: setup.json not found in directory ${
|
|
45
|
+
console.error(`Error: setup.json not found in directory ${arg0} (defaults to setup.json if directory provided)`);
|
|
46
46
|
process.exit(1);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Text, Box } from 'ink';
|
|
3
|
+
import { runGenerateSchemas } from '../commands/generate-schemas.mjs';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
args: string[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function GenerateSchemas({ args }: Props) {
|
|
10
|
+
const [status, setStatus] = useState<'running' | 'success' | 'error'>('running');
|
|
11
|
+
const [error, setError] = useState<string | null>(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
try {
|
|
15
|
+
runGenerateSchemas(args);
|
|
16
|
+
setStatus('success');
|
|
17
|
+
} catch (e: any) {
|
|
18
|
+
setError(e.message || String(e));
|
|
19
|
+
setStatus('error');
|
|
20
|
+
}
|
|
21
|
+
}, [args]);
|
|
22
|
+
|
|
23
|
+
if (status === 'running') {
|
|
24
|
+
return <Text>Generating schemas...</Text>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (status === 'error') {
|
|
28
|
+
return <Text color="red">Error: {error}</Text>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Box flexDirection="column">
|
|
33
|
+
<Text color="green">Schemas generated successfully!</Text>
|
|
34
|
+
</Box>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { FeatureRemover } from '../types.js';
|
|
2
|
+
import { createProject, loadFile, removeImport, removeInlineJSX } from '../../utils/ast.js';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
export const AIFeature: FeatureRemover = {
|
|
7
|
+
key: 'ai',
|
|
8
|
+
cliUI: {
|
|
9
|
+
title: 'AI chat',
|
|
10
|
+
features: [
|
|
11
|
+
'AI chat UI',
|
|
12
|
+
'Tools integration',
|
|
13
|
+
'AI wallet & usage tracking'
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
dependenciesToRemove: [
|
|
17
|
+
'@kit/ai',
|
|
18
|
+
'@ai-sdk/google',
|
|
19
|
+
'@ai-sdk/openai',
|
|
20
|
+
'@ai-sdk/react',
|
|
21
|
+
'@assistant-ui/react',
|
|
22
|
+
'@assistant-ui/react-ai-sdk',
|
|
23
|
+
'@assistant-ui/react-data-stream',
|
|
24
|
+
'@assistant-ui/react-markdown',
|
|
25
|
+
'@assistant-ui/react-ui',
|
|
26
|
+
'ai',
|
|
27
|
+
'ollama-ai-provider-v2'
|
|
28
|
+
],
|
|
29
|
+
i18nNamespacePrefix: ['p_ai', 'ai-content'],
|
|
30
|
+
useFilters: 'useAIFilters',
|
|
31
|
+
crossEnvFilter: 'initAiFilters',
|
|
32
|
+
filesToDelete: [
|
|
33
|
+
'components/providers/ai-assistant-provider.tsx',
|
|
34
|
+
'config/ai.config.ts',
|
|
35
|
+
'app/api/chat',
|
|
36
|
+
'app/dashboard/[slug]/ai-chat',
|
|
37
|
+
'lib/ai-backend-tools'
|
|
38
|
+
],
|
|
39
|
+
apply: async (projectRoot: string) => {
|
|
40
|
+
const dashboardRoot = projectRoot;
|
|
41
|
+
|
|
42
|
+
// 1. Remove from use-filters.ts (extra cleanup)
|
|
43
|
+
const useFiltersPath = path.join(dashboardRoot, 'hooks/use-filters.ts');
|
|
44
|
+
if (fs.existsSync(useFiltersPath)) {
|
|
45
|
+
const project = createProject();
|
|
46
|
+
const sourceFile = loadFile(project, useFiltersPath);
|
|
47
|
+
|
|
48
|
+
removeImport(sourceFile, '~/config/ai.config');
|
|
49
|
+
|
|
50
|
+
await sourceFile.save();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 2. Update layout-client.tsx
|
|
54
|
+
const layoutClientPath = path.join(dashboardRoot, 'app/dashboard/[slug]/layout-client.tsx');
|
|
55
|
+
if (fs.existsSync(layoutClientPath)) {
|
|
56
|
+
const project = createProject();
|
|
57
|
+
const sourceFile = loadFile(project, layoutClientPath);
|
|
58
|
+
|
|
59
|
+
removeImport(sourceFile, '~/components/providers/ai-assistant-provider');
|
|
60
|
+
|
|
61
|
+
// Remove <AIAssistantProvider> wrapper
|
|
62
|
+
let text = sourceFile.getFullText();
|
|
63
|
+
text = text.replace(/<AIAssistantProvider>/g, '<>');
|
|
64
|
+
text = text.replace(/<\/AIAssistantProvider>/g, '</>');
|
|
65
|
+
|
|
66
|
+
sourceFile.replaceWithText(text);
|
|
67
|
+
await sourceFile.save();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 3. Update globals.css
|
|
71
|
+
const globalsCssPath = path.join(dashboardRoot, 'app/globals.css');
|
|
72
|
+
if (fs.existsSync(globalsCssPath)) {
|
|
73
|
+
let content = await fs.readFile(globalsCssPath, 'utf-8');
|
|
74
|
+
content = content.replace("@import '@kit/ai/style.css';", '');
|
|
75
|
+
await fs.writeFile(globalsCssPath, content);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 4. Update dashboard-action-group.tsx
|
|
79
|
+
const actionGroupPath = path.join(dashboardRoot, 'components/dashboard/dashboard-action-group.tsx');
|
|
80
|
+
if (fs.existsSync(actionGroupPath)) {
|
|
81
|
+
const project = createProject();
|
|
82
|
+
const sourceFile = loadFile(project, actionGroupPath);
|
|
83
|
+
|
|
84
|
+
removeImport(sourceFile, '@kit/ai/ui/floating-ai-chat');
|
|
85
|
+
removeInlineJSX(sourceFile, 'FloatingAIChatTrigger');
|
|
86
|
+
|
|
87
|
+
await sourceFile.save();
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
router: {
|
|
91
|
+
importName: 'aiRouter',
|
|
92
|
+
importPath: '@kit/ai/router',
|
|
93
|
+
},
|
|
94
|
+
repo: {
|
|
95
|
+
filesToDelete: [
|
|
96
|
+
'supabase/schemas/026-user-ai-conversation.sql',
|
|
97
|
+
'supabase/schemas/027-user-ai-usage.sql',
|
|
98
|
+
'supabase/schemas/028-user-ai-wallet.sql',
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FeatureRemover } from '../types.js';
|
|
2
|
+
import { createProject, loadFile, removeImport, removePropertyAssignment } from '../../utils/ast.js';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
export const AnalyticsFeature: FeatureRemover = {
|
|
7
|
+
key: 'analytics',
|
|
8
|
+
cliUI: {
|
|
9
|
+
title: 'Analytics',
|
|
10
|
+
description: 'Support for Google analytics & Umami'
|
|
11
|
+
},
|
|
12
|
+
dependenciesToRemove: ['@kit/analytics'],
|
|
13
|
+
useFilters: 'useAnalyticsFilters',
|
|
14
|
+
filesToDelete: [
|
|
15
|
+
'config/analytics.config.ts',
|
|
16
|
+
],
|
|
17
|
+
apply: async (projectRoot: string) => {
|
|
18
|
+
const dashboardRoot = projectRoot;
|
|
19
|
+
const appProviderPath = path.join(dashboardRoot, 'components/providers/app-provider.tsx');
|
|
20
|
+
|
|
21
|
+
if (fs.existsSync(appProviderPath)) {
|
|
22
|
+
const project = createProject();
|
|
23
|
+
const sourceFile = loadFile(project, appProviderPath);
|
|
24
|
+
|
|
25
|
+
removeImport(sourceFile, '~/config/analytics.config');
|
|
26
|
+
removePropertyAssignment(sourceFile, 'analytics');
|
|
27
|
+
|
|
28
|
+
await sourceFile.save();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|