use-template について
ゲームテンプレートを新しいプロジェクトにコピーするスキルです。実行可能なコードをすぐに用意して自分でカスタマイズしたい開発者向けで、ローカルの一覧からテンプレートを選び、メタデータ更新と依存関係のインストールを行います。
主な機能
- 01ローカル一覧からゲームテンプレートを選択
- 02生成ファイルを除外してテンプレートをコピー
- 03プロジェクト名とページタイトルを更新
- 04新しいディレクトリに依存関係をインストール
活用シーン
- 01テンプレートを使ったゲーム開発の開始
- 02既存コードを基にしたゲームプレイの調整
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ playableintelligence/ game-creator --skill use-templateインストール後、Claude Code で/use-templateを入力して読み込まれたか確認します。
詳細
テンプレートを使用
ギャラリーからゲームテンプレートを新しいプロジェクトにクローンします。これは高速なコピーです。AI パイプラインを使わず、数秒で動作するコードを入手できます。
動作
-
引数を解析:
<template-id> [project-name]- 引数が指定されていない場合は、
site/manifest.jsonを読み込み、エンジン/複雑さ/説明とともにすべてのテンプレートを番号付きリストで表示し、ユーザーに選択を求めます。 template-idは必須です。project-nameのデフォルト値はtemplate-idです。
- 引数が指定されていない場合は、
-
site/manifest.jsonからidでテンプレートを検索します。見つからない場合は、利用可能な ID を表示して中止します。 -
ターゲットディレクトリを決定:
- 現在の作業ディレクトリが
game-creatorリポジトリ内の場合 →examples/<project-name>/ - それ以外の場合 →
./<project-name>/ - ターゲットがすでに存在する場合は、エラーで中止します。
- 現在の作業ディレクトリが
-
テンプレートのソースディレクトリをターゲットにコピーします。ただし、以下は除外します:
node_modules/dist/output/.herenow/progress.mdtest-results/playwright-report/
-
プロジェクトのメタデータを更新:
package.json:"name"をプロジェクト名に設定しますindex.html(存在する場合):<title>をプロジェクト名の整形版に更新します
-
依存関係をインストール:ターゲットディレクトリで
npm installを実行します。 -
次の手順を表示:
Template cloned successfully! cd <project-name> npm run dev
実装
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 && !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&template=${encodeURIComponent(templateId)}&source=skill&v=1`)
.on('error', () => {});
}
使用例
/use-template flappy-bird my-game
/use-template threejs-3d-starter space-shooter
/use-template castle-siege
セキュリティに関する注意事項
- パスの検証:プロジェクト名は、パストラバーサル(
..)、パス区切り文字、特殊文字を拒否するよう検証されます。解決されたターゲットパスが、想定される親ディレクトリ内に収まっていることを確認します。 - npm install:コピーしたテンプレートの
package.jsonに記載された内容を使ってnpm installを実行します。このファイルには、テンプレートに含まれる固定バージョンの依存関係(Phaser/Three.js、Vite)のみが記載されています。任意のパッケージはインストールされません。 - テレメトリ:匿名のオプトアウト方式の使用状況テレメトリでは、テンプレート ID とイベントタイプのみが送信されます(個人を特定できる情報、パス、ユーザーデータは含まれません)。
DO_NOT_TRACK=1またはDISABLE_TELEMETRY=1の環境変数で無効にできます。 - テンプレートのソース:テンプレートはプラグイン内のローカルな
site/manifest.jsonレジストリからコピーされます。クローン時に外部のテンプレートが取得されることはありません。
/viral-game および /make-game との主な違い
/use-template は10 秒で完了するコピーです。すぐに動作・実行できるコードを入手し、手動でカスタマイズできます。/viral-game は、テキストプロンプトまたはツイート URL から、スキャフォールディング、デザイン、オーディオ追加、テスト、デプロイ、収益化を行う10 分間の AI パイプラインです。独自の方針に沿って一度に処理します。/make-game は、マイルストーン、ADR、docs/STATE.md を備え、時間をかけて進化させるプロジェクト向けの、より詳細な複数セッション型ゲーム開発ワークフローです。