castle-web-cli 0.4.75 → 0.4.77
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/dist/agent-prompts.d.ts +13 -0
- package/dist/agent-prompts.js +43 -13
- package/dist/agent.d.ts +29 -0
- package/dist/agent.js +1004 -257
- package/dist/castle-host/host.js +59 -5
- package/dist/commonInstructions.d.ts +1 -1
- package/dist/commonInstructions.js +11 -1
- package/dist/filesChanged.d.ts +25 -0
- package/dist/filesChanged.js +140 -0
- package/dist/ide.d.ts +1 -0
- package/dist/ide.js +250 -1
- package/dist/init.js +46 -4
- package/dist/save-deck.js +8 -1
- package/dist/serve.js +45 -3
- package/dist/shell/assets/index-CvHiGhAV.js +141 -0
- package/dist/shell/assets/{index-WNbOHPBj.css → index-QteLRDnK.css} +1 -1
- package/dist/shell/index.html +2 -2
- package/kits/basic-2d/CLAUDE.md +13 -5
- package/kits/basic-2d/castle.json +15 -0
- package/kits/basic-2d/drawings/pig.pxart +22 -55
- package/kits/basic-2d/editors/PlayOnly.jsx +8 -3
- package/kits/basic-2d/editors/PxArtEditor.jsx +237 -4
- package/kits/basic-2d/editors/SingleEditor.jsx +30 -59
- package/kits/basic-2d/editors/pixelEditorChrome.jsx +17 -11
- package/kits/basic-2d/editors/pixelGeometry.js +95 -0
- package/kits/basic-2d/editors/pixelInspector.jsx +229 -51
- package/kits/basic-2d/editors/pxArtTools.js +109 -1
- package/kits/basic-2d/engine/ScenePlayer.jsx +11 -160
- package/kits/basic-2d/engine/liveReload.js +88 -0
- package/kits/basic-2d/engine/ui.jsx +15 -0
- package/kits/basic-2d/engine/ui.module.css +44 -117
- package/kits/basic-2d/main.jsx +6 -10
- package/package.json +10 -1
- package/dist/shell/assets/index-Dfn29Bkt.js +0 -108
- package/kits/basic-2d/editors/App.jsx +0 -226
- package/kits/basic-2d/editors/CodeEditor.jsx +0 -79
- package/kits/basic-2d/editors/FileBrowser.jsx +0 -349
- package/kits/basic-2d/editors/codeTheme.js +0 -135
- package/kits/basic-2d/engine/playConsole.js +0 -66
- package/kits/basic-2d/pnpm-workspace.yaml +0 -3
- package/kits/basic-3d/.prettierrc +0 -8
- package/kits/basic-3d/CLAUDE.md +0 -162
- package/kits/basic-3d/behaviors/Camera.jsx +0 -56
- package/kits/basic-3d/behaviors/Collider.jsx +0 -78
- package/kits/basic-3d/behaviors/Mesh.jsx +0 -82
- package/kits/basic-3d/behaviors/Model.jsx +0 -61
- package/kits/basic-3d/behaviors/Transform.jsx +0 -35
- package/kits/basic-3d/editors/App.jsx +0 -147
- package/kits/basic-3d/editors/CodeEditor.jsx +0 -112
- package/kits/basic-3d/editors/FileBrowser.jsx +0 -143
- package/kits/basic-3d/editors/ModelEditor.jsx +0 -400
- package/kits/basic-3d/editors/PlayOnly.jsx +0 -22
- package/kits/basic-3d/editors/SceneEditor.jsx +0 -1081
- package/kits/basic-3d/editors/behaviorRegistry.js +0 -24
- package/kits/basic-3d/editors/editorHistory.js +0 -52
- package/kits/basic-3d/editors/viewportRig.js +0 -90
- package/kits/basic-3d/engine/ScenePlayer.jsx +0 -58
- package/kits/basic-3d/engine/SceneUI.jsx +0 -67
- package/kits/basic-3d/engine/SceneViewport.jsx +0 -102
- package/kits/basic-3d/engine/autoInspector.jsx +0 -51
- package/kits/basic-3d/engine/files.js +0 -73
- package/kits/basic-3d/engine/scene.js +0 -502
- package/kits/basic-3d/engine/threeUtil.js +0 -260
- package/kits/basic-3d/engine/ui.jsx +0 -352
- package/kits/basic-3d/engine/ui.module.css +0 -944
- package/kits/basic-3d/eslint.config.js +0 -51
- package/kits/basic-3d/index.html +0 -11
- package/kits/basic-3d/main.jsx +0 -10
- package/kits/basic-3d/models/block.model +0 -14
- package/kits/basic-3d/package-lock.json +0 -2713
- package/kits/basic-3d/package.json +0 -41
- package/kits/basic-3d/pnpm-lock.yaml +0 -1769
- package/kits/basic-3d/scenes/main.scene +0 -76
- package/kits/basic-3d/vite.config.js +0 -1
package/dist/agent-prompts.d.ts
CHANGED
|
@@ -9,9 +9,19 @@ export interface PromptTask {
|
|
|
9
9
|
status: string;
|
|
10
10
|
progress: number;
|
|
11
11
|
notes: string;
|
|
12
|
+
files?: string[];
|
|
13
|
+
error?: string;
|
|
14
|
+
blockedBy?: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface PromptSibling {
|
|
17
|
+
title: string;
|
|
18
|
+
status: string;
|
|
19
|
+
files?: string[];
|
|
12
20
|
}
|
|
13
21
|
export declare function buildRouterPrompt(opts: {
|
|
14
22
|
deckLabel: string;
|
|
23
|
+
quickReference?: string;
|
|
24
|
+
deckTree?: string;
|
|
15
25
|
messages: PromptMessage[];
|
|
16
26
|
tasks: PromptTask[];
|
|
17
27
|
instruction: string;
|
|
@@ -30,5 +40,8 @@ export declare function buildTaskPrompt(opts: {
|
|
|
30
40
|
notesPath: string;
|
|
31
41
|
depsSummary?: string;
|
|
32
42
|
backend?: "cursor" | "claude";
|
|
43
|
+
deckTree?: string;
|
|
44
|
+
quickReference?: string;
|
|
45
|
+
siblings?: PromptSibling[];
|
|
33
46
|
}): string;
|
|
34
47
|
export declare const CLAUDE_TASK_SYSTEM_REMINDER = "Castle background task agent: work autonomously to completion -- never pause to ask questions or wait for confirmation; finish the task end-to-end, then stop. Prefer the quickest viable change that fully does the job.";
|
package/dist/agent-prompts.js
CHANGED
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
const TRANSCRIPT_LIMIT = 40;
|
|
9
9
|
const ROUTER_RULES = `You are Castle's create assistant: the fast conversational router for a game-making session. The deck (game project) lives in the current directory and runs live in a pane right next to this chat.
|
|
10
10
|
|
|
11
|
-
What a deck is: a normal web project served by vite -- index.html plus plain JS/JSX modules, with real npm dependencies (more can be installed), the castle-web-sdk package, and usually a kit framework whose engine, behaviors, scenes, editors, and drawings are ordinary files in this directory. The web platform is fully available (DOM, canvas, npm libraries like react, three, etc.). The deck's CLAUDE.md / AGENTS.md
|
|
11
|
+
What a deck is: a normal web project served by vite -- index.html plus plain JS/JSX modules, with real npm dependencies (more can be installed), the castle-web-sdk package, and usually a kit framework whose engine, behaviors, scenes, editors, and drawings are ordinary files in this directory. The web platform is fully available (DOM, canvas, npm libraries like react, three, etc.). The deck's Quick reference and file list below describe its setup; the full CLAUDE.md / AGENTS.md has deeper detail. NEVER claim something is impossible or unsupported on the platform without checking that context (or, for specifics it doesn't cover, the deck's files) first.
|
|
12
12
|
|
|
13
13
|
Hard rules:
|
|
14
14
|
- You NEVER edit files or run state-changing commands. All building and fixing happens through background task agents -- always hand the longer work to them.
|
|
15
|
-
- You are the fast lane: get to your final reply as quickly as possible. When the user reports something broken, do NOT dig into the code to diagnose it first -- spawn a task whose job is to investigate AND fix it. Only read deck files when your reply itself needs them (answering a question about the deck, grounding a claim -- never make things up); never read as pre-work before spawning a task.
|
|
15
|
+
- You are the fast lane: get to your final reply as quickly as possible. When the user reports something broken, do NOT dig into the code to diagnose it first -- spawn a task whose job is to investigate AND fix it. Only read deck files when your reply itself needs them (answering a question about the deck, grounding a claim -- never make things up); never read as pre-work before spawning a task, and never read files just to learn conventions already covered by the Quick reference.
|
|
16
16
|
- Launch a SET of small steps the user tests one by one -- a pipeline, never one big task they wait on, never untestable fragments. One interacting mechanic = one task (paddle + ball + bricks = one playable core, not three). First step = the smallest genuinely playable thing; later steps build it out. Match breadth to ambition ("basic" = a few steps; "go wild" = many). You're optimizing the user's taste and feedback -- more small testable steps = more points where they steer it into something theirs.
|
|
17
17
|
- The whole goal: every piece of work TESTABLE in actual gameplay ASAP. Start every task as early as possible and run them in PARALLEL. Do NOT break tasks down by which files they touch, and never add \`after:\` just to avoid two tasks editing the same file -- tasks make surgical edits and overlap fine. The only real dependency between tasks is INFORMATION: a task is blocked only when it needs a fact it does not yet have.
|
|
18
|
-
- Dissolve those information-dependencies with a CONTRACT decided up front, instead of serializing tasks. Before spawning, pin the shared names the tasks will agree on -- scene file names, behavior names, drawing file names, tags -- and give each parallel task the exact names it must CREATE or REFERENCE. Then they all run at once and converge to a working game with NO follow-up wiring task: the scene task places actors referencing behaviors and drawings by their agreed names before those files even exist; the behavior tasks create those behavior names; the drawing tasks create those drawing files. (Art: pin
|
|
18
|
+
- Dissolve those information-dependencies with a CONTRACT decided up front, instead of serializing tasks. Before spawning, pin the shared names the tasks will agree on -- scene file names, behavior names, drawing file names, tags -- and give each parallel task the exact names it must CREATE or REFERENCE. Pinned names must be EXACT filenames, extension included -- \`drawings/ship.pxart\` in a kit deck (the kit's CLAUDE.md names the format), \`drawings/ship.svg\` in a bare one. A bare deck has no kit to define a default art format or loader, so its contract must also state the format and loading approach every consumer will use. Then they all run at once and converge to a working game with NO follow-up wiring task: the scene task places actors referencing behaviors and drawings by their agreed names before those files even exist; the behavior tasks create those behavior names; the drawing tasks create those drawing files. (Art: pin an exact drawing filename like \`drawings/ship.pxart\` up front -> the scene task points an actor at it while another task draws it, in parallel; no place-it-later task.) Fall back to a separate waiting wire-in task (\`after:\` the maker, on the least possible) ONLY when a task truly needs another's actual produced output, not just its agreed name. Working out this breakdown -- what to parallelize, which names to pin up front -- is the core of your job and differs per game.
|
|
19
19
|
- Write each task prompt for the quickest viable, testable change that still delivers a meaningful step up. Dive deep only if the user asked.
|
|
20
|
-
- Real, editable assets: drawing files under \`drawings/\` for game objects, characters, and scenery as real actors; data-driven UI (health/score/HUD) and effects stay code. The logic/scene task references the REAL drawing
|
|
20
|
+
- Real, editable assets: drawing files under \`drawings/\` for game objects, characters, and scenery as real actors; data-driven UI (health/score/HUD) and effects stay code. The logic/scene task references the REAL drawing filenames immediately (e.g. \`drawings/paddle.pxart\`) -- a not-yet-created drawing auto-renders as a plain block, so the game is playable instantly AND the real art appears the moment the drawing task writes that file, with NO scene change. Ownership is EXCLUSIVE and reciprocal: the drawing task just CREATES the files at those agreed names and must NEVER edit the scene; the scene/logic task only REFERENCES those names and must NEVER create the drawing files (a sibling task owns them). When you split art into its own task, spell out BOTH sides in the prompts you write -- tell the art task exactly which drawing names to create, and tell every consumer task to reference those names and explicitly NOT draw them itself -- so two agents never make the same sprites. (The deck's CLAUDE.md / AGENTS.md gives the deck's drawing format, file extension, and how to make art.) Never say "greybox" or other dev jargon to the user.
|
|
21
21
|
- To spawn a background task, include a fenced block in your reply:
|
|
22
22
|
|
|
23
23
|
\`\`\`castle-task
|
|
@@ -41,12 +41,14 @@ comma-separated active-task titles or ids, or \`all\`
|
|
|
41
41
|
\`\`\`
|
|
42
42
|
|
|
43
43
|
- \`castle-done\` removes FINISHED rows (done/failed). Use it when the user confirms a task works, or to clear a finished task that has become obsolete (replaced by newer work). \`all\` clears every finished row.
|
|
44
|
-
- \`castle-stop\` stops AND removes ACTIVE rows (running/waiting) -- a running task's agent is killed. Use it when the user asks to stop something, or when you spawn a fix/replacement that makes an in-flight task obsolete. \`all\` stops everything active.
|
|
45
|
-
- When you spawn a task that fixes, redoes, or replaces an earlier one, remove the earlier one in the SAME reply: \`castle-done\` if it already finished, \`castle-stop\` if it is still running or
|
|
44
|
+
- \`castle-stop\` stops AND removes ACTIVE rows (running/waiting/blocked) -- a running task's agent is killed. Use it when the user asks to stop something, or when you spawn a fix/replacement that makes an in-flight task obsolete. \`all\` stops everything active.
|
|
45
|
+
- When you spawn a task that fixes, redoes, or replaces an earlier one, remove the earlier one in the SAME reply: \`castle-done\` if it already finished, \`castle-stop\` if it is still running, waiting, or blocked. Keep the board meaning "what to look at right now".
|
|
46
|
+
- A \`failed\` row carries an \`error:\` line -- why it failed. A \`blocked\` row waited on a dep that failed or was stopped, so its output never arrived and it will never start on its own -- \`blocked by:\` names that dep. When you see one, decide for the user: usually spawn a replacement task (or pipeline) that redoes the failed work, and in the SAME reply \`castle-stop\` the blocked row and \`castle-done\` the failed one, so the board stays clean.
|
|
46
47
|
- Do not mark a task done to imply YOU verified its quality -- the user playtests and confirms that. But DO keep the board tidy: clear finished work the user blessed, and remove anything clearly obsolete. When genuinely unsure, leave the row.
|
|
47
48
|
- Never claim the board is cleared without actually emitting the fence.
|
|
48
49
|
- Tasks are one-and-done -- when the user gives feedback on a finished task, spawn a new fix task (and \`castle-done\` the old row) rather than reopening it.
|
|
49
50
|
- Task agents are capable coding agents working in this same deck directory, but they know nothing about this conversation beyond your prompt.
|
|
51
|
+
- Board rows may include \`files:\` for finished work. Use those touched-file lists to aim follow-up/fix tasks and to keep shared names consistent without rereading the deck.
|
|
50
52
|
|
|
51
53
|
Asking with options (the \`\`\`ask block). When you need the user to settle a few choices at once, emit ONE fenced block tagged \`ask\` containing JSON -- it renders inline in the chat as grouped options they tap and submit together (far better than stacking questions they can only half-answer). Reach for it to pin a direction fast when their ask is vague ("make me a game" -> ask what kind), NOT to interrogate. Options only, no free text.
|
|
52
54
|
|
|
@@ -86,22 +88,37 @@ function renderTasks(tasks) {
|
|
|
86
88
|
return tasks
|
|
87
89
|
.map((t) => {
|
|
88
90
|
const notes = t.notes.trim() ? ` -- notes: ${t.notes.trim()}` : "";
|
|
89
|
-
|
|
91
|
+
const files = t.files && t.files.length > 0 ? ` -- files: ${t.files.join(", ")}` : "";
|
|
92
|
+
const error = t.error ? ` -- error: ${t.error}` : "";
|
|
93
|
+
const blockedBy = t.blockedBy && t.blockedBy.length > 0
|
|
94
|
+
? ` -- blocked by: ${t.blockedBy.join(", ")}`
|
|
95
|
+
: "";
|
|
96
|
+
return `- [${t.status} ${t.progress}%] ${t.title} (${t.id})${notes}${files}${error}${blockedBy}`;
|
|
90
97
|
})
|
|
91
98
|
.join("\n");
|
|
92
99
|
}
|
|
93
100
|
export function buildRouterPrompt(opts) {
|
|
101
|
+
const quickReference = opts.quickReference?.trim()
|
|
102
|
+
? `\n\n== how this deck works ==\n${opts.quickReference.trim()}`
|
|
103
|
+
: "";
|
|
104
|
+
const deckFiles = opts.deckTree?.trim()
|
|
105
|
+
? `\n\n== deck files ==\n${opts.deckTree.trim()}`
|
|
106
|
+
: "";
|
|
107
|
+
// Section order is stable-to-volatile for prompt caching: rules + deck
|
|
108
|
+
// identity never change within a serve, the transcript is append-only, and
|
|
109
|
+
// the churning state (file tree, task board, instruction) rides at the tail
|
|
110
|
+
// so a task creating a file doesn't invalidate the cached transcript prefix.
|
|
94
111
|
return `${ROUTER_RULES}
|
|
95
112
|
|
|
96
113
|
== deck ==
|
|
97
|
-
${opts.deckLabel}
|
|
114
|
+
${opts.deckLabel}${quickReference}
|
|
115
|
+
|
|
116
|
+
== conversation so far ==
|
|
117
|
+
${renderTranscript(opts.messages)}${deckFiles}
|
|
98
118
|
|
|
99
119
|
== background tasks ==
|
|
100
120
|
${renderTasks(opts.tasks)}
|
|
101
121
|
|
|
102
|
-
== conversation so far ==
|
|
103
|
-
${renderTranscript(opts.messages)}
|
|
104
|
-
|
|
105
122
|
== now ==
|
|
106
123
|
${opts.instruction}
|
|
107
124
|
|
|
@@ -130,6 +147,18 @@ export function buildTaskPrompt(opts) {
|
|
|
130
147
|
const deps = opts.depsSummary
|
|
131
148
|
? `\n\nThis task waited on earlier tasks:\n${opts.depsSummary}\n`
|
|
132
149
|
: "";
|
|
150
|
+
const siblingRows = (opts.siblings ?? [])
|
|
151
|
+
.map((s) => `- [${s.status}] ${s.title}${s.files && s.files.length > 0 ? ` -- files: ${s.files.join(", ")}` : ""}`)
|
|
152
|
+
.join("\n");
|
|
153
|
+
const siblings = siblingRows
|
|
154
|
+
? `\n\nOther tasks on this deck's board (snapshot at your start):\n${siblingRows}\nRunning and waiting rows are sibling agents working in this same directory. If a sibling plainly owns something your prompt only references (art, a scene, a behavior), leave it to them -- create only what YOUR prompt says to create. Finished rows' file lists show where recent work landed.\n`
|
|
155
|
+
: "";
|
|
156
|
+
const quickReference = opts.quickReference?.trim()
|
|
157
|
+
? `\n\nHow this deck works (from its docs -- read the full CLAUDE.md / AGENTS.md only when you need detail beyond this):\n\n${opts.quickReference.trim()}\n`
|
|
158
|
+
: "";
|
|
159
|
+
const layout = opts.deckTree?.trim()
|
|
160
|
+
? `\n\nDeck files (snapshot at task start; parallel sibling tasks may create files named in your prompt that are absent here):\n\n${opts.deckTree.trim()}\n`
|
|
161
|
+
: "";
|
|
133
162
|
// Claude-only: collapsing the wrap-up (progress 90 + restart + notes) into
|
|
134
163
|
// one shell call reliably saves 1-2 serial ~7s turns there. Cursor's
|
|
135
164
|
// composer sometimes reacts to the same rule with MORE calls, so it stays
|
|
@@ -140,13 +169,14 @@ export function buildTaskPrompt(opts) {
|
|
|
140
169
|
const wrapUp = opts.backend === "claude"
|
|
141
170
|
? `\n- Wrap up in ONE tool call, not several: once your last file edit is done, combine the 90-progress write, the final \`npm run restart\`, and writing the notes file into a single shell command (\`;\`-separated so the notes land even if the restart hiccups). Then stop -- no extra turns after it.`
|
|
142
171
|
: "";
|
|
143
|
-
return `You are a background build agent for the Castle deck "${opts.deckLabel}" (current directory). A separate conversation agent dispatched you with one task. Follow the deck's CLAUDE.md / AGENTS.md conventions, and reload the served deck after changes (\`npm run restart\`)
|
|
172
|
+
return `You are a background build agent for the Castle deck "${opts.deckLabel}" (current directory). A separate conversation agent dispatched you with one task. Follow the deck's CLAUDE.md / AGENTS.md conventions, and reload the served deck after changes (\`npm run restart\`).${quickReference}${layout}
|
|
144
173
|
|
|
145
174
|
Your task (id ${opts.taskId}): ${opts.title}
|
|
146
175
|
|
|
147
|
-
${opts.prompt}${deps}
|
|
176
|
+
${opts.prompt}${deps}${siblings}
|
|
148
177
|
|
|
149
178
|
Operating rules:
|
|
179
|
+
- The file snapshot above is for orientation. Prefer going straight to the files this task touches, but if this is an investigation or debugging task, survey what you need to understand the failure.
|
|
150
180
|
- Speed is of the essence -- go for the quickest viable, testable change, unless this prompt explicitly tells you to take longer and dive deep. It must still amount to at least one meaningful step up (one or a few features, pieces of art, etc.), never a token gesture.
|
|
151
181
|
- The USER is the verifier -- the whole tasks system exists so the user playtests every change themselves. Your first priority is to finish as soon as possible with the change genuinely in place and reachable in the running deck, so the user can test it right away. Do NOT run verification (screenshots especially) unless you are really sure it will catch something a re-read of your own change cannot -- and even then at most one cheap check, never a retry loop. Time spent verifying is time the user is left waiting.
|
|
152
182
|
- The moment implementation is complete and you switch to verifying, write 90 to the progress file -- verification time must not read as stalled progress.
|
package/dist/agent.d.ts
CHANGED
|
@@ -4,6 +4,34 @@ export declare const AGENT_WS_PATH = "/__castle/agent";
|
|
|
4
4
|
export declare const AGENT_ATTACHMENT_PREFIX = "/__castle/agent/attachments/";
|
|
5
5
|
export type AgentBackend = "cursor" | "claude";
|
|
6
6
|
export type ClaudeModel = "sonnet" | "opus" | "fable";
|
|
7
|
+
type TaskStatus = "waiting" | "running" | "blocked" | "done" | "failed" | "interrupted";
|
|
8
|
+
interface TaskRecord {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
prompt: string;
|
|
12
|
+
after: string[];
|
|
13
|
+
status: TaskStatus;
|
|
14
|
+
progress: number;
|
|
15
|
+
notes: string;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
startedAt?: string;
|
|
19
|
+
finishedAt?: string;
|
|
20
|
+
pid?: number;
|
|
21
|
+
originMessageId?: string;
|
|
22
|
+
files?: string[];
|
|
23
|
+
resultSummary?: string;
|
|
24
|
+
avatar?: string;
|
|
25
|
+
phase?: string;
|
|
26
|
+
acknowledged?: boolean;
|
|
27
|
+
rejected?: boolean;
|
|
28
|
+
blockedBy?: string[];
|
|
29
|
+
}
|
|
30
|
+
export interface DepsState {
|
|
31
|
+
kind: "ready" | "waiting" | "blocked";
|
|
32
|
+
blockedBy?: string[];
|
|
33
|
+
}
|
|
34
|
+
export declare function classifyDeps(tasks: Map<string, TaskRecord>, task: TaskRecord): DepsState;
|
|
7
35
|
export interface AgentServer {
|
|
8
36
|
/** Attach the agent WebSocket if the upgrade targets the agent path. */
|
|
9
37
|
handleUpgrade(req: http.IncomingMessage, socket: Duplex, head: Buffer): boolean;
|
|
@@ -15,3 +43,4 @@ export declare function createAgentServer(opts: {
|
|
|
15
43
|
deckDir: string;
|
|
16
44
|
deckLabel: string;
|
|
17
45
|
}): AgentServer;
|
|
46
|
+
export {};
|