akapen 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/akapen.js +7 -6
  2. package/package.json +8 -8
package/dist/akapen.js CHANGED
@@ -71,7 +71,7 @@ else echo "parentenv=notool"; fi`,yp=(e,t)=>`cat ${fp(e)} >/dev/null 2>&1; echo
71
71
  `).flatMap(e=>Wy(e)??[]),Ky=e=>[...new Set(e.flatMap(e=>e.kind===`break`?[]:[e.file]))],qy=async(e,t)=>{let n=await Promise.all(t.map(async t=>({file:t,result:await e(t)}))),r=new Map;for(let{file:e,result:t}of n)`error`in t||r.set(e,t.shown);return r},Jy=(e,t)=>{if(e.kind===`break`)return[e];let n=t.get(e.file);return n===void 0?[]:[{...e,file:n}]},Yy=async(e,t)=>{let n=Ky(t),r=await qy(e,n),i=t.flatMap(e=>Jy(e,r));return{refused:n.length-r.size,rows:i}},Xy=async(e,t,n)=>{let r=Fy(t),i=await ky(e,r.path,r.glob);if(`error`in i)return F(i.error);let a=await Uy(i,r,n);if(typeof a!=`string`)return F(a.error);let o=await Yy(Ay(e,i.target.root),Gy(a));return h_(wy(o.rows,o.refused))},Zy=[[/^\*\*\//u,()=>`(?:.*/)?`],[/^\/\*\*$/u,()=>`(?:/.*)?`],[/^\*\*/u,()=>`.*`],[/^\*/u,()=>`[^/]*`],[/^\?/u,()=>`[^/]`],[/^\[!?[^\]/\\]+\]/u,e=>e.replace(/^\[!/u,`[^`)],[/^[$()+.[\\\]^{|}]/u,e=>`\\${e}`]],Qy=e=>{for(let[t,n]of Zy){let r=t.exec(e);if(r!==null)return{length:r[0].length,pattern:n(r[0])}}return{length:1,pattern:e.slice(0,1)}},$y=e=>{let t=``;for(let n=0;n<e.length;){let r=Qy(e.slice(n));t+=r.pattern,n+=r.length}return RegExp(`^${t}$`,`u`)},eb=(e,t)=>$y(t).test(e),tb=2e4,nb=new Set([`.git`,`node_modules`]),rb=async e=>{let t=[`ls-files`,`-z`,`-co`,`--exclude-standard`,`--`,...e.pathspecs];return{cut:!1,names:(await b(e.target.root,t)).split(`\0`).filter(e=>e!==``)}},ib=async e=>(await te(e,{withFileTypes:!0}).catch(()=>[])).map(e=>({directory:e.isDirectory(),name:e.name})),ab=async(e,n=[])=>{let r=await ib(e),i=r.filter(e=>!e.directory),a=[...n,...i.map(n=>t.join(e,n.name))],o=r.filter(e=>e.directory&&!nb.has(e.name));for(let n of o){if(a.length>=2e4)break;a=[...await ab(t.join(e,n.name),a)]}return a},ob=async e=>{let{glob:n,target:r}=e,i=await ab(r.real),a=i.filter(e=>n===void 0||eb(t.relative(r.real,e).split(t.sep).join(`/`),n)).map(e=>t.relative(r.root,e));return{cut:i.length>=tb,names:a}},sb=async e=>e.repo?rb(e):ob(e),cb=async(e,t,n)=>{if(n.shown.length>=500||n.checked>=t.length)return n;let r=t.slice(n.checked,n.checked+500-n.shown.length),i=(await Promise.all(r.map(async t=>e(t)))).flatMap(e=>`error`in e?[]:[e.shown]);return cb(e,t,{checked:n.checked+r.length,shown:[...n.shown,...i]})},lb=(e,t)=>{let n=[],r=t.checked-t.shown.length;return e.cut&&n.push(`(${String(tb)} 件で探索を打ち切ったため、まだ見ていないファイルがあります。path で絞ってください)`),t.checked<e.names.length&&n.push(`(${e.names.length} 件中 ${t.shown.length} 件まで。path か glob で絞ってください)`),r>0&&n.push(`(範囲外へのリンクなど ${r} 件は除きました)`),n},ub=(e,t)=>{let n=[...t.shown,...lb(e,t)];return n.length===0?`(該当なし)`:n.join(`
72
72
  `)},db=async(e,t)=>{let n=await sb(t),r=n.names.toSorted(),i=await cb(Ay(e,t.target.root),r,{checked:0,shown:[]});return ub({cut:n.cut,names:r},i)},fb=async(e,t)=>{let n=__(t,`引数`),r=await ky(e,v_(n,`path`),C_(n,`glob`));return`error`in r?F(r.error):r.target.identity.directory?h_(await db(e,r)):h_(r.target.shown)},pb=`.git`,mb=`見つかりません`,hb=`読み取りを許可された範囲の外です`,gb=`リンク先が読み取りを許可された範囲の外です`,_b=`.git の中は読めません`,vb=`通常のファイルではありません`,L=(e,n)=>{let r=t.relative(n,e),i=r===`..`||r.startsWith(`..${t.sep}`);return r===``||!i&&!t.isAbsolute(r)},yb=(e,n)=>t.relative(n,e).split(t.sep).some(e=>e.toLowerCase()===pb),bb=(e,t)=>e.gitDirs.some(e=>L(t,e))||e.roots.some(e=>L(t,e.lexical)&&yb(t,e.lexical)||L(t,e.real)&&yb(t,e.real)),xb=(e,t)=>e.roots.some(e=>L(t,e.lexical)||L(t,e.real)),Sb=(e,t)=>e.roots.find(e=>L(t,e.real))?.real,Cb=(e,n)=>{for(let r of[e.cwd.real,e.cwd.lexical])if(L(n,r))return t.relative(r,n)||`.`;let r=e.roots.find(e=>L(n,e.real));return r===void 0?n:t.join(r.lexical,t.relative(r.real,n))},wb=async(e,t)=>{let n=await c(e).catch(()=>void 0),r=n?.isFile()===!0||t===`entry`&&n?.isDirectory()===!0;if(n!==void 0&&r)return{dev:n.dev,directory:n.isDirectory(),ino:n.ino}},Tb=(e,t)=>({error:`${e}: ${t}`}),Eb=async(e,t)=>{let n=await f(t.lexical).catch(()=>void 0);if(n===void 0)return Tb(t.given,mb);let r=Sb(e,n);return r===void 0?Tb(t.given,gb):bb(e,n)?Tb(t.given,_b):{real:n,root:r}},Db=async(e,t)=>{let n=await Eb(e,t);if(`error`in n)return n;let{real:r,root:i}=n,a=await wb(r,t.kind);return a===void 0?Tb(t.given,vb):{identity:a,real:r,root:i,shown:Cb(e,t.lexical)}},Ob=async(e,n,r)=>{if(n===``||n.includes(`\0`))return Tb(n,mb);let i=t.resolve(e.cwd.real,n);return xb(e,i)?bb(e,i)?Tb(n,_b):Db(e,{given:n,kind:r,lexical:i}):Tb(n,hb)},kb=async e=>{let n=t.resolve(e),r=await f(n).catch(()=>void 0);if(r!==void 0)return{lexical:n,real:r}},Ab=async e=>{let t=(await x(e,[`rev-parse`,`--path-format=absolute`,`--absolute-git-dir`,`--git-common-dir`])??``).split(`
73
73
  `).filter(e=>e!==``);return(await Promise.all(t.map(async e=>kb(e)))).flatMap(e=>e?.real??[])},jb=()=>{let e=new Map;return async t=>{let n=e.get(t);return n===void 0&&(n=x(t,[`rev-parse`,`--is-inside-work-tree`]).then(e=>e===`true`),e.set(t,n)),n}},Mb=async(e,t)=>{let n=await kb(e);if(n===void 0)throw Error(`${e}: the reviewed directory does not exist`);let r=[n,...(await Promise.all(t.map(async e=>kb(e)))).flatMap(e=>e??[])],i={cwd:n,gitDirs:await Ab(n.real),roots:r};return{check:async(e,t)=>Ob(i,e,t),isRepo:jb(),roots:r.map(e=>e.real)}},Nb={description:`ファイルを行番号付きで読む。読みたいファイルと行範囲は、何回にも分けず 1 回の呼び出しにまとめて渡すこと(50 ファイルまで)。上限は 1 回 ${Jv} 文字、1 ファイル ${qv} 行。大きなファイルは grep で場所を探してから行範囲で読む`,name:`read_files`,schema:{additionalProperties:!1,properties:{files:{description:`読むファイル。必要なものをまとめて渡す`,items:{additionalProperties:!1,properties:{end_line:{description:`ここまで読む(省略時は末尾)`,minimum:1,type:`integer`},path:{description:`リポジトリからの相対パス(追加の読み取りディレクトリは絶対パス)`,type:`string`},start_line:{description:`ここから読む(1 始まり、省略時は先頭)`,minimum:1,type:`integer`}},required:[`path`],type:`object`},minItems:1,type:`array`}},required:[`files`],type:`object`}},Pb={description:`git grep でファイルの中身を探し、\`パス:行番号:行\` で返す(前後の行は \`パス-行番号-行\`)。.gitignore で無視されたものは探さない。上限は 100 件、${gy} 文字。見つけた場所は read_files でまとめて読む`,name:`grep`,schema:{additionalProperties:!1,properties:{context:{description:`前後に出す行数(0〜3)`,type:`integer`},fixed:{description:`true なら pattern を正規表現でなく文字列として探す`,type:`boolean`},glob:{description:`対象を絞る glob(例: *.ts、src/**/*.test.ts。波括弧 {a,b} は使えない)`,type:`string`},ignore_case:{type:`boolean`},path:{description:`探すディレクトリかファイル(省略時はリポジトリ全体)`,type:`string`},pattern:{description:`拡張正規表現(POSIX ERE)。1 行ごとに照合する`,type:`string`}},required:[`pattern`],type:`object`}},Fb={description:`ファイルの一覧を返す(.gitignore で無視されたものは除く。500 件まで)`,name:`list_files`,schema:{additionalProperties:!1,properties:{glob:{description:`絞り込む glob(例: *.test.ts、src/**/*.ts。波括弧 {a,b} は使えない)`,type:`string`},path:{description:`一覧するディレクトリ(省略時はリポジトリ全体)`,type:`string`}},type:`object`}},Ib=new Map([[`grep`,Xy],[`list_files`,fb],[`read_files`,hy]]),Lb=e=>e instanceof I?`引数が正しくありません: ${e.message}`:`失敗しました: ${String(e)}`,Rb=async(e,t,n)=>{let r=Ib.get(t.name);if(r===void 0)return F(`${t.name} という道具はありません`);try{return await r(await e(),t.input,n)}catch(e){return F(Lb(e))}},zb=async(e,t,n)=>{if(e!==void 0&&t.name===Mv.name)try{return await Iv(e,t.input,n)}catch(e){return F(Lb(e))}},Bb=e=>{let t,n=async()=>(t??=Mb(e.cwd,e.extraReadDirs),t),r=[Nb,Pb,Fb];return e.bash!==void 0&&r.push(Mv),{run:async(t,r)=>{let i=await zb(e.bash,t,r)??await Rb(n,t,r);return{id:t.id,isError:i.isError,output:i.output}},specs:r}},Vb=(e,t)=>async(n,r,i)=>Ef(async a=>{let o={...t,command:n,scratch:a,timeoutMs:r};return i===void 0?e.run(o):e.run({...o,signal:i})}),Hb=class{#e;#t;#n=new Set;constructor(e,t){this.#e=e,this.#t=t}async with(e,t){let n={cwd:e.cwd,extraReadDirs:e.extraReadDirs},r=this.#e;return!e.exec||r===void 0||!await this.#r(r,e.cwd)?t(Bb(n)):t(Bb({...n,bash:Vb(r,n)}))}async#r(e,t){try{return await e.ready(t),!0}catch(e){let t=`サンドボックスの確認が通らないため、bash を使わずにレビューします(読み取り・検索・Web だけで調べます): ${D(e)}`;return this.#n.has(t)||(this.#n.add(t),this.#t(t)),!1}}},Ub=class extends N{usage;searches;constructor(e,t,n){super(e.kind,e.message,e.retryAfterMs),this.name=`LoopError`,this.usage=t,this.searches=n}},Wb=6e4,Gb=(e,t)=>e.reduce((e,n)=>e+n[t],0),Kb=(e,t)=>{if(t.limit===void 0)return!0;let n=Gb(e,t.field);return n===0||n+t.amount<=t.limit},qb=(e,t)=>{let n=e.toSorted((e,t)=>e.expiresAt-t.expiresAt);return n[n.findIndex((e,r)=>Kb(n.slice(r+1),t))]?.expiresAt??0},Jb=(e,t)=>[{amount:t,field:`tokens`,limit:e.tokenLimit},{amount:1,field:`requests`,limit:e.requestLimit}],Yb=(e,t)=>({...e,entries:e.entries.filter(e=>e.expiresAt>t)}),Xb=(e,t)=>Jb(e,t).every(t=>Kb(e.entries,t)),Zb=(e,t,n)=>{let r=Math.max(...Jb(e,t).map(t=>qb(e.entries,t)));return Math.max(r-n,1)},Qb=(e,t,n)=>{if(t.limit===void 0||t.remaining===void 0)return[];let r=t.limit-Gb(e,t.field)-t.remaining;if(r<=0)return[];let i=n+(t.resetMs??6e4);return t.field===`tokens`?[{expiresAt:i,requests:0,tokens:r}]:[{expiresAt:i,requests:r,tokens:0}]},$b=(e,t,{at:n,configured:r})=>{let i=r.tokensPerMinute??t.limitTokens??e.tokenLimit,a=r.requestsPerMinute??t.limitRequests??e.requestLimit,o={field:`tokens`,limit:i,remaining:t.remainingTokens,resetMs:t.resetTokensMs},s={field:`requests`,limit:a,remaining:t.remainingRequests,resetMs:t.resetRequestsMs},{entries:c}=e;return{entries:[...c,...Qb(c,o,n),...Qb(c,s,n)],requestLimit:a,tokenLimit:i}},ex=(e,t,n)=>({...e,entries:e.entries.map(e=>e===t?{expiresAt:e.expiresAt,requests:e.requests,tokens:n}:e)}),tx=(e,t)=>{let n={entries:[],requestLimit:e.requestsPerMinute,tokenLimit:e.tokensPerMinute},r=()=>(n=Yb(n,t()),n);return{fits:e=>Xb(r(),e),release:e=>{n={...n,entries:n.entries.filter(t=>t!==e)}},reserve:e=>{let i=r(),a={expiresAt:t()+Wb,requests:1,tokens:e};return n={...i,entries:[...i.entries,a]},a},settle:(i,a,o)=>{n=$b(ex(r(),i,a),o,{at:t(),configured:e})},waitMs:e=>Zb(r(),e,t())}},nx=async(e,t)=>{await v(e,void 0,{signal:t})},rx=(e,t)=>t.waiter.priority-e.waiter.priority||e.order-t.order,ix=()=>new N(`aborted`,`中断されました`),ax=async(e,t)=>new Promise((n,r)=>{let{signal:i}=t;if(i?.aborted===!0){r(ix());return}let a=new AbortController,o={estimate:t.estimate,grant:e=>{a.abort(),n(e)},priority:t.priority};i?.addEventListener(`abort`,()=>{e.remove(o),r(ix())},{once:!0,signal:a.signal}),e.add(o)}),ox=(e,t)=>{let n=0;for(let{waiter:r}of e){if(!t.fits(r.estimate))break;r.grant(t.reserve(r.estimate)),n+=1}return e.slice(n)},sx=(e,t)=>{let n=[],r=!1,i=new AbortController,a=0,o=async()=>{r=!0,n=ox(n,e);for(let[r]=n;r!==void 0;[r]=n)i=new AbortController,await t(e.waitMs(r.waiter.estimate),i.signal).catch(()=>!1),n=ox(n,e);r=!1},s=()=>{r?i.abort():o().catch(()=>!1)};return{add:e=>{a+=1,n=[...n,{order:a,waiter:e}].toSorted(rx),s()},kick:s,remove:e=>{n=n.filter(t=>t.waiter!==e),s()}}},cx=(e,t)=>{let n=sx(e,t);return{acquire:async e=>ax(n,e),release:t=>{e.release(t),n.kick()},settle:(t,r,i)=>{e.settle(t,r,i),n.kick()}}},lx=(e={})=>{let t=e.now??Date.now,n=e.sleep??nx,r=new Map,i=new Map,a=i=>{let a=r.get(i)??cx(tx(e.limits?.[i]??{},t),n);return r.set(i,a),a},o=e=>{let t=i.get(e);return i.delete(e),t};return{acquire:async(e,t)=>{let n=a(e),r=await n.acquire(t),o={estimate:t.estimate,key:e};return i.set(o,{entry:r,queue:n}),o},release:e=>{let t=o(e);t?.queue.release(t.entry)},settle:(e,t,n)=>{let r=o(e);r?.queue.settle(r.entry,t,n)}}},ux=async(e,t,n)=>{let{promise:r,resolve:i}=Promise.withResolvers();n(r);try{return await e.step({...t,onStarted:()=>{i(void 0)}})}finally{i(void 0)}},dx=e=>{let t=new Map;return{provider:e.provider,step:async n=>{let r=pg(n);if(r===void 0)return e.step(n);let i=t.get(r);return i===void 0?ux(e,n,e=>{t.set(r,e)}):(await i,e.step(n))}}},fx=e=>typeof e==`object`&&!!e&&!Array.isArray(e),R=(e,t)=>{if(!fx(e))return;let n=e[t];if(typeof n==`string`)return n},px=(e,t)=>{if(!fx(e))return[];let n=e[t];return Array.isArray(n)?n:[]},mx=e=>e.flatMap(e=>R(e,`url`)??[]),hx=(e,t)=>e===void 0?{urls:t}:{query:e,urls:t},gx=(e,t)=>{if(fx(e))return e[t]},_x=e=>e.filter(e=>R(e,`type`)===`web_search_call`).map(e=>{let t=gx(e,`action`),n=R(t,`url`)??[];return hx(R(t,`query`),[...mx(px(t,`sources`)),n].flat())}),vx=(e,t)=>e.find(e=>R(e,`type`)===`web_search_tool_result`&&R(e,`tool_use_id`)===t),yx=e=>e.filter(e=>R(e,`type`)===`server_tool_use`&&R(e,`name`)===`web_search`).map(t=>{let n=vx(e,R(t,`id`));return hx(R(gx(t,`input`),`query`),mx(px(n,`content`)))}),bx=e=>{if(e.role!==`assistant`||!Array.isArray(e.turn))return[];let t=e.turn;return e.provider===`openai`?_x(t):yx(t)},xx=new Set([`find_in_page`,`open_page`]),Sx=e=>e.role!==`assistant`||!Array.isArray(e.turn)?0:e.provider===`openai`?e.turn.filter(e=>R(e,`type`)===`web_search_call`&&!xx.has(R(gx(e,`action`),`type`)??``)).length:bx(e).length,Cx=e=>e.filter(e=>R(e,`type`)===`mcp_call`).map(e=>({arguments:R(e,`arguments`)??``,server:R(e,`server_label`)??``,tool:R(e,`name`)??``})),wx=e=>e.filter(e=>R(e,`type`)===`mcp_tool_use`).map(e=>({arguments:JSON.stringify(gx(e,`input`)??{}),server:R(e,`server_name`)??``,tool:R(e,`name`)??``})),Tx=e=>{if(e.role!==`assistant`||!Array.isArray(e.turn))return[];let t=e.turn;return e.provider===`openai`?Cx(t):wx(t)},Ex=e=>e.kind===`refusal`?0:Sx(e.assistant),Dx=(e,t)=>{if(e.kind===`refusal`)return{...t,kind:e.kind,mcpCalls:[],searches:[],tools:[],usage:e.usage};let n={...t,kind:e.kind,mcpCalls:Tx(e.assistant),searches:bx(e.assistant),tools:[],usage:e.usage};return e.kind===`tools`?{...n,tools:e.calls.map(({input:e,name:t})=>({input:e,name:t}))}:n},Ox=1e4,kx=6,Ax=2e3,jx=2,Mx=(e,t,n)=>{n instanceof $g?(e.scheduler.settle(t,e.counted(n.spent),{}),e.spent(n.spent)):e.scheduler.release(t)},Nx=async(e,t)=>{let{signal:n}=t.request,r=await e.scheduler.acquire(e.schedulerKey,{estimate:t.estimate,priority:t.priority,...n&&{signal:n}});try{let n=await e.client.step(t.request);return e.scheduler.settle(r,e.counted(n.usage),n.rate),n}catch(t){throw Mx(e,r,t),t}},Px=(e,t,n)=>{let r=n.throttledSince??e.now(),i=t.retryAfterMs??Ox;if(e.now()+i>r+e.throttleBudgetMs)throw new N(`server`,`レート制限が ${String(e.throttleBudgetMs)} ms のあいだ解けませんでした: ${t.message}`);return{retries:{...n,throttledSince:r},waitMs:i}},Fx=e=>({retries:{...e,serverFailures:e.serverFailures+1},waitMs:Ax*jx**e.serverFailures}),Ix=(e,t,n)=>{if(t instanceof N&&t.kind===`throttled`)return Px(e,t,n);if(t instanceof N&&t.kind===`server`&&n.serverFailures<kx)return Fx(n);throw t},Lx=async(e,t,n)=>{try{await e.sleep(t,n)}catch{throw new N(`aborted`,`中断されました`)}},Rx=async(e,t)=>{let n={serverFailures:0,throttledSince:void 0};for(;;)try{return await Nx(e,t)}catch(r){let i=Ix(e,r,n);({retries:n}=i),await Lx(e,i.waitMs,t.request.signal)}},zx=6e5,Bx=.8,Vx=3.5,Hx=2,Ux=`道具を使える回数、または会話の長さの上限に近づきました。これ以上道具は使えません。ここまでに分かったことで、指定の形式で回答してください。`,Wx={cacheRead:0,cacheWrite:0,output:0,reasoning:0,uncached:0},Gx=(e,t)=>({cacheRead:e.cacheRead+t.cacheRead,cacheWrite:e.cacheWrite+t.cacheWrite,output:e.output+t.output,reasoning:e.reasoning+t.reasoning,uncached:e.uncached+t.uncached}),Kx=e=>e.cacheRead+e.cacheWrite+e.uncached,qx=e=>{switch(e.role){case`user`:case`note`:return e.content.length;case`tool_results`:return e.results.reduce((e,t)=>e+t.output.length,0);case`assistant`:return 0;default:return 0}},Jx=async(e,t)=>{await v(e,void 0,{signal:t})},Yx=class{#e;#t;#n;#r=0;#i=Wx;#a=Wx;#o=0;#s=0;#c=0;#l=0;#u=0;#d=!1;constructor(e){this.#e=e,this.#n=[{content:e.prompt,role:`user`}],this.#t={client:e.client,counted:e.counted,now:e.now??Date.now,scheduler:e.scheduler,schedulerKey:e.schedulerKey,sleep:e.sleep??Jx,spent:e=>{this.#i=Gx(this.#i,e)},throttleBudgetMs:e.throttleBudgetMs??zx}}async run(){try{return await this.#f()}catch(e){throw e instanceof N?new Ub(e,this.#i,this.#l):new Ub(new N(`bad_request`,String(e)),this.#i,this.#l)}}async#f(){for(;;){let e=await this.#g(),t=await this.#v(e);if(t!==void 0)return{searches:this.#l,steps:this.#o,text:t,toolCalls:this.#c,usage:this.#i}}}#p(){return this.#n.slice(this.#r).reduce((e,t)=>e+qx(t),0)/Vx}get#m(){return this.#e.maxSearches??1/0}#h(){return{...this.#e.request,final:this.#d,messages:[...this.#n],tools:this.#e.tools.specs,webSearch:this.#e.request.webSearch&&this.#l<this.#m}}async#g(){let e=Math.ceil(this.#e.counted(this.#a)+this.#a.output+this.#p());this.#r=this.#n.length;let t=this.#t.now(),n=await Rx(this.#t,{estimate:e,priority:e,request:this.#h()});return this.#_(n,this.#t.now()-t),n}#_(e,t){this.#o+=1,this.#a=e.usage,this.#i=Gx(this.#i,e.usage),this.#l+=Ex(e),this.#e.onStep?.(Dx(e,{final:this.#d,ms:t,step:this.#o}))}async#v(e){switch(e.kind){case`tools`:await this.#y(e);return;case`answer`:return this.#x(e);case`refusal`:throw new N(`bad_request`,`モデルが回答を拒否しました: ${e.message}`);default:throw new N(`bad_request`,`モデルの応答の種類が分かりません`)}}async#y(e){if(this.#d)throw new N(`bad_request`,`道具を禁じた最後の step で道具が呼ばれました`);this.#n.push(e.assistant);let{signal:t}=this.#e.request,n=await Promise.all(e.calls.map(async e=>this.#e.tools.run(e,t)));this.#n.push({results:n,role:`tool_results`}),this.#s+=1,this.#c+=e.calls.length,this.#b()}#b(){let e=Kx(this.#a)+this.#a.output+this.#p(),t=this.#e.maxSteps??1/0;this.#s<t&&e<this.#e.contextTokens*Bx||(this.#n.push({content:Ux,role:`note`}),this.#d=!0)}#x(e){if(this.#n.push(e.assistant),this.#u>=Hx)return e.text;let t=this.#e.checkAnswer?.(e.text);if(t===void 0)return e.text;this.#u+=1,this.#n.push({content:t,role:`user`})}},Xx=async e=>new Yx(e).run(),Zx={aborted:`failed`,auth:`auth`,bad_request:`failed`,quota:`rate_limit`,server:`overloaded`,throttled:`overloaded`},Qx=(e,t)=>({cachedInputTokens:e.cacheRead,costUsd:t,inputTokens:e.uncached+e.cacheRead+e.cacheWrite,outputTokens:e.output,reasoningTokens:e.reasoning}),$x=(e,t,n)=>wv(t,e.price)+n*e.searchUsd,eS=(e,t)=>{if(!(e instanceof N))return{kind:`failed`,message:String(e),ok:!1};let n={kind:Zx[e.kind],message:e.message,ok:!1};return!(e instanceof Ub)||t===void 0?n:{...n,usage:Qx(e.usage,$x(t,e.usage,e.searches))}},tS=e=>{try{return{data:JSON.parse(e),ok:!0}}catch{return{ok:!1}}},nS=(e,t)=>Object.fromEntries(t.map(t=>{let n=e.providers[Sv(t).provider],r={};return n.tokensPerMinute!==void 0&&(r.tokensPerMinute=n.tokensPerMinute),n.requestsPerMinute!==void 0&&(r.requestsPerMinute=n.requestsPerMinute),[t,r]})),rS=(e,t,n)=>{let r=tS(e.text),i={...Qx(e.usage,n),toolCalls:e.toolCalls,turns:e.steps};return r.ok?{data:r.data,model:t,ok:!0,raw:e.text,usage:i}:{kind:`bad_output`,message:`回答が JSON ではありません`,model:t,ok:!1,raw:e.text,usage:i}},iS=(e,t,n)=>e===`model`||n===void 0?`akapen:${t}`:`akapen:${t}:${n.split(`.`)[0]??n}`,aS=(e,t)=>{let{trace:n}=e;return n===void 0?{}:{onStep:e=>{n({model:t,type:`step`,...e})}}},oS=class{name=`api`;#e;#t;#n=new Map;#r;constructor(e,t=[]){this.#e=e,this.#r=new dv(e.fetch);let n=[...new Set([...Object.values(e.tiers),...t])];this.#t=e.scheduler??lx({limits:nS(e.api,n)})}resolveModel(e){return this.#e.tiers[e]}async run(e){let t=e.model??this.resolveModel(e.tier),n=this.#i(t);try{let r=Sv(t);if(n===void 0)throw new N(`bad_request`,`${t} の単価が設定にありません(api.providers.${r.provider}.prices.${r.model})`);let i=await this.#a(e,t);return rS(i,t,$x(n,i.usage,i.searches))}catch(e){return eS(e,n)}}#i(e){try{let t=Sv(e),n=this.#e.api.providers[t.provider],r=n.prices[t.model];return r===void 0?void 0:{price:r,searchUsd:n.searchUsd}}catch{return}}async#a(e,t){return(this.#e.commands??new Hb(void 0,()=>void 0)).with(e,async n=>this.#o(e,t,mv(n,this.#r,e.docHosts)))}async#o(e,t,n){let r=Sv(t);return Xx({client:this.#s(r.provider),contextTokens:Dv(r.model),counted:e=>Cv(r.provider,e),prompt:e.prompt,request:{cacheKey:iS(this.#e.api.cacheKeyScope,t,e.label),effort:e.effort,model:r.model,outputSchema:e.schema,webSearch:e.web,...hv(e),...gv(e)},scheduler:this.#t,schedulerKey:t,tools:n,...aS(e,t)})}#s(e){let t=this.#n.get(e);if(t!==void 0)return t;let n=dx(this.#e.clients?.[e]??this.#c(e));return this.#n.set(e,n),n}#c(e){let t=this.#e.api.providers[e],n=this.#e.env[t.apiKeyEnv];if(n===void 0||n===``)throw new N(`auth`,`環境変数 ${t.apiKeyEnv} に ${e} の API キーがありません`);return yv[e]({apiKey:n,..._v(t.baseURL),...vv(t.serviceTier)})}},sS=3e4,cS=()=>{let e=``,t=0,n=()=>{let n=e.length-sS;n>0&&(t+=n,e=e.slice(n))};return{add:t=>{e+=t,e.length>6e4&&n()},take:()=>(n(),{dropped:t,output:e})}},lS=e=>{try{if(e.pid!==void 0&&process.platform!==`win32`){process.kill(-e.pid,`SIGKILL`);return}e.kill(`SIGKILL`)}catch{}},uS=(e,t)=>{let n=!1,r=()=>{lS(e),e.stdout?.destroy(),e.stderr?.destroy()},i=setTimeout(()=>{n=!0,r()},t.timeoutMs);return t.signal?.addEventListener(`abort`,r,{once:!0}),t.signal?.aborted===!0&&r(),{done:()=>(clearTimeout(i),t.signal?.removeEventListener(`abort`,r),n)}},dS=async(e,t,n)=>new Promise((r,i)=>{let a=cS(),o=oe(e,[...t],{detached:process.platform!==`win32`,env:n.env,stdio:[`ignore`,`pipe`,`pipe`]}),s=uS(o,n);o.stdout.setEncoding(`utf8`).on(`data`,e=>{a.add(e)}),o.stderr.setEncoding(`utf8`).on(`data`,e=>{a.add(e)}),o.once(`error`,e=>{s.done(),i(e)}),o.once(`close`,e=>{r({...a.take(),code:e,timedOut:s.done()})})}),fS=(e,t)=>{let n=new Set(t.map(e=>e.toUpperCase())),r={};for(let[t,i]of Object.entries(e))i!==void 0&&!n.has(t.toUpperCase())&&(r[t]=i);return r},pS=e=>e===void 0?{}:{signal:e},mS=class{#e;#t=new Map;constructor(e){this.#e=e}async ready(e){let t=this.#t.get(e);t===void 0&&(t=this.#n(e),this.#t.set(e,t)),await t}async run(e){let t=await this.#r(e,this.#e.sandbox),n=[`sandbox`,`-P`,Gf,`--include-managed-config`,`-C`,e.cwd,...t.flatMap(e=>[`-c`,e]),`--`,`/bin/sh`,`-c`,e.command];return(this.#e.spawn??dS)(Bp(),n,{env:fS(this.#e.env,this.#e.hiddenEnv),timeoutMs:e.timeoutMs,...pS(e.signal)})}async#n(e){await xm(e,{},this.#e.codexHome);let t={...this.#e.sandbox,allowedDomains:this.#e.sandbox.allowedDomains.filter(e=>e.toLowerCase()!==pp)},n=this.#e.check??(async(e,t,n)=>Ip(Bp(),e,t,n));await Ef(async r=>n(e,await this.#r({cwd:e,extraReadDirs:[],scratch:r},t),r))}async#r(e,t){return up({blankedEnv:await um(this.#e.codexHome),cwd:e.cwd,deniedPaths:await tp(process.platform,this.#e.codexHome),deniedWhenRead:await Qf(process.platform,this.#e.codexHome),exec:!0,extraReadDirs:e.extraReadDirs,sandbox:t,scratch:e.scratch,systemReadPaths:Hp()})}},hS=e=>[...Object.values(e.stages),...Object.values(e.reviewerStages)].flatMap(e=>e.model??[]),gS=(e,t)=>{let n=hS(e),{anthropic:r,openai:i}=e.api.providers;Ov({anthropic:r.prices,openai:i.prices},[...Object.values(e.tiers),...n]);let a=new mS({codexHome:dp(t.env),env:t.env,hiddenEnv:[r.apiKeyEnv,i.apiKeyEnv,`ANTHROPIC_API_KEY`,`OPENAI_API_KEY`],sandbox:e.sandbox});return new oS({api:e.api,commands:new Hb(a,t.warn),env:t.env,tiers:e.tiers},n)},_S=e=>{let r=e.CLAUDE_CONFIG_DIR;return r===``?{...e,CLAUDE_CONFIG_DIR:t.join(n(),`.claude`)}:r===void 0||t.isAbsolute(r)?e:{CLAUDE_SECURESTORAGE_CONFIG_DIR:r,...e,CLAUDE_CONFIG_DIR:t.resolve(r)}},vS=(e,t)=>({env:_S(t.env),mcpServers:e.mcpServers,sandbox:e.sandbox,stripApiKey:e.stripApiKey,tiers:e.tiers,warn:t.warn}),yS=e=>t.isAbsolute(e)||!(e.includes(`/`)||e.includes(t.sep))?e:t.resolve(e),bS=(e,t)=>{let n=vS(e,t);if(e.backendKind===`cli`){let e=n.env.AKAPEN_CLAUDE_BIN;return e===void 0||e===``?new jf(n):new jf({...n,bin:yS(e)})}return new Ff(n)},xS=(e,t)=>e.reviewer===`api`?gS(e,t):e.reviewer===`codex`?new ih({codexHome:dp(t.env),mcpServers:e.mcpServers,sandbox:e.sandbox,tiers:e.tiers}):bS(e,t),SS=e=>typeof e==`object`&&!!e&&!Array.isArray(e),CS=(e,t)=>SS(e)&&Array.isArray(e[t])?e[t]:[],z=(e,t)=>{let n=e[t];return typeof n==`string`||typeof n==`number`?String(n):`?`},wS=e=>CS(e,`findings`).flatMap(e=>SS(e)?[` - [${z(e,`severity`)}] ${z(e,`file`)}:${z(e,`start_line`)}-${z(e,`end_line`)} ${z(e,`title`)}`]:[]),TS=e=>{if(!SS(e)||!Array.isArray(e.coverage))return[];let t=CS(e,`coverage`).flatMap(e=>SS(e)?[z(e,`file`)]:[]),n=[...new Set(t)];return[` 確認したファイル ${String(n.length)} 件: ${n.join(`, `)}`]},ES=e=>{let t=CS(e,`verdicts`).filter(e=>SS(e));if(t.length===0)return[];let n=t.filter(e=>e.verdict===`drop`),r=t.filter(e=>e.verdict===`unverified`),i=t.length-n.length-r.length;return[` keep ${String(i)} 件 / unverified ${String(r.length)} 件 / drop ${String(n.length)} 件`,...[...r,...n].map(e=>` - ${z(e,`verdict`)} ${z(e,`id`)}: ${z(e,`reason`)}`)]},DS=e=>CS(e,`groups`).flatMap(e=>{let t=CS(e,`ids`);return t.length<=1?[]:[` - 同じ問題: ${t.map(String).join(`, `)}`]}),OS=e=>CS(e,`results`).flatMap(e=>SS(e)?[` - ${z(e,`id`)}: ${z(e,`status`)}`]:[]),kS=e=>{if(!SS(e)||typeof e.summary!=`string`)return[];let t=CS(e,`dependencies`).length;return[` 文脈 ${String(e.summary.length)} 文字、外部依存 ${String(t)} 件`]},AS=(e,n,r)=>{let i=t.join(r,`${e}.result.json`);if(!n.ok)return`${e} の出力(${i})\n 失敗 ${n.kind}: ${n.message}`;let{data:a}=n;return[`${e} の出力(${i})`,...kS(a),...TS(a),...wS(a),...ES(a),...DS(a),...OS(a)].join(`
74
- `)},jS=1e3,MS=60,NS=60,PS=2,FS=e=>String(e).padStart(PS,`0`),IS=e=>{let t=new Date(e);return`${FS(t.getHours())}:${FS(t.getMinutes())}:${FS(t.getSeconds())}`},LS=e=>{let t=Math.max(0,Math.round(e/jS)),n=Math.floor(t/MS);return n>=NS?`${String(Math.floor(n/NS))}h${FS(n%NS)}m`:`${String(n)}m${FS(t%MS)}s`},RS=(e,t)=>e.finished===0||e.finished>=e.planned?``:`、残り目安 ${LS(t/e.finished*(e.planned-e.finished))}`,zS=(e,t={})=>{let n=t.now??Date.now,r=n(),i={finished:0,planned:0,started:0},a=()=>{let e=n();return`[akapen ${IS(e)} +${LS(e-r)}]`},o=()=>`[${String(i.finished)}/${String(i.planned)}]`;return{finish:(t,s)=>{i.finished+=1;let c=RS(i,n()-r);e(`${a()} ${o()} ${t} ${s}${c}`)},note:t=>{e(`${a()} ${t}`)},output:(n,r,i)=>{t.debug===!0&&e(`${a()} ${AS(n,r,i)}`)},plan:(t,n)=>{t<=0||(i.planned+=t,e(`${a()} 予定: ${n}(+${String(t)}、合計 ${String(i.planned)} 回)`))},start:t=>{i.started+=1,i.planned=Math.max(i.planned,i.started),e(`${a()} ${o()} ${t} 開始`)},unplan:e=>{i.planned=Math.max(i.started,i.planned-e)}}},BS=e=>e.reduce((e,t)=>e+(t.costUsd??0),0),VS=(e,t)=>{e.halted||(e.halted=!0,e.stopped=!0,e.partial=!0,e.errors.push(t))},HS=()=>({errors:[],halted:!1,partial:!1,readDirs:new Set,resetsAt:void 0,stopped:!1,unplaced:[],usage:[]}),US=()=>{let e=HS();return{addReadDirs:t=>{for(let n of t)e.readDirs.add(n)},addUnplaced:t=>{e.unplaced.push(...t)},addUsage:(t,n,r)=>{e.usage.push({label:t,model:n,...r})},costUsd:()=>BS(e.usage),errors:()=>[...e.errors],flag:t=>{e.errors.push(t),e.partial=!0},halted:()=>e.halted,partial:()=>e.partial,readDirs:()=>[...e.readDirs],record:(t,n,r)=>{r.ok||(r.kind===`rate_limit`&&(e.halted=!0,e.resetsAt=r.resetsAt??e.resetsAt),e.errors.push(`${t}: ${r.message}`),e.partial||=n)},resetsAt:()=>e.resetsAt,stop:t=>{VS(e,t)},stopped:()=>e.stopped,unplaced:()=>[...e.unplaced],usage:()=>[...e.usage]}},WS=2,GS=(e,t)=>{let n=e.api.maxCostUsd,r=t.costUsd();e.reviewer===`api`&&n!==void 0&&r>=n&&t.stop(`料金が上限(api.maxCostUsd = ${String(n)} ドル)に達したため、新しい段を始めずに止めました(ここまで ${r.toFixed(WS)} ドル)`)},KS=(e,t)=>{let n=Promise.resolve(),r=!1;return{flush:async()=>n,write:i=>{let o=`${JSON.stringify({at:new Date().toISOString(),...i})}\n`;n=n.then(async()=>a(e,o).catch(n=>{r||(r=!0,t(`トレースを ${e} に書けませんでした: ${String(n)}`))}))}}},qS=(e,n,r)=>e?{traceFor:e=>KS(t.join(n,`${e}.trace.jsonl`),r)}:{},JS=e=>{process.stderr.write(`${e}\n`)},YS=e=>{},XS=async(e,t,n)=>{let r=await Eu(e.repo,e.stateDir),i=e.warn??JS;return{artifactDir:r,backend:e.backend??xS(t,{env:y(),warn:i}),config:t,pool:Ii(t.concurrency),repo:n,steps:zS(e.progress??YS,{debug:e.debug===!0}),tracker:US(),...qS(e.trace===!0,r,i)}},ZS=async(e,t)=>{if(t.scope.kind!==`staged`)return;let n=await Pt(e.repo,t.scope.tree);return n.skipped.length>0&&(e.warn??JS)(`--staged のコピーに、リポジトリと依存キャッシュの外を指す無視ファイルはつなぎませんでした(レビュアーはその中を読めません): ${n.skipped.join(`, `)}`),n},QS=e=>e===void 0?{}:{ask:e},$S=async e=>{let t=await as(e.repo,await Zo(e.repo,e.flags));return t===void 0?{}:_o(e.repo,t,{trust:e.trustRepoConfig===!0,warn:e.warn??JS,...QS(e.askTrust)})},eC=async(e,t)=>{if(t.llmsTxt.length===0||t.llmsTxt.every(e=>e.index!==void 0))return t;let n=await al(t.llmsTxt,{warn:e.warn??JS});return{...t,llmsTxt:n}},tC=async e=>{let t=await eC(e,e.config??await xu(e.reviewer,{ciCompatible:e.ciCompatible===!0,repoLayer:await $S(e),unknownKey:t=>{(e.warn??JS)(tu(t))}})),n=e.allowDomains??[];return n.length===0?t:{...t,sandbox:{...t.sandbox,allowedDomains:[...new Set([...t.sandbox.allowedDomains,...n])]}}},nC=async e=>{let t=await tC(e);return{config:t,kinds:e.kinds??await $o(e.repo,e.flags,t.kinds)}},rC=async()=>{},iC=async(e,t)=>e.knowledge===void 0?Ba(e.repo,await Zo(e.repo,e.flags),{repos:{...t.knowledgeRepos,...e.knowledgeRepos},warn:e.warn??JS}):{cleanup:rC,dirs:[],fingerprint:Pa(e.knowledge),text:e.knowledge},aC={name:`akapen`,version:`0.5.1`,description:`Multi-stage cross-model code review for Claude Code and Codex`,license:`UNLICENSED`,bin:{akapen:`dist/akapen.js`},files:[`dist`,`json-schema`],type:`module`,imports:{"#package.json":`./package.json`,"#src/*":`./src/*`},scripts:{test:`vitest run`,typecheck:`tsc --noEmit`,lint:`oxlint --type-aware --deny-warnings`,format:`oxfmt`,"format:check":`oxfmt --check`,check:`pnpm run format:check && pnpm run lint && pnpm run typecheck && pnpm run test`,build:`vite build`,"json-schema":`node src/settings/write-json-schemas.ts && oxfmt json-schema`,prepublishOnly:`pnpm run check && pnpm run build`},dependencies:{"@anthropic-ai/claude-agent-sdk":`0.3.316`,"@anthropic-ai/sdk":`1.6.0`,"@clack/prompts":`1.8.1`,"@openai/codex-sdk":`0.166.0`,openai:`7.53.0`,"smol-toml":`1.9.1`,zod:`4.6.7`},devDependencies:{"@tsconfig/strictest":`2.0.8`,"@types/node":`26.6.6`,oxfmt:`0.75.0`,oxlint:`1.90.0`,"oxlint-tsgolint":`7.0.2005`,typescript:`7.0.2`,vite:`8.3.5`,vitest:`5.0.5`},engines:{node:`>=24`,pnpm:`>=12.16.1 <13`},packageManager:`pnpm@12.16.1`},oC=async e=>({env:y(),home:n(),project:await x(e,[`rev-parse`,`--show-toplevel`])??t.resolve(e)}),sC=()=>aC.version,cC=/^origin\/[\w./-]+$/u,lC=7,uC=()=>{},dC=e=>{let{base:t,...n}=e.flags;if(!(Object.keys(n).length>0||t===void 0||!cC.test(t)))return t},fC=e=>e.filter(e=>e[0]).map(e=>e[1]),pC=e=>fC([[e.status!==`completed`,`結果の status が completed ではありません(${e.status})。最後まで回った結果だけを上げられます`],[e.sweepComplete===!1,`洗い出し(sweep)が済んでいない結果です(sweepComplete: false)。CI はこの結果を引き継げません`],[dC(e)===void 0,`範囲が --base origin/<名前> だけではありません(${JSON.stringify(e.flags)})。--base origin/<デフォルトブランチ> で回し直してください`],[(e.instructions??``)!==``,`追加指示つきの結果です。CI は追加指示なしで回すので、追加指示なしで回し直してください`],[e.ciCompatible!==!0,`--ci-compatible で回した結果ではありません。--ci-compatible を付けて回し直してください`]]),mC=async(e,t)=>{let n=await x(e.local.repo,[`merge-base`,t,`HEAD`]);if(n===void 0)return[`${t} と HEAD の merge-base が見つかりません`];let{baseline:r}=e.result;return r?.kind===`base`&&r.from===`${n}^{tree}`?[]:[`結果の範囲の起点が、いまの ${t} と HEAD の merge-base(${n.slice(0,lC)})と違います。レビューのあとに fetch か rebase で起点が変わったので、回し直してください`]},hC=async e=>await x(e.local.repo,[`rev-parse`,`--verify`,`HEAD^{tree}`])===e.result.tree?[]:[`結果の tree が HEAD のものと違います(未コミットの変更があったか、レビューのあとにコミットしました)。コミットしてから回し直してください`],gC=e=>{if(typeof e==`string`)return[e];let t=sC();return e.version===t?[]:[`CI が動かす akapen(${e.version})と手元の akapen(${t})の版が違います。手元を CI と同じ版にして回し直してください`]},_C=async e=>{let{settings:t}=eu({knowledgeRepos:(await ss(os({}))).knowledgeRepos});return{...ja(t.knowledgeRepos),...e.knowledgeRepos}},vC=async(e,n)=>{if(e.knowledgeRepository===void 0)return{};let r=e.knowledgeName??t.posix.basename(e.knowledgeRepository),i=(await _C(n))[r];return i===void 0?`CI は知識のリポジトリ ${e.knowledgeRepository} を ${r} という名前で渡しますが、手元の場所が分かりません。--knowledge-repo ${r}=<手元のパス> を付けてください`:{[r]:i}},yC=`ファイルの種類の表が、CI と同じ設定(組み込みとリポジトリの層だけ)で作り直したものと違います。--ci-compatible で回し直してください`,bC=`知識(.akapen/knowledge.md と知識のリポジトリ)が、CI と同じ入力で作り直したものと違います。CI と同じ --knowledge-repo を付け、そのリポジトリをデフォルトブランチにそろえて回し直してください`,xC=async(e,t,n)=>{if(typeof e.ci==`string`)return[];let r=await vC(e.ci,e.local);if(typeof r==`string`)return[r];let i=await iC({...t,knowledgeRepos:r},n);return await i.cleanup(),fC([[i.fingerprint!==(e.result.knowledge??``),bC]])},SC=async(e,t)=>{let n={ciCompatible:!0,flags:{base:t},instructions:``,repo:e.local.repo,reviewer:e.result.reviewedBy?.at(-1)?.reviewer??`codex`,warn:e.local.warn??uC},r=await nC({...n,warn:uC});return[...fC([[gr(r.kinds)!==e.result.kinds,yC]]),...await xC(e,n,r.config)]},CC=async e=>{let t=dC(e.result);if(t===void 0)return[];try{return[...await mC(e,t),...await SC(e,t)]}catch(e){return[`CI と同じ設定で作り直せません: ${D(e)}`]}},wC=async(e,t,n)=>{let r={ci:n,local:e,result:t};return[...pC(t),...await hC(r),...gC(n),...await CC(r)]},TC=e=>typeof e==`object`&&!!e&&!Array.isArray(e),EC=e=>TC(e)&&[`id`,`file`,`title`,`body`,`severity`].every(t=>typeof e[t]==`string`)&&typeof e.start_line==`number`&&typeof e.end_line==`number`&&Array.isArray(e.evidence),DC=e=>Number.isInteger(e)&&Number(e)>=0,OC=e=>e===void 0||Array.isArray(e)&&e.every(e=>typeof e==`string`),kC=e=>TC(e)&&DC(e.pass)&&DC(e.found)&&OC(e.findings)&&OC(e.unchecked)&&(e.open===void 0||typeof e.open==`boolean`)&&(e.pending===void 0||typeof e.pending==`boolean`)&&Array.isArray(e.seeds)&&e.seeds.length>0&&e.seeds.every(e=>EC(e)),AC=e=>e.pass<=1||e.found>0||(e.unchecked?.length??0)>0||e.open===!0||e.pending===!0,jC=be.O_WRONLY|be.O_CREAT|be.O_TRUNC|be.O_NOFOLLOW,MC=async(e,t)=>{await h(e,t,{flag:jC,mode:384})},NC=/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u,PC=/^[\w.#:~-]{1,256}$/u,FC=e=>{let t=Reflect.get(e,`cause`);return t===void 0||typeof t==`string`&&PC.test(t)},IC=e=>e===void 0||Array.isArray(e)&&e.every(e=>typeof e==`object`&&!!e&&`id`in e&&typeof e.id==`string`&&PC.test(e.id)&&FC(e)),LC=e=>{let t=Reflect.get(e,`sweeps`),n=Reflect.get(e,`sweepComplete`);return(t===void 0||Array.isArray(t)&&t.every(e=>kC(e)))&&(n===void 0||typeof n==`boolean`)},RC=e=>{let t=Reflect.get(e,`handoff`);return(t===void 0||fs(t))&&[`reviewedBy`,`rereviewMisses`].every(t=>{let n=Reflect.get(e,t);return n===void 0||Array.isArray(n)})},zC=e=>typeof e==`object`&&!!e&&`schemaVersion`in e&&e.schemaVersion===1&&`tree`in e&&typeof e.tree==`string`&&NC.test(e.tree)&&`findings`in e&&Array.isArray(e.findings)&&IC(e.findings)&&IC(Reflect.get(e,`accepted`))&&IC(Reflect.get(e,`previousStatus`))&&LC(e)&&RC(e),BC=async e=>{if(e===void 0)return;let t=JSON.parse(await d(e,`utf8`));if(!zC(t))throw Error(`${e} は akapen の結果 JSON ではありません`);return t},VC=`使い方: akapen push-result [--remote origin] [-C <dir>] [--knowledge-repo <name=path>] <result.json>
74
+ `)},jS=1e3,MS=60,NS=60,PS=2,FS=e=>String(e).padStart(PS,`0`),IS=e=>{let t=new Date(e);return`${FS(t.getHours())}:${FS(t.getMinutes())}:${FS(t.getSeconds())}`},LS=e=>{let t=Math.max(0,Math.round(e/jS)),n=Math.floor(t/MS);return n>=NS?`${String(Math.floor(n/NS))}h${FS(n%NS)}m`:`${String(n)}m${FS(t%MS)}s`},RS=(e,t)=>e.finished===0||e.finished>=e.planned?``:`、残り目安 ${LS(t/e.finished*(e.planned-e.finished))}`,zS=(e,t={})=>{let n=t.now??Date.now,r=n(),i={finished:0,planned:0,started:0},a=()=>{let e=n();return`[akapen ${IS(e)} +${LS(e-r)}]`},o=()=>`[${String(i.finished)}/${String(i.planned)}]`;return{finish:(t,s)=>{i.finished+=1;let c=RS(i,n()-r);e(`${a()} ${o()} ${t} ${s}${c}`)},note:t=>{e(`${a()} ${t}`)},output:(n,r,i)=>{t.debug===!0&&e(`${a()} ${AS(n,r,i)}`)},plan:(t,n)=>{t<=0||(i.planned+=t,e(`${a()} 予定: ${n}(+${String(t)}、合計 ${String(i.planned)} 回)`))},start:t=>{i.started+=1,i.planned=Math.max(i.planned,i.started),e(`${a()} ${o()} ${t} 開始`)},unplan:e=>{i.planned=Math.max(i.started,i.planned-e)}}},BS=e=>e.reduce((e,t)=>e+(t.costUsd??0),0),VS=(e,t)=>{e.halted||(e.halted=!0,e.stopped=!0,e.partial=!0,e.errors.push(t))},HS=()=>({errors:[],halted:!1,partial:!1,readDirs:new Set,resetsAt:void 0,stopped:!1,unplaced:[],usage:[]}),US=()=>{let e=HS();return{addReadDirs:t=>{for(let n of t)e.readDirs.add(n)},addUnplaced:t=>{e.unplaced.push(...t)},addUsage:(t,n,r)=>{e.usage.push({label:t,model:n,...r})},costUsd:()=>BS(e.usage),errors:()=>[...e.errors],flag:t=>{e.errors.push(t),e.partial=!0},halted:()=>e.halted,partial:()=>e.partial,readDirs:()=>[...e.readDirs],record:(t,n,r)=>{r.ok||(r.kind===`rate_limit`&&(e.halted=!0,e.resetsAt=r.resetsAt??e.resetsAt),e.errors.push(`${t}: ${r.message}`),e.partial||=n)},resetsAt:()=>e.resetsAt,stop:t=>{VS(e,t)},stopped:()=>e.stopped,unplaced:()=>[...e.unplaced],usage:()=>[...e.usage]}},WS=2,GS=(e,t)=>{let n=e.api.maxCostUsd,r=t.costUsd();e.reviewer===`api`&&n!==void 0&&r>=n&&t.stop(`料金が上限(api.maxCostUsd = ${String(n)} ドル)に達したため、新しい段を始めずに止めました(ここまで ${r.toFixed(WS)} ドル)`)},KS=(e,t)=>{let n=Promise.resolve(),r=!1;return{flush:async()=>n,write:i=>{let o=`${JSON.stringify({at:new Date().toISOString(),...i})}\n`;n=n.then(async()=>a(e,o).catch(n=>{r||(r=!0,t(`トレースを ${e} に書けませんでした: ${String(n)}`))}))}}},qS=(e,n,r)=>e?{traceFor:e=>KS(t.join(n,`${e}.trace.jsonl`),r)}:{},JS=e=>{process.stderr.write(`${e}\n`)},YS=e=>{},XS=async(e,t,n)=>{let r=await Eu(e.repo,e.stateDir),i=e.warn??JS;return{artifactDir:r,backend:e.backend??xS(t,{env:y(),warn:i}),config:t,pool:Ii(t.concurrency),repo:n,steps:zS(e.progress??YS,{debug:e.debug===!0}),tracker:US(),...qS(e.trace===!0,r,i)}},ZS=async(e,t)=>{if(t.scope.kind!==`staged`)return;let n=await Pt(e.repo,t.scope.tree);return n.skipped.length>0&&(e.warn??JS)(`--staged のコピーに、リポジトリと依存キャッシュの外を指す無視ファイルはつなぎませんでした(レビュアーはその中を読めません): ${n.skipped.join(`, `)}`),n},QS=e=>e===void 0?{}:{ask:e},$S=async e=>{let t=await as(e.repo,await Zo(e.repo,e.flags));return t===void 0?{}:_o(e.repo,t,{trust:e.trustRepoConfig===!0,warn:e.warn??JS,...QS(e.askTrust)})},eC=async(e,t)=>{if(t.llmsTxt.length===0||t.llmsTxt.every(e=>e.index!==void 0))return t;let n=await al(t.llmsTxt,{warn:e.warn??JS});return{...t,llmsTxt:n}},tC=async e=>{let t=await eC(e,e.config??await xu(e.reviewer,{ciCompatible:e.ciCompatible===!0,repoLayer:await $S(e),unknownKey:t=>{(e.warn??JS)(tu(t))}})),n=e.allowDomains??[];return n.length===0?t:{...t,sandbox:{...t.sandbox,allowedDomains:[...new Set([...t.sandbox.allowedDomains,...n])]}}},nC=async e=>{let t=await tC(e);return{config:t,kinds:e.kinds??await $o(e.repo,e.flags,t.kinds)}},rC=async()=>{},iC=async(e,t)=>e.knowledge===void 0?Ba(e.repo,await Zo(e.repo,e.flags),{repos:{...t.knowledgeRepos,...e.knowledgeRepos},warn:e.warn??JS}):{cleanup:rC,dirs:[],fingerprint:Pa(e.knowledge),text:e.knowledge},aC={name:`akapen`,version:`0.5.2`,description:`Multi-stage cross-model code review for Claude Code and Codex`,license:`UNLICENSED`,bin:{akapen:`dist/akapen.js`},files:[`dist`,`json-schema`],type:`module`,imports:{"#package.json":`./package.json`,"#src/*":`./src/*`},scripts:{test:`vitest run`,typecheck:`tsc --noEmit`,lint:`oxlint --type-aware --deny-warnings`,format:`oxfmt`,"format:check":`oxfmt --check`,check:`pnpm run format:check && pnpm run lint && pnpm run typecheck && pnpm run test`,build:`vite build`,"json-schema":`node src/settings/write-json-schemas.ts && oxfmt json-schema`,prepublishOnly:`pnpm run check && pnpm run build`},dependencies:{"@anthropic-ai/claude-agent-sdk":`0.3.319`,"@anthropic-ai/sdk":`1.6.0`,"@clack/prompts":`1.8.1`,"@openai/codex-sdk":`0.167.0`,openai:`7.53.0`,"smol-toml":`1.9.1`,zod:`4.6.7`},devDependencies:{"@tsconfig/strictest":`2.0.8`,"@types/node":`26.6.6`,oxfmt:`0.77.0`,oxlint:`1.92.0`,"oxlint-tsgolint":`7.0.2006`,typescript:`7.0.2`,vite:`8.3.6`,vitest:`5.1.0`},engines:{node:`>=24`,pnpm:`>=12.16.1 <13`},packageManager:`pnpm@12.16.1`},oC=async e=>({env:y(),home:n(),project:await x(e,[`rev-parse`,`--show-toplevel`])??t.resolve(e)}),sC=()=>aC.version,cC=/^origin\/[\w./-]+$/u,lC=7,uC=()=>{},dC=e=>{let{base:t,...n}=e.flags;if(!(Object.keys(n).length>0||t===void 0||!cC.test(t)))return t},fC=e=>e.filter(e=>e[0]).map(e=>e[1]),pC=e=>fC([[e.status!==`completed`,`結果の status が completed ではありません(${e.status})。最後まで回った結果だけを上げられます`],[e.sweepComplete===!1,`洗い出し(sweep)が済んでいない結果です(sweepComplete: false)。CI はこの結果を引き継げません`],[dC(e)===void 0,`範囲が --base origin/<名前> だけではありません(${JSON.stringify(e.flags)})。--base origin/<デフォルトブランチ> で回し直してください`],[(e.instructions??``)!==``,`追加指示つきの結果です。CI は追加指示なしで回すので、追加指示なしで回し直してください`],[e.ciCompatible!==!0,`--ci-compatible で回した結果ではありません。--ci-compatible を付けて回し直してください`]]),mC=async(e,t)=>{let n=await x(e.local.repo,[`merge-base`,t,`HEAD`]);if(n===void 0)return[`${t} と HEAD の merge-base が見つかりません`];let{baseline:r}=e.result;return r?.kind===`base`&&r.from===`${n}^{tree}`?[]:[`結果の範囲の起点が、いまの ${t} と HEAD の merge-base(${n.slice(0,lC)})と違います。レビューのあとに fetch か rebase で起点が変わったので、回し直してください`]},hC=async e=>await x(e.local.repo,[`rev-parse`,`--verify`,`HEAD^{tree}`])===e.result.tree?[]:[`結果の tree が HEAD のものと違います(未コミットの変更があったか、レビューのあとにコミットしました)。コミットしてから回し直してください`],gC=e=>{if(typeof e==`string`)return[e];let t=sC();return e.version===t?[]:[`CI が動かす akapen(${e.version})と手元の akapen(${t})の版が違います。手元を CI と同じ版にして回し直してください`]},_C=async e=>{let{settings:t}=eu({knowledgeRepos:(await ss(os({}))).knowledgeRepos});return{...ja(t.knowledgeRepos),...e.knowledgeRepos}},vC=async(e,n)=>{if(e.knowledgeRepository===void 0)return{};let r=e.knowledgeName??t.posix.basename(e.knowledgeRepository),i=(await _C(n))[r];return i===void 0?`CI は知識のリポジトリ ${e.knowledgeRepository} を ${r} という名前で渡しますが、手元の場所が分かりません。--knowledge-repo ${r}=<手元のパス> を付けてください`:{[r]:i}},yC=`ファイルの種類の表が、CI と同じ設定(組み込みとリポジトリの層だけ)で作り直したものと違います。--ci-compatible で回し直してください`,bC=`知識(.akapen/knowledge.md と知識のリポジトリ)が、CI と同じ入力で作り直したものと違います。CI と同じ --knowledge-repo を付け、そのリポジトリをデフォルトブランチにそろえて回し直してください`,xC=async(e,t,n)=>{if(typeof e.ci==`string`)return[];let r=await vC(e.ci,e.local);if(typeof r==`string`)return[r];let i=await iC({...t,knowledgeRepos:r},n);return await i.cleanup(),fC([[i.fingerprint!==(e.result.knowledge??``),bC]])},SC=async(e,t)=>{let n={ciCompatible:!0,flags:{base:t},instructions:``,repo:e.local.repo,reviewer:e.result.reviewedBy?.at(-1)?.reviewer??`codex`,warn:e.local.warn??uC},r=await nC({...n,warn:uC});return[...fC([[gr(r.kinds)!==e.result.kinds,yC]]),...await xC(e,n,r.config)]},CC=async e=>{let t=dC(e.result);if(t===void 0)return[];try{return[...await mC(e,t),...await SC(e,t)]}catch(e){return[`CI と同じ設定で作り直せません: ${D(e)}`]}},wC=async(e,t,n)=>{let r={ci:n,local:e,result:t};return[...pC(t),...await hC(r),...gC(n),...await CC(r)]},TC=e=>typeof e==`object`&&!!e&&!Array.isArray(e),EC=e=>TC(e)&&[`id`,`file`,`title`,`body`,`severity`].every(t=>typeof e[t]==`string`)&&typeof e.start_line==`number`&&typeof e.end_line==`number`&&Array.isArray(e.evidence),DC=e=>Number.isInteger(e)&&Number(e)>=0,OC=e=>e===void 0||Array.isArray(e)&&e.every(e=>typeof e==`string`),kC=e=>TC(e)&&DC(e.pass)&&DC(e.found)&&OC(e.findings)&&OC(e.unchecked)&&(e.open===void 0||typeof e.open==`boolean`)&&(e.pending===void 0||typeof e.pending==`boolean`)&&Array.isArray(e.seeds)&&e.seeds.length>0&&e.seeds.every(e=>EC(e)),AC=e=>e.pass<=1||e.found>0||(e.unchecked?.length??0)>0||e.open===!0||e.pending===!0,jC=be.O_WRONLY|be.O_CREAT|be.O_TRUNC|be.O_NOFOLLOW,MC=async(e,t)=>{await h(e,t,{flag:jC,mode:384})},NC=/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u,PC=/^[\w.#:~-]{1,256}$/u,FC=e=>{let t=Reflect.get(e,`cause`);return t===void 0||typeof t==`string`&&PC.test(t)},IC=e=>e===void 0||Array.isArray(e)&&e.every(e=>typeof e==`object`&&!!e&&`id`in e&&typeof e.id==`string`&&PC.test(e.id)&&FC(e)),LC=e=>{let t=Reflect.get(e,`sweeps`),n=Reflect.get(e,`sweepComplete`);return(t===void 0||Array.isArray(t)&&t.every(e=>kC(e)))&&(n===void 0||typeof n==`boolean`)},RC=e=>{let t=Reflect.get(e,`handoff`);return(t===void 0||fs(t))&&[`reviewedBy`,`rereviewMisses`].every(t=>{let n=Reflect.get(e,t);return n===void 0||Array.isArray(n)})},zC=e=>typeof e==`object`&&!!e&&`schemaVersion`in e&&e.schemaVersion===1&&`tree`in e&&typeof e.tree==`string`&&NC.test(e.tree)&&`findings`in e&&Array.isArray(e.findings)&&IC(e.findings)&&IC(Reflect.get(e,`accepted`))&&IC(Reflect.get(e,`previousStatus`))&&LC(e)&&RC(e),BC=async e=>{if(e===void 0)return;let t=JSON.parse(await d(e,`utf8`));if(!zC(t))throw Error(`${e} は akapen の結果 JSON ではありません`);return t},VC=`使い方: akapen push-result [--remote origin] [-C <dir>] [--knowledge-repo <name=path>] <result.json>
75
75
 
76
76
  手元のレビューの結果を、CI が前回の結果として拾えるように refs/akapen/<ブランチ>/<HEAD の sha> に上げます(同じブランチの古いものは消します)。
77
77
  CI と同じ条件で回した結果だけを上げます(--ci-compatible と --base origin/<デフォルトブランチ> で回し、追加指示なし、HEAD をコミットしたまま、CI と同じ版)。
@@ -158,7 +158,7 @@ CI と同じ条件で回した結果だけを上げます(--ci-compatible と
158
158
 
159
159
  1. 筋道: どの入力・操作・タイミングで、どんな誤った結果になるかを具体的に書ける
160
160
  2. 確認: 前提にしたこと(関数の中身、呼び出し元、設定値、ライブラリの挙動)をツールで実際に見た
161
- 3. 原因: 今回の変更行(追加行、または削除した位置の直後の行)に置ける
161
+ 3. 原因: 今回の変更行(追加行、または削除した位置の直後の行)に置ける。足した・変えた条件が守る対象の一部に効いていないときは、その条件の変更行に置き、条件を通らない経路を根拠に示す(直す場所が差分の外でもかまわない)
162
162
  4. 防御: 呼び出し元の検証、型、上位のエラー処理などで、すでに止まっていない
163
163
 
164
164
  確かめないまま出した指摘は、作者に存在しない問題を調べさせます。次のような「確かめる代わりの指摘」は出さないでください。
@@ -174,7 +174,7 @@ CI と同じ条件で回した結果だけを上げます(--ci-compatible と
174
174
 
175
175
  ## 外部の挙動の裏取り(必須)
176
176
 
177
- ライブラリ・フレームワーク・OS・クラウドサービスの挙動を根拠にするときは、記憶で書かないでください。まず \`<external_facts>\` を見て、そこにある事実はそのまま根拠に使ってください(\`evidence\` にはその出典を書きます)。そこにない挙動、または事前調査の事実と食い違う手がかりがある挙動だけを、次の順で自分で確認します。
177
+ 担当する変更や経路が依存するライブラリ・フレームワーク・OS・クラウドサービスの挙動は、記憶から仮説を立ててかまいません(たとえば「このライブラリは既定でこういうルートや設定を有効にしているのではないか」)。リポジトリに痕跡がない既定の挙動ほど、まず疑わないと確かめる機会がありません。ただし、記憶だけを指摘の根拠にはしないでください。仮説は、次の手順で裏が取れたものだけを根拠に使います。まず \`<external_facts>\` を見て、そこにある事実はそのまま根拠に使ってください(\`evidence\` にはその出典を書きます)。そこにない挙動、または事前調査の事実と食い違う手がかりがある挙動だけを、次の順で自分で確認します。
178
178
 
179
179
  1. lockfile(package-lock.json、pnpm-lock.yaml、go.sum、Cargo.lock など)で実際に使われているバージョンを確認する
180
180
  2. インストール済みのソース(\`node_modules\`、Go のモジュールキャッシュ、\`vendor/\`、\`site-packages\` など)を読む
@@ -212,7 +212,7 @@ CI と同じ条件で回した結果だけを上げます(--ci-compatible と
212
212
 
213
213
  ## 調べ方
214
214
 
215
- 調べる対象(定義・呼び出し元・テスト・設定)を先に洗い出し、検索をまとめて行ってから、必要な行範囲だけを読んでください。ファイル全体の通読や、検索と閲覧を一回ずつ往復する進め方は、時間を使い切る原因になります。問題に気づいたらその場で短くメモし、調べ終えてからメモをもとに指摘をまとめてください。
215
+ 調べる対象(定義・呼び出し元・テスト・設定)を先に洗い出し、検索をまとめて行ってから、必要な行範囲だけを読んでください。ファイル全体の通読や、検索と閲覧を一回ずつ往復する進め方は、時間を使い切る原因になります。ただし、担当範囲の変更が、処理を許可・拒否する条件やその適用範囲を足す・動かす・緩めるときは、その条件が何を何から守るのかを押さえ、守る対象に条件を通らず届く経路がないかを確かめてください。差分だけで判断できなければ、変更を囲む関数や、宣言・設定のまとまりを読みます。適用範囲が別の箇所(matcher、ルーティング、上位のミドルウェア、ライブラリの既定値など)で決まるならそこも確かめ、処理をライブラリへ委ねる経路では委譲先の挙動も含めて判断してください。読むのは、この判断に要る範囲だけに絞ってください。問題に気づいたらその場で短くメモし、調べ終えてからメモをもとに指摘をまとめてください。
216
216
 
217
217
  ツールを呼ぶたびに、それまでのやり取りをすべて読み直すことになります。呼ぶ回数と、1 回で受け取る出力の量を抑えてください。
218
218
 
@@ -522,8 +522,9 @@ CI と同じ条件で回した結果だけを上げます(--ci-compatible と
522
522
  - 修正が別の場所を壊していないか(呼び出し元、対になる箇所、テスト、ドキュメント)
523
523
  - 修正が中途半端で、同じ問題が別の経路で残っていないか
524
524
  - 修正によって振る舞いが後退していないか
525
+ - 修正が実現するはずの性質(たとえば足したチェック)が、守る対象のすべてで成り立っているか。その判断に要る既存のコードも読む
525
526
 
526
- 前回の指摘そのものが直ったかどうかは別の担当が確認するので、ここでは扱いません。
527
+ 前回の指摘の発生条件そのものが直ったかどうかは別の担当が確認するので、ここでは扱いません。ただし、修正が不完全で別の経路が残っていることは、ここで指摘してください。前回と同じ指摘は繰り返さず、修正と関係のない既存の問題は出さないでください。
527
528
  `,pO=`{{include:intro}}
528
529
 
529
530
  {{include:shared-inputs}}
@@ -866,7 +867,7 @@ head かその祖先のものがあれば、それを --previous に書き、拾
866
867
  `).flatMap(e=>oR(t,e)??[]).filter(e=>e.path!==n.file),cR=(e,t)=>e.shard.files.some(e=>e.path===t)?`(この shard で変更)`:e.scope.files.some(e=>e.path===t)?`(別の shard で変更)`:``,lR=(e,t,n)=>{let r=`- \`${t.name}\`(${t.file} で定義)`;if(n.length!==0)return n.length>WL?`${r}: 使われている箇所が ${String(n.length)} か所あるため省略`:[r,...n.map(t=>` - ${t.path}:${t.line}: ${t.text}${cR(e,t.path)}`)].join(`
867
868
  `)},uR=async e=>{let t=aR(e.shard),n=e.search??zL(e.repo);return(await Promise.all(t.map(async t=>lR(e,t,await sR(n,e.shard.tree,t))))).filter(e=>e!==void 0)},dR=`(読み物の上限に達したため、ここから先は省略)`,fR=(e,t)=>{let n=[],r=0;for(let i of e){if(r+=i.length,r>t)return n.push(dR),n;n.push(i)}return n},pR=`## 変更の周辺(akapen が機械的に集めたもの)
868
869
 
869
- ファイルを開く手間を減らすために渡しています。ここにあるコードは開き直さずに使ってください。使われている箇所は名前の一致で探しているので、無関係な一致も混じります。ここにない呼び出し元や定義は、これまでどおりツールで探してください。`,mR=(e,t)=>t.length===0?[]:[e,...t],hR=e=>{let t=new Set(e.shard.files.map(e=>e.path));return(e.related??[]).filter(e=>e.files.some(e=>t.has(e))&&e.files.some(e=>!t.has(e))).map(e=>Z(`- ${e.files.join(`、`)}: ${e.reason}`))},gR=async e=>{let t=await uR(e),n=e.shard.files.flatMap(e=>nR(e)??[]),r=[...mR(`### 変更した定義が使われている箇所`,t),...mR(`### 一緒に読むべき変更(context 段の判断)`,hR(e)),...mR(`### 変更を含む関数の全体(変更後)`,n)];return r.length===0?``:[pR,...fR(r,e.budget)].join(`
870
+ ファイルを開く手間を減らすために渡しています。ここにあるコードは開き直さずに使ってください。使われている箇所は名前の一致で探しているので、無関係な一致も混じります。ここにない呼び出し元や定義は、これまでどおりツールで探してください。`,mR=(e,t)=>t.length===0?[]:[e,...t],hR=e=>{let t=new Set(e.shard.files.map(e=>e.path));return(e.related??[]).filter(e=>e.files.some(e=>t.has(e))&&e.files.some(e=>!t.has(e))).map(e=>Z(`- ${e.files.join(`、`)}: ${e.reason}`))},gR=async e=>{let t=await uR(e),n=e.shard.files.flatMap(e=>nR(e)??[]),r=[...mR(`### 変更した定義が使われている箇所`,t),...mR(`### 一緒に読むべき変更(context 段の判断)`,hR(e)),...mR(`### 変更を囲むコード(変更後。${String(VL)} 行を超える関数は変更の前後 ${String(HL)} 行だけ)`,n)];return r.length===0?``:[pR,...fR(r,e.budget)].join(`
870
871
 
871
872
  `)},_R=20,vR=2,yR=(e,t)=>e.map(e=>({files:[...new Set(e.files.filter(e=>t.has(e)))],reason:e.reason})).filter(e=>e.files.length>=vR).slice(0,_R),bR=2,xR=e=>{if(!e.ctx.config.contextLinks)return[];let t=new Set(e.scope.files.map(e=>e.path));return yR(e.context.related,t)},SR=async(e,t,n)=>{let r=await gR({budget:Math.floor(e.ctx.config.shardChars/bR),related:xR(e),repo:e.input.repo,scope:e.scope,search:n,shard:t.scope}),i={...t.scope,readPack:r},{context:a,input:o,kinds:s}=e;return{...t,scope:i,vars:$({context:a,input:o,kinds:s,scope:i})}},CR=async(e,t,n=zL(e.input.repo))=>Promise.all(t.map(async t=>SR(e,t,n))),wR=(e,t)=>{let n=t%Math.max(e.length,1);return[...e.slice(n),...e.slice(0,n)]},TR=(e,t,n)=>{let r=wR(n.scope.files,n.offset);if(r.every((e,n)=>e===t.scope.files[n])&&r.length===t.scope.files.length)return{reviewer:n.reviewer,shard:t};let i=AP(n.scope,r),a=$({context:e.context,input:e.input,kinds:e.kinds,scope:i});return{reviewer:n.reviewer,shard:{label:t.label,scope:i,vars:a}}},ER=(e,t)=>e===1?``:`#${String(t+1)}`,DR=e=>oc.filter(t=>e[t]>0),OR=(e,t)=>{let n=DR(t),r=e.lenses.filter(e=>n.includes(e));return r.length===0?n:r},kR=(e,t)=>e.kinds.findIndex(e=>e.name===t)+1,AR=(e,t)=>{let n=t.files.filter(t=>T(e,t.kind).mode===`pair`).map(e=>({kind:e.kind,path:e.path,reason:`paired`}));return{...AP(t,t.files.filter(t=>T(e,t.kind).mode===`review`).toSorted((t,n)=>kR(e,t.kind)-kR(e,n.kind))),skipped:[...t.skipped,...n]}},jR=(e,t,n)=>AP(t,t.files.filter(t=>{let r=T(e.kinds,t.kind);return r.mode===`review`&&OR(r,e.roles).includes(n)})),MR=3,NR=5,PR=6,FR=10,IR=10,LR=1,RR=3,zR=/(?:\.(?:test|spec)|_test)(?=\.)/u,BR=e=>e.diff.split(`
872
873
  `).flatMap(e=>/^[+-]?\s*\d*\s*\|(?<code>.*)$/u.exec(e)?.groups?.code??[]).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akapen",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Multi-stage cross-model code review for Claude Code and Codex",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
@@ -27,10 +27,10 @@
27
27
  "prepublishOnly": "pnpm run check && pnpm run build"
28
28
  },
29
29
  "dependencies": {
30
- "@anthropic-ai/claude-agent-sdk": "0.3.316",
30
+ "@anthropic-ai/claude-agent-sdk": "0.3.319",
31
31
  "@anthropic-ai/sdk": "1.6.0",
32
32
  "@clack/prompts": "1.8.1",
33
- "@openai/codex-sdk": "0.166.0",
33
+ "@openai/codex-sdk": "0.167.0",
34
34
  "openai": "7.53.0",
35
35
  "smol-toml": "1.9.1",
36
36
  "zod": "4.6.7"
@@ -38,12 +38,12 @@
38
38
  "devDependencies": {
39
39
  "@tsconfig/strictest": "2.0.8",
40
40
  "@types/node": "26.6.6",
41
- "oxfmt": "0.75.0",
42
- "oxlint": "1.90.0",
43
- "oxlint-tsgolint": "7.0.2005",
41
+ "oxfmt": "0.77.0",
42
+ "oxlint": "1.92.0",
43
+ "oxlint-tsgolint": "7.0.2006",
44
44
  "typescript": "7.0.2",
45
- "vite": "8.3.5",
46
- "vitest": "5.0.5"
45
+ "vite": "8.3.6",
46
+ "vitest": "5.1.0"
47
47
  },
48
48
  "engines": {
49
49
  "node": ">=24",