@posthog/wizard 1.27.1 → 1.29.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/dist/src/flask/flask-wizard-agent.d.ts +5 -0
- package/dist/src/flask/flask-wizard-agent.js +160 -0
- package/dist/src/flask/flask-wizard-agent.js.map +1 -0
- package/dist/src/flask/utils.d.ts +28 -0
- package/dist/src/flask/utils.js +363 -0
- package/dist/src/flask/utils.js.map +1 -0
- package/dist/src/lib/agent-interface.js +14 -1
- package/dist/src/lib/agent-interface.js.map +1 -1
- package/dist/src/lib/agent-runner.js +6 -0
- package/dist/src/lib/agent-runner.js.map +1 -1
- package/dist/src/lib/config.d.ts +12 -1
- package/dist/src/lib/config.js +79 -0
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/constants.d.ts +2 -1
- package/dist/src/lib/constants.js +3 -0
- package/dist/src/lib/constants.js.map +1 -1
- package/dist/src/run.js +6 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/utils/anthropic-status.d.ts +28 -0
- package/dist/src/utils/anthropic-status.js +101 -0
- package/dist/src/utils/anthropic-status.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runFlaskWizardAgent = runFlaskWizardAgent;
|
|
40
|
+
const debug_1 = require("../utils/debug");
|
|
41
|
+
const agent_runner_1 = require("../lib/agent-runner");
|
|
42
|
+
const constants_1 = require("../lib/constants");
|
|
43
|
+
const clack_1 = __importDefault(require("../utils/clack"));
|
|
44
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
45
|
+
const semver = __importStar(require("semver"));
|
|
46
|
+
const utils_1 = require("./utils");
|
|
47
|
+
/**
|
|
48
|
+
* Flask framework configuration for the universal agent runner
|
|
49
|
+
*/
|
|
50
|
+
const MINIMUM_FLASK_VERSION = '2.0.0';
|
|
51
|
+
const FLASK_AGENT_CONFIG = {
|
|
52
|
+
metadata: {
|
|
53
|
+
name: 'Flask',
|
|
54
|
+
integration: constants_1.Integration.flask,
|
|
55
|
+
docsUrl: 'https://posthog.com/docs/libraries/python',
|
|
56
|
+
unsupportedVersionDocsUrl: 'https://posthog.com/docs/libraries/python',
|
|
57
|
+
gatherContext: async (options) => {
|
|
58
|
+
const projectType = await (0, utils_1.getFlaskProjectType)(options);
|
|
59
|
+
const appFile = await (0, utils_1.findFlaskAppFile)(options);
|
|
60
|
+
return { projectType, appFile };
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
detection: {
|
|
64
|
+
packageName: 'flask',
|
|
65
|
+
packageDisplayName: 'Flask',
|
|
66
|
+
usesPackageJson: false,
|
|
67
|
+
getVersion: (_packageJson) => {
|
|
68
|
+
// For Flask, we don't use package.json. Version is extracted separately
|
|
69
|
+
// from requirements.txt or pyproject.toml in the wizard entry point
|
|
70
|
+
return undefined;
|
|
71
|
+
},
|
|
72
|
+
getVersionBucket: utils_1.getFlaskVersionBucket,
|
|
73
|
+
},
|
|
74
|
+
environment: {
|
|
75
|
+
uploadToHosting: false,
|
|
76
|
+
getEnvVars: (apiKey, host) => ({
|
|
77
|
+
POSTHOG_API_KEY: apiKey,
|
|
78
|
+
POSTHOG_HOST: host,
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
analytics: {
|
|
82
|
+
getTags: (context) => {
|
|
83
|
+
const projectType = context.projectType;
|
|
84
|
+
return {
|
|
85
|
+
projectType: projectType || 'unknown',
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
prompts: {
|
|
90
|
+
projectTypeDetection: 'This is a Python/Flask project. Look for requirements.txt, pyproject.toml, setup.py, Pipfile, or app.py/wsgi.py to confirm.',
|
|
91
|
+
packageInstallation: 'Use pip, poetry, or pipenv based on existing config files (requirements.txt, pyproject.toml, Pipfile). Do not pin the posthog version - just add "posthog" without version constraints.',
|
|
92
|
+
getAdditionalContextLines: (context) => {
|
|
93
|
+
const projectType = context.projectType;
|
|
94
|
+
const projectTypeName = projectType
|
|
95
|
+
? (0, utils_1.getFlaskProjectTypeName)(projectType)
|
|
96
|
+
: 'unknown';
|
|
97
|
+
// Map project type to framework ID for MCP docs resource
|
|
98
|
+
const frameworkIdMap = {
|
|
99
|
+
[utils_1.FlaskProjectType.STANDARD]: 'flask',
|
|
100
|
+
[utils_1.FlaskProjectType.RESTFUL]: 'flask',
|
|
101
|
+
[utils_1.FlaskProjectType.RESTX]: 'flask',
|
|
102
|
+
[utils_1.FlaskProjectType.SMOREST]: 'flask',
|
|
103
|
+
[utils_1.FlaskProjectType.BLUEPRINT]: 'flask',
|
|
104
|
+
};
|
|
105
|
+
const frameworkId = projectType ? frameworkIdMap[projectType] : 'flask';
|
|
106
|
+
const lines = [
|
|
107
|
+
`Project type: ${projectTypeName}`,
|
|
108
|
+
`Framework docs ID: ${frameworkId} (use posthog://docs/frameworks/${frameworkId} for documentation)`,
|
|
109
|
+
];
|
|
110
|
+
if (context.appFile) {
|
|
111
|
+
lines.push(`App file: ${context.appFile}`);
|
|
112
|
+
}
|
|
113
|
+
return lines;
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
ui: {
|
|
117
|
+
successMessage: 'PostHog integration complete',
|
|
118
|
+
estimatedDurationMinutes: 5,
|
|
119
|
+
getOutroChanges: (context) => {
|
|
120
|
+
const projectType = context.projectType;
|
|
121
|
+
const projectTypeName = projectType
|
|
122
|
+
? (0, utils_1.getFlaskProjectTypeName)(projectType)
|
|
123
|
+
: 'Flask';
|
|
124
|
+
return [
|
|
125
|
+
`Analyzed your ${projectTypeName} project structure`,
|
|
126
|
+
`Installed the PostHog Python package`,
|
|
127
|
+
`Configured PostHog in your Flask application`,
|
|
128
|
+
`Added PostHog initialization with automatic event tracking`,
|
|
129
|
+
];
|
|
130
|
+
},
|
|
131
|
+
getOutroNextSteps: () => [
|
|
132
|
+
'Start your Flask development server to see PostHog in action',
|
|
133
|
+
'Visit your PostHog dashboard to see incoming events',
|
|
134
|
+
'Use posthog.identify() to associate events with users',
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Flask wizard powered by the universal agent runner.
|
|
140
|
+
*/
|
|
141
|
+
async function runFlaskWizardAgent(options) {
|
|
142
|
+
if (options.debug) {
|
|
143
|
+
(0, debug_1.enableDebugLogs)();
|
|
144
|
+
}
|
|
145
|
+
// Check Flask version - agent wizard requires >= 2.0.0
|
|
146
|
+
const flaskVersion = await (0, utils_1.getFlaskVersion)(options);
|
|
147
|
+
if (flaskVersion) {
|
|
148
|
+
const coercedVersion = semver.coerce(flaskVersion);
|
|
149
|
+
if (coercedVersion && semver.lt(coercedVersion, MINIMUM_FLASK_VERSION)) {
|
|
150
|
+
const docsUrl = FLASK_AGENT_CONFIG.metadata.unsupportedVersionDocsUrl ??
|
|
151
|
+
FLASK_AGENT_CONFIG.metadata.docsUrl;
|
|
152
|
+
clack_1.default.log.warn(`Sorry: the wizard can't help you with Flask ${flaskVersion}. Upgrade to Flask ${MINIMUM_FLASK_VERSION} or later, or check out the manual setup guide.`);
|
|
153
|
+
clack_1.default.log.info(`Setup Flask manually: ${chalk_1.default.cyan(docsUrl)}`);
|
|
154
|
+
clack_1.default.outro('PostHog wizard will see you next time!');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
await (0, agent_runner_1.runAgentWizard)(FLASK_AGENT_CONFIG, options);
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=flask-wizard-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flask-wizard-agent.js","sourceRoot":"","sources":["../../../src/flask/flask-wizard-agent.ts"],"names":[],"mappings":"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","sourcesContent":["/* Flask wizard using posthog-agent with PostHog MCP */\nimport type { WizardOptions } from '../utils/types';\nimport type { FrameworkConfig } from '../lib/framework-config';\nimport { enableDebugLogs } from '../utils/debug';\nimport { runAgentWizard } from '../lib/agent-runner';\nimport { Integration } from '../lib/constants';\nimport clack from '../utils/clack';\nimport chalk from 'chalk';\nimport * as semver from 'semver';\nimport {\n getFlaskVersion,\n getFlaskProjectType,\n getFlaskProjectTypeName,\n getFlaskVersionBucket,\n FlaskProjectType,\n findFlaskAppFile,\n} from './utils';\n\n/**\n * Flask framework configuration for the universal agent runner\n */\nconst MINIMUM_FLASK_VERSION = '2.0.0';\n\nconst FLASK_AGENT_CONFIG: FrameworkConfig = {\n metadata: {\n name: 'Flask',\n integration: Integration.flask,\n docsUrl: 'https://posthog.com/docs/libraries/python',\n unsupportedVersionDocsUrl: 'https://posthog.com/docs/libraries/python',\n gatherContext: async (options: WizardOptions) => {\n const projectType = await getFlaskProjectType(options);\n const appFile = await findFlaskAppFile(options);\n return { projectType, appFile };\n },\n },\n\n detection: {\n packageName: 'flask',\n packageDisplayName: 'Flask',\n usesPackageJson: false,\n getVersion: (_packageJson: any) => {\n // For Flask, we don't use package.json. Version is extracted separately\n // from requirements.txt or pyproject.toml in the wizard entry point\n return undefined;\n },\n getVersionBucket: getFlaskVersionBucket,\n },\n\n environment: {\n uploadToHosting: false,\n getEnvVars: (apiKey: string, host: string) => ({\n POSTHOG_API_KEY: apiKey,\n POSTHOG_HOST: host,\n }),\n },\n\n analytics: {\n getTags: (context: any) => {\n const projectType = context.projectType as FlaskProjectType;\n return {\n projectType: projectType || 'unknown',\n };\n },\n },\n\n prompts: {\n projectTypeDetection:\n 'This is a Python/Flask project. Look for requirements.txt, pyproject.toml, setup.py, Pipfile, or app.py/wsgi.py to confirm.',\n packageInstallation:\n 'Use pip, poetry, or pipenv based on existing config files (requirements.txt, pyproject.toml, Pipfile). Do not pin the posthog version - just add \"posthog\" without version constraints.',\n getAdditionalContextLines: (context: any) => {\n const projectType = context.projectType as FlaskProjectType;\n const projectTypeName = projectType\n ? getFlaskProjectTypeName(projectType)\n : 'unknown';\n\n // Map project type to framework ID for MCP docs resource\n const frameworkIdMap: Record<FlaskProjectType, string> = {\n [FlaskProjectType.STANDARD]: 'flask',\n [FlaskProjectType.RESTFUL]: 'flask',\n [FlaskProjectType.RESTX]: 'flask',\n [FlaskProjectType.SMOREST]: 'flask',\n [FlaskProjectType.BLUEPRINT]: 'flask',\n };\n\n const frameworkId = projectType ? frameworkIdMap[projectType] : 'flask';\n\n const lines = [\n `Project type: ${projectTypeName}`,\n `Framework docs ID: ${frameworkId} (use posthog://docs/frameworks/${frameworkId} for documentation)`,\n ];\n\n if (context.appFile) {\n lines.push(`App file: ${context.appFile}`);\n }\n\n return lines;\n },\n },\n\n ui: {\n successMessage: 'PostHog integration complete',\n estimatedDurationMinutes: 5,\n getOutroChanges: (context: any) => {\n const projectType = context.projectType as FlaskProjectType;\n const projectTypeName = projectType\n ? getFlaskProjectTypeName(projectType)\n : 'Flask';\n return [\n `Analyzed your ${projectTypeName} project structure`,\n `Installed the PostHog Python package`,\n `Configured PostHog in your Flask application`,\n `Added PostHog initialization with automatic event tracking`,\n ];\n },\n getOutroNextSteps: () => [\n 'Start your Flask development server to see PostHog in action',\n 'Visit your PostHog dashboard to see incoming events',\n 'Use posthog.identify() to associate events with users',\n ],\n },\n};\n\n/**\n * Flask wizard powered by the universal agent runner.\n */\nexport async function runFlaskWizardAgent(\n options: WizardOptions,\n): Promise<void> {\n if (options.debug) {\n enableDebugLogs();\n }\n\n // Check Flask version - agent wizard requires >= 2.0.0\n const flaskVersion = await getFlaskVersion(options);\n\n if (flaskVersion) {\n const coercedVersion = semver.coerce(flaskVersion);\n if (coercedVersion && semver.lt(coercedVersion, MINIMUM_FLASK_VERSION)) {\n const docsUrl =\n FLASK_AGENT_CONFIG.metadata.unsupportedVersionDocsUrl ??\n FLASK_AGENT_CONFIG.metadata.docsUrl;\n\n clack.log.warn(\n `Sorry: the wizard can't help you with Flask ${flaskVersion}. Upgrade to Flask ${MINIMUM_FLASK_VERSION} or later, or check out the manual setup guide.`,\n );\n clack.log.info(`Setup Flask manually: ${chalk.cyan(docsUrl)}`);\n clack.outro('PostHog wizard will see you next time!');\n return;\n }\n }\n\n await runAgentWizard(FLASK_AGENT_CONFIG, options);\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { WizardOptions } from '../utils/types';
|
|
2
|
+
export declare enum FlaskProjectType {
|
|
3
|
+
STANDARD = "standard",// Basic Flask app
|
|
4
|
+
RESTFUL = "restful",// Flask-RESTful API
|
|
5
|
+
RESTX = "restx",// Flask-RESTX (Swagger docs)
|
|
6
|
+
SMOREST = "smorest",// flask-smorest (OpenAPI)
|
|
7
|
+
BLUEPRINT = "blueprint"
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Get Flask version bucket for analytics
|
|
11
|
+
*/
|
|
12
|
+
export declare function getFlaskVersionBucket(version: string | undefined): string;
|
|
13
|
+
/**
|
|
14
|
+
* Extract Flask version from requirements files or pyproject.toml
|
|
15
|
+
*/
|
|
16
|
+
export declare function getFlaskVersion(options: Pick<WizardOptions, 'installDir'>): Promise<string | undefined>;
|
|
17
|
+
/**
|
|
18
|
+
* Detect Flask project type
|
|
19
|
+
*/
|
|
20
|
+
export declare function getFlaskProjectType(options: WizardOptions): Promise<FlaskProjectType>;
|
|
21
|
+
/**
|
|
22
|
+
* Get human-readable name for Flask project type
|
|
23
|
+
*/
|
|
24
|
+
export declare function getFlaskProjectTypeName(projectType: FlaskProjectType): string;
|
|
25
|
+
/**
|
|
26
|
+
* Find the main Flask app file
|
|
27
|
+
*/
|
|
28
|
+
export declare function findFlaskAppFile(options: Pick<WizardOptions, 'installDir'>): Promise<string | undefined>;
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.FlaskProjectType = void 0;
|
|
40
|
+
exports.getFlaskVersionBucket = getFlaskVersionBucket;
|
|
41
|
+
exports.getFlaskVersion = getFlaskVersion;
|
|
42
|
+
exports.getFlaskProjectType = getFlaskProjectType;
|
|
43
|
+
exports.getFlaskProjectTypeName = getFlaskProjectTypeName;
|
|
44
|
+
exports.findFlaskAppFile = findFlaskAppFile;
|
|
45
|
+
const semver_1 = require("semver");
|
|
46
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
47
|
+
const clack_1 = __importDefault(require("../utils/clack"));
|
|
48
|
+
const fs = __importStar(require("node:fs"));
|
|
49
|
+
const path = __importStar(require("node:path"));
|
|
50
|
+
var FlaskProjectType;
|
|
51
|
+
(function (FlaskProjectType) {
|
|
52
|
+
FlaskProjectType["STANDARD"] = "standard";
|
|
53
|
+
FlaskProjectType["RESTFUL"] = "restful";
|
|
54
|
+
FlaskProjectType["RESTX"] = "restx";
|
|
55
|
+
FlaskProjectType["SMOREST"] = "smorest";
|
|
56
|
+
FlaskProjectType["BLUEPRINT"] = "blueprint";
|
|
57
|
+
})(FlaskProjectType || (exports.FlaskProjectType = FlaskProjectType = {}));
|
|
58
|
+
const IGNORE_PATTERNS = [
|
|
59
|
+
'**/node_modules/**',
|
|
60
|
+
'**/dist/**',
|
|
61
|
+
'**/build/**',
|
|
62
|
+
'**/venv/**',
|
|
63
|
+
'**/.venv/**',
|
|
64
|
+
'**/env/**',
|
|
65
|
+
'**/.env/**',
|
|
66
|
+
'**/__pycache__/**',
|
|
67
|
+
'**/migrations/**',
|
|
68
|
+
'**/instance/**',
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* Get Flask version bucket for analytics
|
|
72
|
+
*/
|
|
73
|
+
function getFlaskVersionBucket(version) {
|
|
74
|
+
if (!version) {
|
|
75
|
+
return 'none';
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const minVer = (0, semver_1.minVersion)(version);
|
|
79
|
+
if (!minVer) {
|
|
80
|
+
return 'invalid';
|
|
81
|
+
}
|
|
82
|
+
const majorVersion = (0, semver_1.major)(minVer);
|
|
83
|
+
if (majorVersion >= 2) {
|
|
84
|
+
return `${majorVersion}.x`;
|
|
85
|
+
}
|
|
86
|
+
return `<2.0.0`;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return 'unknown';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extract Flask version from requirements files or pyproject.toml
|
|
94
|
+
*/
|
|
95
|
+
async function getFlaskVersion(options) {
|
|
96
|
+
const { installDir } = options;
|
|
97
|
+
// Check requirements files
|
|
98
|
+
const requirementsFiles = await (0, fast_glob_1.default)(['**/requirements*.txt', '**/pyproject.toml', '**/setup.py', '**/Pipfile'], {
|
|
99
|
+
cwd: installDir,
|
|
100
|
+
ignore: IGNORE_PATTERNS,
|
|
101
|
+
});
|
|
102
|
+
for (const reqFile of requirementsFiles) {
|
|
103
|
+
try {
|
|
104
|
+
const content = fs.readFileSync(path.join(installDir, reqFile), 'utf-8');
|
|
105
|
+
// Try to extract version from requirements.txt format (Flask==3.0.0 or flask>=2.0)
|
|
106
|
+
const requirementsMatch = content.match(/[Ff]lask[=<>~!]+([0-9]+\.[0-9]+(?:\.[0-9]+)?)/);
|
|
107
|
+
if (requirementsMatch) {
|
|
108
|
+
return requirementsMatch[1];
|
|
109
|
+
}
|
|
110
|
+
// Try to extract from pyproject.toml format
|
|
111
|
+
const pyprojectMatch = content.match(/[Ff]lask["\s]*[=<>~!]+\s*["']?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/);
|
|
112
|
+
if (pyprojectMatch) {
|
|
113
|
+
return pyprojectMatch[1];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Skip files that can't be read
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if Flask-RESTful is installed
|
|
125
|
+
*/
|
|
126
|
+
async function hasFlaskRESTful({ installDir, }) {
|
|
127
|
+
const requirementsFiles = await (0, fast_glob_1.default)(['**/requirements*.txt', '**/pyproject.toml', '**/Pipfile'], {
|
|
128
|
+
cwd: installDir,
|
|
129
|
+
ignore: IGNORE_PATTERNS,
|
|
130
|
+
});
|
|
131
|
+
for (const reqFile of requirementsFiles) {
|
|
132
|
+
try {
|
|
133
|
+
const content = fs.readFileSync(path.join(installDir, reqFile), 'utf-8');
|
|
134
|
+
if (content.includes('flask-restful') ||
|
|
135
|
+
content.includes('Flask-RESTful')) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Also check imports in Python files
|
|
144
|
+
const pyFiles = await (0, fast_glob_1.default)(['**/*.py'], {
|
|
145
|
+
cwd: installDir,
|
|
146
|
+
ignore: IGNORE_PATTERNS,
|
|
147
|
+
});
|
|
148
|
+
for (const pyFile of pyFiles) {
|
|
149
|
+
try {
|
|
150
|
+
const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');
|
|
151
|
+
if (content.includes('from flask_restful import') ||
|
|
152
|
+
content.includes('import flask_restful')) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Check if Flask-RESTX is installed
|
|
164
|
+
*/
|
|
165
|
+
async function hasFlaskRESTX({ installDir, }) {
|
|
166
|
+
const requirementsFiles = await (0, fast_glob_1.default)(['**/requirements*.txt', '**/pyproject.toml', '**/Pipfile'], {
|
|
167
|
+
cwd: installDir,
|
|
168
|
+
ignore: IGNORE_PATTERNS,
|
|
169
|
+
});
|
|
170
|
+
for (const reqFile of requirementsFiles) {
|
|
171
|
+
try {
|
|
172
|
+
const content = fs.readFileSync(path.join(installDir, reqFile), 'utf-8');
|
|
173
|
+
if (content.includes('flask-restx') || content.includes('Flask-RESTX')) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Also check imports in Python files
|
|
182
|
+
const pyFiles = await (0, fast_glob_1.default)(['**/*.py'], {
|
|
183
|
+
cwd: installDir,
|
|
184
|
+
ignore: IGNORE_PATTERNS,
|
|
185
|
+
});
|
|
186
|
+
for (const pyFile of pyFiles) {
|
|
187
|
+
try {
|
|
188
|
+
const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');
|
|
189
|
+
if (content.includes('from flask_restx import') ||
|
|
190
|
+
content.includes('import flask_restx')) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if flask-smorest is installed
|
|
202
|
+
*/
|
|
203
|
+
async function hasFlaskSmorest({ installDir, }) {
|
|
204
|
+
const requirementsFiles = await (0, fast_glob_1.default)(['**/requirements*.txt', '**/pyproject.toml', '**/Pipfile'], {
|
|
205
|
+
cwd: installDir,
|
|
206
|
+
ignore: IGNORE_PATTERNS,
|
|
207
|
+
});
|
|
208
|
+
for (const reqFile of requirementsFiles) {
|
|
209
|
+
try {
|
|
210
|
+
const content = fs.readFileSync(path.join(installDir, reqFile), 'utf-8');
|
|
211
|
+
if (content.includes('flask-smorest')) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Also check imports in Python files
|
|
220
|
+
const pyFiles = await (0, fast_glob_1.default)(['**/*.py'], {
|
|
221
|
+
cwd: installDir,
|
|
222
|
+
ignore: IGNORE_PATTERNS,
|
|
223
|
+
});
|
|
224
|
+
for (const pyFile of pyFiles) {
|
|
225
|
+
try {
|
|
226
|
+
const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');
|
|
227
|
+
if (content.includes('from flask_smorest import') ||
|
|
228
|
+
content.includes('import flask_smorest')) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Check if app uses Flask Blueprints
|
|
240
|
+
*/
|
|
241
|
+
async function hasBlueprints({ installDir, }) {
|
|
242
|
+
const pyFiles = await (0, fast_glob_1.default)(['**/*.py'], {
|
|
243
|
+
cwd: installDir,
|
|
244
|
+
ignore: IGNORE_PATTERNS,
|
|
245
|
+
});
|
|
246
|
+
for (const pyFile of pyFiles) {
|
|
247
|
+
try {
|
|
248
|
+
const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');
|
|
249
|
+
if (content.includes('Blueprint(') ||
|
|
250
|
+
content.includes('register_blueprint(') ||
|
|
251
|
+
content.includes('from flask import Blueprint')) {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Detect Flask project type
|
|
263
|
+
*/
|
|
264
|
+
async function getFlaskProjectType(options) {
|
|
265
|
+
const { installDir } = options;
|
|
266
|
+
// Check for Flask-RESTX first (most specific - includes Swagger)
|
|
267
|
+
if (await hasFlaskRESTX({ installDir })) {
|
|
268
|
+
clack_1.default.log.info('Detected Flask-RESTX project');
|
|
269
|
+
return FlaskProjectType.RESTX;
|
|
270
|
+
}
|
|
271
|
+
// Check for flask-smorest (OpenAPI-first)
|
|
272
|
+
if (await hasFlaskSmorest({ installDir })) {
|
|
273
|
+
clack_1.default.log.info('Detected flask-smorest project');
|
|
274
|
+
return FlaskProjectType.SMOREST;
|
|
275
|
+
}
|
|
276
|
+
// Check for Flask-RESTful
|
|
277
|
+
if (await hasFlaskRESTful({ installDir })) {
|
|
278
|
+
clack_1.default.log.info('Detected Flask-RESTful project');
|
|
279
|
+
return FlaskProjectType.RESTFUL;
|
|
280
|
+
}
|
|
281
|
+
// Check for Blueprints (large app structure)
|
|
282
|
+
if (await hasBlueprints({ installDir })) {
|
|
283
|
+
clack_1.default.log.info('Detected Flask project with Blueprints');
|
|
284
|
+
return FlaskProjectType.BLUEPRINT;
|
|
285
|
+
}
|
|
286
|
+
// Default to standard Flask
|
|
287
|
+
clack_1.default.log.info('Detected standard Flask project');
|
|
288
|
+
return FlaskProjectType.STANDARD;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Get human-readable name for Flask project type
|
|
292
|
+
*/
|
|
293
|
+
function getFlaskProjectTypeName(projectType) {
|
|
294
|
+
switch (projectType) {
|
|
295
|
+
case FlaskProjectType.STANDARD:
|
|
296
|
+
return 'Standard Flask';
|
|
297
|
+
case FlaskProjectType.RESTFUL:
|
|
298
|
+
return 'Flask-RESTful';
|
|
299
|
+
case FlaskProjectType.RESTX:
|
|
300
|
+
return 'Flask-RESTX';
|
|
301
|
+
case FlaskProjectType.SMOREST:
|
|
302
|
+
return 'flask-smorest';
|
|
303
|
+
case FlaskProjectType.BLUEPRINT:
|
|
304
|
+
return 'Flask with Blueprints';
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Find the main Flask app file
|
|
309
|
+
*/
|
|
310
|
+
async function findFlaskAppFile(options) {
|
|
311
|
+
const { installDir } = options;
|
|
312
|
+
// Common Flask app file patterns
|
|
313
|
+
const commonPatterns = [
|
|
314
|
+
'**/app.py',
|
|
315
|
+
'**/wsgi.py',
|
|
316
|
+
'**/application.py',
|
|
317
|
+
'**/run.py',
|
|
318
|
+
'**/main.py',
|
|
319
|
+
'**/__init__.py',
|
|
320
|
+
];
|
|
321
|
+
const appFiles = await (0, fast_glob_1.default)(commonPatterns, {
|
|
322
|
+
cwd: installDir,
|
|
323
|
+
ignore: IGNORE_PATTERNS,
|
|
324
|
+
});
|
|
325
|
+
// Look for files with Flask() instantiation or create_app() factory
|
|
326
|
+
for (const appFile of appFiles) {
|
|
327
|
+
try {
|
|
328
|
+
const content = fs.readFileSync(path.join(installDir, appFile), 'utf-8');
|
|
329
|
+
// Check for Flask app instantiation or application factory
|
|
330
|
+
if (content.includes('Flask(__name__)') ||
|
|
331
|
+
content.includes('Flask(') ||
|
|
332
|
+
content.includes('def create_app(')) {
|
|
333
|
+
return appFile;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// If no file with Flask() found, check all Python files
|
|
341
|
+
const allPyFiles = await (0, fast_glob_1.default)(['**/*.py'], {
|
|
342
|
+
cwd: installDir,
|
|
343
|
+
ignore: IGNORE_PATTERNS,
|
|
344
|
+
});
|
|
345
|
+
for (const pyFile of allPyFiles) {
|
|
346
|
+
try {
|
|
347
|
+
const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');
|
|
348
|
+
if (content.includes('Flask(__name__)') ||
|
|
349
|
+
content.includes('def create_app(')) {
|
|
350
|
+
return pyFile;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Return first common pattern file if exists
|
|
358
|
+
if (appFiles.length > 0) {
|
|
359
|
+
return appFiles[0];
|
|
360
|
+
}
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/flask/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { major, minVersion } from 'semver';\nimport fg from 'fast-glob';\nimport clack from '../utils/clack';\nimport type { WizardOptions } from '../utils/types';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport enum FlaskProjectType {\n STANDARD = 'standard', // Basic Flask app\n RESTFUL = 'restful', // Flask-RESTful API\n RESTX = 'restx', // Flask-RESTX (Swagger docs)\n SMOREST = 'smorest', // flask-smorest (OpenAPI)\n BLUEPRINT = 'blueprint', // Large app with blueprints\n}\n\nconst IGNORE_PATTERNS = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/venv/**',\n '**/.venv/**',\n '**/env/**',\n '**/.env/**',\n '**/__pycache__/**',\n '**/migrations/**',\n '**/instance/**',\n];\n\n/**\n * Get Flask version bucket for analytics\n */\nexport function getFlaskVersionBucket(version: string | undefined): string {\n if (!version) {\n return 'none';\n }\n\n try {\n const minVer = minVersion(version);\n if (!minVer) {\n return 'invalid';\n }\n const majorVersion = major(minVer);\n if (majorVersion >= 2) {\n return `${majorVersion}.x`;\n }\n return `<2.0.0`;\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Extract Flask version from requirements files or pyproject.toml\n */\nexport async function getFlaskVersion(\n options: Pick<WizardOptions, 'installDir'>,\n): Promise<string | undefined> {\n const { installDir } = options;\n\n // Check requirements files\n const requirementsFiles = await fg(\n ['**/requirements*.txt', '**/pyproject.toml', '**/setup.py', '**/Pipfile'],\n {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n },\n );\n\n for (const reqFile of requirementsFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, reqFile), 'utf-8');\n\n // Try to extract version from requirements.txt format (Flask==3.0.0 or flask>=2.0)\n const requirementsMatch = content.match(\n /[Ff]lask[=<>~!]+([0-9]+\\.[0-9]+(?:\\.[0-9]+)?)/,\n );\n if (requirementsMatch) {\n return requirementsMatch[1];\n }\n\n // Try to extract from pyproject.toml format\n const pyprojectMatch = content.match(\n /[Ff]lask[\"\\s]*[=<>~!]+\\s*[\"']?([0-9]+\\.[0-9]+(?:\\.[0-9]+)?)/,\n );\n if (pyprojectMatch) {\n return pyprojectMatch[1];\n }\n } catch {\n // Skip files that can't be read\n continue;\n }\n }\n\n return undefined;\n}\n\n/**\n * Check if Flask-RESTful is installed\n */\nasync function hasFlaskRESTful({\n installDir,\n}: Pick<WizardOptions, 'installDir'>): Promise<boolean> {\n const requirementsFiles = await fg(\n ['**/requirements*.txt', '**/pyproject.toml', '**/Pipfile'],\n {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n },\n );\n\n for (const reqFile of requirementsFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, reqFile), 'utf-8');\n if (\n content.includes('flask-restful') ||\n content.includes('Flask-RESTful')\n ) {\n return true;\n }\n } catch {\n continue;\n }\n }\n\n // Also check imports in Python files\n const pyFiles = await fg(['**/*.py'], {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n });\n\n for (const pyFile of pyFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');\n if (\n content.includes('from flask_restful import') ||\n content.includes('import flask_restful')\n ) {\n return true;\n }\n } catch {\n continue;\n }\n }\n\n return false;\n}\n\n/**\n * Check if Flask-RESTX is installed\n */\nasync function hasFlaskRESTX({\n installDir,\n}: Pick<WizardOptions, 'installDir'>): Promise<boolean> {\n const requirementsFiles = await fg(\n ['**/requirements*.txt', '**/pyproject.toml', '**/Pipfile'],\n {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n },\n );\n\n for (const reqFile of requirementsFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, reqFile), 'utf-8');\n if (content.includes('flask-restx') || content.includes('Flask-RESTX')) {\n return true;\n }\n } catch {\n continue;\n }\n }\n\n // Also check imports in Python files\n const pyFiles = await fg(['**/*.py'], {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n });\n\n for (const pyFile of pyFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');\n if (\n content.includes('from flask_restx import') ||\n content.includes('import flask_restx')\n ) {\n return true;\n }\n } catch {\n continue;\n }\n }\n\n return false;\n}\n\n/**\n * Check if flask-smorest is installed\n */\nasync function hasFlaskSmorest({\n installDir,\n}: Pick<WizardOptions, 'installDir'>): Promise<boolean> {\n const requirementsFiles = await fg(\n ['**/requirements*.txt', '**/pyproject.toml', '**/Pipfile'],\n {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n },\n );\n\n for (const reqFile of requirementsFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, reqFile), 'utf-8');\n if (content.includes('flask-smorest')) {\n return true;\n }\n } catch {\n continue;\n }\n }\n\n // Also check imports in Python files\n const pyFiles = await fg(['**/*.py'], {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n });\n\n for (const pyFile of pyFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');\n if (\n content.includes('from flask_smorest import') ||\n content.includes('import flask_smorest')\n ) {\n return true;\n }\n } catch {\n continue;\n }\n }\n\n return false;\n}\n\n/**\n * Check if app uses Flask Blueprints\n */\nasync function hasBlueprints({\n installDir,\n}: Pick<WizardOptions, 'installDir'>): Promise<boolean> {\n const pyFiles = await fg(['**/*.py'], {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n });\n\n for (const pyFile of pyFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');\n if (\n content.includes('Blueprint(') ||\n content.includes('register_blueprint(') ||\n content.includes('from flask import Blueprint')\n ) {\n return true;\n }\n } catch {\n continue;\n }\n }\n\n return false;\n}\n\n/**\n * Detect Flask project type\n */\nexport async function getFlaskProjectType(\n options: WizardOptions,\n): Promise<FlaskProjectType> {\n const { installDir } = options;\n\n // Check for Flask-RESTX first (most specific - includes Swagger)\n if (await hasFlaskRESTX({ installDir })) {\n clack.log.info('Detected Flask-RESTX project');\n return FlaskProjectType.RESTX;\n }\n\n // Check for flask-smorest (OpenAPI-first)\n if (await hasFlaskSmorest({ installDir })) {\n clack.log.info('Detected flask-smorest project');\n return FlaskProjectType.SMOREST;\n }\n\n // Check for Flask-RESTful\n if (await hasFlaskRESTful({ installDir })) {\n clack.log.info('Detected Flask-RESTful project');\n return FlaskProjectType.RESTFUL;\n }\n\n // Check for Blueprints (large app structure)\n if (await hasBlueprints({ installDir })) {\n clack.log.info('Detected Flask project with Blueprints');\n return FlaskProjectType.BLUEPRINT;\n }\n\n // Default to standard Flask\n clack.log.info('Detected standard Flask project');\n return FlaskProjectType.STANDARD;\n}\n\n/**\n * Get human-readable name for Flask project type\n */\nexport function getFlaskProjectTypeName(projectType: FlaskProjectType): string {\n switch (projectType) {\n case FlaskProjectType.STANDARD:\n return 'Standard Flask';\n case FlaskProjectType.RESTFUL:\n return 'Flask-RESTful';\n case FlaskProjectType.RESTX:\n return 'Flask-RESTX';\n case FlaskProjectType.SMOREST:\n return 'flask-smorest';\n case FlaskProjectType.BLUEPRINT:\n return 'Flask with Blueprints';\n }\n}\n\n/**\n * Find the main Flask app file\n */\nexport async function findFlaskAppFile(\n options: Pick<WizardOptions, 'installDir'>,\n): Promise<string | undefined> {\n const { installDir } = options;\n\n // Common Flask app file patterns\n const commonPatterns = [\n '**/app.py',\n '**/wsgi.py',\n '**/application.py',\n '**/run.py',\n '**/main.py',\n '**/__init__.py',\n ];\n\n const appFiles = await fg(commonPatterns, {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n });\n\n // Look for files with Flask() instantiation or create_app() factory\n for (const appFile of appFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, appFile), 'utf-8');\n // Check for Flask app instantiation or application factory\n if (\n content.includes('Flask(__name__)') ||\n content.includes('Flask(') ||\n content.includes('def create_app(')\n ) {\n return appFile;\n }\n } catch {\n continue;\n }\n }\n\n // If no file with Flask() found, check all Python files\n const allPyFiles = await fg(['**/*.py'], {\n cwd: installDir,\n ignore: IGNORE_PATTERNS,\n });\n\n for (const pyFile of allPyFiles) {\n try {\n const content = fs.readFileSync(path.join(installDir, pyFile), 'utf-8');\n if (\n content.includes('Flask(__name__)') ||\n content.includes('def create_app(')\n ) {\n return pyFile;\n }\n } catch {\n continue;\n }\n }\n\n // Return first common pattern file if exists\n if (appFiles.length > 0) {\n return appFiles[0];\n }\n\n return undefined;\n}\n"]}
|
|
@@ -61,7 +61,20 @@ var AgentErrorType;
|
|
|
61
61
|
/**
|
|
62
62
|
* Package managers that can be used to run commands.
|
|
63
63
|
*/
|
|
64
|
-
const PACKAGE_MANAGERS = [
|
|
64
|
+
const PACKAGE_MANAGERS = [
|
|
65
|
+
// JavaScript
|
|
66
|
+
'npm',
|
|
67
|
+
'pnpm',
|
|
68
|
+
'yarn',
|
|
69
|
+
'bun',
|
|
70
|
+
'npx',
|
|
71
|
+
// Python
|
|
72
|
+
'pip',
|
|
73
|
+
'pip3',
|
|
74
|
+
'poetry',
|
|
75
|
+
'pipenv',
|
|
76
|
+
'uv',
|
|
77
|
+
];
|
|
65
78
|
/**
|
|
66
79
|
* Safe scripts/commands that can be run with any package manager.
|
|
67
80
|
* Uses startsWith matching, so 'build' matches 'build', 'build:prod', etc.
|