@joeyshi12/casper 0.7.3 → 0.8.1

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 (70) hide show
  1. package/README.md +12 -1
  2. package/dist/agents/prompt.md +46 -0
  3. package/dist/mcp.js +412 -0
  4. package/dist/server.js +546 -37
  5. package/dist/web/assets/{abnfDiagram-N423BO3Z-5RMwP9tv.js → abnfDiagram-N423BO3Z-J5DaDTt8.js} +1 -1
  6. package/dist/web/assets/{arc-DEuUsPX5.js → arc-CXWeATz6.js} +1 -1
  7. package/dist/web/assets/{architectureDiagram-T3A2C74G-k4h0zktw.js → architectureDiagram-T3A2C74G-ecXfY9My.js} +1 -1
  8. package/dist/web/assets/{blockDiagram-VBNYF7ZC-qTl9ZS2k.js → blockDiagram-VBNYF7ZC-D5ClKOWV.js} +1 -1
  9. package/dist/web/assets/{c4Diagram-5PPSVZJV-R6o_zt-Q.js → c4Diagram-5PPSVZJV-BxW2-O4j.js} +1 -1
  10. package/dist/web/assets/channel-BXz-ycSk.js +1 -0
  11. package/dist/web/assets/{chunk-2GRJ4B5K-CrJyVfhF.js → chunk-2GRJ4B5K-BBWhz2x0.js} +1 -1
  12. package/dist/web/assets/{chunk-2Q5K7J3B-BvGurkBq.js → chunk-2Q5K7J3B-CfD68qFQ.js} +1 -1
  13. package/dist/web/assets/{chunk-5RXB4S5H-DuE267ol.js → chunk-5RXB4S5H-DaznI8_V.js} +1 -1
  14. package/dist/web/assets/{chunk-5VM5RSS4-DAVY20oh.js → chunk-5VM5RSS4-BYxWXCvw.js} +1 -1
  15. package/dist/web/assets/{chunk-6Q2QTUOP-DvkSg8Wl.js → chunk-6Q2QTUOP-C8zmDMoc.js} +1 -1
  16. package/dist/web/assets/{chunk-GF5L2VYU-B8KQ7FAJ.js → chunk-GF5L2VYU-BXGR_P0f.js} +1 -1
  17. package/dist/web/assets/{chunk-JWPE2WC7-D2J6xFCq.js → chunk-JWPE2WC7-DsHbIvH7.js} +1 -1
  18. package/dist/web/assets/{chunk-KBJHAD2P-BfgGkRN_.js → chunk-KBJHAD2P-B8G6QD3I.js} +1 -1
  19. package/dist/web/assets/{chunk-RYQCIY6F-C0uby4vT.js → chunk-RYQCIY6F-B1rujkgY.js} +1 -1
  20. package/dist/web/assets/{chunk-XXDRQBXY-BAZLB2J1.js → chunk-XXDRQBXY-hjvfwopv.js} +1 -1
  21. package/dist/web/assets/classDiagram-JCYQIIEL-CRBj8GMD.js +1 -0
  22. package/dist/web/assets/classDiagram-v2-OCEON4UE-CRBj8GMD.js +1 -0
  23. package/dist/web/assets/{cose-bilkent-JH36ORCC-8FFjV-r2.js → cose-bilkent-JH36ORCC-D4S1nlYR.js} +1 -1
  24. package/dist/web/assets/{cynefin-VYW2F7L2-CDT1GZi_.js → cynefin-VYW2F7L2-DtN8m_GE.js} +1 -1
  25. package/dist/web/assets/{cynefinDiagram-MW4NZA55-DMs5fXGJ.js → cynefinDiagram-MW4NZA55-Dkk-H0Nw.js} +1 -1
  26. package/dist/web/assets/{dagre-VZM6K2ZE-BUgnrG7T.js → dagre-VZM6K2ZE-BSkCOa-Z.js} +1 -1
  27. package/dist/web/assets/{diagram-7IWD3JNH-DUJeTMJj.js → diagram-7IWD3JNH-Bses1wzI.js} +1 -1
  28. package/dist/web/assets/{diagram-B4RE2ZJO-BsVTn5rU.js → diagram-B4RE2ZJO-BW7sntb1.js} +1 -1
  29. package/dist/web/assets/{diagram-LBJQPF4R--uxNHPeF.js → diagram-LBJQPF4R-DHzgX2HL.js} +1 -1
  30. package/dist/web/assets/{diagram-Q27KOJAE-GYAROo1s.js → diagram-Q27KOJAE-mcXdgoK9.js} +1 -1
  31. package/dist/web/assets/{diagram-UB23O5K3-CpJvxU_K.js → diagram-UB23O5K3-BO5P9LAa.js} +1 -1
  32. package/dist/web/assets/{ebnfDiagram-BXEA7PRR-l3iEqoSP.js → ebnfDiagram-BXEA7PRR-F0zOfsYe.js} +1 -1
  33. package/dist/web/assets/{erDiagram-JOGREHBK-BoFHrs3B.js → erDiagram-JOGREHBK-aOYH_ZnE.js} +1 -1
  34. package/dist/web/assets/{flowDiagram-UKHOOZJN-CXMq4-uD.js → flowDiagram-UKHOOZJN-DmtkMJ60.js} +1 -1
  35. package/dist/web/assets/{ganttDiagram-PKOTCBZU-YMv8lvSL.js → ganttDiagram-PKOTCBZU-BFy8QpeS.js} +1 -1
  36. package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-BgrfufCE.js → gitGraphDiagram-DS77QQ5N-CjbCeQfE.js} +1 -1
  37. package/dist/web/assets/{index-IBYN7wml.js → index-Bbzvwn2R.js} +207 -53
  38. package/dist/web/assets/{index-D7QnyK89.css → index-POlrwDS0.css} +1 -1
  39. package/dist/web/assets/{infoDiagram-6WML65LV-DMpqFKuC.js → infoDiagram-6WML65LV-CGFvyGjJ.js} +1 -1
  40. package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-BKdkzZXu.js → ishikawaDiagram-WSZJBQD7-BbVf1sRd.js} +1 -1
  41. package/dist/web/assets/{journeyDiagram-NVQOT4AX-NPjJ9Fq8.js → journeyDiagram-NVQOT4AX-BNevDB-1.js} +1 -1
  42. package/dist/web/assets/{kanban-definition-27J2QSJJ-MSVfDvXb.js → kanban-definition-27J2QSJJ-BdmYFEey.js} +1 -1
  43. package/dist/web/assets/{linear-Bugxdj26.js → linear-DJt4yr3f.js} +1 -1
  44. package/dist/web/assets/{mermaid.core-BrC5_l7I.js → mermaid.core-D-YupbG3.js} +5 -5
  45. package/dist/web/assets/{mindmap-definition-FAOFIHXS-yB60YQKy.js → mindmap-definition-FAOFIHXS-toscoNiP.js} +1 -1
  46. package/dist/web/assets/{pegDiagram-VL7TDLO6-BfdGiZwB.js → pegDiagram-VL7TDLO6-42qysB7S.js} +1 -1
  47. package/dist/web/assets/{pieDiagram-7S7Q4E2Y-BuJwU-QQ.js → pieDiagram-7S7Q4E2Y-DeC8rSjm.js} +1 -1
  48. package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-DvYy4rS6.js → quadrantDiagram-CIZ2JOQS-BsmBMVxR.js} +1 -1
  49. package/dist/web/assets/{railroadDiagram-AXF67PYL-D0Slzbqv.js → railroadDiagram-AXF67PYL-CUEYoy5N.js} +1 -1
  50. package/dist/web/assets/{requirementDiagram-LRYGKXZP-O1O4JCzu.js → requirementDiagram-LRYGKXZP-QOIVNSdo.js} +1 -1
  51. package/dist/web/assets/{sankeyDiagram-W5VNT64P-WTewsL_K.js → sankeyDiagram-W5VNT64P-BTvBZ4O6.js} +1 -1
  52. package/dist/web/assets/{sequenceDiagram-SI44F4Z6-BUoBi4kR.js → sequenceDiagram-SI44F4Z6-PgaCw-bZ.js} +1 -1
  53. package/dist/web/assets/{sizeCapture-X5ZJPWSS-BV9d3RVn.js → sizeCapture-X5ZJPWSS-xVBXwmWY.js} +1 -1
  54. package/dist/web/assets/{stateDiagram-OKZ733FA-BqdAeMq4.js → stateDiagram-OKZ733FA-BWvlFx8p.js} +1 -1
  55. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CWroHbLn.js +1 -0
  56. package/dist/web/assets/{swimlanes-SLNWSIFB-Dn0Z0yF4.js → swimlanes-SLNWSIFB-B6SREfpi.js} +2 -2
  57. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-B7_kKLQV.js +8 -0
  58. package/dist/web/assets/{timeline-definition-Z64GVDOM-RXIqWmas.js → timeline-definition-Z64GVDOM-pOrViFO-.js} +1 -1
  59. package/dist/web/assets/{vennDiagram-T6HMQDX7-Djv2Kody.js → vennDiagram-T6HMQDX7-DXbf3Z8V.js} +1 -1
  60. package/dist/web/assets/{wardleyDiagram-T6FBY63Y-k8z1iXMI.js → wardleyDiagram-T6FBY63Y-BulJVCrV.js} +1 -1
  61. package/dist/web/assets/{xychartDiagram-ELKLHX3M-DBRaiGnz.js → xychartDiagram-ELKLHX3M-MI1zCRjW.js} +1 -1
  62. package/dist/web/index.html +2 -2
  63. package/dist/web/sw.js +1 -1
  64. package/package.json +1 -1
  65. package/dist/agents/casper.json +0 -16
  66. package/dist/web/assets/channel-BQC-40u0.js +0 -1
  67. package/dist/web/assets/classDiagram-JCYQIIEL-9L45zm4i.js +0 -1
  68. package/dist/web/assets/classDiagram-v2-OCEON4UE-9L45zm4i.js +0 -1
  69. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-DQeB4eKD.js +0 -1
  70. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-Dl6pHbTK.js +0 -8
