@bantay/cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cli.ts +95 -9
- package/src/commands/ci.ts +228 -0
- package/src/commands/status.ts +309 -0
- package/src/detectors/authjs.ts +92 -0
- package/src/detectors/clerk.ts +88 -0
- package/src/detectors/drizzle.ts +105 -0
- package/src/detectors/index.ts +35 -14
- package/src/detectors/nextjs.ts +13 -3
- package/src/detectors/stripe.ts +88 -0
- package/src/detectors/types.ts +12 -0
- package/src/export/all.ts +5 -1
- package/src/export/codex.ts +70 -0
- package/src/export/index.ts +1 -0
- package/src/generators/invariants.ts +170 -54
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFile, access } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import type { AuthDetection } from "./types";
|
|
4
|
+
|
|
5
|
+
async function fileExists(path: string): Promise<boolean> {
|
|
6
|
+
try {
|
|
7
|
+
await access(path);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function readPackageJson(projectPath: string): Promise<Record<string, unknown> | null> {
|
|
15
|
+
try {
|
|
16
|
+
const content = await readFile(join(projectPath, "package.json"), "utf-8");
|
|
17
|
+
return JSON.parse(content);
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function detect(projectPath: string): Promise<AuthDetection | null> {
|
|
24
|
+
const pkg = await readPackageJson(projectPath);
|
|
25
|
+
|
|
26
|
+
let version: string | undefined;
|
|
27
|
+
let confidence: "high" | "medium" | "low" = "low";
|
|
28
|
+
let detected = false;
|
|
29
|
+
let authFunction: string | undefined;
|
|
30
|
+
let sessionFunction: string | undefined;
|
|
31
|
+
|
|
32
|
+
if (pkg) {
|
|
33
|
+
const deps = (pkg.dependencies ?? {}) as Record<string, string>;
|
|
34
|
+
const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;
|
|
35
|
+
|
|
36
|
+
// Check for next-auth (Auth.js v4)
|
|
37
|
+
if (deps["next-auth"]) {
|
|
38
|
+
version = deps["next-auth"];
|
|
39
|
+
confidence = "high";
|
|
40
|
+
detected = true;
|
|
41
|
+
authFunction = "getServerSession";
|
|
42
|
+
sessionFunction = "getServerSession(authOptions)";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check for @auth/nextjs (Auth.js v5)
|
|
46
|
+
if (deps["@auth/nextjs-auth"]) {
|
|
47
|
+
version = deps["@auth/nextjs-auth"];
|
|
48
|
+
confidence = "high";
|
|
49
|
+
detected = true;
|
|
50
|
+
authFunction = "auth";
|
|
51
|
+
sessionFunction = "auth()";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check for auth.js v5 with new package name
|
|
55
|
+
if (deps["next-auth"] && version?.startsWith("5")) {
|
|
56
|
+
authFunction = "auth";
|
|
57
|
+
sessionFunction = "auth()";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check for auth config files
|
|
62
|
+
const configFiles = [
|
|
63
|
+
"auth.ts",
|
|
64
|
+
"auth.config.ts",
|
|
65
|
+
"lib/auth.ts",
|
|
66
|
+
"src/auth.ts",
|
|
67
|
+
"src/lib/auth.ts",
|
|
68
|
+
"app/api/auth/[...nextauth]/route.ts",
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
for (const configFile of configFiles) {
|
|
72
|
+
if (await fileExists(join(projectPath, configFile))) {
|
|
73
|
+
detected = true;
|
|
74
|
+
if (confidence === "low") {
|
|
75
|
+
confidence = "medium";
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!detected) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
name: "authjs",
|
|
87
|
+
version,
|
|
88
|
+
confidence,
|
|
89
|
+
authFunction,
|
|
90
|
+
sessionFunction,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readFile, access } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import type { AuthDetection } from "./types";
|
|
4
|
+
|
|
5
|
+
async function fileExists(path: string): Promise<boolean> {
|
|
6
|
+
try {
|
|
7
|
+
await access(path);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function readPackageJson(projectPath: string): Promise<Record<string, unknown> | null> {
|
|
15
|
+
try {
|
|
16
|
+
const content = await readFile(join(projectPath, "package.json"), "utf-8");
|
|
17
|
+
return JSON.parse(content);
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function detect(projectPath: string): Promise<AuthDetection | null> {
|
|
24
|
+
const pkg = await readPackageJson(projectPath);
|
|
25
|
+
|
|
26
|
+
let version: string | undefined;
|
|
27
|
+
let confidence: "high" | "medium" | "low" = "low";
|
|
28
|
+
let detected = false;
|
|
29
|
+
|
|
30
|
+
if (pkg) {
|
|
31
|
+
const deps = (pkg.dependencies ?? {}) as Record<string, string>;
|
|
32
|
+
const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;
|
|
33
|
+
|
|
34
|
+
// Check for @clerk/nextjs
|
|
35
|
+
if (deps["@clerk/nextjs"]) {
|
|
36
|
+
version = deps["@clerk/nextjs"];
|
|
37
|
+
confidence = "high";
|
|
38
|
+
detected = true;
|
|
39
|
+
} else if (devDeps["@clerk/nextjs"]) {
|
|
40
|
+
version = devDeps["@clerk/nextjs"];
|
|
41
|
+
confidence = "medium";
|
|
42
|
+
detected = true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check for @clerk/clerk-sdk-node
|
|
46
|
+
if (deps["@clerk/clerk-sdk-node"] || devDeps["@clerk/clerk-sdk-node"]) {
|
|
47
|
+
detected = true;
|
|
48
|
+
if (confidence === "low") {
|
|
49
|
+
confidence = "high";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check for Clerk middleware
|
|
55
|
+
const middlewareFiles = [
|
|
56
|
+
"middleware.ts",
|
|
57
|
+
"middleware.js",
|
|
58
|
+
"src/middleware.ts",
|
|
59
|
+
"src/middleware.js",
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
for (const middlewareFile of middlewareFiles) {
|
|
63
|
+
if (await fileExists(join(projectPath, middlewareFile))) {
|
|
64
|
+
try {
|
|
65
|
+
const content = await readFile(join(projectPath, middlewareFile), "utf-8");
|
|
66
|
+
if (content.includes("@clerk") || content.includes("clerkMiddleware") || content.includes("authMiddleware")) {
|
|
67
|
+
detected = true;
|
|
68
|
+
confidence = "high";
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Ignore read errors
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!detected) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
name: "clerk",
|
|
83
|
+
version,
|
|
84
|
+
confidence,
|
|
85
|
+
authFunction: "auth",
|
|
86
|
+
sessionFunction: "auth()",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { readFile, access } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import type { OrmDetection } from "./types";
|
|
4
|
+
|
|
5
|
+
async function fileExists(path: string): Promise<boolean> {
|
|
6
|
+
try {
|
|
7
|
+
await access(path);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function readPackageJson(projectPath: string): Promise<Record<string, unknown> | null> {
|
|
15
|
+
try {
|
|
16
|
+
const content = await readFile(join(projectPath, "package.json"), "utf-8");
|
|
17
|
+
return JSON.parse(content);
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function findSchemaPath(projectPath: string): Promise<string | undefined> {
|
|
24
|
+
// Check common Drizzle schema locations
|
|
25
|
+
const paths = [
|
|
26
|
+
"drizzle/schema.ts",
|
|
27
|
+
"src/db/schema.ts",
|
|
28
|
+
"src/drizzle/schema.ts",
|
|
29
|
+
"lib/db/schema.ts",
|
|
30
|
+
"lib/drizzle/schema.ts",
|
|
31
|
+
"db/schema.ts",
|
|
32
|
+
"schema.ts",
|
|
33
|
+
"src/schema.ts",
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
for (const path of paths) {
|
|
37
|
+
if (await fileExists(join(projectPath, path))) {
|
|
38
|
+
return path;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function detect(projectPath: string): Promise<OrmDetection | null> {
|
|
46
|
+
const pkg = await readPackageJson(projectPath);
|
|
47
|
+
|
|
48
|
+
let version: string | undefined;
|
|
49
|
+
let confidence: "high" | "medium" | "low" = "low";
|
|
50
|
+
let detected = false;
|
|
51
|
+
|
|
52
|
+
if (pkg) {
|
|
53
|
+
const deps = (pkg.dependencies ?? {}) as Record<string, string>;
|
|
54
|
+
const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;
|
|
55
|
+
|
|
56
|
+
// Check for drizzle-orm
|
|
57
|
+
if (deps["drizzle-orm"]) {
|
|
58
|
+
version = deps["drizzle-orm"];
|
|
59
|
+
confidence = "high";
|
|
60
|
+
detected = true;
|
|
61
|
+
} else if (devDeps["drizzle-orm"]) {
|
|
62
|
+
version = devDeps["drizzle-orm"];
|
|
63
|
+
confidence = "medium";
|
|
64
|
+
detected = true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check for drizzle-kit (CLI tool)
|
|
68
|
+
if (deps["drizzle-kit"] || devDeps["drizzle-kit"]) {
|
|
69
|
+
detected = true;
|
|
70
|
+
if (confidence === "low") {
|
|
71
|
+
confidence = "high";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check for drizzle.config.ts
|
|
77
|
+
const configFiles = [
|
|
78
|
+
"drizzle.config.ts",
|
|
79
|
+
"drizzle.config.js",
|
|
80
|
+
"drizzle.config.json",
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
for (const configFile of configFiles) {
|
|
84
|
+
if (await fileExists(join(projectPath, configFile))) {
|
|
85
|
+
detected = true;
|
|
86
|
+
if (confidence === "low") {
|
|
87
|
+
confidence = "high";
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!detected) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const schemaPath = await findSchemaPath(projectPath);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
name: "drizzle",
|
|
101
|
+
version,
|
|
102
|
+
confidence,
|
|
103
|
+
schemaPath,
|
|
104
|
+
};
|
|
105
|
+
}
|
package/src/detectors/index.ts
CHANGED
|
@@ -2,34 +2,48 @@ export type {
|
|
|
2
2
|
FrameworkDetection,
|
|
3
3
|
OrmDetection,
|
|
4
4
|
AuthDetection,
|
|
5
|
+
PaymentsDetection,
|
|
5
6
|
StackDetectionResult,
|
|
6
7
|
} from "./types";
|
|
7
8
|
|
|
8
|
-
import type { StackDetectionResult, FrameworkDetection, OrmDetection, AuthDetection } from "./types";
|
|
9
|
+
import type { StackDetectionResult, FrameworkDetection, OrmDetection, AuthDetection, PaymentsDetection } from "./types";
|
|
9
10
|
import { detect as detectNextjs } from "./nextjs";
|
|
10
11
|
import { detect as detectPrisma } from "./prisma";
|
|
12
|
+
import { detect as detectDrizzle } from "./drizzle";
|
|
13
|
+
import { detect as detectAuthjs } from "./authjs";
|
|
14
|
+
import { detect as detectClerk } from "./clerk";
|
|
15
|
+
import { detect as detectStripe } from "./stripe";
|
|
11
16
|
|
|
12
17
|
// Registry of framework detectors
|
|
13
|
-
const frameworkDetectors: Array<() =>
|
|
14
|
-
|
|
18
|
+
const frameworkDetectors: Array<(projectPath: string) => Promise<FrameworkDetection | null>> = [
|
|
19
|
+
detectNextjs,
|
|
15
20
|
];
|
|
16
21
|
|
|
17
22
|
// Registry of ORM detectors
|
|
18
|
-
const ormDetectors: Array<() =>
|
|
19
|
-
|
|
23
|
+
const ormDetectors: Array<(projectPath: string) => Promise<OrmDetection | null>> = [
|
|
24
|
+
detectPrisma,
|
|
25
|
+
detectDrizzle,
|
|
20
26
|
];
|
|
21
27
|
|
|
22
|
-
// Registry of auth detectors
|
|
23
|
-
const authDetectors: Array<(
|
|
28
|
+
// Registry of auth detectors
|
|
29
|
+
const authDetectors: Array<(projectPath: string) => Promise<AuthDetection | null>> = [
|
|
30
|
+
detectClerk, // Check Clerk first (more specific)
|
|
31
|
+
detectAuthjs,
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Registry of payments detectors
|
|
35
|
+
const paymentsDetectors: Array<(projectPath: string) => Promise<PaymentsDetection | null>> = [
|
|
36
|
+
detectStripe,
|
|
37
|
+
];
|
|
24
38
|
|
|
25
39
|
export async function detectStack(projectPath: string): Promise<StackDetectionResult> {
|
|
26
40
|
let framework: FrameworkDetection | null = null;
|
|
27
41
|
let orm: OrmDetection | null = null;
|
|
28
42
|
let auth: AuthDetection | null = null;
|
|
43
|
+
let payments: PaymentsDetection | null = null;
|
|
29
44
|
|
|
30
45
|
// Run framework detectors
|
|
31
|
-
for (const
|
|
32
|
-
const detector = getDetector();
|
|
46
|
+
for (const detector of frameworkDetectors) {
|
|
33
47
|
const result = await detector(projectPath);
|
|
34
48
|
if (result && (!framework || result.confidence === "high")) {
|
|
35
49
|
framework = result;
|
|
@@ -38,8 +52,7 @@ export async function detectStack(projectPath: string): Promise<StackDetectionRe
|
|
|
38
52
|
}
|
|
39
53
|
|
|
40
54
|
// Run ORM detectors
|
|
41
|
-
for (const
|
|
42
|
-
const detector = getDetector();
|
|
55
|
+
for (const detector of ormDetectors) {
|
|
43
56
|
const result = await detector(projectPath);
|
|
44
57
|
if (result && (!orm || result.confidence === "high")) {
|
|
45
58
|
orm = result;
|
|
@@ -48,8 +61,7 @@ export async function detectStack(projectPath: string): Promise<StackDetectionRe
|
|
|
48
61
|
}
|
|
49
62
|
|
|
50
63
|
// Run auth detectors
|
|
51
|
-
for (const
|
|
52
|
-
const detector = getDetector();
|
|
64
|
+
for (const detector of authDetectors) {
|
|
53
65
|
const result = await detector(projectPath);
|
|
54
66
|
if (result && (!auth || result.confidence === "high")) {
|
|
55
67
|
auth = result;
|
|
@@ -57,5 +69,14 @@ export async function detectStack(projectPath: string): Promise<StackDetectionRe
|
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
// Run payments detectors
|
|
73
|
+
for (const detector of paymentsDetectors) {
|
|
74
|
+
const result = await detector(projectPath);
|
|
75
|
+
if (result && (!payments || result.confidence === "high")) {
|
|
76
|
+
payments = result;
|
|
77
|
+
if (result.confidence === "high") break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { framework, orm, auth, payments };
|
|
61
82
|
}
|
package/src/detectors/nextjs.ts
CHANGED
|
@@ -74,18 +74,27 @@ export async function detect(projectPath: string): Promise<FrameworkDetection |
|
|
|
74
74
|
return null;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// Detect router type
|
|
77
|
+
// Detect router type and determine route pattern
|
|
78
78
|
let router: "app" | "pages" | undefined;
|
|
79
|
+
let routePattern: string | undefined;
|
|
79
80
|
|
|
80
81
|
const hasAppDir = await dirExists(join(projectPath, "app"));
|
|
81
82
|
const hasPagesDir = await dirExists(join(projectPath, "pages"));
|
|
82
83
|
const hasSrcAppDir = await dirExists(join(projectPath, "src", "app"));
|
|
83
84
|
const hasSrcPagesDir = await dirExists(join(projectPath, "src", "pages"));
|
|
84
85
|
|
|
85
|
-
if (hasAppDir
|
|
86
|
+
if (hasAppDir) {
|
|
86
87
|
router = "app";
|
|
87
|
-
|
|
88
|
+
routePattern = "app/api/**/route.ts";
|
|
89
|
+
} else if (hasSrcAppDir) {
|
|
90
|
+
router = "app";
|
|
91
|
+
routePattern = "src/app/api/**/route.ts";
|
|
92
|
+
} else if (hasPagesDir) {
|
|
93
|
+
router = "pages";
|
|
94
|
+
routePattern = "pages/api/**/*.ts";
|
|
95
|
+
} else if (hasSrcPagesDir) {
|
|
88
96
|
router = "pages";
|
|
97
|
+
routePattern = "src/pages/api/**/*.ts";
|
|
89
98
|
}
|
|
90
99
|
|
|
91
100
|
return {
|
|
@@ -93,5 +102,6 @@ export async function detect(projectPath: string): Promise<FrameworkDetection |
|
|
|
93
102
|
version,
|
|
94
103
|
confidence,
|
|
95
104
|
router,
|
|
105
|
+
routePattern,
|
|
96
106
|
};
|
|
97
107
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readFile, access, readdir } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import type { PaymentsDetection } from "./types";
|
|
4
|
+
|
|
5
|
+
async function fileExists(path: string): Promise<boolean> {
|
|
6
|
+
try {
|
|
7
|
+
await access(path);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function readPackageJson(projectPath: string): Promise<Record<string, unknown> | null> {
|
|
15
|
+
try {
|
|
16
|
+
const content = await readFile(join(projectPath, "package.json"), "utf-8");
|
|
17
|
+
return JSON.parse(content);
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function findWebhookPattern(projectPath: string): Promise<string | undefined> {
|
|
24
|
+
// Check common webhook locations
|
|
25
|
+
const patterns = [
|
|
26
|
+
{ path: "app/api/webhooks/stripe/route.ts", pattern: "app/api/webhooks/stripe/route.ts" },
|
|
27
|
+
{ path: "app/api/webhook/stripe/route.ts", pattern: "app/api/webhook/stripe/route.ts" },
|
|
28
|
+
{ path: "app/api/stripe/webhook/route.ts", pattern: "app/api/stripe/webhook/route.ts" },
|
|
29
|
+
{ path: "app/webhooks/stripe/route.ts", pattern: "app/webhooks/stripe/route.ts" },
|
|
30
|
+
{ path: "src/app/api/webhooks/stripe/route.ts", pattern: "src/app/api/webhooks/stripe/route.ts" },
|
|
31
|
+
{ path: "pages/api/webhooks/stripe.ts", pattern: "pages/api/webhooks/stripe.ts" },
|
|
32
|
+
{ path: "pages/api/webhook/stripe.ts", pattern: "pages/api/webhook/stripe.ts" },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
for (const { path, pattern } of patterns) {
|
|
36
|
+
if (await fileExists(join(projectPath, path))) {
|
|
37
|
+
return pattern;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function detect(projectPath: string): Promise<PaymentsDetection | null> {
|
|
45
|
+
const pkg = await readPackageJson(projectPath);
|
|
46
|
+
|
|
47
|
+
let version: string | undefined;
|
|
48
|
+
let confidence: "high" | "medium" | "low" = "low";
|
|
49
|
+
let detected = false;
|
|
50
|
+
|
|
51
|
+
// Check package.json for stripe dependency
|
|
52
|
+
if (pkg) {
|
|
53
|
+
const deps = (pkg.dependencies ?? {}) as Record<string, string>;
|
|
54
|
+
const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;
|
|
55
|
+
|
|
56
|
+
if (deps.stripe) {
|
|
57
|
+
version = deps.stripe;
|
|
58
|
+
confidence = "high";
|
|
59
|
+
detected = true;
|
|
60
|
+
} else if (devDeps.stripe) {
|
|
61
|
+
version = devDeps.stripe;
|
|
62
|
+
confidence = "medium";
|
|
63
|
+
detected = true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Also check for @stripe/stripe-js (client-side)
|
|
67
|
+
if (deps["@stripe/stripe-js"] || devDeps["@stripe/stripe-js"]) {
|
|
68
|
+
detected = true;
|
|
69
|
+
if (confidence === "low") {
|
|
70
|
+
confidence = "medium";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!detected) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const webhookPattern = await findWebhookPattern(projectPath);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
name: "stripe",
|
|
83
|
+
version,
|
|
84
|
+
confidence,
|
|
85
|
+
webhookPattern,
|
|
86
|
+
secretEnvVar: "STRIPE_SECRET_KEY",
|
|
87
|
+
};
|
|
88
|
+
}
|
package/src/detectors/types.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface FrameworkDetection {
|
|
|
3
3
|
version?: string;
|
|
4
4
|
confidence: "high" | "medium" | "low";
|
|
5
5
|
router?: "app" | "pages";
|
|
6
|
+
routePattern?: string; // e.g., "app/api/**/route.ts" or "pages/api/**/*.ts"
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
export interface OrmDetection {
|
|
@@ -16,12 +17,23 @@ export interface AuthDetection {
|
|
|
16
17
|
name: string;
|
|
17
18
|
version?: string;
|
|
18
19
|
confidence: "high" | "medium" | "low";
|
|
20
|
+
authFunction?: string; // e.g., "auth()" for Auth.js, "getAuth()" for Clerk
|
|
21
|
+
sessionFunction?: string; // e.g., "getServerSession()" or "currentUser()"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PaymentsDetection {
|
|
25
|
+
name: string;
|
|
26
|
+
version?: string;
|
|
27
|
+
confidence: "high" | "medium" | "low";
|
|
28
|
+
webhookPattern?: string; // e.g., "app/api/webhooks/stripe/route.ts"
|
|
29
|
+
secretEnvVar?: string; // e.g., "STRIPE_SECRET_KEY"
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
export interface StackDetectionResult {
|
|
22
33
|
framework: FrameworkDetection | null;
|
|
23
34
|
orm: OrmDetection | null;
|
|
24
35
|
auth: AuthDetection | null;
|
|
36
|
+
payments: PaymentsDetection | null;
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
export interface Detector<T> {
|
package/src/export/all.ts
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
import { exportInvariants } from "./invariants";
|
|
6
6
|
import { exportClaude } from "./claude";
|
|
7
7
|
import { exportCursor } from "./cursor";
|
|
8
|
+
import { exportCodex } from "./codex";
|
|
8
9
|
import type { ExportOptions, ExportResult } from "./types";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* Export all targets: invariants.md, CLAUDE.md, .cursorrules
|
|
12
|
+
* Export all targets: invariants.md, CLAUDE.md, .cursorrules, AGENTS.md
|
|
12
13
|
*/
|
|
13
14
|
export async function exportAll(
|
|
14
15
|
projectPath: string,
|
|
@@ -25,5 +26,8 @@ export async function exportAll(
|
|
|
25
26
|
// Export .cursorrules
|
|
26
27
|
results.push(await exportCursor(projectPath, options));
|
|
27
28
|
|
|
29
|
+
// Export AGENTS.md
|
|
30
|
+
results.push(await exportCodex(projectPath, options));
|
|
31
|
+
|
|
28
32
|
return results;
|
|
29
33
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export to AGENTS.md with section markers
|
|
3
|
+
* Works for Codex, Copilot, and any agent that reads AGENTS.md
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile, writeFile, access } from "fs/promises";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { read as readAide } from "../aide";
|
|
9
|
+
import {
|
|
10
|
+
extractConstraints,
|
|
11
|
+
extractFoundations,
|
|
12
|
+
extractInvariants,
|
|
13
|
+
} from "./aide-reader";
|
|
14
|
+
import { generateClaudeSection, insertSection } from "./claude";
|
|
15
|
+
import type { ExportOptions, ExportResult } from "./types";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if a file exists
|
|
19
|
+
*/
|
|
20
|
+
async function fileExists(path: string): Promise<boolean> {
|
|
21
|
+
try {
|
|
22
|
+
await access(path);
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Export to AGENTS.md
|
|
31
|
+
*/
|
|
32
|
+
export async function exportCodex(
|
|
33
|
+
projectPath: string,
|
|
34
|
+
options: ExportOptions = {}
|
|
35
|
+
): Promise<ExportResult> {
|
|
36
|
+
const aidePath = options.aidePath || join(projectPath, "bantay.aide");
|
|
37
|
+
const outputPath = options.outputPath || join(projectPath, "AGENTS.md");
|
|
38
|
+
|
|
39
|
+
// Read the aide tree
|
|
40
|
+
const tree = await readAide(aidePath);
|
|
41
|
+
|
|
42
|
+
// Extract entities
|
|
43
|
+
const constraints = extractConstraints(tree);
|
|
44
|
+
const foundations = extractFoundations(tree);
|
|
45
|
+
const invariants = extractInvariants(tree);
|
|
46
|
+
|
|
47
|
+
// Generate section content (same format as claude)
|
|
48
|
+
const section = generateClaudeSection(constraints, foundations, invariants);
|
|
49
|
+
|
|
50
|
+
// Read existing file if it exists
|
|
51
|
+
let existingContent = "";
|
|
52
|
+
if (await fileExists(outputPath)) {
|
|
53
|
+
existingContent = await readFile(outputPath, "utf-8");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Insert or replace section
|
|
57
|
+
const content = insertSection(existingContent, section);
|
|
58
|
+
|
|
59
|
+
// Write unless dry run
|
|
60
|
+
if (!options.dryRun) {
|
|
61
|
+
await writeFile(outputPath, content, "utf-8");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
target: "codex",
|
|
66
|
+
outputPath,
|
|
67
|
+
content,
|
|
68
|
+
bytesWritten: Buffer.byteLength(content, "utf-8"),
|
|
69
|
+
};
|
|
70
|
+
}
|
package/src/export/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
export { exportInvariants, generateInvariantsMd } from "./invariants";
|
|
6
6
|
export { exportClaude, generateClaudeSection, insertSection } from "./claude";
|
|
7
7
|
export { exportCursor } from "./cursor";
|
|
8
|
+
export { exportCodex } from "./codex";
|
|
8
9
|
export { exportAll } from "./all";
|
|
9
10
|
|
|
10
11
|
export type {
|