kelra 0.1.1 → 0.1.2

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/AGENT_GUIDE.md CHANGED
@@ -8,9 +8,10 @@ kelra measures and compares iOS UI on Xcode simulators. Use it when the user say
8
8
  1. Get the reference as a file: a path the user gives, `latest_pasted_image` if they pasted a screenshot into the chat, or `clipboard_image` if they copied one.
9
9
  2. `analyze_reference` for a measured spec (its `textLines` have the words it read, with estimated sizes and weights; do not read words or guess sizes by eye).
10
10
  3. Build the SwiftUI screen from the spec. Install and launch it on the simulator the user has running. `set_status_bar` makes the status bar match the reference.
11
- 4. `compare` the reference against the device. Follow its `status`: while `critical` items exist you are not done. Apply `textAdjustments` and `adjustments` largest first, rebuild, and run `compare` again after every change. `compare` and `inspect_app` wait for the screen to stop changing before they capture it, so a launch animation is not mistaken for your build. If a result still says almost everything is missing with a very low similarity, run it once more before changing any code.
12
- 5. `extract_assets` crops photos out of a reference for local prototyping; pass `referenceTrimBottomPx` for watermark strips and `ignoreTopPt` / `ignoreBottomPt` for status bars or glass.
13
- 6. If the app runs the KelraInspector package, `inspect_app` gives its real frames, fills, radii and font sizes, and `compare` then says the size to set.
11
+ 4. `compare` the reference against the device. Read `summary` and `scores.match` (text and layout matter; the pixel score flatters flat backgrounds), then follow `status`: while `critical` items exist you are not done. Work through `fixes` from the top: a whole-screen shift, a vertical spacing factor, one text scale or typeface fix for all lines, groups of identical elements, then single items. Apply one round, rebuild, and run `compare` again with `summary=true`. Do not apply per-line numbers that a global fix above them already explains. `compare` and `inspect_app` wait for the screen to stop changing before they capture it, so a launch animation is not mistaken for your build; a blank capture is reported as critical, so retake it. The status bar is found from the clock and left out, a watermark strip under the reference is cut off, and the part of the render a cropped reference does not reach is not compared (the warning says so; it is not a layout error).
12
+ 5. Low-resolution references (a 299px Mobbin preview, a thumbnail): `analyze_reference` warns and suggests the point scale; sizes are then good to about ±10-15%, colours and weights are unreliable, and moves under 3pt are noise. Prefer a 2x or 3x screenshot when one exists.
13
+ 6. `extract_assets` crops photos out of a reference for local prototyping; pass `referenceTrimBottomPx` for watermark strips and `ignoreTopPt` / `ignoreBottomPt` for areas that must not be compared (live content, glass).
14
+ 7. If the app runs the KelraInspector package, `inspect_app` gives its real frames, fills, radii and font sizes, and `compare` then says the size to set.
14
15
 
15
16
  Never change a number by guess: measure it first.
16
17
 
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import { callService } from "./service.js";
19
19
  import { guide } from "./guide.js";
20
20
  // The guide for AI agents (usage and how to handle the helper) is sent to the model whenever this server is connected.
21
21
  const instructions = guide;
22
- const server = new McpServer({ name: "kelra", version: "0.1.1" }, { instructions });
22
+ const server = new McpServer({ name: "kelra", version: "0.1.2" }, { instructions });
23
23
  // the same guide, readable on demand
24
24
  server.resource("kelra-guide", "kelra://guide", { mimeType: "text/markdown", description: "How to use kelra and how to handle the helper and the license" }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: "text/markdown", text: guide }] }));
25
25
  const device = z.string().describe("Device name, UDID or ECID from list_devices");
@@ -195,7 +195,7 @@ server.tool("read_text", "Read the text in a screenshot with Apple Vision (OCR):
195
195
  throw new Error("Reading text needs a Mac with Xcode installed (Apple Vision).");
196
196
  return text(await callService("/v1/read-text", { image: b64(png), pointScale, minConfidence, includeIcons, ocr }));
197
197
  }));
