paper-mono 0.62.2 → 0.62.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Release notes
2
2
 
3
+ ## 0.62.3 — 2026-08-28
4
+
5
+ - Added `paper_list_files` so the specialist can resolve open and recently accessed Paper files by name instead of depending on the frontmost document.
6
+ - Added `paper files --limit 200` and explicit `--file <file-id>` targeting across deterministic reads without changing Paper Desktop focus.
7
+ - Updated the runtime workflow to discover named sources first, preserve exact `fileId` across reads, and ask the operator only when returned names are genuinely ambiguous.
8
+
3
9
  ## 0.62.2 — 2026-08-28
4
10
 
5
11
  - Made fresh-cache `npx --yes paper-mono` startup fast by carrying the Pi runtime in the public artifact and reducing npm's install closure to four small runtime dependencies.
package/DEPENDENCIES.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "paper-mono-dependencies.v1",
3
- "package": "paper-mono@0.62.2",
3
+ "package": "paper-mono@0.62.3",
4
4
  "external": [
5
5
  {
6
6
  "name": "@silvia-odwyer/photon-node",
@@ -7620,6 +7620,7 @@ import { dirname as dirname2, isAbsolute as isAbsolute2 } from "node:path";
7620
7620
  var PAPER_MCP_URL = "http://127.0.0.1:29979/mcp";
7621
7621
  var TIMEOUT_MS = 3e4;
7622
7622
  var PAPER_DESKTOP_TOOL_NAMES = {
7623
+ paper_list_files: "list_files",
7623
7624
  paper_get_basic_info: "get_basic_info",
7624
7625
  paper_get_selection: "get_selection",
7625
7626
  paper_get_node_info: "get_node_info",
@@ -8053,6 +8054,8 @@ var PAPER_TREE_DEFAULT_DEPTH = 4;
8053
8054
  var PAPER_TREE_MIN_DEPTH = 1;
8054
8055
  var PAPER_TREE_MAX_DEPTH = 8;
8055
8056
  var PAPER_COMPUTED_STYLES_MAX_NODE_IDS = 50;
8057
+ var PAPER_FILES_DEFAULT_LIMIT = 50;
8058
+ var PAPER_FILES_MAX_LIMIT = 200;
8056
8059
  function highVolumeReadBudget(retryGuidance) {
8057
8060
  return { ...PAPER_HIGH_VOLUME_RESPONSE_LIMITS, retryGuidance };
8058
8061
  }
@@ -8090,6 +8093,13 @@ function boundedComputedStyleNodeIds(value) {
8090
8093
  }
8091
8094
  return value;
8092
8095
  }
8096
+ function boundedFileLimit(value) {
8097
+ if (value === void 0) return PAPER_FILES_DEFAULT_LIMIT;
8098
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > PAPER_FILES_MAX_LIMIT) {
8099
+ throw new TypeError(`paper_list_files limit must be an integer from 1 through ${PAPER_FILES_MAX_LIMIT}.`);
8100
+ }
8101
+ return value;
8102
+ }
8093
8103
  function providerArgsForTool(def, args) {
8094
8104
  const { _fixture: _ignoredFixture, dryRun: _ignoredDryRun, fileId: rawFileId, ...toolArgs } = args;
8095
8105
  const mappedArgs = def.mapArgs ? def.mapArgs(toolArgs) : toolArgs;
@@ -8849,10 +8859,26 @@ var paperTokenTypeSchema = import_typebox.Type.Union([
8849
8859
  ]);
8850
8860
  var paperMcpTools = [
8851
8861
  // ── Read tools ──────────────────────────────────────────────────────────
8862
+ createPaperTool({
8863
+ name: "paper_list_files",
8864
+ label: "Paper: List Files",
8865
+ description: "List Paper files that are open or recently accessed in the active team, newest first. Use this before asking the operator for a file ID, then pass a matched result's id as fileId to every file-scoped read. This does not change the focused Paper file.",
8866
+ parameters: import_typebox.Type.Object({
8867
+ limit: import_typebox.Type.Optional(
8868
+ import_typebox.Type.Integer({
8869
+ minimum: 1,
8870
+ maximum: PAPER_FILES_MAX_LIMIT,
8871
+ default: PAPER_FILES_DEFAULT_LIMIT,
8872
+ description: `Maximum files to return (default ${PAPER_FILES_DEFAULT_LIMIT}; maximum ${PAPER_FILES_MAX_LIMIT})`
8873
+ })
8874
+ )
8875
+ }),
8876
+ mapArgs: (args) => ({ limit: boundedFileLimit(args.limit) })
8877
+ }),
8852
8878
  createPaperTool({
8853
8879
  name: "paper_get_basic_info",
8854
8880
  label: "Paper: Get Basic Info",
8855
- description: "Get file name, page name, node count, and list of artboards with dimensions from the currently open Paper file.",
8881
+ description: "Get file name, page name, node count, and artboards from an explicit Paper file, without requiring it to be focused. Omit fileId only when the current MCP target is intentional.",
8856
8882
  parameters: import_typebox.Type.Object({ fileId: optionalFileIdSchema })
8857
8883
  }),
8858
8884
  createPaperTool({
@@ -19,6 +19,7 @@ function resolvePaperWorkflowMode(auditOnly, copilot) {
19
19
  return "reference-to-code";
20
20
  }
21
21
  var PAPER_TOOL_SAFETY_CLASSIFICATIONS = Object.freeze({
22
+ paper_list_files: "read-only",
22
23
  paper_get_basic_info: "read-only",
23
24
  paper_get_selection: "read-only",
24
25
  paper_get_node_info: "read-only",
@@ -61,6 +62,7 @@ var PAPER_MUTATION_TOOL_NAMES = [
61
62
  "paper_delete_nodes"
62
63
  ];
63
64
  var PAPER_TOOL_IDS = Object.freeze([
65
+ "paper_list_files",
64
66
  "paper_get_basic_info",
65
67
  "paper_get_selection",
66
68
  "paper_get_node_info",
@@ -107,12 +109,13 @@ Arguments:
107
109
  -- messages... Treat following option-shaped text as specialist prompt input
108
110
 
109
111
  Commands:
110
- ${APP_NAME} show|info [--json] Show the open Paper document
111
- ${APP_NAME} selection [--json] Show the current Paper selection
112
- ${APP_NAME} tree [--json] Show the open document tree
113
- ${APP_NAME} node <id> [options] Inspect node info, children, or JSX
114
- ${APP_NAME} screenshot --out <file> Save a node or single-selection screenshot
115
- ${APP_NAME} doctor [--json] Check all 27 Paper tools and reachability
112
+ ${APP_NAME} files [--limit <1-200>] [--json] List open and recent Paper files
113
+ ${APP_NAME} show|info [--file <id>] [--json] Show an explicit or current Paper document
114
+ ${APP_NAME} selection [--file <id>] [--json] Show a Paper file's current selection
115
+ ${APP_NAME} tree [--file <id>] [--json] Show a Paper document tree
116
+ ${APP_NAME} node <id> [--file <id>] [options] Inspect node info, children, or JSX
117
+ ${APP_NAME} screenshot --out <file> [--file <id>] Save a node or single-selection screenshot
118
+ ${APP_NAME} doctor [--json] Check all 28 Paper tools and reachability
116
119
 
117
120
  Options:
118
121
  --about-json Print identity, privacy, lock, workflow, and provenance receipts
@@ -8,7 +8,7 @@ import {
8
8
  resolveMonoAgentHome,
9
9
  resolveMonoContractRef,
10
10
  resolvePaperAgentHome
11
- } from "./chunk-ZIII3ND7.js";
11
+ } from "./chunk-7OKHV2KG.js";
12
12
  import {
13
13
  PACKAGE_DISTRIBUTION,
14
14
  VERSION,
@@ -34,7 +34,7 @@ import {
34
34
  PAPER_TOOL_SAFETY_CLASSIFICATIONS,
35
35
  printHelp,
36
36
  resolvePaperWorkflowMode
37
- } from "./chunk-ICDPC6EL.js";
37
+ } from "./chunk-DV5A4MYH.js";
38
38
  import {
39
39
  invalidPaperArgument,
40
40
  safeArgumentForDiagnostic
@@ -44,7 +44,7 @@ import {
44
44
  paperTools,
45
45
  renderPaperToolInventoryXml,
46
46
  resolvePaperAgentHome
47
- } from "./chunk-ZIII3ND7.js";
47
+ } from "./chunk-7OKHV2KG.js";
48
48
  import {
49
49
  APP_NAME,
50
50
  CONFIG_DIR_NAME,
@@ -187574,7 +187574,7 @@ Eleven package-owned mutating Paper operations are exposed behind the runtime ap
187574
187574
  - paper_revise_artboard replaces only one artboard's children and screenshots the result
187575
187575
  - paper_delete_nodes deletes only listed IDs and descendants after naming them, then verifies they are absent
187576
187576
 
187577
- Pass fileId on every Paper tool call, including reads. Read schemas keep fileId optional for provider compatibility, but omitting it follows that MCP session's most recently opened file and is unsafe when several files or agents are active. Every receipted mutation requires fileId and refuses to infer the frontmost file; its internal preflight, write, and verification calls preserve that same ID.
187577
+ Use paper_list_files whenever the user names a file that is not already identified, requests comparisons across files, or refers to another Paper file. It lists open and recently accessed files without changing focus; use the matched result's id as fileId. Pass fileId on every file-scoped Paper tool call, including reads; paper_list_files is the global discovery exception. Read schemas keep fileId optional for provider compatibility, but omitting it follows that MCP session's most recently opened file and is unsafe when several files or agents are active. Every receipted mutation requires fileId and refuses to infer the frontmost file; its internal preflight, write, and verification calls preserve that same ID.
187578
187578
 
187579
187579
  Page creation and switching require explicit file IDs plus an explicit page name or page ID; never infer a page target, merge creation with opening, or treat a successful create receipt as proof that the page is active. Direct page-control CLI commands are disabled: invoke these tools only after the exact page operation appears in an approved brief. Paper Desktop has no delete_page, rename_page, or reorder_pages calls, so page cleanup/order remains an upstream limit. For dry-run-capable artboard/node/token mutations, always run with dryRun=true first and require confirm=true for the live call. Existing source targets additionally require the exact action-specific sourceAcknowledgement described by the tool schema. Session provenance is process- and file-local, so a restarted runtime or explicit file switch treats all existing nodes as source work.
187580
187580
 
@@ -187605,15 +187605,16 @@ Note: start_working_on_nodes does NOT exist \u2014 do not call it.
187605
187605
  ${guidelinesSection}## Workflows
187606
187606
 
187607
187607
  Snapshot/reference \u2192 Code:
187608
- 1. resolve the exact fileId, call paper_get_basic_info to confirm file identity and artboard IDs, and pass that fileId to every following Paper call
187609
- 2. use paper_get_selection when the operator points at a selection; otherwise begin with a bounded tree summary or targeted node search (tree depth defaults to 4 and must remain 1 through 8)
187610
- 3. call paper_get_screenshot on one exact artboard or node and start with scale 1
187611
- 4. call paper_get_jsx for that subtree and paper_get_computed_styles for only the nodes needed for the current decision (maximum 50 IDs)
187612
- 5. summarize the reference's structure, hierarchy, spacing, typography, color, and interaction implications before reading the next subtree
187613
- 6. emit one valid paper-implementation-brief.v1 block bound to the exact Paper file/page/artboard/nodes, typed move destinations (or an empty array), any exact page-create/open arguments, destination cwd and paths, permitted commands, acceptance criteria, and verification plan; do not mutate yet
187614
- 7. wait for the user's exact standalone APPROVE PAPER BRIEF <brief-id> phrase; ordinary agreement, prior approval, or the selected node is not authorization
187615
- 8. implement only the approved brief by adapting those findings through the destination product's design system; do not copy the reference's brand or CSS wholesale
187616
- 9. run independent product VQA in the destination's real route, viewport, auth state, and interaction loop; a Paper screenshot proves the reference, not the shipped product
187608
+ 1. if the requested source file is not already identified, call paper_list_files and resolve it by exact name; ask the operator only when multiple returned files remain ambiguous
187609
+ 2. call paper_get_basic_info with that exact fileId to confirm file identity and artboard IDs, and pass the same fileId to every following file-scoped Paper call; explicit reads do not require the file to be focused
187610
+ 3. use paper_get_selection when the operator points at a selection; otherwise begin with a bounded tree summary or targeted node search (tree depth defaults to 4 and must remain 1 through 8)
187611
+ 4. call paper_get_screenshot on one exact artboard or node and start with scale 1
187612
+ 5. call paper_get_jsx for that subtree and paper_get_computed_styles for only the nodes needed for the current decision (maximum 50 IDs)
187613
+ 6. summarize the reference's structure, hierarchy, spacing, typography, color, and interaction implications before reading the next subtree
187614
+ 7. emit one valid paper-implementation-brief.v1 block bound to the exact Paper file/page/artboard/nodes, typed move destinations (or an empty array), any exact page-create/open arguments, destination cwd and paths, permitted commands, acceptance criteria, and verification plan; do not mutate yet
187615
+ 8. wait for the user's exact standalone APPROVE PAPER BRIEF <brief-id> phrase; ordinary agreement, prior approval, or the selected node is not authorization
187616
+ 9. implement only the approved brief by adapting those findings through the destination product's design system; do not copy the reference's brand or CSS wholesale
187617
+ 10. run independent product VQA in the destination's real route, viewport, auth state, and interaction loop; a Paper screenshot proves the reference, not the shipped product
187617
187618
 
187618
187619
  Code \u2192 Design / imported reference \u2192 Design:
187619
187620
  1. read the codebase, or confirm that the operator has already imported the web reference through the external Paper Snapshot extension
@@ -14,6 +14,7 @@ var PAPER_NODE_PLACEHOLDER = "replace-with-node-id";
14
14
  var PAPER_NO_SCREENSHOT_SELECTION = "No Paper node is selected. Select exactly one node in Paper or pass --node <node-id>.";
15
15
  var PAPER_AMBIGUOUS_SCREENSHOT_SELECTION = "Multiple Paper nodes are selected. Select exactly one node in Paper or pass --node <node-id>.";
16
16
  var OWNER_DESKTOP_TOOL_NAMES = Object.freeze({
17
+ paper_list_files: "list_files",
17
18
  paper_get_basic_info: "get_basic_info",
18
19
  paper_get_selection: "get_selection",
19
20
  paper_get_node_info: "get_node_info",
@@ -25,14 +26,15 @@ var OWNER_DESKTOP_TOOL_NAMES = Object.freeze({
25
26
  var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
26
27
  var STRICT_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
27
28
  var ownerRequestId = 1;
28
- var READ_COMMANDS = /* @__PURE__ */ new Set(["show", "info", "selection", "tree", "node", "screenshot"]);
29
+ var READ_COMMANDS = /* @__PURE__ */ new Set(["files", "show", "info", "selection", "tree", "node", "screenshot"]);
29
30
  var READ_COMMAND_HELP = {
30
- show: "Usage: paper show [--json]\nShow basic information about the open Paper document.\n",
31
- info: "Usage: paper info [--json]\nAlias for `paper show`.\n",
32
- selection: "Usage: paper selection [--json]\nShow the current Paper selection.\n",
33
- tree: "Usage: paper tree [--json]\nShow a bounded summary of the open document tree.\n",
34
- node: "Usage: paper node <nodeId> [--children | --jsx] [--json]\nInspect one Paper node.\n",
35
- screenshot: "Usage: paper screenshot --out <file> [--node <nodeId>] [--json]\nSave a node or single-selection screenshot using Paper's returned image format.\n"
31
+ files: "Usage: paper files [--limit <1-200>] [--json]\nList open and recently accessed Paper files.\n",
32
+ show: "Usage: paper show [--file <file-id>] [--json]\nShow basic information about an explicit or current Paper document.\n",
33
+ info: "Usage: paper info [--file <file-id>] [--json]\nAlias for `paper show`.\n",
34
+ selection: "Usage: paper selection [--file <file-id>] [--json]\nShow a Paper file's current selection.\n",
35
+ tree: "Usage: paper tree [--file <file-id>] [--json]\nShow a bounded summary of a Paper document tree.\n",
36
+ node: "Usage: paper node <nodeId> [--file <file-id>] [--children | --jsx] [--json]\nInspect one Paper node.\n",
37
+ screenshot: "Usage: paper screenshot --out <file> [--file <file-id>] [--node <nodeId>] [--json]\nSave a node or single-selection screenshot using Paper's returned image format.\n"
36
38
  };
37
39
  function providerError(result) {
38
40
  if (!result || typeof result !== "object" || Array.isArray(result)) return void 0;
@@ -173,6 +175,14 @@ function paperText(result) {
173
175
  const text = paperContentParts(result).filter((part) => part.type === "text").map((part) => part.text).join("\n");
174
176
  return text || JSON.stringify(result, null, 2);
175
177
  }
178
+ function paperJson(result) {
179
+ const text = paperText(result);
180
+ try {
181
+ return JSON.parse(text);
182
+ } catch {
183
+ return { result };
184
+ }
185
+ }
176
186
  function selectedNodeIds(result) {
177
187
  let selection;
178
188
  try {
@@ -213,6 +223,47 @@ function assertNoArguments(command, args) {
213
223
  invalidPaperArgument(`${command} does not accept '${safeArgumentForDiagnostic(args[0] ?? "")}'.`);
214
224
  }
215
225
  }
226
+ function parseFilesArgs(args) {
227
+ let limit = 50;
228
+ let seenLimit = false;
229
+ for (let index = 0; index < args.length; index += 1) {
230
+ const arg = args[index];
231
+ if (!arg) continue;
232
+ const [flag, inlineValue] = arg.includes("=") ? arg.split(/=(.*)/s, 2) : [arg, void 0];
233
+ if (flag !== "--limit") {
234
+ invalidPaperArgument(`files does not accept '${safeArgumentForDiagnostic(arg)}'.`);
235
+ }
236
+ if (seenLimit) throw new TypeError("--limit may be provided only once.");
237
+ const value = inlineValue ?? args[index + 1];
238
+ if (inlineValue === void 0) index += 1;
239
+ if (!value || value.startsWith("--")) throw new TypeError("--limit requires a value.");
240
+ limit = Number(value);
241
+ if (!Number.isInteger(limit) || limit < 1 || limit > 200) {
242
+ throw new TypeError("--limit must be an integer from 1 through 200.");
243
+ }
244
+ seenLimit = true;
245
+ }
246
+ return { limit };
247
+ }
248
+ function stripFileFlag(args) {
249
+ let fileId;
250
+ const remaining = [];
251
+ for (let index = 0; index < args.length; index += 1) {
252
+ const arg = args[index];
253
+ if (!arg) continue;
254
+ const [flag, inlineValue] = arg.includes("=") ? arg.split(/=(.*)/s, 2) : [arg, void 0];
255
+ if (flag !== "--file") {
256
+ remaining.push(arg);
257
+ continue;
258
+ }
259
+ if (fileId) throw new TypeError("--file may be provided only once.");
260
+ const value = inlineValue ?? args[index + 1];
261
+ if (inlineValue === void 0) index += 1;
262
+ if (!value || value.startsWith("--")) throw new TypeError("--file requires a Paper file ID or URL.");
263
+ fileId = value;
264
+ }
265
+ return { args: remaining, ...fileId ? { fileId } : {} };
266
+ }
216
267
  function parseNodeArgs(args) {
217
268
  let nodeId;
218
269
  let children = false;
@@ -308,7 +359,7 @@ async function requirePaperResult(callTool, toolName, args) {
308
359
  return response.result;
309
360
  }
310
361
  function writeReadResult(result, json, writeOutput) {
311
- writeOutput(json ? `${JSON.stringify({ result }, null, 2)}
362
+ writeOutput(json ? `${JSON.stringify(paperJson(result), null, 2)}
312
363
  ` : `${paperText(result)}
313
364
  `);
314
365
  }
@@ -327,19 +378,26 @@ async function handleOwnerReadCommand(argv, io = {}) {
327
378
  return true;
328
379
  }
329
380
  const parsed = stripJsonFlag(argv.slice(1));
381
+ if (command === "files") {
382
+ const files = parseFilesArgs(parsed.args);
383
+ writeReadResult(await requirePaperResult(callTool, "paper_list_files", files), parsed.json, writeOutput);
384
+ return true;
385
+ }
386
+ const scoped = stripFileFlag(parsed.args);
387
+ const fileArgs = scoped.fileId ? { fileId: scoped.fileId } : {};
330
388
  if (command === "show" || command === "info") {
331
- assertNoArguments(command, parsed.args);
332
- writeReadResult(await requirePaperResult(callTool, "paper_get_basic_info", {}), parsed.json, writeOutput);
389
+ assertNoArguments(command, scoped.args);
390
+ writeReadResult(await requirePaperResult(callTool, "paper_get_basic_info", fileArgs), parsed.json, writeOutput);
333
391
  return true;
334
392
  }
335
393
  if (command === "selection") {
336
- assertNoArguments(command, parsed.args);
337
- writeReadResult(await requirePaperResult(callTool, "paper_get_selection", {}), parsed.json, writeOutput);
394
+ assertNoArguments(command, scoped.args);
395
+ writeReadResult(await requirePaperResult(callTool, "paper_get_selection", fileArgs), parsed.json, writeOutput);
338
396
  return true;
339
397
  }
340
398
  if (command === "tree") {
341
- assertNoArguments(command, parsed.args);
342
- const basicInfo = await requirePaperResult(callTool, "paper_get_basic_info", {});
399
+ assertNoArguments(command, scoped.args);
400
+ const basicInfo = await requirePaperResult(callTool, "paper_get_basic_info", fileArgs);
343
401
  let rootNodeId;
344
402
  try {
345
403
  rootNodeId = JSON.parse(paperText(basicInfo)).rootNodeId;
@@ -349,26 +407,26 @@ async function handleOwnerReadCommand(argv, io = {}) {
349
407
  if (typeof rootNodeId !== "string" || !rootNodeId.trim()) {
350
408
  throw new TypeError("Paper get_basic_info returned no rootNodeId for the tree summary.");
351
409
  }
352
- const result2 = await requirePaperResult(callTool, "paper_get_tree_summary", { nodeId: rootNodeId });
410
+ const result2 = await requirePaperResult(callTool, "paper_get_tree_summary", { ...fileArgs, nodeId: rootNodeId });
353
411
  writeReadResult(result2, parsed.json, writeOutput);
354
412
  return true;
355
413
  }
356
414
  if (command === "node") {
357
- const node = parseNodeArgs(parsed.args);
358
- const result2 = await requirePaperResult(callTool, node.toolName, { nodeId: node.nodeId });
415
+ const node = parseNodeArgs(scoped.args);
416
+ const result2 = await requirePaperResult(callTool, node.toolName, { ...fileArgs, nodeId: node.nodeId });
359
417
  writeReadResult(result2, parsed.json, writeOutput);
360
418
  return true;
361
419
  }
362
- const screenshot = parseScreenshotArgs(parsed.args);
420
+ const screenshot = parseScreenshotArgs(scoped.args);
363
421
  let screenshotNodeId = screenshot.nodeId;
364
422
  if (!screenshotNodeId) {
365
- const selection = await requirePaperResult(callTool, "paper_get_selection", {});
423
+ const selection = await requirePaperResult(callTool, "paper_get_selection", fileArgs);
366
424
  const nodeIds = selectedNodeIds(selection);
367
425
  if (nodeIds.length === 0) throw new TypeError(PAPER_NO_SCREENSHOT_SELECTION);
368
426
  if (nodeIds.length !== 1) throw new TypeError(PAPER_AMBIGUOUS_SCREENSHOT_SELECTION);
369
427
  screenshotNodeId = nodeIds[0];
370
428
  }
371
- const result = await requirePaperResult(callTool, "paper_get_screenshot", { nodeId: screenshotNodeId });
429
+ const result = await requirePaperResult(callTool, "paper_get_screenshot", { ...fileArgs, nodeId: screenshotNodeId });
372
430
  const images = paperContentParts(result).filter(
373
431
  (part) => part.type === "image"
374
432
  );
@@ -13,7 +13,7 @@ import {
13
13
  renderPaperToolInventoryXml,
14
14
  resolvePaperAgentHome,
15
15
  resolvePaperContractRef
16
- } from "./chunk-ZIII3ND7.js";
16
+ } from "./chunk-7OKHV2KG.js";
17
17
  import "./chunk-U4I455JM.js";
18
18
  import "./chunk-QPVBM6BW.js";
19
19
  export {
package/bin/paper.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  STANDARD_TOOL_IDS,
9
9
  printHelp,
10
10
  resolvePaperWorkflowMode
11
- } from "./chunks/chunk-ICDPC6EL.js";
11
+ } from "./chunks/chunk-DV5A4MYH.js";
12
12
  import {
13
13
  invalidPaperArgument,
14
14
  safeArgumentForDiagnostic
@@ -29,7 +29,7 @@ import { existsSync, readFileSync } from "node:fs";
29
29
  import { dirname, join } from "node:path";
30
30
  import { fileURLToPath } from "node:url";
31
31
  var STANDARD_LOCKED_TOOL_IDS = /* @__PURE__ */ new Set(["read", "grep", "find", "ls"]);
32
- var OWNER_READ_COMMANDS = /* @__PURE__ */ new Set(["show", "info", "selection", "tree", "node", "screenshot"]);
32
+ var OWNER_READ_COMMANDS = /* @__PURE__ */ new Set(["files", "show", "info", "selection", "tree", "node", "screenshot"]);
33
33
  function resolveModulePath(specifier) {
34
34
  return fileURLToPath(import.meta.resolve(specifier));
35
35
  }
@@ -71,7 +71,7 @@ async function createAboutJson(options = {}) {
71
71
  PAPER_TOOL_SCHEMA_HASHES,
72
72
  paperToolContract,
73
73
  resolvePaperAgentHome
74
- } = await import("./chunks/tool-contract-5H55QIEJ.js");
74
+ } = await import("./chunks/tool-contract-A7BQH6PV.js");
75
75
  const packageLoadErrors = [];
76
76
  const paperEntry = fileURLToPath(import.meta.url);
77
77
  const monoForkRuntime = resolveReportedModule("@creative-int/mono/fork-runtime", paperEntry, packageLoadErrors);
@@ -170,7 +170,7 @@ function specialistAboutOptions(args) {
170
170
  }
171
171
  function printDoctorHelp() {
172
172
  process.stdout.write(
173
- "Usage: paper doctor [--json]\nCheck the 27-tool contract and live Paper Desktop reachability.\n"
173
+ "Usage: paper doctor [--json]\nCheck the 28-tool contract and live Paper Desktop reachability.\n"
174
174
  );
175
175
  }
176
176
  function assertOnly(args, accepted, command) {
@@ -187,7 +187,7 @@ async function runSpecialist(args) {
187
187
  const [undiciModule, { registerBunOAuthFlows }, { main: runSpecialistMain }] = await Promise.all([
188
188
  import("./chunks/undici-EMZDFWT4.js"),
189
189
  import("./chunks/bun-oauth-JJSCAKFO.js"),
190
- import("./chunks/main-HS6FSCUI.js")
190
+ import("./chunks/main-3L2ZQP4E.js")
191
191
  ]);
192
192
  const undiciRuntime = typeof undiciModule.EnvHttpProxyAgent === "function" ? undiciModule : undiciModule.default;
193
193
  const { EnvHttpProxyAgent, setGlobalDispatcher } = undiciRuntime;
@@ -229,7 +229,7 @@ async function main(args) {
229
229
  const rejected = normalizedArgs[1] ?? normalizedArgs[2] ?? "extra arguments";
230
230
  invalidPaperArgument(`doctor does not accept '${safeArgumentForDiagnostic(rejected)}'.`);
231
231
  }
232
- const { createPaperToolDoctorReport, paperDoctorExitCode, renderPaperToolDoctorReport } = await import("./chunks/doctor-TOH2BSQ6.js");
232
+ const { createPaperToolDoctorReport, paperDoctorExitCode, renderPaperToolDoctorReport } = await import("./chunks/doctor-MUUZ6CJM.js");
233
233
  const report = await createPaperToolDoctorReport();
234
234
  process.stdout.write(
235
235
  normalizedArgs[1] === "--json" ? `${JSON.stringify(report, null, 2)}
@@ -239,7 +239,7 @@ async function main(args) {
239
239
  return;
240
240
  }
241
241
  if (!isCompatibilityMonoRoute && OWNER_READ_COMMANDS.has(normalizedArgs[0] ?? "")) {
242
- const { handleOwnerReadCommand } = await import("./chunks/owner-commands-VZECUCWE.js");
242
+ const { handleOwnerReadCommand } = await import("./chunks/owner-commands-XXVPK5WS.js");
243
243
  if (await handleOwnerReadCommand(normalizedArgs)) return;
244
244
  }
245
245
  if (normalizedArgs[0] === "page") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paper-mono",
3
- "version": "0.62.2",
3
+ "version": "0.62.3",
4
4
  "description": "Local-first Paper Desktop design-to-code specialist for agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,6 +1,28 @@
1
1
  {
2
2
  "schemaVersion": "paper-tool-walk-fixtures.v1",
3
3
  "cases": [
4
+ {
5
+ "id": "paper-list-files",
6
+ "tool": "paper_list_files",
7
+ "mode": "fixture",
8
+ "safety": "read",
9
+ "input": {
10
+ "limit": 50,
11
+ "_fixture": {
12
+ "content": [
13
+ {
14
+ "type": "text",
15
+ "text": "{\"files\":[{\"id\":\"file-poke\",\"name\":\"poke\",\"updatedAt\":1787639688831},{\"id\":\"file-paper\",\"name\":\"paper\",\"updatedAt\":1787961946457}]}"
16
+ }
17
+ ]
18
+ }
19
+ },
20
+ "expect": {
21
+ "status": ["pass"],
22
+ "summaryIncludes": "file-poke",
23
+ "maxBytes": 49152
24
+ }
25
+ },
4
26
  {
5
27
  "id": "paper-get-basic-info",
6
28
  "tool": "paper_get_basic_info",
@@ -1,20 +1,48 @@
1
1
  {
2
2
  "schemaVersion": "mono-safety-card.v1",
3
- "cardId": "paper:sha256:b53fab419474899ca25a0b84546025fbe758920be3dcddc5dd1e6fd97bf242b8",
3
+ "cardId": "paper:sha256:17c5b377d51ae3cbe81c2691a67e42da2b57c2b741c1eec6063239c857bf3724",
4
4
  "specialist": "paper",
5
- "contractHash": "sha256:b53fab419474899ca25a0b84546025fbe758920be3dcddc5dd1e6fd97bf242b8",
5
+ "contractHash": "sha256:17c5b377d51ae3cbe81c2691a67e42da2b57c2b741c1eec6063239c857bf3724",
6
6
  "status": "pass",
7
7
  "conjunctive": true,
8
- "verifiedAt": "2026-08-28T22:55:46.054Z",
8
+ "verifiedAt": "2026-08-29T01:48:42.859Z",
9
9
  "verificationSource": "pnpm -C packages/mono safety:verify (L1 engine over executable tool-walk fixtures)",
10
10
  "summary": {
11
- "tools": 27,
12
- "passed": 27,
11
+ "tools": 28,
12
+ "passed": 28,
13
13
  "failed": 0,
14
- "cases": 35,
14
+ "cases": 36,
15
15
  "autoFails": 0
16
16
  },
17
17
  "tools": [
18
+ {
19
+ "tool": "paper_list_files",
20
+ "status": "pass",
21
+ "declaredModes": [
22
+ "mock"
23
+ ],
24
+ "coveredModes": [
25
+ "mock"
26
+ ],
27
+ "cases": [
28
+ {
29
+ "id": "paper_list_files-mock-safety",
30
+ "tool": "paper_list_files",
31
+ "mode": "mock",
32
+ "status": "pass",
33
+ "outputBytes": 259,
34
+ "outputLines": 9,
35
+ "effects": {
36
+ "networkAttempts": [],
37
+ "providerCalls": [],
38
+ "filesystemWrites": [],
39
+ "externalMutations": [],
40
+ "confirmation": "not-applicable"
41
+ },
42
+ "violations": []
43
+ }
44
+ ]
45
+ },
18
46
  {
19
47
  "tool": "paper_get_basic_info",
20
48
  "status": "pass",
@@ -2,6 +2,15 @@
2
2
  "schemaVersion": "mono-safety-fixtures.v1",
3
3
  "specialist": "paper",
4
4
  "cases": [
5
+ {
6
+ "id": "paper_list_files-mock-safety",
7
+ "tool": "paper_list_files",
8
+ "mode": "mock",
9
+ "input": {
10
+ "sourceFixtureRef": "./tool-walk/fixtures.json#paper-list-files",
11
+ "execution": "recorded-tool-walk"
12
+ }
13
+ },
5
14
  {
6
15
  "id": "paper_get_basic_info-mock-safety",
7
16
  "tool": "paper_get_basic_info",
@@ -6,6 +6,7 @@
6
6
  "domain": {
7
7
  "comment": "REQ: Paper Desktop design-to-code agent CLI powered by the Paper MCP bridge at http://127.0.0.1:29979/mcp.",
8
8
  "toolIds": [
9
+ "paper_list_files",
9
10
  "paper_get_basic_info",
10
11
  "paper_get_selection",
11
12
  "paper_get_node_info",
@@ -36,11 +37,71 @@
36
37
  ]
37
38
  },
38
39
  "tools": [
40
+ {
41
+ "id": "paper_list_files",
42
+ "version": 1,
43
+ "label": "Paper: List Files",
44
+ "description": "List Paper files that are open or recently accessed in the active team, newest first. Use this before asking the operator for a file ID, then pass a matched result's id as fileId to every file-scoped read. This does not change the focused Paper file.",
45
+ "handlerRef": "./src/core/tools/paper-mcp.ts#paperTools",
46
+ "sourcePack": null,
47
+ "demandRefs": [
48
+ "mono-fleet-era:paper-tool-contract",
49
+ "audit:paper-cross-file-reads"
50
+ ],
51
+ "safety": "read",
52
+ "requires": {
53
+ "allOf": [
54
+ "Paper Desktop running at http://127.0.0.1:29979/mcp"
55
+ ],
56
+ "anyOf": [],
57
+ "cli": [
58
+ "paper"
59
+ ],
60
+ "endpoint": [],
61
+ "optional": false
62
+ },
63
+ "inputSchemaRef": "./src/core/tools/paper-mcp.ts",
64
+ "output": {
65
+ "envelope": "mono-tool-result.v1",
66
+ "maxBytes": 49152,
67
+ "maxItems": 200
68
+ },
69
+ "doctorProbe": "endpoint",
70
+ "cases": [
71
+ "./tool-walk/fixtures.json#paper-list-files"
72
+ ],
73
+ "cliFallback": "paper files --limit 200 --json",
74
+ "guidelines": [
75
+ "Use before asking the operator for a Paper file ID or URL.",
76
+ "Resolve an exact file name, then pass its id as fileId to every file-scoped read without changing focus."
77
+ ],
78
+ "safetyContract": {
79
+ "modes": [
80
+ "mock"
81
+ ],
82
+ "guarantees": {
83
+ "mock": {
84
+ "networkSpend": "forbidden",
85
+ "filesystemWrites": "none",
86
+ "filesystemRoots": [],
87
+ "externalMutations": "forbidden",
88
+ "requiresConfirmation": false
89
+ }
90
+ },
91
+ "fixtureRefs": [
92
+ "./tool-walk/safety-fixtures.json#paper_list_files-mock-safety"
93
+ ],
94
+ "outputBudget": {
95
+ "maxBytes": 49152,
96
+ "maxLines": 1200
97
+ }
98
+ }
99
+ },
39
100
  {
40
101
  "id": "paper_get_basic_info",
41
102
  "version": 1,
42
103
  "label": "Paper: Get Basic Info",
43
- "description": "Get file name, page name, node count, and list of artboards with dimensions from the currently open Paper file.",
104
+ "description": "Get file name, page name, node count, and artboards from an explicit Paper file, without requiring it to be focused. Omit fileId only when the current MCP target is intentional.",
44
105
  "handlerRef": "./src/core/tools/paper-mcp.ts#paperTools",
45
106
  "sourcePack": null,
46
107
  "demandRefs": [
@@ -71,7 +132,7 @@
71
132
  ],
72
133
  "cliFallback": null,
73
134
  "guidelines": [
74
- "Use as the first read when grounding in the open Paper file.",
135
+ "Use after paper_list_files to ground an exact file without requiring it to be focused.",
75
136
  "If Paper Desktop is not running, fall back to fixtures or ask the operator to open a file."
76
137
  ],
77
138
  "safetyContract": {