Skip to content

Commit 5b6a098

Browse files
danilocjoshsny
andauthored
Integrating Cursor rules (#8)
* feat(wip): react setup * fix: add back some changes from merge * feat(wip): pulling out nextjs * feat(wip): pull out nextjs internals * chore: remove posthog-js / posthog-node from package.json * feat(wip): react support * chore: ditch semver on react-version * chore: lint * chore: specify to use '.js' for next config * Naive addition of cursor rules * Only install cursor rules if we're running in a cursor session * chore: fix analytics, add back empty next config in example app * 0.2.11 * fix: add react option when not detected * 0.3.0 * chore: initial action for publishing on a new version (#10) * chore: basic publishing workflow * chore: add husky * fix: formatting on docs * fix: skip install test for now * fix: linter issues * Update readme for react (#11) * chore: basic publishing workflow * chore: add husky * fix: formatting on docs * fix: skip install test for now * fix: linter issues * chore: update readme * chore: add tracking for current integration (#12) * feat: detect env var prefix + imports in react (#13) * feat(wip): detect env var prefix * feat: env var detection * chore: formatting * chore: update vite imports, move cra file * fix: formatting * chore: add a tanstack test app * fix: formatting * 0.4.0 * 0.4.1 * Rework the rules structure * Adopt new rules structure * Naive addition of cursor rules * Rework the rules structure * Extract rules function for any integration to call * Finish dealing with merge * Store source rules as .md so Cursor doesn’t mangle their frontmatter * Lead with the more correct TS approach then degrade to JS, otherwise the robot will ignore * chore: run prettier, add integration tracking, trace step * chore: formatting * chore: add some tests * chore: formatting * chore: rename to editor rules for future editor support * chore: ask user if they want rules, default to true * 0.4.2 * 0.5.0 * fix: update postbuild * fix: clack select in tests --------- Co-authored-by: Joshua Snyder <joshua@posthog.com>
1 parent 2d5b733 commit 5b6a098

File tree

11 files changed

+441
-6
lines changed

11 files changed

+441
-6
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@posthog/wizard",
3-
"version": "0.4.1",
3+
"version": "0.5.0",
44
"homepage": "https://github.com/posthog/wizard",
55
"repository": "https://github.com/posthog/wizard",
66
"description": "The PostHog wizard helps you to configure your project",
@@ -90,7 +90,7 @@
9090
"prebuild": "pnpm clean",
9191
"build:watch": "pnpm tsc -w",
9292
"build": "pnpm tsc",
93-
"postbuild": "chmod +x ./dist/bin.js && cp -r scripts/** dist",
93+
"postbuild": "chmod +x ./dist/bin.js && cp -r scripts/** dist && cp -r src/utils/rules dist/src/utils",
9494
"lint": "pnpm lint:prettier && pnpm lint:eslint",
9595
"lint:prettier": "prettier --check \"{lib,src,test}/**/*.ts\"",
9696
"lint:eslint": "eslint . --cache --format stylish",

src/nextjs/docs.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
3333
ui_host: "${getUiHostFromHost(host)}",
3434
capture_pageview: false, // We capture pageviews manually
3535
capture_pageleave: true, // Enable pageleave capture
36+
debug: process.env.NODE_ENV === "development",
3637
})
3738
}, [])
3839
@@ -191,6 +192,7 @@ export default function App({ Component, pageProps }) {
191192
loaded: (posthog) => {
192193
if (process.env.NODE_ENV === "development") posthog.debug()
193194
},
195+
debug: process.env.NODE_ENV === "development",
194196
})
195197
196198
const handleRouteChange = () => posthog?.capture("$pageview")

src/nextjs/nextjs-wizard.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
} from '../utils/file-utils';
3434
import type { WizardOptions } from '../utils/types';
3535
import { askForCloudRegion } from '../utils/clack-utils';
36+
import { addEditorRules } from '../utils/rules/add-editor-rules';
3637

