Skip to content

Commit 7239269

Browse files
committed
monorepo clean
1 parent edae68f commit 7239269

32 files changed

+2632
-388
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@ plugins
3434
.env
3535
.vercel
3636

37-
e2e-tests/fixtures/.tracking/*
37+
e2e-tests/fixtures/.tracking/*
38+
39+
CLAUDE.local.md

src/android/android-wizard-agent.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,6 @@ export const ANDROID_AGENT_CONFIG: FrameworkConfig<AndroidContext> = {
9696
prompts: {
9797
projectTypeDetection:
9898
'This is an Android/Kotlin project. Look for build.gradle or build.gradle.kts files, AndroidManifest.xml, and Kotlin source files (.kt) to confirm.',
99-
packageInstallation:
100-
'Add the PostHog Android SDK dependency to the app-level build.gradle(.kts) file. Use implementation("com.posthog:posthog-android:<VERSION>"). Check the existing dependency format (Groovy vs Kotlin DSL) and match it.',
10199
getAdditionalContextLines: (context) => {
102100
const lines = [
103101
`Framework docs ID: android (use posthog://docs/frameworks/android for documentation)`,

src/angular/angular-wizard-agent.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ export const ANGULAR_AGENT_CONFIG: FrameworkConfig<AngularContext> = {
6060

6161
return [
6262
`Framework docs ID: ${frameworkId} (use posthog://docs/frameworks/${frameworkId} for documentation)`,
63-
'Angular uses dependency injection for services. PostHog should be initialized as a service.',
64-
'For standalone components, ensure PostHog is properly provided in the application config.',
6563
];
6664
},
6765
},

src/django/django-wizard-agent.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Integration } from '../lib/constants';
77
import fg from 'fast-glob';
88
import * as fs from 'node:fs';
99
import * as path from 'node:path';
10+
import { PYTHON_DETECTION_IGNORES } from '../lib/glob-patterns';
1011
import {
1112
getDjangoVersion,
1213
getDjangoProjectType,
@@ -48,7 +49,7 @@ export const DJANGO_AGENT_CONFIG: FrameworkConfig<DjangoContext> = {
4849

4950
const managePyMatches = await fg('**/manage.py', {
5051
cwd: installDir,
51-
ignore: ['**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**'],
52+
ignore: PYTHON_DETECTION_IGNORES,
5253
});
5354

