@posthog/wizard 1.33.0 → 1.35.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/bin.js +9 -0
- package/dist/bin.js.map +1 -1
- package/dist/src/javascript-node/javascript-node-wizard-agent.d.ts +4 -0
- package/dist/src/javascript-node/javascript-node-wizard-agent.js +61 -0
- package/dist/src/javascript-node/javascript-node-wizard-agent.js.map +1 -0
- package/dist/src/javascript-web/javascript-web-wizard-agent.d.ts +3 -0
- package/dist/src/javascript-web/javascript-web-wizard-agent.js +150 -0
- package/dist/src/javascript-web/javascript-web-wizard-agent.js.map +1 -0
- package/dist/src/javascript-web/utils.d.ts +23 -0
- package/dist/src/javascript-web/utils.js +99 -0
- package/dist/src/javascript-web/utils.js.map +1 -0
- package/dist/src/lib/__tests__/agent-interface.test.js +1 -0
- package/dist/src/lib/__tests__/agent-interface.test.js.map +1 -1
- package/dist/src/lib/agent-interface.d.ts +5 -0
- package/dist/src/lib/agent-interface.js +19 -2
- package/dist/src/lib/agent-interface.js.map +1 -1
- package/dist/src/lib/agent-runner.js +8 -4
- package/dist/src/lib/agent-runner.js.map +1 -1
- package/dist/src/lib/api.js +3 -0
- package/dist/src/lib/api.js.map +1 -1
- package/dist/src/lib/constants.d.ts +6 -1
- package/dist/src/lib/constants.js +15 -1
- package/dist/src/lib/constants.js.map +1 -1
- package/dist/src/lib/middleware/benchmark.d.ts +54 -0
- package/dist/src/lib/middleware/benchmark.js +49 -0
- package/dist/src/lib/middleware/benchmark.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/cache-tracker.d.ts +44 -0
- package/dist/src/lib/middleware/benchmarks/cache-tracker.js +81 -0
- package/dist/src/lib/middleware/benchmarks/cache-tracker.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/compaction-tracker.d.ts +29 -0
- package/dist/src/lib/middleware/benchmarks/compaction-tracker.js +60 -0
- package/dist/src/lib/middleware/benchmarks/compaction-tracker.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/context-size-tracker.d.ts +26 -0
- package/dist/src/lib/middleware/benchmarks/context-size-tracker.js +56 -0
- package/dist/src/lib/middleware/benchmarks/context-size-tracker.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/cost-tracker.d.ts +16 -0
- package/dist/src/lib/middleware/benchmarks/cost-tracker.js +76 -0
- package/dist/src/lib/middleware/benchmarks/cost-tracker.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/duration-tracker.d.ts +20 -0
- package/dist/src/lib/middleware/benchmarks/duration-tracker.js +40 -0
- package/dist/src/lib/middleware/benchmarks/duration-tracker.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/index.d.ts +9 -0
- package/dist/src/lib/middleware/benchmarks/index.js +60 -0
- package/dist/src/lib/middleware/benchmarks/index.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/json-writer.d.ts +15 -0
- package/dist/src/lib/middleware/benchmarks/json-writer.js +145 -0
- package/dist/src/lib/middleware/benchmarks/json-writer.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/summary.d.ts +9 -0
- package/dist/src/lib/middleware/benchmarks/summary.js +106 -0
- package/dist/src/lib/middleware/benchmarks/summary.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/token-tracker.d.ts +40 -0
- package/dist/src/lib/middleware/benchmarks/token-tracker.js +77 -0
- package/dist/src/lib/middleware/benchmarks/token-tracker.js.map +1 -0
- package/dist/src/lib/middleware/benchmarks/turn-counter.d.ts +34 -0
- package/dist/src/lib/middleware/benchmarks/turn-counter.js +59 -0
- package/dist/src/lib/middleware/benchmarks/turn-counter.js.map +1 -0
- package/dist/src/lib/middleware/config.d.ts +24 -0
- package/dist/src/lib/middleware/config.js +78 -0
- package/dist/src/lib/middleware/config.js.map +1 -0
- package/dist/src/lib/middleware/index.d.ts +11 -0
- package/dist/src/lib/middleware/index.js +18 -0
- package/dist/src/lib/middleware/index.js.map +1 -0
- package/dist/src/lib/middleware/phase-detector.d.ts +7 -0
- package/dist/src/lib/middleware/phase-detector.js +64 -0
- package/dist/src/lib/middleware/phase-detector.js.map +1 -0
- package/dist/src/lib/middleware/pipeline.d.ts +29 -0
- package/dist/src/lib/middleware/pipeline.js +82 -0
- package/dist/src/lib/middleware/pipeline.js.map +1 -0
- package/dist/src/lib/middleware/types.d.ts +40 -0
- package/dist/src/lib/middleware/types.js +9 -0
- package/dist/src/lib/middleware/types.js.map +1 -0
- package/dist/src/lib/package-manager-detection.d.ts +1 -0
- package/dist/src/lib/package-manager-detection.js +17 -0
- package/dist/src/lib/package-manager-detection.js.map +1 -1
- package/dist/src/lib/registry.js +8 -0
- package/dist/src/lib/registry.js.map +1 -1
- package/dist/src/python/python-wizard-agent.js +1 -78
- package/dist/src/python/python-wizard-agent.js.map +1 -1
- package/dist/src/rails/rails-wizard-agent.d.ts +8 -0
- package/dist/src/rails/rails-wizard-agent.js +90 -0
- package/dist/src/rails/rails-wizard-agent.js.map +1 -0
- package/dist/src/rails/utils.d.ts +37 -0
- package/dist/src/rails/utils.js +187 -0
- package/dist/src/rails/utils.js.map +1 -0
- package/dist/src/ruby/ruby-wizard-agent.d.ts +7 -0
- package/dist/src/ruby/ruby-wizard-agent.js +113 -0
- package/dist/src/ruby/ruby-wizard-agent.js.map +1 -0
- package/dist/src/ruby/utils.d.ts +25 -0
- package/dist/src/ruby/utils.js +158 -0
- package/dist/src/ruby/utils.js.map +1 -0
- package/dist/src/run.d.ts +2 -0
- package/dist/src/run.js +8 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +1 -1
- package/dist/src/utils/clack-utils.js +26 -7
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/debug.d.ts +11 -3
- package/dist/src/utils/debug.js +25 -6
- package/dist/src/utils/debug.js.map +1 -1
- package/dist/src/utils/oauth.js +1 -0
- package/dist/src/utils/oauth.js.map +1 -1
- package/dist/src/utils/types.d.ts +11 -0
- package/dist/src/utils/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -161,88 +161,11 @@ exports.PYTHON_AGENT_CONFIG = {
|
|
|
161
161
|
const packageManagerName = context.packageManager
|
|
162
162
|
? (0, utils_1.getPackageManagerName)(context.packageManager)
|
|
163
163
|
: 'unknown';
|
|
164
|
-
|
|
164
|
+
return [
|
|
165
165
|
`Package manager: ${packageManagerName}`,
|
|
166
166
|
`Framework docs ID: python (use posthog://docs/frameworks/python for documentation)`,
|
|
167
167
|
`Project type: Generic Python application (CLI, script, worker, data pipeline, etc.)`,
|
|
168
|
-
``,
|
|
169
|
-
`## CRITICAL: Python PostHog Best Practices`,
|
|
170
|
-
``,
|
|
171
|
-
`### 1. Use Instance-Based API (REQUIRED)`,
|
|
172
|
-
`Always use the Posthog() class constructor instead of module-level posthog:`,
|
|
173
|
-
``,
|
|
174
|
-
`CORRECT:`,
|
|
175
|
-
`from posthog import Posthog`,
|
|
176
|
-
`posthog_client = Posthog(`,
|
|
177
|
-
` api_key="your_api_key",`,
|
|
178
|
-
` host="https://us.i.posthog.com",`,
|
|
179
|
-
` debug=False,`,
|
|
180
|
-
` enable_exception_autocapture=True, # Auto-capture exceptions`,
|
|
181
|
-
`)`,
|
|
182
|
-
``,
|
|
183
|
-
`INCORRECT (DO NOT USE):`,
|
|
184
|
-
`import posthog`,
|
|
185
|
-
`posthog.api_key = "your_api_key" # Don't use module-level config`,
|
|
186
|
-
``,
|
|
187
|
-
`### 2. Enable Exception Autocapture`,
|
|
188
|
-
`ALWAYS include enable_exception_autocapture=True in the Posthog() initialization to automatically track exceptions.`,
|
|
189
|
-
``,
|
|
190
|
-
`### 3. NEVER Send PII (Personally Identifiable Information)`,
|
|
191
|
-
`DO NOT include in event properties:`,
|
|
192
|
-
`- Email addresses`,
|
|
193
|
-
`- Full names`,
|
|
194
|
-
`- Phone numbers`,
|
|
195
|
-
`- Physical addresses`,
|
|
196
|
-
`- IP addresses`,
|
|
197
|
-
`- Any user-generated content (messages, comments, form submissions)`,
|
|
198
|
-
``,
|
|
199
|
-
`SAFE event properties:`,
|
|
200
|
-
`posthog_client.capture('contact_form_submitted', properties={`,
|
|
201
|
-
` 'message_length': len(message), # Metadata is OK`,
|
|
202
|
-
` 'has_email': bool(email), # Boolean flags are OK`,
|
|
203
|
-
` 'form_type': 'contact' # Categories are OK`,
|
|
204
|
-
`})`,
|
|
205
|
-
``,
|
|
206
|
-
`UNSAFE (DO NOT DO THIS):`,
|
|
207
|
-
`posthog_client.capture('form_submitted', properties={`,
|
|
208
|
-
` 'email': user_email, # NEVER send actual email`,
|
|
209
|
-
` 'message': message_content, # NEVER send user content`,
|
|
210
|
-
` 'name': user_name # NEVER send names`,
|
|
211
|
-
`})`,
|
|
212
|
-
``,
|
|
213
|
-
`### 4. Implement Graceful Shutdown`,
|
|
214
|
-
`ALWAYS call posthog_client.shutdown() when your application exits to ensure all events are flushed:`,
|
|
215
|
-
``,
|
|
216
|
-
`import atexit`,
|
|
217
|
-
`atexit.register(posthog_client.shutdown) # Ensures events are sent on exit`,
|
|
218
|
-
``,
|
|
219
|
-
`For Django, use AppConfig.ready() to register the shutdown handler.`,
|
|
220
|
-
`For Flask, use @app.teardown_appcontext or atexit.`,
|
|
221
|
-
`For scripts/workers, call shutdown() explicitly or use atexit.`,
|
|
222
|
-
``,
|
|
223
|
-
`### 5. For Django Projects`,
|
|
224
|
-
`Initialize PostHog in your AppConfig.ready() method:`,
|
|
225
|
-
``,
|
|
226
|
-
`from django.apps import AppConfig`,
|
|
227
|
-
`from posthog import Posthog`,
|
|
228
|
-
``,
|
|
229
|
-
`class YourAppConfig(AppConfig):`,
|
|
230
|
-
` posthog_client = None`,
|
|
231
|
-
` `,
|
|
232
|
-
` def ready(self):`,
|
|
233
|
-
` if YourAppConfig.posthog_client is None:`,
|
|
234
|
-
` YourAppConfig.posthog_client = Posthog(`,
|
|
235
|
-
` settings.POSTHOG_API_KEY,`,
|
|
236
|
-
` host=settings.POSTHOG_HOST,`,
|
|
237
|
-
` debug=settings.DEBUG,`,
|
|
238
|
-
` enable_exception_autocapture=True,`,
|
|
239
|
-
` )`,
|
|
240
|
-
` import atexit`,
|
|
241
|
-
` atexit.register(YourAppConfig.posthog_client.shutdown)`,
|
|
242
|
-
``,
|
|
243
|
-
`IMPORTANT: These best practices are MANDATORY. The implementation will fail review if they are not followed.`,
|
|
244
168
|
];
|
|
245
|
-
return lines;
|
|
246
169
|
},
|
|
247
170
|
},
|
|
248
171
|
ui: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"python-wizard-agent.js","sourceRoot":"","sources":["../../../src/python/python-wizard-agent.ts"],"names":[],"mappings":"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","sourcesContent":["/* Generic Python language wizard using posthog-agent with PostHog MCP */\nimport type { WizardOptions } from '../utils/types';\nimport type { FrameworkConfig } from '../lib/framework-config';\nimport { PYTHON_PACKAGE_INSTALLATION } from '../lib/framework-config';\nimport { detectPythonPackageManagers } from '../lib/package-manager-detection';\nimport { Integration } from '../lib/constants';\nimport fg from 'fast-glob';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n getPythonVersion,\n getPythonVersionBucket,\n detectPackageManager,\n getPackageManagerName,\n PythonPackageManager,\n} from './utils';\n\ntype PythonContext = {\n packageManager?: PythonPackageManager;\n};\n\nexport const PYTHON_AGENT_CONFIG: FrameworkConfig<PythonContext> = {\n metadata: {\n name: 'Python Language',\n integration: Integration.python,\n beta: true,\n docsUrl: 'https://posthog.com/docs/libraries/python',\n gatherContext: async (options: WizardOptions) => {\n const packageManager = await detectPackageManager(options);\n return { packageManager };\n },\n },\n\n detection: {\n packageName: 'python',\n packageDisplayName: 'Python',\n usesPackageJson: false,\n getVersion: () => undefined,\n getVersionBucket: getPythonVersionBucket,\n minimumVersion: '3.8.0',\n getInstalledVersion: (options: WizardOptions) =>\n Promise.resolve(getPythonVersion(options)),\n detect: async (options) => {\n const { installDir } = options;\n\n // Look for Python package management files\n const pythonConfigFiles = await fg(\n [\n '**/requirements*.txt',\n '**/pyproject.toml',\n '**/setup.py',\n '**/Pipfile',\n ],\n {\n cwd: installDir,\n ignore: ['**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**'],\n },\n );\n\n if (pythonConfigFiles.length === 0) {\n return false;\n }\n\n // Make sure this isn't Django or Flask (those should be detected first)\n // Check for Django\n const managePyMatches = await fg('**/manage.py', {\n cwd: installDir,\n ignore: ['**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**'],\n });\n\n for (const match of managePyMatches) {\n try {\n const content = fs.readFileSync(\n path.join(installDir, match),\n 'utf-8',\n );\n if (\n content.includes('django') ||\n content.includes('DJANGO_SETTINGS_MODULE')\n ) {\n return false; // Django detected, use django agent instead\n }\n } catch {\n continue;\n }\n }\n\n // Check for Flask\n for (const configFile of pythonConfigFiles) {\n try {\n const content = fs.readFileSync(\n path.join(installDir, configFile),\n 'utf-8',\n );\n if (\n /^flask([<>=~!]|$|\\s)/im.test(content) ||\n /[\"']flask[\"']/i.test(content)\n ) {\n return false; // Flask detected, use flask agent instead\n }\n } catch {\n continue;\n }\n }\n\n const pyFiles = await fg(\n ['**/app.py', '**/wsgi.py', '**/application.py', '**/__init__.py'],\n {\n cwd: installDir,\n ignore: [\n '**/venv/**',\n '**/.venv/**',\n '**/env/**',\n '**/.env/**',\n '**/__pycache__/**',\n ],\n },\n );\n\n for (const pyFile of pyFiles) {\n try {\n const content = fs.readFileSync(\n path.join(installDir, pyFile),\n 'utf-8',\n );\n if (\n content.includes('from flask import') ||\n content.includes('import flask') ||\n /Flask\\s*\\(/.test(content)\n ) {\n return false; // Flask detected, use flask agent instead\n }\n } catch {\n continue;\n }\n }\n\n // If we have Python config files but it's not Django or Flask, it's a generic Python project\n return true;\n },\n detectPackageManager: detectPythonPackageManagers,\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) => {\n const packageManagerName = context.packageManager\n ? getPackageManagerName(context.packageManager)\n : 'unknown';\n return {\n packageManager: packageManagerName,\n };\n },\n },\n\n prompts: {\n packageInstallation: PYTHON_PACKAGE_INSTALLATION,\n projectTypeDetection:\n 'This is a generic Python project. Look for requirements.txt, pyproject.toml, setup.py, or Pipfile to confirm.',\n getAdditionalContextLines: (context) => {\n const packageManagerName = context.packageManager\n ? getPackageManagerName(context.packageManager)\n : 'unknown';\n\n const lines = [\n `Package manager: ${packageManagerName}`,\n `Framework docs ID: python (use posthog://docs/frameworks/python for documentation)`,\n `Project type: Generic Python application (CLI, script, worker, data pipeline, etc.)`,\n ``,\n `## CRITICAL: Python PostHog Best Practices`,\n ``,\n `### 1. Use Instance-Based API (REQUIRED)`,\n `Always use the Posthog() class constructor instead of module-level posthog:`,\n ``,\n `CORRECT:`,\n `from posthog import Posthog`,\n `posthog_client = Posthog(`,\n ` api_key=\"your_api_key\",`,\n ` host=\"https://us.i.posthog.com\",`,\n ` debug=False,`,\n ` enable_exception_autocapture=True, # Auto-capture exceptions`,\n `)`,\n ``,\n `INCORRECT (DO NOT USE):`,\n `import posthog`,\n `posthog.api_key = \"your_api_key\" # Don't use module-level config`,\n ``,\n `### 2. Enable Exception Autocapture`,\n `ALWAYS include enable_exception_autocapture=True in the Posthog() initialization to automatically track exceptions.`,\n ``,\n `### 3. NEVER Send PII (Personally Identifiable Information)`,\n `DO NOT include in event properties:`,\n `- Email addresses`,\n `- Full names`,\n `- Phone numbers`,\n `- Physical addresses`,\n `- IP addresses`,\n `- Any user-generated content (messages, comments, form submissions)`,\n ``,\n `SAFE event properties:`,\n `posthog_client.capture('contact_form_submitted', properties={`,\n ` 'message_length': len(message), # Metadata is OK`,\n ` 'has_email': bool(email), # Boolean flags are OK`,\n ` 'form_type': 'contact' # Categories are OK`,\n `})`,\n ``,\n `UNSAFE (DO NOT DO THIS):`,\n `posthog_client.capture('form_submitted', properties={`,\n ` 'email': user_email, # NEVER send actual email`,\n ` 'message': message_content, # NEVER send user content`,\n ` 'name': user_name # NEVER send names`,\n `})`,\n ``,\n `### 4. Implement Graceful Shutdown`,\n `ALWAYS call posthog_client.shutdown() when your application exits to ensure all events are flushed:`,\n ``,\n `import atexit`,\n `atexit.register(posthog_client.shutdown) # Ensures events are sent on exit`,\n ``,\n `For Django, use AppConfig.ready() to register the shutdown handler.`,\n `For Flask, use @app.teardown_appcontext or atexit.`,\n `For scripts/workers, call shutdown() explicitly or use atexit.`,\n ``,\n `### 5. For Django Projects`,\n `Initialize PostHog in your AppConfig.ready() method:`,\n ``,\n `from django.apps import AppConfig`,\n `from posthog import Posthog`,\n ``,\n `class YourAppConfig(AppConfig):`,\n ` posthog_client = None`,\n ` `,\n ` def ready(self):`,\n ` if YourAppConfig.posthog_client is None:`,\n ` YourAppConfig.posthog_client = Posthog(`,\n ` settings.POSTHOG_API_KEY,`,\n ` host=settings.POSTHOG_HOST,`,\n ` debug=settings.DEBUG,`,\n ` enable_exception_autocapture=True,`,\n ` )`,\n ` import atexit`,\n ` atexit.register(YourAppConfig.posthog_client.shutdown)`,\n ``,\n `IMPORTANT: These best practices are MANDATORY. The implementation will fail review if they are not followed.`,\n ];\n\n return lines;\n },\n },\n\n ui: {\n successMessage: 'PostHog integration complete',\n estimatedDurationMinutes: 5,\n getOutroChanges: (context) => {\n const packageManagerName = context.packageManager\n ? getPackageManagerName(context.packageManager)\n : 'package manager';\n return [\n `Analyzed your Python project structure`,\n `Installed the PostHog Python package using ${packageManagerName}`,\n `Created PostHog initialization using instance-based API (Posthog class)`,\n `Configured exception autocapture and graceful shutdown`,\n `Added example code for events, feature flags, and error capture (without PII)`,\n ];\n },\n getOutroNextSteps: () => [\n 'Use Posthog() class (not module-level posthog) with enable_exception_autocapture=True',\n 'Call posthog_client.shutdown() on application exit (use atexit.register)',\n 'NEVER send PII in event properties (no emails, names, or user content)',\n 'Use posthog_client.capture() for events and posthog_client.identify() for users',\n 'Visit your PostHog dashboard to see incoming events',\n ],\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"python-wizard-agent.js","sourceRoot":"","sources":["../../../src/python/python-wizard-agent.ts"],"names":[],"mappings":"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","sourcesContent":["/* Generic Python wizard using posthog-agent with PostHog MCP */\nimport type { WizardOptions } from '../utils/types';\nimport type { FrameworkConfig } from '../lib/framework-config';\nimport { PYTHON_PACKAGE_INSTALLATION } from '../lib/framework-config';\nimport { detectPythonPackageManagers } from '../lib/package-manager-detection';\nimport { Integration } from '../lib/constants';\nimport fg from 'fast-glob';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n getPythonVersion,\n getPythonVersionBucket,\n detectPackageManager,\n getPackageManagerName,\n PythonPackageManager,\n} from './utils';\n\ntype PythonContext = {\n packageManager?: PythonPackageManager;\n};\n\nexport const PYTHON_AGENT_CONFIG: FrameworkConfig<PythonContext> = {\n metadata: {\n name: 'Python Language',\n integration: Integration.python,\n beta: true,\n docsUrl: 'https://posthog.com/docs/libraries/python',\n gatherContext: async (options: WizardOptions) => {\n const packageManager = await detectPackageManager(options);\n return { packageManager };\n },\n },\n\n detection: {\n packageName: 'python',\n packageDisplayName: 'Python',\n usesPackageJson: false,\n getVersion: () => undefined,\n getVersionBucket: getPythonVersionBucket,\n minimumVersion: '3.8.0',\n getInstalledVersion: (options: WizardOptions) =>\n Promise.resolve(getPythonVersion(options)),\n detect: async (options) => {\n const { installDir } = options;\n\n // Look for Python package management files\n const pythonConfigFiles = await fg(\n [\n '**/requirements*.txt',\n '**/pyproject.toml',\n '**/setup.py',\n '**/Pipfile',\n ],\n {\n cwd: installDir,\n ignore: ['**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**'],\n },\n );\n\n if (pythonConfigFiles.length === 0) {\n return false;\n }\n\n // Make sure this isn't Django or Flask (those should be detected first)\n // Check for Django\n const managePyMatches = await fg('**/manage.py', {\n cwd: installDir,\n ignore: ['**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**'],\n });\n\n for (const match of managePyMatches) {\n try {\n const content = fs.readFileSync(\n path.join(installDir, match),\n 'utf-8',\n );\n if (\n content.includes('django') ||\n content.includes('DJANGO_SETTINGS_MODULE')\n ) {\n return false; // Django detected, use django agent instead\n }\n } catch {\n continue;\n }\n }\n\n // Check for Flask\n for (const configFile of pythonConfigFiles) {\n try {\n const content = fs.readFileSync(\n path.join(installDir, configFile),\n 'utf-8',\n );\n if (\n /^flask([<>=~!]|$|\\s)/im.test(content) ||\n /[\"']flask[\"']/i.test(content)\n ) {\n return false; // Flask detected, use flask agent instead\n }\n } catch {\n continue;\n }\n }\n\n const pyFiles = await fg(\n ['**/app.py', '**/wsgi.py', '**/application.py', '**/__init__.py'],\n {\n cwd: installDir,\n ignore: [\n '**/venv/**',\n '**/.venv/**',\n '**/env/**',\n '**/.env/**',\n '**/__pycache__/**',\n ],\n },\n );\n\n for (const pyFile of pyFiles) {\n try {\n const content = fs.readFileSync(\n path.join(installDir, pyFile),\n 'utf-8',\n );\n if (\n content.includes('from flask import') ||\n content.includes('import flask') ||\n /Flask\\s*\\(/.test(content)\n ) {\n return false; // Flask detected, use flask agent instead\n }\n } catch {\n continue;\n }\n }\n\n // If we have Python config files but it's not Django or Flask, it's a generic Python project\n return true;\n },\n detectPackageManager: detectPythonPackageManagers,\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) => {\n const packageManagerName = context.packageManager\n ? getPackageManagerName(context.packageManager)\n : 'unknown';\n return {\n packageManager: packageManagerName,\n };\n },\n },\n\n prompts: {\n packageInstallation: PYTHON_PACKAGE_INSTALLATION,\n projectTypeDetection:\n 'This is a generic Python project. Look for requirements.txt, pyproject.toml, setup.py, or Pipfile to confirm.',\n getAdditionalContextLines: (context) => {\n const packageManagerName = context.packageManager\n ? getPackageManagerName(context.packageManager)\n : 'unknown';\n\n return [\n `Package manager: ${packageManagerName}`,\n `Framework docs ID: python (use posthog://docs/frameworks/python for documentation)`,\n `Project type: Generic Python application (CLI, script, worker, data pipeline, etc.)`,\n ];\n },\n },\n\n ui: {\n successMessage: 'PostHog integration complete',\n estimatedDurationMinutes: 5,\n getOutroChanges: (context) => {\n const packageManagerName = context.packageManager\n ? getPackageManagerName(context.packageManager)\n : 'package manager';\n return [\n `Analyzed your Python project structure`,\n `Installed the PostHog Python package using ${packageManagerName}`,\n `Created PostHog initialization using instance-based API (Posthog class)`,\n `Configured exception autocapture and graceful shutdown`,\n `Added example code for events, feature flags, and error capture (without PII)`,\n ];\n },\n getOutroNextSteps: () => [\n 'Use Posthog() class (not module-level posthog) with enable_exception_autocapture=True',\n 'Call posthog_client.shutdown() on application exit (use atexit.register)',\n 'NEVER send PII in event properties (no emails, names, or user content)',\n 'Use posthog_client.capture() for events and posthog_client.identify() for users',\n 'Visit your PostHog dashboard to see incoming events',\n ],\n },\n};\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FrameworkConfig } from '../lib/framework-config';
|
|
2
|
+
import { RailsProjectType } from './utils';
|
|
3
|
+
type RailsContext = {
|
|
4
|
+
projectType?: RailsProjectType;
|
|
5
|
+
initializersDir?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const RAILS_AGENT_CONFIG: FrameworkConfig<RailsContext>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RAILS_AGENT_CONFIG = void 0;
|
|
4
|
+
const package_manager_detection_1 = require("../lib/package-manager-detection");
|
|
5
|
+
const constants_1 = require("../lib/constants");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
exports.RAILS_AGENT_CONFIG = {
|
|
8
|
+
metadata: {
|
|
9
|
+
name: 'Ruby on Rails',
|
|
10
|
+
integration: constants_1.Integration.rails,
|
|
11
|
+
beta: true,
|
|
12
|
+
docsUrl: 'https://posthog.com/docs/libraries/ruby-on-rails',
|
|
13
|
+
unsupportedVersionDocsUrl: 'https://posthog.com/docs/libraries/ruby',
|
|
14
|
+
gatherContext: (options) => {
|
|
15
|
+
const projectType = (0, utils_1.getRailsProjectType)(options);
|
|
16
|
+
const initializersDir = (0, utils_1.findInitializersDir)(options);
|
|
17
|
+
return Promise.resolve({ projectType, initializersDir });
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
detection: {
|
|
21
|
+
packageName: 'rails',
|
|
22
|
+
packageDisplayName: 'Ruby on Rails',
|
|
23
|
+
usesPackageJson: false,
|
|
24
|
+
getVersion: () => undefined,
|
|
25
|
+
getVersionBucket: utils_1.getRailsVersionBucket,
|
|
26
|
+
minimumVersion: '6.0.0',
|
|
27
|
+
getInstalledVersion: (options) => Promise.resolve((0, utils_1.getRailsVersion)(options)),
|
|
28
|
+
detect: async (options) => (0, utils_1.isRailsProject)(options),
|
|
29
|
+
detectPackageManager: package_manager_detection_1.bundlerPackageManager,
|
|
30
|
+
},
|
|
31
|
+
environment: {
|
|
32
|
+
uploadToHosting: false,
|
|
33
|
+
getEnvVars: (apiKey, host) => ({
|
|
34
|
+
POSTHOG_API_KEY: apiKey,
|
|
35
|
+
POSTHOG_HOST: host,
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
analytics: {
|
|
39
|
+
getTags: (context) => ({
|
|
40
|
+
projectType: context.projectType || 'unknown',
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
prompts: {
|
|
44
|
+
projectTypeDetection: 'This is a Ruby on Rails project. Look for Gemfile, config/application.rb, bin/rails, and config/routes.rb to confirm.',
|
|
45
|
+
packageInstallation: "Use Bundler to install gems. Add `gem 'posthog-ruby'` and `gem 'posthog-rails'` to the Gemfile and run `bundle install`. Do not pin specific versions.",
|
|
46
|
+
getAdditionalContextLines: (context) => {
|
|
47
|
+
const projectTypeName = context.projectType
|
|
48
|
+
? (0, utils_1.getRailsProjectTypeName)(context.projectType)
|
|
49
|
+
: 'unknown';
|
|
50
|
+
const lines = [
|
|
51
|
+
`Project type: ${projectTypeName}`,
|
|
52
|
+
`Framework docs ID: ruby-on-rails (use posthog://docs/frameworks/ruby-on-rails for documentation)`,
|
|
53
|
+
];
|
|
54
|
+
if (context.initializersDir) {
|
|
55
|
+
lines.push(`Initializers directory: ${context.initializersDir}`);
|
|
56
|
+
}
|
|
57
|
+
if (context.projectType === utils_1.RailsProjectType.API) {
|
|
58
|
+
lines.push('Note: This is an API-only Rails app — skip frontend posthog-js integration');
|
|
59
|
+
}
|
|
60
|
+
return lines;
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
ui: {
|
|
64
|
+
successMessage: 'PostHog integration complete',
|
|
65
|
+
estimatedDurationMinutes: 5,
|
|
66
|
+
getOutroChanges: (context) => {
|
|
67
|
+
const projectTypeName = context.projectType
|
|
68
|
+
? (0, utils_1.getRailsProjectTypeName)(context.projectType)
|
|
69
|
+
: 'Rails';
|
|
70
|
+
const changes = [
|
|
71
|
+
`Analyzed your ${projectTypeName} project structure`,
|
|
72
|
+
`Installed the posthog-ruby and posthog-rails gems via Bundler`,
|
|
73
|
+
`Created PostHog initializer in config/initializers/posthog.rb`,
|
|
74
|
+
`Configured automatic exception capture and ActiveJob instrumentation`,
|
|
75
|
+
];
|
|
76
|
+
if (context.projectType !== utils_1.RailsProjectType.API) {
|
|
77
|
+
changes.push('Added posthog-js snippet to the layout template for frontend tracking');
|
|
78
|
+
}
|
|
79
|
+
return changes;
|
|
80
|
+
},
|
|
81
|
+
getOutroNextSteps: () => [
|
|
82
|
+
'Start your Rails development server with `bin/rails server`',
|
|
83
|
+
'Visit your PostHog dashboard to see incoming events',
|
|
84
|
+
'Use PostHog.capture() to track custom events',
|
|
85
|
+
'Use PostHog.identify() to associate events with users',
|
|
86
|
+
'Define posthog_distinct_id on your User model for automatic user association',
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=rails-wizard-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rails-wizard-agent.js","sourceRoot":"","sources":["../../../src/rails/rails-wizard-agent.ts"],"names":[],"mappings":"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","sourcesContent":["/* Ruby on Rails wizard using posthog-agent with PostHog MCP */\nimport type { WizardOptions } from '../utils/types';\nimport type { FrameworkConfig } from '../lib/framework-config';\nimport { bundlerPackageManager } from '../lib/package-manager-detection';\nimport { Integration } from '../lib/constants';\nimport {\n getRailsVersion,\n getRailsProjectType,\n getRailsProjectTypeName,\n getRailsVersionBucket,\n RailsProjectType,\n findInitializersDir,\n isRailsProject,\n} from './utils';\n\ntype RailsContext = {\n projectType?: RailsProjectType;\n initializersDir?: string;\n};\n\nexport const RAILS_AGENT_CONFIG: FrameworkConfig<RailsContext> = {\n metadata: {\n name: 'Ruby on Rails',\n integration: Integration.rails,\n beta: true,\n docsUrl: 'https://posthog.com/docs/libraries/ruby-on-rails',\n unsupportedVersionDocsUrl: 'https://posthog.com/docs/libraries/ruby',\n gatherContext: (options: WizardOptions) => {\n const projectType = getRailsProjectType(options);\n const initializersDir = findInitializersDir(options);\n return Promise.resolve({ projectType, initializersDir });\n },\n },\n\n detection: {\n packageName: 'rails',\n packageDisplayName: 'Ruby on Rails',\n usesPackageJson: false,\n getVersion: () => undefined,\n getVersionBucket: getRailsVersionBucket,\n minimumVersion: '6.0.0',\n getInstalledVersion: (options: WizardOptions) =>\n Promise.resolve(getRailsVersion(options)),\n detect: async (options) => isRailsProject(options),\n detectPackageManager: bundlerPackageManager,\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) => ({\n projectType: context.projectType || 'unknown',\n }),\n },\n\n prompts: {\n projectTypeDetection:\n 'This is a Ruby on Rails project. Look for Gemfile, config/application.rb, bin/rails, and config/routes.rb to confirm.',\n packageInstallation:\n \"Use Bundler to install gems. Add `gem 'posthog-ruby'` and `gem 'posthog-rails'` to the Gemfile and run `bundle install`. Do not pin specific versions.\",\n getAdditionalContextLines: (context) => {\n const projectTypeName = context.projectType\n ? getRailsProjectTypeName(context.projectType)\n : 'unknown';\n\n const lines = [\n `Project type: ${projectTypeName}`,\n `Framework docs ID: ruby-on-rails (use posthog://docs/frameworks/ruby-on-rails for documentation)`,\n ];\n\n if (context.initializersDir) {\n lines.push(`Initializers directory: ${context.initializersDir}`);\n }\n\n if (context.projectType === RailsProjectType.API) {\n lines.push(\n 'Note: This is an API-only Rails app — skip frontend posthog-js integration',\n );\n }\n\n return lines;\n },\n },\n\n ui: {\n successMessage: 'PostHog integration complete',\n estimatedDurationMinutes: 5,\n getOutroChanges: (context) => {\n const projectTypeName = context.projectType\n ? getRailsProjectTypeName(context.projectType)\n : 'Rails';\n\n const changes = [\n `Analyzed your ${projectTypeName} project structure`,\n `Installed the posthog-ruby and posthog-rails gems via Bundler`,\n `Created PostHog initializer in config/initializers/posthog.rb`,\n `Configured automatic exception capture and ActiveJob instrumentation`,\n ];\n\n if (context.projectType !== RailsProjectType.API) {\n changes.push(\n 'Added posthog-js snippet to the layout template for frontend tracking',\n );\n }\n\n return changes;\n },\n getOutroNextSteps: () => [\n 'Start your Rails development server with `bin/rails server`',\n 'Visit your PostHog dashboard to see incoming events',\n 'Use PostHog.capture() to track custom events',\n 'Use PostHog.identify() to associate events with users',\n 'Define posthog_distinct_id on your User model for automatic user association',\n ],\n },\n};\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { WizardOptions } from '../utils/types';
|
|
2
|
+
export declare enum RailsProjectType {
|
|
3
|
+
STANDARD = "standard",// Traditional Rails app (rails new)
|
|
4
|
+
API = "api"
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Get Rails version bucket for analytics
|
|
8
|
+
*/
|
|
9
|
+
export declare const getRailsVersionBucket: (version: string | undefined) => string;
|
|
10
|
+
/**
|
|
11
|
+
* Check if a gem is present in the Gemfile
|
|
12
|
+
*/
|
|
13
|
+
export declare function hasGem(gemName: string, options: Pick<WizardOptions, 'installDir'>): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Extract version for a gem from Gemfile
|
|
16
|
+
*/
|
|
17
|
+
export declare function getGemVersion(gemName: string, options: Pick<WizardOptions, 'installDir'>): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Get Rails version from Gemfile
|
|
20
|
+
*/
|
|
21
|
+
export declare function getRailsVersion(options: Pick<WizardOptions, 'installDir'>): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Detect Rails project type
|
|
24
|
+
*/
|
|
25
|
+
export declare function getRailsProjectType(options: WizardOptions): RailsProjectType;
|
|
26
|
+
/**
|
|
27
|
+
* Get human-readable name for Rails project type
|
|
28
|
+
*/
|
|
29
|
+
export declare function getRailsProjectTypeName(projectType: RailsProjectType): string;
|
|
30
|
+
/**
|
|
31
|
+
* Find the Rails initializers directory
|
|
32
|
+
*/
|
|
33
|
+
export declare function findInitializersDir(options: Pick<WizardOptions, 'installDir'>): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Detect if the project is a Rails project by looking for typical Rails files
|
|
36
|
+
*/
|
|
37
|
+
export declare function isRailsProject(options: Pick<WizardOptions, 'installDir'>): Promise<boolean>;
|
|
@@ -0,0 +1,187 @@
|
|
|
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.getRailsVersionBucket = exports.RailsProjectType = void 0;
|
|
40
|
+
exports.hasGem = hasGem;
|
|
41
|
+
exports.getGemVersion = getGemVersion;
|
|
42
|
+
exports.getRailsVersion = getRailsVersion;
|
|
43
|
+
exports.getRailsProjectType = getRailsProjectType;
|
|
44
|
+
exports.getRailsProjectTypeName = getRailsProjectTypeName;
|
|
45
|
+
exports.findInitializersDir = findInitializersDir;
|
|
46
|
+
exports.isRailsProject = isRailsProject;
|
|
47
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
48
|
+
const clack_1 = __importDefault(require("../utils/clack"));
|
|
49
|
+
const semver_1 = require("../utils/semver");
|
|
50
|
+
const fs = __importStar(require("node:fs"));
|
|
51
|
+
const path = __importStar(require("node:path"));
|
|
52
|
+
var RailsProjectType;
|
|
53
|
+
(function (RailsProjectType) {
|
|
54
|
+
RailsProjectType["STANDARD"] = "standard";
|
|
55
|
+
RailsProjectType["API"] = "api";
|
|
56
|
+
})(RailsProjectType || (exports.RailsProjectType = RailsProjectType = {}));
|
|
57
|
+
const IGNORE_PATTERNS = [
|
|
58
|
+
'**/node_modules/**',
|
|
59
|
+
'**/vendor/**',
|
|
60
|
+
'**/vendor/bundle/**',
|
|
61
|
+
'**/tmp/**',
|
|
62
|
+
'**/log/**',
|
|
63
|
+
'**/storage/**',
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* Get Rails version bucket for analytics
|
|
67
|
+
*/
|
|
68
|
+
exports.getRailsVersionBucket = (0, semver_1.createVersionBucket)();
|
|
69
|
+
/**
|
|
70
|
+
* Read and parse Gemfile contents
|
|
71
|
+
*/
|
|
72
|
+
function readGemfile(options) {
|
|
73
|
+
const { installDir } = options;
|
|
74
|
+
const gemfilePath = path.join(installDir, 'Gemfile');
|
|
75
|
+
try {
|
|
76
|
+
return fs.readFileSync(gemfilePath, 'utf-8');
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if a gem is present in the Gemfile
|
|
84
|
+
*/
|
|
85
|
+
function hasGem(gemName, options) {
|
|
86
|
+
const content = readGemfile(options);
|
|
87
|
+
if (!content)
|
|
88
|
+
return false;
|
|
89
|
+
// Match gem declarations like: gem 'rails', gem "rails", gem 'rails', '~> 7.0'
|
|
90
|
+
const gemPattern = new RegExp(`^\\s*gem\\s+['"]${gemName}['"]`, 'im');
|
|
91
|
+
return gemPattern.test(content);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Extract version for a gem from Gemfile
|
|
95
|
+
*/
|
|
96
|
+
function getGemVersion(gemName, options) {
|
|
97
|
+
const content = readGemfile(options);
|
|
98
|
+
if (!content)
|
|
99
|
+
return undefined;
|
|
100
|
+
const versionPattern = new RegExp(`^\\s*gem\\s+['"]${gemName}['"]\\s*,\\s*['"][^0-9]*([0-9]+\\.[0-9]+(?:\\.[0-9]+)?)['"]`, 'im');
|
|
101
|
+
const match = content.match(versionPattern);
|
|
102
|
+
return match?.[1];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get Rails version from Gemfile
|
|
106
|
+
*/
|
|
107
|
+
function getRailsVersion(options) {
|
|
108
|
+
return getGemVersion('rails', options);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Detect Rails project type
|
|
112
|
+
*/
|
|
113
|
+
function getRailsProjectType(options) {
|
|
114
|
+
const { installDir } = options;
|
|
115
|
+
// Check for API-only mode in config/application.rb
|
|
116
|
+
const appConfigPath = path.join(installDir, 'config/application.rb');
|
|
117
|
+
if (fs.existsSync(appConfigPath)) {
|
|
118
|
+
try {
|
|
119
|
+
const content = fs.readFileSync(appConfigPath, 'utf-8');
|
|
120
|
+
if (content.includes('config.api_only = true')) {
|
|
121
|
+
clack_1.default.log.info('Detected Rails API-only project');
|
|
122
|
+
return RailsProjectType.API;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Continue to default
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
clack_1.default.log.info('Detected standard Rails project');
|
|
130
|
+
return RailsProjectType.STANDARD;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get human-readable name for Rails project type
|
|
134
|
+
*/
|
|
135
|
+
function getRailsProjectTypeName(projectType) {
|
|
136
|
+
switch (projectType) {
|
|
137
|
+
case RailsProjectType.STANDARD:
|
|
138
|
+
return 'Standard Rails';
|
|
139
|
+
case RailsProjectType.API:
|
|
140
|
+
return 'Rails API';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Find the Rails initializers directory
|
|
145
|
+
*/
|
|
146
|
+
function findInitializersDir(options) {
|
|
147
|
+
const { installDir } = options;
|
|
148
|
+
const initializersDir = path.join(installDir, 'config/initializers');
|
|
149
|
+
if (fs.existsSync(initializersDir)) {
|
|
150
|
+
return 'config/initializers';
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Detect if the project is a Rails project by looking for typical Rails files
|
|
156
|
+
*/
|
|
157
|
+
async function isRailsProject(options) {
|
|
158
|
+
const { installDir } = options;
|
|
159
|
+
// Check for bin/rails
|
|
160
|
+
const binRailsPath = path.join(installDir, 'bin/rails');
|
|
161
|
+
if (fs.existsSync(binRailsPath)) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
// Check for config/application.rb with Rails reference
|
|
165
|
+
const appConfigPath = path.join(installDir, 'config/application.rb');
|
|
166
|
+
if (fs.existsSync(appConfigPath)) {
|
|
167
|
+
try {
|
|
168
|
+
const content = fs.readFileSync(appConfigPath, 'utf-8');
|
|
169
|
+
if (content.includes('Rails::Application') ||
|
|
170
|
+
content.includes('require "rails"') ||
|
|
171
|
+
content.includes("require 'rails'")) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Continue to other checks
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Check Gemfile for rails gem
|
|
180
|
+
if (hasGem('rails', options)) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
// Check for typical Rails directory structure
|
|
184
|
+
const railsStructureFiles = await (0, fast_glob_1.default)(['config/routes.rb', 'config/environment.rb'], { cwd: installDir, ignore: IGNORE_PATTERNS });
|
|
185
|
+
return railsStructureFiles.length >= 2;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/rails/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import fg from 'fast-glob';\nimport clack from '../utils/clack';\nimport type { WizardOptions } from '../utils/types';\nimport { createVersionBucket } from '../utils/semver';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport enum RailsProjectType {\n STANDARD = 'standard', // Traditional Rails app (rails new)\n API = 'api', // Rails API-only (rails new --api)\n}\n\nconst IGNORE_PATTERNS = [\n '**/node_modules/**',\n '**/vendor/**',\n '**/vendor/bundle/**',\n '**/tmp/**',\n '**/log/**',\n '**/storage/**',\n];\n\n/**\n * Get Rails version bucket for analytics\n */\nexport const getRailsVersionBucket = createVersionBucket();\n\n/**\n * Read and parse Gemfile contents\n */\nfunction readGemfile(\n options: Pick<WizardOptions, 'installDir'>,\n): string | undefined {\n const { installDir } = options;\n\n const gemfilePath = path.join(installDir, 'Gemfile');\n try {\n return fs.readFileSync(gemfilePath, 'utf-8');\n } catch {\n return undefined;\n }\n}\n\n/**\n * Check if a gem is present in the Gemfile\n */\nexport function hasGem(\n gemName: string,\n options: Pick<WizardOptions, 'installDir'>,\n): boolean {\n const content = readGemfile(options);\n if (!content) return false;\n\n // Match gem declarations like: gem 'rails', gem \"rails\", gem 'rails', '~> 7.0'\n const gemPattern = new RegExp(`^\\\\s*gem\\\\s+['\"]${gemName}['\"]`, 'im');\n return gemPattern.test(content);\n}\n\n/**\n * Extract version for a gem from Gemfile\n */\nexport function getGemVersion(\n gemName: string,\n options: Pick<WizardOptions, 'installDir'>,\n): string | undefined {\n const content = readGemfile(options);\n if (!content) return undefined;\n\n const versionPattern = new RegExp(\n `^\\\\s*gem\\\\s+['\"]${gemName}['\"]\\\\s*,\\\\s*['\"][^0-9]*([0-9]+\\\\.[0-9]+(?:\\\\.[0-9]+)?)['\"]`,\n 'im',\n );\n const match = content.match(versionPattern);\n return match?.[1];\n}\n\n/**\n * Get Rails version from Gemfile\n */\nexport function getRailsVersion(\n options: Pick<WizardOptions, 'installDir'>,\n): string | undefined {\n return getGemVersion('rails', options);\n}\n\n/**\n * Detect Rails project type\n */\nexport function getRailsProjectType(options: WizardOptions): RailsProjectType {\n const { installDir } = options;\n\n // Check for API-only mode in config/application.rb\n const appConfigPath = path.join(installDir, 'config/application.rb');\n if (fs.existsSync(appConfigPath)) {\n try {\n const content = fs.readFileSync(appConfigPath, 'utf-8');\n if (content.includes('config.api_only = true')) {\n clack.log.info('Detected Rails API-only project');\n return RailsProjectType.API;\n }\n } catch {\n // Continue to default\n }\n }\n\n clack.log.info('Detected standard Rails project');\n return RailsProjectType.STANDARD;\n}\n\n/**\n * Get human-readable name for Rails project type\n */\nexport function getRailsProjectTypeName(projectType: RailsProjectType): string {\n switch (projectType) {\n case RailsProjectType.STANDARD:\n return 'Standard Rails';\n case RailsProjectType.API:\n return 'Rails API';\n }\n}\n\n/**\n * Find the Rails initializers directory\n */\nexport function findInitializersDir(\n options: Pick<WizardOptions, 'installDir'>,\n): string | undefined {\n const { installDir } = options;\n\n const initializersDir = path.join(installDir, 'config/initializers');\n if (fs.existsSync(initializersDir)) {\n return 'config/initializers';\n }\n\n return undefined;\n}\n\n/**\n * Detect if the project is a Rails project by looking for typical Rails files\n */\nexport async function isRailsProject(\n options: Pick<WizardOptions, 'installDir'>,\n): Promise<boolean> {\n const { installDir } = options;\n\n // Check for bin/rails\n const binRailsPath = path.join(installDir, 'bin/rails');\n if (fs.existsSync(binRailsPath)) {\n return true;\n }\n\n // Check for config/application.rb with Rails reference\n const appConfigPath = path.join(installDir, 'config/application.rb');\n if (fs.existsSync(appConfigPath)) {\n try {\n const content = fs.readFileSync(appConfigPath, 'utf-8');\n if (\n content.includes('Rails::Application') ||\n content.includes('require \"rails\"') ||\n content.includes(\"require 'rails'\")\n ) {\n return true;\n }\n } catch {\n // Continue to other checks\n }\n }\n\n // Check Gemfile for rails gem\n if (hasGem('rails', options)) {\n return true;\n }\n\n // Check for typical Rails directory structure\n const railsStructureFiles = await fg(\n ['config/routes.rb', 'config/environment.rb'],\n { cwd: installDir, ignore: IGNORE_PATTERNS },\n );\n\n return railsStructureFiles.length >= 2;\n}\n"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RUBY_AGENT_CONFIG = void 0;
|
|
4
|
+
const package_manager_detection_1 = require("../lib/package-manager-detection");
|
|
5
|
+
const constants_1 = require("../lib/constants");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
exports.RUBY_AGENT_CONFIG = {
|
|
8
|
+
metadata: {
|
|
9
|
+
name: 'Ruby',
|
|
10
|
+
integration: constants_1.Integration.ruby,
|
|
11
|
+
beta: true,
|
|
12
|
+
docsUrl: 'https://posthog.com/docs/libraries/ruby',
|
|
13
|
+
gatherContext: (options) => {
|
|
14
|
+
const packageManager = (0, utils_1.detectPackageManager)(options);
|
|
15
|
+
return Promise.resolve({ packageManager });
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
detection: {
|
|
19
|
+
packageName: 'ruby',
|
|
20
|
+
packageDisplayName: 'Ruby',
|
|
21
|
+
usesPackageJson: false,
|
|
22
|
+
getVersion: () => undefined,
|
|
23
|
+
getVersionBucket: utils_1.getRubyVersionBucket,
|
|
24
|
+
minimumVersion: '2.7.0',
|
|
25
|
+
getInstalledVersion: (options) => Promise.resolve((0, utils_1.getRubyVersion)(options)),
|
|
26
|
+
detect: async (options) => (0, utils_1.isRubyProject)(options),
|
|
27
|
+
detectPackageManager: package_manager_detection_1.bundlerPackageManager,
|
|
28
|
+
},
|
|
29
|
+
environment: {
|
|
30
|
+
uploadToHosting: false,
|
|
31
|
+
getEnvVars: (apiKey, host) => ({
|
|
32
|
+
POSTHOG_API_KEY: apiKey,
|
|
33
|
+
POSTHOG_HOST: host,
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
analytics: {
|
|
37
|
+
getTags: (context) => {
|
|
38
|
+
const packageManagerName = context.packageManager
|
|
39
|
+
? (0, utils_1.getPackageManagerName)(context.packageManager)
|
|
40
|
+
: 'unknown';
|
|
41
|
+
return {
|
|
42
|
+
packageManager: packageManagerName,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
prompts: {
|
|
47
|
+
projectTypeDetection: 'This is a Ruby project. Look for Gemfile, *.gemspec, .ruby-version, or *.rb files to confirm.',
|
|
48
|
+
packageInstallation: "Use Bundler if a Gemfile is present (add `gem 'posthog-ruby'` and run `bundle install`). Otherwise use `gem install posthog-ruby`. Do not pin a specific version.",
|
|
49
|
+
getAdditionalContextLines: (context) => {
|
|
50
|
+
const packageManagerName = context.packageManager
|
|
51
|
+
? (0, utils_1.getPackageManagerName)(context.packageManager)
|
|
52
|
+
: 'unknown';
|
|
53
|
+
const lines = [
|
|
54
|
+
`Package manager: ${packageManagerName}`,
|
|
55
|
+
`Framework docs ID: ruby (use posthog://docs/frameworks/ruby for documentation)`,
|
|
56
|
+
`Project type: Generic Ruby application (CLI, script, gem, worker, etc.)`,
|
|
57
|
+
``,
|
|
58
|
+
`## CRITICAL: Ruby PostHog Best Practices`,
|
|
59
|
+
``,
|
|
60
|
+
`### 1. Gem Name vs Require`,
|
|
61
|
+
`The gem is named posthog-ruby but you require it as 'posthog':`,
|
|
62
|
+
` gem 'posthog-ruby' # in Gemfile`,
|
|
63
|
+
` require 'posthog' # in code (NOT require 'posthog-ruby')`,
|
|
64
|
+
``,
|
|
65
|
+
`### 2. Use Instance-Based API (REQUIRED for scripts/CLIs)`,
|
|
66
|
+
`Use PostHog::Client.new for scripts and standalone applications:`,
|
|
67
|
+
``,
|
|
68
|
+
`client = PostHog::Client.new(`,
|
|
69
|
+
` api_key: ENV['POSTHOG_API_KEY'],`,
|
|
70
|
+
` host: ENV['POSTHOG_HOST'] || 'https://us.i.posthog.com'`,
|
|
71
|
+
`)`,
|
|
72
|
+
``,
|
|
73
|
+
`### 3. MUST Call shutdown Before Exit`,
|
|
74
|
+
`In scripts and CLIs, you MUST call client.shutdown or events will be lost:`,
|
|
75
|
+
``,
|
|
76
|
+
`begin`,
|
|
77
|
+
` client.capture(distinct_id: 'user_123', event: 'my_event')`,
|
|
78
|
+
`ensure`,
|
|
79
|
+
` client.shutdown`,
|
|
80
|
+
`end`,
|
|
81
|
+
``,
|
|
82
|
+
`### 4. capture_exception Takes Positional Args`,
|
|
83
|
+
`client.capture_exception(exception, distinct_id, additional_properties)`,
|
|
84
|
+
`Do NOT use keyword arguments for capture_exception.`,
|
|
85
|
+
``,
|
|
86
|
+
`### 5. NEVER Send PII`,
|
|
87
|
+
`Do NOT include emails, names, phone numbers, or user content in event properties.`,
|
|
88
|
+
];
|
|
89
|
+
return lines;
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
ui: {
|
|
93
|
+
successMessage: 'PostHog integration complete',
|
|
94
|
+
estimatedDurationMinutes: 5,
|
|
95
|
+
getOutroChanges: (context) => {
|
|
96
|
+
const packageManagerName = context.packageManager
|
|
97
|
+
? (0, utils_1.getPackageManagerName)(context.packageManager)
|
|
98
|
+
: 'package manager';
|
|
99
|
+
return [
|
|
100
|
+
`Analyzed your Ruby project structure`,
|
|
101
|
+
`Installed the posthog-ruby gem using ${packageManagerName}`,
|
|
102
|
+
`Created PostHog initialization with instance-based API`,
|
|
103
|
+
`Configured shutdown handler for proper event flushing`,
|
|
104
|
+
];
|
|
105
|
+
},
|
|
106
|
+
getOutroNextSteps: () => [
|
|
107
|
+
'Use client.capture() for events and client.identify() for users',
|
|
108
|
+
'Always call client.shutdown() before your application exits',
|
|
109
|
+
'Visit your PostHog dashboard to see incoming events',
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=ruby-wizard-agent.js.map
|