@lalalic/markcut 3.2.7 → 3.2.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lalalic/markcut",
3
- "version": "3.2.7",
3
+ "version": "3.2.9",
4
4
  "description": "Markdown-to-video engine. Describe scenes in markdown, get a rendered video.",
5
5
  "bin": {
6
6
  "markcut": "bin/markcut"
@@ -78,10 +78,20 @@ npx @lalalic/markcut spots --waypoints "lat,lng;lat,lng" # discover POIs along a
78
78
 
79
79
  ---
80
80
 
81
+ ## MCP Markdown Review
82
+
83
+ Use the model-visible MCP tool `markcut.preview({ path })` when a human needs to review a Markdown artifact. This is a generic Markdown review surface and is separate from the video-specific `npx @lalalic/markcut preview <file>` CLI.
84
+
85
+ - `path` must be an absolute path to a Markdown file.
86
+ - The user sees the rendered Markdown and can either **Approve** or submit feedback with **Request changes**.
87
+ - Markcut does not edit the file. The calling agent owns all source changes.
88
+ - On `approved`, continue the workflow.
89
+ - On `changes_requested`, apply the feedback to the source Markdown, then call `markcut.preview({ path })` again for another review round.
90
+
81
91
  ## review
82
92
  use the review contract defined in [./review.md](./review.md) to guide the review process.
83
93
  review as early as possible in the video production process to catch issues before they propagate.
84
- * review md file
94
+ * review md file with `markcut.preview({ path })` when the MCP tool is available
85
95
  * review compiled.json
86
96
  * review the rendered video
87
97
 
@@ -7,7 +7,7 @@ function send(message){window.parent.postMessage(message,'*');}
7
7
  function rpcRequest(method,params){const id=++nextRequestId;send({jsonrpc:'2.0',id,method,params});return new Promise((resolve,reject)=>pendingRequests.set(id,{resolve,reject}));}
8
8
  function applyToolResult(params){const result=params?.result||params||{},data=result.structuredContent||result.structured_content||{};if(typeof data.path==='string')pathLabel.textContent=data.path;if(typeof data.markdown==='string'){article.innerHTML=data.markdown.split(/\\r?\\n/).map(line=>{const escaped=line.replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));if(line.startsWith('### '))return '<h3>'+escaped.slice(4)+'</h3>';if(line.startsWith('## '))return '<h2>'+escaped.slice(3)+'</h2>';if(line.startsWith('# '))return '<h1>'+escaped.slice(2)+'</h1>';if(line.startsWith('- '))return '<li>'+escaped.slice(2)+'</li>';return line.trim()?'<p>'+escaped+'</p>':''}).join('\\n');}}
9
9
  window.addEventListener('message',event=>{if(event.source!==window.parent)return;const message=event.data;if(!message||typeof message!=='object'||message.jsonrpc!=='2.0')return;if(Object.prototype.hasOwnProperty.call(message,'id')&&pendingRequests.has(message.id)){const pending=pendingRequests.get(message.id);pendingRequests.delete(message.id);if(message.error)pending.reject(new Error(message.error.message||'MCP request failed'));else pending.resolve(message.result);return;}if(message.method==='ui/notifications/tool-result')applyToolResult(message.params);});
10
- async function initializeBridge(){try{await rpcRequest('ui/initialize',{protocolVersion:'2026-01-26',capabilities:{},clientInfo:{name:'markcut-preview',version:'1'}});send({jsonrpc:'2.0',method:'ui/notifications/initialized'});status.textContent='Ready for review.';}catch(error){status.textContent=error.message||'MCP initialization failed.';}}
10
+ async function initializeBridge(){try{await rpcRequest('ui/initialize',{appInfo:{name:'Markcut Preview',version:'1.0.0'},appCapabilities:{},protocolVersion:'2026-01-26'});send({jsonrpc:'2.0',method:'ui/notifications/initialized'});status.textContent='Ready for review.';}catch(error){status.textContent=error.message||'MCP initialization failed.';}}
11
11
  async function submit(decision){const value=feedback.value.trim();if(decision==='changes_requested'&&!value){status.textContent='Feedback is required.';feedback.focus();return;}const result={decision};if(value&&decision==='changes_requested')result.feedback=value;status.textContent='Submitting...';try{await rpcRequest('tools/call',{name:'${submitToolName}',arguments:result});send({jsonrpc:'2.0',method:'ui/message',params:{role:'user',content:[{type:'text',text:JSON.stringify(result)}]}});status.textContent=decision==='approved'?'Approved.':'Changes requested.';}catch(error){status.textContent=error.message||'Unable to submit review.';}}
12
12
  initializeBridge();
13
13
  document.getElementById('approve').onclick=()=>submit('approved');document.getElementById('changes').onclick=()=>submit('changes_requested');
@@ -53,8 +53,26 @@ describe("MarkCut MCP Markdown preview", () => {
53
53
  it("performs the MCP Apps handshake and awaits a successful submit", async () => {
54
54
  const harness = createBridgeHarness();
55
55
  harness.run();
56
- expect(harness.messages[0]).toMatchObject({ id: 1, method: "ui/initialize" });
57
- harness.respond({ id: 1, result: { protocolVersion: "2026-01-26" } });
56
+ expect(harness.messages[0]).toEqual({
57
+ jsonrpc: "2.0",
58
+ id: 1,
59
+ method: "ui/initialize",
60
+ params: {
61
+ appInfo: { name: "Markcut Preview", version: "1.0.0" },
62
+ appCapabilities: {},
63
+ protocolVersion: "2026-01-26",
64
+ },
65
+ });
66
+ expect(harness.messages.some((message) => message.method === "ui/notifications/initialized")).toBe(false);
67
+ harness.respond({
68
+ id: 1,
69
+ result: {
70
+ protocolVersion: "2026-01-26",
71
+ hostInfo: { name: "test-host", version: "1" },
72
+ hostCapabilities: {},
73
+ hostContext: {},
74
+ },
75
+ });
58
76
  await tick();
59
77
  expect(harness.messages[1]).toMatchObject({ method: "ui/notifications/initialized" });
60
78