castle-web-cli 0.4.78 → 0.4.80

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.
Files changed (46) hide show
  1. package/dist/agent-prompts.d.ts +4 -1
  2. package/dist/agent-prompts.js +28 -7
  3. package/dist/agent.d.ts +7 -2
  4. package/dist/agent.js +655 -51
  5. package/dist/native/loop.d.ts +2 -0
  6. package/dist/native/loop.js +698 -0
  7. package/dist/native/openrouter.d.ts +55 -0
  8. package/dist/native/openrouter.js +354 -0
  9. package/dist/native/playtest-browser.d.ts +34 -0
  10. package/dist/native/playtest-browser.js +354 -0
  11. package/dist/native/playtest-executor.d.ts +3 -0
  12. package/dist/native/playtest-executor.js +156 -0
  13. package/dist/native/playtest.d.ts +131 -0
  14. package/dist/native/playtest.js +314 -0
  15. package/dist/native/tools.d.ts +38 -0
  16. package/dist/native/tools.js +690 -0
  17. package/dist/native/types.d.ts +40 -0
  18. package/dist/native/types.js +41 -0
  19. package/dist/serve.js +12 -0
  20. package/dist/shell/assets/{index-yGdKhgfZ.js → index-D3unT7do.js} +37 -37
  21. package/dist/shell/assets/{index-WE24qX3d.css → index-RZrw5gQ2.css} +1 -1
  22. package/dist/shell/index.html +2 -2
  23. package/kits/basic-2d/CLAUDE.md +29 -3
  24. package/kits/basic-2d/behaviors/Layout.jsx +10 -0
  25. package/kits/basic-2d/behaviors/Sprite.jsx +1 -1
  26. package/kits/basic-2d/blueprints/cauldron.scene +22 -0
  27. package/kits/basic-2d/castle.json +5 -7
  28. package/kits/basic-2d/docs/pxart-format.md +4 -3
  29. package/kits/basic-2d/drawings/cauldron.pxart +113 -0
  30. package/kits/basic-2d/editors/BlueprintLibrary.jsx +247 -0
  31. package/kits/basic-2d/editors/PlayOnly.jsx +1 -0
  32. package/kits/basic-2d/editors/SceneEditor.jsx +399 -411
  33. package/kits/basic-2d/editors/SelectionOverlay.jsx +125 -63
  34. package/kits/basic-2d/editors/SingleEditor.jsx +11 -2
  35. package/kits/basic-2d/editors/editorHistory.js +8 -2
  36. package/kits/basic-2d/editors/inspectorSheet.js +5 -19
  37. package/kits/basic-2d/engine/ScenePlayer.jsx +2 -2
  38. package/kits/basic-2d/engine/blueprint.js +423 -0
  39. package/kits/basic-2d/engine/files.js +1 -1
  40. package/kits/basic-2d/engine/scene.js +29 -29
  41. package/kits/basic-2d/engine/ui.jsx +160 -21
  42. package/kits/basic-2d/engine/ui.module.css +155 -13
  43. package/kits/basic-2d/pnpm-workspace.yaml +3 -0
  44. package/kits/basic-2d/scenes/main.scene +3 -13
  45. package/package.json +2 -1
  46. package/kits/basic-2d/drawings/pig.pxart +0 -26
@@ -12,6 +12,7 @@ export interface PromptTask {
12
12
  files?: string[];
13
13
  error?: string;
14
14
  blockedBy?: string[];
15
+ suspectNoChanges?: boolean;
15
16
  }
