jiranimo 1.7.0 → 1.9.0
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/.build-meta.json +1 -1
- package/dist/index.js +24 -11
- package/package.json +1 -1
package/dist/.build-meta.json
CHANGED
package/dist/index.js
CHANGED
|
@@ -288,17 +288,23 @@ ${l}
|
|
|
288
288
|
|
|
289
289
|
The repository is at: \`${r}\`
|
|
290
290
|
|
|
291
|
-
**Step 1 -
|
|
291
|
+
**Step 1 - Fetch latest remote state** (required before branch creation):
|
|
292
|
+
\`\`\`bash
|
|
293
|
+
git -C ${r} fetch origin
|
|
292
294
|
\`\`\`
|
|
295
|
+
|
|
296
|
+
**Step 2 - Create a worktree** for exploration:
|
|
297
|
+
\`\`\`bash
|
|
293
298
|
git -C ${r} worktree add ${d} -b ${a}${t.key}-plan origin/${s}
|
|
294
299
|
cd ${d}
|
|
300
|
+
pwd # must show ${d} \u2014 abort if it does not
|
|
295
301
|
\`\`\`
|
|
296
302
|
If \`origin/${s}\` does not exist, detect the actual default branch first:
|
|
297
303
|
\`git -C ${r} remote show ${c} | grep 'HEAD branch'\`
|
|
298
304
|
|
|
299
|
-
**Step
|
|
305
|
+
**Step 3 - Write the plan** to \`${yy(t.key)}\` \u2014 do NOT commit this file or anything else.
|
|
300
306
|
|
|
301
|
-
**Step
|
|
307
|
+
**Step 4 - Report back using the jiranimo MCP tools** (available as \`jiranimo_*\`):
|
|
302
308
|
- \`jiranimo_progress\` \u2014 send progress updates as you work (task_key="${t.key}")
|
|
303
309
|
- \`jiranimo_complete\` \u2014 when the plan file is written (task_key="${t.key}")
|
|
304
310
|
- \`jiranimo_fail\` \u2014 if you hit an unrecoverable error (task_key="${t.key}")
|
|
@@ -319,19 +325,26 @@ ${f}
|
|
|
319
325
|
|
|
320
326
|
The repository you should work on is at: \`${r}\`
|
|
321
327
|
|
|
322
|
-
**Step 1 -
|
|
328
|
+
**Step 1 - Fetch latest remote state** (required before branch creation):
|
|
329
|
+
\`\`\`bash
|
|
330
|
+
git -C ${r} fetch origin
|
|
323
331
|
\`\`\`
|
|
332
|
+
|
|
333
|
+
**Step 2 - Create a worktree** for isolation:
|
|
334
|
+
\`\`\`bash
|
|
324
335
|
if [ ! -d "${d}" ]; then
|
|
325
336
|
git -C ${r} worktree add ${d} -b ${a}${t.key}-<short-slug> origin/${s}
|
|
326
337
|
fi
|
|
327
338
|
cd ${d}
|
|
339
|
+
pwd # must show ${d} \u2014 if it does not, STOP and call jiranimo_fail
|
|
340
|
+
git branch --show-current # must NOT be master or main \u2014 if it is, STOP and call jiranimo_fail
|
|
328
341
|
\`\`\`
|
|
329
342
|
If \`origin/${s}\` does not exist, detect the actual default branch first:
|
|
330
343
|
\`git -C ${r} remote show ${c} | grep 'HEAD branch'\`
|
|
331
344
|
|
|
332
|
-
**Step
|
|
345
|
+
**Step 3 - Implement** the changes described above. Write or update tests as appropriate and ensure they pass.
|
|
333
346
|
|
|
334
|
-
**Step
|
|
347
|
+
**Step 4 - Commit and push**:
|
|
335
348
|
\`\`\`
|
|
336
349
|
git add -A
|
|
337
350
|
git commit -m "<type>(${t.key}): <short description>"
|
|
@@ -339,7 +352,7 @@ git push -u ${c} <branch-name>
|
|
|
339
352
|
\`\`\`
|
|
340
353
|
Use conventional commit types: \`feat\` for features, \`fix\` for bugs, \`chore\` for other work.
|
|
341
354
|
|
|
342
|
-
**Step
|
|
355
|
+
**Step 5 \u2014 Screenshot (frontend tasks only)**
|
|
343
356
|
If your implementation touches any UI files (HTML, CSS, frontend JS, browser extension files), take a screenshot of the real running feature.
|
|
344
357
|
|
|
345
358
|
Think of yourself as a developer who just finished implementing this feature and wants to show it working. How would you demo it to a colleague? Do that \u2014 use the real app, the real dev server, the real test suite.
|
|
@@ -359,7 +372,7 @@ Call \`jiranimo_screenshot_failed\` only if you genuinely tried both approaches
|
|
|
359
372
|
|
|
360
373
|
If your changes are server-only, skip this step.
|
|
361
374
|
|
|
362
|
-
**Step
|
|
375
|
+
**Step 6 \u2014 Upload the screenshot and create a PR**:
|
|
363
376
|
If a screenshot was taken, call \`jiranimo_upload_screenshot\` with \`file_path="/tmp/jiranimo-${t.key}-screenshot.png"\`. It returns a URL \u2014 use it in the PR body as \`\`.
|
|
364
377
|
|
|
365
378
|
\`\`\`bash
|
|
@@ -367,10 +380,10 @@ gh pr create ${u?"--draft ":""}--title "#${t.key} ${t.summary}" --body "Implemen
|
|
|
367
380
|
# append screenshot line here if upload succeeded: "
|
|
368
381
|
\`\`\`
|
|
369
382
|
|
|
370
|
-
**Step
|
|
383
|
+
**Step 6b \u2014 Verify the screenshot is in the PR body** (required if a screenshot was uploaded):
|
|
371
384
|
Run \`gh pr view --json body --jq '.body'\` and confirm it contains the screenshot URL. If not, run \`gh pr edit\` to add it.
|
|
372
385
|
|
|
373
|
-
**Step
|
|
386
|
+
**Step 7 - Report back using the jiranimo MCP tools** (available as \`jiranimo_*\`):
|
|
374
387
|
- \`jiranimo_progress\` \u2014 send progress updates as you work (task_key="${t.key}")
|
|
375
388
|
- \`jiranimo_report_pr\` \u2014 once the PR is created, report its url, number, and branch name (task_key="${t.key}")
|
|
376
389
|
- \`jiranimo_complete\` \u2014 when all work is done (task_key="${t.key}")
|
|
@@ -434,7 +447,7 @@ data:
|
|
|
434
447
|
`;return n&&(o+=`id: ${n}
|
|
435
448
|
`),o+=`data: ${JSON.stringify(i)}
|
|
436
449
|
|
|
437
|
-
`,e.enqueue(r.encode(o)),!0}catch(o){return this.onerror?.(o),!1}}handleUnsupportedRequest(){return this.onerror?.(new Error("Method not allowed.")),new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}),{status:405,headers:{Allow:"GET, POST, DELETE","Content-Type":"application/json"}})}async handlePostRequest(e,r){try{let i=e.headers.get("accept");if(!i?.includes("application/json")||!i.includes("text/event-stream"))return this.onerror?.(new Error("Not Acceptable: Client must accept both application/json and text/event-stream")),this.createJsonErrorResponse(406,-32e3,"Not Acceptable: Client must accept both application/json and text/event-stream");let n=e.headers.get("content-type");if(!n||!n.includes("application/json"))return this.onerror?.(new Error("Unsupported Media Type: Content-Type must be application/json")),this.createJsonErrorResponse(415,-32e3,"Unsupported Media Type: Content-Type must be application/json");let o={headers:Object.fromEntries(e.headers.entries()),url:new URL(e.url)},a;if(r?.parsedBody!==void 0)a=r.parsedBody;else try{a=await e.json()}catch{return this.onerror?.(new Error("Parse error: Invalid JSON")),this.createJsonErrorResponse(400,-32700,"Parse error: Invalid JSON")}let s;try{Array.isArray(a)?s=a.map(x=>Cy.parse(x)):s=[Cy.parse(a)]}catch{return this.onerror?.(new Error("Parse error: Invalid JSON-RPC message")),this.createJsonErrorResponse(400,-32700,"Parse error: Invalid JSON-RPC message")}let c=s.some(Ay);if(c){if(this._initialized&&this.sessionId!==void 0)return this.onerror?.(new Error("Invalid Request: Server already initialized")),this.createJsonErrorResponse(400,-32600,"Invalid Request: Server already initialized");if(s.length>1)return this.onerror?.(new Error("Invalid Request: Only one initialization request is allowed")),this.createJsonErrorResponse(400,-32600,"Invalid Request: Only one initialization request is allowed");this.sessionId=this.sessionIdGenerator?.(),this._initialized=!0,this.sessionId&&this._onsessioninitialized&&await Promise.resolve(this._onsessioninitialized(this.sessionId))}if(!c){let x=this.validateSession(e);if(x)return x;let $=this.validateProtocolVersion(e);if($)return $}if(!s.some(zn)){for(let x of s)this.onmessage?.(x,{authInfo:r?.authInfo,requestInfo:o});return new Response(null,{status:202})}let l=crypto.randomUUID(),p=s.find(x=>Ay(x)),d=p?p.params.protocolVersion:e.headers.get("mcp-protocol-version")??hS;if(this._enableJsonResponse)return new Promise(x=>{this._streamMapping.set(l,{resolveJson:x,cleanup:()=>{this._streamMapping.delete(l)}});for(let $ of s)zn($)&&this._requestToStreamMapping.set($.id,l);for(let $ of s)this.onmessage?.($,{authInfo:r?.authInfo,requestInfo:o})});let f=new TextEncoder,m,h=new ReadableStream({start:x=>{m=x},cancel:()=>{this._streamMapping.delete(l)}}),g={"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"};this.sessionId!==void 0&&(g["mcp-session-id"]=this.sessionId);for(let x of s)zn(x)&&(this._streamMapping.set(l,{controller:m,encoder:f,cleanup:()=>{this._streamMapping.delete(l);try{m.close()}catch{}}}),this._requestToStreamMapping.set(x.id,l));await this.writePrimingEvent(m,f,l,d);for(let x of s){let $,w;zn(x)&&this._eventStore&&d>="2025-11-25"&&($=()=>{this.closeSSEStream(x.id)},w=()=>{this.closeStandaloneSSEStream()}),this.onmessage?.(x,{authInfo:r?.authInfo,requestInfo:o,closeSSEStream:$,closeStandaloneSSEStream:w})}return new Response(h,{status:200,headers:g})}catch(i){return this.onerror?.(i),this.createJsonErrorResponse(400,-32700,"Parse error",{data:String(i)})}}async handleDeleteRequest(e){let r=this.validateSession(e);if(r)return r;let i=this.validateProtocolVersion(e);return i||(await Promise.resolve(this._onsessionclosed?.(this.sessionId)),await this.close(),new Response(null,{status:200}))}validateSession(e){if(this.sessionIdGenerator===void 0)return;if(!this._initialized)return this.onerror?.(new Error("Bad Request: Server not initialized")),this.createJsonErrorResponse(400,-32e3,"Bad Request: Server not initialized");let r=e.headers.get("mcp-session-id");if(!r)return this.onerror?.(new Error("Bad Request: Mcp-Session-Id header is required")),this.createJsonErrorResponse(400,-32e3,"Bad Request: Mcp-Session-Id header is required");if(r!==this.sessionId)return this.onerror?.(new Error("Session not found")),this.createJsonErrorResponse(404,-32001,"Session not found")}validateProtocolVersion(e){let r=e.headers.get("mcp-protocol-version");if(r!==null&&!Do.includes(r))return this.onerror?.(new Error(`Bad Request: Unsupported protocol version: ${r} (supported versions: ${Do.join(", ")})`)),this.createJsonErrorResponse(400,-32e3,`Bad Request: Unsupported protocol version: ${r} (supported versions: ${Do.join(", ")})`)}async close(){this._streamMapping.forEach(({cleanup:e})=>{e()}),this._streamMapping.clear(),this._requestResponseMap.clear(),this.onclose?.()}closeSSEStream(e){let r=this._requestToStreamMapping.get(e);if(!r)return;let i=this._streamMapping.get(r);i&&i.cleanup()}closeStandaloneSSEStream(){let e=this._streamMapping.get(this._standaloneSseStreamId);e&&e.cleanup()}async send(e,r){let i=r?.relatedRequestId;if((Vr(e)||Uo(e))&&(i=e.id),i===void 0){if(Vr(e)||Uo(e))throw new Error("Cannot send a response on a standalone SSE stream unless resuming a previous client request");let a;this._eventStore&&(a=await this._eventStore.storeEvent(this._standaloneSseStreamId,e));let s=this._streamMapping.get(this._standaloneSseStreamId);if(s===void 0)return;s.controller&&s.encoder&&this.writeSSEEvent(s.controller,s.encoder,e,a);return}let n=this._requestToStreamMapping.get(i);if(!n)throw new Error(`No connection established for request ID: ${String(i)}`);let o=this._streamMapping.get(n);if(!this._enableJsonResponse&&o?.controller&&o?.encoder){let a;this._eventStore&&(a=await this._eventStore.storeEvent(n,e)),this.writeSSEEvent(o.controller,o.encoder,e,a)}if(Vr(e)||Uo(e)){this._requestResponseMap.set(i,e);let a=Array.from(this._requestToStreamMapping.entries()).filter(([c,u])=>u===n).map(([c])=>c);if(a.every(c=>this._requestResponseMap.has(c))){if(!o)throw new Error(`No connection established for request ID: ${String(i)}`);if(this._enableJsonResponse&&o.resolveJson){let c={"Content-Type":"application/json"};this.sessionId!==void 0&&(c["mcp-session-id"]=this.sessionId);let u=a.map(l=>this._requestResponseMap.get(l));u.length===1?o.resolveJson(new Response(JSON.stringify(u[0]),{status:200,headers:c})):o.resolveJson(new Response(JSON.stringify(u),{status:200,headers:c}))}else o.cleanup();for(let c of a)this._requestResponseMap.delete(c),this._requestToStreamMapping.delete(c)}}}};var Kf=class{constructor(e={}){this._requestContext=new WeakMap,this._webStandardTransport=new Wf(e),this._requestListener=e0(async r=>{let i=this._requestContext.get(r);return this._webStandardTransport.handleRequest(r,{authInfo:i?.authInfo,parsedBody:i?.parsedBody})},{overrideGlobalObjects:!1})}get sessionId(){return this._webStandardTransport.sessionId}set onclose(e){this._webStandardTransport.onclose=e}get onclose(){return this._webStandardTransport.onclose}set onerror(e){this._webStandardTransport.onerror=e}get onerror(){return this._webStandardTransport.onerror}set onmessage(e){this._webStandardTransport.onmessage=e}get onmessage(){return this._webStandardTransport.onmessage}async start(){return this._webStandardTransport.start()}async close(){return this._webStandardTransport.close()}async send(e,r){return this._webStandardTransport.send(e,r)}async handleRequest(e,r,i){let n=e.auth;await e0(async a=>this._webStandardTransport.handleRequest(a,{authInfo:n,parsedBody:i}),{overrideGlobalObjects:!1})(e,r)}closeSSEStream(e){this._webStandardTransport.closeSSEStream(e)}closeStandaloneSSEStream(){this._webStandardTransport.closeStandaloneSSEStream()}};import{writeFileSync as pZ,unlinkSync as dZ,readFileSync as fZ}from"node:fs";import{join as mI}from"node:path";var mZ="7922f663cfcfe9b4b0c3119c2b61b7d8";async function hZ(t){let r=fZ(t).toString("base64"),i=new URLSearchParams({key:mZ,image:r}),n=await fetch("https://api.imgbb.com/1/upload",{method:"POST",body:i});if(!n.ok)throw new Error(`imgbb upload failed: ${n.status} ${await n.text()}`);return(await n.json()).data.url}function hI(t){return async(e,r)=>{let i=new Vf({name:"jiranimo",version:"1.7.0"});i.tool("jiranimo_progress",{task_key:G.string(),message:G.string()},async({task_key:o,message:a})=>(t.reportProgress(o,a),{content:[{type:"text",text:"ok"}]})),i.tool("jiranimo_report_pr",{task_key:G.string(),pr_url:G.string(),pr_number:G.number().int(),branch_name:G.string()},async({task_key:o,pr_url:a,pr_number:s,branch_name:c})=>(t.reportPr(o,a,s,c),{content:[{type:"text",text:"PR recorded"}]})),i.tool("jiranimo_complete",{task_key:G.string(),summary:G.string()},async({task_key:o,summary:a})=>(t.completeViaAgent(o,a),{content:[{type:"text",text:"Task marked complete"}]})),i.tool("jiranimo_fail",{task_key:G.string(),error_message:G.string()},async({task_key:o,error_message:a})=>(t.failViaAgent(o,a),{content:[{type:"text",text:"Task marked failed"}]})),i.tool("jiranimo_screenshot_failed",{task_key:G.string(),reason:G.string()},async({task_key:o,reason:a})=>(t.reportScreenshotFailed(o,a),{content:[{type:"text",text:"Screenshot failure recorded"}]})),i.tool("jiranimo_upload_screenshot",{file_path:G.string()},async({file_path:o})=>({content:[{type:"text",text:await hZ(o)}]}));let n=new Kf({sessionIdGenerator:void 0});await i.connect(n),await n.handleRequest(e,r,e.body),r.on("close",()=>{n.close(),i.close()})}}function gI(t,e){let r={mcpServers:{jiranimo:{type:"http",url:`http://127.0.0.1:${e}/mcp`},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--headless"]}}};pZ(mI(t,".mcp.json"),JSON.stringify(r,null,2))}function vI(t){try{dZ(mI(t,".mcp.json"))}catch{}}import{appendFileSync as gZ,mkdirSync as vZ}from"node:fs";import{homedir as yZ}from"node:os";import{dirname as xZ,join as bZ,resolve as _Z}from"node:path";var wZ={level:"info",logHttpRequests:!1,logHttpBodies:!1,logClaudeRawOutput:!1},yI={debug:10,info:20,warn:30,error:40},t0=class t{config;filePath;scope;constructor(e,r){this.config=Lc(e);let i=e?.logsDir??_Z(yZ(),".jiranimo","logs");vZ(i,{recursive:!0}),this.filePath=bZ(i,"server.log"),this.scope=r}child(e){let r=this.scope?`${this.scope}:${e}`:e;return new t({logsDir:xZ(this.filePath),logging:this.config},r)}debug(e,r){this.write("debug",e,r)}info(e,r){this.write("info",e,r)}warn(e,r){this.write("warn",e,r)}error(e,r){this.write("error",e,r)}isConsoleLevelEnabled(e){return yI[e]>=yI[this.config.level]}getConfig(){return this.config}write(e,r,i){if(!this.isConsoleLevelEnabled(e))return;let n=kZ(e,this.scope,r,i),o=$Z(n);try{gZ(this.filePath,`${o}
|
|
450
|
+
`,e.enqueue(r.encode(o)),!0}catch(o){return this.onerror?.(o),!1}}handleUnsupportedRequest(){return this.onerror?.(new Error("Method not allowed.")),new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}),{status:405,headers:{Allow:"GET, POST, DELETE","Content-Type":"application/json"}})}async handlePostRequest(e,r){try{let i=e.headers.get("accept");if(!i?.includes("application/json")||!i.includes("text/event-stream"))return this.onerror?.(new Error("Not Acceptable: Client must accept both application/json and text/event-stream")),this.createJsonErrorResponse(406,-32e3,"Not Acceptable: Client must accept both application/json and text/event-stream");let n=e.headers.get("content-type");if(!n||!n.includes("application/json"))return this.onerror?.(new Error("Unsupported Media Type: Content-Type must be application/json")),this.createJsonErrorResponse(415,-32e3,"Unsupported Media Type: Content-Type must be application/json");let o={headers:Object.fromEntries(e.headers.entries()),url:new URL(e.url)},a;if(r?.parsedBody!==void 0)a=r.parsedBody;else try{a=await e.json()}catch{return this.onerror?.(new Error("Parse error: Invalid JSON")),this.createJsonErrorResponse(400,-32700,"Parse error: Invalid JSON")}let s;try{Array.isArray(a)?s=a.map(x=>Cy.parse(x)):s=[Cy.parse(a)]}catch{return this.onerror?.(new Error("Parse error: Invalid JSON-RPC message")),this.createJsonErrorResponse(400,-32700,"Parse error: Invalid JSON-RPC message")}let c=s.some(Ay);if(c){if(this._initialized&&this.sessionId!==void 0)return this.onerror?.(new Error("Invalid Request: Server already initialized")),this.createJsonErrorResponse(400,-32600,"Invalid Request: Server already initialized");if(s.length>1)return this.onerror?.(new Error("Invalid Request: Only one initialization request is allowed")),this.createJsonErrorResponse(400,-32600,"Invalid Request: Only one initialization request is allowed");this.sessionId=this.sessionIdGenerator?.(),this._initialized=!0,this.sessionId&&this._onsessioninitialized&&await Promise.resolve(this._onsessioninitialized(this.sessionId))}if(!c){let x=this.validateSession(e);if(x)return x;let $=this.validateProtocolVersion(e);if($)return $}if(!s.some(zn)){for(let x of s)this.onmessage?.(x,{authInfo:r?.authInfo,requestInfo:o});return new Response(null,{status:202})}let l=crypto.randomUUID(),p=s.find(x=>Ay(x)),d=p?p.params.protocolVersion:e.headers.get("mcp-protocol-version")??hS;if(this._enableJsonResponse)return new Promise(x=>{this._streamMapping.set(l,{resolveJson:x,cleanup:()=>{this._streamMapping.delete(l)}});for(let $ of s)zn($)&&this._requestToStreamMapping.set($.id,l);for(let $ of s)this.onmessage?.($,{authInfo:r?.authInfo,requestInfo:o})});let f=new TextEncoder,m,h=new ReadableStream({start:x=>{m=x},cancel:()=>{this._streamMapping.delete(l)}}),g={"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"};this.sessionId!==void 0&&(g["mcp-session-id"]=this.sessionId);for(let x of s)zn(x)&&(this._streamMapping.set(l,{controller:m,encoder:f,cleanup:()=>{this._streamMapping.delete(l);try{m.close()}catch{}}}),this._requestToStreamMapping.set(x.id,l));await this.writePrimingEvent(m,f,l,d);for(let x of s){let $,w;zn(x)&&this._eventStore&&d>="2025-11-25"&&($=()=>{this.closeSSEStream(x.id)},w=()=>{this.closeStandaloneSSEStream()}),this.onmessage?.(x,{authInfo:r?.authInfo,requestInfo:o,closeSSEStream:$,closeStandaloneSSEStream:w})}return new Response(h,{status:200,headers:g})}catch(i){return this.onerror?.(i),this.createJsonErrorResponse(400,-32700,"Parse error",{data:String(i)})}}async handleDeleteRequest(e){let r=this.validateSession(e);if(r)return r;let i=this.validateProtocolVersion(e);return i||(await Promise.resolve(this._onsessionclosed?.(this.sessionId)),await this.close(),new Response(null,{status:200}))}validateSession(e){if(this.sessionIdGenerator===void 0)return;if(!this._initialized)return this.onerror?.(new Error("Bad Request: Server not initialized")),this.createJsonErrorResponse(400,-32e3,"Bad Request: Server not initialized");let r=e.headers.get("mcp-session-id");if(!r)return this.onerror?.(new Error("Bad Request: Mcp-Session-Id header is required")),this.createJsonErrorResponse(400,-32e3,"Bad Request: Mcp-Session-Id header is required");if(r!==this.sessionId)return this.onerror?.(new Error("Session not found")),this.createJsonErrorResponse(404,-32001,"Session not found")}validateProtocolVersion(e){let r=e.headers.get("mcp-protocol-version");if(r!==null&&!Do.includes(r))return this.onerror?.(new Error(`Bad Request: Unsupported protocol version: ${r} (supported versions: ${Do.join(", ")})`)),this.createJsonErrorResponse(400,-32e3,`Bad Request: Unsupported protocol version: ${r} (supported versions: ${Do.join(", ")})`)}async close(){this._streamMapping.forEach(({cleanup:e})=>{e()}),this._streamMapping.clear(),this._requestResponseMap.clear(),this.onclose?.()}closeSSEStream(e){let r=this._requestToStreamMapping.get(e);if(!r)return;let i=this._streamMapping.get(r);i&&i.cleanup()}closeStandaloneSSEStream(){let e=this._streamMapping.get(this._standaloneSseStreamId);e&&e.cleanup()}async send(e,r){let i=r?.relatedRequestId;if((Vr(e)||Uo(e))&&(i=e.id),i===void 0){if(Vr(e)||Uo(e))throw new Error("Cannot send a response on a standalone SSE stream unless resuming a previous client request");let a;this._eventStore&&(a=await this._eventStore.storeEvent(this._standaloneSseStreamId,e));let s=this._streamMapping.get(this._standaloneSseStreamId);if(s===void 0)return;s.controller&&s.encoder&&this.writeSSEEvent(s.controller,s.encoder,e,a);return}let n=this._requestToStreamMapping.get(i);if(!n)throw new Error(`No connection established for request ID: ${String(i)}`);let o=this._streamMapping.get(n);if(!this._enableJsonResponse&&o?.controller&&o?.encoder){let a;this._eventStore&&(a=await this._eventStore.storeEvent(n,e)),this.writeSSEEvent(o.controller,o.encoder,e,a)}if(Vr(e)||Uo(e)){this._requestResponseMap.set(i,e);let a=Array.from(this._requestToStreamMapping.entries()).filter(([c,u])=>u===n).map(([c])=>c);if(a.every(c=>this._requestResponseMap.has(c))){if(!o)throw new Error(`No connection established for request ID: ${String(i)}`);if(this._enableJsonResponse&&o.resolveJson){let c={"Content-Type":"application/json"};this.sessionId!==void 0&&(c["mcp-session-id"]=this.sessionId);let u=a.map(l=>this._requestResponseMap.get(l));u.length===1?o.resolveJson(new Response(JSON.stringify(u[0]),{status:200,headers:c})):o.resolveJson(new Response(JSON.stringify(u),{status:200,headers:c}))}else o.cleanup();for(let c of a)this._requestResponseMap.delete(c),this._requestToStreamMapping.delete(c)}}}};var Kf=class{constructor(e={}){this._requestContext=new WeakMap,this._webStandardTransport=new Wf(e),this._requestListener=e0(async r=>{let i=this._requestContext.get(r);return this._webStandardTransport.handleRequest(r,{authInfo:i?.authInfo,parsedBody:i?.parsedBody})},{overrideGlobalObjects:!1})}get sessionId(){return this._webStandardTransport.sessionId}set onclose(e){this._webStandardTransport.onclose=e}get onclose(){return this._webStandardTransport.onclose}set onerror(e){this._webStandardTransport.onerror=e}get onerror(){return this._webStandardTransport.onerror}set onmessage(e){this._webStandardTransport.onmessage=e}get onmessage(){return this._webStandardTransport.onmessage}async start(){return this._webStandardTransport.start()}async close(){return this._webStandardTransport.close()}async send(e,r){return this._webStandardTransport.send(e,r)}async handleRequest(e,r,i){let n=e.auth;await e0(async a=>this._webStandardTransport.handleRequest(a,{authInfo:n,parsedBody:i}),{overrideGlobalObjects:!1})(e,r)}closeSSEStream(e){this._webStandardTransport.closeSSEStream(e)}closeStandaloneSSEStream(){this._webStandardTransport.closeStandaloneSSEStream()}};import{writeFileSync as pZ,unlinkSync as dZ,readFileSync as fZ}from"node:fs";import{join as mI}from"node:path";var mZ="7922f663cfcfe9b4b0c3119c2b61b7d8";async function hZ(t){let r=fZ(t).toString("base64"),i=new URLSearchParams({key:mZ,image:r}),n=await fetch("https://api.imgbb.com/1/upload",{method:"POST",body:i});if(!n.ok)throw new Error(`imgbb upload failed: ${n.status} ${await n.text()}`);return(await n.json()).data.url}function hI(t){return async(e,r)=>{let i=new Vf({name:"jiranimo",version:"1.9.0"});i.tool("jiranimo_progress",{task_key:G.string(),message:G.string()},async({task_key:o,message:a})=>(t.reportProgress(o,a),{content:[{type:"text",text:"ok"}]})),i.tool("jiranimo_report_pr",{task_key:G.string(),pr_url:G.string(),pr_number:G.number().int(),branch_name:G.string()},async({task_key:o,pr_url:a,pr_number:s,branch_name:c})=>(t.reportPr(o,a,s,c),{content:[{type:"text",text:"PR recorded"}]})),i.tool("jiranimo_complete",{task_key:G.string(),summary:G.string()},async({task_key:o,summary:a})=>(t.completeViaAgent(o,a),{content:[{type:"text",text:"Task marked complete"}]})),i.tool("jiranimo_fail",{task_key:G.string(),error_message:G.string()},async({task_key:o,error_message:a})=>(t.failViaAgent(o,a),{content:[{type:"text",text:"Task marked failed"}]})),i.tool("jiranimo_screenshot_failed",{task_key:G.string(),reason:G.string()},async({task_key:o,reason:a})=>(t.reportScreenshotFailed(o,a),{content:[{type:"text",text:"Screenshot failure recorded"}]})),i.tool("jiranimo_upload_screenshot",{file_path:G.string()},async({file_path:o})=>({content:[{type:"text",text:await hZ(o)}]}));let n=new Kf({sessionIdGenerator:void 0});await i.connect(n),await n.handleRequest(e,r,e.body),r.on("close",()=>{n.close(),i.close()})}}function gI(t,e){let r={mcpServers:{jiranimo:{type:"http",url:`http://127.0.0.1:${e}/mcp`},playwright:{type:"stdio",command:"npx",args:["@playwright/mcp@latest","--headless"]}}};pZ(mI(t,".mcp.json"),JSON.stringify(r,null,2))}function vI(t){try{dZ(mI(t,".mcp.json"))}catch{}}import{appendFileSync as gZ,mkdirSync as vZ}from"node:fs";import{homedir as yZ}from"node:os";import{dirname as xZ,join as bZ,resolve as _Z}from"node:path";var wZ={level:"info",logHttpRequests:!1,logHttpBodies:!1,logClaudeRawOutput:!1},yI={debug:10,info:20,warn:30,error:40},t0=class t{config;filePath;scope;constructor(e,r){this.config=Lc(e);let i=e?.logsDir??_Z(yZ(),".jiranimo","logs");vZ(i,{recursive:!0}),this.filePath=bZ(i,"server.log"),this.scope=r}child(e){let r=this.scope?`${this.scope}:${e}`:e;return new t({logsDir:xZ(this.filePath),logging:this.config},r)}debug(e,r){this.write("debug",e,r)}info(e,r){this.write("info",e,r)}warn(e,r){this.write("warn",e,r)}error(e,r){this.write("error",e,r)}isConsoleLevelEnabled(e){return yI[e]>=yI[this.config.level]}getConfig(){return this.config}write(e,r,i){if(!this.isConsoleLevelEnabled(e))return;let n=kZ(e,this.scope,r,i),o=$Z(n);try{gZ(this.filePath,`${o}
|
|
438
451
|
`,"utf-8")}catch{}if(e==="error"){console.error(n);return}if(e==="warn"){console.warn(n);return}console.log(n)}};function $Z(t){return t.replace(/\u001B\[[0-9;]*m/g,"")}function Ln(t,e){return new t0(t,e)}function Lc(t){return{...wZ,...t?.logging??{}}}function xI(t){return[/chrome\/updater\//i,/GoogleUpdater/i,/VERBOSE\d?:chrome\//i].some(e=>e.test(t))}function kZ(t,e,r,i){let n=[new Date().toISOString(),t.toUpperCase()];e&&n.push(`[${e}]`),n.push(r);let o=SZ(i);return o&&n.push(o),n.join(" ")}function SZ(t){if(!t)return"";let e=Object.entries(t).filter(([,r])=>r!==void 0);return e.length===0?"":JSON.stringify(Object.fromEntries(e))}import{execFile as EZ}from"node:child_process";var TZ=10*1024*1024;function IZ(t){let e;try{e=new URL(t)}catch{throw new Error(`Invalid PR URL: ${t}`)}let r=e.pathname.match(/^\/([^/]+)\/([^/]+)\/pull\/(\d+)(?:\/|$)/);if(!r)throw new Error(`Unsupported PR URL: ${t}`);return{owner:r[1],repo:r[2],prNumber:Number(r[3])}}function _I(t,e){if(typeof e.id!="number")return;let r=e.updated_at??e.created_at;return r?`${t}:${e.id}:${r}`:void 0}function PZ(t){if(typeof t.id!="number"||typeof t.body!="string"||t.body.trim().length===0)return;let e=_I("review",t);if(e)return{id:t.id,fingerprint:e,kind:"review",author:t.user?.login||"unknown",body:t.body,path:typeof t.path=="string"?t.path:void 0,line:typeof t.line=="number"?t.line:typeof t.original_line=="number"?t.original_line:void 0,url:typeof t.html_url=="string"?t.html_url:void 0,created:typeof t.created_at=="string"?t.created_at:void 0,updated:typeof t.updated_at=="string"?t.updated_at:void 0}}function zZ(t){if(typeof t.id!="number"||typeof t.body!="string"||t.body.trim().length===0)return;let e=_I("conversation",t);if(e)return{id:t.id,fingerprint:e,kind:"conversation",author:t.user?.login||"unknown",body:t.body,url:typeof t.html_url=="string"?t.html_url:void 0,created:typeof t.created_at=="string"?t.created_at:void 0,updated:typeof t.updated_at=="string"?t.updated_at:void 0}}async function bI(t){let e=await new Promise((n,o)=>{EZ("gh",["api",t,"--paginate","--slurp"],{maxBuffer:TZ},(a,s)=>{if(a){o(a);return}n(s)})}),r=JSON.parse(e);return(Array.isArray(r)?r:[]).flatMap(n=>Array.isArray(n)?n:[])}async function r0(t,e=[]){let{owner:r,repo:i,prNumber:n}=IZ(t),o=new Set(e),[a,s]=await Promise.all([bI(`repos/${r}/${i}/pulls/${n}/comments`),bI(`repos/${r}/${i}/issues/${n}/comments`)]);return[...a.map(PZ),...s.map(zZ)].filter(c=>!!c&&!o.has(c.fingerprint)).sort((c,u)=>{let l=c.updated??c.created??"",p=u.updated??u.created??"";return l.localeCompare(p)})}var n0=2e4;async function i0(t,e,r,i){let n=jZ(t);if(!n.trim())return"No conversation content found in log.";let o=["You are summarizing an AI agent execution log for a developer following task progress.",`Task: "${e}"`,"","Write a concise but sufficiently detailed summary that covers:","1. The main steps the agent took","2. Important findings, decisions, or deviations","3. Any problems or failures encountered","4. The final outcome","","Rules:","- Use 5-12 bullet points.","- Focus on task-specific implementation details, not filler.","- Prefer what changed in the repo, what was discovered, and how the task was solved.","- Do not spend bullets on routine workflow boilerplate such as worktree setup/cleanup, generic git add/commit/push, PR creation, or Jira status/reporting unless something unusual happened there.","- Mention concrete files, commands, tools, or errors when they materially explain the implementation.","- Do not mention token usage, compaction, or transcript mechanics.","- Do not invent details that are not in the log.","","Execution log:",OZ(n)].join(`
|
|
439
452
|
`),a=await qr({prompt:o,cwd:i,config:r});if(!a.resultText.trim())throw new Error("No compact log generated");return a.resultText.trim()}function jZ(t){let e=t.split(`
|
|
440
453
|
`).filter(i=>i.trim()),r=[];for(let i of e){let n;try{n=JSON.parse(i)}catch{continue}switch(n.type){case"assistant":{let o=n.message?.content??[];for(let a of o)if(a.type==="text"&&a.text)r.push(`[Claude] ${a.text}`);else if(a.type==="tool_use"){let s=a.name??"unknown";if(CZ(s,a.input))continue;let c=RZ(a.input);r.push(`[Tool] ${s}${c?`(${c})`:""}`)}break}case"result":{let o=typeof n.cost_usd=="number"?` (cost: $${n.cost_usd.toFixed(4)})`:"",a=n.result?`
|