create-stackforge 0.1.2 → 0.1.4
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 +543 -518
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -275,7 +275,6 @@ async function promptForConfig(input) {
|
|
|
275
275
|
|
|
276
276
|
// src/generators/core/project-creator.ts
|
|
277
277
|
import { join as join3 } from "path";
|
|
278
|
-
import { fileURLToPath } from "url";
|
|
279
278
|
|
|
280
279
|
// src/utils/file-system.ts
|
|
281
280
|
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
@@ -643,19 +642,33 @@ async function readProjectConfig(root) {
|
|
|
643
642
|
}
|
|
644
643
|
|
|
645
644
|
// src/generators/core/project-creator.ts
|
|
645
|
+
var GITIGNORE_CONTENT = `node_modules/
|
|
646
|
+
.next/
|
|
647
|
+
dist/
|
|
648
|
+
.env
|
|
649
|
+
.env.local
|
|
650
|
+
.env.*.local
|
|
651
|
+
.DS_Store
|
|
652
|
+
`;
|
|
653
|
+
var EDITORCONFIG_CONTENT = `root = true
|
|
654
|
+
|
|
655
|
+
[*]
|
|
656
|
+
charset = utf-8
|
|
657
|
+
end_of_line = lf
|
|
658
|
+
insert_final_newline = true
|
|
659
|
+
indent_style = space
|
|
660
|
+
indent_size = 2
|
|
661
|
+
`;
|
|
646
662
|
async function createProjectSkeleton(root, config, ctx) {
|
|
647
663
|
const projectRoot = join3(root, config.projectName);
|
|
648
|
-
const templatesRoot = fileURLToPath(new URL("../../../templates", import.meta.url));
|
|
649
664
|
await ensureDir(projectRoot, ctx);
|
|
650
665
|
const readme = buildProjectReadme(config);
|
|
651
666
|
await writeTextFile(join3(projectRoot, "README.md"), readme + "\n", ctx);
|
|
652
667
|
const envExample = `# Environment Variables
|
|
653
668
|
`;
|
|
654
669
|
await writeTextFile(join3(projectRoot, ".env.example"), envExample, ctx);
|
|
655
|
-
|
|
656
|
-
await writeTextFile(join3(projectRoot, ".
|
|
657
|
-
const editorconfig = await readTextFile(join3(templatesRoot, "shared", ".editorconfig"));
|
|
658
|
-
await writeTextFile(join3(projectRoot, ".editorconfig"), editorconfig, ctx);
|
|
670
|
+
await writeTextFile(join3(projectRoot, ".gitignore"), GITIGNORE_CONTENT, ctx);
|
|
671
|
+
await writeTextFile(join3(projectRoot, ".editorconfig"), EDITORCONFIG_CONTENT, ctx);
|
|
659
672
|
const pkg = {
|
|
660
673
|
name: config.projectName,
|
|
661
674
|
version: "0.0.0",
|
|
@@ -698,8 +711,7 @@ async function createProjectSkeleton(root, config, ctx) {
|
|
|
698
711
|
}
|
|
699
712
|
|
|
700
713
|
// src/generators/database/database-files.ts
|
|
701
|
-
import { join as
|
|
702
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
714
|
+
import { join as join5 } from "path";
|
|
703
715
|
|
|
704
716
|
// src/utils/env-file.ts
|
|
705
717
|
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
@@ -740,12 +752,30 @@ async function removeEnvKey(path, key, ctx) {
|
|
|
740
752
|
await writeFile3(path, filtered.join("\n"), "utf8");
|
|
741
753
|
}
|
|
742
754
|
|
|
755
|
+
// src/utils/templates-dir.ts
|
|
756
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
757
|
+
import { existsSync as existsSync2 } from "fs";
|
|
758
|
+
import { fileURLToPath } from "url";
|
|
759
|
+
function findPackageRoot() {
|
|
760
|
+
let dir = dirname3(fileURLToPath(import.meta.url));
|
|
761
|
+
for (let i = 0; i < 10; i++) {
|
|
762
|
+
if (existsSync2(join4(dir, "package.json"))) {
|
|
763
|
+
return dir;
|
|
764
|
+
}
|
|
765
|
+
const parent = dirname3(dir);
|
|
766
|
+
if (parent === dir) break;
|
|
767
|
+
dir = parent;
|
|
768
|
+
}
|
|
769
|
+
throw new Error("Could not find package root (package.json)");
|
|
770
|
+
}
|
|
771
|
+
var TEMPLATES_DIR = join4(findPackageRoot(), "templates");
|
|
772
|
+
|
|
743
773
|
// src/generators/database/database-files.ts
|
|
744
774
|
async function generateDatabaseFiles(root, config, ctx) {
|
|
745
|
-
const projectRoot =
|
|
746
|
-
const templatesRoot =
|
|
775
|
+
const projectRoot = join5(root, config.projectName);
|
|
776
|
+
const templatesRoot = TEMPLATES_DIR;
|
|
747
777
|
if (config.database.provider !== "none") {
|
|
748
|
-
const envPath =
|
|
778
|
+
const envPath = join5(projectRoot, ".env.example");
|
|
749
779
|
if (config.database.provider === "mongodb") {
|
|
750
780
|
await appendEnvLine(envPath, 'MONGODB_URI=""', ctx);
|
|
751
781
|
} else {
|
|
@@ -754,91 +784,90 @@ async function generateDatabaseFiles(root, config, ctx) {
|
|
|
754
784
|
if (config.database.provider === "neon") {
|
|
755
785
|
await appendEnvLine(envPath, 'NEON_API_KEY=""', ctx);
|
|
756
786
|
await appendEnvLine(envPath, 'NEON_PROJECT_ID=""', ctx);
|
|
757
|
-
const providerDir =
|
|
787
|
+
const providerDir = join5(projectRoot, "database");
|
|
758
788
|
await ensureDir(providerDir, ctx);
|
|
759
|
-
const providerReadme = await readTextFile(
|
|
760
|
-
await writeTextFile(
|
|
789
|
+
const providerReadme = await readTextFile(join5(templatesRoot, "database", "providers", "neon.README.md"));
|
|
790
|
+
await writeTextFile(join5(providerDir, "README.md"), providerReadme, ctx);
|
|
761
791
|
}
|
|
762
792
|
if (config.database.provider === "supabase") {
|
|
763
793
|
await appendEnvLine(envPath, 'SUPABASE_URL=""', ctx);
|
|
764
794
|
await appendEnvLine(envPath, 'SUPABASE_ANON_KEY=""', ctx);
|
|
765
|
-
const providerDir =
|
|
795
|
+
const providerDir = join5(projectRoot, "database");
|
|
766
796
|
await ensureDir(providerDir, ctx);
|
|
767
|
-
const providerReadme = await readTextFile(
|
|
768
|
-
await writeTextFile(
|
|
797
|
+
const providerReadme = await readTextFile(join5(templatesRoot, "database", "providers", "supabase.README.md"));
|
|
798
|
+
await writeTextFile(join5(providerDir, "README.md"), providerReadme, ctx);
|
|
769
799
|
}
|
|
770
800
|
}
|
|
771
801
|
if (config.database.orm === "drizzle") {
|
|
772
|
-
const drizzleDir =
|
|
802
|
+
const drizzleDir = join5(projectRoot, "drizzle");
|
|
773
803
|
await ensureDir(drizzleDir, ctx);
|
|
774
|
-
const configContent = await readTextFile(
|
|
775
|
-
const schemaContent = await readTextFile(
|
|
804
|
+
const configContent = await readTextFile(join5(templatesRoot, "database", "drizzle", "drizzle.config.ts"));
|
|
805
|
+
const schemaContent = await readTextFile(join5(templatesRoot, "database", "drizzle", "schema.ts"));
|
|
776
806
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
777
|
-
const clientContent = await readTextFile(
|
|
778
|
-
const exampleContent = await readTextFile(
|
|
779
|
-
await writeTextFile(
|
|
780
|
-
await writeTextFile(
|
|
781
|
-
await writeTextFile(
|
|
782
|
-
await writeTextFile(
|
|
807
|
+
const clientContent = await readTextFile(join5(templatesRoot, "database", "drizzle", `client.${ext}`));
|
|
808
|
+
const exampleContent = await readTextFile(join5(templatesRoot, "database", "drizzle", `example.${ext}`));
|
|
809
|
+
await writeTextFile(join5(projectRoot, "drizzle.config.ts"), configContent, ctx);
|
|
810
|
+
await writeTextFile(join5(drizzleDir, "schema.ts"), schemaContent, ctx);
|
|
811
|
+
await writeTextFile(join5(drizzleDir, `client.${ext}`), clientContent, ctx);
|
|
812
|
+
await writeTextFile(join5(drizzleDir, `example.${ext}`), exampleContent, ctx);
|
|
783
813
|
}
|
|
784
814
|
if (config.database.orm === "prisma") {
|
|
785
|
-
const prismaDir =
|
|
815
|
+
const prismaDir = join5(projectRoot, "prisma");
|
|
786
816
|
await ensureDir(prismaDir, ctx);
|
|
787
|
-
const schema = await readTextFile(
|
|
788
|
-
await writeTextFile(
|
|
789
|
-
const dbDir =
|
|
817
|
+
const schema = await readTextFile(join5(templatesRoot, "database", "prisma", "schema.prisma"));
|
|
818
|
+
await writeTextFile(join5(prismaDir, "schema.prisma"), schema, ctx);
|
|
819
|
+
const dbDir = join5(projectRoot, "src", "db");
|
|
790
820
|
await ensureDir(dbDir, ctx);
|
|
791
821
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
792
|
-
const client = await readTextFile(
|
|
793
|
-
const example = await readTextFile(
|
|
794
|
-
await writeTextFile(
|
|
795
|
-
await writeTextFile(
|
|
796
|
-
const usage = await readTextFile(
|
|
797
|
-
await writeTextFile(
|
|
822
|
+
const client = await readTextFile(join5(templatesRoot, "database", "prisma", `client.${ext}`));
|
|
823
|
+
const example = await readTextFile(join5(templatesRoot, "database", "prisma", `example.${ext}`));
|
|
824
|
+
await writeTextFile(join5(dbDir, `prisma.${ext}`), client, ctx);
|
|
825
|
+
await writeTextFile(join5(dbDir, `prisma-example.${ext}`), example, ctx);
|
|
826
|
+
const usage = await readTextFile(join5(templatesRoot, "database", "usage", `prisma-users.${ext}`));
|
|
827
|
+
await writeTextFile(join5(dbDir, `users.${ext}`), usage, ctx);
|
|
798
828
|
}
|
|
799
829
|
if (config.database.orm === "mongoose") {
|
|
800
|
-
const dbDir =
|
|
830
|
+
const dbDir = join5(projectRoot, "src", "db");
|
|
801
831
|
await ensureDir(dbDir, ctx);
|
|
802
832
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
803
|
-
const connection = await readTextFile(
|
|
804
|
-
const model = await readTextFile(
|
|
805
|
-
await writeTextFile(
|
|
806
|
-
await writeTextFile(
|
|
807
|
-
const usage = await readTextFile(
|
|
808
|
-
await writeTextFile(
|
|
833
|
+
const connection = await readTextFile(join5(templatesRoot, "database", "mongoose", `connection.${ext}`));
|
|
834
|
+
const model = await readTextFile(join5(templatesRoot, "database", "mongoose", `model.${ext}`));
|
|
835
|
+
await writeTextFile(join5(dbDir, `mongoose.${ext}`), connection, ctx);
|
|
836
|
+
await writeTextFile(join5(dbDir, `mongoose-model.${ext}`), model, ctx);
|
|
837
|
+
const usage = await readTextFile(join5(templatesRoot, "database", "usage", `mongoose-users.${ext}`));
|
|
838
|
+
await writeTextFile(join5(dbDir, `users.${ext}`), usage, ctx);
|
|
809
839
|
}
|
|
810
840
|
if (config.database.orm === "typeorm") {
|
|
811
|
-
const dbDir =
|
|
841
|
+
const dbDir = join5(projectRoot, "src", "db");
|
|
812
842
|
await ensureDir(dbDir, ctx);
|
|
813
843
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
814
|
-
const template = await readTextFile(
|
|
844
|
+
const template = await readTextFile(join5(templatesRoot, "database", "typeorm", `data-source.${ext}`));
|
|
815
845
|
const typeormType = config.database.provider === "mysql" ? "mysql" : config.database.provider === "sqlite" ? "sqlite" : "postgres";
|
|
816
846
|
const migrationsPath = config.frontend.language === "ts" ? "migrations/*.ts" : "migrations/*.js";
|
|
817
847
|
const content = template.replace("{{typeormType}}", typeormType).replace("{{migrationsPath}}", migrationsPath);
|
|
818
|
-
await writeTextFile(
|
|
819
|
-
const entitiesDir =
|
|
848
|
+
await writeTextFile(join5(dbDir, `data-source.${ext}`), content, ctx);
|
|
849
|
+
const entitiesDir = join5(dbDir, "entities");
|
|
820
850
|
await ensureDir(entitiesDir, ctx);
|
|
821
|
-
const entity = await readTextFile(
|
|
822
|
-
await writeTextFile(
|
|
823
|
-
const migrationsDir =
|
|
851
|
+
const entity = await readTextFile(join5(templatesRoot, "database", "typeorm", `entity.${ext}`));
|
|
852
|
+
await writeTextFile(join5(entitiesDir, `User.${ext}`), entity, ctx);
|
|
853
|
+
const migrationsDir = join5(dbDir, "migrations");
|
|
824
854
|
await ensureDir(migrationsDir, ctx);
|
|
825
|
-
const migrationReadme = await readTextFile(
|
|
826
|
-
await writeTextFile(
|
|
827
|
-
const usage = await readTextFile(
|
|
828
|
-
await writeTextFile(
|
|
855
|
+
const migrationReadme = await readTextFile(join5(templatesRoot, "database", "typeorm", "migrations", "README.md"));
|
|
856
|
+
await writeTextFile(join5(migrationsDir, "README.md"), migrationReadme, ctx);
|
|
857
|
+
const usage = await readTextFile(join5(templatesRoot, "database", "usage", `typeorm-users.${ext}`));
|
|
858
|
+
await writeTextFile(join5(dbDir, `users.${ext}`), usage, ctx);
|
|
829
859
|
}
|
|
830
860
|
if (config.database.orm === "drizzle") {
|
|
831
|
-
const dbDir =
|
|
861
|
+
const dbDir = join5(projectRoot, "src", "db");
|
|
832
862
|
await ensureDir(dbDir, ctx);
|
|
833
863
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
834
|
-
const usage = await readTextFile(
|
|
835
|
-
await writeTextFile(
|
|
864
|
+
const usage = await readTextFile(join5(templatesRoot, "database", "usage", `drizzle-users.${ext}`));
|
|
865
|
+
await writeTextFile(join5(dbDir, `users.${ext}`), usage, ctx);
|
|
836
866
|
}
|
|
837
867
|
}
|
|
838
868
|
|
|
839
869
|
// src/generators/frontend/frontend-files.ts
|
|
840
|
-
import { join as
|
|
841
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
870
|
+
import { join as join6 } from "path";
|
|
842
871
|
|
|
843
872
|
// src/generators/templates/template-engine.ts
|
|
844
873
|
function applyTemplate(content, vars) {
|
|
@@ -851,10 +880,10 @@ function applyTemplate(content, vars) {
|
|
|
851
880
|
|
|
852
881
|
// src/generators/frontend/frontend-files.ts
|
|
853
882
|
async function generateFrontendFiles(root, config, ctx) {
|
|
854
|
-
const projectRoot =
|
|
855
|
-
const templatesRoot =
|
|
883
|
+
const projectRoot = join6(root, config.projectName);
|
|
884
|
+
const templatesRoot = TEMPLATES_DIR;
|
|
856
885
|
if (config.frontend.type === "nextjs") {
|
|
857
|
-
const appDir =
|
|
886
|
+
const appDir = join6(projectRoot, "app");
|
|
858
887
|
await ensureDir(appDir, ctx);
|
|
859
888
|
const uiCssImports = buildUiCssImports(config, "nextjs");
|
|
860
889
|
const importCss = uiCssImports.length ? uiCssImports.join("") + "\n" : "";
|
|
@@ -863,25 +892,25 @@ async function generateFrontendFiles(root, config, ctx) {
|
|
|
863
892
|
const hasProviders = hasTrpc || hasUiProvider;
|
|
864
893
|
const providersImport = hasProviders ? "import { Providers } from './providers';\n" : "";
|
|
865
894
|
const wrapChildren = hasProviders ? "<Providers>{children}</Providers>" : "{children}";
|
|
866
|
-
const layoutTemplatePath =
|
|
895
|
+
const layoutTemplatePath = join6(
|
|
867
896
|
templatesRoot,
|
|
868
897
|
"nextjs",
|
|
869
898
|
"app",
|
|
870
899
|
config.frontend.language === "ts" ? "layout.tsx" : "layout.jsx"
|
|
871
900
|
);
|
|
872
|
-
const pageTemplatePath =
|
|
901
|
+
const pageTemplatePath = join6(
|
|
873
902
|
templatesRoot,
|
|
874
903
|
"nextjs",
|
|
875
904
|
"app",
|
|
876
905
|
config.frontend.language === "ts" ? "page.tsx" : "page.jsx"
|
|
877
906
|
);
|
|
878
|
-
const actionsTemplatePath =
|
|
907
|
+
const actionsTemplatePath = join6(
|
|
879
908
|
templatesRoot,
|
|
880
909
|
"nextjs",
|
|
881
910
|
"app",
|
|
882
911
|
config.frontend.language === "ts" ? "actions.ts" : "actions.js"
|
|
883
912
|
);
|
|
884
|
-
const nextConfigTemplatePath =
|
|
913
|
+
const nextConfigTemplatePath = join6(
|
|
885
914
|
templatesRoot,
|
|
886
915
|
"nextjs",
|
|
887
916
|
config.frontend.language === "ts" ? "next.config.ts" : "next.config.js"
|
|
@@ -889,7 +918,7 @@ async function generateFrontendFiles(root, config, ctx) {
|
|
|
889
918
|
const layoutTemplate = await readTextFile(layoutTemplatePath);
|
|
890
919
|
const pageTemplate = await readTextFile(pageTemplatePath);
|
|
891
920
|
const examplesTemplate = await readTextFile(
|
|
892
|
-
|
|
921
|
+
join6(
|
|
893
922
|
templatesRoot,
|
|
894
923
|
"nextjs",
|
|
895
924
|
"app",
|
|
@@ -906,15 +935,15 @@ async function generateFrontendFiles(root, config, ctx) {
|
|
|
906
935
|
const links = buildPageLinks(config);
|
|
907
936
|
const page = applyTemplate(pageTemplate, { projectName: config.projectName, links });
|
|
908
937
|
await writeTextFile(
|
|
909
|
-
|
|
938
|
+
join6(projectRoot, config.frontend.language === "ts" ? "next.config.ts" : "next.config.js"),
|
|
910
939
|
nextConfigTemplate,
|
|
911
940
|
ctx
|
|
912
941
|
);
|
|
913
|
-
await writeTextFile(
|
|
914
|
-
await writeTextFile(
|
|
915
|
-
await writeTextFile(
|
|
942
|
+
await writeTextFile(join6(appDir, config.frontend.language === "ts" ? "layout.tsx" : "layout.jsx"), layout, ctx);
|
|
943
|
+
await writeTextFile(join6(appDir, config.frontend.language === "ts" ? "page.tsx" : "page.jsx"), page, ctx);
|
|
944
|
+
await writeTextFile(join6(appDir, config.frontend.language === "ts" ? "actions.ts" : "actions.js"), actionsTemplate, ctx);
|
|
916
945
|
if (config.api.type !== "none") {
|
|
917
|
-
const examplesDir =
|
|
946
|
+
const examplesDir = join6(appDir, "examples");
|
|
918
947
|
await ensureDir(examplesDir, ctx);
|
|
919
948
|
const { imports, components } = buildApiExamples(config, "nextjs");
|
|
920
949
|
const featureNotes = buildFeatureNotes(config);
|
|
@@ -927,7 +956,7 @@ async function generateFrontendFiles(root, config, ctx) {
|
|
|
927
956
|
uiDemoComponent
|
|
928
957
|
});
|
|
929
958
|
await writeTextFile(
|
|
930
|
-
|
|
959
|
+
join6(examplesDir, config.frontend.language === "ts" ? "page.tsx" : "page.jsx"),
|
|
931
960
|
examplesPage,
|
|
932
961
|
ctx
|
|
933
962
|
);
|
|
@@ -935,23 +964,23 @@ async function generateFrontendFiles(root, config, ctx) {
|
|
|
935
964
|
if (hasProviders) {
|
|
936
965
|
const providers = buildProvidersComponent(config, "nextjs");
|
|
937
966
|
await writeTextFile(
|
|
938
|
-
|
|
967
|
+
join6(appDir, config.frontend.language === "ts" ? "providers.tsx" : "providers.jsx"),
|
|
939
968
|
providers,
|
|
940
969
|
ctx
|
|
941
970
|
);
|
|
942
971
|
}
|
|
943
972
|
}
|
|
944
973
|
if (config.frontend.type === "vite") {
|
|
945
|
-
const srcDir =
|
|
974
|
+
const srcDir = join6(projectRoot, "src");
|
|
946
975
|
await ensureDir(srcDir, ctx);
|
|
947
976
|
const uiCssImports = buildUiCssImports(config, "vite");
|
|
948
977
|
const cssImport = uiCssImports.length ? uiCssImports.join("") : "";
|
|
949
978
|
const ext = config.frontend.language === "ts" ? "tsx" : "jsx";
|
|
950
|
-
const mainTemplatePath =
|
|
951
|
-
const appTemplatePath =
|
|
952
|
-
const indexTemplatePath =
|
|
953
|
-
const viteConfigTemplatePath =
|
|
954
|
-
const viteEnvTemplatePath =
|
|
979
|
+
const mainTemplatePath = join6(templatesRoot, "vite", config.frontend.language === "ts" ? "main.tsx" : "main.jsx");
|
|
980
|
+
const appTemplatePath = join6(templatesRoot, "vite", config.frontend.language === "ts" ? "App.tsx" : "App.jsx");
|
|
981
|
+
const indexTemplatePath = join6(templatesRoot, "vite", "index.html");
|
|
982
|
+
const viteConfigTemplatePath = join6(templatesRoot, "vite", "vite.config.ts");
|
|
983
|
+
const viteEnvTemplatePath = join6(templatesRoot, "vite", "vite-env.d.ts");
|
|
955
984
|
const mainTemplate = await readTextFile(mainTemplatePath);
|
|
956
985
|
const appTemplate = await readTextFile(appTemplatePath);
|
|
957
986
|
const indexTemplate = await readTextFile(indexTemplatePath);
|
|
@@ -984,17 +1013,17 @@ async function generateFrontendFiles(root, config, ctx) {
|
|
|
984
1013
|
uiDemoComponent
|
|
985
1014
|
});
|
|
986
1015
|
const indexHtml = applyTemplate(indexTemplate, { projectName: config.projectName, ext });
|
|
987
|
-
await writeTextFile(
|
|
988
|
-
await writeTextFile(
|
|
989
|
-
await writeTextFile(
|
|
990
|
-
await writeTextFile(
|
|
1016
|
+
await writeTextFile(join6(projectRoot, "index.html"), indexHtml, ctx);
|
|
1017
|
+
await writeTextFile(join6(projectRoot, "vite.config.ts"), viteConfigTemplate, ctx);
|
|
1018
|
+
await writeTextFile(join6(srcDir, config.frontend.language === "ts" ? "main.tsx" : "main.jsx"), main, ctx);
|
|
1019
|
+
await writeTextFile(join6(srcDir, config.frontend.language === "ts" ? "App.tsx" : "App.jsx"), app, ctx);
|
|
991
1020
|
if (config.frontend.language === "ts") {
|
|
992
|
-
await writeTextFile(
|
|
1021
|
+
await writeTextFile(join6(srcDir, "vite-env.d.ts"), viteEnvTemplate, ctx);
|
|
993
1022
|
}
|
|
994
|
-
await appendEnvLine(
|
|
1023
|
+
await appendEnvLine(join6(projectRoot, ".env.example"), 'VITE_API_URL="http://localhost:3001"', ctx);
|
|
995
1024
|
if (hasProviders) {
|
|
996
1025
|
const providers = buildProvidersComponent(config, "vite");
|
|
997
|
-
await writeTextFile(
|
|
1026
|
+
await writeTextFile(join6(srcDir, config.frontend.language === "ts" ? "providers.tsx" : "providers.jsx"), providers, ctx);
|
|
998
1027
|
}
|
|
999
1028
|
}
|
|
1000
1029
|
}
|
|
@@ -1188,70 +1217,69 @@ function buildPageLinks(config) {
|
|
|
1188
1217
|
}
|
|
1189
1218
|
|
|
1190
1219
|
// src/generators/ui/ui-files.ts
|
|
1191
|
-
import { join as
|
|
1192
|
-
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1220
|
+
import { join as join7 } from "path";
|
|
1193
1221
|
async function generateUiFiles(root, config, ctx) {
|
|
1194
|
-
const projectRoot =
|
|
1195
|
-
const templatesRoot =
|
|
1222
|
+
const projectRoot = join7(root, config.projectName);
|
|
1223
|
+
const templatesRoot = TEMPLATES_DIR;
|
|
1196
1224
|
if (config.ui.library === "tailwind") {
|
|
1197
|
-
const tailwindTemplate = await readTextFile(
|
|
1198
|
-
const postcssTemplate = await readTextFile(
|
|
1199
|
-
const stylesTemplate = await readTextFile(
|
|
1200
|
-
await writeTextFile(
|
|
1201
|
-
await writeTextFile(
|
|
1202
|
-
const stylesDir =
|
|
1225
|
+
const tailwindTemplate = await readTextFile(join7(templatesRoot, "ui", "tailwind.config.js"));
|
|
1226
|
+
const postcssTemplate = await readTextFile(join7(templatesRoot, "ui", "postcss.config.js"));
|
|
1227
|
+
const stylesTemplate = await readTextFile(join7(templatesRoot, "ui", "styles.css"));
|
|
1228
|
+
await writeTextFile(join7(projectRoot, "tailwind.config.js"), tailwindTemplate, ctx);
|
|
1229
|
+
await writeTextFile(join7(projectRoot, "postcss.config.js"), postcssTemplate, ctx);
|
|
1230
|
+
const stylesDir = join7(projectRoot, "src");
|
|
1203
1231
|
await ensureDir(stylesDir, ctx);
|
|
1204
|
-
await writeTextFile(
|
|
1232
|
+
await writeTextFile(join7(stylesDir, "styles.css"), stylesTemplate, ctx);
|
|
1205
1233
|
const demo = await readTextFile(
|
|
1206
|
-
|
|
1234
|
+
join7(templatesRoot, "ui", config.frontend.language === "ts" ? "demo-tailwind.tsx" : "demo-tailwind.jsx")
|
|
1207
1235
|
);
|
|
1208
|
-
await ensureDir(
|
|
1236
|
+
await ensureDir(join7(projectRoot, "src", "components"), ctx);
|
|
1209
1237
|
await writeTextFile(
|
|
1210
|
-
|
|
1238
|
+
join7(projectRoot, "src", "components", config.frontend.language === "ts" ? "ui-demo.tsx" : "ui-demo.jsx"),
|
|
1211
1239
|
demo,
|
|
1212
1240
|
ctx
|
|
1213
1241
|
);
|
|
1214
1242
|
}
|
|
1215
1243
|
if (config.ui.library === "shadcn") {
|
|
1216
|
-
await ensureDir(
|
|
1217
|
-
const shadcnReadme = await readTextFile(
|
|
1218
|
-
await writeTextFile(
|
|
1219
|
-
const componentsJson = await readTextFile(
|
|
1220
|
-
await writeTextFile(
|
|
1221
|
-
const srcDir =
|
|
1222
|
-
await ensureDir(
|
|
1223
|
-
await ensureDir(
|
|
1244
|
+
await ensureDir(join7(projectRoot, "components"), ctx);
|
|
1245
|
+
const shadcnReadme = await readTextFile(join7(templatesRoot, "ui", "shadcn.README.md"));
|
|
1246
|
+
await writeTextFile(join7(projectRoot, "components", "README.md"), shadcnReadme, ctx);
|
|
1247
|
+
const componentsJson = await readTextFile(join7(templatesRoot, "ui", "components.json"));
|
|
1248
|
+
await writeTextFile(join7(projectRoot, "components.json"), componentsJson, ctx);
|
|
1249
|
+
const srcDir = join7(projectRoot, "src");
|
|
1250
|
+
await ensureDir(join7(srcDir, "lib"), ctx);
|
|
1251
|
+
await ensureDir(join7(srcDir, "components", "ui"), ctx);
|
|
1224
1252
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
1225
|
-
const utilsTemplate = await readTextFile(
|
|
1226
|
-
await writeTextFile(
|
|
1253
|
+
const utilsTemplate = await readTextFile(join7(templatesRoot, "ui", `utils.${ext}`));
|
|
1254
|
+
await writeTextFile(join7(srcDir, "lib", `utils.${ext}`), utilsTemplate, ctx);
|
|
1227
1255
|
const buttonTemplate = await readTextFile(
|
|
1228
|
-
|
|
1256
|
+
join7(templatesRoot, "ui", config.frontend.language === "ts" ? "button.tsx" : "button.jsx")
|
|
1229
1257
|
);
|
|
1230
|
-
await writeTextFile(
|
|
1258
|
+
await writeTextFile(join7(srcDir, "components", "ui", config.frontend.language === "ts" ? "button.tsx" : "button.jsx"), buttonTemplate, ctx);
|
|
1231
1259
|
const demo = await readTextFile(
|
|
1232
|
-
|
|
1260
|
+
join7(templatesRoot, "ui", config.frontend.language === "ts" ? "demo-shadcn.tsx" : "demo-shadcn.jsx")
|
|
1233
1261
|
);
|
|
1234
1262
|
await writeTextFile(
|
|
1235
|
-
|
|
1263
|
+
join7(srcDir, "components", config.frontend.language === "ts" ? "ui-demo.tsx" : "ui-demo.jsx"),
|
|
1236
1264
|
demo,
|
|
1237
1265
|
ctx
|
|
1238
1266
|
);
|
|
1239
1267
|
}
|
|
1240
1268
|
if (["mui", "chakra", "mantine", "antd", "nextui"].includes(config.ui.library)) {
|
|
1241
|
-
await ensureDir(
|
|
1242
|
-
const readme = await readTextFile(
|
|
1243
|
-
await writeTextFile(
|
|
1244
|
-
const srcDir =
|
|
1269
|
+
await ensureDir(join7(projectRoot, "components"), ctx);
|
|
1270
|
+
const readme = await readTextFile(join7(templatesRoot, "ui", `${config.ui.library}.README.md`));
|
|
1271
|
+
await writeTextFile(join7(projectRoot, "components", "README.md"), readme, ctx);
|
|
1272
|
+
const srcDir = join7(projectRoot, "src");
|
|
1245
1273
|
await ensureDir(srcDir, ctx);
|
|
1246
1274
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
1247
|
-
const themeTemplate = await readTextFile(
|
|
1248
|
-
await writeTextFile(
|
|
1275
|
+
const themeTemplate = await readTextFile(join7(templatesRoot, "ui", `${config.ui.library}.theme.${ext}`));
|
|
1276
|
+
await writeTextFile(join7(srcDir, `theme.${ext}`), themeTemplate, ctx);
|
|
1249
1277
|
const demo = await readTextFile(
|
|
1250
|
-
|
|
1278
|
+
join7(templatesRoot, "ui", `demo-${config.ui.library}.${config.frontend.language === "ts" ? "tsx" : "jsx"}`)
|
|
1251
1279
|
);
|
|
1252
|
-
await ensureDir(
|
|
1280
|
+
await ensureDir(join7(srcDir, "components"), ctx);
|
|
1253
1281
|
await writeTextFile(
|
|
1254
|
-
|
|
1282
|
+
join7(srcDir, "components", config.frontend.language === "ts" ? "ui-demo.tsx" : "ui-demo.jsx"),
|
|
1255
1283
|
demo,
|
|
1256
1284
|
ctx
|
|
1257
1285
|
);
|
|
@@ -1259,54 +1287,53 @@ async function generateUiFiles(root, config, ctx) {
|
|
|
1259
1287
|
}
|
|
1260
1288
|
|
|
1261
1289
|
// src/generators/auth/auth-files.ts
|
|
1262
|
-
import { join as
|
|
1263
|
-
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1290
|
+
import { join as join8 } from "path";
|
|
1264
1291
|
async function generateAuthFiles(root, config, ctx) {
|
|
1265
|
-
const projectRoot =
|
|
1266
|
-
const templatesRoot =
|
|
1292
|
+
const projectRoot = join8(root, config.projectName);
|
|
1293
|
+
const templatesRoot = TEMPLATES_DIR;
|
|
1267
1294
|
if (config.auth.provider === "nextauth") {
|
|
1268
|
-
await appendEnvLine(
|
|
1269
|
-
await appendEnvLine(
|
|
1295
|
+
await appendEnvLine(join8(projectRoot, ".env.example"), 'NEXTAUTH_SECRET=""', ctx);
|
|
1296
|
+
await appendEnvLine(join8(projectRoot, ".env.example"), 'NEXTAUTH_URL=""', ctx);
|
|
1270
1297
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
1271
1298
|
if (config.frontend.type === "nextjs") {
|
|
1272
|
-
const routeDir =
|
|
1299
|
+
const routeDir = join8(projectRoot, "app", "api", "auth", "[...nextauth]");
|
|
1273
1300
|
await ensureDir(routeDir, ctx);
|
|
1274
|
-
const route = await readTextFile(
|
|
1275
|
-
await writeTextFile(
|
|
1301
|
+
const route = await readTextFile(join8(templatesRoot, "auth", `nextauth-route.${ext}`));
|
|
1302
|
+
await writeTextFile(join8(routeDir, config.frontend.language === "ts" ? "route.ts" : "route.js"), route, ctx);
|
|
1276
1303
|
}
|
|
1277
|
-
const authDir =
|
|
1304
|
+
const authDir = join8(projectRoot, "auth");
|
|
1278
1305
|
await ensureDir(authDir, ctx);
|
|
1279
|
-
const readme = await readTextFile(
|
|
1280
|
-
await writeTextFile(
|
|
1281
|
-
const options = await readTextFile(
|
|
1282
|
-
await writeTextFile(
|
|
1306
|
+
const readme = await readTextFile(join8(templatesRoot, "auth", "nextauth.README.md"));
|
|
1307
|
+
await writeTextFile(join8(authDir, "README.md"), readme, ctx);
|
|
1308
|
+
const options = await readTextFile(join8(templatesRoot, "auth", `nextauth-options.${ext}`));
|
|
1309
|
+
await writeTextFile(join8(authDir, `auth-options.${ext}`), options, ctx);
|
|
1283
1310
|
if (config.frontend.type === "nextjs") {
|
|
1284
|
-
const protectedDir =
|
|
1311
|
+
const protectedDir = join8(projectRoot, "app", "auth", "protected");
|
|
1285
1312
|
await ensureDir(protectedDir, ctx);
|
|
1286
1313
|
const protectedPage = await readTextFile(
|
|
1287
|
-
|
|
1314
|
+
join8(templatesRoot, "auth", `nextauth-protected-page.${ext}x`)
|
|
1288
1315
|
);
|
|
1289
|
-
await writeTextFile(
|
|
1290
|
-
const signInDir =
|
|
1316
|
+
await writeTextFile(join8(protectedDir, `page.${ext}x`), protectedPage, ctx);
|
|
1317
|
+
const signInDir = join8(projectRoot, "app", "auth", "signin");
|
|
1291
1318
|
await ensureDir(signInDir, ctx);
|
|
1292
|
-
const signInPage = await readTextFile(
|
|
1293
|
-
await writeTextFile(
|
|
1319
|
+
const signInPage = await readTextFile(join8(templatesRoot, "auth", `nextauth-signin.${ext}x`));
|
|
1320
|
+
await writeTextFile(join8(signInDir, `page.${ext}x`), signInPage, ctx);
|
|
1294
1321
|
} else {
|
|
1295
|
-
const protectedPage = await readTextFile(
|
|
1296
|
-
await writeTextFile(
|
|
1322
|
+
const protectedPage = await readTextFile(join8(templatesRoot, "auth", `nextauth-protected.${ext}x`));
|
|
1323
|
+
await writeTextFile(join8(authDir, `protected.${ext}x`), protectedPage, ctx);
|
|
1297
1324
|
}
|
|
1298
1325
|
}
|
|
1299
1326
|
if (config.auth.provider === "clerk") {
|
|
1300
|
-
await appendEnvLine(
|
|
1301
|
-
await appendEnvLine(
|
|
1302
|
-
const libDir =
|
|
1327
|
+
await appendEnvLine(join8(projectRoot, ".env.example"), 'CLERK_SECRET_KEY=""', ctx);
|
|
1328
|
+
await appendEnvLine(join8(projectRoot, ".env.example"), 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=""', ctx);
|
|
1329
|
+
const libDir = join8(projectRoot, "src", "lib");
|
|
1303
1330
|
await ensureDir(libDir, ctx);
|
|
1304
1331
|
const clerkClient = `import { clerkClient } from '@clerk/nextjs/server';
|
|
1305
1332
|
|
|
1306
1333
|
export { clerkClient };
|
|
1307
1334
|
`;
|
|
1308
1335
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
1309
|
-
await writeTextFile(
|
|
1336
|
+
await writeTextFile(join8(libDir, `clerk.${ext}`), clerkClient, ctx);
|
|
1310
1337
|
if (config.frontend.type === "nextjs") {
|
|
1311
1338
|
const middleware = `import { authMiddleware } from '@clerk/nextjs';
|
|
1312
1339
|
|
|
@@ -1316,63 +1343,63 @@ export const config = {
|
|
|
1316
1343
|
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)']
|
|
1317
1344
|
};
|
|
1318
1345
|
`;
|
|
1319
|
-
await writeTextFile(
|
|
1346
|
+
await writeTextFile(join8(projectRoot, `middleware.${ext}`), middleware, ctx);
|
|
1320
1347
|
}
|
|
1321
|
-
const authDir =
|
|
1348
|
+
const authDir = join8(projectRoot, "auth");
|
|
1322
1349
|
await ensureDir(authDir, ctx);
|
|
1323
|
-
const readme = await readTextFile(
|
|
1324
|
-
await writeTextFile(
|
|
1350
|
+
const readme = await readTextFile(join8(templatesRoot, "auth", "clerk.README.md"));
|
|
1351
|
+
await writeTextFile(join8(authDir, "README.md"), readme, ctx);
|
|
1325
1352
|
if (config.frontend.type === "nextjs") {
|
|
1326
|
-
const protectedDir =
|
|
1353
|
+
const protectedDir = join8(projectRoot, "app", "auth", "protected");
|
|
1327
1354
|
await ensureDir(protectedDir, ctx);
|
|
1328
1355
|
const protectedPage = await readTextFile(
|
|
1329
|
-
|
|
1356
|
+
join8(templatesRoot, "auth", `clerk-protected-page.${ext}x`)
|
|
1330
1357
|
);
|
|
1331
|
-
await writeTextFile(
|
|
1332
|
-
const signInDir =
|
|
1358
|
+
await writeTextFile(join8(protectedDir, `page.${ext}x`), protectedPage, ctx);
|
|
1359
|
+
const signInDir = join8(projectRoot, "app", "auth", "signin");
|
|
1333
1360
|
await ensureDir(signInDir, ctx);
|
|
1334
|
-
const signInPage = await readTextFile(
|
|
1335
|
-
await writeTextFile(
|
|
1361
|
+
const signInPage = await readTextFile(join8(templatesRoot, "auth", `clerk-signin.${ext}x`));
|
|
1362
|
+
await writeTextFile(join8(signInDir, `page.${ext}x`), signInPage, ctx);
|
|
1336
1363
|
} else {
|
|
1337
|
-
const protectedPage = await readTextFile(
|
|
1338
|
-
await writeTextFile(
|
|
1364
|
+
const protectedPage = await readTextFile(join8(templatesRoot, "auth", `clerk-protected.${ext}x`));
|
|
1365
|
+
await writeTextFile(join8(authDir, `protected.${ext}x`), protectedPage, ctx);
|
|
1339
1366
|
}
|
|
1340
1367
|
}
|
|
1341
1368
|
if (config.auth.provider === "better-auth") {
|
|
1342
|
-
await appendEnvLine(
|
|
1343
|
-
await appendEnvLine(
|
|
1369
|
+
await appendEnvLine(join8(projectRoot, ".env.example"), 'BETTER_AUTH_SECRET=""', ctx);
|
|
1370
|
+
await appendEnvLine(join8(projectRoot, ".env.example"), 'BETTER_AUTH_URL=""', ctx);
|
|
1344
1371
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
1345
|
-
const authDir =
|
|
1372
|
+
const authDir = join8(projectRoot, "auth");
|
|
1346
1373
|
await ensureDir(authDir, ctx);
|
|
1347
|
-
const readme = await readTextFile(
|
|
1348
|
-
await writeTextFile(
|
|
1349
|
-
const serverConfig = await readTextFile(
|
|
1350
|
-
await writeTextFile(
|
|
1351
|
-
const libDir =
|
|
1374
|
+
const readme = await readTextFile(join8(templatesRoot, "auth", "better-auth.README.md"));
|
|
1375
|
+
await writeTextFile(join8(authDir, "README.md"), readme, ctx);
|
|
1376
|
+
const serverConfig = await readTextFile(join8(templatesRoot, "auth", `better-auth-server.${ext}`));
|
|
1377
|
+
await writeTextFile(join8(authDir, `auth.${ext}`), serverConfig, ctx);
|
|
1378
|
+
const libDir = join8(projectRoot, "src", "lib");
|
|
1352
1379
|
await ensureDir(libDir, ctx);
|
|
1353
|
-
const clientConfig = await readTextFile(
|
|
1354
|
-
await writeTextFile(
|
|
1380
|
+
const clientConfig = await readTextFile(join8(templatesRoot, "auth", `better-auth-client.${ext}`));
|
|
1381
|
+
await writeTextFile(join8(libDir, `auth-client.${ext}`), clientConfig, ctx);
|
|
1355
1382
|
if (config.frontend.type === "nextjs") {
|
|
1356
|
-
const routeDir =
|
|
1383
|
+
const routeDir = join8(projectRoot, "app", "api", "auth", "[...all]");
|
|
1357
1384
|
await ensureDir(routeDir, ctx);
|
|
1358
|
-
const route = await readTextFile(
|
|
1359
|
-
await writeTextFile(
|
|
1360
|
-
const protectedDir =
|
|
1385
|
+
const route = await readTextFile(join8(templatesRoot, "auth", `better-auth-route.${ext}`));
|
|
1386
|
+
await writeTextFile(join8(routeDir, `route.${ext}`), route, ctx);
|
|
1387
|
+
const protectedDir = join8(projectRoot, "app", "auth", "protected");
|
|
1361
1388
|
await ensureDir(protectedDir, ctx);
|
|
1362
1389
|
const protectedPage = await readTextFile(
|
|
1363
|
-
|
|
1390
|
+
join8(templatesRoot, "auth", `better-auth-protected-page.${ext}x`)
|
|
1364
1391
|
);
|
|
1365
|
-
await writeTextFile(
|
|
1366
|
-
const signInDir =
|
|
1392
|
+
await writeTextFile(join8(protectedDir, `page.${ext}x`), protectedPage, ctx);
|
|
1393
|
+
const signInDir = join8(projectRoot, "app", "auth", "signin");
|
|
1367
1394
|
await ensureDir(signInDir, ctx);
|
|
1368
|
-
const signInPage = await readTextFile(
|
|
1369
|
-
await writeTextFile(
|
|
1395
|
+
const signInPage = await readTextFile(join8(templatesRoot, "auth", `better-auth-signin.${ext}x`));
|
|
1396
|
+
await writeTextFile(join8(signInDir, `page.${ext}x`), signInPage, ctx);
|
|
1370
1397
|
}
|
|
1371
1398
|
}
|
|
1372
1399
|
if (config.auth.provider === "supabase") {
|
|
1373
|
-
await appendEnvLine(
|
|
1374
|
-
await appendEnvLine(
|
|
1375
|
-
const libDir =
|
|
1400
|
+
await appendEnvLine(join8(projectRoot, ".env.example"), 'NEXT_PUBLIC_SUPABASE_URL=""', ctx);
|
|
1401
|
+
await appendEnvLine(join8(projectRoot, ".env.example"), 'NEXT_PUBLIC_SUPABASE_ANON_KEY=""', ctx);
|
|
1402
|
+
const libDir = join8(projectRoot, "src", "lib");
|
|
1376
1403
|
await ensureDir(libDir, ctx);
|
|
1377
1404
|
const supabaseClient = `import { createClient } from '@supabase/supabase-js';
|
|
1378
1405
|
|
|
@@ -1382,7 +1409,7 @@ const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || '';
|
|
|
1382
1409
|
export const supabase = createClient(url, key);
|
|
1383
1410
|
`;
|
|
1384
1411
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
1385
|
-
await writeTextFile(
|
|
1412
|
+
await writeTextFile(join8(libDir, `supabase.${ext}`), supabaseClient, ctx);
|
|
1386
1413
|
if (config.frontend.type === "nextjs") {
|
|
1387
1414
|
const supabaseServer = `import { createServerClient } from '@supabase/ssr';
|
|
1388
1415
|
import { cookies } from 'next/headers';
|
|
@@ -1402,47 +1429,46 @@ export function createSupabaseServerClient() {
|
|
|
1402
1429
|
);
|
|
1403
1430
|
}
|
|
1404
1431
|
`;
|
|
1405
|
-
await writeTextFile(
|
|
1432
|
+
await writeTextFile(join8(libDir, `supabase-server.${ext}`), supabaseServer, ctx);
|
|
1406
1433
|
}
|
|
1407
|
-
const authDir =
|
|
1434
|
+
const authDir = join8(projectRoot, "auth");
|
|
1408
1435
|
await ensureDir(authDir, ctx);
|
|
1409
|
-
const readme = await readTextFile(
|
|
1410
|
-
await writeTextFile(
|
|
1436
|
+
const readme = await readTextFile(join8(templatesRoot, "auth", "supabase.README.md"));
|
|
1437
|
+
await writeTextFile(join8(authDir, "README.md"), readme, ctx);
|
|
1411
1438
|
if (config.frontend.type === "nextjs") {
|
|
1412
|
-
const protectedDir =
|
|
1439
|
+
const protectedDir = join8(projectRoot, "app", "auth", "protected");
|
|
1413
1440
|
await ensureDir(protectedDir, ctx);
|
|
1414
1441
|
const protectedPage = await readTextFile(
|
|
1415
|
-
|
|
1442
|
+
join8(templatesRoot, "auth", `supabase-protected-page.${ext}x`)
|
|
1416
1443
|
);
|
|
1417
|
-
await writeTextFile(
|
|
1418
|
-
const signInDir =
|
|
1444
|
+
await writeTextFile(join8(protectedDir, `page.${ext}x`), protectedPage, ctx);
|
|
1445
|
+
const signInDir = join8(projectRoot, "app", "auth", "signin");
|
|
1419
1446
|
await ensureDir(signInDir, ctx);
|
|
1420
|
-
const signInPage = await readTextFile(
|
|
1421
|
-
await writeTextFile(
|
|
1447
|
+
const signInPage = await readTextFile(join8(templatesRoot, "auth", `supabase-signin.${ext}x`));
|
|
1448
|
+
await writeTextFile(join8(signInDir, `page.${ext}x`), signInPage, ctx);
|
|
1422
1449
|
} else {
|
|
1423
|
-
const protectedPage = await readTextFile(
|
|
1424
|
-
await writeTextFile(
|
|
1450
|
+
const protectedPage = await readTextFile(join8(templatesRoot, "auth", `supabase-protected.${ext}x`));
|
|
1451
|
+
await writeTextFile(join8(authDir, `protected.${ext}x`), protectedPage, ctx);
|
|
1425
1452
|
const signin = await readTextFile(
|
|
1426
|
-
|
|
1453
|
+
join8(templatesRoot, "auth", `supabase-vite-signin.${ext}x`)
|
|
1427
1454
|
);
|
|
1428
|
-
const authUiDir =
|
|
1455
|
+
const authUiDir = join8(projectRoot, "src", "auth");
|
|
1429
1456
|
await ensureDir(authUiDir, ctx);
|
|
1430
|
-
await writeTextFile(
|
|
1457
|
+
await writeTextFile(join8(authUiDir, `signin.${ext}x`), signin, ctx);
|
|
1431
1458
|
}
|
|
1432
1459
|
}
|
|
1433
1460
|
}
|
|
1434
1461
|
|
|
1435
1462
|
// src/generators/api/api-files.ts
|
|
1436
|
-
import { join as
|
|
1437
|
-
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
1463
|
+
import { join as join9 } from "path";
|
|
1438
1464
|
async function generateApiFiles(root, config, ctx) {
|
|
1439
|
-
const projectRoot =
|
|
1440
|
-
const templatesRoot =
|
|
1465
|
+
const projectRoot = join9(root, config.projectName);
|
|
1466
|
+
const templatesRoot = TEMPLATES_DIR;
|
|
1441
1467
|
if (config.api.type === "rest") {
|
|
1442
|
-
const apiDir =
|
|
1468
|
+
const apiDir = join9(projectRoot, "api");
|
|
1443
1469
|
await ensureDir(apiDir, ctx);
|
|
1444
1470
|
await writeTextFile(
|
|
1445
|
-
|
|
1471
|
+
join9(apiDir, "README.md"),
|
|
1446
1472
|
`# REST API
|
|
1447
1473
|
|
|
1448
1474
|
## Overview
|
|
@@ -1458,56 +1484,56 @@ async function generateApiFiles(root, config, ctx) {
|
|
|
1458
1484
|
ctx
|
|
1459
1485
|
);
|
|
1460
1486
|
if (config.frontend.type === "nextjs") {
|
|
1461
|
-
const routeDir =
|
|
1487
|
+
const routeDir = join9(projectRoot, "app", "api", "hello");
|
|
1462
1488
|
await ensureDir(routeDir, ctx);
|
|
1463
1489
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
1464
|
-
const handler = await readTextFile(
|
|
1465
|
-
await writeTextFile(
|
|
1490
|
+
const handler = await readTextFile(join9(templatesRoot, "api", "rest", `route.${ext}`));
|
|
1491
|
+
await writeTextFile(join9(routeDir, `route.${ext}`), handler, ctx);
|
|
1466
1492
|
if (config.database.orm) {
|
|
1467
|
-
const usersDir =
|
|
1493
|
+
const usersDir = join9(projectRoot, "app", "api", "users");
|
|
1468
1494
|
await ensureDir(usersDir, ctx);
|
|
1469
1495
|
const usersRoute = await readTextFile(
|
|
1470
|
-
|
|
1496
|
+
join9(templatesRoot, "api", "rest", config.frontend.language === "ts" ? "users-route.ts" : "users-route.js")
|
|
1471
1497
|
);
|
|
1472
1498
|
await writeTextFile(
|
|
1473
|
-
|
|
1499
|
+
join9(usersDir, config.frontend.language === "ts" ? "route.ts" : "route.js"),
|
|
1474
1500
|
usersRoute,
|
|
1475
1501
|
ctx
|
|
1476
1502
|
);
|
|
1477
1503
|
}
|
|
1478
1504
|
}
|
|
1479
1505
|
if (config.frontend.type === "vite") {
|
|
1480
|
-
const serverDir =
|
|
1506
|
+
const serverDir = join9(projectRoot, "src", "server");
|
|
1481
1507
|
await ensureDir(serverDir, ctx);
|
|
1482
1508
|
const serverTemplate = await readTextFile(
|
|
1483
|
-
|
|
1509
|
+
join9(templatesRoot, "api", "rest", config.frontend.language === "ts" ? "vite-server.ts" : "vite-server.js")
|
|
1484
1510
|
);
|
|
1485
1511
|
await writeTextFile(
|
|
1486
|
-
|
|
1512
|
+
join9(serverDir, config.frontend.language === "ts" ? "index.ts" : "index.js"),
|
|
1487
1513
|
serverTemplate,
|
|
1488
1514
|
ctx
|
|
1489
1515
|
);
|
|
1490
1516
|
}
|
|
1491
|
-
const clientDir =
|
|
1517
|
+
const clientDir = join9(projectRoot, "src", "api");
|
|
1492
1518
|
await ensureDir(clientDir, ctx);
|
|
1493
1519
|
const client = await readTextFile(
|
|
1494
|
-
|
|
1520
|
+
join9(templatesRoot, "api", "rest", config.frontend.language === "ts" ? "client.ts" : "client.js")
|
|
1495
1521
|
);
|
|
1496
|
-
await writeTextFile(
|
|
1522
|
+
await writeTextFile(join9(clientDir, config.frontend.language === "ts" ? "client.ts" : "client.js"), client, ctx);
|
|
1497
1523
|
const usage = await readTextFile(
|
|
1498
|
-
|
|
1524
|
+
join9(templatesRoot, "api", "rest", config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx")
|
|
1499
1525
|
);
|
|
1500
1526
|
await writeTextFile(
|
|
1501
|
-
|
|
1527
|
+
join9(clientDir, config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx"),
|
|
1502
1528
|
usage,
|
|
1503
1529
|
ctx
|
|
1504
1530
|
);
|
|
1505
1531
|
}
|
|
1506
1532
|
if (config.api.type === "trpc") {
|
|
1507
|
-
const apiDir =
|
|
1533
|
+
const apiDir = join9(projectRoot, "api");
|
|
1508
1534
|
await ensureDir(apiDir, ctx);
|
|
1509
1535
|
await writeTextFile(
|
|
1510
|
-
|
|
1536
|
+
join9(apiDir, "README.md"),
|
|
1511
1537
|
`# tRPC Setup
|
|
1512
1538
|
|
|
1513
1539
|
## Overview
|
|
@@ -1523,69 +1549,69 @@ async function generateApiFiles(root, config, ctx) {
|
|
|
1523
1549
|
ctx
|
|
1524
1550
|
);
|
|
1525
1551
|
if (config.frontend.type === "nextjs") {
|
|
1526
|
-
const trpcDir =
|
|
1552
|
+
const trpcDir = join9(projectRoot, "src", "server", "api");
|
|
1527
1553
|
await ensureDir(trpcDir, ctx);
|
|
1528
|
-
const router = await readTextFile(
|
|
1529
|
-
const appRouter = await readTextFile(
|
|
1530
|
-
await writeTextFile(
|
|
1531
|
-
await writeTextFile(
|
|
1532
|
-
const routeDir =
|
|
1554
|
+
const router = await readTextFile(join9(templatesRoot, "api", "trpc", "trpc.ts"));
|
|
1555
|
+
const appRouter = await readTextFile(join9(templatesRoot, "api", "trpc", "root.ts"));
|
|
1556
|
+
await writeTextFile(join9(trpcDir, "trpc.ts"), router, ctx);
|
|
1557
|
+
await writeTextFile(join9(trpcDir, "root.ts"), appRouter, ctx);
|
|
1558
|
+
const routeDir = join9(projectRoot, "app", "api", "trpc", "[trpc]");
|
|
1533
1559
|
await ensureDir(routeDir, ctx);
|
|
1534
|
-
const handler = await readTextFile(
|
|
1535
|
-
await writeTextFile(
|
|
1536
|
-
const clientDir =
|
|
1560
|
+
const handler = await readTextFile(join9(templatesRoot, "api", "trpc", "route.ts"));
|
|
1561
|
+
await writeTextFile(join9(routeDir, config.frontend.language === "ts" ? "route.ts" : "route.js"), handler, ctx);
|
|
1562
|
+
const clientDir = join9(projectRoot, "src", "trpc");
|
|
1537
1563
|
await ensureDir(clientDir, ctx);
|
|
1538
1564
|
const client = await readTextFile(
|
|
1539
|
-
|
|
1565
|
+
join9(templatesRoot, "api", "trpc", config.frontend.language === "ts" ? "client.ts" : "client.js")
|
|
1540
1566
|
);
|
|
1541
|
-
await writeTextFile(
|
|
1567
|
+
await writeTextFile(join9(clientDir, config.frontend.language === "ts" ? "client.ts" : "client.js"), client, ctx);
|
|
1542
1568
|
const usage = await readTextFile(
|
|
1543
|
-
|
|
1569
|
+
join9(templatesRoot, "api", "trpc", config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx")
|
|
1544
1570
|
);
|
|
1545
1571
|
await writeTextFile(
|
|
1546
|
-
|
|
1572
|
+
join9(clientDir, config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx"),
|
|
1547
1573
|
usage,
|
|
1548
1574
|
ctx
|
|
1549
1575
|
);
|
|
1550
1576
|
}
|
|
1551
1577
|
if (config.frontend.type === "vite") {
|
|
1552
|
-
const trpcDir =
|
|
1578
|
+
const trpcDir = join9(projectRoot, "src", "server", "api");
|
|
1553
1579
|
await ensureDir(trpcDir, ctx);
|
|
1554
|
-
const router = await readTextFile(
|
|
1555
|
-
const appRouter = await readTextFile(
|
|
1556
|
-
await writeTextFile(
|
|
1557
|
-
await writeTextFile(
|
|
1558
|
-
const clientDir =
|
|
1580
|
+
const router = await readTextFile(join9(templatesRoot, "api", "trpc", "trpc.ts"));
|
|
1581
|
+
const appRouter = await readTextFile(join9(templatesRoot, "api", "trpc", "root.ts"));
|
|
1582
|
+
await writeTextFile(join9(trpcDir, "trpc.ts"), router, ctx);
|
|
1583
|
+
await writeTextFile(join9(trpcDir, "root.ts"), appRouter, ctx);
|
|
1584
|
+
const clientDir = join9(projectRoot, "src", "trpc");
|
|
1559
1585
|
await ensureDir(clientDir, ctx);
|
|
1560
1586
|
const client = await readTextFile(
|
|
1561
|
-
|
|
1587
|
+
join9(templatesRoot, "api", "trpc", config.frontend.language === "ts" ? "client-vite.ts" : "client-vite.js")
|
|
1562
1588
|
);
|
|
1563
|
-
await writeTextFile(
|
|
1589
|
+
await writeTextFile(join9(clientDir, config.frontend.language === "ts" ? "client.ts" : "client.js"), client, ctx);
|
|
1564
1590
|
const usage = await readTextFile(
|
|
1565
|
-
|
|
1591
|
+
join9(templatesRoot, "api", "trpc", config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx")
|
|
1566
1592
|
);
|
|
1567
1593
|
await writeTextFile(
|
|
1568
|
-
|
|
1594
|
+
join9(clientDir, config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx"),
|
|
1569
1595
|
usage,
|
|
1570
1596
|
ctx
|
|
1571
1597
|
);
|
|
1572
|
-
const serverDir =
|
|
1598
|
+
const serverDir = join9(projectRoot, "src", "server");
|
|
1573
1599
|
await ensureDir(serverDir, ctx);
|
|
1574
1600
|
const serverTemplate = await readTextFile(
|
|
1575
|
-
|
|
1601
|
+
join9(templatesRoot, "api", "trpc", config.frontend.language === "ts" ? "vite-server.ts" : "vite-server.js")
|
|
1576
1602
|
);
|
|
1577
1603
|
await writeTextFile(
|
|
1578
|
-
|
|
1604
|
+
join9(serverDir, config.frontend.language === "ts" ? "index.ts" : "index.js"),
|
|
1579
1605
|
serverTemplate,
|
|
1580
1606
|
ctx
|
|
1581
1607
|
);
|
|
1582
1608
|
}
|
|
1583
1609
|
}
|
|
1584
1610
|
if (config.api.type === "graphql") {
|
|
1585
|
-
const apiDir =
|
|
1611
|
+
const apiDir = join9(projectRoot, "api");
|
|
1586
1612
|
await ensureDir(apiDir, ctx);
|
|
1587
1613
|
await writeTextFile(
|
|
1588
|
-
|
|
1614
|
+
join9(apiDir, "README.md"),
|
|
1589
1615
|
`# GraphQL Setup
|
|
1590
1616
|
|
|
1591
1617
|
## Overview
|
|
@@ -1601,54 +1627,54 @@ async function generateApiFiles(root, config, ctx) {
|
|
|
1601
1627
|
ctx
|
|
1602
1628
|
);
|
|
1603
1629
|
if (config.frontend.type === "nextjs") {
|
|
1604
|
-
const gqlDir =
|
|
1630
|
+
const gqlDir = join9(projectRoot, "src", "graphql");
|
|
1605
1631
|
await ensureDir(gqlDir, ctx);
|
|
1606
|
-
const schema = await readTextFile(
|
|
1607
|
-
await writeTextFile(
|
|
1608
|
-
const routeDir =
|
|
1632
|
+
const schema = await readTextFile(join9(templatesRoot, "api", "graphql", "schema.graphql"));
|
|
1633
|
+
await writeTextFile(join9(gqlDir, "schema.graphql"), schema, ctx);
|
|
1634
|
+
const routeDir = join9(projectRoot, "app", "api", "graphql");
|
|
1609
1635
|
await ensureDir(routeDir, ctx);
|
|
1610
1636
|
const gqlExt = config.frontend.language === "ts" ? "ts" : "js";
|
|
1611
|
-
const handler = await readTextFile(
|
|
1612
|
-
await writeTextFile(
|
|
1613
|
-
const clientDir =
|
|
1637
|
+
const handler = await readTextFile(join9(templatesRoot, "api", "graphql", `route.${gqlExt}`));
|
|
1638
|
+
await writeTextFile(join9(routeDir, `route.${gqlExt}`), handler, ctx);
|
|
1639
|
+
const clientDir = join9(projectRoot, "src", "graphql");
|
|
1614
1640
|
const client = await readTextFile(
|
|
1615
|
-
|
|
1641
|
+
join9(templatesRoot, "api", "graphql", config.frontend.language === "ts" ? "client.ts" : "client.js")
|
|
1616
1642
|
);
|
|
1617
|
-
await writeTextFile(
|
|
1643
|
+
await writeTextFile(join9(clientDir, config.frontend.language === "ts" ? "client.ts" : "client.js"), client, ctx);
|
|
1618
1644
|
const usage = await readTextFile(
|
|
1619
|
-
|
|
1645
|
+
join9(templatesRoot, "api", "graphql", config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx")
|
|
1620
1646
|
);
|
|
1621
1647
|
await writeTextFile(
|
|
1622
|
-
|
|
1648
|
+
join9(clientDir, config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx"),
|
|
1623
1649
|
usage,
|
|
1624
1650
|
ctx
|
|
1625
1651
|
);
|
|
1626
1652
|
}
|
|
1627
1653
|
if (config.frontend.type === "vite") {
|
|
1628
|
-
const gqlDir =
|
|
1654
|
+
const gqlDir = join9(projectRoot, "src", "graphql");
|
|
1629
1655
|
await ensureDir(gqlDir, ctx);
|
|
1630
|
-
const schema = await readTextFile(
|
|
1631
|
-
await writeTextFile(
|
|
1632
|
-
const serverDir =
|
|
1656
|
+
const schema = await readTextFile(join9(templatesRoot, "api", "graphql", "schema.graphql"));
|
|
1657
|
+
await writeTextFile(join9(gqlDir, "schema.graphql"), schema, ctx);
|
|
1658
|
+
const serverDir = join9(projectRoot, "src", "server");
|
|
1633
1659
|
await ensureDir(serverDir, ctx);
|
|
1634
1660
|
const serverTemplate = await readTextFile(
|
|
1635
|
-
|
|
1661
|
+
join9(templatesRoot, "api", "graphql", config.frontend.language === "ts" ? "vite-server.ts" : "vite-server.js")
|
|
1636
1662
|
);
|
|
1637
1663
|
await writeTextFile(
|
|
1638
|
-
|
|
1664
|
+
join9(serverDir, config.frontend.language === "ts" ? "index.ts" : "index.js"),
|
|
1639
1665
|
serverTemplate,
|
|
1640
1666
|
ctx
|
|
1641
1667
|
);
|
|
1642
|
-
const clientDir =
|
|
1668
|
+
const clientDir = join9(projectRoot, "src", "graphql");
|
|
1643
1669
|
const client = await readTextFile(
|
|
1644
|
-
|
|
1670
|
+
join9(templatesRoot, "api", "graphql", config.frontend.language === "ts" ? "client.ts" : "client.js")
|
|
1645
1671
|
);
|
|
1646
|
-
await writeTextFile(
|
|
1672
|
+
await writeTextFile(join9(clientDir, config.frontend.language === "ts" ? "client.ts" : "client.js"), client, ctx);
|
|
1647
1673
|
const usage = await readTextFile(
|
|
1648
|
-
|
|
1674
|
+
join9(templatesRoot, "api", "graphql", config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx")
|
|
1649
1675
|
);
|
|
1650
1676
|
await writeTextFile(
|
|
1651
|
-
|
|
1677
|
+
join9(clientDir, config.frontend.language === "ts" ? "client-usage.tsx" : "client-usage.jsx"),
|
|
1652
1678
|
usage,
|
|
1653
1679
|
ctx
|
|
1654
1680
|
);
|
|
@@ -1657,8 +1683,8 @@ async function generateApiFiles(root, config, ctx) {
|
|
|
1657
1683
|
}
|
|
1658
1684
|
|
|
1659
1685
|
// src/ai-agents/config-generator.ts
|
|
1660
|
-
import { join as
|
|
1661
|
-
import { existsSync as
|
|
1686
|
+
import { join as join10 } from "path";
|
|
1687
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1662
1688
|
function buildAgentContext(config) {
|
|
1663
1689
|
return {
|
|
1664
1690
|
stack: {
|
|
@@ -1840,22 +1866,22 @@ function buildHints(config) {
|
|
|
1840
1866
|
async function generateAiAgentConfigs(root, config, ctx) {
|
|
1841
1867
|
if (!config.aiAgents || config.aiAgents.length === 0) return;
|
|
1842
1868
|
const projectRoot = resolveProjectRoot(root, config);
|
|
1843
|
-
const agentsRoot =
|
|
1869
|
+
const agentsRoot = join10(projectRoot, ".ai-agents");
|
|
1844
1870
|
await ensureDir(agentsRoot, ctx);
|
|
1845
|
-
const serversRoot =
|
|
1871
|
+
const serversRoot = join10(agentsRoot, "servers");
|
|
1846
1872
|
await ensureDir(serversRoot, ctx);
|
|
1847
|
-
const protocolsRoot =
|
|
1873
|
+
const protocolsRoot = join10(agentsRoot, "protocols");
|
|
1848
1874
|
await ensureDir(protocolsRoot, ctx);
|
|
1849
1875
|
const tools = buildTools(config);
|
|
1850
1876
|
const hints = buildHints(config);
|
|
1851
1877
|
for (const agent of config.aiAgents) {
|
|
1852
|
-
const agentDir =
|
|
1878
|
+
const agentDir = join10(agentsRoot, agent);
|
|
1853
1879
|
await ensureDir(agentDir, ctx);
|
|
1854
1880
|
const ext = config.frontend.language === "ts" ? "ts" : "js";
|
|
1855
1881
|
const contextJson = JSON.stringify(buildAgentContext(config), null, 2);
|
|
1856
|
-
await writeTextFile(
|
|
1882
|
+
await writeTextFile(join10(agentDir, "context.json"), contextJson + "\n", ctx);
|
|
1857
1883
|
const toolsJson = JSON.stringify({ tools }, null, 2);
|
|
1858
|
-
await writeTextFile(
|
|
1884
|
+
await writeTextFile(join10(agentDir, "tools.json"), toolsJson + "\n", ctx);
|
|
1859
1885
|
if (agent === "claude") {
|
|
1860
1886
|
const content = JSON.stringify(
|
|
1861
1887
|
{
|
|
@@ -1869,19 +1895,19 @@ async function generateAiAgentConfigs(root, config, ctx) {
|
|
|
1869
1895
|
null,
|
|
1870
1896
|
2
|
|
1871
1897
|
);
|
|
1872
|
-
await writeTextFile(
|
|
1873
|
-
const claudeRoot =
|
|
1898
|
+
await writeTextFile(join10(agentDir, "claude_desktop_config.json"), content + "\n", ctx);
|
|
1899
|
+
const claudeRoot = join10(projectRoot, ".claude");
|
|
1874
1900
|
await ensureDir(claudeRoot, ctx);
|
|
1875
|
-
await writeTextFile(
|
|
1901
|
+
await writeTextFile(join10(claudeRoot, "claude_desktop_config.json"), content + "\n", ctx);
|
|
1876
1902
|
await writeTextFile(
|
|
1877
|
-
|
|
1903
|
+
join10(claudeRoot, "README.md"),
|
|
1878
1904
|
"StackForge generated this folder for Claude. Use claude_desktop_config.json to configure the MCP server. The server lives in .ai-agents/servers/claude/mcp-server.(ts|js).\n",
|
|
1879
1905
|
ctx
|
|
1880
1906
|
);
|
|
1881
|
-
const serverDir =
|
|
1907
|
+
const serverDir = join10(serversRoot, "claude");
|
|
1882
1908
|
await ensureDir(serverDir, ctx);
|
|
1883
1909
|
const serverContent = buildMcpServerContent(ext, tools, hints);
|
|
1884
|
-
await writeTextFile(
|
|
1910
|
+
await writeTextFile(join10(serverDir, `mcp-server.${ext}`), serverContent, ctx);
|
|
1885
1911
|
const mcpSpec = JSON.stringify(
|
|
1886
1912
|
{
|
|
1887
1913
|
name: "stackforge",
|
|
@@ -1890,17 +1916,17 @@ async function generateAiAgentConfigs(root, config, ctx) {
|
|
|
1890
1916
|
null,
|
|
1891
1917
|
2
|
|
1892
1918
|
);
|
|
1893
|
-
await writeTextFile(
|
|
1919
|
+
await writeTextFile(join10(serverDir, "mcp.json"), mcpSpec + "\n", ctx);
|
|
1894
1920
|
await writeTextFile(
|
|
1895
|
-
|
|
1921
|
+
join10(serverDir, "README.md"),
|
|
1896
1922
|
"Run the MCP server: `node mcp-server.js` (or .ts with tsx). Endpoints: /tools and /invoke.\n",
|
|
1897
1923
|
ctx
|
|
1898
1924
|
);
|
|
1899
1925
|
}
|
|
1900
1926
|
if (agent === "copilot") {
|
|
1901
1927
|
const content = JSON.stringify({ functions: buildFunctionDefinitions(tools) }, null, 2);
|
|
1902
|
-
await writeTextFile(
|
|
1903
|
-
const serverDir =
|
|
1928
|
+
await writeTextFile(join10(agentDir, "functions.json"), content + "\n", ctx);
|
|
1929
|
+
const serverDir = join10(serversRoot, "copilot");
|
|
1904
1930
|
await ensureDir(serverDir, ctx);
|
|
1905
1931
|
const serverContent = `export const functions = ${JSON.stringify(buildFunctionDefinitions(tools), null, 2)};
|
|
1906
1932
|
|
|
@@ -1908,21 +1934,21 @@ export function handleFunctionCall(name, args) {
|
|
|
1908
1934
|
return { name, args, ok: true, message: 'Implement tool logic here.' };
|
|
1909
1935
|
}
|
|
1910
1936
|
`;
|
|
1911
|
-
await writeTextFile(
|
|
1912
|
-
await writeTextFile(
|
|
1937
|
+
await writeTextFile(join10(serverDir, `functions.${ext}`), serverContent, ctx);
|
|
1938
|
+
await writeTextFile(join10(serverDir, "functions.json"), content + "\n", ctx);
|
|
1913
1939
|
}
|
|
1914
1940
|
if (agent === "codex") {
|
|
1915
1941
|
const content = JSON.stringify({ functions: buildFunctionDefinitions(tools) }, null, 2);
|
|
1916
|
-
await writeTextFile(
|
|
1917
|
-
const codexRoot =
|
|
1942
|
+
await writeTextFile(join10(agentDir, "functions.json"), content + "\n", ctx);
|
|
1943
|
+
const codexRoot = join10(projectRoot, ".codex");
|
|
1918
1944
|
await ensureDir(codexRoot, ctx);
|
|
1919
|
-
await writeTextFile(
|
|
1945
|
+
await writeTextFile(join10(codexRoot, "functions.json"), content + "\n", ctx);
|
|
1920
1946
|
await writeTextFile(
|
|
1921
|
-
|
|
1947
|
+
join10(codexRoot, "README.md"),
|
|
1922
1948
|
"StackForge generated this folder for Codex. Use functions.json for OpenAI function calling integrations.\n",
|
|
1923
1949
|
ctx
|
|
1924
1950
|
);
|
|
1925
|
-
const serverDir =
|
|
1951
|
+
const serverDir = join10(serversRoot, "codex");
|
|
1926
1952
|
await ensureDir(serverDir, ctx);
|
|
1927
1953
|
const serverContent = `export const functions = ${JSON.stringify(buildFunctionDefinitions(tools), null, 2)};
|
|
1928
1954
|
|
|
@@ -1930,13 +1956,13 @@ export function handleFunctionCall(name, args) {
|
|
|
1930
1956
|
return { name, args, ok: true, message: 'Implement tool logic here.' };
|
|
1931
1957
|
}
|
|
1932
1958
|
`;
|
|
1933
|
-
await writeTextFile(
|
|
1934
|
-
await writeTextFile(
|
|
1959
|
+
await writeTextFile(join10(serverDir, `functions.${ext}`), serverContent, ctx);
|
|
1960
|
+
await writeTextFile(join10(serverDir, "functions.json"), content + "\n", ctx);
|
|
1935
1961
|
}
|
|
1936
1962
|
if (agent === "gemini") {
|
|
1937
1963
|
const content = JSON.stringify({ functions: buildFunctionDefinitions(tools) }, null, 2);
|
|
1938
|
-
await writeTextFile(
|
|
1939
|
-
const serverDir =
|
|
1964
|
+
await writeTextFile(join10(agentDir, "function_declarations.json"), content + "\n", ctx);
|
|
1965
|
+
const serverDir = join10(serversRoot, "gemini");
|
|
1940
1966
|
await ensureDir(serverDir, ctx);
|
|
1941
1967
|
const serverContent = `export const functions = ${JSON.stringify(buildFunctionDefinitions(tools), null, 2)};
|
|
1942
1968
|
|
|
@@ -1944,46 +1970,46 @@ export function handleFunctionCall(name, args) {
|
|
|
1944
1970
|
return { name, args, ok: true, message: 'Implement tool logic here.' };
|
|
1945
1971
|
}
|
|
1946
1972
|
`;
|
|
1947
|
-
await writeTextFile(
|
|
1948
|
-
await writeTextFile(
|
|
1973
|
+
await writeTextFile(join10(serverDir, `functions.${ext}`), serverContent, ctx);
|
|
1974
|
+
await writeTextFile(join10(serverDir, "function_declarations.json"), content + "\n", ctx);
|
|
1949
1975
|
}
|
|
1950
1976
|
if (agent === "cursor") {
|
|
1951
1977
|
const content = `# Cursor rules
|
|
1952
1978
|
# See context.json for project stack details
|
|
1953
1979
|
`;
|
|
1954
|
-
await writeTextFile(
|
|
1955
|
-
await writeTextFile(
|
|
1956
|
-
const cursorRoot =
|
|
1980
|
+
await writeTextFile(join10(agentDir, ".cursorrules"), content, ctx);
|
|
1981
|
+
await writeTextFile(join10(projectRoot, ".cursorrules"), content, ctx);
|
|
1982
|
+
const cursorRoot = join10(projectRoot, ".cursor");
|
|
1957
1983
|
await ensureDir(cursorRoot, ctx);
|
|
1958
1984
|
const extensions = JSON.stringify({ recommendations: ["cursor.cursor"] }, null, 2);
|
|
1959
|
-
await writeTextFile(
|
|
1960
|
-
const serverDir =
|
|
1985
|
+
await writeTextFile(join10(cursorRoot, "extensions.json"), extensions + "\n", ctx);
|
|
1986
|
+
const serverDir = join10(serversRoot, "cursor");
|
|
1961
1987
|
await ensureDir(serverDir, ctx);
|
|
1962
1988
|
const serverContent = `# Cursor uses .cursorrules for guidance.
|
|
1963
1989
|
`;
|
|
1964
|
-
await writeTextFile(
|
|
1990
|
+
await writeTextFile(join10(serverDir, "README.md"), serverContent, ctx);
|
|
1965
1991
|
}
|
|
1966
1992
|
if (agent === "codeium") {
|
|
1967
1993
|
const content = JSON.stringify({ protocol: "lsp", tools, hints }, null, 2);
|
|
1968
|
-
await writeTextFile(
|
|
1994
|
+
await writeTextFile(join10(agentDir, "server-config.json"), content + "\n", ctx);
|
|
1969
1995
|
}
|
|
1970
1996
|
if (agent === "windsurf") {
|
|
1971
|
-
const windsurfRoot =
|
|
1997
|
+
const windsurfRoot = join10(projectRoot, ".windsurf");
|
|
1972
1998
|
await ensureDir(windsurfRoot, ctx);
|
|
1973
1999
|
const content = JSON.stringify({ protocol: "cascade", tools, hints }, null, 2);
|
|
1974
|
-
await writeTextFile(
|
|
2000
|
+
await writeTextFile(join10(windsurfRoot, "cascade.json"), content + "\n", ctx);
|
|
1975
2001
|
}
|
|
1976
2002
|
if (agent === "tabnine") {
|
|
1977
|
-
const tabnineRoot =
|
|
2003
|
+
const tabnineRoot = join10(projectRoot, ".tabnine");
|
|
1978
2004
|
await ensureDir(tabnineRoot, ctx);
|
|
1979
2005
|
const content = JSON.stringify({ tools, hints }, null, 2);
|
|
1980
|
-
await writeTextFile(
|
|
2006
|
+
await writeTextFile(join10(tabnineRoot, "config.json"), content + "\n", ctx);
|
|
1981
2007
|
}
|
|
1982
2008
|
}
|
|
1983
2009
|
const protocolTools = tools;
|
|
1984
2010
|
const openAiFunctions = buildFunctionDefinitions(protocolTools);
|
|
1985
2011
|
const openAiSchema = JSON.stringify({ functions: openAiFunctions }, null, 2);
|
|
1986
|
-
await writeTextFile(
|
|
2012
|
+
await writeTextFile(join10(protocolsRoot, "openai-functions.json"), openAiSchema + "\n", ctx);
|
|
1987
2013
|
const lspSchema = JSON.stringify(
|
|
1988
2014
|
{
|
|
1989
2015
|
version: "0.1",
|
|
@@ -1994,16 +2020,16 @@ export function handleFunctionCall(name, args) {
|
|
|
1994
2020
|
null,
|
|
1995
2021
|
2
|
|
1996
2022
|
);
|
|
1997
|
-
await writeTextFile(
|
|
1998
|
-
const docsDir =
|
|
2023
|
+
await writeTextFile(join10(protocolsRoot, "lsp.json"), lspSchema + "\n", ctx);
|
|
2024
|
+
const docsDir = join10(projectRoot, "docs");
|
|
1999
2025
|
await ensureDir(docsDir, ctx);
|
|
2000
2026
|
const aiDoc = buildAiAgentsDoc(config);
|
|
2001
|
-
await writeTextFile(
|
|
2027
|
+
await writeTextFile(join10(docsDir, "AI_AGENTS.md"), aiDoc + "\n", ctx);
|
|
2002
2028
|
}
|
|
2003
2029
|
function resolveProjectRoot(root, config) {
|
|
2004
|
-
const candidate =
|
|
2005
|
-
if (
|
|
2006
|
-
return
|
|
2030
|
+
const candidate = join10(root, "stackforge.json");
|
|
2031
|
+
if (existsSync3(candidate)) return root;
|
|
2032
|
+
return join10(root, config.projectName);
|
|
2007
2033
|
}
|
|
2008
2034
|
function buildAiAgentsDoc(config) {
|
|
2009
2035
|
const agentsList = config.aiAgents.map((agent) => `- ${agent}`).join("\n");
|
|
@@ -2113,73 +2139,72 @@ server.listen(port, () => {
|
|
|
2113
2139
|
}
|
|
2114
2140
|
|
|
2115
2141
|
// src/generators/features/feature-files.ts
|
|
2116
|
-
import { join as
|
|
2117
|
-
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
2142
|
+
import { join as join11 } from "path";
|
|
2118
2143
|
async function generateFeatureFiles(root, config, ctx) {
|
|
2119
|
-
const projectRoot =
|
|
2120
|
-
const templatesRoot =
|
|
2121
|
-
const libDir =
|
|
2144
|
+
const projectRoot = join11(root, config.projectName);
|
|
2145
|
+
const templatesRoot = TEMPLATES_DIR;
|
|
2146
|
+
const libDir = join11(projectRoot, "src", "lib");
|
|
2122
2147
|
const isTs = config.frontend.language === "ts";
|
|
2123
2148
|
if (config.features.includes("email")) {
|
|
2124
|
-
const dir =
|
|
2149
|
+
const dir = join11(projectRoot, "features", "email");
|
|
2125
2150
|
await ensureDir(dir, ctx);
|
|
2126
2151
|
await ensureDir(libDir, ctx);
|
|
2127
|
-
const readme = await readTextFile(
|
|
2152
|
+
const readme = await readTextFile(join11(templatesRoot, "features", "email", "README.md"));
|
|
2128
2153
|
const resendClient = await readTextFile(
|
|
2129
|
-
|
|
2154
|
+
join11(templatesRoot, "features", "email", isTs ? "resend.ts" : "resend.js")
|
|
2130
2155
|
);
|
|
2131
|
-
await writeTextFile(
|
|
2132
|
-
await writeTextFile(
|
|
2133
|
-
await appendEnvLine(
|
|
2156
|
+
await writeTextFile(join11(dir, "README.md"), readme, ctx);
|
|
2157
|
+
await writeTextFile(join11(libDir, isTs ? "resend.ts" : "resend.js"), resendClient, ctx);
|
|
2158
|
+
await appendEnvLine(join11(projectRoot, ".env.example"), 'RESEND_API_KEY=""', ctx);
|
|
2134
2159
|
}
|
|
2135
2160
|
if (config.features.includes("storage")) {
|
|
2136
|
-
const dir =
|
|
2161
|
+
const dir = join11(projectRoot, "features", "storage");
|
|
2137
2162
|
await ensureDir(dir, ctx);
|
|
2138
2163
|
await ensureDir(libDir, ctx);
|
|
2139
|
-
const readme = await readTextFile(
|
|
2140
|
-
await writeTextFile(
|
|
2164
|
+
const readme = await readTextFile(join11(templatesRoot, "features", "storage", "README.md"));
|
|
2165
|
+
await writeTextFile(join11(dir, "README.md"), readme, ctx);
|
|
2141
2166
|
const storageClient = await readTextFile(
|
|
2142
|
-
|
|
2167
|
+
join11(templatesRoot, "features", "storage", isTs ? "storage.ts" : "storage.js")
|
|
2143
2168
|
);
|
|
2144
|
-
await writeTextFile(
|
|
2145
|
-
await appendEnvLine(
|
|
2169
|
+
await writeTextFile(join11(libDir, isTs ? "storage.ts" : "storage.js"), storageClient, ctx);
|
|
2170
|
+
await appendEnvLine(join11(projectRoot, ".env.example"), 'CLOUDINARY_URL=""', ctx);
|
|
2146
2171
|
}
|
|
2147
2172
|
if (config.features.includes("payments")) {
|
|
2148
|
-
const dir =
|
|
2173
|
+
const dir = join11(projectRoot, "features", "payments");
|
|
2149
2174
|
await ensureDir(dir, ctx);
|
|
2150
2175
|
await ensureDir(libDir, ctx);
|
|
2151
|
-
const readme = await readTextFile(
|
|
2176
|
+
const readme = await readTextFile(join11(templatesRoot, "features", "payments", "README.md"));
|
|
2152
2177
|
const stripeClient = await readTextFile(
|
|
2153
|
-
|
|
2178
|
+
join11(templatesRoot, "features", "payments", isTs ? "stripe.ts" : "stripe.js")
|
|
2154
2179
|
);
|
|
2155
|
-
await writeTextFile(
|
|
2156
|
-
await writeTextFile(
|
|
2157
|
-
await appendEnvLine(
|
|
2180
|
+
await writeTextFile(join11(dir, "README.md"), readme, ctx);
|
|
2181
|
+
await writeTextFile(join11(libDir, isTs ? "stripe.ts" : "stripe.js"), stripeClient, ctx);
|
|
2182
|
+
await appendEnvLine(join11(projectRoot, ".env.example"), 'STRIPE_SECRET_KEY=""', ctx);
|
|
2158
2183
|
}
|
|
2159
2184
|
if (config.features.includes("analytics")) {
|
|
2160
|
-
const dir =
|
|
2185
|
+
const dir = join11(projectRoot, "features", "analytics");
|
|
2161
2186
|
await ensureDir(dir, ctx);
|
|
2162
2187
|
await ensureDir(libDir, ctx);
|
|
2163
|
-
const readme = await readTextFile(
|
|
2188
|
+
const readme = await readTextFile(join11(templatesRoot, "features", "analytics", "README.md"));
|
|
2164
2189
|
const client = await readTextFile(
|
|
2165
|
-
|
|
2190
|
+
join11(templatesRoot, "features", "analytics", isTs ? "posthog.ts" : "posthog.js")
|
|
2166
2191
|
);
|
|
2167
|
-
await writeTextFile(
|
|
2168
|
-
await writeTextFile(
|
|
2169
|
-
await appendEnvLine(
|
|
2170
|
-
await appendEnvLine(
|
|
2192
|
+
await writeTextFile(join11(dir, "README.md"), readme, ctx);
|
|
2193
|
+
await writeTextFile(join11(libDir, isTs ? "posthog.ts" : "posthog.js"), client, ctx);
|
|
2194
|
+
await appendEnvLine(join11(projectRoot, ".env.example"), 'NEXT_PUBLIC_POSTHOG_KEY=""', ctx);
|
|
2195
|
+
await appendEnvLine(join11(projectRoot, ".env.example"), 'NEXT_PUBLIC_POSTHOG_HOST=""', ctx);
|
|
2171
2196
|
}
|
|
2172
2197
|
if (config.features.includes("error-tracking")) {
|
|
2173
|
-
const dir =
|
|
2198
|
+
const dir = join11(projectRoot, "features", "error-tracking");
|
|
2174
2199
|
await ensureDir(dir, ctx);
|
|
2175
2200
|
await ensureDir(libDir, ctx);
|
|
2176
|
-
const readme = await readTextFile(
|
|
2201
|
+
const readme = await readTextFile(join11(templatesRoot, "features", "error-tracking", "README.md"));
|
|
2177
2202
|
const client = await readTextFile(
|
|
2178
|
-
|
|
2203
|
+
join11(templatesRoot, "features", "error-tracking", isTs ? "sentry.ts" : "sentry.js")
|
|
2179
2204
|
);
|
|
2180
|
-
await writeTextFile(
|
|
2181
|
-
await writeTextFile(
|
|
2182
|
-
await appendEnvLine(
|
|
2205
|
+
await writeTextFile(join11(dir, "README.md"), readme, ctx);
|
|
2206
|
+
await writeTextFile(join11(libDir, isTs ? "sentry.ts" : "sentry.js"), client, ctx);
|
|
2207
|
+
await appendEnvLine(join11(projectRoot, ".env.example"), 'SENTRY_DSN=""', ctx);
|
|
2183
2208
|
}
|
|
2184
2209
|
}
|
|
2185
2210
|
|
|
@@ -2316,7 +2341,7 @@ function runInstall(pm, cwd) {
|
|
|
2316
2341
|
}
|
|
2317
2342
|
|
|
2318
2343
|
// src/cli/commands/create.ts
|
|
2319
|
-
import { join as
|
|
2344
|
+
import { join as join12, resolve } from "path";
|
|
2320
2345
|
function parseCsv(input) {
|
|
2321
2346
|
if (!input) return void 0;
|
|
2322
2347
|
return input.split(",").map((a) => a.trim()).filter(Boolean);
|
|
@@ -2341,7 +2366,7 @@ var createCommand = new Command("create").argument("[project-name]", "name of th
|
|
|
2341
2366
|
const outDir = options.outDir ? resolve(options.outDir) : process.cwd();
|
|
2342
2367
|
await runGenerators(outDir, config, ctx);
|
|
2343
2368
|
if (options.install !== false && !options.dryRun) {
|
|
2344
|
-
const projectRoot =
|
|
2369
|
+
const projectRoot = join12(outDir, config.projectName);
|
|
2345
2370
|
logger.info("Installing dependencies...");
|
|
2346
2371
|
await runInstall(config.packageManager, projectRoot);
|
|
2347
2372
|
}
|
|
@@ -2519,7 +2544,7 @@ async function syncPackageJson(path, oldConfig, newConfig) {
|
|
|
2519
2544
|
}
|
|
2520
2545
|
|
|
2521
2546
|
// src/cli/commands/add.ts
|
|
2522
|
-
import { dirname as
|
|
2547
|
+
import { dirname as dirname4, join as join13 } from "path";
|
|
2523
2548
|
function parseFeature(feature) {
|
|
2524
2549
|
const parts = feature.split(":");
|
|
2525
2550
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
@@ -2539,14 +2564,14 @@ var addCommand = new Command3("add").argument("<feature>", "feature to add (cate
|
|
|
2539
2564
|
validateDependencies(next);
|
|
2540
2565
|
await writeProjectConfig(cwd, next);
|
|
2541
2566
|
await syncPackageJson(`${cwd}/package.json`, current, next);
|
|
2542
|
-
const root =
|
|
2567
|
+
const root = dirname4(cwd);
|
|
2543
2568
|
if (category === "ui") await generateUiFiles(root, next);
|
|
2544
2569
|
if (category === "database" || category === "orm") await generateDatabaseFiles(root, next);
|
|
2545
2570
|
if (category === "auth") await generateAuthFiles(root, next);
|
|
2546
2571
|
if (category === "api") await generateApiFiles(root, next);
|
|
2547
2572
|
if (category === "feature") await generateFeatureFiles(root, next);
|
|
2548
2573
|
const readme = buildProjectReadme(next);
|
|
2549
|
-
await writeTextFile(
|
|
2574
|
+
await writeTextFile(join13(cwd, "README.md"), readme + "\n");
|
|
2550
2575
|
logger.info(`Added ${feature}`);
|
|
2551
2576
|
} catch (err) {
|
|
2552
2577
|
logger.error(err instanceof Error ? err.message : String(err));
|
|
@@ -2558,188 +2583,188 @@ var addCommand = new Command3("add").argument("<feature>", "feature to add (cate
|
|
|
2558
2583
|
import { Command as Command4 } from "commander";
|
|
2559
2584
|
|
|
2560
2585
|
// src/utils/feature-cleanup.ts
|
|
2561
|
-
import { join as
|
|
2586
|
+
import { join as join14 } from "path";
|
|
2562
2587
|
async function cleanupFeature(cwd, config, category, value) {
|
|
2563
2588
|
const root = cwd;
|
|
2564
2589
|
if (category === "ui") {
|
|
2565
2590
|
if (config.ui.library === "tailwind") {
|
|
2566
|
-
await removePath(
|
|
2567
|
-
await removePath(
|
|
2568
|
-
await removePath(
|
|
2569
|
-
await removePath(
|
|
2570
|
-
await removePath(
|
|
2591
|
+
await removePath(join14(root, "tailwind.config.js"));
|
|
2592
|
+
await removePath(join14(root, "postcss.config.js"));
|
|
2593
|
+
await removePath(join14(root, "src", "styles.css"));
|
|
2594
|
+
await removePath(join14(root, "src", "components", "ui-demo.tsx"));
|
|
2595
|
+
await removePath(join14(root, "src", "components", "ui-demo.jsx"));
|
|
2571
2596
|
}
|
|
2572
2597
|
if (config.ui.library === "shadcn") {
|
|
2573
|
-
await removePath(
|
|
2574
|
-
await removePath(
|
|
2575
|
-
await removePath(
|
|
2576
|
-
await removePath(
|
|
2577
|
-
await removePath(
|
|
2578
|
-
await removePath(
|
|
2579
|
-
await removePath(
|
|
2598
|
+
await removePath(join14(root, "components"));
|
|
2599
|
+
await removePath(join14(root, "components.json"));
|
|
2600
|
+
await removePath(join14(root, "src", "lib", "utils.ts"));
|
|
2601
|
+
await removePath(join14(root, "src", "lib", "utils.js"));
|
|
2602
|
+
await removePath(join14(root, "src", "components", "ui"));
|
|
2603
|
+
await removePath(join14(root, "src", "components", "ui-demo.tsx"));
|
|
2604
|
+
await removePath(join14(root, "src", "components", "ui-demo.jsx"));
|
|
2580
2605
|
}
|
|
2581
2606
|
if (config.ui.library === "mui" || config.ui.library === "chakra" || config.ui.library === "mantine" || config.ui.library === "antd" || config.ui.library === "nextui") {
|
|
2582
|
-
await removePath(
|
|
2583
|
-
await removePath(
|
|
2584
|
-
await removePath(
|
|
2585
|
-
await removePath(
|
|
2586
|
-
await removePath(
|
|
2607
|
+
await removePath(join14(root, "components"));
|
|
2608
|
+
await removePath(join14(root, "src", "theme.ts"));
|
|
2609
|
+
await removePath(join14(root, "src", "theme.js"));
|
|
2610
|
+
await removePath(join14(root, "src", "components", "ui-demo.tsx"));
|
|
2611
|
+
await removePath(join14(root, "src", "components", "ui-demo.jsx"));
|
|
2587
2612
|
}
|
|
2588
2613
|
}
|
|
2589
2614
|
if (category === "auth") {
|
|
2590
2615
|
if (config.auth.provider === "nextauth") {
|
|
2591
|
-
await removePath(
|
|
2592
|
-
await removePath(
|
|
2593
|
-
await removePath(
|
|
2594
|
-
await removeEnvKey(
|
|
2595
|
-
await removeEnvKey(
|
|
2616
|
+
await removePath(join14(root, "app", "api", "auth"));
|
|
2617
|
+
await removePath(join14(root, "app", "auth", "protected"));
|
|
2618
|
+
await removePath(join14(root, "app", "auth", "signin"));
|
|
2619
|
+
await removeEnvKey(join14(root, ".env.example"), "NEXTAUTH_SECRET");
|
|
2620
|
+
await removeEnvKey(join14(root, ".env.example"), "NEXTAUTH_URL");
|
|
2596
2621
|
}
|
|
2597
2622
|
if (config.auth.provider === "clerk") {
|
|
2598
|
-
await removePath(
|
|
2599
|
-
await removePath(
|
|
2600
|
-
await removePath(
|
|
2601
|
-
await removePath(
|
|
2602
|
-
await removePath(
|
|
2603
|
-
await removePath(
|
|
2604
|
-
await removeEnvKey(
|
|
2605
|
-
await removeEnvKey(
|
|
2623
|
+
await removePath(join14(root, "middleware.ts"));
|
|
2624
|
+
await removePath(join14(root, "middleware.js"));
|
|
2625
|
+
await removePath(join14(root, "src", "lib", "clerk.ts"));
|
|
2626
|
+
await removePath(join14(root, "src", "lib", "clerk.js"));
|
|
2627
|
+
await removePath(join14(root, "app", "auth", "protected"));
|
|
2628
|
+
await removePath(join14(root, "app", "auth", "signin"));
|
|
2629
|
+
await removeEnvKey(join14(root, ".env.example"), "CLERK_SECRET_KEY");
|
|
2630
|
+
await removeEnvKey(join14(root, ".env.example"), "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY");
|
|
2606
2631
|
}
|
|
2607
2632
|
if (config.auth.provider === "supabase") {
|
|
2608
|
-
await removePath(
|
|
2609
|
-
await removePath(
|
|
2610
|
-
await removePath(
|
|
2611
|
-
await removePath(
|
|
2612
|
-
await removePath(
|
|
2613
|
-
await removePath(
|
|
2614
|
-
await removePath(
|
|
2615
|
-
await removePath(
|
|
2616
|
-
await removeEnvKey(
|
|
2617
|
-
await removeEnvKey(
|
|
2618
|
-
}
|
|
2619
|
-
await removePath(
|
|
2633
|
+
await removePath(join14(root, "src", "lib", "supabase.ts"));
|
|
2634
|
+
await removePath(join14(root, "src", "lib", "supabase.js"));
|
|
2635
|
+
await removePath(join14(root, "src", "lib", "supabase-server.ts"));
|
|
2636
|
+
await removePath(join14(root, "src", "lib", "supabase-server.js"));
|
|
2637
|
+
await removePath(join14(root, "app", "auth", "protected"));
|
|
2638
|
+
await removePath(join14(root, "app", "auth", "signin"));
|
|
2639
|
+
await removePath(join14(root, "src", "auth", "signin.tsx"));
|
|
2640
|
+
await removePath(join14(root, "src", "auth", "signin.jsx"));
|
|
2641
|
+
await removeEnvKey(join14(root, ".env.example"), "NEXT_PUBLIC_SUPABASE_URL");
|
|
2642
|
+
await removeEnvKey(join14(root, ".env.example"), "NEXT_PUBLIC_SUPABASE_ANON_KEY");
|
|
2643
|
+
}
|
|
2644
|
+
await removePath(join14(root, "auth"));
|
|
2620
2645
|
}
|
|
2621
2646
|
if (category === "api") {
|
|
2622
2647
|
if (config.api.type === "rest") {
|
|
2623
|
-
await removePath(
|
|
2624
|
-
await removePath(
|
|
2625
|
-
await removePath(
|
|
2626
|
-
await removePath(
|
|
2627
|
-
await removePath(
|
|
2628
|
-
await removePath(
|
|
2629
|
-
await removePath(
|
|
2648
|
+
await removePath(join14(root, "app", "api", "hello"));
|
|
2649
|
+
await removePath(join14(root, "app", "api", "users"));
|
|
2650
|
+
await removePath(join14(root, "app", "examples"));
|
|
2651
|
+
await removePath(join14(root, "src", "server"));
|
|
2652
|
+
await removePath(join14(root, "src", "api"));
|
|
2653
|
+
await removePath(join14(root, "src", "api", "client-usage.tsx"));
|
|
2654
|
+
await removePath(join14(root, "src", "api", "client-usage.jsx"));
|
|
2630
2655
|
}
|
|
2631
2656
|
if (config.api.type === "trpc") {
|
|
2632
|
-
await removePath(
|
|
2633
|
-
await removePath(
|
|
2634
|
-
await removePath(
|
|
2635
|
-
await removePath(
|
|
2636
|
-
await removePath(
|
|
2637
|
-
await removePath(
|
|
2638
|
-
await removePath(
|
|
2657
|
+
await removePath(join14(root, "app", "api", "trpc"));
|
|
2658
|
+
await removePath(join14(root, "app", "examples"));
|
|
2659
|
+
await removePath(join14(root, "src", "server", "api"));
|
|
2660
|
+
await removePath(join14(root, "src", "trpc"));
|
|
2661
|
+
await removePath(join14(root, "src", "trpc", "client-usage.tsx"));
|
|
2662
|
+
await removePath(join14(root, "src", "trpc", "client-usage.jsx"));
|
|
2663
|
+
await removePath(join14(root, "src", "server"));
|
|
2639
2664
|
}
|
|
2640
2665
|
if (config.api.type === "graphql") {
|
|
2641
|
-
await removePath(
|
|
2642
|
-
await removePath(
|
|
2643
|
-
await removePath(
|
|
2644
|
-
await removePath(
|
|
2645
|
-
await removePath(
|
|
2646
|
-
await removePath(
|
|
2666
|
+
await removePath(join14(root, "app", "api", "graphql"));
|
|
2667
|
+
await removePath(join14(root, "app", "examples"));
|
|
2668
|
+
await removePath(join14(root, "src", "graphql"));
|
|
2669
|
+
await removePath(join14(root, "src", "server"));
|
|
2670
|
+
await removePath(join14(root, "src", "graphql", "client-usage.tsx"));
|
|
2671
|
+
await removePath(join14(root, "src", "graphql", "client-usage.jsx"));
|
|
2647
2672
|
}
|
|
2648
|
-
await removePath(
|
|
2673
|
+
await removePath(join14(root, "api"));
|
|
2649
2674
|
}
|
|
2650
2675
|
if (category === "database") {
|
|
2651
2676
|
if (config.database.orm === "drizzle") {
|
|
2652
|
-
await removePath(
|
|
2653
|
-
await removePath(
|
|
2677
|
+
await removePath(join14(root, "drizzle.config.ts"));
|
|
2678
|
+
await removePath(join14(root, "drizzle"));
|
|
2654
2679
|
}
|
|
2655
2680
|
if (config.database.orm === "prisma") {
|
|
2656
|
-
await removePath(
|
|
2657
|
-
await removePath(
|
|
2658
|
-
await removePath(
|
|
2659
|
-
await removePath(
|
|
2660
|
-
await removePath(
|
|
2681
|
+
await removePath(join14(root, "prisma"));
|
|
2682
|
+
await removePath(join14(root, "src", "db", "prisma.ts"));
|
|
2683
|
+
await removePath(join14(root, "src", "db", "prisma.js"));
|
|
2684
|
+
await removePath(join14(root, "src", "db", "prisma-example.ts"));
|
|
2685
|
+
await removePath(join14(root, "src", "db", "prisma-example.js"));
|
|
2661
2686
|
}
|
|
2662
2687
|
if (config.database.orm === "mongoose") {
|
|
2663
|
-
await removePath(
|
|
2664
|
-
await removePath(
|
|
2665
|
-
await removePath(
|
|
2666
|
-
await removePath(
|
|
2688
|
+
await removePath(join14(root, "src", "db", "mongoose.ts"));
|
|
2689
|
+
await removePath(join14(root, "src", "db", "mongoose.js"));
|
|
2690
|
+
await removePath(join14(root, "src", "db", "mongoose-model.ts"));
|
|
2691
|
+
await removePath(join14(root, "src", "db", "mongoose-model.js"));
|
|
2667
2692
|
}
|
|
2668
2693
|
if (config.database.orm === "typeorm") {
|
|
2669
|
-
await removePath(
|
|
2670
|
-
await removePath(
|
|
2671
|
-
await removePath(
|
|
2672
|
-
await removePath(
|
|
2694
|
+
await removePath(join14(root, "src", "db", "data-source.ts"));
|
|
2695
|
+
await removePath(join14(root, "src", "db", "data-source.js"));
|
|
2696
|
+
await removePath(join14(root, "src", "db", "entities"));
|
|
2697
|
+
await removePath(join14(root, "src", "db", "migrations"));
|
|
2673
2698
|
}
|
|
2674
|
-
await removeEnvKey(
|
|
2675
|
-
await removeEnvKey(
|
|
2676
|
-
await removeEnvKey(
|
|
2677
|
-
await removeEnvKey(
|
|
2678
|
-
await removeEnvKey(
|
|
2699
|
+
await removeEnvKey(join14(root, ".env.example"), "DATABASE_URL");
|
|
2700
|
+
await removeEnvKey(join14(root, ".env.example"), "NEON_API_KEY");
|
|
2701
|
+
await removeEnvKey(join14(root, ".env.example"), "NEON_PROJECT_ID");
|
|
2702
|
+
await removeEnvKey(join14(root, ".env.example"), "SUPABASE_URL");
|
|
2703
|
+
await removeEnvKey(join14(root, ".env.example"), "SUPABASE_ANON_KEY");
|
|
2679
2704
|
}
|
|
2680
2705
|
if (category === "orm") {
|
|
2681
2706
|
if (config.database.orm === "drizzle") {
|
|
2682
|
-
await removePath(
|
|
2683
|
-
await removePath(
|
|
2707
|
+
await removePath(join14(root, "drizzle.config.ts"));
|
|
2708
|
+
await removePath(join14(root, "drizzle"));
|
|
2684
2709
|
}
|
|
2685
2710
|
if (config.database.orm === "prisma") {
|
|
2686
|
-
await removePath(
|
|
2687
|
-
await removePath(
|
|
2688
|
-
await removePath(
|
|
2689
|
-
await removePath(
|
|
2690
|
-
await removePath(
|
|
2711
|
+
await removePath(join14(root, "prisma"));
|
|
2712
|
+
await removePath(join14(root, "src", "db", "prisma.ts"));
|
|
2713
|
+
await removePath(join14(root, "src", "db", "prisma.js"));
|
|
2714
|
+
await removePath(join14(root, "src", "db", "prisma-example.ts"));
|
|
2715
|
+
await removePath(join14(root, "src", "db", "prisma-example.js"));
|
|
2691
2716
|
}
|
|
2692
2717
|
if (config.database.orm === "mongoose") {
|
|
2693
|
-
await removePath(
|
|
2694
|
-
await removePath(
|
|
2695
|
-
await removePath(
|
|
2696
|
-
await removePath(
|
|
2718
|
+
await removePath(join14(root, "src", "db", "mongoose.ts"));
|
|
2719
|
+
await removePath(join14(root, "src", "db", "mongoose.js"));
|
|
2720
|
+
await removePath(join14(root, "src", "db", "mongoose-model.ts"));
|
|
2721
|
+
await removePath(join14(root, "src", "db", "mongoose-model.js"));
|
|
2697
2722
|
}
|
|
2698
2723
|
if (config.database.orm === "typeorm") {
|
|
2699
|
-
await removePath(
|
|
2700
|
-
await removePath(
|
|
2701
|
-
await removePath(
|
|
2702
|
-
await removePath(
|
|
2724
|
+
await removePath(join14(root, "src", "db", "data-source.ts"));
|
|
2725
|
+
await removePath(join14(root, "src", "db", "data-source.js"));
|
|
2726
|
+
await removePath(join14(root, "src", "db", "entities"));
|
|
2727
|
+
await removePath(join14(root, "src", "db", "migrations"));
|
|
2703
2728
|
}
|
|
2704
2729
|
}
|
|
2705
2730
|
if (category === "feature") {
|
|
2706
2731
|
const targets = value ? [value] : config.features;
|
|
2707
2732
|
if (targets.includes("email")) {
|
|
2708
|
-
await removePath(
|
|
2709
|
-
await removeEnvKey(
|
|
2710
|
-
await removePath(
|
|
2711
|
-
await removePath(
|
|
2733
|
+
await removePath(join14(root, "features", "email"));
|
|
2734
|
+
await removeEnvKey(join14(root, ".env.example"), "RESEND_API_KEY");
|
|
2735
|
+
await removePath(join14(root, "src", "lib", "resend.ts"));
|
|
2736
|
+
await removePath(join14(root, "src", "lib", "resend.js"));
|
|
2712
2737
|
}
|
|
2713
2738
|
if (targets.includes("storage")) {
|
|
2714
|
-
await removePath(
|
|
2715
|
-
await removeEnvKey(
|
|
2716
|
-
await removePath(
|
|
2717
|
-
await removePath(
|
|
2739
|
+
await removePath(join14(root, "features", "storage"));
|
|
2740
|
+
await removeEnvKey(join14(root, ".env.example"), "CLOUDINARY_URL");
|
|
2741
|
+
await removePath(join14(root, "src", "lib", "storage.ts"));
|
|
2742
|
+
await removePath(join14(root, "src", "lib", "storage.js"));
|
|
2718
2743
|
}
|
|
2719
2744
|
if (targets.includes("payments")) {
|
|
2720
|
-
await removePath(
|
|
2721
|
-
await removeEnvKey(
|
|
2722
|
-
await removePath(
|
|
2723
|
-
await removePath(
|
|
2745
|
+
await removePath(join14(root, "features", "payments"));
|
|
2746
|
+
await removeEnvKey(join14(root, ".env.example"), "STRIPE_SECRET_KEY");
|
|
2747
|
+
await removePath(join14(root, "src", "lib", "stripe.ts"));
|
|
2748
|
+
await removePath(join14(root, "src", "lib", "stripe.js"));
|
|
2724
2749
|
}
|
|
2725
2750
|
if (targets.includes("analytics")) {
|
|
2726
|
-
await removePath(
|
|
2727
|
-
await removeEnvKey(
|
|
2728
|
-
await removeEnvKey(
|
|
2729
|
-
await removePath(
|
|
2730
|
-
await removePath(
|
|
2751
|
+
await removePath(join14(root, "features", "analytics"));
|
|
2752
|
+
await removeEnvKey(join14(root, ".env.example"), "NEXT_PUBLIC_POSTHOG_KEY");
|
|
2753
|
+
await removeEnvKey(join14(root, ".env.example"), "NEXT_PUBLIC_POSTHOG_HOST");
|
|
2754
|
+
await removePath(join14(root, "src", "lib", "posthog.ts"));
|
|
2755
|
+
await removePath(join14(root, "src", "lib", "posthog.js"));
|
|
2731
2756
|
}
|
|
2732
2757
|
if (targets.includes("error-tracking")) {
|
|
2733
|
-
await removePath(
|
|
2734
|
-
await removeEnvKey(
|
|
2735
|
-
await removePath(
|
|
2736
|
-
await removePath(
|
|
2758
|
+
await removePath(join14(root, "features", "error-tracking"));
|
|
2759
|
+
await removeEnvKey(join14(root, ".env.example"), "SENTRY_DSN");
|
|
2760
|
+
await removePath(join14(root, "src", "lib", "sentry.ts"));
|
|
2761
|
+
await removePath(join14(root, "src", "lib", "sentry.js"));
|
|
2737
2762
|
}
|
|
2738
2763
|
}
|
|
2739
2764
|
}
|
|
2740
2765
|
|
|
2741
2766
|
// src/cli/commands/remove.ts
|
|
2742
|
-
import { join as
|
|
2767
|
+
import { join as join15 } from "path";
|
|
2743
2768
|
function parseFeature2(feature) {
|
|
2744
2769
|
const parts = feature.split(":");
|
|
2745
2770
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
@@ -2798,7 +2823,7 @@ var removeCommand = new Command4("remove").argument("<feature>", "feature to rem
|
|
|
2798
2823
|
await writeProjectConfig(cwd, next);
|
|
2799
2824
|
await syncPackageJson(`${cwd}/package.json`, current, next);
|
|
2800
2825
|
const readme = buildProjectReadme(next);
|
|
2801
|
-
await writeTextFile(
|
|
2826
|
+
await writeTextFile(join15(cwd, "README.md"), readme + "\n");
|
|
2802
2827
|
logger.info(`Removed ${feature}`);
|
|
2803
2828
|
} catch (err) {
|
|
2804
2829
|
logger.error(err instanceof Error ? err.message : String(err));
|
|
@@ -2808,13 +2833,13 @@ var removeCommand = new Command4("remove").argument("<feature>", "feature to rem
|
|
|
2808
2833
|
|
|
2809
2834
|
// src/cli/commands/update.ts
|
|
2810
2835
|
import { Command as Command5 } from "commander";
|
|
2811
|
-
import { join as
|
|
2836
|
+
import { join as join16 } from "path";
|
|
2812
2837
|
import { readFile as readFile5 } from "fs/promises";
|
|
2813
2838
|
var updateCommand = new Command5("update").option("--check", "check for updates").option("--major", "allow major updates").option("--live", "compare against latest registry versions").action(async (options) => {
|
|
2814
2839
|
try {
|
|
2815
2840
|
const cwd = process.cwd();
|
|
2816
2841
|
const config = await readProjectConfig(cwd);
|
|
2817
|
-
const pkgPath =
|
|
2842
|
+
const pkgPath = join16(cwd, "package.json");
|
|
2818
2843
|
if (options.check) {
|
|
2819
2844
|
const pkg = JSON.parse(await readFile5(pkgPath, "utf8"));
|
|
2820
2845
|
const expectedScripts = resolveScripts(config);
|
|
@@ -2884,7 +2909,7 @@ var updateCommand = new Command5("update").option("--check", "check for updates"
|
|
|
2884
2909
|
await writePackageJson(pkgPath, pkg);
|
|
2885
2910
|
}
|
|
2886
2911
|
const readme = buildProjectReadme(config);
|
|
2887
|
-
await writeTextFile(
|
|
2912
|
+
await writeTextFile(join16(cwd, "README.md"), readme + "\n");
|
|
2888
2913
|
logger.info("Project updated.");
|
|
2889
2914
|
} catch (err) {
|
|
2890
2915
|
logger.error(err instanceof Error ? err.message : String(err));
|
|
@@ -2899,13 +2924,13 @@ function parseMajor(version2) {
|
|
|
2899
2924
|
|
|
2900
2925
|
// src/cli/commands/doctor.ts
|
|
2901
2926
|
import { Command as Command6 } from "commander";
|
|
2902
|
-
import { existsSync as
|
|
2903
|
-
import { join as
|
|
2927
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2928
|
+
import { join as join18 } from "path";
|
|
2904
2929
|
|
|
2905
2930
|
// src/utils/doctor.ts
|
|
2906
|
-
import { existsSync as
|
|
2931
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2907
2932
|
import { readFile as readFile6 } from "fs/promises";
|
|
2908
|
-
import { join as
|
|
2933
|
+
import { join as join17, dirname as dirname5, basename } from "path";
|
|
2909
2934
|
function requiredEnvKeys(config) {
|
|
2910
2935
|
const keys = [];
|
|
2911
2936
|
if (config.database.provider !== "none") keys.push("DATABASE_URL");
|
|
@@ -2934,21 +2959,21 @@ function agentFiles(agent) {
|
|
|
2934
2959
|
return files;
|
|
2935
2960
|
}
|
|
2936
2961
|
async function checkProject(cwd) {
|
|
2937
|
-
const configPath =
|
|
2938
|
-
const pkgPath =
|
|
2939
|
-
const envPath =
|
|
2962
|
+
const configPath = join17(cwd, "stackforge.json");
|
|
2963
|
+
const pkgPath = join17(cwd, "package.json");
|
|
2964
|
+
const envPath = join17(cwd, ".env.example");
|
|
2940
2965
|
const issues = [];
|
|
2941
|
-
if (!
|
|
2966
|
+
if (!existsSync4(configPath)) {
|
|
2942
2967
|
return {
|
|
2943
2968
|
issues: ["Missing stackforge.json. Run from a StackForge project root."],
|
|
2944
2969
|
config: void 0,
|
|
2945
2970
|
pkgPath,
|
|
2946
2971
|
envPath,
|
|
2947
2972
|
hasConfig: false,
|
|
2948
|
-
hasPackageJson:
|
|
2973
|
+
hasPackageJson: existsSync4(pkgPath)
|
|
2949
2974
|
};
|
|
2950
2975
|
}
|
|
2951
|
-
if (!
|
|
2976
|
+
if (!existsSync4(pkgPath)) {
|
|
2952
2977
|
return {
|
|
2953
2978
|
issues: ["Missing package.json"],
|
|
2954
2979
|
config: await readProjectConfig(cwd),
|
|
@@ -2978,7 +3003,7 @@ async function checkProject(cwd) {
|
|
|
2978
3003
|
}
|
|
2979
3004
|
}
|
|
2980
3005
|
let envContent = "";
|
|
2981
|
-
if (
|
|
3006
|
+
if (existsSync4(envPath)) {
|
|
2982
3007
|
envContent = await readFile6(envPath, "utf8");
|
|
2983
3008
|
}
|
|
2984
3009
|
const envKeys = new Set(
|
|
@@ -2991,25 +3016,25 @@ async function checkProject(cwd) {
|
|
|
2991
3016
|
}
|
|
2992
3017
|
}
|
|
2993
3018
|
for (const agent of config.aiAgents) {
|
|
2994
|
-
const agentRoot =
|
|
3019
|
+
const agentRoot = join17(cwd, ".ai-agents", agent);
|
|
2995
3020
|
for (const file of agentFiles(agent)) {
|
|
2996
|
-
if (!
|
|
3021
|
+
if (!existsSync4(join17(agentRoot, file))) {
|
|
2997
3022
|
issues.push(`Missing AI agent file: .ai-agents/${agent}/${file}`);
|
|
2998
3023
|
}
|
|
2999
3024
|
}
|
|
3000
|
-
if (agent === "claude" && !
|
|
3025
|
+
if (agent === "claude" && !existsSync4(join17(cwd, ".claude", "claude_desktop_config.json"))) {
|
|
3001
3026
|
issues.push("Missing Claude config: .claude/claude_desktop_config.json");
|
|
3002
3027
|
}
|
|
3003
|
-
if (agent === "codex" && !
|
|
3028
|
+
if (agent === "codex" && !existsSync4(join17(cwd, ".codex", "functions.json"))) {
|
|
3004
3029
|
issues.push("Missing Codex config: .codex/functions.json");
|
|
3005
3030
|
}
|
|
3006
|
-
if (agent === "cursor" && !
|
|
3031
|
+
if (agent === "cursor" && !existsSync4(join17(cwd, ".cursor", "extensions.json"))) {
|
|
3007
3032
|
issues.push("Missing Cursor config: .cursor/extensions.json");
|
|
3008
3033
|
}
|
|
3009
|
-
if (agent === "windsurf" && !
|
|
3034
|
+
if (agent === "windsurf" && !existsSync4(join17(cwd, ".windsurf", "cascade.json"))) {
|
|
3010
3035
|
issues.push("Missing Windsurf config: .windsurf/cascade.json");
|
|
3011
3036
|
}
|
|
3012
|
-
if (agent === "tabnine" && !
|
|
3037
|
+
if (agent === "tabnine" && !existsSync4(join17(cwd, ".tabnine", "config.json"))) {
|
|
3013
3038
|
issues.push("Missing Tabnine config: .tabnine/config.json");
|
|
3014
3039
|
}
|
|
3015
3040
|
}
|
|
@@ -3026,7 +3051,7 @@ async function fixProject(result, cwd) {
|
|
|
3026
3051
|
const { config, pkgPath, envPath } = result;
|
|
3027
3052
|
if (!config) return;
|
|
3028
3053
|
await syncPackageJson(pkgPath, config, config);
|
|
3029
|
-
const envContent =
|
|
3054
|
+
const envContent = existsSync4(envPath) ? await readFile6(envPath, "utf8") : "";
|
|
3030
3055
|
const envKeys = new Set(
|
|
3031
3056
|
envContent.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.split("=")[0])
|
|
3032
3057
|
);
|
|
@@ -3035,19 +3060,19 @@ async function fixProject(result, cwd) {
|
|
|
3035
3060
|
await appendEnvLine(envPath, `${key}=""`);
|
|
3036
3061
|
}
|
|
3037
3062
|
if (config.aiAgents.length > 0) {
|
|
3038
|
-
const base = basename(cwd) === config.projectName ?
|
|
3063
|
+
const base = basename(cwd) === config.projectName ? dirname5(cwd) : cwd;
|
|
3039
3064
|
await generateAiAgentConfigs(base, config);
|
|
3040
3065
|
}
|
|
3041
3066
|
const readme = buildProjectReadme(config);
|
|
3042
|
-
await writeTextFile(
|
|
3067
|
+
await writeTextFile(join17(cwd, "README.md"), readme + "\n");
|
|
3043
3068
|
}
|
|
3044
3069
|
|
|
3045
3070
|
// src/cli/commands/doctor.ts
|
|
3046
3071
|
import { readFile as readFile7 } from "fs/promises";
|
|
3047
3072
|
var doctorCommand = new Command6("doctor").option("--fix", "apply non-destructive fixes").action(async (options) => {
|
|
3048
3073
|
const cwd = process.cwd();
|
|
3049
|
-
const configPath =
|
|
3050
|
-
if (!
|
|
3074
|
+
const configPath = join18(cwd, "stackforge.json");
|
|
3075
|
+
if (!existsSync5(configPath)) {
|
|
3051
3076
|
logger.error("Missing stackforge.json. Run from a StackForge project root.");
|
|
3052
3077
|
return;
|
|
3053
3078
|
}
|
|
@@ -3065,8 +3090,8 @@ var doctorCommand = new Command6("doctor").option("--fix", "apply non-destructiv
|
|
|
3065
3090
|
logger.error("Failed to read stackforge.json");
|
|
3066
3091
|
return;
|
|
3067
3092
|
}
|
|
3068
|
-
const pkgPath =
|
|
3069
|
-
if (!
|
|
3093
|
+
const pkgPath = join18(cwd, "package.json");
|
|
3094
|
+
if (!existsSync5(pkgPath)) {
|
|
3070
3095
|
logger.error("Missing package.json");
|
|
3071
3096
|
return;
|
|
3072
3097
|
}
|
|
@@ -3145,11 +3170,11 @@ var listAgentsCommand = new Command10("list-agents").action(async () => {
|
|
|
3145
3170
|
// src/cli/commands/migrate.ts
|
|
3146
3171
|
import { Command as Command11 } from "commander";
|
|
3147
3172
|
import { readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
3148
|
-
import { join as
|
|
3173
|
+
import { join as join19 } from "path";
|
|
3149
3174
|
var migrateCommand = new Command11("migrate").option("--dry-run", "show planned migration without writing").action(async (options) => {
|
|
3150
3175
|
try {
|
|
3151
3176
|
const cwd = process.cwd();
|
|
3152
|
-
const path =
|
|
3177
|
+
const path = join19(cwd, "stackforge.json");
|
|
3153
3178
|
const raw = await readFile8(path, "utf8");
|
|
3154
3179
|
const parsed = JSON.parse(raw);
|
|
3155
3180
|
const current = parsed._schemaVersion ?? 0;
|
|
@@ -3239,7 +3264,7 @@ var fixCommand = new Command15("fix").description("apply safe fixes to project c
|
|
|
3239
3264
|
|
|
3240
3265
|
// src/cli/commands/upgrade.ts
|
|
3241
3266
|
import { Command as Command16 } from "commander";
|
|
3242
|
-
import { join as
|
|
3267
|
+
import { join as join20, dirname as dirname6 } from "path";
|
|
3243
3268
|
var upgradeCommand = new Command16("upgrade").option("--preset <name>", "preset to upgrade to").action(async (options) => {
|
|
3244
3269
|
const cwd = process.cwd();
|
|
3245
3270
|
const current = await readProjectConfig(cwd);
|
|
@@ -3281,8 +3306,8 @@ var upgradeCommand = new Command16("upgrade").option("--preset <name>", "preset
|
|
|
3281
3306
|
await cleanupFeature(cwd, current, "feature", feature);
|
|
3282
3307
|
}
|
|
3283
3308
|
await writeProjectConfig(cwd, next);
|
|
3284
|
-
await syncPackageJson(
|
|
3285
|
-
const root =
|
|
3309
|
+
await syncPackageJson(join20(cwd, "package.json"), current, next);
|
|
3310
|
+
const root = dirname6(cwd);
|
|
3286
3311
|
await generateFrontendFiles(root, next);
|
|
3287
3312
|
await generateUiFiles(root, next);
|
|
3288
3313
|
await generateDatabaseFiles(root, next);
|
|
@@ -3290,7 +3315,7 @@ var upgradeCommand = new Command16("upgrade").option("--preset <name>", "preset
|
|
|
3290
3315
|
await generateApiFiles(root, next);
|
|
3291
3316
|
await generateFeatureFiles(root, next);
|
|
3292
3317
|
const readme = buildProjectReadme(next);
|
|
3293
|
-
await writeTextFile(
|
|
3318
|
+
await writeTextFile(join20(cwd, "README.md"), readme + "\n");
|
|
3294
3319
|
logger.info(`Upgraded project to preset: ${options.preset}`);
|
|
3295
3320
|
});
|
|
3296
3321
|
|