3738
export async function runNextjsWizard(options: WizardOptions): Promise<void> {
3839
printWelcome({
@@ -143,8 +144,14 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
143144
integration: Integration.nextjs,
144145
});
145146

146-
clack.outro(`
147-
${chalk.green('Successfully installed PostHog!')} ${`\n\n${
147+
await addEditorRules({
148+
rulesName: 'next-rules.md',
149+
installDir: options.installDir,
150+
integration: Integration.nextjs,
151+
default: options.default,
152+
});
153+
154+
clack.outro(`${chalk.green('Successfully installed PostHog!')} ${`\n\n${
148155
aiConsent
149156
? `Note: This uses experimental AI to setup your project. It might have got it wrong, please check!\n`
150157
: ``

src/react/docs.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ root.render(
3636
<PostHogProvider
3737
apiKey={${apiKeyText}}
3838
options={{
39-
api_host: ${host},
40-
}}
39+
api_host: ${host},
40+
debug: ${
41+
envVarPrefix === 'VITE_PUBLIC_'
42+
? 'import.meta.env.MODE === "development"'
43+
: 'process.env.NODE_ENV === "development"'
44+
},
45+
}}
4146
>
4247
<App />
4348
</PostHogProvider>

src/react/react-wizard.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '../utils/file-utils';
3131
import type { WizardOptions } from '../utils/types';
3232
import { askForCloudRegion } from '../utils/clack-utils';
33+
import { addEditorRules } from '../utils/rules/add-editor-rules';
3334

3435
export async function runReactWizard(options: WizardOptions): Promise<void> {
3536
printWelcome({
@@ -129,6 +130,13 @@ export async function runReactWizard(options: WizardOptions): Promise<void> {
129130
integration: Integration.react,
130131
});
131132

133+
await addEditorRules({
134+
installDir: options.installDir,
135+
rulesName: 'react-rules.md',
136+
integration: Integration.react,
137+
default: options.default,
138+
});
139+
132140
clack.outro(`
133141
${chalk.green('Successfully installed PostHog!')} ${`\n\n${
134142
aiConsent

src/rules-stubs/universal.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Never hallucinate an API key. Instead, always use the API key populated in the .env file.
2+
3+
If an installation already exists, do not modify its code in any way.
4+
5+
# Feature flags
6+
7+
A given feature flag should be used in as few places as possible. Do not increase the risk of undefined behavior by scattering the same feature flag across multiple areas of code. If the same feature flag needs to be introduced at multiple callsites, flag this for the developer to inspect carefully.
8+
9+
If a job requires creating new feature flag names, make them as clear and descriptive as possible.
10+
11+
If using JavaScript, store flag names as strings to an object declared as a constant, to simulate an enum. If using TypeScript, use an enum. Use a consistent naming convention for this storage. enum members should be written UPPERCASE_WITH_UNDERSCORE.
12+
13+
Gate flag-dependent code on a check that verifies the flag's values are valid and expected.
14+
15+
# Identification
16+
17+
How PostHog identifies users and whether events are identified have significant billing consequences for an integration. Consult with the developer before writing any code to implement or alter the approach to this task.
18+
19+
# Custom properties
20+
21+
If a custom property is at any point referenced in two or more files or two or more callsites in the same file, use an enum or const object, as above in feature flags.
22+
23+
# Naming
24+
25+
Before creating any new event or property names, consult with the developer for any existing naming convention. Consistency in naming is essential. Similarly, be careful about any changes to existing naming as this may break reporting and distort data for the project.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as fs from 'fs';
2+
import chalk from 'chalk';
3+
import path from 'path';
4+
import { Integration } from '../../lib/constants';
5+
import { analytics } from '../analytics';
6+
import clack from '../clack';
7+
import { traceStep } from '../../telemetry';
8+
import { abortIfCancelled } from '../clack-utils';
9+
10+
type AddEditorRulesOptions = {
11+
installDir: string;
12+
rulesName: string;
13+
integration: Integration;
14+
default?: boolean;
15+
};
16+
17+
export const addEditorRules = async ({
18+
installDir,
19+
rulesName,
20+
integration,
21+
default: defaultAddEditorRules,
22+
}: AddEditorRulesOptions) => {
23+
// Add rules file if in Cursor environment
24+
if (process.env.CURSOR_TRACE_ID) {
25+
const addEditorRules: boolean = defaultAddEditorRules
26+
? true
27+
: await abortIfCancelled(
28+
clack.select({
29+
message:
30+
'Would you like to have PostHog added to your Cursor rules?',
31+
options: [
32+
{
33+
label: 'Yes, please!',
34+
value: true,
35+
},
36+
{
37+
label: 'No, thanks',
38+
value: false,
39+
},
40+
],
41+
}),
42+
);
43+
44+
if (!addEditorRules) {
45+
return;
46+
}
47+
48+
return traceStep('add-editor-rules', async () => {
49+
const docsDir = path.join(installDir, '.cursor', 'rules');
50+
51+
await fs.promises.mkdir(docsDir, { recursive: true });
52+
53+
const frameworkRules = await fs.promises.readFile(
54+
path.join(__dirname, rulesName),
55+
'utf8',
56+
);
57+
const universalRulesPath = path.join(__dirname, 'universal.md');
58+
59+
const universalRules = await fs.promises.readFile(
60+
universalRulesPath,
61+
'utf8',
62+
);
63+
64+
// Replace {universal} placeholder with universal rules content
65+
const combinedRules = frameworkRules.replace(
66+
'{universal}',
67+
universalRules,
68+
);
69+
const targetPath = path.join(docsDir, 'posthog-integration.mdc');
70+
71+
// Write the combined rules
72+
await fs.promises.writeFile(targetPath, combinedRules, 'utf8');
73+
74+
analytics.capture('wizard interaction', {
75+
action: 'added editor rules',
76+
integration,
77+
});
78+
79+
clack.log.info(`Added Cursor rules to ${chalk.bold.cyan(docsDir)}`);
80+
});
81+
}
82+
};

src/utils/rules/next-rules.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
description: apply when interacting with PostHog/analytics tasks
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
{universal}
8+
9+

src/utils/rules/react-rules.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
description: apply when interacting with PostHog/analytics tasks
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
{universal}

src/utils/rules/universal.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Never hallucinate an API key. Instead, always use the API key populated in the .env file.
2+
3+
# Feature flags
4+
5+
A given feature flag should be used in as few places as possible. Do not increase the risk of undefined behavior by scattering the same feature flag across multiple areas of code. If the same feature flag needs to be introduced at multiple callsites, flag this for the developer to inspect carefully.
6+
7+
If a job requires creating new feature flag names, make them as clear and descriptive as possible.
8+
9+
If using TypeScript, use an enum to store flag names. If using JavaScript, store flag names as strings to an object declared as a constant, to simulate an enum. Use a consistent naming convention for this storage. enum/const object members should be written UPPERCASE_WITH_UNDERSCORE.
10+
11+
Gate flag-dependent code on a check that verifies the flag's values are valid and expected.
12+
13+
# Custom properties
14+
15+
If a custom property for a person or event is at any point referenced in two or more files or two or more callsites in the same file, use an enum or const object, as above in feature flags.
16+
17+
# Naming
18+
19+
Before creating any new event or property names, consult with the developer for any existing naming convention. Consistency in naming is essential, and additional context may exist outside this project. Similarly, be careful about any changes to existing event and property names, as this may break reporting and distort data for the project.
20+

0 commit comments

Comments
 (0)