package/dist/server.js CHANGED
@@ -74,26 +74,62 @@ var init_settings = __esm({
74
74
  // server/src/cli/agentFile.ts
75
75
  var agentFile_exports = {};
76
76
  __export(agentFile_exports, {
77
+ agentConfig: () => agentConfig,
78
+ agentPrompt: () => agentPrompt,
77
79
  installAgentFile: () => installAgentFile
78
80
  });
79
81
  import crypto2 from "node:crypto";
80
82
  import fs3 from "node:fs";
81
83
  import path4 from "node:path";
82
84
  import url from "node:url";
83
- function sourceFile() {
85
+ function agentPrompt() {
84
86
  const here = path4.dirname(url.fileURLToPath(import.meta.url));
85
- const packaged = path4.resolve(here, "agents/casper.json");
86
- const workspace = path4.resolve(here, "../../../assets/agents/casper.json");
87
- return fs3.existsSync(packaged) ? packaged : workspace;
87
+ const candidates = [
88
+ path4.resolve(here, "agents/prompt.md"),
89
+ // bundled: beside server.js
90
+ path4.resolve(here, "../../../assets/agents/prompt.md")
91
+ // from source
92
+ ];
93
+ const found = candidates.find((c) => fs3.existsSync(c));
94
+ return found ? fs3.readFileSync(found, "utf8").trimEnd() : null;
95
+ }
96
+ function mcpServerPath() {
97
+ const here = path4.dirname(url.fileURLToPath(import.meta.url));
98
+ const candidates = [
99
+ path4.resolve(here, "mcp.js"),
100
+ // bundled: beside server.js
101
+ path4.resolve(here, "../../dist/mcp.js")
102
+ // from source, if built
103
+ ];
104
+ return candidates.find((c) => fs3.existsSync(c)) ?? null;
105
+ }
106
+ function agentConfig(prompt, mcp) {
107
+ return {
108
+ name: "casper",
109
+ description: "Casper \u2014 a coding agent for developing from the chat web interface.",
110
+ prompt,
111
+ mcpServers: {
112
+ casper: mcp ? { command: process.execPath, args: [mcp], env: {}, timeout: 1e4 } : { command: "casper", args: ["mcp"], env: {}, timeout: 1e4 }
113
+ },
114
+ tools: ["*"],
115
+ toolAliases: {},
116
+ allowedTools: [],
117
+ resources: [],
118
+ hooks: {},
119
+ toolsSettings: {},
120
+ includeMcpJson: true,
121
+ model: null
122
+ };
88
123
  }
89
- function sha256(text) {
90
- return crypto2.createHash("sha256").update(text).digest("hex");
124
+ function sha256(text2) {
125
+ return crypto2.createHash("sha256").update(text2).digest("hex");
91
126
  }
92
127
  function installAgentFile(home2, dataDir) {
93
- const src = sourceFile();
94
128
  const target = path4.join(home2, ".kiro", "agents", "casper.json");
95
- if (!fs3.existsSync(src)) return { action: "no-source", target };
96
- const desired = fs3.readFileSync(src, "utf8");
129
+ const prompt = agentPrompt();
130
+ if (prompt === null) return { action: "no-source", target };
131
+ const desired = `${JSON.stringify(agentConfig(prompt, mcpServerPath()), null, 2)}
132
+ `;
97
133
  const stampFile = path4.join(dataDir, "agent-stamp");
98
134
  const stamp = fs3.existsSync(stampFile) ? fs3.readFileSync(stampFile, "utf8").trim() : "";
99
135
  const write = () => {
@@ -611,20 +647,20 @@ var init_acp = __esm({
611
647
  });
612
648
 
613
649
  // shared/dist/attachments.js
614
- function attachmentPaths(text) {
615
- const line = text.split("\n").find((l) => l.startsWith(ATTACHMENTS_PREFIX));
650
+ function attachmentPaths(text2) {
651
+ const line = text2.split("\n").find((l) => l.startsWith(ATTACHMENTS_PREFIX));
616
652
  if (!line)
617
653
  return [];
618
654
  return line.slice(ATTACHMENTS_PREFIX.length).split(", ").map((s) => s.trim()).filter(Boolean);
619
655
  }
620
- function imageAttachmentPaths(text) {
621
- return attachmentPaths(text).filter((p) => {
656
+ function imageAttachmentPaths(text2) {
657
+ return attachmentPaths(text2).filter((p) => {
622
658
  const lower = p.toLowerCase();
623
659
  return IMAGE_EXTS.some((ext) => lower.endsWith(ext));
624
660
  });
625
661
  }
626
- function stripAttachmentsLine(text) {
627
- return text.split("\n").filter((l) => !l.startsWith(ATTACHMENTS_PREFIX)).join("\n").trim();
662
+ function stripAttachmentsLine(text2) {
663
+ return text2.split("\n").filter((l) => !l.startsWith(ATTACHMENTS_PREFIX)).join("\n").trim();
628
664
  }
629
665
  var ATTACHMENTS_PREFIX, IMAGE_EXTS;
630
666
  var init_attachments = __esm({
@@ -1304,17 +1340,17 @@ async function hydrateTranscript(sessionId) {
1304
1340
  const pushMsg = (msg) => items.push({ type: "message", message: msg });
1305
1341
  if (entry.kind === "Prompt") {
1306
1342
  const raw2 = textOf("text");
1307
- const text = stripAttachmentsLine(raw2);
1343
+ const text2 = stripAttachmentsLine(raw2);
1308
1344
  const imagePaths = imageAttachmentPaths(raw2);
1309
- if (text.trim() || imagePaths.length)
1310
- pushMsg({ id: `u-${baseId}`, role: "user", text, timestamp: ts, imagePaths });
1345
+ if (text2.trim() || imagePaths.length)
1346
+ pushMsg({ id: `u-${baseId}`, role: "user", text: text2, timestamp: ts, imagePaths });
1311
1347
  } else if (entry.kind === "AssistantMessage") {
1312
1348
  const thinking = textOf("thinking");
1313
1349
  if (thinking.trim())
1314
1350
  pushMsg({ id: `t-${baseId}`, role: "thinking", text: thinking, timestamp: ts });
1315
- const text = textOf("text");
1316
- if (text.trim())
1317
- pushMsg({ id: `a-${baseId}`, role: "assistant", text, timestamp: ts });
1351
+ const text2 = textOf("text");
1352
+ if (text2.trim())
1353
+ pushMsg({ id: `a-${baseId}`, role: "assistant", text: text2, timestamp: ts });
1318
1354
  for (const c of content) {
1319
1355
  const d = toolUseData(c);
1320
1356
  if (!d) continue;
@@ -2107,8 +2143,8 @@ async function listAgents() {
2107
2143
  cwd: config.defaultCwd,
2108
2144
  maxBuffer: 2 * 1024 * 1024
2109
2145
  });
2110
- const text = (stderr || "") + "\n" + (stdout || "");
2111
- for (const rawLine of text.split("\n")) {
2146
+ const text2 = (stderr || "") + "\n" + (stdout || "");
2147
+ for (const rawLine of text2.split("\n")) {
2112
2148
  const line = rawLine.replace(ANSI, "");
2113
2149
  const m = /^\s{0,2}(\*\s)?([A-Za-z0-9_-]+)\s{2,}(Global|Workspace|Local)\b/.exec(
2114
2150
  line
@@ -3271,7 +3307,8 @@ var init_server = __esm({
3271
3307
  // server/src/cli/doctor.ts
3272
3308
  var doctor_exports = {};
3273
3309
  __export(doctor_exports, {
3274
- doctor: () => doctor
3310
+ doctor: () => doctor,
3311
+ mcpWiring: () => mcpWiring
3275
3312
  });
3276
3313
  import { execFileSync as execFileSync3 } from "node:child_process";
3277
3314
  import fs14 from "node:fs";
@@ -3289,6 +3326,28 @@ function which(bin) {
3289
3326
  }
3290
3327
  return void 0;
3291
3328
  }
3329
+ function mcpWiring(agentJson, resolve) {
3330
+ let servers;
3331
+ try {
3332
+ servers = JSON.parse(agentJson).mcpServers;
3333
+ } catch {
3334
+ return { ok: false, detail: "the agent file is not valid JSON" };
3335
+ }
3336
+ const server = servers?.casper;
3337
+ if (!server) {
3338
+ return { ok: false, detail: "not declared in the agent file, so widgets are unavailable" };
3339
+ }
3340
+ const command = typeof server.command === "string" ? server.command : "";
3341
+ const args = Array.isArray(server.args) ? server.args.filter((a) => typeof a === "string") : [];
3342
+ if (!resolve(command)) {
3343
+ return { ok: false, detail: `${command || "(no command)"} not found - widgets are unavailable` };
3344
+ }
3345
+ const script = args.find((a) => a.endsWith(".js"));
3346
+ if (script && !fs14.existsSync(script)) {
3347
+ return { ok: false, detail: `${script} is gone - delete the agent file and restart to rewrite it` };
3348
+ }
3349
+ return { ok: true, detail: script ?? `${command} ${args.join(" ")}` };
3350
+ }
3292
3351
  function checks() {
3293
3352
  const out = [];
3294
3353
  out.push({
@@ -3357,11 +3416,16 @@ function checks() {
3357
3416
  }
3358
3417
  );
3359
3418
  const agent = path15.join(config.kiroSessionsDir, "..", "..", "agents", "casper.json");
3419
+ const hasAgent = fs14.existsSync(agent);
3360
3420
  out.push({
3361
- ok: fs14.existsSync(agent),
3421
+ ok: hasAgent,
3362
3422
  name: "casper agent",
3363
- detail: fs14.existsSync(agent) ? agent : `${agent} missing - it is installed on first run`
3423
+ detail: hasAgent ? agent : `${agent} missing - it is installed on first run`
3364
3424
  });
3425
+ if (hasAgent) {
3426
+ const wiring = mcpWiring(fs14.readFileSync(agent, "utf8"), which);
3427
+ out.push({ ok: wiring.ok, name: "casper mcp", detail: wiring.detail });
3428
+ }
3365
3429
  return out;
3366
3430
  }
3367
3431
  function doctor() {
@@ -3388,6 +3452,445 @@ var init_doctor = __esm({
3388
3452
  }
3389
3453
  });
3390
3454
 
3455
+ // server/src/mcp/guidelines.ts
3456
+ function getGuidelines(modules) {
3457
+ const parts = [CORE];
3458
+ const seen = /* @__PURE__ */ new Set();
3459
+ for (const mod of modules) {
3460
+ const sections = MODULE_SECTIONS[mod];
3461
+ if (!sections) continue;
3462
+ for (const section of sections) {
3463
+ if (seen.has(section)) continue;
3464
+ seen.add(section);
3465
+ parts.push(section);
3466
+ }
3467
+ }
3468
+ return parts.join("\n\n");
3469
+ }
3470
+ var MODULES, CORE, COLOR, UI, CHARTS, SVG, DIAGRAMS, ART, MODULE_SECTIONS;
3471
+ var init_guidelines = __esm({
3472
+ "server/src/mcp/guidelines.ts"() {
3473
+ "use strict";
3474
+ MODULES = ["interactive", "chart", "diagram", "art"];
3475
+ CORE = `# Widgets
3476
+
3477
+ A widget is a self-contained HTML fragment rendered inline in the conversation. No
3478
+ doctype, html, head or body tag - just content. It is not a file and not a
3479
+ deliverable; it is part of what you are saying. Reach for one when a picture
3480
+ answers better than a paragraph.
3481
+
3482
+ ## Structure, in this order
3483
+
3484
+ 1. A short \`<style>\` block.
3485
+ 2. The content markup.
3486
+ 3. A single \`<script>\` at the end.
3487
+
3488
+ Order matters. Content renders as it arrives, and scripts run only once the message
3489
+ is complete, so a widget whose script sits at the top looks inert until the end.
3490
+
3491
+ ## Hard rules
3492
+
3493
+ - No gradients, drop shadows or blur. They flash while content is patched in.
3494
+ - No HTML comments. They cost tokens and buy nothing.
3495
+ - Two font weights only: 400 and 500. Never 600 or 700.
3496
+ - Sentence case for every label, heading and button. Never Title Case, never ALL CAPS.
3497
+ - Never hardcode a colour that has a variable below.
3498
+ - Size text in px, 11 to 15 for body and labels, up to 18 for a single heading.
3499
+ - Keep the whole widget under 600px tall unless the content genuinely needs more.
3500
+
3501
+ ## The sandbox
3502
+
3503
+ The frame has an opaque origin. localStorage, sessionStorage, cookies and requests
3504
+ to Casper all throw or fail. Hold state in a variable for the life of the page.
3505
+
3506
+ Libraries load from these hosts only: cdnjs.cloudflare.com, cdn.jsdelivr.net,
3507
+ unpkg.com, esm.sh. Anything else is refused by the content security policy, as is
3508
+ eval and new Function, so pick libraries that don't rely on them.
3509
+
3510
+ ## Theme
3511
+
3512
+ The background is transparent and the app's own variables are set. Use them:
3513
+
3514
+ --color-text-primary, --color-text-secondary
3515
+ --color-background-primary, --color-background-secondary
3516
+ --color-border
3517
+ --color-accent, --color-accent-alt
3518
+ --color-teal, --color-green, --color-yellow, --color-orange, --color-red
3519
+ --font-body, --font-mono
3520
+
3521
+ Read them in script with getComputedStyle(document.documentElement).getPropertyValue.
3522
+
3523
+ ## Talking back
3524
+
3525
+ casper.sendPrompt(text) sends a message as if the user typed it, indistinguishable
3526
+ in the transcript from one they wrote. Most widgets need no button at all. Add one
3527
+ only when the obvious next step is a question they would otherwise type out by hand,
3528
+ and never as decoration or to invite engagement. One at most.`;
3529
+ COLOR = `## Colour
3530
+
3531
+ Six accents are available: --color-accent (purple), --color-accent-alt (pink),
3532
+ --color-teal, --color-green, --color-yellow, --color-orange, --color-red.
3533
+
3534
+ Colour encodes meaning, never sequence. If two series differ only by being
3535
+ different series, use one accent at two opacities rather than two hues. Two or
3536
+ three hues per widget is the ceiling.
3537
+
3538
+ Semantics people already expect: green for success or growth, red for error or
3539
+ loss, orange for warning or pending, teal for neutral information, purple for the
3540
+ primary subject.
3541
+
3542
+ For text on a filled background, use --color-background-primary as the text colour
3543
+ rather than black. For a tint, use the accent at 12 to 20 percent opacity with the
3544
+ accent itself as the border.`;
3545
+ UI = `## Components
3546
+
3547
+ Card: background --color-background-secondary, 1px --color-border, radius 10px,
3548
+ padding 12px 14px. No shadow.
3549
+
3550
+ Metric: label 11px --color-text-secondary above a 20px 500-weight value. Group
3551
+ metrics in a grid with 12px gaps, never a table.
3552
+
3553
+ Button: 12px text, background --color-background-secondary, 1px --color-border,
3554
+ radius 6px, padding 5px 10px. On hover, move the border to --color-accent. Never
3555
+ paint a button in an accent colour unless it is the single primary action.
3556
+
3557
+ Input and range: set accent-color to var(--color-accent) and let the browser style
3558
+ the rest.
3559
+
3560
+ Table: only for genuinely tabular data. 1px --color-border between rows, no
3561
+ vertical rules, 12px text, numbers right-aligned in --font-mono.
3562
+
3563
+ Rows of key and value: a flex row with the label in --color-text-secondary and the
3564
+ value in --font-mono, separated by a border-bottom on all but the last.`;
3565
+ CHARTS = `## Charts
3566
+
3567
+ Chart.js from cdn.jsdelivr.net is the default choice. Load it with a script tag
3568
+ before your own script; it will have finished loading by the time your script runs,
3569
+ so there's no need to poll for it.
3570
+
3571
+ Wrap every canvas in a div with position relative and an explicit height, and set
3572
+ maintainAspectRatio to false. Without both, the canvas grows without bound.
3573
+
3574
+ Turn the built-in legend off (plugins.legend.display false) and write the legend as
3575
+ HTML if it's needed - the built-in one ignores the theme.
3576
+
3577
+ Grid lines in --color-border at low opacity, ticks in --color-text-secondary, 11px.
3578
+ Datasets take an accent colour with borderWidth 2 and no point markers unless
3579
+ individual readings matter.
3580
+
3581
+ Format numbers so the sign leads: -$5M, never $-5M. Abbreviate above four digits
3582
+ (12.4k, 3.1M). Percentages get one decimal at most.
3583
+
3584
+ Pick the form from the question: trend over time is a line, comparison across
3585
+ categories is a horizontal bar when labels are words, composition is a stacked bar
3586
+ rather than a pie unless there are three slices or fewer.`;
3587
+ SVG = `## SVG
3588
+
3589
+ Set viewBox and leave width and height off, so the drawing scales to the frame.
3590
+ Compute the viewBox from the content bounds and add 8 units of padding on every
3591
+ side; anything drawn outside is invisible and reviewers will not see the mistake.
3592
+
3593
+ Text width is the usual cause of overflow. At font-size 12 with the body font,
3594
+ budget 6.2px per character for lowercase and 7.4px for uppercase, then size boxes
3595
+ from the longest label rather than by eye.
3596
+
3597
+ Connector paths need fill="none" explicitly. SVG fills paths black by default, so a
3598
+ curve without it renders as a filled blob.
3599
+
3600
+ Define arrowheads once in defs with a marker whose fill is context-stroke, so the
3601
+ head inherits the line colour instead of needing one marker per colour.
3602
+
3603
+ Stroke widths: 1.5 for connectors, 2 for emphasis, 1 for gridlines. Never below 1.`;
3604
+ DIAGRAMS = `## Diagrams
3605
+
3606
+ Two mistakes account for most bad diagrams. First, arrows that cross boxes they
3607
+ have nothing to do with: trace every arrow's straight path and if it passes through
3608
+ a box, route it around or move the box. Second, boxes sized by guess rather than by
3609
+ the longest label they contain.
3610
+
3611
+ Route on the verb, not the noun. "How does a request flow" wants a sequence, "what
3612
+ are the parts" wants a structural diagram, "why is it slow" wants an annotated
3613
+ before and after.
3614
+
3615
+ Budgets: at most four boxes across one tier, at most five words in a label, at most
3616
+ three tiers deep. Beyond that, split into two diagrams or drop detail.
3617
+
3618
+ Label every arrow that isn't obvious, in 10px --color-text-secondary. An unlabelled
3619
+ arrow asserts a relationship without saying which.
3620
+
3621
+ Lay out left to right for flow and top to bottom for hierarchy, and keep a single
3622
+ consistent gap between tiers.`;
3623
+ ART = `## Illustration
3624
+
3625
+ Build from geometry: circles, rounded rectangles, and paths with two or three
3626
+ control points. Detail beyond that reads as noise at conversation size.
3627
+
3628
+ Use flat fills from the palette with no more than three colours plus a background.
3629
+ Depth comes from overlap and size, never from gradients or shadows.
3630
+
3631
+ Animate with CSS on transform and opacity only, 0.3s or slower, and only where the
3632
+ motion carries meaning. Anything faster or busier is a distraction in a chat.
3633
+
3634
+ For anything representational, prefer a diagram with labels. An unlabelled drawing
3635
+ of a concept is decoration.`;
3636
+ MODULE_SECTIONS = {
3637
+ interactive: [UI, COLOR],
3638
+ chart: [UI, COLOR, CHARTS],
3639
+ diagram: [COLOR, SVG, DIAGRAMS],
3640
+ art: [COLOR, SVG, ART]
3641
+ };
3642
+ }
3643
+ });
3644
+
3645
+ // server/src/mcp/choice.ts
3646
+ function validateChoice(data) {
3647
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
3648
+ return "arguments must be an object.";
3649
+ }
3650
+ const d = data;
3651
+ if (typeof d.question !== "string" || !d.question.trim()) {
3652
+ return "question is required: what you are asking the user to decide.";
3653
+ }
3654
+ if (!Array.isArray(d.options) || d.options.length < 2) {
3655
+ return "at least two options are needed.";
3656
+ }
3657
+ if (d.options.length > MAX_OPTIONS) {
3658
+ return `at most ${MAX_OPTIONS} options; ${d.options.length} is too many to tap through.`;
3659
+ }
3660
+ for (const [i, raw] of d.options.entries()) {
3661
+ if (typeof raw !== "object" || raw === null) return `options[${i}] must be an object.`;
3662
+ const o = raw;
3663
+ if (typeof o.label !== "string" || !o.label.trim()) {
3664
+ return `options[${i}].label is required and must be a short phrase.`;
3665
+ }
3666
+ if (o.prompt !== void 0 && typeof o.prompt !== "string") {
3667
+ return `options[${i}].prompt must be a string when given.`;
3668
+ }
3669
+ if (o.detail !== void 0 && typeof o.detail !== "string") {
3670
+ return `options[${i}].detail must be a string when given.`;
3671
+ }
3672
+ }
3673
+ return null;
3674
+ }
3675
+ var MAX_OPTIONS;
3676
+ var init_choice = __esm({
3677
+ "server/src/mcp/choice.ts"() {
3678
+ "use strict";
3679
+ MAX_OPTIONS = 6;
3680
+ }
3681
+ });
3682
+
3683
+ // server/src/mcp/protocol.ts
3684
+ function ok(id, result) {
3685
+ return { jsonrpc: "2.0", id, result };
3686
+ }
3687
+ function text(body, isError = false) {
3688
+ return { content: [{ type: "text", text: body }], isError };
3689
+ }
3690
+ function callError(id, message) {
3691
+ return ok(id, text(message, true));
3692
+ }
3693
+ function asStringArray(value) {
3694
+ return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
3695
+ }
3696
+ function handleReadMe(id, args) {
3697
+ const modules = asStringArray(args.modules);
3698
+ const known = modules.filter((m) => MODULES.includes(m));
3699
+ if (known.length === 0) {
3700
+ return callError(id, `Name at least one module: ${MODULES.join(", ")}.`);
3701
+ }
3702
+ return ok(id, text(getGuidelines(known)));
3703
+ }
3704
+ function handleShowWidget(id, args) {
3705
+ if (args.i_have_seen_read_me !== true) {
3706
+ return callError(id, "Call read_me first, then set i_have_seen_read_me to true.");
3707
+ }
3708
+ const title = typeof args.title === "string" ? args.title.trim() : "";
3709
+ if (!title) return callError(id, "title is required: a short snake_case identifier.");
3710
+ const code = typeof args.widget_code === "string" ? args.widget_code : "";
3711
+ if (!code.trim()) return callError(id, "widget_code is required.");
3712
+ if (code.length > MAX_WIDGET_CHARS) {
3713
+ return callError(
3714
+ id,
3715
+ `widget_code is ${Math.round(code.length / 1024)} KB, over the ${MAX_WIDGET_CHARS / 1024} KB limit. Simplify it.`
3716
+ );
3717
+ }
3718
+ if (/<(!doctype|html|head|body)\b/i.test(code)) {
3719
+ return callError(id, "Pass a fragment, not a document: drop the doctype, html and body tags.");
3720
+ }
3721
+ return ok(id, text(`Widget "${title}" is on screen. Don't repeat its content in prose.`));
3722
+ }
3723
+ function handleChoice(id, args) {
3724
+ const problem = validateChoice(args);
3725
+ if (problem) return callError(id, problem);
3726
+ return ok(id, text("Options are on screen. Wait for the user to pick one."));
3727
+ }
3728
+ function handleMessage(req, version) {
3729
+ const id = req.id ?? null;
3730
+ const method = req.method ?? "";
3731
+ const params = req.params ?? {};
3732
+ if (method.startsWith("notifications/")) return null;
3733
+ switch (method) {
3734
+ case "initialize": {
3735
+ const asked = typeof params.protocolVersion === "string" ? params.protocolVersion : null;
3736
+ return ok(id, {
3737
+ protocolVersion: asked ?? DEFAULT_PROTOCOL_VERSION,
3738
+ capabilities: { tools: { listChanged: false } },
3739
+ serverInfo: { name: SERVER_NAME, version }
3740
+ });
3741
+ }
3742
+ case "ping":
3743
+ return ok(id, {});
3744
+ case "tools/list":
3745
+ return ok(id, { tools: TOOLS });
3746
+ case "tools/call": {
3747
+ const name = typeof params.name === "string" ? params.name : "";
3748
+ const args = params.arguments ?? {};
3749
+ if (name === "read_me") return handleReadMe(id, args);
3750
+ if (name === "show_widget") return handleShowWidget(id, args);
3751
+ if (name === "show_choice") return handleChoice(id, args);
3752
+ return { jsonrpc: "2.0", id, error: { code: -32602, message: `Unknown tool: ${name}` } };
3753
+ }
3754
+ default:
3755
+ if (id === null) return null;
3756
+ return { jsonrpc: "2.0", id, error: { code: -32601, message: `Unknown method: ${method}` } };
3757
+ }
3758
+ }
3759
+ var DEFAULT_PROTOCOL_VERSION, SERVER_NAME, MAX_WIDGET_CHARS, TOOLS;
3760
+ var init_protocol = __esm({
3761
+ "server/src/mcp/protocol.ts"() {
3762
+ "use strict";
3763
+ init_guidelines();
3764
+ init_choice();
3765
+ DEFAULT_PROTOCOL_VERSION = "2025-06-18";
3766
+ SERVER_NAME = "casper-generative-ui";
3767
+ MAX_WIDGET_CHARS = 256 * 1024;
3768
+ TOOLS = [
3769
+ {
3770
+ name: "read_me",
3771
+ title: "Widget design guidelines",
3772
+ description: "Read the widget design guidelines. Call this once, before your first show_widget call, with the modules relevant to what you are about to build. Do not mention this call to the user.",
3773
+ inputSchema: {
3774
+ type: "object",
3775
+ properties: {
3776
+ modules: {
3777
+ type: "array",
3778
+ items: { type: "string", enum: [...MODULES] },
3779
+ minItems: 1,
3780
+ description: "interactive for controls, sketches and simulations, chart for data, diagram for structure and flow, art for illustration."
3781
+ }
3782
+ },
3783
+ required: ["modules"]
3784
+ }
3785
+ },
3786
+ {
3787
+ name: "show_widget",
3788
+ title: "Show widget",
3789
+ description: "Render an interactive widget inline in the conversation. Pass an HTML fragment: no doctype, html or body tag. Style first, then content, then a single script last. Use this instead of describing something visual in prose.",
3790
+ inputSchema: {
3791
+ type: "object",
3792
+ properties: {
3793
+ i_have_seen_read_me: {
3794
+ type: "boolean",
3795
+ description: "True only if you have already called read_me in this conversation."
3796
+ },
3797
+ title: {
3798
+ type: "string",
3799
+ description: "Short snake_case identifier, e.g. compound_interest."
3800
+ },
3801
+ widget_code: {
3802
+ type: "string",
3803
+ description: "The HTML fragment to render."
3804
+ }
3805
+ },
3806
+ required: ["i_have_seen_read_me", "title", "widget_code"]
3807
+ }
3808
+ },
3809
+ {
3810
+ name: "show_choice",
3811
+ title: "Ask the user to choose",
3812
+ description: "Ask the user to pick from a few options, which they tap instead of typing. Use it wherever you would otherwise end a message asking which of several ways to proceed.",
3813
+ inputSchema: {
3814
+ type: "object",
3815
+ properties: {
3816
+ question: { type: "string", description: "What they are deciding." },
3817
+ options: {
3818
+ type: "array",
3819
+ minItems: 2,
3820
+ maxItems: MAX_OPTIONS,
3821
+ items: {
3822
+ type: "object",
3823
+ properties: {
3824
+ label: { type: "string", description: "Short, tappable phrase." },
3825
+ detail: { type: "string", description: "One line of context." },
3826
+ prompt: {
3827
+ type: "string",
3828
+ description: "Message sent when tapped. Defaults to the label."
3829
+ }
3830
+ },
3831
+ required: ["label"]
3832
+ }
3833
+ }
3834
+ },
3835
+ required: ["question", "options"]
3836
+ }
3837
+ }
3838
+ ];
3839
+ }
3840
+ });
3841
+
3842
+ // server/src/mcp/server.ts
3843
+ var server_exports2 = {};
3844
+ __export(server_exports2, {
3845
+ runMcpServer: () => runMcpServer
3846
+ });
3847
+ import { createInterface } from "node:readline";
3848
+ function runMcpServer() {
3849
+ const rl = createInterface({ input: process.stdin });
3850
+ rl.on("line", (line) => {
3851
+ const trimmed = line.trim();
3852
+ if (!trimmed) return;
3853
+ let req;
3854
+ try {
3855
+ req = JSON.parse(trimmed);
3856
+ } catch {
3857
+ process.stdout.write(
3858
+ JSON.stringify({
3859
+ jsonrpc: "2.0",
3860
+ id: null,
3861
+ error: { code: -32700, message: "Parse error" }
3862
+ }) + "\n"
3863
+ );
3864
+ return;
3865
+ }
3866
+ try {
3867
+ const res = handleMessage(req, VERSION);
3868
+ if (res) process.stdout.write(JSON.stringify(res) + "\n");
3869
+ } catch (err) {
3870
+ process.stderr.write(`casper-mcp: ${err.message}
3871
+ `);
3872
+ if (req.id !== void 0 && req.id !== null) {
3873
+ process.stdout.write(
3874
+ JSON.stringify({
3875
+ jsonrpc: "2.0",
3876
+ id: req.id,
3877
+ error: { code: -32603, message: "Internal error" }
3878
+ }) + "\n"
3879
+ );
3880
+ }
3881
+ }
3882
+ });
3883
+ rl.on("close", () => process.exit(0));
3884
+ }
3885
+ var VERSION;
3886
+ var init_server2 = __esm({
3887
+ "server/src/mcp/server.ts"() {
3888
+ "use strict";
3889
+ init_protocol();
3890
+ VERSION = true ? "0.8.1" : "dev";
3891
+ }
3892
+ });
3893
+
3391
3894
  // server/src/cli/program.ts
3392
3895
  import fs15 from "node:fs";
3393
3896
  import os4 from "node:os";
@@ -6523,9 +7026,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
6523
7026
  helpWidth: context.helpWidth,
6524
7027
  outputHasColors: context.hasColors
6525
7028
  });
6526
- const text = helper.formatHelp(this, helper);
6527
- if (context.hasColors) return text;
6528
- return this._outputConfiguration.stripColor(text);
7029
+ const text2 = helper.formatHelp(this, helper);
7030
+ if (context.hasColors) return text2;
7031
+ return this._outputConfiguration.stripColor(text2);
6529
7032
  }
6530
7033
  /**
6531
7034
  * @typedef HelpContext
@@ -6685,7 +7188,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
6685
7188
  * @param {(string | Function)} text - string to add, or a function returning a string
6686
7189
  * @return {Command} `this` command for chaining
6687
7190
  */
6688
- addHelpText(position, text) {
7191
+ addHelpText(position, text2) {
6689
7192
  const allowedValues = ["beforeAll", "before", "after", "afterAll"];
6690
7193
  if (!allowedValues.includes(position)) {
6691
7194
  throw new Error(`Unexpected value for position to addHelpText.
@@ -6694,10 +7197,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
6694
7197
  const helpEvent = `${position}Help`;
6695
7198
  this.on(helpEvent, (context) => {
6696
7199
  let helpStr;
6697
- if (typeof text === "function") {
6698
- helpStr = text({ error: context.error, command: context.command });
7200
+ if (typeof text2 === "function") {
7201
+ helpStr = text2({ error: context.error, command: context.command });
6699
7202
  } else {
6700
- helpStr = text;
7203
+ helpStr = text2;
6701
7204
  }
6702
7205
  if (helpStr) {
6703
7206
  context.write(`${helpStr}
@@ -6764,7 +7267,7 @@ var program = new Command();
6764
7267
  // server/src/cli/program.ts
6765
7268
  init_paths();
6766
7269
  init_settings();
6767
- var VERSION = true ? "0.7.3" : "dev";
7270
+ var VERSION2 = true ? "0.8.1" : "dev";
6768
7271
  async function bootstrap() {
6769
7272
  const file = configFilePath();
6770
7273
  const settings = readSettings(file);
@@ -6782,8 +7285,11 @@ async function bootstrap() {
6782
7285
  process.stdout.write(`casper agent ${agent.action}: ${agent.target}
6783
7286
  `);
6784
7287
  } else if (agent.action === "kept-yours") {
6785
- process.stdout.write(`keeping your edited agent file at ${agent.target}
6786
- `);
7288
+ process.stdout.write(
7289
+ `keeping your edited agent file at ${agent.target}
7290
+ Prompt and tool updates are being skipped. Delete it to take them.
7291
+ `
7292
+ );
6787
7293
  }
6788
7294
  }
6789
7295
  function printToken() {
@@ -6825,7 +7331,7 @@ async function resetToken(value) {
6825
7331
  }
6826
7332
  function buildProgram() {
6827
7333
  const program2 = new Command();
6828
- program2.name("casper").description("Web client for kiro-cli over the Agent Client Protocol").version(VERSION, "-v, --version").showHelpAfterError().allowUnknownOption(false).allowExcessArguments(false).addHelpText("after", `
7334
+ program2.name("casper").description("Web client for kiro-cli over the Agent Client Protocol").version(VERSION2, "-v, --version").showHelpAfterError().allowUnknownOption(false).allowExcessArguments(false).addHelpText("after", `
6829
7335
  Settings live in ${configFilePath()}.
6830
7336
  Update with: npm install -g @joeyshi12/casper`);
6831
7337
  program2.command("start").description("run the server in the foreground").allowUnknownOption(false).allowExcessArguments(false).action(async () => {
@@ -6840,6 +7346,9 @@ Update with: npm install -g @joeyshi12/casper`);
6840
7346
  program2.command("doctor").description("check kiro-cli, settings, data directory and web app").allowUnknownOption(false).allowExcessArguments(false).action(async () => {
6841
7347
  process.exitCode = (await Promise.resolve().then(() => (init_doctor(), doctor_exports))).doctor();
6842
7348
  });
7349
+ program2.command("mcp").description("run the generative-UI MCP server on stdio (kiro spawns this itself)").allowUnknownOption(false).allowExcessArguments(false).action(async () => {
7350
+ (await Promise.resolve().then(() => (init_server2(), server_exports2))).runMcpServer();
7351
+ });
6843
7352
  const service = program2.command("service").description("manage the systemd user service");
6844
7353
  const svcCommand = (name, describe, run) => service.command(name).description(describe).allowUnknownOption(false).allowExcessArguments(false).action(async () => {
6845
7354
  process.exitCode = run(await Promise.resolve().then(() => (init_service(), service_exports)));