198
- server.tool("compare", "Compare a reference screenshot against the current render and return `status` (done or not, and the next step: follow it), `critical` (e.g. text invisible in the render: fix these first), `textAdjustments` (per text line, matched by its words: exact font-size multiplier, move, weight), `adjustments` (move/resize/recolor for elements that matched, largest first), `missingInRender` and `extraInRender` (structure differences), a similarity score, the worst-differing screen areas, and a red diff overlay. Render comes from a live device screenshot, or renderPath for a saved PNG. The reference is scaled to the render's size; the aspect ratios should match.", {
198
+ server.tool("compare", "Compare a reference screenshot against the current render. Returns `summary` and `scores` (match, text, layout, pixels: watch `match`, not the pixel score), `status` (done or not, and the next step: follow it), `fixes` (what to change, in order: whole-screen shift, vertical spacing factor, one text scale or typeface fix for all lines, groups of identical elements, then single items), `critical` (text invisible in the render, a blank capture: fix these first), then the detail lists `textAdjustments`, `adjustments`, `missingInRender`, `extraInRender`, and a red diff overlay. The status bar is found from the clock and left out, a watermark strip under the reference is cut off, and the part of the render a cropped reference does not reach is not compared. Render comes from a live device screenshot, or renderPath for a saved PNG. Pass summary=true after the first round to get only scores, status, fixes and criticals.", {
199
199
  referencePath: z.string().describe("Absolute path to the reference PNG"),
200
200
  device: device.optional().describe("Device to capture as the render"),
201
201
  renderPath: z.string().optional().describe("Saved PNG to use instead of a device capture"),
@@ -208,7 +208,8 @@ server.tool("compare", "Compare a reference screenshot against the current rende
208
208
  ignoreRegions: z.array(z.object({ x: z.number(), y: z.number(), w: z.number(), h: z.number() })).default([]).describe("Rectangles in render points to leave out of the comparison (live content, glass effects, product photos)"),
209
209
  inspectPort: z.number().int().optional().describe("Port of KelraInspector in the running app. With a device, port 8765 is tried automatically. Adds the app's current exact font size and the size to set to each text adjustment."),
210
210
  align: z.enum(["auto", "stretch"]).default("auto").describe("auto: fit the reference to the render by scale and offset (handles crops, footers, other device sizes); stretch: force it to the render's exact size"),
211
- }, ({ referencePath, device, renderPath, pointScale, tolerance, align, inspectPort, referenceTrimTopPx, referenceTrimBottomPx, ignoreTopPt, ignoreBottomPt, ignoreRegions }) => wrap(async () => {
211
+ summary: z.boolean().default(false).describe("Return only summary, scores, status, fixes, critical, warnings and alignment (about a fifth of the tokens). Use it for every round after the first."),
212
+ }, ({ referencePath, device, renderPath, pointScale, tolerance, align, inspectPort, referenceTrimTopPx, referenceTrimBottomPx, ignoreTopPt, ignoreBottomPt, ignoreRegions, summary }) => wrap(async () => {
212
213
  if (!device && !renderPath)
213
214
  throw new Error("Provide device or renderPath");
214
215
  const renderPng = renderPath ? await readPng(renderPath, "render image") : await settledScreenshotPng(device);
@@ -222,7 +223,7 @@ server.tool("compare", "Compare a reference screenshot against the current rende
222
223
  ]);
223
224
  const r = await callService("/v1/compare", {
224
225
  reference: b64(refPng), render: b64(renderPng), pointScale, tolerance, ignoreTopPt, ignoreBottomPt, ignoreRegions, align,
225
- refOcr: refOcr && renderOcr ? refOcr : null, refOcrPixelsPerPoint: refPpp, renderOcr: refOcr && renderOcr ? renderOcr : null, live,
226
+ refOcr: refOcr && renderOcr ? refOcr : null, refOcrPixelsPerPoint: refPpp, renderOcr: refOcr && renderOcr ? renderOcr : null, live, summary,
226
227
  });
227
228
  const shape = align === "stretch" ? null : shapeWarning(ref, render);
228
229
  if (shape)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kelra",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {