md-feedback 1.3.0 → 1.3.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/dist/mcp-server.js +1 -1
- package/package.json +73 -73
package/dist/mcp-server.js
CHANGED
|
@@ -77,4 +77,4 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
77
77
|
`),m=-1;if(l.anchor){let y=l.anchor.match(/^L(\d+)(?::L\d+)?\|(.+)$/);if(y){let v=parseInt(y[1],10)-1,w=bd(f[v]||"");if(v>=0&&v<f.length&&w===y[2])m=v;else for(let b=1;b<=10;b++){for(let $ of[v-b,v+b])if($>=0&&$<f.length&&bd(f[$])===y[2]){m=$;break}if(m>=0)break}}}if(m===-1&&l.anchorText){for(let y=0;y<f.length;y++)if(f[y].includes(l.anchorText)){m=y;break}}m===-1&&(m=f.length-1);let p=a.split(`
|
|
78
78
|
`),g=m+1;f.splice(g,0,...p);for(let y of u.responses)y.bodyStartIdx>=g&&(y.bodyStartIdx+=p.length,y.bodyEndIdx+=p.length);u.body=f.join(`
|
|
79
79
|
`),u.responses.push({id:`resp_${i}`,to:i,bodyStartIdx:g,bodyEndIdx:g+p.length-1})}l.status==="open"&&(l.status="answered",l.updatedAt=new Date().toISOString()),u.gates.length>0&&(u.gates=It(u.gates,u.memos));let h=Rt(u);return o(s,h),{content:[{type:"text",text:JSON.stringify({memoId:i,status:l.status,responseInserted:!0,totalResponses:u.responses.length},null,2)}]}}catch(c){return{content:[{type:"text",text:JSON.stringify({error:c instanceof Error?c.message:String(c)})}],isError:!0}}})),t.tool("update_memo_status","Update the status of a memo annotation. Writes the change back to the markdown file. Returns the updated memo.",{file:R.string().describe("Path to the annotated markdown file"),memoId:R.string().describe("The memo ID to update"),status:R.enum(["open","in_progress","needs_review","answered","done","failed","wontfix"]).describe("New status"),owner:R.enum(["human","agent","tool"]).optional().describe("Optionally change the owner")},async({file:s,memoId:i,status:a,owner:c})=>vt(s,async()=>{try{let u=n(s),l=Oe(u),d=l.memos.find(g=>g.id===i);if(!d)return{content:[{type:"text",text:JSON.stringify({error:`Memo not found: ${i}`})}],isError:!0};d.status=a,c&&(d.owner=c),d.updatedAt=new Date().toISOString(),l.gates.length===0&&l.memos.length>0&&l.gates.push({id:`gate-${Date.now().toString(36)}`,type:"merge",status:"blocked",blockedBy:[],canProceedIf:"",doneDefinition:"All review annotations resolved"}),l.gates=It(l.gates,l.memos);let h=l.memos.filter(g=>g.status!=="open").length,f=l.memos.length,m=l.memos.filter(g=>g.status==="open");l.cursor={taskId:i,step:`${h}/${f} resolved`,nextAction:m.length===0?"All annotations resolved \u2014 review complete":`Resolve: ${m.map(g=>g.id).slice(0,3).join(", ")}${m.length>3?"...":""}`,lastSeenHash:cr(l.body),updatedAt:new Date().toISOString()};let p=Rt(l);return o(s,p),{content:[{type:"text",text:JSON.stringify({memo:d,gatesUpdated:l.gates.length,cursor:l.cursor},null,2)}]}}catch(u){return{content:[{type:"text",text:JSON.stringify({error:u instanceof Error?u.message:String(u)})}],isError:!0}}})),t.tool("update_cursor",'Update the plan cursor position in a markdown file. The cursor tracks "where we are" in a plan. Only one cursor per document.',{file:R.string().describe("Path to the annotated markdown file"),taskId:R.string().describe("Current task ID"),step:R.string().describe('Current step (e.g., "3/7" or "Phase 2")'),nextAction:R.string().describe("Description of the next action to take")},async({file:s,taskId:i,step:a,nextAction:c})=>vt(s,async()=>{try{let u=n(s),l=Oe(u);if(l.memos.length>0&&!l.memos.some(h=>h.id===i))return{content:[{type:"text",text:JSON.stringify({error:`Task ID not found: "${i}". Valid IDs: ${l.memos.map(h=>h.id).join(", ")}`})}],isError:!0};l.cursor={taskId:i,step:a,nextAction:c,lastSeenHash:cr(l.body),updatedAt:new Date().toISOString()};let d=Rt(l);return o(s,d),{content:[{type:"text",text:JSON.stringify({cursor:l.cursor},null,2)}]}}catch(u){return{content:[{type:"text",text:JSON.stringify({error:u instanceof Error?u.message:String(u)})}],isError:!0}}})),t.tool("evaluate_gates","Evaluate all gates in a markdown file against current memo statuses. Returns updated gate statuses without modifying the file.",{file:R.string().describe("Path to the annotated markdown file")},async({file:s})=>{try{let i=n(s),a=Oe(i),c=It(a.gates,a.memos);return{content:[{type:"text",text:JSON.stringify({gates:c,summary:{total:c.length,blocked:c.filter(u=>u.status==="blocked").length,proceed:c.filter(u=>u.status==="proceed").length,done:c.filter(u=>u.status==="done").length}},null,2)}]}}catch(i){return{content:[{type:"text",text:JSON.stringify({error:i instanceof Error?i.message:String(i)})}],isError:!0}}}),t.tool("export_review","Export review feedback in a format optimized for a specific AI coding tool. Targets: claude-code, cursor, codex, copilot, cline, windsurf, roo-code, gemini, generic, handoff. Returns formatted markdown ready to save to the appropriate file.",{file:R.string().describe("Path to the annotated markdown file"),target:R.enum(["claude-code","cursor","codex","copilot","cline","windsurf","roo-code","gemini","antigravity","generic","handoff"]).describe("Target AI tool format")},async({file:s,target:i})=>{try{let a=n(s);if(i==="handoff"){let p=_d(a,s);return{content:[{type:"text",text:vd(p,"standalone")}]}}let{memos:c}=hd(a),u=zn(a),l=u[0]||"Plan Review",d={red:"#fca5a5",blue:"#93c5fd",yellow:"#fef08a"},h=c.map(p=>({text:p.anchorText||"",color:d[p.color]||"#fef08a",section:"",context:""})),f=c.map(p=>({id:p.id,text:p.text,color:p.color,section:"",context:p.anchorText||""}));return{content:[{type:"text",text:$y(l,s,u,h,f,i)}]}}catch(a){return{content:[{type:"text",text:JSON.stringify({error:a instanceof Error?a.message:String(a)})}],isError:!0}}}),t.tool("apply_memo","Apply an implementation action to a memo. Supports text_replace (replaces all occurrences in current document), file_patch (overwrites target file \u2014 snapshot saved first), and file_create (create a new file). Creates a snapshot before modification, records the implementation, and updates memo status to done.",{file:R.string().describe("Path to the annotated markdown file"),memoId:R.string().describe("The memo ID to apply implementation to"),action:R.enum(["text_replace","file_patch","file_create"]).describe("Type of implementation action"),dryRun:R.boolean().optional().default(!1).describe("If true, return preview without writing"),oldText:R.string().optional().describe("For text_replace: the text to find and replace"),newText:R.string().optional().describe("For text_replace: the replacement text"),targetFile:R.string().optional().describe("For file_patch/file_create: target file path"),patch:R.string().optional().describe("For file_patch: the patch content"),content:R.string().optional().describe("For file_create: the file content to write")},async({file:s,memoId:i,action:a,dryRun:c,oldText:u,newText:l,targetFile:d,patch:h,content:f})=>vt(s,async()=>{try{let m=n(s),p=Oe(m),g=p.memos.find(F=>F.id===i);if(!g)return{content:[{type:"text",text:JSON.stringify({error:`Memo not found: ${i}`})}],isError:!0};let y;if(a==="text_replace"){if(!u||l===void 0)return{content:[{type:"text",text:JSON.stringify({error:"text_replace requires oldText and newText"})}],isError:!0};y={type:"text_replace",file:"",before:u,after:l}}else if(a==="file_patch"){if(!d||!h)return{content:[{type:"text",text:JSON.stringify({error:"file_patch requires targetFile and patch"})}],isError:!0};y={type:"file_patch",file:d,patch:h}}else{if(!d||f===void 0)return{content:[{type:"text",text:JSON.stringify({error:"file_create requires targetFile and content"})}],isError:!0};y={type:"file_create",file:d,content:f}}let v={id:`impl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,6)}`,memoId:i,status:"applied",operations:[y],summary:`${a} for ${i}`,appliedAt:new Date().toISOString()};if(c)return{content:[{type:"text",text:JSON.stringify({dryRun:!0,impl:v,operation:y,memo:{id:g.id,status:g.status}},null,2)}]};if(Mo(s,m),a==="text_replace"){if(!p.body.includes(u))return{content:[{type:"text",text:JSON.stringify({error:"oldText not found in document body"})}],isError:!0};p.body=p.body.split(u).join(l)}else a==="file_patch"?((0,$d.existsSync)(d)&&Mo(d,Ri(d)),o(d,h)):a==="file_create"&&o(d,f);p.impls.push(v),g.status="needs_review",g.updatedAt=new Date().toISOString(),p.gates.length>0&&(p.gates=It(p.gates,p.memos));let w=p.memos.filter(F=>Le(F.status)).length,b=p.memos.filter(F=>!Le(F.status));p.cursor={taskId:i,step:`${w}/${p.memos.length} resolved`,nextAction:b.length===0?"All annotations resolved \u2014 review complete":`Resolve: ${b.map(F=>F.id).slice(0,3).join(", ")}${b.length>3?"...":""}`,lastSeenHash:cr(p.body),updatedAt:new Date().toISOString()};let $=Rt(p);return o(s,$),{content:[{type:"text",text:JSON.stringify({impl:v,memo:{id:g.id,status:g.status},gatesUpdated:p.gates.length},null,2)}]}}catch(m){return{content:[{type:"text",text:JSON.stringify({error:m instanceof Error?m.message:String(m)})}],isError:!0}}})),t.tool("link_artifacts","Link file artifacts (source files, configs, etc.) to a memo. Creates a MemoArtifact record in the document.",{file:R.string().describe("Path to the annotated markdown file"),memoId:R.string().describe("The memo ID to link artifacts to"),files:R.array(R.string()).describe("Array of relative file paths to link")},async({file:s,memoId:i,files:a})=>vt(s,async()=>{try{let c=n(s),u=Oe(c);if(!u.memos.find(f=>f.id===i))return{content:[{type:"text",text:JSON.stringify({error:`Memo not found: ${i}`})}],isError:!0};let d={id:`art_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,6)}`,memoId:i,files:a,linkedAt:new Date().toISOString()};u.artifacts.push(d);let h=Rt(u);return o(s,h),{content:[{type:"text",text:JSON.stringify({artifact:d},null,2)}]}}catch(c){return{content:[{type:"text",text:JSON.stringify({error:c instanceof Error?c.message:String(c)})}],isError:!0}}})),t.tool("update_memo_progress","Update the progress of a memo with a status change and message. Writes progress to .md-feedback/progress.json and updates the memo status.",{file:R.string().describe("Path to the annotated markdown file"),memoId:R.string().describe("The memo ID to update progress for"),status:R.enum(["in_progress","needs_review","done","failed"]).describe("New progress status"),message:R.string().describe("Progress message describing what was done or what failed")},async({file:s,memoId:i,status:a,message:c})=>vt(s,async()=>{try{let u=n(s),l=Oe(u),d=l.memos.find(g=>g.id===i);if(!d)return{content:[{type:"text",text:JSON.stringify({error:`Memo not found: ${i}`})}],isError:!0};d.status=a,d.updatedAt=new Date().toISOString();let h={memoId:i,status:a,message:c,timestamp:new Date().toISOString()};vy(s,h),l.gates.length>0&&(l.gates=It(l.gates,l.memos));let f=l.memos.filter(g=>Le(g.status)).length,m=l.memos.filter(g=>!Le(g.status));l.cursor={taskId:i,step:`${f}/${l.memos.length} resolved`,nextAction:m.length===0?"All annotations resolved \u2014 review complete":`Resolve: ${m.map(g=>g.id).slice(0,3).join(", ")}${m.length>3?"...":""}`,lastSeenHash:cr(l.body),updatedAt:new Date().toISOString()};let p=Rt(l);return o(s,p),{content:[{type:"text",text:JSON.stringify({memo:{id:d.id,status:d.status},progressEntry:h,gatesUpdated:l.gates.length},null,2)}]}}catch(u){return{content:[{type:"text",text:JSON.stringify({error:u instanceof Error?u.message:String(u)})}],isError:!0}}})),t.tool("rollback_memo","Rollback the latest implementation for a memo. Reverses text_replace operations (swaps before/after), marks the impl as reverted, and sets the memo status back to open.",{file:R.string().describe("Path to the annotated markdown file"),memoId:R.string().describe("The memo ID to rollback")},async({file:s,memoId:i})=>vt(s,async()=>{try{let a=n(s),c=Oe(a),u=c.memos.find(p=>p.id===i);if(!u)return{content:[{type:"text",text:JSON.stringify({error:`Memo not found: ${i}`})}],isError:!0};let l=c.impls.filter(p=>p.memoId===i&&p.status==="applied");if(l.length===0)return{content:[{type:"text",text:JSON.stringify({error:`No applied implementation found for memo: ${i}`})}],isError:!0};let d=l[l.length-1];for(let p of d.operations)p.type==="text_replace"&&c.body.includes(p.after)&&(c.body=c.body.split(p.after).join(p.before));d.status="reverted",u.status="open",u.updatedAt=new Date().toISOString(),c.gates.length>0&&(c.gates=It(c.gates,c.memos));let h=c.memos.filter(p=>Le(p.status)).length,f=c.memos.filter(p=>!Le(p.status));c.cursor={taskId:i,step:`${h}/${c.memos.length} resolved`,nextAction:f.length===0?"All annotations resolved \u2014 review complete":`Resolve: ${f.map(p=>p.id).slice(0,3).join(", ")}${f.length>3?"...":""}`,lastSeenHash:cr(c.body),updatedAt:new Date().toISOString()};let m=Rt(c);return o(s,m),{content:[{type:"text",text:JSON.stringify({rolledBack:d.id,memo:{id:u.id,status:u.status},gatesUpdated:c.gates.length},null,2)}]}}catch(a){return{content:[{type:"text",text:JSON.stringify({error:a instanceof Error?a.message:String(a)})}],isError:!0}}})),t.tool("batch_apply","Apply multiple implementation operations in a single transaction. Parses the document once, applies all operations sequentially, then writes once. Each operation follows the same format as apply_memo.",{file:R.string().describe("Path to the annotated markdown file"),operations:R.array(R.object({memoId:R.string().describe("The memo ID to apply implementation to"),action:R.enum(["text_replace","file_patch","file_create"]).describe("Type of implementation action"),oldText:R.string().optional().describe("For text_replace: the text to find and replace"),newText:R.string().optional().describe("For text_replace: the replacement text"),targetFile:R.string().optional().describe("For file_patch/file_create: target file path"),patch:R.string().optional().describe("For file_patch: the patch content"),content:R.string().optional().describe("For file_create: the file content to write")})).describe("Array of operations to apply")},async({file:s,operations:i})=>vt(s,async()=>{try{let a=n(s),c=Oe(a);Mo(s,a);let u=[];for(let f of i){let m=c.memos.find(y=>y.id===f.memoId);if(!m){u.push({memoId:f.memoId,implId:"",status:"error: memo not found"});continue}let p;if(f.action==="text_replace"){if(!f.oldText||f.newText===void 0){u.push({memoId:f.memoId,implId:"",status:"error: text_replace requires oldText and newText"});continue}if(p={type:"text_replace",file:"",before:f.oldText,after:f.newText},!c.body.includes(f.oldText)){u.push({memoId:f.memoId,implId:"",status:"error: oldText not found in body"});continue}c.body=c.body.split(f.oldText).join(f.newText)}else if(f.action==="file_patch"){if(!f.targetFile||!f.patch){u.push({memoId:f.memoId,implId:"",status:"error: file_patch requires targetFile and patch"});continue}p={type:"file_patch",file:f.targetFile,patch:f.patch},(0,$d.existsSync)(f.targetFile)&&Mo(f.targetFile,Ri(f.targetFile)),o(f.targetFile,f.patch)}else{if(!f.targetFile||f.content===void 0){u.push({memoId:f.memoId,implId:"",status:"error: file_create requires targetFile and content"});continue}p={type:"file_create",file:f.targetFile,content:f.content},o(f.targetFile,f.content)}let g={id:`impl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,6)}`,memoId:f.memoId,status:"applied",operations:[p],summary:`${f.action} for ${f.memoId}`,appliedAt:new Date().toISOString()};c.impls.push(g),m.status="needs_review",m.updatedAt=new Date().toISOString(),u.push({memoId:f.memoId,implId:g.id,status:"applied"})}c.gates.length>0&&(c.gates=It(c.gates,c.memos));let l=c.memos.filter(f=>Le(f.status)).length,d=c.memos.filter(f=>!Le(f.status));c.cursor={taskId:i[0]?.memoId||"",step:`${l}/${c.memos.length} resolved`,nextAction:d.length===0?"All annotations resolved \u2014 review complete":`Resolve: ${d.map(f=>f.id).slice(0,3).join(", ")}${d.length>3?"...":""}`,lastSeenHash:cr(c.body),updatedAt:new Date().toISOString()},xy(s,{type:"batch_apply",results:u,timestamp:new Date().toISOString()});let h=Rt(c);return o(s,h),{content:[{type:"text",text:JSON.stringify({results:u,gatesUpdated:c.gates.length,cursor:c.cursor},null,2)}]}}catch(a){return{content:[{type:"text",text:JSON.stringify({error:a instanceof Error?a.message:String(a)})}],isError:!0}}})),t.tool("get_memo_changes","Get the implementation history and progress for a memo. Returns all MemoImpl records and progress entries from .md-feedback/progress.json. If memoId is omitted, returns all changes.",{file:R.string().describe("Path to the annotated markdown file"),memoId:R.string().optional().describe("Optional memo ID to filter by \u2014 if omitted, returns all changes")},async({file:s,memoId:i})=>{try{let a=n(s),c=Oe(a),u=i?c.impls.filter(h=>h.memoId===i):c.impls,l=fd(s),d=i?l.filter(h=>h.memoId===i):l;return{content:[{type:"text",text:JSON.stringify({impls:u,progress:d},null,2)}]}}catch(a){return{content:[{type:"text",text:JSON.stringify({error:a instanceof Error?a.message:String(a)})}],isError:!0}}})}function wd(t){process.stderr.write(`[md-feedback] ${t}
|
|
80
|
-
`)}function wE(){let t=process.argv.find(e=>e.startsWith("--workspace="));return t?t.split("=")[1]:process.env.MD_FEEDBACK_WORKSPACE||void 0}var My=wE(),Cy=new Ti({name:"md-feedback",version:"1.3.
|
|
80
|
+
`)}function wE(){let t=process.argv.find(e=>e.startsWith("--workspace="));return t?t.split("=")[1]:process.env.MD_FEEDBACK_WORKSPACE||void 0}var My=wE(),Cy=new Ti({name:"md-feedback",version:"1.3.2"});Oy(Cy,My);async function kE(){let t=new Pi;await Cy.connect(t);let e=My||process.cwd();wd(`v1.3.2 ready (stdio) workspace=${e}`)}kE().catch(t=>{wd(`fatal: ${t}`),process.exit(1)});0&&(module.exports={log});
|
package/package.json
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "md-feedback",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "MCP server for markdown plan review — AI agents read annotations, mark tasks done, evaluate quality gates, and generate session handoffs. 19 tools for Claude Code, Cursor, Copilot, and 8 more AI tools.",
|
|
5
|
-
"license": "SUL-1.0",
|
|
6
|
-
"author": "Yeomin Seon",
|
|
7
|
-
"type": "commonjs",
|
|
8
|
-
"bin": {
|
|
9
|
-
"md-feedback": "./bin/md-feedback.cjs"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist/mcp-server.js",
|
|
13
|
-
"bin/md-feedback.cjs",
|
|
14
|
-
"README.md"
|
|
15
|
-
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "node esbuild.mjs"
|
|
18
|
-
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
21
|
-
"zod": "^3.23.0"
|
|
22
|
-
},
|
|
23
|
-
"devDependencies": {
|
|
24
|
-
"esbuild": "^0.24.2",
|
|
25
|
-
"typescript": "^5.7.0"
|
|
26
|
-
},
|
|
27
|
-
"engines": {
|
|
28
|
-
"node": ">=18"
|
|
29
|
-
},
|
|
30
|
-
"repository": {
|
|
31
|
-
"type": "git",
|
|
32
|
-
"url": "https://github.com/yeominux/md-feedback"
|
|
33
|
-
},
|
|
34
|
-
"bugs": {
|
|
35
|
-
"url": "https://github.com/yeominux/md-feedback/issues"
|
|
36
|
-
},
|
|
37
|
-
"homepage": "https://github.com/yeominux/md-feedback#mcp-server",
|
|
38
|
-
"keywords": [
|
|
39
|
-
"mcp",
|
|
40
|
-
"mcp-server",
|
|
41
|
-
"model-context-protocol",
|
|
42
|
-
"markdown",
|
|
43
|
-
"feedback",
|
|
44
|
-
"ai",
|
|
45
|
-
"annotation",
|
|
46
|
-
"review",
|
|
47
|
-
"plan-review",
|
|
48
|
-
"ai-agent",
|
|
49
|
-
"coding-workflow",
|
|
50
|
-
"handoff",
|
|
51
|
-
"session-handoff",
|
|
52
|
-
"structured-feedback",
|
|
53
|
-
"checkpoint",
|
|
54
|
-
"ai-context",
|
|
55
|
-
"ai-coding",
|
|
56
|
-
"claude-code",
|
|
57
|
-
"cursor-ai",
|
|
58
|
-
"vibe-coding",
|
|
59
|
-
"context-engineering",
|
|
60
|
-
"gates",
|
|
61
|
-
"plan-review-tool",
|
|
62
|
-
"document-annotation",
|
|
63
|
-
"code-review",
|
|
64
|
-
"ai-workflow",
|
|
65
|
-
"copilot",
|
|
66
|
-
"markdown-review",
|
|
67
|
-
"developer-tools",
|
|
68
|
-
"quality-gate",
|
|
69
|
-
"file-safety",
|
|
70
|
-
"concurrent-safety",
|
|
71
|
-
"gate-override"
|
|
72
|
-
]
|
|
73
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "md-feedback",
|
|
3
|
+
"version": "1.3.2",
|
|
4
|
+
"description": "MCP server for markdown plan review — AI agents read annotations, mark tasks done, evaluate quality gates, and generate session handoffs. 19 tools for Claude Code, Cursor, Copilot, and 8 more AI tools.",
|
|
5
|
+
"license": "SUL-1.0",
|
|
6
|
+
"author": "Yeomin Seon",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"bin": {
|
|
9
|
+
"md-feedback": "./bin/md-feedback.cjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/mcp-server.js",
|
|
13
|
+
"bin/md-feedback.cjs",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node esbuild.mjs"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
21
|
+
"zod": "^3.23.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"esbuild": "^0.24.2",
|
|
25
|
+
"typescript": "^5.7.0"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/yeominux/md-feedback"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/yeominux/md-feedback/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/yeominux/md-feedback#mcp-server",
|
|
38
|
+
"keywords": [
|
|
39
|
+
"mcp",
|
|
40
|
+
"mcp-server",
|
|
41
|
+
"model-context-protocol",
|
|
42
|
+
"markdown",
|
|
43
|
+
"feedback",
|
|
44
|
+
"ai",
|
|
45
|
+
"annotation",
|
|
46
|
+
"review",
|
|
47
|
+
"plan-review",
|
|
48
|
+
"ai-agent",
|
|
49
|
+
"coding-workflow",
|
|
50
|
+
"handoff",
|
|
51
|
+
"session-handoff",
|
|
52
|
+
"structured-feedback",
|
|
53
|
+
"checkpoint",
|
|
54
|
+
"ai-context",
|
|
55
|
+
"ai-coding",
|
|
56
|
+
"claude-code",
|
|
57
|
+
"cursor-ai",
|
|
58
|
+
"vibe-coding",
|
|
59
|
+
"context-engineering",
|
|
60
|
+
"gates",
|
|
61
|
+
"plan-review-tool",
|
|
62
|
+
"document-annotation",
|
|
63
|
+
"code-review",
|
|
64
|
+
"ai-workflow",
|
|
65
|
+
"copilot",
|
|
66
|
+
"markdown-review",
|
|
67
|
+
"developer-tools",
|
|
68
|
+
"quality-gate",
|
|
69
|
+
"file-safety",
|
|
70
|
+
"concurrent-safety",
|
|
71
|
+
"gate-override"
|
|
72
|
+
]
|
|
73
|
+
}
|