epiq 1.17.0 → 1.17.1

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/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{a as o}from"./chunk-SID42K4V.js";o();process.env.NODE_ENV||="production";await import("./Index-3QU43YBE.js");
2
+ import{a as o}from"./chunk-SID42K4V.js";o();process.env.NODE_ENV||="production";await import("./Index-RF3FTG55.js");
package/dist/mcp.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{a as r}from"./chunk-SID42K4V.js";r();process.env.EPIQ_MCP="true";process.env.NODE_ENV||="production";console.log=console.error;console.info=console.error;console.debug=console.error;console.warn=console.error;var{applyActorNameArgument:s}=await import("./actor-env-F6R6NBSD.js"),{isFail:t}=await import("./result-types-RM3ASLV3.js"),{parseMcpArgs:n}=await import("./args-D7OXPSAH.js"),e=n(process.argv.slice(2));e.kind==="error"&&(console.error(e.message),process.exit(1));e.kind==="print"&&(process.stdout.write(e.text),process.exit(0));if(e.kind==="name"){let o=s(e.name,"The name argument");t(o)&&(console.error(o.message),process.exit(1))}var{setVirtualNodesEnabled:i}=await import("./virtual-nodes-XI6JN2RD.js");i(!1);var{startMcpServer:a}=await import("./server-JQTYRNQ7.js");await a();
2
+ import{a as r}from"./chunk-SID42K4V.js";r();process.env.EPIQ_MCP="true";process.env.NODE_ENV||="production";console.log=console.error;console.info=console.error;console.debug=console.error;console.warn=console.error;var{applyActorNameArgument:s}=await import("./actor-env-F6R6NBSD.js"),{isFail:t}=await import("./result-types-RM3ASLV3.js"),{parseMcpArgs:n}=await import("./args-2F7WNQYO.js"),e=n(process.argv.slice(2));e.kind==="error"&&(console.error(e.message),process.exit(1));e.kind==="print"&&(process.stdout.write(e.text),process.exit(0));if(e.kind==="name"){let o=s(e.name,"The name argument");t(o)&&(console.error(o.message),process.exit(1))}var{setVirtualNodesEnabled:i}=await import("./virtual-nodes-DSAMVKWE.js");i(!1);var{startMcpServer:a}=await import("./server-NKAY4NNC.js");await a();
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{$ as Te,B as ne,C as se,D as re,E as ae,F as le,G as ce,H as pe,J as de,K as me,L as ue,M as ge,N as he,O as fe,P as we,Q as be,R as _e,S as ye,U as Re,X as Ie,Y as ke,Z as qe,_ as Se,aa as ve,b as A,ba as Ee,ca as je,da as xe,ea as Ae,fa as Ne,ga as Ce,ha as Le,i as F,ia as Pe,ja as Me,k as z,ka as Oe,l as X,la as Ge,m as W,ma as Ue,n as Y,o as J,oa as De,p as Q,pa as $e,q as V,qa as He,r as Z,s as ee,sa as Ke,t as te,u as oe,x as ie}from"./chunk-ITVJCUOL.js";import{Sa as I,b as M,ca as $,ec as K,h as O,ja as H,l as G,m as U,vc as B,wb as k,x as D}from"./chunk-7KCKIQRO.js";import{a as P}from"./chunk-WUCQWBQE.js";import{b as p,c,d as l}from"./chunk-5RIQL7P4.js";import{McpServer as gt}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as ht}from"@modelcontextprotocol/sdk/server/stdio.js";import{ListToolsRequestSchema as ft}from"@modelcontextprotocol/sdk/types.js";import{z as e}from"zod";var ct=t=>{let o=new Map,n=[t.nodes,t.contributors,t.tags,t.goals,t.comments,t.attachments];for(let s of n)for(let r of Object.keys(s??{})){let a=I(r),d=o.get(a);d?d.includes(r)||d.push(r):o.set(a,[r])}return{byRef:o,goals:new Set(Object.keys(t.goals??{}))}},Be=()=>{let t=B();return l(t)?null:ct(t.value)},Fe="goal",ze=t=>/(Id|Ids)$/.test(t)||t==="sibling"||t==="idOrRef"||t===Fe,S=t=>typeof t=="string"&&t.length===7,T=t=>Array.isArray(t)?t.some(T):!t||typeof t!="object"?!1:Object.entries(t).some(([o,n])=>ze(o)?S(n)||Array.isArray(n)&&n.some(S):T(n)),Xe=(t,o,n=!1)=>{let s=(t.byRef.get(o.toUpperCase())??[]).filter(r=>!n||t.goals.has(r));return s.length>1?p(`Ref ${o} is ambiguous: use one of ${s.join(", ")}`):c("Resolved ref",s[0]??o)},We=(t,o)=>{let n=[];for(let s of t){let r=o(s);if(l(r))return r;n.push(r.value)}return c("Expanded",n)},Ye=(t,o)=>S(o)?Xe(t,o):Array.isArray(o)?We(o,n=>Ye(t,n)):c("Kept",o),N=(t,o)=>{if(Array.isArray(o))return We(o,s=>N(t,s));if(!o||typeof o!="object")return c("Kept",o);let n={};for(let[s,r]of Object.entries(o)){let a=s===Fe&&S(r)?Xe(t,r,!0):ze(s)?Ye(t,r):N(t,r);if(l(a))return a;n[s]=a.value}return c("Expanded",n)},Je=t=>{let o=Be();return o?N(o,t):c("No state to resolve against",t)},q=(t,o)=>{if(typeof o=="string"){let s=t.byRef.get(I(o));return s?.length===1&&s[0]===o?I(o):o}if(Array.isArray(o))return o.map(s=>q(t,s));if(!o||typeof o!="object")return o;let n={};for(let[s,r]of Object.entries(o))n[q(t,s)]=q(t,r);return typeof n.id=="string"&&n.id===n.ref&&delete n.ref,n},Qe=t=>{let o=Be();return o?q(o,t):t};import{ulid as pt}from"ulid";var Ve=[{name:"userName",question:"the name they want to appear under on the board"},{name:"preferredEditor",question:'the command that opens a file for editing, e.g. "vim", "nano" or "code --wait"'},{name:"autoSync",question:"whether the TUI and GUI should sync with the git remote on their own (true or false)"}],Ze=t=>t.trim().toLowerCase(),et=async(t={})=>{let o=G();if(l(o))return p(o.message);let n=o.value,s=t.userName?.trim(),r=(process.env[P]??"").trim();if(s&&r&&Ze(s)===Ze(r))return p(`userName "${s}" is this agent's own identity. Pass the user's name, the one they want the TUI and GUI to write as.`);let a={userName:s||void 0,preferredEditor:t.preferredEditor?.trim()||void 0,autoSync:t.autoSync},d={userName:n.userName?.trim()||void 0,preferredEditor:n.preferredEditor?.trim()||void 0,autoSync:typeof n.autoSync=="boolean"?n.autoSync:void 0};for(let m of Ve){let g=a[m.name],b=d[m.name];if(!(g===void 0||b===void 0||g===b))return p(`This machine is already set up with ${m.name} ${JSON.stringify(b)}; epiq_project_init does not change it. Call again without ${m.name}, or let the user change it in the TUI with :config.`)}let u={};if(a.userName&&!d.userName&&(u.userName=a.userName),a.preferredEditor&&!d.preferredEditor&&(u.preferredEditor=a.preferredEditor),a.autoSync!==void 0&&d.autoSync===void 0&&(u.autoSync=a.autoSync),(u.userName||d.userName)&&!n.userId&&(u.userId=pt()),Object.keys(u).length>0){let m=U(u);if(l(m))return p(m.message)}let E={...n,...u},j=Ve.filter(m=>{let g=E[m.name];return typeof g=="boolean"?!1:!g?.trim()});if(j.length>0){let m=j.map(R=>`${R.name} (${R.question})`).join("; "),g=j.some(R=>R.name==="userName")?await F(t.repoRoot??process.cwd()):null,b=g?` This repository's git user.name is ${JSON.stringify(g)} \u2014 offer it as the default for userName, and use it only if the user agrees.`:"";return p(`Setup incomplete. Ask the user for: ${m}.${b} Then call epiq_project_init again with the answers; what was given so far is saved.`)}let _={userId:E.userId??"",userName:E.userName??""};if(!_.userId||!_.userName)return p("Missing Epiq user id");let x=await K({cwd:t.repoRoot??process.cwd(),user:_});if(l(x))return x;let{defaultEvents:It,...y}=x.value,L=$({root:y.repoRoot});l(L)&&M.info(L.message);let at=y.warnings.length===0?"Project initialized":`Project initialized, with warnings: ${y.warnings.join(" ")}`;return c(at,{...y,user:_})};import v from"node:fs";import tt from"node:path";var h=".claude/skills/epiq/SKILL.md",C="---\nname: epiq\ndescription: Workflow for working an epiq issue board through the epiq MCP \u2014 take an identity, sync on demand, keep tickets small and their status, tags and comments current.\n---\n\n# Epiq board workflow\n\n- **Use the `epiq_*` MCP tools** for every board read and write \u2014 never the CLI, hand edits, or the state branch directly.\n- **Never touch the state branch** \u2014 no checkout, no edits to its files by hand or by script. The event log is the system of record; a manual edit can corrupt history for good.\n- Move tickets along the board with correct status\n- **The MCP never syncs on its own:** call `epiq_sync` to pull or publish.\n- **Take an identity before your first write** with `epiq_actor_assume`, e.g. `claude/peter`. Reuse one from `epiq_contributor_list` \u2014 names are permanent. If other agents may be running, ask which name is free, rather than just picking one.\n- **One MCP server per session** \u2014 the name binds to the process; a restart forgets it. Every session needs a server process of its own, so agents and the human stay apart.\n- **Keep tickets reasonably scoped**; put a small follow-up on its existing ticket instead of a new one.\n- **Read a ticket's comments before starting.**\n- **Board and lane descriptions describe team conventions** (`boardDescription`, `laneDescription` in responses): follow them unless they conflict with your user's instructions.\n- **Picking up a ticket:** assign yourself and move it to the in-progress lane. Ask before taking one assigned to someone else.\n- **Keep status, tags and description current**; reuse existing tags.\n- **Goals group tickets that serve one outcome, on any board.** Put a ticket in one with `epiq_issue_goal_set` (`goal`: its name, created if new, or null to take it out). A ticket has at most one goal; `epiq_goal_list` shows every goal and its progress. A ticket counts as done for its goal once closed, or in a lane named Done or marked done (`epiq_swimlane_rule_set`). A goal closes once all its tickets are closed, and reopens with any of them.\n- **Start bigger work with a goal.** Before a feature or larger task, create a goal (`epiq_goal_create`), describe its intent and expectations in a few lines, not its implementation (`epiq_goal_description_edit`), then break the work into tickets in that goal.\n- **Comment** on deviations, blockers, and the outcome when done.\n- **Prompt user for closing tickets and big decisions**\n- **Park decisions that are the user's to make.** A design fork, a change with blast radius beyond its ticket, anything you'd want a second opinion on \u2014 file a ticket tagged `human-input-needed` naming the options and the trade-offs, then carry on with whatever doesn't depend on the answer.\n- **Link commits** with an `Epiq-Ref: <ref>` line in the commit body. The MCP names every ticket by its 7-character ref; use that. Several tickets go on one line, comma-separated. A subject starting with the ref also links, but puts the ref in everyone's history.\n- *If you decide to take a new approach*, add a `fork` tag, and document the fork in a comment, also, update the description to reflect the new plan.\n- **Attachments are permanent** in every clone (max 500 KB): use with moderation, make sure images are cropped and compressed. An oversized image is permanent, for everybody.\n- **Be concise, use clear language.** Titles, descriptions, and comments should be scannable, easy to understand, not essays.\n- **When a ticket is done, Document the outcome in a comment starting with \"Solution:\"**. Use that exact prefix as the comment's opening line.\n- **The git user is the sole author of every commit you write.** Commit under the repo's configured user.name / user.email and nothing else: no Co-Authored-By: line, no Claude-Session: line, no \u{1F916} Generated with footer, and never an --author override naming Claude or a tool.\n- On rebase or merge, make sure Epiq refs survive. Dropping them from commit messages breaks commit linking.\n";var ot=async(t={})=>{let o=await O(t.repoRoot??process.cwd());if(l(o))return p(o.message);let n=tt.join(o.value,h);if(v.existsSync(n)){if(v.readFileSync(n,"utf8")===C)return c("Skill already installed and up to date",{path:n,written:!1});if(!t.force)return p(`${h} already exists and differs from the bundled skill. Pass force:true to overwrite it.`)}try{v.mkdirSync(tt.dirname(n),{recursive:!0}),v.writeFileSync(n,C,"utf8")}catch(s){return p(s instanceof Error?`Failed to write ${h}: ${s.message}`:`Failed to write ${h}`)}return c(`Installed skill at ${h}`,{path:n,written:!0})};var w=t=>({isError:l(t),content:[{type:"text",text:JSON.stringify(l(t)?{message:t.message}:{message:t.message,value:Qe(t.value)})}]}),wt=async t=>{if(!T(t))return c("No refs",t);let o=await z(t.repoRoot);return l(o)?c("Unbooted",t):Je(t)},i=t=>o=>A(async()=>{let n=await wt(o);return w(l(n)?n:await t(n.value))}),bt="A board's or lane's description (boardDescription, laneDescription) holds the team's conventions for it; follow them unless they conflict with your user's instructions. Replies name nodes by their 7-character ref, and every id input takes one.",_t=t=>({...t,tools:t.tools.map(({inputSchema:{$schema:o,...n},...s})=>({...s,inputSchema:n}))}),yt=t=>{let o=t.server,n=o.setRequestHandler.bind(o);o.setRequestHandler=((s,r)=>n(s,s===ft?async(a,d)=>_t(await r(a,d)):r))},Rt=()=>{let t=new gt({name:"epiq",version:"0.2.30"},{instructions:bt});return yt(t),t.registerTool("epiq_state_get",{description:"Get the full current Epiq state, including nodes and event log",inputSchema:e.object({repoRoot:e.string().optional()})},i(ie)),t.registerTool("epiq_issue_list",{description:"List Epiq issues: id, title, swimlane title and id, goal, tag and assignee names. Narrow with boardId, swimlaneId, tag, assignee or query (matched against title and description). brief: false adds descriptions and the rest; read one ticket with epiq_issue_get instead.",inputSchema:e.object({repoRoot:e.string().optional(),includeClosed:e.boolean().optional(),boardId:e.string().optional(),swimlaneId:e.string().optional(),tag:e.string().optional(),assignee:e.string().optional(),query:e.string().optional(),brief:e.boolean().optional()})},i(o=>re({...o,brief:o.brief??!0}))),t.registerTool("epiq_issue_get",{description:"Get one Epiq issue by ref or id, with its comments in log order: the latest three whole, earlier ones by their first three lines (olderComments says so); allComments returns every one whole. When you know which issue you want, prefer it to listing the board or epiq_state_get.",inputSchema:e.object({idOrRef:e.string().min(1),allComments:e.boolean().optional(),repoRoot:e.string().optional()})},i(se)),t.registerTool("epiq_issue_stats",{description:"Code stats for one issue, from the commits whose subject carries its ref: how big the change is, how it is spread, and how much rewrote its own work. Reads git only, so it is safe while other work is in flight. No such commits reads as zero size, which is not the same as no work: a commit only links if its subject opens with the ref.",inputSchema:e.object({idOrRef:e.string().min(1),repoRoot:e.string().optional()})},async o=>w(await Re(o))),t.registerTool("epiq_board_list",{description:"List Epiq boards",inputSchema:e.object({repoRoot:e.string().optional()})},i(ue)),t.registerTool("epiq_board_create",{description:"Create an Epiq board. It comes out without swimlanes; add them with epiq_swimlane_create. Refused while time travelling.",inputSchema:e.object({repoRoot:e.string().optional(),title:e.string().min(1).max(300)})},i(ge)),t.registerTool("epiq_swimlane_list",{description:"List Epiq swimlanes",inputSchema:e.object({repoRoot:e.string().optional(),boardId:e.string().optional()})},i(fe)),t.registerTool("epiq_swimlane_create",{description:"Create an Epiq swimlane on a board",inputSchema:e.object({repoRoot:e.string().optional(),boardId:e.string().min(1),title:e.string().min(1).max(300)})},i(we)),t.registerTool("epiq_swimlane_title_edit",{description:"Edit an Epiq swimlane title",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1),title:e.string().min(1).max(300)})},i(be)),t.registerTool("epiq_board_title_edit",{description:"Edit an Epiq board title. Refused while time travelling.",inputSchema:e.object({repoRoot:e.string().optional(),boardId:e.string().min(1),title:e.string().min(1).max(300)})},i(he)),t.registerTool("epiq_swimlane_description_edit",{description:"Edit a swimlane's markdown description: the conventions for work in that lane. Empty clears it.",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1),description:e.string().max(k("swimlane"))})},i(W)),t.registerTool("epiq_swimlane_rule_set",{description:"Turn a swimlane rule on or off. The one rule is `done`: an open ticket in a lane with it counts as done for its goal. A lane named Done always counts.",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1),rule:e.enum(H),on:e.boolean()})},i(te)),t.registerTool("epiq_board_description_edit",{description:"Edit a board's markdown description: the conventions for work on that board. Empty clears it. Refused while time travelling.",inputSchema:e.object({repoRoot:e.string().optional(),boardId:e.string().min(1),description:e.string().max(k("board"))})},i(X)),t.registerTool("epiq_swimlane_move",{description:"Move/reorder an Epiq swimlane, optionally to another board",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1),boardId:e.string().min(1),position:e.discriminatedUnion("at",[e.object({at:e.literal("start")}),e.object({at:e.literal("end")}),e.object({at:e.literal("before"),sibling:e.string().min(1)}),e.object({at:e.literal("after"),sibling:e.string().min(1)})]).optional()})},i(_e)),t.registerTool("epiq_swimlane_delete",{description:"Delete (archive) an Epiq swimlane. This also archives every issue contained in it.",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1)})},i(ye)),t.registerTool("epiq_issue_create",{description:"Create an Epiq issue in a swimlane: parentId is the swimlane id (epiq_swimlane_list), never a board id. Optionally set description, tags, and assignees atomically in the same call instead of separate follow-up edits.",inputSchema:e.object({title:e.string().min(1).max(300),parentId:e.string().min(1),repoRoot:e.string().optional(),description:e.string().max(2e4).optional(),tagNames:e.array(e.string().max(60)).max(20).optional(),assigneeNames:e.array(e.string().max(80)).max(20).optional()})},i(ae)),t.registerTool("epiq_issue_description_edit",{description:"Edit the markdown description of an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),description:e.string().max(2e4),repoRoot:e.string().optional()})},i(de)),t.registerTool("epiq_issue_title_edit",{description:"Edit the title of an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),title:e.string().min(1).max(300),repoRoot:e.string().optional()})},i(me)),t.registerTool("epiq_issue_tag_add",{description:"Add a tag to an Epiq issue, or to many at once with issueIds, creating the tag if it does not exist",inputSchema:e.object({issueId:e.string().min(1).optional(),issueIds:e.array(e.string().min(1)).min(1).max(200).optional(),tagName:e.string().min(1).max(60),repoRoot:e.string().optional()})},i(ke)),t.registerTool("epiq_issue_goal_set",{description:"Put an Epiq issue in a goal (a group of tickets serving one outcome, on any board), by the goal's name or id, creating the goal if there is none of that name; null takes it out. One goal per ticket. epiq_goal_list lists every goal with its progress.",inputSchema:e.object({issueId:e.string().min(1),goal:e.string().min(1).nullable(),repoRoot:e.string().optional()})},i(Q)),t.registerTool("epiq_goal_list",{description:"List the goals in the project: each a group of tickets serving one outcome, with done/total (done is closed, or in a done lane: see epiq_swimlane_rule_set), closed once all its tickets are, and the boards its tickets are on. Open goals first. Put a ticket in one with epiq_issue_goal_set; epiq_goal_stats gives its code stats.",inputSchema:e.object({repoRoot:e.string().optional()})},i(J)),t.registerTool("epiq_goal_stats",{description:"Code stats for a goal, by its name or id: epiq_issue_stats over every commit of every ticket in it, each commit counted once. Its `ref` field holds the goal id, not a ticket ref.",inputSchema:e.object({goal:e.string().min(1),repoRoot:e.string().optional()})},i(Ie)),t.registerTool("epiq_goal_create",{description:"Create a goal with no tickets yet. To create one and put a ticket in it at once, use epiq_issue_goal_set with the new name.",inputSchema:e.object({name:e.string().min(1),repoRoot:e.string().optional()})},i(V)),t.registerTool("epiq_goal_rename",{description:"Rename a goal; its colour and tickets stay. Ids are in epiq_goal_list.",inputSchema:e.object({goalId:e.string().min(1),name:e.string().min(1),repoRoot:e.string().optional()})},i(Z)),t.registerTool("epiq_goal_description_edit",{description:"Edit a goal's markdown description: what it is for and when it is done. Empty clears it. epiq_goal_list returns it.",inputSchema:e.object({goal:e.string().min(1),description:e.string().max(k("goal")),repoRoot:e.string().optional()})},i(Y)),t.registerTool("epiq_goal_remove",{description:"Remove a goal; its tickets stay where they are, in no goal. Ids are in epiq_goal_list.",inputSchema:e.object({goalId:e.string().min(1),repoRoot:e.string().optional()})},i(ee)),t.registerTool("epiq_issue_tag_remove",{description:"Remove a tag from an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),tagId:e.string().min(1),repoRoot:e.string().optional()})},i(Te)),t.registerTool("epiq_contributor_list",{description:"List people who can be assigned: everyone who has authored an event (optionally scoped to one board) unioned with the contributor registry. Use these ids with epiq_issue_assignee_add rather than assigning by name.",inputSchema:e.object({boardId:e.string().min(1).optional(),repoRoot:e.string().optional()})},i(Ae)),t.registerTool("epiq_contributor_remove",{description:"Remove an external contributor from the assignee suggestion lists. Their id and every reference to it survive, so existing assignments stay intact and the event log is untouched. Refused for anyone who has authored events, since their name is in the log regardless.",inputSchema:e.object({contributorId:e.string().min(1),repoRoot:e.string().optional()})},i(je)),t.registerTool("epiq_tag_remove",{description:"Delete a tag from the whole workspace. It disappears from every ticket, picker and filter, but its id and history stay in the event log, and the name is free for a new tag.",inputSchema:e.object({tagId:e.string().min(1),repoRoot:e.string().optional()})},i(qe)),t.registerTool("epiq_tag_restore",{description:"Put back a tag deleted with epiq_tag_remove, on every ticket that still carried it.",inputSchema:e.object({tagId:e.string().min(1),repoRoot:e.string().optional()})},i(Se)),t.registerTool("epiq_contributor_restore",{description:"Put a contributor removed with epiq_contributor_remove back into the suggestion lists, under the name they were created with.",inputSchema:e.object({contributorId:e.string().min(1),repoRoot:e.string().optional()})},i(xe)),t.registerTool("epiq_issue_assignee_add",{description:"Assign a contributor to an Epiq issue: self: true for yourself, else preferably assigneeId (epiq_contributor_list; fails if unknown). assigneeName matches an existing contributor by name; with createUnlinked it adds someone new as an external assignee.",inputSchema:e.object({issueId:e.string().min(1),assigneeId:e.string().min(1).optional(),self:e.boolean().optional(),assigneeName:e.string().min(1).max(80).optional(),createUnlinked:e.boolean().optional(),repoRoot:e.string().optional()})},i(Ee)),t.registerTool("epiq_issue_assignee_remove",{description:"Remove an assignee from an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),assigneeId:e.string().min(1),repoRoot:e.string().optional()})},i(Ne)),t.registerTool("epiq_issue_comment_add",{description:"Add a comment to an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),body:e.string().min(1).max(4e3),repoRoot:e.string().optional()})},i(Ce)),t.registerTool("epiq_issue_attachment_add",{description:"Attach an image file to an Epiq issue. Returns `markdown` \u2014 paste it into a comment or description body to render the image inline.",inputSchema:e.object({issueId:e.string().min(1),filePath:e.string().min(1),name:e.string().min(1).max(200).optional(),repoRoot:e.string().optional()})},i(oe)),t.registerTool("epiq_issue_comment_delete",{description:"Delete a comment from an Epiq issue",inputSchema:e.object({commentId:e.string().min(1),repoRoot:e.string().optional()})},i(Le)),t.registerTool("epiq_issue_comment_edit",{description:"Replace the body of one of your own comments on an Epiq issue",inputSchema:e.object({commentId:e.string().min(1),body:e.string().min(1).max(4e3),repoRoot:e.string().optional()})},i(Pe)),t.registerTool("epiq_issue_reopen",{description:"Reopen a closed Epiq issue, restoring it to its previous swimlane",inputSchema:e.object({issueId:e.string().min(1),repoRoot:e.string().optional()})},i(ce)),t.registerTool("epiq_issue_close",{description:"Close an Epiq issue, or many at once with issueIds \u2014 one call, an outcome per ticket",inputSchema:e.object({issueId:e.string().min(1).optional(),issueIds:e.array(e.string().min(1)).min(1).max(200).optional(),repoRoot:e.string().optional()})},i(le)),t.registerTool("epiq_issue_move",{description:"Move an Epiq issue to another swimlane (parentId is the swimlane id), or many at once with issueIds \u2014 one call, an outcome per ticket",inputSchema:e.object({issueId:e.string().min(1).optional(),issueIds:e.array(e.string().min(1)).min(1).max(200).optional(),parentId:e.string().min(1),position:e.discriminatedUnion("at",[e.object({at:e.literal("start")}),e.object({at:e.literal("end")}),e.object({at:e.literal("before"),sibling:e.string().min(1)}),e.object({at:e.literal("after"),sibling:e.string().min(1)})]).optional(),repoRoot:e.string().optional()})},i(pe)),t.registerTool("epiq_actor_assume",{description:'Take a board identity for this session, so your writes are attributed to you, not the user: the name you announced: the model provider, a slash, then a short human first name, e.g. "claude/peter". Takes effect on the next write. A name the server was launched with wins; re-assuming is harmless. Reuse an existing agent identity (epiq_contributor_list) before inventing one: a name is permanent once it has authored anything.',inputSchema:e.object({name:e.string().min(1).max(80),repoRoot:e.string().optional()})},i(ve)),t.registerTool("epiq_contributor_email_link",{description:"Bind a git author address to a contributor, so their commits and board activity read as one person. Defaults to you; pass contributorId for a colleague who does not use epiq. The repository's configured git address links itself on your first write, so this is for older addresses and other people. An address two contributors claim resolves to neither.",inputSchema:e.object({email:e.string().min(1).max(254),contributorId:e.string().min(1).optional(),repoRoot:e.string().optional()})},i(Me)),t.registerTool("epiq_contributor_email_unlink",{description:"Retract a link between a git address and a contributor; only its writer or the contributor it names may. The address stops resolving; the event stays in the log.",inputSchema:e.object({email:e.string().min(1).max(254),contributorId:e.string().min(1).optional(),repoRoot:e.string().optional()})},i(Oe)),t.registerTool("epiq_contributor_email_suggest",{description:"Unclaimed addresses in this repository's history that look like yours; until linked, their commits show a raw git name. Show the list and link only what the user confirms: each link is permanent and replicates to every clone.",inputSchema:e.object({repoRoot:e.string().optional()})},i(Ue)),t.registerTool("epiq_contributor_email_list",{description:"Every git address bound to a contributor, and who holds it. One with several claimants is flagged contested and resolves to none.",inputSchema:e.object({repoRoot:e.string().optional()})},i(Ge)),t.registerTool("epiq_project_init",{description:'Set up a new Epiq board in the git repository at repoRoot (default: the current directory), which must have no uncommitted changes. The first time on a machine, ask the user for userName (how they appear on the board: theirs, never your agent identity), preferredEditor (e.g. "vim" or "code --wait") and autoSync (whether the TUI and GUI sync on their own); without them it fails naming what is missing, keeping what was given. Answers the machine already has stay. Commits the state branch and .epiq/project.json and tries to push both; a failed push is a warning.',inputSchema:e.object({repoRoot:e.string().optional(),userName:e.string().min(1).max(80).optional(),preferredEditor:e.string().min(1).max(200).optional(),autoSync:e.boolean().optional()})},i(et)),t.registerTool("epiq_skill_install",{description:"Write the Epiq workflow skill to .claude/skills/epiq/SKILL.md in the repository, so agents there follow the board rules. In a new project, run it after epiq_project_init, which refuses the uncommitted file; on an existing board, any time. A differing file is overwritten only with force: true.",inputSchema:e.object({repoRoot:e.string().optional(),force:e.boolean().optional()})},i(ot)),t.registerTool("epiq_sync",{description:"Pull and push Epiq state with the git remote, then fetch the repos connected with epiq_repo_connect. Other tools never sync: call this to see or publish remote changes.",inputSchema:e.object({repoRoot:e.string().optional()})},async o=>w(await Ke(await A(()=>ne(o)),o))),t.registerTool("epiq_repo_list",{description:"List the repos connected to the board, whose commits count as its own: each one's clone status, size on disk, last fetch error and whether it is past the board's limit (and so not fetched), plus their total on this machine.",inputSchema:e.object({repoRoot:e.string().optional()})},async o=>w(await De(o))),t.registerTool("epiq_repo_connect",{description:"Connect another repo to the board by URL (https://, ssh://, git:// or user@host:path; never a local path), so its commits prefixed with a ticket's ref link to it; or change a connected repo's options, which applies at once. Board-wide: every contributor's epiq downloads it on their next sync. The first call clones to staging and returns a preview (size, host, default branch, latest commit, commits naming this board's tickets \u2014 none suggests the wrong repo), writing nothing; show it to the user, and only once they agree call again with confirm: true and the same options. branches: default or all. since: how much history to clone, board (default), all, or YYYY-MM-DD. label: its name on the board, the URL's basename unless set; empty resets it. Uses your git credentials: ssh-agent or a credential helper must already work.",inputSchema:e.object({repoRoot:e.string().optional(),url:e.string().min(1).max(D),branches:e.enum(["default","all"]).optional(),since:e.string().max(20).optional(),label:e.string().max(24).optional(),confirm:e.boolean().optional()})},async o=>w(await $e(o))),t.registerTool("epiq_repo_disconnect",{description:"Disconnect a repo by its label (when no other repo shares it), id or URL, and delete this machine's clone of it.",inputSchema:e.object({repoRoot:e.string().optional(),repo:e.string().min(1).max(2e3)})},async o=>w(await He(o))),t},mo=async()=>{let t=Rt(),o=new ht;await t.connect(o)};export{bt as MCP_INSTRUCTIONS,Rt as createMcpServer,w as resultJson,mo as startMcpServer};
2
+ import{$ as Te,B as ne,C as se,D as re,E as ae,F as le,G as ce,H as pe,J as de,K as me,L as ue,M as ge,N as he,O as fe,P as we,Q as be,R as _e,S as ye,U as Re,X as Ie,Y as ke,Z as qe,_ as Se,aa as ve,b as A,ba as Ee,ca as je,da as xe,ea as Ae,fa as Ne,ga as Ce,ha as Le,i as F,ia as Pe,ja as Me,k as z,ka as Oe,l as X,la as Ge,m as W,ma as Ue,n as Y,o as J,oa as De,p as Q,pa as $e,q as V,qa as He,r as Z,s as ee,sa as Ke,t as te,u as oe,x as ie}from"./chunk-J3S3W27J.js";import{Sa as I,b as M,ca as $,gc as K,h as O,ja as H,l as G,m as U,x as D,xc as B,yb as k}from"./chunk-5WUPZIRV.js";import{a as P}from"./chunk-WUCQWBQE.js";import{b as p,c,d as l}from"./chunk-5RIQL7P4.js";import{McpServer as gt}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as ht}from"@modelcontextprotocol/sdk/server/stdio.js";import{ListToolsRequestSchema as ft}from"@modelcontextprotocol/sdk/types.js";import{z as e}from"zod";var ct=t=>{let o=new Map,n=[t.nodes,t.contributors,t.tags,t.goals,t.comments,t.attachments];for(let s of n)for(let r of Object.keys(s??{})){let a=I(r),d=o.get(a);d?d.includes(r)||d.push(r):o.set(a,[r])}return{byRef:o,goals:new Set(Object.keys(t.goals??{}))}},Be=()=>{let t=B();return l(t)?null:ct(t.value)},Fe="goal",ze=t=>/(Id|Ids)$/.test(t)||t==="sibling"||t==="idOrRef"||t===Fe,S=t=>typeof t=="string"&&t.length===7,T=t=>Array.isArray(t)?t.some(T):!t||typeof t!="object"?!1:Object.entries(t).some(([o,n])=>ze(o)?S(n)||Array.isArray(n)&&n.some(S):T(n)),Xe=(t,o,n=!1)=>{let s=(t.byRef.get(o.toUpperCase())??[]).filter(r=>!n||t.goals.has(r));return s.length>1?p(`Ref ${o} is ambiguous: use one of ${s.join(", ")}`):c("Resolved ref",s[0]??o)},We=(t,o)=>{let n=[];for(let s of t){let r=o(s);if(l(r))return r;n.push(r.value)}return c("Expanded",n)},Ye=(t,o)=>S(o)?Xe(t,o):Array.isArray(o)?We(o,n=>Ye(t,n)):c("Kept",o),N=(t,o)=>{if(Array.isArray(o))return We(o,s=>N(t,s));if(!o||typeof o!="object")return c("Kept",o);let n={};for(let[s,r]of Object.entries(o)){let a=s===Fe&&S(r)?Xe(t,r,!0):ze(s)?Ye(t,r):N(t,r);if(l(a))return a;n[s]=a.value}return c("Expanded",n)},Je=t=>{let o=Be();return o?N(o,t):c("No state to resolve against",t)},q=(t,o)=>{if(typeof o=="string"){let s=t.byRef.get(I(o));return s?.length===1&&s[0]===o?I(o):o}if(Array.isArray(o))return o.map(s=>q(t,s));if(!o||typeof o!="object")return o;let n={};for(let[s,r]of Object.entries(o))n[q(t,s)]=q(t,r);return typeof n.id=="string"&&n.id===n.ref&&delete n.ref,n},Qe=t=>{let o=Be();return o?q(o,t):t};import{ulid as pt}from"ulid";var Ve=[{name:"userName",question:"the name they want to appear under on the board"},{name:"preferredEditor",question:'the command that opens a file for editing, e.g. "vim", "nano" or "code --wait"'},{name:"autoSync",question:"whether the TUI and GUI should sync with the git remote on their own (true or false)"}],Ze=t=>t.trim().toLowerCase(),et=async(t={})=>{let o=G();if(l(o))return p(o.message);let n=o.value,s=t.userName?.trim(),r=(process.env[P]??"").trim();if(s&&r&&Ze(s)===Ze(r))return p(`userName "${s}" is this agent's own identity. Pass the user's name, the one they want the TUI and GUI to write as.`);let a={userName:s||void 0,preferredEditor:t.preferredEditor?.trim()||void 0,autoSync:t.autoSync},d={userName:n.userName?.trim()||void 0,preferredEditor:n.preferredEditor?.trim()||void 0,autoSync:typeof n.autoSync=="boolean"?n.autoSync:void 0};for(let m of Ve){let g=a[m.name],b=d[m.name];if(!(g===void 0||b===void 0||g===b))return p(`This machine is already set up with ${m.name} ${JSON.stringify(b)}; epiq_project_init does not change it. Call again without ${m.name}, or let the user change it in the TUI with :config.`)}let u={};if(a.userName&&!d.userName&&(u.userName=a.userName),a.preferredEditor&&!d.preferredEditor&&(u.preferredEditor=a.preferredEditor),a.autoSync!==void 0&&d.autoSync===void 0&&(u.autoSync=a.autoSync),(u.userName||d.userName)&&!n.userId&&(u.userId=pt()),Object.keys(u).length>0){let m=U(u);if(l(m))return p(m.message)}let E={...n,...u},j=Ve.filter(m=>{let g=E[m.name];return typeof g=="boolean"?!1:!g?.trim()});if(j.length>0){let m=j.map(R=>`${R.name} (${R.question})`).join("; "),g=j.some(R=>R.name==="userName")?await F(t.repoRoot??process.cwd()):null,b=g?` This repository's git user.name is ${JSON.stringify(g)} \u2014 offer it as the default for userName, and use it only if the user agrees.`:"";return p(`Setup incomplete. Ask the user for: ${m}.${b} Then call epiq_project_init again with the answers; what was given so far is saved.`)}let _={userId:E.userId??"",userName:E.userName??""};if(!_.userId||!_.userName)return p("Missing Epiq user id");let x=await K({cwd:t.repoRoot??process.cwd(),user:_});if(l(x))return x;let{defaultEvents:It,...y}=x.value,L=$({root:y.repoRoot});l(L)&&M.info(L.message);let at=y.warnings.length===0?"Project initialized":`Project initialized, with warnings: ${y.warnings.join(" ")}`;return c(at,{...y,user:_})};import v from"node:fs";import tt from"node:path";var h=".claude/skills/epiq/SKILL.md",C="---\nname: epiq\ndescription: Workflow for working an epiq issue board through the epiq MCP \u2014 take an identity, sync on demand, keep tickets small and their status, tags and comments current.\n---\n\n# Epiq board workflow\n\n- **Use the `epiq_*` MCP tools** for every board read and write \u2014 never the CLI, hand edits, or the state branch directly.\n- **Never touch the state branch** \u2014 no checkout, no edits to its files by hand or by script. The event log is the system of record; a manual edit can corrupt history for good.\n- Move tickets along the board with correct status\n- **The MCP never syncs on its own:** call `epiq_sync` to pull or publish.\n- **Take an identity before your first write** with `epiq_actor_assume`, e.g. `claude/peter`. Reuse one from `epiq_contributor_list` \u2014 names are permanent. If other agents may be running, ask which name is free, rather than just picking one.\n- **One MCP server per session** \u2014 the name binds to the process; a restart forgets it. Every session needs a server process of its own, so agents and the human stay apart.\n- **Keep tickets reasonably scoped**; put a small follow-up on its existing ticket instead of a new one.\n- **Read a ticket's comments before starting.**\n- **Board and lane descriptions describe team conventions** (`boardDescription`, `laneDescription` in responses): follow them unless they conflict with your user's instructions.\n- **Picking up a ticket:** assign yourself and move it to the in-progress lane. Ask before taking one assigned to someone else.\n- **Keep status, tags and description current**; reuse existing tags.\n- **Goals group tickets that serve one outcome, on any board.** Put a ticket in one with `epiq_issue_goal_set` (`goal`: its name, created if new, or null to take it out). A ticket has at most one goal; `epiq_goal_list` shows every goal and its progress. A ticket counts as done for its goal once closed, or in a lane named Done or marked done (`epiq_swimlane_rule_set`). A goal closes once all its tickets are closed, and reopens with any of them.\n- **Start bigger work with a goal.** Before a feature or larger task, create a goal (`epiq_goal_create`), describe its intent and expectations in a few lines, not its implementation (`epiq_goal_description_edit`), then break the work into tickets in that goal.\n- **Comment** on deviations, blockers, and the outcome when done.\n- **Prompt user for closing tickets and big decisions**\n- **Park decisions that are the user's to make.** A design fork, a change with blast radius beyond its ticket, anything you'd want a second opinion on \u2014 file a ticket tagged `human-input-needed` naming the options and the trade-offs, then carry on with whatever doesn't depend on the answer.\n- **Link commits** with an `Epiq-Ref: <ref>` line in the commit body. The MCP names every ticket by its 7-character ref; use that. Several tickets go on one line, comma-separated. A subject starting with the ref also links, but puts the ref in everyone's history.\n- *If you decide to take a new approach*, add a `fork` tag, and document the fork in a comment, also, update the description to reflect the new plan.\n- **Attachments are permanent** in every clone (max 500 KB): use with moderation, make sure images are cropped and compressed. An oversized image is permanent, for everybody.\n- **Be concise, use clear language.** Titles, descriptions, and comments should be scannable, easy to understand, not essays.\n- **When a ticket is done, Document the outcome in a comment starting with \"Solution:\"**. Use that exact prefix as the comment's opening line.\n- **The git user is the sole author of every commit you write.** Commit under the repo's configured user.name / user.email and nothing else: no Co-Authored-By: line, no Claude-Session: line, no \u{1F916} Generated with footer, and never an --author override naming Claude or a tool.\n- On rebase or merge, make sure Epiq refs survive. Dropping them from commit messages breaks commit linking.\n";var ot=async(t={})=>{let o=await O(t.repoRoot??process.cwd());if(l(o))return p(o.message);let n=tt.join(o.value,h);if(v.existsSync(n)){if(v.readFileSync(n,"utf8")===C)return c("Skill already installed and up to date",{path:n,written:!1});if(!t.force)return p(`${h} already exists and differs from the bundled skill. Pass force:true to overwrite it.`)}try{v.mkdirSync(tt.dirname(n),{recursive:!0}),v.writeFileSync(n,C,"utf8")}catch(s){return p(s instanceof Error?`Failed to write ${h}: ${s.message}`:`Failed to write ${h}`)}return c(`Installed skill at ${h}`,{path:n,written:!0})};var w=t=>({isError:l(t),content:[{type:"text",text:JSON.stringify(l(t)?{message:t.message}:{message:t.message,value:Qe(t.value)})}]}),wt=async t=>{if(!T(t))return c("No refs",t);let o=await z(t.repoRoot);return l(o)?c("Unbooted",t):Je(t)},i=t=>o=>A(async()=>{let n=await wt(o);return w(l(n)?n:await t(n.value))}),bt="A board's or lane's description (boardDescription, laneDescription) holds the team's conventions for it; follow them unless they conflict with your user's instructions. Replies name nodes by their 7-character ref, and every id input takes one.",_t=t=>({...t,tools:t.tools.map(({inputSchema:{$schema:o,...n},...s})=>({...s,inputSchema:n}))}),yt=t=>{let o=t.server,n=o.setRequestHandler.bind(o);o.setRequestHandler=((s,r)=>n(s,s===ft?async(a,d)=>_t(await r(a,d)):r))},Rt=()=>{let t=new gt({name:"epiq",version:"0.2.30"},{instructions:bt});return yt(t),t.registerTool("epiq_state_get",{description:"Get the full current Epiq state, including nodes and event log",inputSchema:e.object({repoRoot:e.string().optional()})},i(ie)),t.registerTool("epiq_issue_list",{description:"List Epiq issues: id, title, swimlane title and id, goal, tag and assignee names. Narrow with boardId, swimlaneId, tag, assignee or query (matched against title and description). brief: false adds descriptions and the rest; read one ticket with epiq_issue_get instead.",inputSchema:e.object({repoRoot:e.string().optional(),includeClosed:e.boolean().optional(),boardId:e.string().optional(),swimlaneId:e.string().optional(),tag:e.string().optional(),assignee:e.string().optional(),query:e.string().optional(),brief:e.boolean().optional()})},i(o=>re({...o,brief:o.brief??!0}))),t.registerTool("epiq_issue_get",{description:"Get one Epiq issue by ref or id, with its comments in log order: the latest three whole, earlier ones by their first three lines (olderComments says so); allComments returns every one whole. When you know which issue you want, prefer it to listing the board or epiq_state_get.",inputSchema:e.object({idOrRef:e.string().min(1),allComments:e.boolean().optional(),repoRoot:e.string().optional()})},i(se)),t.registerTool("epiq_issue_stats",{description:"Code stats for one issue, from the commits whose subject carries its ref: how big the change is, how it is spread, and how much rewrote its own work. Reads git only, so it is safe while other work is in flight. No such commits reads as zero size, which is not the same as no work: a commit only links if its subject opens with the ref.",inputSchema:e.object({idOrRef:e.string().min(1),repoRoot:e.string().optional()})},async o=>w(await Re(o))),t.registerTool("epiq_board_list",{description:"List Epiq boards",inputSchema:e.object({repoRoot:e.string().optional()})},i(ue)),t.registerTool("epiq_board_create",{description:"Create an Epiq board. It comes out without swimlanes; add them with epiq_swimlane_create. Refused while time travelling.",inputSchema:e.object({repoRoot:e.string().optional(),title:e.string().min(1).max(300)})},i(ge)),t.registerTool("epiq_swimlane_list",{description:"List Epiq swimlanes",inputSchema:e.object({repoRoot:e.string().optional(),boardId:e.string().optional()})},i(fe)),t.registerTool("epiq_swimlane_create",{description:"Create an Epiq swimlane on a board",inputSchema:e.object({repoRoot:e.string().optional(),boardId:e.string().min(1),title:e.string().min(1).max(300)})},i(we)),t.registerTool("epiq_swimlane_title_edit",{description:"Edit an Epiq swimlane title",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1),title:e.string().min(1).max(300)})},i(be)),t.registerTool("epiq_board_title_edit",{description:"Edit an Epiq board title. Refused while time travelling.",inputSchema:e.object({repoRoot:e.string().optional(),boardId:e.string().min(1),title:e.string().min(1).max(300)})},i(he)),t.registerTool("epiq_swimlane_description_edit",{description:"Edit a swimlane's markdown description: the conventions for work in that lane. Empty clears it.",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1),description:e.string().max(k("swimlane"))})},i(W)),t.registerTool("epiq_swimlane_rule_set",{description:"Turn a swimlane rule on or off. The one rule is `done`: an open ticket in a lane with it counts as done for its goal. A lane named Done always counts.",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1),rule:e.enum(H),on:e.boolean()})},i(te)),t.registerTool("epiq_board_description_edit",{description:"Edit a board's markdown description: the conventions for work on that board. Empty clears it. Refused while time travelling.",inputSchema:e.object({repoRoot:e.string().optional(),boardId:e.string().min(1),description:e.string().max(k("board"))})},i(X)),t.registerTool("epiq_swimlane_move",{description:"Move/reorder an Epiq swimlane, optionally to another board",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1),boardId:e.string().min(1),position:e.discriminatedUnion("at",[e.object({at:e.literal("start")}),e.object({at:e.literal("end")}),e.object({at:e.literal("before"),sibling:e.string().min(1)}),e.object({at:e.literal("after"),sibling:e.string().min(1)})]).optional()})},i(_e)),t.registerTool("epiq_swimlane_delete",{description:"Delete (archive) an Epiq swimlane. This also archives every issue contained in it.",inputSchema:e.object({repoRoot:e.string().optional(),swimlaneId:e.string().min(1)})},i(ye)),t.registerTool("epiq_issue_create",{description:"Create an Epiq issue in a swimlane: parentId is the swimlane id (epiq_swimlane_list), never a board id. Optionally set description, tags, and assignees atomically in the same call instead of separate follow-up edits.",inputSchema:e.object({title:e.string().min(1).max(300),parentId:e.string().min(1),repoRoot:e.string().optional(),description:e.string().max(2e4).optional(),tagNames:e.array(e.string().max(60)).max(20).optional(),assigneeNames:e.array(e.string().max(80)).max(20).optional()})},i(ae)),t.registerTool("epiq_issue_description_edit",{description:"Edit the markdown description of an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),description:e.string().max(2e4),repoRoot:e.string().optional()})},i(de)),t.registerTool("epiq_issue_title_edit",{description:"Edit the title of an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),title:e.string().min(1).max(300),repoRoot:e.string().optional()})},i(me)),t.registerTool("epiq_issue_tag_add",{description:"Add a tag to an Epiq issue, or to many at once with issueIds, creating the tag if it does not exist",inputSchema:e.object({issueId:e.string().min(1).optional(),issueIds:e.array(e.string().min(1)).min(1).max(200).optional(),tagName:e.string().min(1).max(60),repoRoot:e.string().optional()})},i(ke)),t.registerTool("epiq_issue_goal_set",{description:"Put an Epiq issue in a goal (a group of tickets serving one outcome, on any board), by the goal's name or id, creating the goal if there is none of that name; null takes it out. One goal per ticket. epiq_goal_list lists every goal with its progress.",inputSchema:e.object({issueId:e.string().min(1),goal:e.string().min(1).nullable(),repoRoot:e.string().optional()})},i(Q)),t.registerTool("epiq_goal_list",{description:"List the goals in the project: each a group of tickets serving one outcome, with done/total (done is closed, or in a done lane: see epiq_swimlane_rule_set), closed once all its tickets are, and the boards its tickets are on. Open goals first. Put a ticket in one with epiq_issue_goal_set; epiq_goal_stats gives its code stats.",inputSchema:e.object({repoRoot:e.string().optional()})},i(J)),t.registerTool("epiq_goal_stats",{description:"Code stats for a goal, by its name or id: epiq_issue_stats over every commit of every ticket in it, each commit counted once. Its `ref` field holds the goal id, not a ticket ref.",inputSchema:e.object({goal:e.string().min(1),repoRoot:e.string().optional()})},i(Ie)),t.registerTool("epiq_goal_create",{description:"Create a goal with no tickets yet. To create one and put a ticket in it at once, use epiq_issue_goal_set with the new name.",inputSchema:e.object({name:e.string().min(1),repoRoot:e.string().optional()})},i(V)),t.registerTool("epiq_goal_rename",{description:"Rename a goal; its colour and tickets stay. Ids are in epiq_goal_list.",inputSchema:e.object({goalId:e.string().min(1),name:e.string().min(1),repoRoot:e.string().optional()})},i(Z)),t.registerTool("epiq_goal_description_edit",{description:"Edit a goal's markdown description: what it is for and when it is done. Empty clears it. epiq_goal_list returns it.",inputSchema:e.object({goal:e.string().min(1),description:e.string().max(k("goal")),repoRoot:e.string().optional()})},i(Y)),t.registerTool("epiq_goal_remove",{description:"Remove a goal; its tickets stay where they are, in no goal. Ids are in epiq_goal_list.",inputSchema:e.object({goalId:e.string().min(1),repoRoot:e.string().optional()})},i(ee)),t.registerTool("epiq_issue_tag_remove",{description:"Remove a tag from an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),tagId:e.string().min(1),repoRoot:e.string().optional()})},i(Te)),t.registerTool("epiq_contributor_list",{description:"List people who can be assigned: everyone who has authored an event (optionally scoped to one board) unioned with the contributor registry. Use these ids with epiq_issue_assignee_add rather than assigning by name.",inputSchema:e.object({boardId:e.string().min(1).optional(),repoRoot:e.string().optional()})},i(Ae)),t.registerTool("epiq_contributor_remove",{description:"Remove an external contributor from the assignee suggestion lists. Their id and every reference to it survive, so existing assignments stay intact and the event log is untouched. Refused for anyone who has authored events, since their name is in the log regardless.",inputSchema:e.object({contributorId:e.string().min(1),repoRoot:e.string().optional()})},i(je)),t.registerTool("epiq_tag_remove",{description:"Delete a tag from the whole workspace. It disappears from every ticket, picker and filter, but its id and history stay in the event log, and the name is free for a new tag.",inputSchema:e.object({tagId:e.string().min(1),repoRoot:e.string().optional()})},i(qe)),t.registerTool("epiq_tag_restore",{description:"Put back a tag deleted with epiq_tag_remove, on every ticket that still carried it.",inputSchema:e.object({tagId:e.string().min(1),repoRoot:e.string().optional()})},i(Se)),t.registerTool("epiq_contributor_restore",{description:"Put a contributor removed with epiq_contributor_remove back into the suggestion lists, under the name they were created with.",inputSchema:e.object({contributorId:e.string().min(1),repoRoot:e.string().optional()})},i(xe)),t.registerTool("epiq_issue_assignee_add",{description:"Assign a contributor to an Epiq issue: self: true for yourself, else preferably assigneeId (epiq_contributor_list; fails if unknown). assigneeName matches an existing contributor by name; with createUnlinked it adds someone new as an external assignee.",inputSchema:e.object({issueId:e.string().min(1),assigneeId:e.string().min(1).optional(),self:e.boolean().optional(),assigneeName:e.string().min(1).max(80).optional(),createUnlinked:e.boolean().optional(),repoRoot:e.string().optional()})},i(Ee)),t.registerTool("epiq_issue_assignee_remove",{description:"Remove an assignee from an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),assigneeId:e.string().min(1),repoRoot:e.string().optional()})},i(Ne)),t.registerTool("epiq_issue_comment_add",{description:"Add a comment to an Epiq issue",inputSchema:e.object({issueId:e.string().min(1),body:e.string().min(1).max(4e3),repoRoot:e.string().optional()})},i(Ce)),t.registerTool("epiq_issue_attachment_add",{description:"Attach an image file to an Epiq issue. Returns `markdown` \u2014 paste it into a comment or description body to render the image inline.",inputSchema:e.object({issueId:e.string().min(1),filePath:e.string().min(1),name:e.string().min(1).max(200).optional(),repoRoot:e.string().optional()})},i(oe)),t.registerTool("epiq_issue_comment_delete",{description:"Delete a comment from an Epiq issue",inputSchema:e.object({commentId:e.string().min(1),repoRoot:e.string().optional()})},i(Le)),t.registerTool("epiq_issue_comment_edit",{description:"Replace the body of one of your own comments on an Epiq issue",inputSchema:e.object({commentId:e.string().min(1),body:e.string().min(1).max(4e3),repoRoot:e.string().optional()})},i(Pe)),t.registerTool("epiq_issue_reopen",{description:"Reopen a closed Epiq issue, restoring it to its previous swimlane",inputSchema:e.object({issueId:e.string().min(1),repoRoot:e.string().optional()})},i(ce)),t.registerTool("epiq_issue_close",{description:"Close an Epiq issue, or many at once with issueIds \u2014 one call, an outcome per ticket",inputSchema:e.object({issueId:e.string().min(1).optional(),issueIds:e.array(e.string().min(1)).min(1).max(200).optional(),repoRoot:e.string().optional()})},i(le)),t.registerTool("epiq_issue_move",{description:"Move an Epiq issue to another swimlane (parentId is the swimlane id), or many at once with issueIds \u2014 one call, an outcome per ticket",inputSchema:e.object({issueId:e.string().min(1).optional(),issueIds:e.array(e.string().min(1)).min(1).max(200).optional(),parentId:e.string().min(1),position:e.discriminatedUnion("at",[e.object({at:e.literal("start")}),e.object({at:e.literal("end")}),e.object({at:e.literal("before"),sibling:e.string().min(1)}),e.object({at:e.literal("after"),sibling:e.string().min(1)})]).optional(),repoRoot:e.string().optional()})},i(pe)),t.registerTool("epiq_actor_assume",{description:'Take a board identity for this session, so your writes are attributed to you, not the user: the name you announced: the model provider, a slash, then a short human first name, e.g. "claude/peter". Takes effect on the next write. A name the server was launched with wins; re-assuming is harmless. Reuse an existing agent identity (epiq_contributor_list) before inventing one: a name is permanent once it has authored anything.',inputSchema:e.object({name:e.string().min(1).max(80),repoRoot:e.string().optional()})},i(ve)),t.registerTool("epiq_contributor_email_link",{description:"Bind a git author address to a contributor, so their commits and board activity read as one person. Defaults to you; pass contributorId for a colleague who does not use epiq. The repository's configured git address links itself on your first write, so this is for older addresses and other people. An address two contributors claim resolves to neither.",inputSchema:e.object({email:e.string().min(1).max(254),contributorId:e.string().min(1).optional(),repoRoot:e.string().optional()})},i(Me)),t.registerTool("epiq_contributor_email_unlink",{description:"Retract a link between a git address and a contributor; only its writer or the contributor it names may. The address stops resolving; the event stays in the log.",inputSchema:e.object({email:e.string().min(1).max(254),contributorId:e.string().min(1).optional(),repoRoot:e.string().optional()})},i(Oe)),t.registerTool("epiq_contributor_email_suggest",{description:"Unclaimed addresses in this repository's history that look like yours; until linked, their commits show a raw git name. Show the list and link only what the user confirms: each link is permanent and replicates to every clone.",inputSchema:e.object({repoRoot:e.string().optional()})},i(Ue)),t.registerTool("epiq_contributor_email_list",{description:"Every git address bound to a contributor, and who holds it. One with several claimants is flagged contested and resolves to none.",inputSchema:e.object({repoRoot:e.string().optional()})},i(Ge)),t.registerTool("epiq_project_init",{description:'Set up a new Epiq board in the git repository at repoRoot (default: the current directory), which must have no uncommitted changes. The first time on a machine, ask the user for userName (how they appear on the board: theirs, never your agent identity), preferredEditor (e.g. "vim" or "code --wait") and autoSync (whether the TUI and GUI sync on their own); without them it fails naming what is missing, keeping what was given. Answers the machine already has stay. Commits the state branch and .epiq/project.json and tries to push both; a failed push is a warning.',inputSchema:e.object({repoRoot:e.string().optional(),userName:e.string().min(1).max(80).optional(),preferredEditor:e.string().min(1).max(200).optional(),autoSync:e.boolean().optional()})},i(et)),t.registerTool("epiq_skill_install",{description:"Write the Epiq workflow skill to .claude/skills/epiq/SKILL.md in the repository, so agents there follow the board rules. In a new project, run it after epiq_project_init, which refuses the uncommitted file; on an existing board, any time. A differing file is overwritten only with force: true.",inputSchema:e.object({repoRoot:e.string().optional(),force:e.boolean().optional()})},i(ot)),t.registerTool("epiq_sync",{description:"Pull and push Epiq state with the git remote, then fetch the repos connected with epiq_repo_connect. Other tools never sync: call this to see or publish remote changes.",inputSchema:e.object({repoRoot:e.string().optional()})},async o=>w(await Ke(await A(()=>ne(o)),o))),t.registerTool("epiq_repo_list",{description:"List the repos connected to the board, whose commits count as its own: each one's clone status, size on disk, last fetch error and whether it is past the board's limit (and so not fetched), plus their total on this machine.",inputSchema:e.object({repoRoot:e.string().optional()})},async o=>w(await De(o))),t.registerTool("epiq_repo_connect",{description:"Connect another repo to the board by URL (https://, ssh://, git:// or user@host:path; never a local path), so its commits prefixed with a ticket's ref link to it; or change a connected repo's options, which applies at once. Board-wide: every contributor's epiq downloads it on their next sync. The first call clones to staging and returns a preview (size, host, default branch, latest commit, commits naming this board's tickets \u2014 none suggests the wrong repo), writing nothing; show it to the user, and only once they agree call again with confirm: true and the same options. branches: default or all. since: how much history to clone, board (default), all, or YYYY-MM-DD. label: its name on the board, the URL's basename unless set; empty resets it. Uses your git credentials: ssh-agent or a credential helper must already work.",inputSchema:e.object({repoRoot:e.string().optional(),url:e.string().min(1).max(D),branches:e.enum(["default","all"]).optional(),since:e.string().max(20).optional(),label:e.string().max(24).optional(),confirm:e.boolean().optional()})},async o=>w(await $e(o))),t.registerTool("epiq_repo_disconnect",{description:"Disconnect a repo by its label (when no other repo shares it), id or URL, and delete this machine's clone of it.",inputSchema:e.object({repoRoot:e.string().optional(),repo:e.string().min(1).max(2e3)})},async o=>w(await He(o))),t},mo=async()=>{let t=Rt(),o=new ht;await t.connect(o)};export{bt as MCP_INSTRUCTIONS,Rt as createMcpServer,w as resultJson,mo as startMcpServer};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Gc as a,Hc as b,Ic as c,Jc as d,Kc as e,Lc as f}from"./chunk-7KCKIQRO.js";import"./chunk-WUCQWBQE.js";import"./chunk-5RIQL7P4.js";export{e as areVirtualNodesEnabled,a as createOrUpdateVirtualField,b as createOrUpdateVirtualFieldList,c as materializeTicketVirtualNodes,f as materializeVirtualNodes,d as setVirtualNodesEnabled};
2
+ import{Ic as a,Jc as b,Kc as c,Lc as d,Mc as e,Nc as f}from"./chunk-5WUPZIRV.js";import"./chunk-WUCQWBQE.js";import"./chunk-5RIQL7P4.js";export{e as areVirtualNodesEnabled,a as createOrUpdateVirtualField,b as createOrUpdateVirtualFieldList,c as materializeTicketVirtualNodes,f as materializeVirtualNodes,d as setVirtualNodesEnabled};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epiq",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "EPIQ - ergonomic, distributed CLI-first issue tracker TUI ready for the agentic era",
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
- import{a as F}from"./chunk-2KWSZBVJ.js";import{$ as vn,A as ee,B as Et,D as on,E as rn,F as nn,G as sn,H as an,I as ln,J as mn,K as cn,M as dn,N as un,P as pn,Q as fn,R as gn,S as yn,T as io,U as Ct,V as hn,W as xn,Y as bn,Z as wn,a as Dr,b as pe,ba as Sn,c as ie,ca as In,d as Or,e as Ur,ea as Tn,f as Gr,fa as En,g as $r,ga as Pt,h as Fr,ha as At,i as Hr,ia as Cn,j as kr,ja as Pn,ka as An,l as Wr,la as so,m as Vr,ma as ao,n as Yr,na as Nn,oa as Ae,p as Kr,pa as lo,q as qr,qa as Mn,r as Jr,ra as Rn,s as Xr,t as zr,ta as Bn,u as Qr,ua as Ln,v as Zr,va as jn,w as en,y as tn,z as Tt}from"./chunk-ITVJCUOL.js";import{$ as zo,$b as wr,Ab as cr,Ac as Br,Bb as ne,Bc as Lr,Cb as dr,Cc as jr,Db as ur,Dc as q,E as ro,Ea as Qo,Eb as x,Fb as pr,G as ko,Gb as ht,Jb as Ce,Jc as _r,L as Wo,La as $e,Lb as fr,Ma as Zo,Mb as ue,P as Vo,Q as Yo,Qb as xt,Rb as bt,Sa as de,T as ct,U as Te,Ua as Ee,V as Ko,Vb as gr,Wb as yr,X as H,Xb as hr,Y as qo,Yb as xr,Z as Jo,Zb as wt,_ as Xo,_b as br,a as _o,aa as dt,ac as vt,b as st,ba as Ge,bb as er,bc as vr,c as at,cb as tr,cc as Sr,da as ut,db as or,dc as Ir,ea as pt,eb as gt,g as lt,gc as Tr,ha as ft,hb as yt,hc as Er,ib as rr,ic as Cr,j as Do,jc as Pr,k as oe,l as Oo,n as Ie,nc as Ar,o as re,oc as no,p as Uo,pc as Nr,q as Ue,qc as St,r as m,rb as nr,rc as It,s as Go,sb as ir,sc as Mr,t as $o,tb as sr,tc as Rr,u as Fo,ub as ar,uc as Q,v as Ho,vb as lr,vc as Pe,w as oo,x as mt,xc as O,yc as Fe,zb as mr}from"./chunk-7KCKIQRO.js";import{f as it,g as jo}from"./chunk-WUCQWBQE.js";import{b as G,c as T,d as h,e as $}from"./chunk-5RIQL7P4.js";import z from"chalk";import{render as Ol}from"ink";import Ul from"meow";import Gl from"react";import ys from"node:os";import hs from"node:path";import He from"chalk";var Nt=["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ","\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557","\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551","\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551\u2584\u2584 \u2588\u2588\u2551","\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D","\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2580\u2580\u255D "];var Mt=" ",On=He.hex(m.secondary2),_n=He.hex(m.gray),xs=()=>{let t=Nt[0].length+Nt.length*2;return Nt.map((o,n)=>Array.from(o).map((s,u)=>{let[r,f,y]=$o((u+n*2)/t);return He.rgb(r,f,y)(s)}).join(""))},bs=e=>{let t=ys.homedir();return e===t||e.startsWith(t+hs.sep)?"~"+e.slice(t.length):e},Dn=(e,t)=>`${Mt}${He.hex(m.accent)("\u279C")} ${On(e.padEnd(9))}${t}`,mo=({url:e,repoRoot:t,version:o,reused:n})=>["",...xs().map(s=>Mt+s),"",`${Mt}${On("gui")} ${_n(`\xB7 v${o}`)}`,"",Dn("Local",He.hex(m.accent).bold(e)),Dn("Project",bs(t)),"",Mt+_n(n?"already running for this project \xB7 opened in your browser":"press ctrl+c to stop"),""].join(`
3
- `);import{spawn as ws}from"node:child_process";import ke from"node:process";var Un=(e=ke.platform,t=ke.env)=>e==="darwin"||e==="win32"||!!(t.DISPLAY||t.WAYLAND_DISPLAY||t.BROWSER),Gn=e=>{let t=ke.platform==="darwin"?"open":ke.platform==="win32"?"cmd":"xdg-open",o=ke.platform==="win32"?["/c","start","",e]:[e];try{let n=ws(t,o,{detached:!0,stdio:"ignore"});n.on("error",s=>{logger.info(`[gui] could not open a browser: ${s.message}`)}),n.unref()}catch(n){logger.info(`[gui] could not open a browser: ${String(n)}`)}};import{readFile as hi,stat as da}from"node:fs/promises";import{createHash as ua}from"node:crypto";import pa from"node:http";import be from"node:path";import{fileURLToPath as fa}from"node:url";import vs from"node:fs";import $n from"node:path";var We=Number(process.env.EPIQ_GUI_PORT)||3710,co="/api/instance",uo="epiq",Rt=e=>{try{return vs.realpathSync($n.resolve(e))}catch{return $n.resolve(e)}},Ss=e=>{let t=e;return typeof t=="object"&&t!==null&&t.app===uo&&typeof t.repoRoot=="string"&&typeof t.version=="string"&&typeof t.pid=="number"},Fn=async(e,t=500)=>{let o=new AbortController,n=setTimeout(()=>o.abort(),t);try{let s=await fetch(`http://127.0.0.1:${e}${co}`,{signal:o.signal});if(!s.ok)return null;let u=await s.json();return Ss(u)?u:null}catch{return null}finally{clearTimeout(n)}};var Is=6e4,Hn=e=>{let t,o=!1,n=!1,s=0,u=()=>{let g=Oo();return h(g)?(st.error(g.message),null):g.value},r=()=>{let g=u();if(!g)return!1;let l=Ie();return h(l)?!1:!!g.autoSync&&Ar(l.value)===null},f=()=>{let g=_o(u()?.autoSyncDebounceMs);return Math.max(0,g-(Date.now()-s))},y=async()=>{if(!(o||n)&&r()){n=!0,s=Date.now();try{await pe(async()=>{if(ie().mode!=="live")return!1;let l=await Et({repoRoot:e.project.repoRoot});return await e.publisher.publishIfLogMoved(),!h(l)})&&e.onReposFetch?.(Is)}finally{n=!1,p()}}};function p(){o||!r()||n||t||(t=setTimeout(()=>{t=void 0,y()},f()))}return p(),{queueSync:p,reschedule:()=>{t&&(clearTimeout(t),t=void 0),p()},dispose:()=>{o=!0,t&&clearTimeout(t)}}};var Bt=(e,t)=>{let o,n=!1,s=async()=>{let u=Date.now();try{await e()}catch(r){logger.error("[watch] check threw",r)}n||(o=setTimeout(s,Math.max(t,Date.now()-u)))};return o=setTimeout(s,t),()=>{n=!0,o&&clearTimeout(o)}};var Ts=/^(```|~~~)[^\n]*\n[\s\S]*?\n\1[ \t]*$/gm,Es=/^(?:```|~~~)[\s\S]*$/m,Cs=/^[ \t]*\|?[ \t]*:?-{2,}:?[ \t]*(?:\|[ \t]*:?-{2,}:?[ \t]*)*\|?[ \t]*$/gm,Ps=/^[ \t]*\|.*$/gm,As=/!\[[^\]]*\]\([^)]*\)/g,Ns=/\[([^\]]*)\]\([^)]*\)/g,Ms=/^[ \t]*(?:[#>]+[ \t]*|[-*+][ \t]+|\d+[.)][ \t]+)/gm,Rs=/^[ \t]*(?:-{3,}|\*{3,}|_{3,})[ \t]*$/gm,Bs=/[*`~]/g,Ls=/(?<![0-9A-Za-z])_|_(?![0-9A-Za-z])/g,js=e=>Xo(e.replace(Ts," ").replace(Es," ").replace(As," ").replace(Ns,"$1").replace(Rs," ").replace(Cs," ").replace(Ps,t=>t.replaceAll("|"," ")).replace(Ms,"").replace(Bs,"").replace(Ls,"")),_s=.5,kn=(e,t)=>{if(t<=0)return"";let o=js(e);if(o.length<=t)return o;let n="\u2026",s=t-n.length;if(s<=0)return n.slice(0,t);let u=o.slice(0,s),r=u.lastIndexOf(" ");return`${(r>=s*_s?u.slice(0,r):u).trimEnd()}${n}`};var Ds=e=>({...e,boards:(e.boards??[]).map(t=>({...t,swimlanes:(t.swimlanes??[]).map(o=>({...o,issues:(o.issues??[]).map(n=>({...n,description:""}))}))})),commentsByIssueId:Object.fromEntries(Object.entries(e.commentsByIssueId??{}).filter(([,t])=>t.length>0).map(([t,o])=>[t,o.map(n=>({...n,body:""}))])),attachmentsByIssueId:Object.fromEntries(Object.entries(e.attachmentsByIssueId??{}).filter(([,t])=>t.length>0))}),xe=e=>h(e)?e:T(e.message,Ds(e.value)),Os=(e,t)=>(e.boards??[]).flatMap(o=>o.swimlanes??[]).flatMap(o=>o.issues??[]).find(o=>o.id===t),Wn=(e,t)=>{let o=Os(e,t);return{issueId:t,description:o?.description??"",comments:e.commentsByIssueId?.[t]??[]}},Us=280,Vn=(e,t)=>({issueId:e,excerpt:kn(t,Us)});var Gs=1e3,Yn=e=>{let t=oe(e.repoRoot);if(h(t))return null;let o=lt({repoRoot:t.value});return h(o)?null:o.value},Kn=e=>{let t=()=>{let r=Yn(e.project);return r===null?null:$e(r)},o=()=>{let r=or(e.project.repoRoot);return h(r)?null:JSON.stringify([r.value.live,r.value.overLimit])},n=o(),s=t();return{publishIfLogMoved:async()=>{let r=Yn(e.project);if(r===null||await gt(r))return;let f=$e(r);if(f===s)return;s=f;let y=xe(await ee({repoRoot:e.project.repoRoot}));if(h(y))return;ue({type:"state",payload:y});let p=o();p!==null&&p!==n&&(n=p,ue({type:"repos:changed"}))}}},qn=(e,t=Gs)=>Bt(()=>pe(async()=>{ie().mode==="live"&&await e.publishIfLogMoved()}),t);var $s=new Set(["127.0.0.1","[::1]","localhost","epiq.localhost"]),po=(e,t)=>{if(!e)return!1;if(e==="null")return!0;let o;try{o=new URL(e)}catch{return!0}return o.protocol!=="http:"||o.port!==String(t)?!0:!$s.has(o.hostname)},Jn=(e,t)=>e.method==="GET"||e.method==="HEAD"?null:po(e.origin,t)?{status:403,message:"Cross-origin request"}:e.method==="POST"&&!(e.contentType??"").toLowerCase().startsWith("application/json")?{status:415,message:"Expected content-type: application/json"}:null;import{WebSocketServer as aa}from"ws";var Hs=new RegExp(`^[0-9A-Z]{${7}}$`),ks=e=>{let t=e.indexOf(" ");if(t===-1)return null;let o=e.slice(0,t).toUpperCase();return Hs.test(o)?o:null},Ws=e=>{let t={};for(let o of e){let n=ks(o.subject),s=new Set([...n?[n]:[],...o.refs??[]]);for(let u of s)(t[u]??=[]).push({time:o.time,insertions:o.insertions,deletions:o.deletions})}return t},Xn=async(e={})=>{let t=await yt({repoRoot:e.repoRoot});return h(t)?t:T("Collected commits by ref",Ws(t.value))};var Vs="Filed here",zn=(e,t)=>e.set(t,(e.get(t)??0)+1),Qn=(e,t,o)=>[...e.entries()].map(([n,s])=>({laneId:n,title:n===null?Vs:o(n),count:s,share:t===0?0:s/t})).sort((n,s)=>s.count-n.count||n.title.localeCompare(s.title)),Zn=(e,t,o)=>{let n=new Map,s=new Map,u=0,r=0;for(let f of t)for(let[y,p]of f.entries()){if(p!==e)continue;u++,zn(n,f[y-1]??null);let g=f[y+1];g!==void 0&&(r++,zn(s,g))}return{arrived:u,departed:r,arrivesFrom:Qn(n,u,o),movesOnTo:Qn(s,r,o)}};var Ve=1440*60*1e3,Ys=({laneIds:e,journeys:t,now:o,days:n})=>{let s=o-(n-1)*Ve,u=new Map(e.map(r=>[r,Array.from({length:n},()=>[])]));for(let r of t)for(let[f,y]of r.entries()){let p=u.get(y.laneId);if(!p)continue;let g=r[f+1]?.enteredAt,l=Math.max(0,Math.ceil((y.enteredAt-s)/Ve)),d=g===void 0?n-1:Math.min(n-1,Math.ceil((g-s)/Ve)-1);for(let a=l;a<=d;a++)p[a].push(s+a*Ve-y.enteredAt)}return Object.fromEntries([...u].map(([r,f])=>[r,f.map((y,p)=>({t:s+p*Ve,median:Ln(y.sort((g,l)=>g-l)),count:y.length}))]))},ei=({laneId:e,journeys:t,now:o,days:n})=>Ys({laneIds:[e],journeys:t,now:o,days:n})[e];var Ks=40,qs=30,Js=()=>({tickets:0,commits:0,insertions:0,deletions:0}),Xs=async(e,t)=>{let o=Js();for(let n of t.slice(0,Ks)){let s=await Ct({repoRoot:e,idOrRef:de(n)});h(s)||s.value.shape.commits===0||(o.tickets++,o.commits+=s.value.shape.commits,o.insertions+=s.value.shape.insertions,o.deletions+=s.value.shape.deletions)}return o},ti=async e=>{let t=kr();if(h(t))return t;let{nodes:o}=t.value;if(!o[e.swimlaneId])return G("Swimlane not found");let s=Object.values(o).filter(ct),u=s.map(y=>Dr(y.log??[],ro(y.id))),r=Zn(e.swimlaneId,u.map(y=>y.map(p=>p.laneId)),y=>o[y]?.title??"Elsewhere"),f=s.filter(y=>y.parentNodeId===e.swimlaneId).map(y=>y.id);return T("Derived swimlane stats",{swimlaneId:e.swimlaneId,createdAt:ro(e.swimlaneId),...r,stayTrend:ei({laneId:e.swimlaneId,journeys:u,now:Date.now(),days:qs}),code:await Xs(e.repoRoot,f)})};var oi=new Set(["sync","email:link","email:unlink","repo:connect","repo:disconnect","issues:create","board:create","board:edit:title","swimlane:create","swimlane:edit:title","board:edit:description","swimlane:edit:description","swimlane:rule:set","goal:edit:description","swimlane:delete","swimlane:move","issues:move","issue:close","issue:reopen","issue:edit:title","issue:edit:description","issue:tag:add","issue:tag:remove","issue:goal:set","goal:create","goal:rename","goal:remove","contributor:remove","tag:remove","issue:assignee:add","issue:assignee:remove","issue:comment:add","issue:comment:delete"]);var ri=e=>{let t=Ge({exclude:e});return h(t)?[]:t.value.map(o=>({projectId:o.projectId,name:dt(o.root),root:o.root,lastOpenedAt:o.lastOpenedAt}))},ni=e=>{let t=Ge();if(h(t))return G(t.message);let o=t.value.find(n=>n.root===e);return o?T("Resolved recent project",o.root):G(`Not a recent project: ${e}`)};import{z as c}from"zod";var v=c.string().min(1),ii=c.discriminatedUnion("at",[c.object({at:c.literal("start")}),c.object({at:c.literal("end")}),c.object({at:c.literal("before"),sibling:v}),c.object({at:c.literal("after"),sibling:v})]),ae=c.number().finite(),w=(e,t)=>c.object({type:c.literal(e),payload:t}),le=e=>c.object({type:c.literal(e)}),zs=c.discriminatedUnion("type",[le("state:get"),le("issues:list"),le("sync"),le("time-travel:live"),le("diff-stats:get"),w("project:open",c.object({root:c.string().min(1)})),w("issues:create",c.object({title:c.string(),parentId:v,description:c.string().optional(),tagNames:c.array(c.string()).optional()})),w("board:create",c.object({title:c.string()})),w("board:edit:title",c.object({boardId:v,title:c.string()})),w("swimlane:create",c.object({title:c.string(),boardId:v})),w("swimlane:edit:title",c.object({swimlaneId:v,title:c.string()})),w("board:edit:description",c.object({boardId:v,description:c.string()})),w("swimlane:edit:description",c.object({swimlaneId:v,description:c.string()})),w("swimlane:rule:set",c.object({swimlaneId:v,rule:c.string().min(1).max(64),on:c.boolean()})),w("goal:edit:description",c.object({goalId:v,description:c.string()})),w("swimlane:delete",c.object({swimlaneId:v})),w("swimlane:move",c.object({swimlaneId:v,boardId:v,position:ii.optional()})),w("issues:move",c.object({issueId:v,parentId:v,position:ii.optional()})),w("issue:edit:title",c.object({issueId:v,title:c.string()})),w("issue:edit:description",c.object({issueId:v,description:c.string()})),w("issue:tag:add",c.object({issueId:v,tagName:c.string()})),w("issue:goal:set",c.object({issueId:v,goal:v.nullable()})),w("goal:create",c.object({name:c.string(),issueId:v.optional()})),w("goal:rename",c.object({goalId:v,name:c.string()})),w("goal:remove",c.object({goalId:v})),w("issue:tag:remove",c.object({issueId:v,tagId:v})),w("contributor:remove",c.object({contributorId:v})),w("tag:remove",c.object({tagId:v})),c.object({type:c.literal("contributors:get"),payload:c.object({boardId:v.optional()}).optional()}),w("issue:assignee:add",c.object({issueId:v,assigneeId:v.optional(),assigneeName:c.string().optional(),createUnlinked:c.boolean().optional()})),w("issue:assignee:remove",c.object({issueId:v,assigneeId:v})),w("issue:close",c.object({issueId:v})),w("issue:reopen",c.object({issueId:v})),w("issue:comment:add",c.object({issueId:v,body:c.string()})),w("issue:comment:delete",c.object({issueId:v,commentId:v})),w("issue:comment:edit",c.object({issueId:v,commentId:v,body:c.string()})),w("issue:get",c.object({issueId:v})),w("issue:preview:get",c.object({issueId:v})),c.object({type:c.literal("timeline:get"),payload:c.object({start:ae.optional(),end:ae.optional(),boardId:v.optional(),requestId:c.number().finite().optional()}).optional()}),c.object({type:c.literal("commits:get"),payload:c.object({start:ae.optional(),end:ae.optional(),requestId:c.number().finite().optional()}).optional()}),w("time-travel:scrub",c.object({targetTime:ae})),w("time-travel:checkout-event",c.object({eventId:c.string().min(1)})),w("commit:inspect",c.object({sha:c.string(),repo:c.string().max(80).optional()})),le("repos:get"),w("repo:preview",c.object({url:c.string().min(1).max(mt),branches:c.enum(["default","all"]).optional(),since:c.string().max(20).optional(),label:c.string().max(24).optional()})),w("repo:preview:cancel",c.union([c.object({repo:c.string().min(1).max(80)}),c.object({url:c.string().min(1).max(mt)})])),w("repo:connect",c.object({url:c.string().min(1).max(mt),branches:c.enum(["default","all"]).optional(),since:c.string().max(20).optional(),label:c.string().max(24).optional()})),w("repo:disconnect",c.object({repo:c.string().min(1).max(2e3)})),c.object({type:c.literal("emails:get")}),w("email:link",c.object({email:c.string().min(1).max(254),contributorId:c.string().min(1).optional()})),w("email:unlink",c.object({email:c.string().min(1).max(254),contributorId:c.string().min(1).optional()})),w("commit:diff:get",c.object({sha:c.string(),repo:c.string().max(80).optional()})),w("issue:commits:get",c.object({issueId:v})),w("issue:squashed-diff:get",c.object({issueId:v,asOfTime:ae.optional()})),w("issue:stats:get",c.object({issueId:v,asOfTime:ae.optional()})),w("swimlane:stats:get",c.object({swimlaneId:v})),w("goal:stats:get",c.object({goalId:v,key:c.string().max(200),asOfTime:ae.optional()})),w("goal:commits:get",c.object({goalId:v,key:c.string().max(200),asOfTime:ae.optional()})),w("board:overview:get",c.object({boardId:v})),le("me:stats:get"),le("settings:get"),le("version:get"),w("settings:set",c.object({autoSync:c.boolean().optional(),autoSyncIntervalMs:c.number().finite().optional()}))]);var si=e=>{let t=zs.safeParse(e);return t.success?{ok:!0,message:t.data}:{ok:!1,error:`Invalid message: ${t.error.issues.map(n=>`${n.path.join(".")||"<root>"}: ${n.message}`).join(", ")}`}};var Qs="https://registry.npmjs.org/epiq/latest",Zs="https://api.github.com/repos/ljtn/epiq/releases?per_page=30",ea="https://raw.githubusercontent.com/ljtn/epiq/main/install.sh";var ai=new Map,fo=new Map;var yo=/^v?\d+\.\d+\.\d+$/,go=e=>e.replace(/^v/,""),Ne=(e,t)=>{let o=go(e).split(".").map(Number),n=go(t).split(".").map(Number);for(let s=0;s<Math.max(o.length,n.length);s++){let u=o[s]??0,r=n[s]??0;if(u>r)return 1;if(u<r)return-1}return 0},ta=()=>process.env.IS_CI==="true"||process.env.EPIQ_NO_UPDATE_CHECK==="1",oa=async e=>{try{let t=await fetch(e,{signal:AbortSignal.timeout(5e3),headers:{accept:"application/json"}});return t.ok?await t.json():null}catch{return null}},ci=(e,t=()=>!0)=>{let o=ai.get(e);if(o&&Date.now()-o.at<o.ttl)return Promise.resolve(o.body);let n=fo.get(e);if(n)return n;let s=oa(e).then(u=>(ai.set(e,{at:Date.now(),ttl:u!==null&&t(u)?864e5:36e5,body:u}),fo.delete(e),u));return fo.set(e,s),s},ho=async e=>{if(ta())return{current:e,latest:null,updateAvailable:!1};let t=await ci(Qs),o=typeof t?.version=="string"&&yo.test(t.version)?t.version:null;return{current:e,latest:o,updateAvailable:o?Ne(o,e)>0:!1}},ra=e=>{let t=[],o=null;for(let n of e.replace(/\r/g,"").split(`
4
- `)){let s=n.trimEnd();if(s.trim())if(/^[-*] /.test(s.trimStart())&&!/^\s{2,}/.test(s))o||(o={title:null,items:[]},t.push(o)),o.items.push(s.trimStart().slice(2).trim());else if(/^\s+/.test(s)&&o?.items.length)o.items[o.items.length-1]+=` ${s.trim()}`;else{if(yo.test(s.trim())&&t.length===0)continue;o={title:s.replace(/^#+\s*/,"").trim(),items:[]},t.push(o)}}return t.filter(n=>n.items.length>0)},na=e=>{let t=e;return typeof t?.tag_name!="string"||!yo.test(t.tag_name)||t.draft||t.prerelease?null:{version:go(t.tag_name),url:typeof t.html_url=="string"?t.html_url:"",publishedAt:typeof t.published_at=="string"&&!Number.isNaN(Date.parse(t.published_at))?t.published_at:null,sections:typeof t.body=="string"?ra(t.body):[]}},li=e=>Array.isArray(e)?e.map(na).filter(t=>!!t):[],ia=async e=>li(await ci(Zs,t=>li(t).some(o=>Ne(o.version,e)===0))),sa=(e,t,o)=>e.filter(n=>Ne(n.version,t)>0&&Ne(n.version,o)<=0).sort((n,s)=>Ne(s.version,n.version)),mi={script:`curl -fsSL ${ea} | sh`,npm:"npm install --global epiq@latest"},di=async e=>{let t=await ho(e);if(!t.updateAvailable||!t.latest)return{...t,releases:[],currentPublishedAt:null,commands:mi};let o=await ia(t.latest);return{...t,releases:sa(o,e,t.latest),currentPublishedAt:o.find(n=>Ne(n.version,e)===0)?.publishedAt??null,commands:mi}};var la=1024*1024,xo=()=>{ue({type:"state",payload:xe(Tt())})},ui=()=>{ue({type:"repos:changed"})},bo=async(e,t)=>{ma(await Bn({repoRoot:e,minIntervalMs:t}))},ma=e=>{h(e)||(e.value.some(t=>t.changed)&&ui(),e.value.some(t=>t.changed||!t.ok)&&ue({type:"repos:fetched"}))},S=(e,t)=>{e.send(JSON.stringify(t))},Ye=async(e,t)=>{let o=xe(await ee({repoRoot:t}));return h(o)&&o.message===Do?S(e,{type:"state:unavailable",payload:{message:o.message,repoRoot:t,recentProjects:ri(t)}}):S(e,{type:"state",payload:o})},ca=async(e,t)=>{let o=()=>S(e,{type:"state",payload:xe(Tt())});if(ie().mode!=="live")return o();let n=await ee({repoRoot:t});return ie().mode!=="live"?o():S(e,{type:"state",payload:xe(n)})},E=async(e,t,o,n,s)=>{if(S(e,{type:n,payload:s}),h(s))return S(e,{type:"failed",payload:s.message});o(),ca(e,t)},pi=(e,t,{onStateChanged:o,onSyncSettingsChanged:n,getPort:s})=>{new aa({server:e,path:"/ws",maxPayload:la,verifyClient:r=>!po(r.origin,s())}).on("connection",r=>{fr(r),r.on("message",async f=>{let y=async l=>{let{type:d}=l,a=t.repoRoot;if(d==="project:open"){let i=ni(l.payload.root);return S(r,{type:"project:open:result",payload:i}),h(i)?void 0:(t.repoRoot=i.value,Ye(r,i.value))}if(d==="timeline:get"){let{requestId:i,...b}=l.payload??{};return S(r,{type:"timeline",requestId:i,payload:await Or({repoRoot:a,...b})})}if(d==="issue:get"){let i=l.payload?.issueId,b=Tt(),L=ln(i);return S(r,{type:"issue",payload:h(b)?b:T("Issue detail",{...Wn(b.value,i),history:h(L)?[]:L.value})})}if(d==="issue:preview:get"){let{issueId:i}=l.payload,b=tn(i);return S(r,{type:"issue:preview:result",payload:{issueId:i,result:h(b)?b:T("Issue preview",Vn(i,b.value))}})}if(d==="commits:get"){let{requestId:i,...b}=l.payload??{};return S(r,{type:"commits",requestId:i,payload:io(await yt({repoRoot:a,...b}))})}if(d==="emails:get")return S(r,{type:"emails",payload:await so({repoRoot:a}),suggestions:await ao({repoRoot:a})});if(d==="email:link"||d==="email:unlink"){let b=await(d==="email:link"?Pn:An)({repoRoot:a,...l.payload});return S(r,{type:"emails",payload:await so({repoRoot:a}),suggestions:await ao({repoRoot:a}),lastAction:h(b)?{ok:!1,message:b.message}:{ok:!0,message:b.message}}),E(r,a,o,`${d}:result`,b)}if(d==="me:stats:get")return S(r,{type:"me:stats",payload:await Nn({repoRoot:a})});if(d==="version:get")return S(r,{type:"version",payload:await di(F)});if(d==="settings:get")return S(r,{type:"settings",payload:no()});if(d==="settings:set"){let i=Nr({enabled:l.payload.autoSync,intervalMs:l.payload.autoSyncIntervalMs});return h(i)||n(),S(r,{type:"settings",payload:h(i)?no():i,lastAction:h(i)?{ok:!1,message:i.message}:{ok:!0,message:i.message}})}if(d==="commit:inspect")return S(r,{type:"commit:inspect:result",payload:await lr({repoRoot:a,sha:l.payload.sha,repo:l.payload.repo})});if(d==="commit:diff:get"){let{sha:i,repo:b}=l.payload;return S(r,{type:"commit:diff:result",payload:{sha:i,repo:b,result:await sr({repoRoot:a,sha:i,repo:b})}})}if(d==="repos:get"){let i=await nr(a);return S(r,{type:"repos",payload:await Ae({repoRoot:a}),own:i})}if(d==="repo:preview"){let i=await lo({repoRoot:a,...l.payload,previewOnly:!0});return S(r,{type:"repos",for:d,url:l.payload.url.trim(),payload:h(i)?await Ae({repoRoot:a}):i,preview:h(i)?null:i.value.preview??null,lastAction:{ok:!h(i),message:i.message}})}if(d==="repo:preview:cancel")return Rn(l.payload),S(r,{type:"repos",for:d,payload:await Ae({repoRoot:a}),preview:null});if(d==="repo:connect"||d==="repo:disconnect"){let i=d==="repo:connect"?await lo({repoRoot:a,...l.payload,confirm:!0}):await Mn({repoRoot:a,...l.payload});return S(r,{type:"repos",for:d,payload:h(i)?await Ae({repoRoot:a}):i,preview:null,lastAction:{ok:!h(i),message:i.message}}),h(i)?S(r,{type:`${d}:result`,payload:i}):(!h(i)&&i.message!==er&&!i.message.startsWith(tr)&&ui(),E(r,a,o,`${d}:result`,i))}if(d==="issue:commits:get"){let{issueId:i}=l.payload;return S(r,{type:"issue:commits:result",payload:{issueId:i,result:await ir(a,io(await rr({repoRoot:a,ref:de(i)})))}})}if(d==="issue:squashed-diff:get"){let{issueId:i,asOfTime:b}=l.payload;return S(r,{type:"issue:squashed-diff:result",payload:{issueId:i,asOfTime:b??null,result:await ar({repoRoot:a,ref:de(i),end:b})}})}if(d==="issue:stats:get"){let{issueId:i,asOfTime:b}=l.payload;return S(r,{type:"issue:stats:result",payload:{issueId:i,asOfTime:b??null,result:await Ct({repoRoot:a,idOrRef:de(i),end:b})}})}if(d==="diff-stats:get")return S(r,{type:"diff-stats",payload:await Xn({repoRoot:a})});if(d==="swimlane:stats:get"){let{swimlaneId:i}=l.payload;return S(r,{type:"swimlane:stats:result",payload:{swimlaneId:i,result:await ti({repoRoot:a,swimlaneId:i})}})}if(d==="goal:stats:get"){let{goalId:i,key:b,asOfTime:L}=l.payload;return S(r,{type:"goal:stats:result",payload:{goalId:i,key:b,result:await hn({repoRoot:a,goal:i,end:L})}})}if(d==="goal:commits:get"){let{goalId:i,key:b,asOfTime:L}=l.payload;return S(r,{type:"goal:commits:result",payload:{goalId:i,key:b,result:await xn({repoRoot:a,goal:i,end:L})}})}if(d==="board:overview:get"){let{boardId:i}=l.payload;return S(r,{type:"board:overview:result",payload:{boardId:i,result:jn({boardId:i})}})}if(d==="time-travel:scrub"){let i=await Ur({repoRoot:a,targetTime:l.payload.targetTime});return S(r,{type:"time-travel:result",payload:i}),h(i)?void 0:xo()}if(d==="time-travel:checkout-event"){let i=await Gr({repoRoot:a,eventId:l.payload.eventId});return S(r,{type:"time-travel:result",payload:i}),h(i)?void 0:xo()}if(d==="time-travel:live"){let i=await $r({repoRoot:a});return S(r,{type:"time-travel:result",payload:i}),h(i)?void 0:xo()}if(d==="state:get")return Ye(r,a);if(d==="sync"){let i=await Et({repoRoot:a});return S(r,{type:"sync:result",payload:i}),o(),h(i)||bo(a),Ye(r,a)}if(d==="issue:comment:add"){let i=await Pt({repoRoot:a,...l.payload});return E(r,a,o,"issue:comment:add:result",i)}if(d==="issue:comment:edit"){let i=await Cn({repoRoot:a,...l.payload});return E(r,a,o,"issue:comment:edit:result",i)}if(d==="issue:comment:delete"){let i=await At({repoRoot:a,...l.payload});return S(r,{type:"issue:comment:delete:result",payload:i}),h(i)?S(r,{type:"failed",payload:i.message}):(o(),Ye(r,a))}if(d==="issue:edit:description"){let i=await mn({repoRoot:a,...l.payload});return E(r,a,o,"issue:edit:description:result",i)}if(d==="issue:edit:title"){let i=await cn({repoRoot:a,...l.payload});return E(r,a,o,"issue:edit:title:result",i)}if(d==="issue:tag:add"){let i=await bn({repoRoot:a,...l.payload});return E(r,a,o,"issue:tag:add:result",i)}if(d==="issue:goal:set")return E(r,a,o,"issue:goal:set:result",await Kr({repoRoot:a,...l.payload}));if(d==="goal:create")return E(r,a,o,"goal:create:result",await qr({repoRoot:a,...l.payload}));if(d==="goal:rename")return E(r,a,o,"goal:rename:result",await Jr({repoRoot:a,...l.payload}));if(d==="goal:remove")return E(r,a,o,"goal:remove:result",await Xr({repoRoot:a,...l.payload}));if(d==="issue:tag:remove"){let i=await vn({repoRoot:a,...l.payload});return E(r,a,o,"issue:tag:remove:result",i)}if(d==="contributor:remove"){let i=await In({repoRoot:a,...l.payload});return E(r,a,o,"contributor:remove:result",i)}if(d==="tag:remove"){let i=await wn({repoRoot:a,...l.payload});return E(r,a,o,"tag:remove:result",i)}if(d==="contributors:get")return S(r,{type:"contributors",boardId:l.payload?.boardId,payload:await Tn({repoRoot:a,...l.payload})});if(d==="issue:assignee:add"){let i=await Sn({repoRoot:a,...l.payload});return E(r,a,o,"issue:assignee:add:result",i)}if(d==="issue:assignee:remove"){let i=await En({repoRoot:a,...l.payload});return E(r,a,o,"issue:assignee:remove:result",i)}if(d==="issues:list")return S(r,{type:"issues",payload:await on({repoRoot:a})});if(d==="issues:create"){let i=await rn({...l.payload,repoRoot:a});return S(r,{type:"issues:create:result",payload:i}),h(i)?ue({type:"failed",payload:i.message}):(o(),Ye(r,a))}if(d==="board:create"){let i=await dn({...l.payload,repoRoot:a});return E(r,a,o,"board:create:result",i)}if(d==="board:edit:title"){let i=await un({...l.payload,repoRoot:a});return E(r,a,o,"board:edit:title:result",i)}if(d==="swimlane:create"){let i=await pn({...l.payload,repoRoot:a});return E(r,a,o,"swimlane:create:result",i)}if(d==="swimlane:edit:title"){let i=await fn({...l.payload,repoRoot:a});return E(r,a,o,"swimlane:edit:title:result",i)}if(d==="board:edit:description"){let i=await Wr({...l.payload,repoRoot:a});return E(r,a,o,"board:edit:description:result",i)}if(d==="swimlane:edit:description"){let i=await Vr({...l.payload,repoRoot:a});return E(r,a,o,"swimlane:edit:description:result",i)}if(d==="swimlane:rule:set"){let i=await zr({...l.payload,repoRoot:a});return E(r,a,o,"swimlane:rule:set:result",i)}if(d==="goal:edit:description"){let i=await Yr({repoRoot:a,goal:l.payload.goalId,description:l.payload.description});return E(r,a,o,"goal:edit:description:result",i)}if(d==="swimlane:delete"){let i=await yn({...l.payload,repoRoot:a});return E(r,a,o,"swimlane:delete:result",i)}if(d==="swimlane:move"){if(!l.payload.position)return S(r,{type:"error",message:"Missing move position"});let i=await gn({...l.payload,repoRoot:a});return E(r,a,o,"swimlane:move:result",i)}if(d==="issues:move"){if(!l.payload.position)return S(r,{type:"error",message:"Missing move position"});let i=await an({...l.payload,repoRoot:a});return E(r,a,o,"issues:move:result",i)}if(d==="issue:close"){let i=await nn({repoRoot:a,issueId:l.payload.issueId});return E(r,a,o,"issue:close:result",i)}if(d==="issue:reopen"){let i=await sn({repoRoot:a,issueId:l.payload.issueId});return E(r,a,o,"issue:reopen:result",i)}return S(r,{type:"error",message:"Unknown message type"})},p,g=async l=>{if(p!=="repo:connect"&&p!=="repo:disconnect"&&p!=="repo:preview")return S(r,{type:"error",message:l});S(r,{type:"repos",for:p,payload:await Ae({repoRoot:t.repoRoot}),preview:null,lastAction:{ok:!1,message:l}}),p!=="repo:preview"&&S(r,{type:`${p}:result`,payload:G(l)})};try{let l=JSON.parse(f.toString());p=l?.type;let d=si(l);if(!d.ok)return await g(d.error);let a=d.message;return oi.has(a.type)?await pe(async()=>ie().mode!=="live"?S(r,{type:"failed",payload:"Read-only while viewing history"}):y(a)):await y(a)}catch(l){return g(l instanceof Error?l.message:String(l))}})})};var ga=be.dirname(fa(import.meta.url)),ya=process.env.IS_LOCAL==="true"?be.resolve(process.cwd(),"dist/gui"):be.join(ga,"gui"),Lt,ha=async()=>(Lt===void 0&&(Lt=await import("node:sea").catch(()=>null)),Lt?.isSea()?Lt:null),R=(e,t,o)=>{e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(o))},xa=e=>R(e,409,{isError:!0,message:"Read-only while viewing history"}),jt=(e,t)=>pe(async()=>ie().mode!=="live"?xa(e):t()),fi=async(e,t=1024*1024)=>new Promise((o,n)=>{let s=[],u=0;e.on("data",r=>{if(u+=r.length,u>t){e.destroy(),n(new Error("Request body too large"));return}s.push(r)}),e.on("end",()=>{let r=Buffer.concat(s).toString("utf8");try{o(r?JSON.parse(r):{})}catch{n(new Error("Invalid JSON body"))}}),e.on("error",n)}),ba=2*1024*1024,wa={png:"image/png",jpg:"image/jpeg",gif:"image/gif",webp:"image/webp"},va=e=>e.endsWith(".html")?"text/html; charset=utf-8":e.endsWith(".js")?"text/javascript; charset=utf-8":e.endsWith(".css")?"text/css; charset=utf-8":e.endsWith(".json")?"application/json; charset=utf-8":e.endsWith(".ico")?"image/x-icon":e.endsWith(".woff2")?"font/woff2":"application/octet-stream",gi=new Map,Sa=e=>`"${ua("sha1").update(e).digest("base64url")}"`,Ia=async e=>{try{let{size:t,mtimeMs:o}=await da(e);return`${t}:${o}`}catch{return null}},Ta=e=>{try{return decodeURIComponent(e)}catch{return null}},yi=async(e,t,o)=>{let n=e==="/"?"/index.html":Ta(e);if(n===null)return R(o,404,{isError:!0,message:"Not found"});let s=n.replace(/^\/+/,""),u=be.resolve(ya),r=be.resolve(u,s),f=be.relative(u,r);if(f.startsWith("..")||be.isAbsolute(f))return R(o,403,{isError:!0,message:"Forbidden"});let y=await ha(),p=y?"sea":await Ia(r),g=p===null?void 0:gi.get(f),l=g&&g.stamp===p?g.etag:null;if(l&&t.headers["if-none-match"]===l)return o.writeHead(304,{etag:l,"cache-control":"no-cache"}),o.end();try{let d=y?Buffer.from(y.getAsset(`gui/${f}`)):await hi(r),a=l??Sa(d);return p!==null&&gi.set(f,{stamp:p,etag:a}),o.writeHead(200,{"content-type":va(r),etag:a,"cache-control":"no-cache"}),o.end(d)}catch{return R(o,404,{isError:!0,message:`Not found: ${s}`})}},Ea=async(e,t=We)=>new Promise((o,n)=>{let s=u=>{let r=y=>{if(e.off("listening",f),y.code==="EADDRINUSE"&&u===t){e.removeListener("error",r),s(0);return}n(y)},f=()=>{e.off("error",r);let y=e.address();if(!y||typeof y=="string"){n(new Error("Unable to resolve address"));return}o(y.port)};e.once("error",r),e.once("listening",f),e.listen(u,"127.0.0.1")};s(t)}),xi=async e=>{_r(!1);let t=0,o={repoRoot:e.repoRoot},n=pa.createServer((p,g)=>{s(p,g).catch(l=>(logger.error(`[gui] ${p.method} ${p.url}: ${l instanceof Error?l.message:String(l)}`),g.headersSent?g.end():R(g,500,{isError:!0,message:"Internal error"})))});async function s(p,g){let l=new URL(p.url??"/","http://127.0.0.1"),d=Jn({method:p.method,origin:p.headers.origin,contentType:p.headers["content-type"]},t);if(d)return R(g,d.status,{isError:!0,message:d.message});if(p.method==="GET"&&l.pathname===co)return R(g,200,{app:uo,repoRoot:Rt(o.repoRoot),version:F,pid:process.pid});if(l.pathname==="/api/state")return R(g,200,await ee({repoRoot:o.repoRoot}));if(p.method==="POST"&&l.pathname==="/api/comments")try{let a=await fi(p);if(!a.issueId||!a.body?.trim())return R(g,400,{isError:!0,message:"Missing issueId or body"});let i=a.issueId,b=a.body;return await jt(g,async()=>{let L=await Pt({repoRoot:o.repoRoot,issueId:i,body:b});return h(L)?R(g,400,{isError:!0,message:L.message}):R(g,200,await ee({repoRoot:o.repoRoot}))})}catch(a){return R(g,400,{isError:!0,message:a instanceof Error?a.message:"Unable to add comment"})}if(p.method==="POST"&&l.pathname==="/api/attachments")try{let a=await fi(p,ba);if(!a.issueId||!a.dataBase64)return R(g,400,{isError:!0,message:"Missing issueId or dataBase64"});let i=a.issueId,b=a.dataBase64,L=a.name??"";return await jt(g,async()=>{let D=await Qr({repoRoot:o.repoRoot,issueId:i,name:L,dataBase64:b});return h(D)?R(g,400,{isError:!0,message:D.message}):R(g,200,{...await ee({repoRoot:o.repoRoot}),attachment:D.value})})}catch(a){return R(g,400,{isError:!0,message:a instanceof Error?a.message:"Unable to add attachment"})}if(p.method==="DELETE"&&l.pathname.startsWith("/api/attachments/")){let a=decodeURIComponent(l.pathname.replace("/api/attachments/",""));return a?jt(g,async()=>{let i=await Zr({repoRoot:o.repoRoot,attachmentId:a});return h(i)?R(g,400,{isError:!0,message:i.message}):R(g,200,await ee({repoRoot:o.repoRoot}))}):R(g,400,{isError:!0,message:"Missing attachment id"})}if(p.method==="GET"&&l.pathname.startsWith("/media/")){let a=decodeURIComponent(l.pathname.replace("/media/","")),i=await en({repoRoot:o.repoRoot,fileName:a});if(h(i))return R(g,404,{isError:!0,message:i.message});try{let b=await hi(i.value.filePath);return g.writeHead(200,{"content-type":wa[i.value.ext]??"application/octet-stream","cache-control":"public, max-age=31536000, immutable"}),g.end(b)}catch{return R(g,404,{isError:!0,message:"Attachment blob not found"})}}if(p.method==="DELETE"&&l.pathname.startsWith("/api/comments/")){let a=decodeURIComponent(l.pathname.replace("/api/comments/",""));return a?jt(g,async()=>{let i=await At({repoRoot:o.repoRoot,commentId:a});return h(i)?R(g,400,{isError:!0,message:i.message}):R(g,200,await ee({repoRoot:o.repoRoot}))}):R(g,400,{isError:!0,message:"Missing comment id"})}return l.pathname.startsWith("/board/")||l.pathname==="/log"?yi("/",p,g):yi(l.pathname,p,g)}try{t=await Ea(n)}catch(p){return G(p instanceof Error?`Unable to start GUI server: ${p.message}`:`Unable to start GUI server: ${String(p)}`)}let u=Kn({project:o}),r=Hn({project:o,publisher:u,onReposFetch:p=>bo(o.repoRoot,p)}),f=qn(u);pi(n,o,{onStateChanged:()=>r.queueSync(),onSyncSettingsChanged:()=>r.reschedule(),getPort:()=>t}),n.on("close",()=>{r.dispose(),f()});let y=n.address();return!y||typeof y=="string"?(n.close(),G("Unable to resolve GUI server address")):T("Started GUI server",{url:`http://epiq.localhost:${y.port}${e.boardId?`/board/${e.boardId}`:""}`,server:n})};var bi=async e=>{let t=f=>{e.open!==!1&&Un()&&Gn(f)},o=it({});if(h(o))return G(o.message);let n=Ie();$(n)&&Ue(n.value);let s=await Fn(We);if(s&&s.repoRoot===Rt(e.repoRoot)){let f=`http://127.0.0.1:${We}`;return console.log(mo({url:f,repoRoot:e.repoRoot,version:F,reused:!0})),t(f),T("Reused running GUI",{url:f})}let u=await xi({repoRoot:e.repoRoot,boardId:""});if(h(u))return u;let{url:r}=u.value;return console.log(mo({url:r,repoRoot:e.repoRoot,version:F,reused:!1})),t(r),T("Started GUI",{url:r})};import{Box as ce}from"ink";import M from"react";import{Box as wo,Text as Ke}from"ink";import te,{useEffect as Ca,useMemo as Pa}from"react";import{ulid as Aa}from"ulid";var wi=({text:e,match:t,color:o,highlightColor:n=m.secondary,dimColor:s=!1})=>{if(!t)return te.createElement(Ke,{color:o,dimColor:s},e);let u=e.toLowerCase(),r=t.toLowerCase(),f=u.indexOf(r);return f===-1?te.createElement(Ke,{color:o,dimColor:s},e):te.createElement(Ke,{color:o,dimColor:s},e.slice(0,f),te.createElement(Ke,{backgroundColor:n,bold:!0},e.slice(f,f+t.length)),e.slice(f+t.length))},vi=Aa(),Na=e=>`${e}`,Ma=e=>{let{pendingNavTarget:t}=Sr();if(!t)return[];let{breadCrumb:o,contextNode:n,selectedNode:s}=t,u=new Set(Qo(ft.NONE,{breadCrumb:o,contextNode:n,readOnly:Q().readOnly,selectedNode:s}));return[...new Set(Object.values(ft))].filter(r=>r!=="move").filter(r=>r.length>0).filter(r=>r!==ft.PALETTE).map(r=>{let f=hr(r),y=Mr.find(p=>p.intent===f);return{command:r,description:y?.description??`[${r}] Run command`,isAvailable:u.has(r)}}).filter(r=>r.command.toLowerCase().startsWith(e)||e.length>=2&&r.description.toLowerCase().includes(e)).sort((r,f)=>r.isAvailable!==f.isAvailable?r.isAvailable?-1:1:r.command.localeCompare(f.command))},Ra=e=>Ee.text({id:vi,parentNodeId:e,rank:"000000",name:"Command Palette",readonly:!0,isVirtual:!0}),Ba=(e,t,o)=>Ee.text({id:Na(e.command),parentNodeId:o,rank:String(t).padStart(6,"0"),name:e.command,disabled:!e.isAvailable,readonly:!0,isVirtual:!0}),vo=[],La=e=>{Si();let t=q.createNode(Ra(Q().rootNodeId));h(t)||!Te(t.value)||(vo=[t.value,...e.map((o,n)=>Ba(o,n,vi)).map(q.createNode).filter($).map(({value:o})=>o).filter(Te)],Ce.navigate({contextNode:t.value,selectedIndex:0}))},Si=()=>{let e=vo.map(t=>t.id);vo=[];for(let t of e)q.deleteNode(t)};function Ii({width:e,height:t}){let n=ur().value.trim().toLowerCase(),s=Pa(()=>Ma(n),[n]);Ca(()=>(La(s),()=>{Si()}),[s]);let u=Q().selectedIndex;return te.createElement(wo,{flexDirection:"column",width:e,height:t-2},te.createElement(wt,{height:t-4,itemHeight:3,selectedIndex:u},s.map((r,f)=>{let y=f===u,p=r.isAvailable?m.accent:m.secondary2,g=r.isAvailable?"":"[unavailable] ";return te.createElement(wo,{key:r.command,flexDirection:"column",paddingX:1,borderLeft:!1,borderBottom:!1,borderRight:!1,borderColor:m.secondary,borderStyle:"single"},te.createElement(Ke,{color:p,dimColor:!r.isAvailable},y?"\u276F ":" ",te.createElement(wi,{text:zo(r.command),match:n,color:p,dimColor:!r.isAvailable})),te.createElement(wo,{paddingLeft:2},te.createElement(wi,{text:g+r.description,match:n.length>=2?n:"",color:y&&r.isAvailable?m.primary:m.secondary2,dimColor:!r.isAvailable})))})))}import{Box as qa}from"ink";import Ot from"react";import Je from"chalk";import{Box as qe,Text as Pi}from"ink";import we,{useEffect as Ai,useMemo as ja,useState as Ni}from"react";var Me=["\u2803","\u2806","\u2830","\u2818"],Ti=120,Ei=({isPalette:e,isPending:t,frame:o})=>{if(t){let n=(o%Me.length+Me.length)%Me.length;return Me[n]}return e?"?":":"},Ci=({infoMessage:e,isPending:t})=>t&&e.includes(cr)?"":e;var Mi=Je.hex(m.secondary),_a=Je.inverse,Da=Je.inverse.hex(m.secondary2),Oa={hint:"",hints:[],overlap:0,remainder:""},Bi=({value:e,command:t})=>{if(!t)return null;let o=e.trimStart(),s=e.length-o.length;return e.slice(s,s+t.length)!==t?null:{start:s,end:s+t.length}},Ua=({command:e,modifier:t})=>{if(!t)return null;if(e==="tag"||e==="assign"){let o=Go(t);return{bg:n=>Je.bgHex(o).black(n),bgCursor:n=>Je.bgHex(o).inverse.black(n)}}return oo(t)},Ga=({value:e,command:t,modifier:o})=>{if(!t||!o)return null;let n=Bi({value:e,command:t});if(!n)return null;let u=e.slice(n.end).match(/^\s*/)?.[0].length??0,r=n.end+u;return e.slice(r,r+o.length)!==o?null:{start:r,end:r+o.length}},Ri=()=>({value:ne.value,cursorPosition:ne.cursorPosition,commandIsPending:ne.commandIsPending,autoCompletion:ne.commandMeta.autoCompletion??Oa,infoMessage:ne.commandMeta.infoMessage,validationStatus:ne.commandMeta.validity,command:ne.commandMeta.command,inputString:ne.commandMeta.inputString,modifier:ne.commandMeta.modifier}),$a=(e,t)=>e.value===t.value&&e.cursorPosition===t.cursorPosition&&e.commandIsPending===t.commandIsPending&&e.infoMessage===t.infoMessage&&e.autoCompletion.hint===t.autoCompletion.hint&&e.autoCompletion.overlap===t.autoCompletion.overlap&&e.autoCompletion.remainder===t.autoCompletion.remainder&&e.validationStatus===t.validationStatus&&e.command===t.command&&e.modifier===t.modifier&&e.inputString===t.inputString,Li=({width:e,mode:t})=>{let[o,n]=Ni(Ri());Ai(()=>{let B=()=>{let C=Ri();n(I=>$a(I,C)?I:C)},P=dr(B);return B(),()=>{P()}},[]);let{value:s,cursorPosition:u,infoMessage:r,commandIsPending:f,autoCompletion:y,command:p,modifier:g,validationStatus:l}=o,d=f&&l===mr.Invalid,a=Ci({infoMessage:r,isPending:f}),[i,b]=Ni(0);Ai(()=>{if(!f){b(0);return}let B=setInterval(()=>{b(P=>(P+1)%Me.length)},Ti);return()=>{clearInterval(B)}},[f]);let L=Ei({isPalette:t===H.PALETTE,isPending:f,frame:i}),D=ja(()=>{let B=Math.max(0,Math.min(u,s.length)),P=Bi({value:s,command:p}),C=Ga({value:s,command:p,modifier:g}),I=p?oo(p):null,V=Ua({command:p,modifier:g}),Se=(Y,K)=>{let eo=P&&K>=P.start&&K<P.end,to=C&&K>=C.start&&K<C.end;return eo&&I?I.fg(Y):to&&V?V.bg(Y):Y},De=(Y,K)=>{let eo=P&&K>=P.start&&K<P.end,to=C&&K>=C.start&&K<C.end;return eo&&I?I.fgCursor(Y):to&&V?V.bgCursor(Y):_a(Y)},he=s.slice(0,B),Z=s[B]??" ",Oe=B<s.length?s.slice(B+1):"",nt=Array.from(he).map((Y,K)=>Se(Y,K)).join(""),Bo=De(Z,B),Lo=Array.from(Oe).map((Y,K)=>Se(Y,B+1+K)).join("");if(y.hint){let Y=y.hint[y.overlap]??Z;Bo=Da(Y),Lo=Mi(y.remainder.slice(1)+Oe)}return nt+Bo+Lo},[s,u,y,p,g]);return we.createElement(qe,{flexDirection:"column",justifyContent:"flex-start"},we.createElement(qe,{flexDirection:"column",paddingX:1,borderColor:m.secondary,borderStyle:"round",width:e},we.createElement(qe,{height:1,flexWrap:"nowrap"},we.createElement(qe,{flexShrink:0},we.createElement(Pi,{wrap:"truncate-start"},Mi(L),D)),a&&we.createElement(qe,{flexShrink:1,minWidth:0},we.createElement(Pi,{wrap:"truncate",color:d?m.red:m.secondary2},` ${a} `)))))};import{Box as Fa,Text as Xe}from"ink";import Re from"react";var ji=" ",Ha=({keys:e,label:t})=>e.length+1+t.length,ka=(e,t)=>{let o=[],n=0;for(let s of e){let r=(o.length>0?ji.length:0)+Ha(s);if(n+r>t-4)break;o.push(s),n+=r}return o},_i=({width:e,availableHints:t})=>{let o=ka(t,e);return Re.createElement(Fa,{width:e,borderStyle:"round",borderColor:m.secondary,paddingX:1},o.length?Re.createElement(Xe,{wrap:"truncate"},o.map(({keys:s,label:u},r)=>Re.createElement(Xe,{key:`${s}-${u}`},r>0?ji:"",Re.createElement(Xe,{color:m.accent},s),Re.createElement(Xe,{color:m.secondary2}," ",u)))):Re.createElement(Xe,{color:m.secondary2},"\xA0"))};import{Box as Wa,Text as _t}from"ink";import ze,{useEffect as Va,useRef as Be,useState as Ya}from"react";var Ka=40,Dt=.3,So="\u2500",Di=1,Oi=16,Ui=({width:e})=>{let{replay:t}=O(),o=t!==null,n=t?Math.min(1,Math.max(0,t.progress)):0,u=Math.max(4,e-6),r=u*Di,[f,y]=Ya(0),p=Be(n);p.current=n;let g=Be(0),l=Be(0),d=Be(0),a=Be(0),i=Be(0);if(Va(()=>{if(!o){g.current=0,d.current=0,a.current=0,i.current=0,y(0);return}g.current=p.current,l.current=Date.now(),d.current=p.current;let P=setInterval(()=>{let C=Date.now(),I=p.current;if(I!==d.current){let Z=I-d.current,Oe=C-l.current;if(Z<0)a.current=0,i.current=0;else if(Z>0&&Oe>0){let nt=Z/Oe;a.current=a.current===0?nt:a.current*(1-Dt)+nt*Dt,i.current=i.current===0?Z:i.current*(1-Dt)+Z*Dt}g.current=I,l.current=C,d.current=I}let V=C-l.current,Se=Math.min(i.current,a.current*V),De=Math.min(1,g.current+Se),he=Math.round(De*r);y(Z=>Z===he?Z:he)},Ka);return()=>clearInterval(P)},[o,r]),!t)return null;let b=P=>{let C=u>1?P/(u-1):0,I=Math.round(C*(Oi-1))/(Oi-1);return Ho(I)},L=Math.min(u,Math.floor(f/Di)),D=[];for(let P=0;P<L;P++){let C=b(P),I=D[D.length-1];I&&I.color===C?I.text+=So:D.push({text:So,color:C})}let B=So.repeat(Math.max(0,u-L));return ze.createElement(Wa,{width:e,borderStyle:"round",borderColor:m.secondary,paddingX:1,columnGap:1},ze.createElement(_t,{color:m.secondary2},"\u25B6"),ze.createElement(_t,null,D.map((P,C)=>ze.createElement(_t,{key:C,color:P.color},P.text)),ze.createElement(_t,{color:m.secondary,dimColor:!0},B)))};var Qe=({width:e,mode:t,availableHints:o})=>{let{timeMode:n}=O(),s=t===H.COMMAND_LINE||t===H.PALETTE;return Ot.createElement(qa,null,s?Ot.createElement(Li,{width:e,mode:t}):n==="replay"?Ot.createElement(Ui,{width:e}):Ot.createElement(_i,{width:e,mode:t,availableHints:o}))};import{Box as fe,Text as Le}from"ink";import k,{useEffect as Ja,useMemo as Xa}from"react";import{ulid as Gi}from"ulid";var Gt=Gi(),za=()=>[...new Set([...Q().availableActions].map(e=>e.description).filter(e=>e!==void 0))].map(Rr).filter(e=>e!==null).sort(({keys:e},{keys:t})=>e.length===1&&t.length!==1?-1:e.length!==1&&t.length===1?1:e.localeCompare(t,void 0,{sensitivity:"base"})).map(({keys:e,label:t})=>({keys:e,action:t})),Qa=e=>Ee.text({id:Gt,parentNodeId:e,rank:"000000",name:"Help",isVirtual:!0,readonly:!0}),Za=(e,t,o)=>Ee.text({id:Gi(),parentNodeId:o,rank:String(t).padStart(6,"0"),name:e.keys,readonly:!0,isVirtual:!0}),Ut=[],$i=()=>{let e=Ut.filter(o=>o.id!==Gt).map(o=>o.id),t=Ut.filter(o=>o.id===Gt).map(o=>o.id);Ut=[];for(let o of[...e,...t])q.deleteNode(o)},el=e=>{$i();let t=q.createNode(Qa(Q().rootNodeId));if(h(t)||!Te(t.value))return logger.error("Could not attach help nodes");Ut=[t.value,...e.map((o,n)=>Za(o,n,Gt)).map(q.createNode).filter($).map(({value:o})=>o).filter(Te)],Ce.navigate({contextNode:t.value,selectedIndex:0})},Fi=({width:e,height:t})=>{let o=Xa(()=>za(),[]);Ja(()=>(el(o),()=>{$i()}),[o]);let n=Q().selectedIndex;return k.createElement(fe,{flexDirection:"column",marginTop:3,borderColor:m.secondary,borderStyle:"round",width:e,height:t-2},k.createElement(fe,{paddingX:1,justifyContent:"center"},k.createElement(Le,{color:m.accent},"Help"),k.createElement(Le,{dimColor:!0}," \u2014 navigate with arrows/hjkl, press q to close")),k.createElement(fe,{paddingX:1,marginTop:1},k.createElement(fe,{width:20,justifyContent:"flex-end"},k.createElement(Le,{color:m.secondary},"Key(s)")),k.createElement(fe,{flexGrow:1,width:40,paddingLeft:2},k.createElement(Le,{color:m.secondary},"Action"))),k.createElement(wt,{height:t-7,itemHeight:1,selectedIndex:n},o.map(({keys:s,action:u},r)=>{let f=r===n;return k.createElement(fe,{paddingX:1,key:`${s}-${u}-${r}`,flexDirection:"row"},k.createElement(fe,{width:20,justifyContent:"flex-end"},k.createElement(Le,{color:m.accent},s)),k.createElement(fe,{flexGrow:1,width:40,paddingLeft:2},k.createElement(Le,{color:f?m.primary:m.secondary2},u)))})))};import{Box as je,Text as W}from"ink";import A,{useEffect as tl,useState as Io}from"react";var $t=({index:e,email:t,note:o,noteColor:n})=>A.createElement(je,null,A.createElement(W,{color:m.accent},e===void 0?" ":` ${e}.`),A.createElement(W,null," ",t),o&&A.createElement(W,{color:n??m.secondary2},` ${o}`)),Hi=({height:e})=>{let[t,o]=Io([]),[n,s]=Io([]),[u,r]=Io(null),{userName:f,gitEmail:y,gitName:p,userId:g}=re();return tl(()=>{let l=!0;return(async()=>{let a=Pe(),i=oe(process.cwd());if(h(a)||h(i)||!g)return;let b=a.value.emailLinks,L=Yo(b,g);o(L.map(C=>({email:C,others:Vo(b,C).filter(I=>I!==g).map(I=>a.value.contributors[I]?.name??I)})));let D=at(i.value),B=await St({repoRoot:i.value,stateBranch:h(D)?void 0:D.value,names:[f,p],links:b});if(!l)return;if(h(B)){r(B.message);return}let P=It(B.value);ut(P.map(C=>C.email)),s(P)})(),()=>{l=!1}},[g,f,p,y]),A.createElement(je,{height:e-4,flexDirection:"column",paddingTop:1,paddingLeft:2,borderStyle:"round",borderColor:m.secondary,rowGap:1},A.createElement(je,null,A.createElement(W,{color:m.accent,bold:!0},f??"Not configured"),A.createElement(W,{color:m.secondary2}," on this board")),u&&A.createElement(W,{color:m.red},u),A.createElement(je,{flexDirection:"column"},A.createElement(W,{color:m.secondary2},"CLAIMED"),t.length===0?A.createElement($t,{email:"Nothing claimed \u2014 your commits show the name git signed them with.",note:""}):t.map(l=>A.createElement($t,{key:l.email,email:l.email,note:l.others.length>0?`CONTESTED: also ${l.others.join(", ")}, resolves to nobody`:l.email===y?"your git identity here":void 0,noteColor:l.others.length>0?m.yellow:void 0}))),A.createElement(je,{flexDirection:"column"},A.createElement(W,{color:m.secondary2},"UNCLAIMED IN THIS HISTORY"),n.length===0?A.createElement($t,{email:"Nothing left to claim here."}):n.map(l=>A.createElement($t,{key:l.email,email:l.email,note:`${l.commits} commit${l.commits===1?"":"s"} as ${l.names.join(", ")||"unknown"}`}))),A.createElement(je,{flexDirection:"column"},A.createElement(W,{color:m.secondary2},A.createElement(W,{color:m.accent},"[enter]")," writes a claim for the first into the command line, to confirm or change."),A.createElement(W,{color:m.secondary2},A.createElement(W,{color:m.accent},":config emails")," claims by address,",A.createElement(W,{color:m.accent}," :config emails-unclaim "),"gives one back."),A.createElement(W,{color:m.secondary2},A.createElement(W,{color:m.accent},"[esc]")," to close")))};import ol from"chalk";import{Box as ve,Text as U}from"ink";import N from"react";var rl=8,nl=()=>{let e=Ge({exclude:process.cwd()});return h(e)?[]:e.value.slice(0,rl)},ki=({width:e,height:t})=>{let o=nl();return N.createElement(ve,{height:t-4,flexDirection:"column",width:e,paddingTop:1,paddingLeft:2,borderStyle:"round",borderColor:m.secondary,rowGap:1},N.createElement(U,{color:m.accent,bold:!0},"Initialize project"),N.createElement(U,null,`This folder is not an ${ol.hex(m.accent)("epiq")} project yet.`),N.createElement(U,{color:m.primary},"To start tracking issues here, we need to initialize a new",N.createElement(U,{color:m.primary,backgroundColor:m.secondary}," .epiq/project.json "),"file in this repository."),N.createElement(ve,{marginTop:1,flexDirection:"column"},N.createElement(ve,null,N.createElement(U,{color:m.accent}," "),N.createElement(U,{color:m.primary},"Type "),N.createElement(U,{backgroundColor:m.secondary}," :init "))),N.createElement(ve,{marginTop:1},N.createElement(U,{color:m.secondary2},"This will create, commit and push "),N.createElement(U,{color:m.primary,backgroundColor:m.secondary}," .epiq/project.json ")),o.length>0&&N.createElement(ve,{marginTop:1,flexDirection:"column"},N.createElement(U,{color:m.accent,bold:!0},"Or open a recent project"),o.map((n,s)=>N.createElement(ve,{key:n.projectId},N.createElement(U,{color:m.accent},` ${s+1} `),N.createElement(U,{color:m.primary},dt(n.root)),N.createElement(U,{color:m.secondary2},` ${n.root}`))),N.createElement(ve,{marginTop:1},N.createElement(U,{color:m.accent}," "),N.createElement(U,{color:m.primary},"Type "),N.createElement(U,{backgroundColor:m.secondary}," :open 1 "),N.createElement(U,{color:m.secondary2}," or "),N.createElement(U,{backgroundColor:m.secondary}," :open <path> "))))};import{Box as Ht,Text as X}from"ink";import j from"react";import{Box as Ft,Text as me}from"ink";import J,{useEffect as il,useState as Wi}from"react";var Vi=()=>{let[e,t]=Wi(null),[o,n]=Wi(null);return il(()=>{let s=!0;return(async()=>{let r=Pe(),f=oe(process.cwd());if(h(r)||h(f))return;let y=at(f.value),{userName:p,gitName:g}=re(),l=await St({repoRoot:f.value,stateBranch:h(y)?void 0:y.value,names:[p,g],links:r.value.emailLinks});if(!s)return;if(h(l)){n(l.message);return}let d=It(l.value);ut(d.map(a=>a.email)),t(d)})(),()=>{s=!1}},[]),o?J.createElement(Ft,{marginTop:1},J.createElement(me,{color:m.red},o)):e===null?null:e.length===0?J.createElement(Ft,{marginTop:1},J.createElement(me,{color:m.secondary2},"No unclaimed addresses in this history \u2014 type",J.createElement(me,{color:m.accent}," :config emails none "),"to move on.")):J.createElement(Ft,{flexDirection:"column",marginTop:1},e.map(s=>J.createElement(me,{key:s.email},J.createElement(me,{color:m.accent}," \xB7 "),J.createElement(me,null,s.email),J.createElement(me,{color:m.secondary2},` ${s.commits} commit${s.commits===1?"":"s"} as ${s.names.join(", ")||"unknown"}`))),J.createElement(Ft,{marginTop:1},J.createElement(me,{color:m.secondary2},"Claim yours by address, or",J.createElement(me,{color:m.accent}," none "),"to skip.")))};var sl=({isDone:e,command:t,value:o,verb:n="Type",hint:s})=>j.createElement(Ht,null,j.createElement(X,{color:m.accent,dimColor:e},e?" \u2714 ":" "),j.createElement(X,{dimColor:e},n," "),j.createElement(X,{backgroundColor:m.secondary,dimColor:e}," "+t+" "),o!==void 0&&j.createElement(j.Fragment,null,j.createElement(X,{dimColor:e}," "),j.createElement(X,{color:m.secondary,dimColor:e},"<"+o+">")),s),al=(e,t)=>e.length===0?void 0:Jo(e.join(", "),Math.max(12,t-34)),ll=e=>{if(typeof e=="string")return e;if(typeof e=="boolean")return e?"on":"off"};function To({width:e,height:t}){let{isSetPreferredEditor:o,isSetUserName:n,userName:s,preferredEditor:u,autoSync:r,isSetAutoSync:f,isSetEmails:y,claimedEmails:p,emailSetupDeclined:g}=pt(),l=[{key:"username",done:n,command:":config username",value:s,message:"First, choose your username."},{key:"editor",done:o,command:":config editor",value:u,message:"Nice. Now pick your editor."},{key:"autosync",done:f,command:":config autosync",value:r,message:"Almost there. Configure auto sync."},{key:"emails",done:y,command:":config emails",value:g?"declined":al(p,e),message:"Last one. Claim the git addresses whose commits are yours, so the board calls them by your name."}],d=l.findIndex(i=>!i.done),a=d===-1?void 0:l[d];return j.createElement(Ht,{height:t-4,flexDirection:"column",width:e,paddingTop:1,paddingLeft:2,borderStyle:"round",borderColor:m.secondary,rowGap:1},j.createElement(X,{color:m.accent,bold:!0},"Welcome! \u{1F539}"),j.createElement(X,null,"Let's get you started - it's a breeze."),j.createElement(X,{color:m.secondary2},"Remember - you can always search available commands by typing:",j.createElement(X,{color:m.accent}," ? ")),a&&j.createElement(X,{color:m.secondary2},a.message),j.createElement(Ht,{flexDirection:"column"},l.map((i,b)=>{if(!(d===-1||b<=d))return null;let D=b===d,B=D&&d===0;return j.createElement(Ht,{key:i.key,flexDirection:"column",marginBottom:1},j.createElement(sl,{isDone:i.done,command:B?":config us":i.command,value:i.done?ll(i.value):void 0,verb:D&&!B?"Waiting:":"Type",hint:B?j.createElement(X,{color:m.secondary2}," then ",j.createElement(X,{color:m.accent},"tab")," to auto-complete ",j.createElement(X,{backgroundColor:m.secondary},` ${i.command.split(" ").pop()} `)):void 0}),i.key==="emails"&&!i.done&&j.createElement(Vi,null))})))}import Kt from"chalk";import{Box as tt,Text as _e}from"ink";import _ from"react";import{Box as kt,Text as Yi}from"ink";import ge from"react";import ml from"chalk";var cl=e=>(q.getTag(e)?.name??"").length+3,dl=e=>{let t=q.getContributor(e);return(t?t.name:"").length+3},Ki=({width:e})=>{let{viewMode:t}=re(),o=O(),{breadCrumb:n,contextNode:s,selectedIndex:u,renderedChildrenIndex:r}=o,f=r[s.id]?.[u],y=Lr(f?.id??s.id,"TICKET"),p=$(y)?y.value:void 0,g=(p?.props.tags??[]).filter(I=>q.getTag(I)),l=p?.props.assignees??[],d=p?.parentNodeId?!jr(s.id,p.parentNodeId)&&t==="dense":!1,a=n.map((I,V)=>{let Se=V===n.length-1,De=ht(I.id),he=Se?De?.[u]?.title:void 0;return`${V?" / ":""}${I.title??""}${he?` \u25B8 ${he}`:""}`}).join(""),i=p?q.getCommentsByIssue(p.id):[],b=i.length?String(i.length).length+11:0,L=d?g.reduce((I,V)=>I+cl(V),0)+l.reduce((I,V)=>I+dl(V),0)+b:0,D=f?.id!=null&&!(t==="wide"&&ct(f)),B=Math.max(0,e-L),P=a.substring(0,B)+(D?ml.hex(m.secondary2).dim(` ${de(f.id)}`):""),C=d?[...g.map(I=>ge.createElement(kt,{key:`tag-${I}`,paddingLeft:1},ge.createElement(wr,{id:I}))),...l.map(I=>ge.createElement(kt,{key:`assignee-${I}`,paddingLeft:1},ge.createElement(br,{id:I}))),i.length?ge.createElement(kt,{key:"comments",paddingLeft:1},ge.createElement(Yi,{color:m.accent},"[",i.length," comment",i.length===1?"":"s","]")):null]:[];return ge.createElement(kt,{overflow:"hidden",justifyContent:"flex-start",alignItems:"flex-start"},ge.createElement(Yi,{color:m.secondary2},P),C)};import{Box as ul,Text as qi}from"ink";import Wt from"react";var pl=({filters:e})=>Wt.createElement(ul,{columnGap:1},Wt.createElement(qi,{color:"red"},"APPLIED FILTERS:"),e.map(({value:t,target:o,operator:n},s)=>Wt.createElement(qi,{key:s,backgroundColor:"black",color:"white"},` ${o}${n}"${t}" `))),Ji=Wt.memo(pl);import{Box as fl,Text as Vt}from"ink";import Ze from"react";function Xi(){let{eventLog:e,unappliedEvents:t}=O(),o=e.at(-1)?.id,n=ko(o??""),s=$(n)?vt(n.value):"INVALID DATE";return Ze.createElement(fl,{paddingLeft:1},Ze.createElement(Vt,{backgroundColor:m.accent,color:m.secondary}," Readonly "),Ze.createElement(Vt,{color:m.accent}," "+t.length+" edits ago at "+s+"."),Ze.createElement(Vt,{color:m.accent}," Resume with "),Ze.createElement(Vt,{backgroundColor:m.accent,color:m.secondary}," ",":peek now"," "))}import{Box as gl,Text as Eo}from"ink";import Yt from"react";function zi(){let{replay:e}=O();if(!e)return null;let{appliedCount:t,totalCount:o,currentTime:n}=e;return Yt.createElement(gl,{paddingRight:2,columnGap:1,justifyContent:"flex-end"},Yt.createElement(Eo,{backgroundColor:m.accent,color:m.secondary}," \u25B6 REPLAY "),Yt.createElement(Eo,{color:m.secondary2},vt(new Date(n))),Yt.createElement(Eo,{color:m.accent},`${t}/${o} edits`))}import Co from"chalk";import{Box as yl,Text as Qi}from"ink";import Po,{useEffect as hl,useState as xl}from"react";var bl=["#4c567a","#9d7cd8","#7aa2f7","#7dcfff","#9d7cd8"],wl=150,Zi=12,vl=.36;function es({syncStatus:e,autoSync:t}){let[o,n]=xl(0);hl(()=>{if(e.status!=="syncing"){n(0);return}let f=0,y=setInterval(()=>{f+=1;let p=(Math.sin(f*vl)+1)/2,g=Math.round(p*(Zi-1));n(l=>l===g?l:g)},wl);return()=>clearInterval(y)},[e.status]);let s={synced:" ",failed:"---",offline:"off",syncing:"...",pending:"idle"},r={synced:m.secondary2,failed:m.yellow,offline:m.secondary2,pending:m.secondary2,syncing:Fo(bl,o/(Zi-1))??""}[e.status];return Po.createElement(yl,null,Po.createElement(Qi,null,Co.hex(m.secondary2).dim(t?"Sync (auto): ":"Sync: ")),Po.createElement(Qi,null,Co.hex(r)("\u25CF")+Co.hex(m.secondary2).dim(` ${s[e.status].padEnd(4)} `)))}import et from"chalk";import{Text as ts}from"ink";import os,{useEffect as Sl,useState as Il}from"react";var Tl=(e,t)=>{let o=e.split("."),n=t.split("."),s=n.findIndex((u,r)=>u!=o[r]);return n.map((u,r)=>r>=s?et.hex(m.accent).dim(u):et.hex(m.secondary).dim(u)).join(et.hex(m.secondary2).dim("."))};function rs(){let[e,t]=Il({current:F,latest:null,updateAvailable:!1});return Sl(()=>{let o=!0;return ho(F).then(n=>{o&&t(n)}),()=>{o=!1}},[]),!e.updateAvailable||!e.latest?os.createElement(ts,{dimColor:!0,color:m.secondary2},F+" "):os.createElement(ts,null,et.hex(m.secondary2).dim(F),et.hex(m.accent).dim(" \u2197 "),Tl(F,e.latest))}function ot({filters:e,hideBreadCrumb:t=!1}){let{timeMode:o,syncStatus:n,mode:s,replay:u}=O(),{userName:r,preferredEditor:f,autoSync:y}=re(),p=64,g=process.stdout.columns-p-8,l=t||o==="replay",d=u?qo(u.currentLabel,Math.max(0,g)):"";return _.createElement(tt,{justifyContent:"space-between",flexDirection:"row","max-width":process.stdout.columns-40,height:1,overflow:"hidden"},o==="replay"?_.createElement(tt,{paddingLeft:1},_.createElement(_e,{color:m.secondary2},d)):l?_.createElement(_e,null," "):s===H.PALETTE?_.createElement(tt,null,_.createElement(_e,{color:m.accent},"Command Palette"),_.createElement(_e,{dimColor:!0,color:m.secondary2}," ","- search, select and press enter")):_.createElement(tt,{paddingLeft:1},e.length>0?_.createElement(Ji,{filters:e}):_.createElement(Ki,{width:g})),o==="live"?_.createElement(tt,{columnGap:1,paddingRight:2,"max-width":p,overflow:"hidden",justifyContent:"flex-end"},_.createElement(es,{autoSync:!!y,syncStatus:n}),_.createElement(_e,null,Kt.hex(m.secondary2).dim("Mode: ")+Kt.hex(m.secondary2).dim(s.padEnd(8," "))," "),_.createElement(ns,{icon:"@",value:r}),_.createElement(ns,{icon:"\u276F",value:f}),_.createElement(rs,null)):"",o==="peek"?_.createElement(Xi,null):"",o==="replay"?_.createElement(zi,null):"")}function ns({icon:e,value:t}){return _.createElement(_e,null,Kt.dim(e)+Kt.hex(m.accent)(` ${t??"-"} `))}import{Box as El,Text as Cl}from"ink";import rt from"react";import{Box as qt,Text as Ao}from"ink";import ye from"react";function No({height:e,width:t}){let o=O(),n=o.nodes[o.rootNodeId],s=n?.id?Fe(n?.id):[];return ye.createElement(qt,{flexDirection:"column",height:e,padding:1,borderStyle:"round",borderColor:m.secondary,width:t},ye.createElement(qt,{padding:1,paddingTop:0,paddingBottom:0},ye.createElement(Ao,null,"Select a board:")),ye.createElement(qt,{padding:1,flexDirection:"column"},s.map((u,r)=>{let f=o.contextNode.context==="WORKSPACE"&&o.selectedIndex===r,p=Fe(u.id).flatMap(({id:g})=>ht(g)).length;return ye.createElement(qt,{key:u.id??r},ye.createElement(Ao,{color:f?m.accent:m.secondary2},ye.createElement(xr,{isSelected:f})),ye.createElement(Ao,{color:f?m.accent:m.secondary2},u.title," (",p," issues)",u.readonly?" \u{1F512}":""))})))}var Pl=({contextNode:e,selectedIndex:t,breadCrumb:o,mode:n,viewMode:s,height:u,width:r})=>{let{renderedChildrenIndex:f}=O(),y=o.find(({context:d})=>d==="BOARD"),l=u-1-3;return rt.createElement(El,{flexDirection:"row"},e.context==="WORKSPACE"?rt.createElement(No,{height:l,width:r}):y?rt.createElement(vr,{height:l,width:r,swimlanes:(f[y.id]??[]).filter(d=>d!==void 0&&Ko(d)),contextNode:e,selectedIndex:t,breadCrumb:o,viewMode:s,mode:n}):rt.createElement(Cl,null))},is=rt.memo(Pl);import{useEffect as Al}from"react";var Nl=e=>{Al(()=>{if(!e)return;let t,o=!1,n=()=>{if(o)return;let{autoSyncIntervalMs:s,userName:u,preferredEditor:r}=re();r&&u&&s&&(t=setTimeout(()=>{if(o)return;let{readOnly:f,mode:y}=Q();y==="default"&&!f&&yr(),n()},s))};return n(),()=>{o=!0,t&&clearTimeout(t)}},[e])},ss=({autoSync:e})=>(Nl(e),null);import{useEffect as Ml}from"react";var as=()=>{let e=oe(process.cwd());return h(e)?T("No project here",null):lt({repoRoot:e.value})},ls=async()=>{let e=as();if(h(e))return G(e.message);if(e.value===null)return T("No project here",null);let t=e.value,o=await gt(t);if(!Br())return T("State not initialized",null);let n=Pe();if(h(n))return T("State not initialized",null);let{mode:s,readOnly:u,timeMode:r,syncStatus:f,selectedNode:y,contextNode:p}=n.value;if(Tr())return T("Project loading",null);let g=as();return h(g)||g.value!==t?T("Project changed",null):s!==H.DEFAULT||u||r!=="live"?T("Skipped while not on the live board",null):y?.isVirtual||p?.isVirtual?T("Skipped on a virtual node",null):f?.status==="syncing"?T("Sync in progress",null):o?T("Worktree held by a sync",null):$e(t)===Zo(t)?T("Log unchanged",null):(st.debug("[watch] event log moved, reloading state"),gr(t))};var Rl=1e3,ms=()=>(Ml(()=>Bt(ls,Rl),[]),null);function Mo({width:e,height:t}){let o=O(),{autoSync:n,viewMode:s}=Uo(),u=o.filters;if(o.mode===H.HELP)return M.createElement(ce,{flexDirection:"column"},M.createElement(Fi,{width:e,height:t}));if(o.mode===H.IDENTITY)return M.createElement(ce,{flexDirection:"column"},M.createElement(Hi,{width:e,height:t}));if(o.mode===H.PALETTE)return M.createElement(ce,{flexDirection:"column"},M.createElement(ot,{filters:u}),M.createElement(Ii,{width:e,height:t-2}),M.createElement(Qe,{width:e,mode:o.mode,availableHints:o.availableHints}));let{isSetupDone:r}=pt(),f=!r,y=r&&!o.hasProjectDefinition;if(f)return M.createElement(ce,{flexDirection:"column"},M.createElement(ce,{flexDirection:"column"},M.createElement(ot,{hideBreadCrumb:!0,filters:u}),M.createElement(To,{height:t,width:e})),M.createElement(Qe,{width:e,mode:o.mode,availableHints:[]}));if(y)return M.createElement(ce,{flexDirection:"column"},M.createElement(ce,{flexDirection:"column"},M.createElement(ot,{hideBreadCrumb:!0,filters:u}),M.createElement(ki,{height:t,width:e})),M.createElement(Qe,{width:e,mode:o.mode,availableHints:[]}));let p=Wo(o.breadCrumb??[],"BOARD");if($(p)){let g=p.value.id,l=Fe(g).length,d=3,a=Math.floor(e/d),i=Math.floor(e/Math.max(l,1));e=Math.min(i,a)*Math.max(l,d)}return M.createElement(ce,{flexDirection:"column"},M.createElement(ss,{autoSync:n}),M.createElement(ms,null),M.createElement(ce,{flexDirection:"column"},M.createElement(ot,{filters:u}),M.createElement(is,{width:e,height:t,contextNode:o.contextNode,selectedIndex:o.selectedIndex,breadCrumb:o.breadCrumb,viewMode:s??"dense",mode:o.mode})),M.createElement(Qe,{width:e,mode:o.mode,availableHints:o.availableHints}))}import Bl from"readline";var Jt,Xt,se,Ll=new Set([x.MoveCursorLeft,x.EraseInputWord,x.MoveCursorLeftOfWord,x.MoveCursorRight,x.MoveCursorRightOfWord,x.NavToPreviousContainer,x.NavToNextContainer,x.NavPreviousItem,x.NavNextItem,x.NavPreviousItemJump,x.NavNextItemJump,x.SetViewDense,x.SetViewWide,x.Confirm,x.Exit,x.Yank,x.InitCommandLine,x.InitCommandPalette,x.ExitCommandLine,x.CaptureInput,x.EraseInput,x.EraseInputWord,x.HideHelp,x.ViewHelp,x.SetViewDense,x.SetViewWide,x.GetLastCommandFromHistory,x.GetNextCommandFromHistory,x.AutoCompleteCommand]),jl=new Set([x.NavPreviousItem,x.NavNextItem,x.NavPreviousItemJump,x.NavNextItemJump,x.NavToPreviousContainer,x.NavToNextContainer,x.Confirm,x.Exit,x.InitCommandPalette,x.InitMove,x.MoveNextItem,x.MovePreviousItem,x.MoveNextItemJump,x.MovePreviousItemJump,x.MoveToNextContainer,x.MoveToPreviousContainer,x.Delete,x.AddItem,x.EditTitle,x.EditDescription,x.ConfirmMove,x.Edit,x.ViewHelp,x.Yank]),cs=async e=>{if(e.ctrl&&e.name==="c")return Ce.exit();let{actionIndex:t,mode:o,readOnly:n,timeMode:s}=Q(),u=pr(e,o);if(!u||n&&!Ll.has(u)||s==="replay"&&o===H.DEFAULT&&jl.has(u))return;let r=t[o]?.[u];if(r?.action)try{await r.action(r,e)}catch(f){logger.error(f)}},_l=()=>async function(t,o){o.name!=="escape"&&(se&&(clearTimeout(se),se=void 0),await cs(o))},Dl=()=>async function(t){if(t.length===1&&t[0]===27){se&&clearTimeout(se),se=setTimeout(()=>{cs({name:"escape",ctrl:!1,meta:!1,shift:!1,sequence:"\x1B"}),se=void 0},25);return}se&&(clearTimeout(se),se=void 0)};function ds(){Jt&&process.stdin.removeListener("keypress",Jt),Xt&&process.stdin.removeListener("data",Xt),Jt=_l(),Xt=Dl(),Bl.emitKeypressEvents(process.stdin),process.stdin.isTTY&&process.stdin.setRawMode(!0),process.stdin.on("data",Xt),process.stdin.on("keypress",Jt)}async function us(){try{let e=it({});if(h(e))return xt(0,e.message);let t=Ie();$(t)&&Ue(t.value);let o=oe(process.cwd());Ue({gitEmail:await Fr(process.cwd()),gitName:await Hr(process.cwd())});let n=$(o)?await Er(o.value):Pr();if(h(n))return n;let s=Ro();return h(s)?xt(6,s.message):(ds(),$(o)&&Cr(o.value),T("Booted Epiq",void 0))}catch(e){return xt(0,bt(e))}}Ir();var $l=`${z.bold("Epiq CLI")}
5
-
6
- ${z.dim("Boot in directory:")}
7
- ${z.cyan("$ epiq")}
8
-
9
- ${z.dim("Launch GUI:")}
10
- ${z.cyan("$ epiq gui")}
11
- ${z.cyan("$ epiq gui --no-open")} ${z.dim("no browser")}
12
-
13
- ${z.dim("Act as somebody other than the configured user:")}
14
- ${z.cyan("$ epiq --as claude")}
15
- `,Zt=Ul($l,{importMeta:import.meta,version:F,flags:{init:{type:"boolean",default:!1},as:{type:"string"},open:{type:"boolean",default:!0}}}),zt=Zt.input[0];zt&&zt!=="gui"&&(console.error(z.red(`Unknown command: ${zt}`)),process.exit(1));if(Zt.flags.as!==void 0){let e=jo(Zt.flags.as,"--as");h(e)&&(console.error(z.red(e.message)),process.exit(1))}var ps=process.stdout.columns||120,fs=()=>Math.max(1,(process.stdout.rows||20)-(process.platform==="win32"?1:0)),gs=fs(),Qt=null,Fl=e=>{try{return Qt?(Qt.rerender(e),T("Rerendered app",void 0)):(Qt=Ol(e,{incrementalRendering:!0,interactive:!!process.stdout.isTTY}),T("Rendered app",void 0))}catch(t){return G(`Unable to render app: ${bt(t)}`)}},Ro=()=>Fl(Gl.createElement(Mo,{width:ps,height:gs}));process.stdout.on("resize",()=>{if(ps=process.stdout.columns||120,gs=fs(),!Qt)return;let e=Ro();h(e)&&logger.info(`[boot:resize] ${e.message}`)});if(zt==="gui"){let e=await bi({repoRoot:process.cwd(),open:Zt.flags.open});h(e)&&(console.error(z.red(`Failed to start Epiq GUI:
16
- ${e.message}`)),process.exitCode=1)}else{process.stdin.isTTY&&process.stdin.setRawMode(!0),console.clear();let e=await us();h(e)&&(logger.info(e.message),console.error(z.red(`Failed to boot Epiq:
17
- ${e.message}`)),process.exitCode=1)}export{Ro as renderApp};
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- var o="1.17.0";export{o as a};