5455
if (managePyMatches.length > 0) {
@@ -77,7 +78,7 @@ export const DJANGO_AGENT_CONFIG: FrameworkConfig<DjangoContext> = {
7778
['**/requirements*.txt', '**/pyproject.toml', '**/setup.py'],
7879
{
7980
cwd: installDir,
80-
ignore: ['**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**'],
81+
ignore: PYTHON_DETECTION_IGNORES,
8182
},
8283
);
8384

src/fastapi/fastapi-wizard-agent.ts

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@ import type { WizardOptions } from '../utils/types';
33
import type { FrameworkConfig } from '../lib/framework-config';
44
import { PYTHON_PACKAGE_INSTALLATION } from '../lib/framework-config';
55
import { detectPythonPackageManagers } from '../lib/package-manager-detection';
6-
import { enableDebugLogs } from '../utils/debug';
7-
import { runAgentWizard } from '../lib/agent-runner';
86
import { Integration } from '../lib/constants';
9-
import clack from '../utils/clack';
10-
import chalk from 'chalk';
11-
import * as semver from 'semver';
127
import {
138
getFastAPIVersion,
149
getFastAPIProjectType,
@@ -20,12 +15,14 @@ import {
2015
import fg from 'fast-glob';
2116
import * as fs from 'node:fs';
2217
import * as path from 'node:path';
18+
import {
19+
PYTHON_DETECTION_IGNORES,
20+
PYTHON_SOURCE_IGNORES,
21+
} from '../lib/glob-patterns';
2322

2423
/**
2524
* FastAPI framework configuration for the universal agent runner
2625
*/
27-
const MINIMUM_FASTAPI_VERSION = '0.100.0';
28-
2926
export const FASTAPI_AGENT_CONFIG: FrameworkConfig = {
3027
metadata: {
3128
name: 'FastAPI',
@@ -49,6 +46,7 @@ export const FASTAPI_AGENT_CONFIG: FrameworkConfig = {
4946
return undefined;
5047
},
5148
getVersionBucket: getFastAPIVersionBucket,
49+
minimumVersion: '0.100.0',
5250
getInstalledVersion: getFastAPIVersion,
5351
detect: async (options) => {
5452
const { installDir } = options;
@@ -66,7 +64,7 @@ export const FASTAPI_AGENT_CONFIG: FrameworkConfig = {
6664
],
6765
{
6866
cwd: installDir,
69-
ignore: ['**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**'],
67+
ignore: PYTHON_DETECTION_IGNORES,
7068
},
7169
);
7270

@@ -94,13 +92,7 @@ export const FASTAPI_AGENT_CONFIG: FrameworkConfig = {
9492
['**/main.py', '**/app.py', '**/application.py', '**/__init__.py'],
9593
{
9694
cwd: installDir,
97-
ignore: [
98-
'**/venv/**',
99-
'**/.venv/**',
100-
'**/env/**',
101-
'**/.env/**',
102-
'**/__pycache__/**',
103-
],
95+
ignore: PYTHON_SOURCE_IGNORES,
10496
},
10597
);
10698

@@ -198,35 +190,3 @@ export const FASTAPI_AGENT_CONFIG: FrameworkConfig = {
198190
],
199191
},
200192
};
201-
202-
/**
203-
* FastAPI wizard powered by the universal agent runner.
204-
*/
205-
export async function runFastAPIWizardAgent(
206-
options: WizardOptions,
207-
): Promise<void> {
208-
if (options.debug) {
209-
enableDebugLogs();
210-
}
211-
212-
// Check FastAPI version - agent wizard requires >= 0.100.0
213-
const fastapiVersion = await getFastAPIVersion(options);
214-
215-
if (fastapiVersion) {
216-
const coercedVersion = semver.coerce(fastapiVersion);
217-
if (coercedVersion && semver.lt(coercedVersion, MINIMUM_FASTAPI_VERSION)) {
218-
const docsUrl =
219-
FASTAPI_AGENT_CONFIG.metadata.unsupportedVersionDocsUrl ??
220-
FASTAPI_AGENT_CONFIG.metadata.docsUrl;
221-
222-
clack.log.warn(
223-
`Sorry: the wizard can't help you with FastAPI ${fastapiVersion}. Upgrade to FastAPI ${MINIMUM_FASTAPI_VERSION} or later, or check out the manual setup guide.`,
224-
);
225-
clack.log.info(`Setup FastAPI manually: ${chalk.cyan(docsUrl)}`);
226-
clack.outro('PostHog wizard will see you next time!');
227-
return;
228-
}
229-
}
230-
231-
await runAgentWizard(FASTAPI_AGENT_CONFIG, options);
232-
}

src/flask/flask-wizard-agent.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { Integration } from '../lib/constants';
77
import fg from 'fast-glob';
88
import * as fs from 'node:fs';
99
import * as path from 'node:path';
10+
import {
11+
PYTHON_DETECTION_IGNORES,
12+
PYTHON_SOURCE_IGNORES,
13+
} from '../lib/glob-patterns';
1014
import {
1115
getFlaskVersion,
1216
getFlaskProjectType,
@@ -55,7 +59,7 @@ export const FLASK_AGENT_CONFIG: FrameworkConfig<FlaskContext> = {
5559
],
5660
{
5761
cwd: installDir,
58-
ignore: ['**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**'],
62+
ignore: PYTHON_DETECTION_IGNORES,
5963
},
6064
);
6165

@@ -80,13 +84,7 @@ export const FLASK_AGENT_CONFIG: FrameworkConfig<FlaskContext> = {
8084
['**/app.py', '**/wsgi.py', '**/application.py', '**/__init__.py'],
8185
{
8286
cwd: installDir,
83-
ignore: [
84-
'**/venv/**',
85-
'**/.venv/**',
86-
'**/env/**',
87-
'**/.env/**',
88-
'**/__pycache__/**',
89-
],
87+
ignore: PYTHON_SOURCE_IGNORES,
9088
},
9189
);
9290

src/javascript-node/javascript-node-wizard-agent.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import type { FrameworkConfig } from '../lib/framework-config';
33
import { Integration } from '../lib/constants';
44
import { tryGetPackageJson } from '../utils/clack-utils';
55
import { detectNodePackageManagers } from '../lib/package-manager-detection';
6+
import { hasPackageInstalled } from '../utils/package-json';
7+
import { FRAMEWORK_PACKAGES } from '../javascript-web/utils';
8+
import { hasLockfileOrDeps } from '../utils/js-detection';
69

710
type JavaScriptNodeContext = Record<string, unknown>;
811

@@ -23,7 +26,22 @@ export const JAVASCRIPT_NODE_AGENT_CONFIG: FrameworkConfig<JavaScriptNodeContext
2326
detectPackageManager: detectNodePackageManagers,
2427
detect: async (options) => {
2528
const packageJson = await tryGetPackageJson(options);
26-
return !!packageJson;
29+
if (!packageJson) {
30+
return false;
31+
}
32+
33+
// Exclude projects with known framework packages (handled by
34+
// their dedicated detectors earlier in the enum)
35+
for (const frameworkPkg of FRAMEWORK_PACKAGES) {
36+
if (hasPackageInstalled(frameworkPkg, packageJson)) {
37+
return false;
38+
}
39+
}
40+
41+
// Catch-all for JS projects without browser signals (those
42+
// matched javascript_web already). Require a lockfile or real
43+
// dependencies so we don't match bare tooling package.json files.
44+
return hasLockfileOrDeps(options.installDir, packageJson);
2745
},
2846
},
2947

@@ -42,8 +60,6 @@ export const JAVASCRIPT_NODE_AGENT_CONFIG: FrameworkConfig<JavaScriptNodeContext
4260
prompts: {
4361
projectTypeDetection:
4462
'This is a server-side Node.js project. Look for package.json and lockfiles to confirm.',
45-
packageInstallation:
46-
'Use npm, yarn, pnpm, or bun based on the existing lockfile (package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb). Install posthog-node as a regular dependency.',
4763
getAdditionalContextLines: () => [
4864
`Framework docs ID: javascript_node (use posthog://docs/frameworks/javascript_node for documentation)`,
4965
],

src/javascript-web/javascript-web-wizard-agent.ts

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,30 +49,25 @@ export const JAVASCRIPT_WEB_AGENT_CONFIG: FrameworkConfig<JavaScriptContext> = {
4949
}
5050
}
5151

52-
// Ensure this is actually a JS project, not just a package.json for tooling
53-
const { installDir } = options;
52+
// Require a positive browser signal — without one, the project is
53+
// more likely a Node.js server/CLI/worker and should fall through
54+
// to the javascript_node catch-all (posthog-node is the safer
55+
// default since posthog-js crashes without window/document).
56+
//
57+
// Bundlers alone are NOT a reliable browser signal — Vite/esbuild
58+
// are commonly used for server-side builds (Cloudflare Workers, SSR,
59+
// Vitest, etc.). Instead we check for:
60+
// 1. An HTML entry point (fundamental to browser apps)
61+
// 2. A "browser" field in package.json (standard npm browser flag)
62+
const hasHtmlEntry = [
63+
'index.html',
64+
'public/index.html',
65+
'src/index.html',
66+
].some((f) => fs.existsSync(path.join(options.installDir, f)));
5467

55-
// Check for a lockfile
56-
const hasLockfile = [
57-
'package-lock.json',
58-
'yarn.lock',
59-
'pnpm-lock.yaml',
60-
'bun.lockb',
61-
'bun.lock',
62-
].some((lockfile) => fs.existsSync(path.join(installDir, lockfile)));
68+
const hasBrowserField = 'browser' in packageJson;
6369

64-
if (hasLockfile) {
65-
return true;
66-
}
67-
68-
// Fallback: check if package.json has actual dependencies
69-
const hasDeps =
70-
(packageJson.dependencies &&
71-
Object.keys(packageJson.dependencies).length > 0) ||
72-
(packageJson.devDependencies &&
73-
Object.keys(packageJson.devDependencies).length > 0);
74-
75-
return !!hasDeps;
70+
return hasHtmlEntry || hasBrowserField;
7671
},
7772
},
7873

