use-template

Clone a game template from the gallery as a starting point. Use when the user says "use a template", "start from a template", "clone flappy-bird", "use the…

INSTALLATION
npx skills add https://github.com/opusgamelabs/game-creator --skill use-template
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

-

Copy the template source directory to the target, excluding:

  • node_modules/
  • dist/
  • output/
  • .herenow/
  • progress.md
  • test-results/
  • playwright-report/

-

Update project metadata:

  • In package.json: set "name" to the project name
  • In index.html (if exists): update <title> to a formatted version of the project name

-

Install dependencies: Run npm install in the target directory.

-

Print next steps:

Template cloned successfully!

cd <project-name>

npm run dev

Implementation

const fs = require('fs');

const path = require('path');

const { execSync } = require('child_process');

// Find game-creator root (contains site/manifest.json)

function findRoot(dir) {

  let d = dir;

  while (d !== path.dirname(d)) {

    if (fs.existsSync(path.join(d, 'gallery', 'manifest.json'))) return d;

    d = path.dirname(d);

  }

  return null;

}

const root = findRoot(process.cwd());

const manifest = JSON.parse(fs.readFileSync(path.join(root, 'gallery', 'manifest.json'), 'utf-8'));

// Parse args

const [templateId, projectName] = args; // provided by the agent

const template = manifest.find(t => t.id === templateId);

const name = projectName || templateId;

// Validate project name — reject path traversal and special characters

if (/[\/\\]|^\.\.?$|\.\./.test(name)) {

  throw new Error(`Invalid project name: "${name}". Must not contain path separators or ".." sequences.`);

}

if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)) {

  throw new Error(`Invalid project name: "${name}". Use only alphanumeric characters, hyphens, underscores, and dots.`);

}

// Determine target

const inGameCreator = process.cwd().startsWith(root);

const target = inGameCreator

  ? path.join(root, 'examples', name)

  : path.join(process.cwd(), name);

// Verify resolved path stays within expected parent directory

const expectedParent = inGameCreator ? path.join(root, 'examples') : process.cwd();

if (!path.resolve(target).startsWith(path.resolve(expectedParent))) {

  throw new Error('Target directory resolved outside expected parent. Aborting.');

}

// Copy with exclusions

const EXCLUDE = ['node_modules', 'dist', 'output', '.herenow', 'progress.md', 'test-results', 'playwright-report'];

function copyDir(src, dst) {

  fs.mkdirSync(dst, { recursive: true });

  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {

    if (EXCLUDE.includes(entry.name)) continue;

    const s = path.join(src, entry.name);

    const d = path.join(dst, entry.name);

    if (entry.isDirectory()) copyDir(s, d);

    else fs.copyFileSync(s, d);

  }

}

copyDir(path.join(root, template.source), target);

// Update package.json

const pkgPath = path.join(target, 'package.json');

if (fs.existsSync(pkgPath)) {

  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));

  pkg.name = name;

  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');

}

// Update index.html title

const indexPath = path.join(target, 'index.html');

if (fs.existsSync(indexPath)) {

  let html = fs.readFileSync(indexPath, 'utf-8');

  const prettyName = name.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());

  html = html.replace(/<title>[^<]*<\/title>/, `<title>${prettyName}</title>`);

  fs.writeFileSync(indexPath, html);

}

// Install

execSync('npm install', { cwd: target, stdio: 'inherit' });

// Optional anonymous telemetry — tracks which templates are popular

// Data sent: event type ("clone"), template ID, source ("skill"), schema version

// No PII, IP addresses, file paths, or user-identifiable data is transmitted

// Opt out: set DO_NOT_TRACK=1 or DISABLE_TELEMETRY=1 in your environment

if (!process.env.DO_NOT_TRACK &#x26;&#x26; !process.env.DISABLE_TELEMETRY) {

  const https = require('https');

  const telemetryUrl = process.env.TELEMETRY_URL || 'https://gallery-telemetry.up.railway.app';

  https.get(`${telemetryUrl}/t?event=clone&#x26;template=${encodeURIComponent(templateId)}&#x26;source=skill&#x26;v=1`)

    .on('error', () => {});

}

Example Usage

/use-template flappy-bird my-game

/use-template threejs-3d-starter space-shooter

/use-template castle-siege

Security Notes

  • Path validation: Project names are validated to reject path traversal (..), path separators, and special characters. The resolved target path is verified to stay within the expected parent directory.
  • npm install: Runs npm install from the copied template's package.json, which contains only pinned dependencies from the template (Phaser/Three.js, Vite). No arbitrary packages are installed.
  • Telemetry: Anonymous, opt-out usage telemetry sends only the template ID and event type (no PII, paths, or user data). Disable with DO_NOT_TRACK=1 or DISABLE_TELEMETRY=1 environment variables.
  • Template source: Templates are copied from the local site/manifest.json registry within the plugin — no external templates are fetched at clone time.

Key Difference from /viral-game and /make-game

/use-template is a 10-second copy. You get working, runnable code instantly and customize it manually. /viral-game is a 10-minute AI pipeline that scaffolds, designs, adds audio, tests, deploys, and monetizes from a text prompt or tweet URL — opinionated and one-shot. /make-game is the deeper, multi-session game-dev workflow with milestones, ADRs, and docs/STATE.md for projects that need to evolve over time.

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card