castle-web-cli 0.4.33 → 0.4.35
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
CHANGED
|
@@ -28,5 +28,6 @@ export declare function buildTaskPrompt(opts: {
|
|
|
28
28
|
progressPath: string;
|
|
29
29
|
notesPath: string;
|
|
30
30
|
depsSummary?: string;
|
|
31
|
+
backend?: 'cursor' | 'claude';
|
|
31
32
|
}): string;
|
|
32
33
|
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
|
@@ -21,8 +21,10 @@ Hard rules:
|
|
|
21
21
|
short imperative title on the first line
|
|
22
22
|
after: comma-separated titles or ids this task must wait for (optional line)
|
|
23
23
|
supersedes: comma-separated titles or ids this task replaces (optional line)
|
|
24
|
-
Then a
|
|
25
|
-
what to build or fix
|
|
24
|
+
Then a SHORT self-contained prompt -- one tight paragraph (aim under 100
|
|
25
|
+
words): what to build or fix and what "done" looks like. Task agents read
|
|
26
|
+
the deck's own docs for framework/API detail, so never restate recipes,
|
|
27
|
+
file layouts, or implementation steps.
|
|
26
28
|
\`\`\`
|
|
27
29
|
|
|
28
30
|
- Use \`after:\` only when a task truly builds on or would conflict with another (it may reference tasks spawned in this same reply, by title). Independent tasks must NOT wait on each other.
|
|
@@ -34,6 +36,7 @@ comma-separated titles or ids of the finished tasks
|
|
|
34
36
|
\`\`\`
|
|
35
37
|
|
|
36
38
|
- NEVER check a task off on your own judgment -- only a clear user statement that it works (or an explicit ask to clear it) counts. When in doubt, leave the row on the board.
|
|
39
|
+
- The background-tasks list below IS the board the user sees -- every row, with its id. To clear the WHOLE board at once (e.g. "clear all the tasks"), use \`all\` instead of listing ids: a \`castle-done\` with body \`all\` checks off every finished row, and a \`castle-stop\` with body \`all\` stops everything still running. Never claim the board is cleared without actually emitting the fence.
|
|
37
40
|
- To STOP tasks (running or waiting) when the user asks or their work is clearly no longer wanted, include:
|
|
38
41
|
|
|
39
42
|
\`\`\`castle-stop
|
|
@@ -101,6 +104,16 @@ export function buildTaskPrompt(opts) {
|
|
|
101
104
|
const deps = opts.depsSummary
|
|
102
105
|
? `\n\nThis task waited on earlier tasks:\n${opts.depsSummary}\n`
|
|
103
106
|
: '';
|
|
107
|
+
// Claude-only: collapsing the wrap-up (progress 90 + restart + notes) into
|
|
108
|
+
// one shell call reliably saves 1-2 serial ~7s turns there. Cursor's
|
|
109
|
+
// composer sometimes reacts to the same rule with MORE calls, so it stays
|
|
110
|
+
// on the plain instructions. Read-before-overwrite likewise targets
|
|
111
|
+
// claude's Write-tool precondition (a blind Write to an existing file is
|
|
112
|
+
// rejected, forcing a wasted failed-call + Read + retry cycle).
|
|
113
|
+
const wrapUp = opts.backend === 'claude'
|
|
114
|
+
? `\n- NEVER point your Write tool at a file that already exists (scenes/main.scene especially): it rejects files it has not Read, and the failed call + forced Read wastes two turns. Replace existing files with ONE shell command instead (\`cat > scenes/main.scene <<'EOF' ... EOF\`); reserve the Write tool for brand-new files.
|
|
115
|
+
- 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.`
|
|
116
|
+
: '';
|
|
104
117
|
return `You are a background build agent for the Castle deck "${opts.deckLabel}" (current directory). A separate conversation agent dispatched you with one task. Read the deck's CLAUDE.md / AGENTS.md first and follow its workflow (including reloading the served deck after changes, e.g. \`npm run restart\`).
|
|
105
118
|
|
|
106
119
|
Your task (id ${opts.taskId}): ${opts.title}
|
|
@@ -113,7 +126,7 @@ Operating rules:
|
|
|
113
126
|
- The moment implementation is complete and you switch to verifying, write 90 to the progress file -- verification time must not read as stalled progress.
|
|
114
127
|
- Do this one task completely, then stop. Do not expand scope.
|
|
115
128
|
- Update your progress VERY frequently: write a bare integer 0-100 to ${opts.progressPath} (e.g. \`echo 30 > ${opts.progressPath}\`) every time you advance -- at least every 10 points, or every 20 for properly small tasks. Start near 10, write 90 just before wrapping up. Never let it sit stale while you work.
|
|
116
|
-
- Before finishing, write ${opts.notesPath}: a SHORT test guide for the user -- 2-4 brief sentences. Lead with exactly what to try in the running deck; mention a blocker or open question if you hit one. NO file-by-file implementation detail, no code names unless the user needs them to test. The user reads this verbatim when checking your work off
|
|
129
|
+
- Before finishing, write ${opts.notesPath}: a SHORT test guide for the user -- 2-4 brief sentences. Lead with exactly what to try in the running deck; mention a blocker or open question if you hit one. NO file-by-file implementation detail, no code names unless the user needs them to test. The user reads this verbatim when checking your work off.${wrapUp}
|
|
117
130
|
- If you are truly blocked, write the blocker to the notes file and stop rather than guessing wildly.
|
|
118
131
|
- Never touch files under .castle/ other than those two paths.`;
|
|
119
132
|
}
|
package/dist/agent.js
CHANGED
|
@@ -428,6 +428,7 @@ async function runTaskAgentIn(ctx, task) {
|
|
|
428
428
|
progressPath: path.join(relDir, 'progress'),
|
|
429
429
|
notesPath: path.join(relDir, 'notes.md'),
|
|
430
430
|
depsSummary: ctx.depsSummary,
|
|
431
|
+
backend: ctx.backend,
|
|
431
432
|
});
|
|
432
433
|
// No /goal wrapper: it makes a fresh evaluator re-check the WHOLE task
|
|
433
434
|
// prompt (including user-only "done when you reach wave 5"-style play
|
|
@@ -601,16 +602,31 @@ function createTaskStore(opts) {
|
|
|
601
602
|
}
|
|
602
603
|
}
|
|
603
604
|
}
|
|
604
|
-
//
|
|
605
|
+
// True when a fence body is the special token "all" / "*" (clear/stop
|
|
606
|
+
// everything, no per-task enumeration).
|
|
607
|
+
function meansAll(tokens) {
|
|
608
|
+
return tokens.some((t) => t.toLowerCase() === 'all' || t === '*');
|
|
609
|
+
}
|
|
610
|
+
// The router checks finished tasks off by title or id (castle-done fence),
|
|
611
|
+
// or "all" to clear every finished row off the board at once.
|
|
605
612
|
function checkOff(tokens) {
|
|
606
|
-
|
|
613
|
+
const ids = meansAll(tokens)
|
|
614
|
+
? [...tasks.values()].filter((t) => isTerminal(t.status) && !t.acknowledged).map((t) => t.id)
|
|
615
|
+
: resolveDeps(tasks, tokens);
|
|
616
|
+
for (const id of ids)
|
|
607
617
|
acknowledge(id, false);
|
|
608
618
|
}
|
|
609
|
-
// The router stops tasks by title or id (castle-stop fence)
|
|
610
|
-
//
|
|
611
|
-
//
|
|
619
|
+
// The router stops tasks by title or id (castle-stop fence), or "all" to
|
|
620
|
+
// stop everything still active. Waiting tasks are cancelled outright;
|
|
621
|
+
// running ones get their agent process killed and finalize as interrupted
|
|
622
|
+
// via the stopRequested path.
|
|
612
623
|
function stop(tokens) {
|
|
613
|
-
|
|
624
|
+
const ids = meansAll(tokens)
|
|
625
|
+
? [...tasks.values()]
|
|
626
|
+
.filter((t) => t.status === 'running' || t.status === 'waiting')
|
|
627
|
+
.map((t) => t.id)
|
|
628
|
+
: resolveDeps(tasks, tokens);
|
|
629
|
+
for (const id of ids) {
|
|
614
630
|
const task = tasks.get(id);
|
|
615
631
|
if (!task)
|
|
616
632
|
continue;
|
|
@@ -763,7 +779,10 @@ function runRouterTurnIn(ctx, instruction) {
|
|
|
763
779
|
messages: ctx.log.messages
|
|
764
780
|
.filter((m) => m.role !== 'log' && m.id !== message.id && m.status !== 'streaming')
|
|
765
781
|
.map((m) => ({ role: m.role, text: m.text })),
|
|
766
|
-
|
|
782
|
+
// Only the live board (unacknowledged tasks) -- match what the user sees.
|
|
783
|
+
// Showing every already-cleared task confused the router about what was
|
|
784
|
+
// actually on the board to clear / act on.
|
|
785
|
+
tasks: ctx.taskStore.sorted().filter((t) => !t.acknowledged).map(asPromptTask),
|
|
767
786
|
instruction,
|
|
768
787
|
});
|
|
769
788
|
const backend = ctx.backend();
|
|
@@ -127,6 +127,7 @@ function buildFileTree(paths) {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
pruneChildMaps(root);
|
|
130
|
+
sortRootChildren(root);
|
|
130
131
|
return root;
|
|
131
132
|
}
|
|
132
133
|
function pruneChildMaps(node) {
|
|
@@ -134,6 +135,17 @@ function pruneChildMaps(node) {
|
|
|
134
135
|
for (const child of node.children) pruneChildMaps(child);
|
|
135
136
|
delete node.childMap;
|
|
136
137
|
}
|
|
138
|
+
// Top-level folders list in a fixed order: drawings, then scenes, then
|
|
139
|
+
// behaviors, then anything else alphabetically.
|
|
140
|
+
function sortRootChildren(root) {
|
|
141
|
+
const rank = (name) => {
|
|
142
|
+
if (name === 'drawings') return 0;
|
|
143
|
+
if (name === 'scenes') return 1;
|
|
144
|
+
if (name === 'behaviors') return 2;
|
|
145
|
+
return 3;
|
|
146
|
+
};
|
|
147
|
+
root.children.sort((a, b) => rank(a.name) - rank(b.name) || a.name.localeCompare(b.name));
|
|
148
|
+
}
|
|
137
149
|
function collectDirectoryPaths(node) {
|
|
138
150
|
if (node.type !== 'directory') return [];
|
|
139
151
|
return [
|
|
@@ -44,6 +44,15 @@
|
|
|
44
44
|
box-sizing: border-box;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/* Hide scrollbars across the whole editor while keeping scroll functional. */
|
|
48
|
+
:global(*) {
|
|
49
|
+
scrollbar-width: none;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:global(*)::-webkit-scrollbar {
|
|
53
|
+
display: none;
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
:global(html),
|
|
48
57
|
:global(body),
|
|
49
58
|
:global(#root) {
|