src/laravel/laravel-wizard-agent.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,6 @@ export const LARAVEL_AGENT_CONFIG: FrameworkConfig<LaravelContext> = {
114114
prompts: {
115115
projectTypeDetection:
116116
'This is a PHP/Laravel project. Look for composer.json, artisan CLI, and app/ directory structure to confirm. Check for Laravel-specific packages like laravel/framework.',
117-
packageInstallation:
118-
'Use Composer to install packages. Run `composer require posthog/posthog-php` without pinning a specific version.',
119117
getAdditionalContextLines: (context) => {
120118
const projectTypeName = context.projectType
121119
? getLaravelProjectTypeName(context.projectType)
@@ -135,17 +133,6 @@ export const LARAVEL_AGENT_CONFIG: FrameworkConfig<LaravelContext> = {
135133
lines.push(`Bootstrap file: ${context.bootstrapFile}`);
136134
}
137135

138-
// Add Laravel-specific guidance based on version structure
139-
if (context.laravelStructure === 'latest') {
140-
lines.push(
141-
'Note: Laravel 11+ uses simplified bootstrap/app.php for middleware and providers',
142-
);
143-
} else {
144-
lines.push(
145-
'Note: Use app/Http/Kernel.php for middleware, app/Providers for service providers',
146-
);
147-
}
148-
149136
return lines;
150137
},
151138
},

src/lib/__tests__/wizard-tools.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,27 @@ describe('resolveEnvPath', () => {
4747
// edge case: filePath resolves to exactly workingDirectory
4848
expect(() => resolveEnvPath('/project', '.')).not.toThrow();
4949
});
50+
51+
it('strips redundant subdirectory prefix from relative path', () => {
52+
// Agent passes "services/mcp/.env" when workingDir is already "/ws/services/mcp"
53+
const result = resolveEnvPath('/ws/services/mcp', 'services/mcp/.env');
54+
expect(result).toBe(path.resolve('/ws/services/mcp', '.env'));
55+
});
56+
57+
it('strips single-level redundant prefix', () => {
58+
const result = resolveEnvPath('/ws/frontend', 'frontend/.env.local');
59+
expect(result).toBe(path.resolve('/ws/frontend', '.env.local'));
60+
});
61+
62+
it('does not strip when there is no redundant prefix', () => {
63+
const result = resolveEnvPath('/ws/services/mcp', '.env');
64+
expect(result).toBe(path.resolve('/ws/services/mcp', '.env'));
65+
});
66+
67+
it('does not strip legitimate nested paths', () => {
68+
const result = resolveEnvPath('/ws/services/mcp', 'config/.env');
69+
expect(result).toBe(path.resolve('/ws/services/mcp', 'config/.env'));
70+
});
5071
});
5172

5273
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)