netforge 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 NetNet Capital Management
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,56 @@
1
- # Temporary Holding Version
1
+ # netforge
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The Net Forge command line for coding agents: one command makes a project
4
+ ready for Claude Code, Cursor, VS Code, Codex and Gemini CLI, and the
5
+ package ships Agent Skills for building Net Forge games.
6
+
7
+ ## Set up a project for your agent
8
+
9
+ ```sh
10
+ npx netforge setup agent
11
+ ```
12
+
13
+ It writes, without replacing anything you already have:
14
+
15
+ | File | For |
16
+ |---|---|
17
+ | `AGENTS.md` | Every agent: where the game lives, the rules, how to validate and playtest. Added as a section when the file exists. |
18
+ | `CLAUDE.md` | Claude Code: imports `AGENTS.md`. |
19
+ | `.cursor/rules/netforge.mdc` | Cursor rules. |
20
+ | `.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json` | The `@netforge/mcp` server, merged next to your other servers. |
21
+
22
+ Then it prints the install lines for agents that keep MCP config outside
23
+ the project, such as `claude mcp add netforge -- npx -y @netforge/mcp`.
24
+
25
+ ## Agent Skills
26
+
27
+ Four skills, each a folder with a `SKILL.md`:
28
+
29
+ | Skill | When the agent uses it |
30
+ |---|---|
31
+ | `netforge-create-game` | Start a game from one of ten genre templates. |
32
+ | `netforge-add-content` | Add an enemy, weapon, upgrade, earn rule or store item. |
33
+ | `netforge-playtest` | Validate and run bot playtests after every change. |
34
+ | `netforge-play-locally` | Play the game in the browser with play money. |
35
+
36
+ Install them in one line with `npx skills add netnetdev/net-forge`, or copy
37
+ them yourself:
38
+
39
+ ```sh
40
+ npx netforge skills list
41
+ npx netforge skills copy <your agent's skills folder>
42
+ ```
43
+
44
+ ## Start a new game
45
+
46
+ ```sh
47
+ npm create netforge@latest
48
+ ```
49
+
50
+ ## Docs
51
+
52
+ https://forge.netnet.capital/docs/agents
53
+
54
+ ## License
55
+
56
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ export declare const HELP = "netforge: set up a project for coding agents that build Net Forge games.\n\nUsage:\n netforge setup agent [dir] Write AGENTS.md, CLAUDE.md, .cursor/rules/netforge.mdc,\n .mcp.json, .cursor/mcp.json and .vscode/mcp.json (merged,\n never replacing other servers) and print the install lines.\n netforge skills list List the bundled Agent Skills.\n netforge skills path Print the folder that holds them.\n netforge skills copy <dir> Copy every skill folder into <dir> (your agent's skills folder).\n\nStart a new game: npm create netforge@latest\nDocs: https://forge.netnet.capital/docs";
3
+ export declare function run(argv: readonly string[], io: {
4
+ out(s: string): void;
5
+ err(s: string): void;
6
+ cwd: string;
7
+ }): number;
package/dist/cli.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ // netforge: the Net Forge command line for coding agents.
3
+ //
4
+ // netforge setup agent [dir] write AGENTS.md, CLAUDE.md, Cursor rules and MCP configs
5
+ // netforge skills path print where the bundled Agent Skills are
6
+ // netforge skills list list the bundled skills
7
+ // netforge skills copy <dir> copy the skills into a skills folder
8
+ // netforge help
9
+ import { realpathSync } from "node:fs";
10
+ import { resolve } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { INSTALL_LINES, setupAgent } from "./setup.js";
13
+ import { copySkills, listSkills, skillsDir } from "./skills.js";
14
+ export const HELP = `netforge: set up a project for coding agents that build Net Forge games.
15
+
16
+ Usage:
17
+ netforge setup agent [dir] Write AGENTS.md, CLAUDE.md, .cursor/rules/netforge.mdc,
18
+ .mcp.json, .cursor/mcp.json and .vscode/mcp.json (merged,
19
+ never replacing other servers) and print the install lines.
20
+ netforge skills list List the bundled Agent Skills.
21
+ netforge skills path Print the folder that holds them.
22
+ netforge skills copy <dir> Copy every skill folder into <dir> (your agent's skills folder).
23
+
24
+ Start a new game: npm create netforge@latest
25
+ Docs: https://forge.netnet.capital/docs`;
26
+ export function run(argv, io) {
27
+ const [cmd, sub, arg] = argv;
28
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
29
+ io.out(HELP);
30
+ return 0;
31
+ }
32
+ if (cmd === "setup" && (sub === "agent" || sub === undefined)) {
33
+ const dir = resolve(io.cwd, arg ?? ".");
34
+ const r = setupAgent(dir);
35
+ for (const f of r.written)
36
+ io.out(`wrote ${f}`);
37
+ for (const f of r.skipped)
38
+ io.out(`skipped ${f}`);
39
+ io.out("");
40
+ io.out(INSTALL_LINES);
41
+ return 0;
42
+ }
43
+ if (cmd === "skills") {
44
+ if (sub === "path") {
45
+ io.out(skillsDir());
46
+ return 0;
47
+ }
48
+ if (sub === "list" || sub === undefined) {
49
+ for (const s of listSkills())
50
+ io.out(`${s.name.padEnd(26)} ${s.description}`);
51
+ return 0;
52
+ }
53
+ if (sub === "copy" && arg) {
54
+ for (const f of copySkills(resolve(io.cwd, arg)))
55
+ io.out(`copied ${f}`);
56
+ return 0;
57
+ }
58
+ }
59
+ io.err(`Unknown command: ${argv.join(" ")}\n\n${HELP}`);
60
+ return 2;
61
+ }
62
+ const isMain = (() => {
63
+ try {
64
+ return process.argv[1] !== undefined && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ })();
70
+ if (isMain)
71
+ process.exitCode = run(process.argv.slice(2), { out: (s) => console.log(s), err: (s) => console.error(s), cwd: process.cwd() });
@@ -0,0 +1,4 @@
1
+ /** netforge: project setup and Agent Skills for coding agents that build Net Forge games. */
2
+ export { setupAgent, AGENTS_SECTION, CLAUDE_MD, CURSOR_RULE, INSTALL_LINES, MCP_PACKAGE, type SetupResult } from "./setup.js";
3
+ export { copySkills, listSkills, skillsDir, type SkillInfo } from "./skills.js";
4
+ export { run, HELP } from "./cli.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ /** netforge: project setup and Agent Skills for coding agents that build Net Forge games. */
2
+ export { setupAgent, AGENTS_SECTION, CLAUDE_MD, CURSOR_RULE, INSTALL_LINES, MCP_PACKAGE } from "./setup.js";
3
+ export { copySkills, listSkills, skillsDir } from "./skills.js";
4
+ export { run, HELP } from "./cli.js";
@@ -0,0 +1,12 @@
1
+ export declare const MCP_PACKAGE = "@netforge/mcp";
2
+ export declare const SKILLS_SOURCE = "netnetdev/net-forge";
3
+ export declare const DOCS_URL = "https://forge.netnet.capital/docs";
4
+ export declare const AGENTS_SECTION = "<!-- netforge:agents -->\n## Net Forge\n\nThis project builds a game on Net Forge, an MIT TypeScript engine for\nmultiplayer browser games. One simulation (ECS, Rapier physics, fixed tick,\nsnapshots) runs in the browser and on the room server, and a template draws,\nnetworks and simulates the game.\n\n- The whole game is `src/game.ts`: declarations (game, prefab, weapon,\n upgrade, director, system, earn.rule, earn.stake, store.sku and the genre's\n own). It may import only `@netforge/engine`.\n- Ids are lower_snake_case. Every game declares at least one `earn.rule`\n and one `earn.stake`.\n- Never write renderers, netcode or physics. Models are asset library ids\n such as `\"style.lowpoly.ghost\"`, never URLs or paths.\n- After each change: `netforge_validate_game`, then `netforge_run_playtest`\n (MCP server `@netforge/mcp`). To try it in a browser:\n `netforge_play_locally` or `npx kit-play`.\n- Look things up with `netforge_search_docs` and `netforge_explain_api`\n before guessing. Docs: https://forge.netnet.capital/docs\n";
5
+ export declare const CLAUDE_MD = "@AGENTS.md\n";
6
+ export declare const CURSOR_RULE = "---\ndescription: Net Forge game rules (src/game.ts, validation, playtests)\nalwaysApply: true\n---\n\nRead AGENTS.md. The game is src/game.ts and it may import only @netforge/engine.\nValidate every change with netforge_validate_game and playtest with\nnetforge_run_playtest before calling it done.\n";
7
+ export interface SetupResult {
8
+ readonly written: string[];
9
+ readonly skipped: string[];
10
+ }
11
+ export declare function setupAgent(dir: string): SetupResult;
12
+ export declare const INSTALL_LINES = "MCP server (@netforge/mcp, runs locally, no keys):\n Claude Code claude mcp add netforge -- npx -y @netforge/mcp\n Cursor written to .cursor/mcp.json (project); or add the same entry to ~/.cursor/mcp.json\n VS Code code --add-mcp '{\"name\":\"netforge\",\"command\":\"npx\",\"args\":[\"-y\",\"@netforge/mcp\"]}'\n Codex codex mcp add netforge -- npx -y @netforge/mcp\n Gemini CLI gemini mcp add netforge npx -y @netforge/mcp\n\nAgent Skills (create a game, add content, playtest, play locally):\n npx skills add netnetdev/net-forge\n or copy them from: npx netforge skills path\n\nDocs: https://forge.netnet.capital/docs";
package/dist/setup.js ADDED
@@ -0,0 +1,118 @@
1
+ // `netforge setup agent`: makes a project ready for a coding agent. Writes
2
+ // AGENTS.md (or adds a Net Forge section to an existing one), a CLAUDE.md
3
+ // that imports it, Cursor rules, and the MCP server entry in .mcp.json,
4
+ // .cursor/mcp.json and .vscode/mcp.json (merged into existing files, never replacing other
5
+ // servers). Prints the install lines for the agents that configure MCP
6
+ // outside the project.
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8
+ import { dirname, join } from "node:path";
9
+ export const MCP_PACKAGE = "@netforge/mcp";
10
+ export const SKILLS_SOURCE = "netnetdev/net-forge";
11
+ export const DOCS_URL = "https://forge.netnet.capital/docs";
12
+ const MARK = "<!-- netforge:agents -->";
13
+ export const AGENTS_SECTION = `${MARK}
14
+ ## Net Forge
15
+
16
+ This project builds a game on Net Forge, an MIT TypeScript engine for
17
+ multiplayer browser games. One simulation (ECS, Rapier physics, fixed tick,
18
+ snapshots) runs in the browser and on the room server, and a template draws,
19
+ networks and simulates the game.
20
+
21
+ - The whole game is \`src/game.ts\`: declarations (game, prefab, weapon,
22
+ upgrade, director, system, earn.rule, earn.stake, store.sku and the genre's
23
+ own). It may import only \`@netforge/engine\`.
24
+ - Ids are lower_snake_case. Every game declares at least one \`earn.rule\`
25
+ and one \`earn.stake\`.
26
+ - Never write renderers, netcode or physics. Models are asset library ids
27
+ such as \`"style.lowpoly.ghost"\`, never URLs or paths.
28
+ - After each change: \`netforge_validate_game\`, then \`netforge_run_playtest\`
29
+ (MCP server \`${MCP_PACKAGE}\`). To try it in a browser:
30
+ \`netforge_play_locally\` or \`npx kit-play\`.
31
+ - Look things up with \`netforge_search_docs\` and \`netforge_explain_api\`
32
+ before guessing. Docs: ${DOCS_URL}
33
+ `;
34
+ export const CLAUDE_MD = "@AGENTS.md\n";
35
+ export const CURSOR_RULE = `---
36
+ description: Net Forge game rules (src/game.ts, validation, playtests)
37
+ alwaysApply: true
38
+ ---
39
+
40
+ Read AGENTS.md. The game is src/game.ts and it may import only @netforge/engine.
41
+ Validate every change with netforge_validate_game and playtest with
42
+ netforge_run_playtest before calling it done.
43
+ `;
44
+ const server = { command: "npx", args: ["-y", MCP_PACKAGE] };
45
+ function readJson(file) {
46
+ if (!existsSync(file))
47
+ return {};
48
+ try {
49
+ const v = JSON.parse(readFileSync(file, "utf8"));
50
+ return v && typeof v === "object" && !Array.isArray(v) ? v : null;
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ function write(dir, rel, text, r) {
57
+ mkdirSync(dirname(join(dir, rel)), { recursive: true });
58
+ writeFileSync(join(dir, rel), text);
59
+ r.written.push(rel);
60
+ }
61
+ /** Adds the netforge server under `key` in a JSON config, keeping everything else. */
62
+ function mergeMcp(dir, rel, key, entry, r) {
63
+ const file = join(dir, rel);
64
+ const cfg = readJson(file);
65
+ if (cfg === null) {
66
+ r.skipped.push(`${rel} (not plain JSON, perhaps comments; left as it is: add the netforge entry by hand)`);
67
+ return;
68
+ }
69
+ const current = cfg[key];
70
+ if (current !== undefined && (current === null || typeof current !== "object" || Array.isArray(current))) {
71
+ r.skipped.push(`${rel} ("${key}" is not an object, left as it is)`);
72
+ return;
73
+ }
74
+ const servers = (current ?? {});
75
+ if (servers.netforge) {
76
+ r.skipped.push(`${rel} (already has netforge)`);
77
+ return;
78
+ }
79
+ cfg[key] = { ...servers, netforge: entry };
80
+ write(dir, rel, JSON.stringify(cfg, null, 2) + "\n", r);
81
+ }
82
+ export function setupAgent(dir) {
83
+ const r = { written: [], skipped: [] };
84
+ const agents = join(dir, "AGENTS.md");
85
+ if (!existsSync(agents))
86
+ write(dir, "AGENTS.md", `# Notes for coding agents\n\n${AGENTS_SECTION}`, r);
87
+ else if (readFileSync(agents, "utf8").includes(MARK))
88
+ r.skipped.push("AGENTS.md (already has the Net Forge section)");
89
+ else
90
+ write(dir, "AGENTS.md", `${readFileSync(agents, "utf8").trimEnd()}\n\n${AGENTS_SECTION}`, r);
91
+ const claude = join(dir, "CLAUDE.md");
92
+ if (!existsSync(claude))
93
+ write(dir, "CLAUDE.md", CLAUDE_MD, r);
94
+ else if (/@AGENTS\.md/.test(readFileSync(claude, "utf8")))
95
+ r.skipped.push("CLAUDE.md (already imports AGENTS.md)");
96
+ else
97
+ write(dir, "CLAUDE.md", `${readFileSync(claude, "utf8").trimEnd()}\n\n${CLAUDE_MD}`, r);
98
+ if (existsSync(join(dir, ".cursor", "rules", "netforge.mdc")))
99
+ r.skipped.push(".cursor/rules/netforge.mdc (exists)");
100
+ else
101
+ write(dir, ".cursor/rules/netforge.mdc", CURSOR_RULE, r);
102
+ mergeMcp(dir, ".mcp.json", "mcpServers", server, r);
103
+ mergeMcp(dir, ".cursor/mcp.json", "mcpServers", server, r);
104
+ mergeMcp(dir, ".vscode/mcp.json", "servers", { type: "stdio", ...server }, r);
105
+ return r;
106
+ }
107
+ export const INSTALL_LINES = `MCP server (${MCP_PACKAGE}, runs locally, no keys):
108
+ Claude Code claude mcp add netforge -- npx -y ${MCP_PACKAGE}
109
+ Cursor written to .cursor/mcp.json (project); or add the same entry to ~/.cursor/mcp.json
110
+ VS Code code --add-mcp '{"name":"netforge","command":"npx","args":["-y","${MCP_PACKAGE}"]}'
111
+ Codex codex mcp add netforge -- npx -y ${MCP_PACKAGE}
112
+ Gemini CLI gemini mcp add netforge npx -y ${MCP_PACKAGE}
113
+
114
+ Agent Skills (create a game, add content, playtest, play locally):
115
+ npx skills add ${SKILLS_SOURCE}
116
+ or copy them from: npx netforge skills path
117
+
118
+ Docs: ${DOCS_URL}`;
@@ -0,0 +1,9 @@
1
+ /** The package's skills folder (next to dist/ and src/). */
2
+ export declare function skillsDir(): string;
3
+ export interface SkillInfo {
4
+ readonly name: string;
5
+ readonly description: string;
6
+ }
7
+ export declare function listSkills(): SkillInfo[];
8
+ /** Copies every skill folder into `to`; returns the folders copied. */
9
+ export declare function copySkills(to: string): string[];
package/dist/skills.js ADDED
@@ -0,0 +1,33 @@
1
+ // The Agent Skills shipped in this package (skills/<name>/SKILL.md).
2
+ import { cpSync, existsSync, readdirSync, readFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ /** The package's skills folder (next to dist/ and src/). */
6
+ export function skillsDir() {
7
+ return join(dirname(fileURLToPath(import.meta.url)), "..", "skills");
8
+ }
9
+ export function listSkills() {
10
+ const root = skillsDir();
11
+ if (!existsSync(root))
12
+ return [];
13
+ return readdirSync(root)
14
+ .filter((d) => existsSync(join(root, d, "SKILL.md")))
15
+ .sort()
16
+ .map((d) => {
17
+ const text = readFileSync(join(root, d, "SKILL.md"), "utf8");
18
+ const field = (k) => new RegExp(`^${k}:\\s*(.+)$`, "m").exec(text)?.[1]?.trim() ?? "";
19
+ return { name: field("name") || d, description: field("description") };
20
+ });
21
+ }
22
+ /** Copies every skill folder into `to`; returns the folders copied. */
23
+ export function copySkills(to) {
24
+ const root = skillsDir();
25
+ const out = [];
26
+ for (const s of readdirSync(root)) {
27
+ if (!existsSync(join(root, s, "SKILL.md")))
28
+ continue;
29
+ cpSync(join(root, s), join(to, s), { recursive: true });
30
+ out.push(join(to, s));
31
+ }
32
+ return out;
33
+ }
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "netforge",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "Net Forge command line for coding agents: `npx netforge setup agent` writes AGENTS.md, CLAUDE.md, Cursor rules and MCP configs into a game project, and ships Agent Skills for building Net Forge games.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "netforge": "./dist/cli.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "skills",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "sideEffects": false,
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/netnetdev/net-forge.git",
32
+ "directory": "packages/netforge"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/netnetdev/net-forge/issues"
36
+ },
37
+ "homepage": "https://forge.netnet.capital/docs/agents",
38
+ "keywords": [
39
+ "net-forge",
40
+ "game-engine",
41
+ "ai-agents",
42
+ "agent-skills",
43
+ "agents-md",
44
+ "mcp",
45
+ "cli",
46
+ "vibe-coding",
47
+ "browser-game",
48
+ "multiplayer",
49
+ "gamedev"
50
+ ],
51
+ "engines": {
52
+ "node": ">=20"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "22.20.1",
56
+ "typescript": "5.8.3",
57
+ "vitest": "3.2.7"
58
+ },
59
+ "scripts": {
60
+ "build": "tsc -p tsconfig.json",
61
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json",
62
+ "test": "vitest run"
63
+ }
6
64
  }
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: netforge-add-content
3
+ description: Add or change content in a Net Forge game's src/game.ts, such as a new enemy, weapon, upgrade, earn rule, store item or spawn pacing. Use when the user asks to add an enemy, a boss, a weapon, a power-up, a reward or to rebalance a Net Forge game.
4
+ ---
5
+
6
+ # Add content to a Net Forge game
7
+
8
+ All content is declared in `src/game.ts` with functions imported from
9
+ `@netforge/engine`. Look up any declaration before using it:
10
+ `netforge_explain_api { "name": "weapon" }` or
11
+ `netforge_search_docs { "query": "spawn director curves" }`.
12
+
13
+ ## Common declarations (action templates)
14
+
15
+ ```ts
16
+ import { earn, prefab, weapon } from "@netforge/engine";
17
+
18
+ // An enemy. model is an asset library id, never a URL.
19
+ prefab("brute", { model: "style.lowpoly.orc_brute", health: 160, speed: 2.2, radius: 0.8, damage: 18, ai: "tank", mass: 6, drops: ["ember_mid"] });
20
+
21
+ // A weapon that fires by itself, with level-up steps.
22
+ weapon("spark_bolt", { kind: "bolt", damage: 14, cooldown: 0.6, range: 14, speed: 24, pierce: 1, knockback: 2,
23
+ levels: [{ count: 1 }, { damage: "+40%" }, { cooldown: "-20%" }] });
24
+
25
+ // A reward: pays when the event happens n times in a run.
26
+ earn.rule("first_light", { when: "wave.cleared", n: 2, pay: 5, oncePer: "run" });
27
+ ```
28
+
29
+ Board templates (tower defense, idle, cards, collector) use their own
30
+ declarations, such as `tower(...)`, `waves(...)`, `plant(...)`, `card(...)`,
31
+ `creature(...)` and `move(...)`. Read the template's own `src/game.ts` for
32
+ the pattern and copy it.
33
+
34
+ ## Checklist
35
+
36
+ 1. New ids are lower_snake_case and unique per kind.
37
+ 2. Anything an enemy drops is a declared pickup prefab.
38
+ 3. A new enemy appears only when the spawn `director({ pool })` (or the
39
+ template's own spawner) names it.
40
+ 4. Run `netforge_validate_game` and fix every error it returns; each error
41
+ has the likely line and a hint.
42
+ 5. Run `netforge_run_playtest` and act on its findings.
43
+
44
+ Docs: https://forge.netnet.capital/docs
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: netforge-create-game
3
+ description: Start a new multiplayer browser game with Net Forge from one of ten finished genre templates (survivor-like, arena shooter, tower defense, dungeon crawl, idle, platformer, card battler, monster collector, kart racer, party). Use when the user asks to make, scaffold or start a browser game, a multiplayer game, a co-op game or an .io-style game.
4
+ ---
5
+
6
+ # Create a Net Forge game
7
+
8
+ Net Forge games are one declarative file, `src/game.ts`, on top of a
9
+ template that already has rendering (three.js, WebGPU with a WebGL2
10
+ fallback), multiplayer netcode, physics, input, HUD and bots.
11
+
12
+ ## Steps
13
+
14
+ 1. Pick the template that matches the request. With the MCP server, call
15
+ `netforge_list_templates`; without it, use one of these ids: `survivor`,
16
+ `arena`, `tower-defense`, `dungeon`, `idle`, `platformer`, `cards`,
17
+ `collector`, `racer`, `party`.
18
+ 2. Create the folder:
19
+ - MCP: `netforge_create_game { "template": "survivor", "directory": "/abs/path/my-game" }`
20
+ - CLI: `npm create netforge@latest my-game -- --template survivor --yes`
21
+ 3. `cd my-game && npm install`
22
+ 4. Read `AGENTS.md` in the new folder, then make the user's changes in
23
+ `src/game.ts` (see the netforge-add-content skill).
24
+ 5. Validate and playtest (see the netforge-playtest skill), then let the
25
+ user play it (see the netforge-play-locally skill).
26
+
27
+ ## Rules
28
+
29
+ - `src/game.ts` may import only `@netforge/engine`.
30
+ - Ids are lower_snake_case.
31
+ - Every game keeps at least one `earn.rule(...)` and one `earn.stake(...)`.
32
+ - Keep `game({ style })`: the art style the template draws in.
33
+ - Do not write renderers, netcode or physics, and never use asset URLs.
34
+
35
+ Docs: https://forge.netnet.capital/docs
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: netforge-play-locally
3
+ description: Run a Net Forge game on this computer in the browser with play money, for one or more players, with no wallet, chain or servers. Use when the user wants to try, see or play their Net Forge game, or test multiplayer with several browser tabs.
4
+ ---
5
+
6
+ # Play a Net Forge game locally
7
+
8
+ 1. In the game folder (it has `netnet.json` and `src/game.ts`), run
9
+ `npm install` once.
10
+ 2. Start it:
11
+ - MCP: `netforge_play_locally { "projectDir": "/abs/path/my-game" }` returns the URL.
12
+ - CLI: `npm run play` or `npx kit-play` in the folder, which opens
13
+ `http://127.0.0.1:4663`.
14
+ 3. Open the URL in more tabs or windows for more players. `--host 0.0.0.0`
15
+ lets phones on the same network join.
16
+ 4. Stop it with `netforge_play_locally { "projectDir": "...", "stop": true }`
17
+ or Ctrl+C.
18
+
19
+ kit play builds the template's browser client with `src/game.ts` and runs a
20
+ local room host. Coins are play money and nothing pays out. It runs the
21
+ game's code with your permissions, so play only code you trust.
22
+
23
+ Docs: https://forge.netnet.capital/docs
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: netforge-playtest
3
+ description: Validate a Net Forge game and run headless bot playtests that report kills, waves, deaths, softlocks, earn awards and server milliseconds per tick. Use after every change to a Net Forge game's src/game.ts, or when the user asks whether the game works, is fun or is balanced.
4
+ ---
5
+
6
+ # Validate and playtest a Net Forge game
7
+
8
+ ## With the MCP server (@netforge/mcp)
9
+
10
+ 1. `netforge_validate_game { "projectDir": "/abs/path/my-game" }`
11
+ returns `ok` and, for each problem, the stage, the engine's message, the
12
+ likely line in `src/game.ts` and a hint. Fix and repeat until `ok` is true.
13
+ 2. `netforge_run_playtest { "projectDir": "/abs/path/my-game", "bots": 4, "seconds": 120 }`
14
+ runs bots on the same simulation the room server uses and returns the
15
+ report plus plain findings. Pass `"bot": "human"` for players who react
16
+ like people (the fun metrics), and `"response_format": "detailed"` for
17
+ every metric.
18
+ 3. Read `findings` first. Fix softlocks, failed checks, missing earn and a
19
+ high p99 tick cost before anything else.
20
+
21
+ ## Without MCP
22
+
23
+ The playtest is `engine.test.run` from `@netforge/engine`:
24
+
25
+ ```ts
26
+ import { engine } from "@netforge/engine";
27
+ const r = await engine.test.run({ ticks: 30 * 120, bots: 4, seed: 1 });
28
+ console.log(r.kills, r.wave, r.softlock, r.msPerTick, r.failed);
29
+ ```
30
+
31
+ Run it from a test file in the template package after importing the
32
+ template's `src/manifest.ts`, the way each template's `test/harness.test.ts`
33
+ does.
34
+
35
+ Docs: https://forge.netnet.capital/docs