Skip to content

Commit

Permalink
Add LLM instructions file suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
madmath committed Dec 11, 2024
1 parent 026da63 commit 1979137
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 1 deletion.
31 changes: 31 additions & 0 deletions packages/vscode-extension/resources/llm-instructions.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
You are a very experienced Shopify theme developer. You are tasked with writing high-quality Liquid code and JSON files.

Remember the following important mindset when providing code, in the following order:
- Adherance to conventions and patterns in the rest of the codebase
- Simplicity
- Readability

The theme folder structure is as follows:
/assets
/config
/layout
/locales
/sections
/snippets
/templates
/templates/customers
/templates/metaobject
Files can also be placed in the root directory. Subdirectories, other than the ones listed, aren't supported.

Liquid filters are used to modify Liquid output and are documented at https://shopify.dev/docs/api/liquid/filters.txt.

Liquid tags are used to define logic that tells templates what to do and are documented at https://shopify.dev/api/liquid/tags.txt.
Liquid objects represent variables that you can use to build your theme and are documented at https://shopify.dev/api/liquid/objects.txt.

Some best practices from Shopify on theme development (more available at https://shopify.dev/docs/themes/best-practices.txt):
* With the large majority of online store traffic happening on mobile, designing for mobile devices must be at the forefront throughout the theme build process.
* To provide the best experience to a wide range of merchants and customers, themes must be built from the ground up with accessibility best practices in mind.
* Themes should minimize the use of JavaScript and rely on modern and native web browser features for most functionality.
* Use responsive images by using the `image_tag` filter. This filter returns a `srcset` for the image using a smart default set of widths. An example is `{{ product.featured_image | image_url: width: 2000 | image_tag }}`.


90 changes: 89 additions & 1 deletion packages/vscode-extension/src/node/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FileStat, FileTuple, path as pathUtils } from '@shopify/theme-check-common';
import * as path from 'node:path';
import { commands, ExtensionContext, languages, Uri, workspace } from 'vscode';
import { commands, ExtensionContext, languages, Uri, workspace, window } from 'vscode';
import {
DocumentSelector,
LanguageClient,
Expand All @@ -16,9 +16,97 @@ const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms));

let client: LanguageClient | undefined;

async function isShopifyTheme(workspaceRoot: string): Promise<boolean> {
try {
// Check for typical Shopify theme folders
const requiredFolders = ['sections', 'templates', 'assets', 'config'];
for (const folder of requiredFolders) {
const folderUri = Uri.file(path.join(workspaceRoot, folder));
try {
await workspace.fs.stat(folderUri);
} catch {
return false;
}
}
return true;
} catch {
return false;
}
}

function isCursor(): boolean {
// Check if we're running in Cursor's electron process
const processTitle = process.title.toLowerCase();
const isElectronCursor = processTitle.includes('cursor') && process.versions.electron !== undefined;

// Check for Cursor-specific environment variables that are set by Cursor itself
const hasCursorEnv = process.env.CURSOR_CHANNEL !== undefined || process.env.CURSOR_VERSION !== undefined;

return isElectronCursor || hasCursorEnv;
}

interface ConfigFile {
path: string;
templateName: string;
prompt: string;
}

async function getConfigFileDetails(workspaceRoot: string): Promise<ConfigFile> {
if (isCursor()) {
return {
path: path.join(workspaceRoot, '.cursorrules'),
templateName: 'llm_instructions.template',
prompt: 'Detected Shopify theme project in Cursor. Do you want a .cursorrules file to be created?'
};
}
return {
path: path.join(workspaceRoot, '.github', 'copilot-instructions.md'),
templateName: 'llm_instructions.template',
prompt: 'Detected Shopify theme project in VSCode. Do you want a Copilot instructions file to be created?'
};
}

export async function activate(context: ExtensionContext) {
const runChecksCommand = 'themeCheck/runChecks';

if (workspace.workspaceFolders?.length) {
const workspaceRoot = workspace.workspaceFolders[0].uri.fsPath;
const instructionsConfig = await getConfigFileDetails(workspaceRoot);

// Don't do anything if the file already exists
try {
await workspace.fs.stat(Uri.file(instructionsConfig.path));
return;
} catch {
// File doesn't exist, continue
}

if (await isShopifyTheme(workspaceRoot)) {
const response = await window.showInformationMessage(
instructionsConfig.prompt,
'Yes',
'No'
);

if (response === 'Yes') {
// Create directory if it doesn't exist (needed for .github case)
const dir = path.dirname(instructionsConfig.path);
try {
await workspace.fs.createDirectory(Uri.file(dir));
} catch {
// Directory might already exist, continue
}

// Read the template file from the extension's resources
const templateContent = await workspace.fs.readFile(
Uri.file(context.asAbsolutePath(`resources/${instructionsConfig.templateName}`))
);
await workspace.fs.writeFile(Uri.file(instructionsConfig.path), templateContent);
console.log(`Wrote instructions file to ${instructionsConfig.path}`);
}
}
}

context.subscriptions.push(
commands.registerCommand('shopifyLiquid.restart', () => restartServer(context)),
);
Expand Down

0 comments on commit 1979137

Please sign in to comment.