16
17
  export interface PromptSibling {
17
18
  title: string;
@@ -22,6 +23,7 @@ export declare function buildRouterPrompt(opts: {
22
23
  deckLabel: string;
23
24
  quickReference?: string;
24
25
  deckTree?: string;
26
+ deckContents?: string;
25
27
  messages: PromptMessage[];
26
28
  tasks: PromptTask[];
27
29
  instruction: string;
@@ -39,8 +41,9 @@ export declare function buildTaskPrompt(opts: {
39
41
  progressPath: string;
40
42
  notesPath: string;
41
43
  depsSummary?: string;
42
- backend?: "cursor" | "claude";
44
+ backend?: "cursor" | "claude" | "smith";
43
45
  deckTree?: string;
46
+ deckContents?: string;
44
47
  quickReference?: string;
45
48
  siblings?: PromptSibling[];
46
49
  }): string;
@@ -93,17 +93,29 @@ function renderTasks(tasks) {
93
93
  const blockedBy = t.blockedBy && t.blockedBy.length > 0
94
94
  ? ` -- blocked by: ${t.blockedBy.join(", ")}`
95
95
  : "";
96
- return `- [${t.status} ${t.progress}%] ${t.title} (${t.id})${notes}${files}${error}${blockedBy}`;
96
+ const suspect = t.suspectNoChanges
97
+ ? " -- caution: done but touched no tracked files (bash side effects aren't tracked); verify the work actually landed"
98
+ : "";
99
+ return `- [${t.status} ${t.progress}%] ${t.title} (${t.id})${notes}${files}${error}${blockedBy}${suspect}`;
97
100
  })
98
101
  .join("\n");
99
102
  }
100
103
  export function buildRouterPrompt(opts) {
104
+ // Making the ABSENCE explicit, not just omitting the section, matters: a
105
+ // router on a bare/greenfield deck otherwise has no way to tell "no docs
106
+ // exist" from "the docs section just wasn't included in this prompt", and
107
+ // burns its first tool-call turns hunting for CLAUDE.md / **/*.md that
108
+ // isn't there (real incident: 2 of a router turn's 9 iterations, on an
109
+ // 8-file deck with zero markdown files).
101
110
  const quickReference = opts.quickReference?.trim()
102
111
  ? `\n\n== how this deck works ==\n${opts.quickReference.trim()}`
103
- : "";
112
+ : "\n\n== how this deck works ==\nThis deck has no CLAUDE.md / AGENTS.md quick reference. There is no documentation to read -- do not search for one (CLAUDE.md, README, **/*.md, etc.).";
104
113
  const deckFiles = opts.deckTree?.trim()
105
114
  ? `\n\n== deck files ==\n${opts.deckTree.trim()}`
106
115
  : "";
116
+ const deckSource = opts.deckContents?.trim()
117
+ ? `\n\n== deck source (current contents -- no need to read_file these to see what's already there) ==\n${opts.deckContents.trim()}`
118
+ : "";
107
119
  // Section order is stable-to-volatile for prompt caching: rules + deck
108
120
  // identity never change within a serve, the transcript is append-only, and
109
121
  // the churning state (file tree, task board, instruction) rides at the tail
@@ -114,7 +126,7 @@ export function buildRouterPrompt(opts) {
114
126
  ${opts.deckLabel}${quickReference}
115
127
 
116
128
  == conversation so far ==
117
- ${renderTranscript(opts.messages)}${deckFiles}
129
+ ${renderTranscript(opts.messages)}${deckFiles}${deckSource}
118
130
 
119
131
  == background tasks ==
120
132
  ${renderTasks(opts.tasks)}
@@ -139,7 +151,10 @@ export function userTurnInstruction(opts) {
139
151
  .join("\n\n")}`);
140
152
  }
141
153
  if (opts.attachments && opts.attachments.length > 0) {
142
- parts.push(`The user attached image file(s), saved in the deck at: ${opts.attachments.join(", ")}. Open them with your read tool and take them into account; pass the paths along to task agents that need them.`);
154
+ // Wording covers both delivery modes: smith gets the images inline as
155
+ // content blocks in this same message (its read tool rejects binaries),
156
+ // while the CLI backends open the saved files themselves.
157
+ parts.push(`The user attached image file(s), saved in the deck at: ${opts.attachments.join(", ")}. If the images are not already visible in this message, open them with your read tool. Take them into account; pass the paths along to task agents that need them.`);
143
158
  }
144
159
  return parts.join("\n\n");
145
160
  }
@@ -153,12 +168,18 @@ export function buildTaskPrompt(opts) {
153
168
  const siblings = siblingRows
154
169
  ? `\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
170
  : "";
171
+ // Same "say there are no docs" reasoning as buildRouterPrompt's
172
+ // quickReference above -- a task agent wastes turns the same way a router
173
+ // does if it has to discover the absence itself via a failed read/grep.
156
174
  const quickReference = opts.quickReference?.trim()
157
175
  ? `\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
- : "";
176
+ : "\n\nThis deck has no CLAUDE.md / AGENTS.md quick reference. There is no documentation to read -- do not search for one (CLAUDE.md, README, **/*.md, etc.).\n";
159
177
  const layout = opts.deckTree?.trim()
160
178
  ? `\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
179
  : "";
180
+ const deckSource = opts.deckContents?.trim()
181
+ ? `\n\nDeck source (complete current contents of the deck's text files, snapshot at task start -- you do NOT need to read these files before editing them, edit directly; a file marked "not inlined" is either binary/generated or didn't fit the budget, so read it yourself first if you need it):\n\n${opts.deckContents.trim()}\n`
182
+ : "";
162
183
  // Claude-only: collapsing the wrap-up (progress 90 + restart + notes) into
163
184
  // one shell call reliably saves 1-2 serial ~7s turns there. Cursor's
164
185
  // composer sometimes reacts to the same rule with MORE calls, so it stays
@@ -166,10 +187,10 @@ export function buildTaskPrompt(opts) {
166
187
  // (We deliberately do NOT tell agents to overwrite files via `cat > … <<EOF`
167
188
  // -- blind whole-file rewrites made parallel agents clobber each other's
168
189
  // edits. Read-then-edit is slower but safe; that is the right tradeoff.)
169
- const wrapUp = opts.backend === "claude"
190
+ const wrapUp = opts.backend === "claude" || opts.backend === "smith"
170
191
  ? `\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.`
171
192
  : "";
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}
193
+ 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}${deckSource}
173
194
 
174
195
  Your task (id ${opts.taskId}): ${opts.title}
175
196
 
package/dist/agent.d.ts CHANGED
@@ -2,8 +2,9 @@ import * as http from "http";
2
2
  import { Duplex } from "stream";
3
3
  export declare const AGENT_WS_PATH = "/__castle/agent";
4
4
  export declare const AGENT_ATTACHMENT_PREFIX = "/__castle/agent/attachments/";
5
- export type AgentBackend = "cursor" | "claude";
6
- export type ClaudeModel = "sonnet" | "opus" | "fable";
5
+ export declare const AGENT_PLAYTEST_PREFIX = "/__castle/agent/playtest/";
6
+ export type AgentBackend = "cursor" | "claude" | "smith";
7
+ export type ClaudeModel = "sonnet" | "opus" | "fable" | "openrouter";
7
8
  type TaskStatus = "waiting" | "running" | "blocked" | "done" | "failed" | "interrupted";
8
9
  interface TaskRecord {
9
10
  id: string;
@@ -20,12 +21,14 @@ interface TaskRecord {
20
21
  pid?: number;
21
22
  originMessageId?: string;
22
23
  files?: string[];
24
+ playtestFrames?: string[];
23
25
  resultSummary?: string;
24
26
  avatar?: string;
25
27
  phase?: string;
26
28
  acknowledged?: boolean;
27
29
  rejected?: boolean;
28
30
  blockedBy?: string[];
31
+ suspectNoChanges?: boolean;
29
32
  }
30
33
  export interface DepsState {
31
34
  kind: "ready" | "waiting" | "blocked";
@@ -42,5 +45,7 @@ export interface AgentServer {
42
45
  export declare function createAgentServer(opts: {
43
46
  deckDir: string;
44
47
  deckLabel: string;
48
+ port?: number;
49
+ restart?: () => void;
45
50
  }): AgentServer;
46
51
  export {};