loki-mode 9.54.2 → 9.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var dt=Object.create;var{getPrototypeOf:pt,defineProperty:jG,getOwnPropertyNames:ct}=Object;var lt=Object.prototype.hasOwnProperty;function it($){return this[$]}var at,ot,st=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?at??=new WeakMap:ot??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?dt(pt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of ct($))if(!lt.call(J,q))jG(J,q,{get:it.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var Zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var nt=($)=>$;function rt($,X){this[$]=nt.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:rt.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var WR={};B1(WR,{lokiDir:()=>h0,homeLokiDir:()=>GQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>O1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as tt}from"url";import{existsSync as Kq}from"fs";import{homedir as et}from"os";function $e(){let $=HR;for(let X=0;X<6;X++){if(Kq(h2($,"VERSION"))&&Kq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(HR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Kq(h2(X,"VERSION"))&&Kq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function GQ(){return h2(et(),".loki")}var HR,O1;var k1=s(()=>{HR=LG(tt(import.meta.url));O1=$e()});import{readFileSync as Xe}from"fs";import{resolve as Qe,dirname as ze}from"path";import{fileURLToPath as Ze}from"url";function L9(){if(t3!==null)return t3;let $="9.54.2";if(typeof $==="string"&&$.length>0)return t3=$,t3;try{let X=ze(Ze(import.meta.url)),Q=AG(X);t3=Xe(Qe(Q,"VERSION"),"utf-8").trim()}catch{t3="unknown"}return t3}var t3=null;var Jq=s(()=>{k1()});var LR={};B1(LR,{runOrThrow:()=>Ae,run:()=>X1,readStreamCapped:()=>qq,commandVersion:()=>Te,commandExists:()=>g5,ShellError:()=>TG,MAX_STDOUT_BYTES:()=>jR});async function qq($,X=jR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function X1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([qq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Ae($,X={}){let Q=await X1($,X);if(Q.exitCode!==0)throw new TG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=Ce($),Q=await X1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Ce($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function Te($,X="--version"){if(!await g5($))return null;let z=await X1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var jR=16777216,TG;var y8=s(()=>{TG=class TG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return De?"":$}var De,p0,$5,Z1,E61,A1,f1,m5,n;var e7=s(()=>{De=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),Z1=g2("\x1B[1;33m"),E61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),n=g2("\x1B[0m")});import{existsSync as ye}from"fs";async function Z2(){if(BQ!==void 0)return BQ;let $="/opt/homebrew/bin/python3.12";if(ye($))return BQ=$,$;let X=await g5("python3.12");if(X)return BQ=X,X;let Q=await g5("python3");return BQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return X1([Q,"-c",$],X)}var BQ;var m2=s(()=>{y8()});var gR={};B1(gR,{runStatus:()=>Z00});import{existsSync as u5,readFileSync as T9,readdirSync as kR,statSync as SR}from"fs";import{resolve as A5,basename as se}from"path";import{homedir as ne}from"os";function yR($){let X=Math.trunc($);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function bR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Yq/X);if(Z>Yq)Z=Yq;let K=Yq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=Z1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=yR($),Y=yR(X);return` ${f1}${Q}${n} ${J}[${q}]${n} ${z}% (${V} / ${Y})`}async function te(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${n}
2
+ var dt=Object.create;var{getPrototypeOf:pt,defineProperty:jG,getOwnPropertyNames:ct}=Object;var lt=Object.prototype.hasOwnProperty;function it($){return this[$]}var at,ot,st=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?at??=new WeakMap:ot??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?dt(pt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of ct($))if(!lt.call(J,q))jG(J,q,{get:it.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var Zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var nt=($)=>$;function rt($,X){this[$]=nt.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:rt.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var WR={};B1(WR,{lokiDir:()=>h0,homeLokiDir:()=>GQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>O1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as tt}from"url";import{existsSync as Kq}from"fs";import{homedir as et}from"os";function $e(){let $=HR;for(let X=0;X<6;X++){if(Kq(h2($,"VERSION"))&&Kq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(HR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Kq(h2(X,"VERSION"))&&Kq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function GQ(){return h2(et(),".loki")}var HR,O1;var k1=s(()=>{HR=LG(tt(import.meta.url));O1=$e()});import{readFileSync as Xe}from"fs";import{resolve as Qe,dirname as ze}from"path";import{fileURLToPath as Ze}from"url";function L9(){if(t3!==null)return t3;let $="9.55.0";if(typeof $==="string"&&$.length>0)return t3=$,t3;try{let X=ze(Ze(import.meta.url)),Q=AG(X);t3=Xe(Qe(Q,"VERSION"),"utf-8").trim()}catch{t3="unknown"}return t3}var t3=null;var Jq=s(()=>{k1()});var LR={};B1(LR,{runOrThrow:()=>Ae,run:()=>X1,readStreamCapped:()=>qq,commandVersion:()=>Te,commandExists:()=>g5,ShellError:()=>TG,MAX_STDOUT_BYTES:()=>jR});async function qq($,X=jR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function X1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([qq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Ae($,X={}){let Q=await X1($,X);if(Q.exitCode!==0)throw new TG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=Ce($),Q=await X1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Ce($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function Te($,X="--version"){if(!await g5($))return null;let z=await X1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var jR=16777216,TG;var y8=s(()=>{TG=class TG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return De?"":$}var De,p0,$5,Z1,E61,A1,f1,m5,n;var e7=s(()=>{De=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),Z1=g2("\x1B[1;33m"),E61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),n=g2("\x1B[0m")});import{existsSync as ye}from"fs";async function Z2(){if(BQ!==void 0)return BQ;let $="/opt/homebrew/bin/python3.12";if(ye($))return BQ=$,$;let X=await g5("python3.12");if(X)return BQ=X,X;let Q=await g5("python3");return BQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return X1([Q,"-c",$],X)}var BQ;var m2=s(()=>{y8()});var gR={};B1(gR,{runStatus:()=>Z00});import{existsSync as u5,readFileSync as T9,readdirSync as kR,statSync as SR}from"fs";import{resolve as A5,basename as se}from"path";import{homedir as ne}from"os";function yR($){let X=Math.trunc($);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function bR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Yq/X);if(Z>Yq)Z=Yq;let K=Yq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=Z1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=yR($),Y=yR(X);return` ${f1}${Q}${n} ${J}[${q}]${n} ${z}% (${V} / ${Y})`}async function te(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${n}
3
3
  `),process.stdout.write(`Install with:
4
4
  `),process.stdout.write(` brew install jq (macOS)
5
5
  `),process.stdout.write(` apt install jq (Debian/Ubuntu)
@@ -335,10 +335,13 @@ print(json.dumps(result, indent=2))
335
335
  Start a session with: loki start <prd>`}}let Z=Y00(z);return{exitCode:0,stdout:X?U00(Z,Q):H00(Z,Q)}}async function W00($){let{emitDeprecatedAlias:X}=await Promise.resolve().then(() => (IG(),dR));X("stats","report session",$);let Q=lR($);return console.log(Q.stdout),Q.exitCode}var aR=s(()=>{k1();e7()});var ZI={};B1(ZI,{skillsForJson:()=>eR,runDoctor:()=>E00,renderProviderAvailability:()=>QI,readProviderAvailability:()=>XI,readEffectiveProvider:()=>tR,pythonImportOk:()=>SG,httpReachable:()=>xG,describeCatalogFreshness:()=>fG,checkTool:()=>rR,checkSkills:()=>yG,checkDisk:()=>kG,buildDoctorJson:()=>zI,_setPythonImportOkForTest:()=>j00});import{existsSync as MQ,lstatSync as G00,readFileSync as oR,readlinkSync as B00,statfsSync as N00}from"fs";import{spawnSync as I9}from"child_process";import{homedir as OQ}from"os";import{resolve as e3}from"path";function O00($){let X=$.match(M00);return X?X[1]:null}async function sR($){try{let X=await X1([$,"--version"],{timeoutMs:5000}),Q=(X.stdout||X.stderr||"").trim();return O00(Q)}catch{return null}}function nR($,X){let Q=$.split(".").map((Z)=>parseInt(Z,10)),z=X.split(".").map((Z)=>parseInt(Z,10));while(Q.length<2)Q.push(0);while(z.length<2)z.push(0);for(let Z=0;Z<2;Z++){let K=Q[Z]??0,J=z[Z]??0;if(Number.isNaN(K)||Number.isNaN(J))return 0;if(K!==J)return K-J}return 0}async function rR($,X,Q,z=null){let Z=await g5(X),K=Z!==null,J=K?await sR(X):null,q="pass";if(!K)q=Q==="required"?"fail":"warn";else if(z&&J){if(nR(J,z)<0)q=Q==="required"?"fail":"warn"}return{name:$,command:X,found:K,version:J,required:Q,min_version:z,status:q,path:Z}}function kG(){let $=null;try{let Q=N00(OQ()),z=Number(Q.bavail)*Number(Q.bsize);$=Math.round(z/1073741824*10)/10}catch{$=null}let X="pass";if($!==null){if($<1)X="fail";else if($<5)X="warn"}return{available_gb:$,status:X}}async function xG($,X=2000){try{return(await fetch($,{signal:AbortSignal.timeout(X)})).ok}catch{return!1}}async function SG($,X=!1){let Q=`import ${$}`,z=X?30000:5000;if(!X)return(await I4(Q,{timeoutMs:z})).exitCode===0;let Z=await Z2();if(!Z)return!1;return(await X1([Z,"-c",Q],{timeoutMs:z})).exitCode===0}function j00($){Bq.fn=$??SG}function tR(){let $=bG();if(!$)return null;try{let X=I9("bash",[$,"effective-provider"],{encoding:"utf8"});if(X.status!==0||!X.stdout)return null;let Q=X.stdout.trim();return Q===""?null:Q}catch{return null}}function yG($){let X=$===void 0?tR():$,Q=OQ();return L00.map(({name:z,dir:Z,id:K})=>{let J=e3(Q,Z),q=J,V=e3(J,"SKILL.md");if(MQ(V))return{name:z,path:q,status:"pass",detail:"",dangling:!1};try{if(G00(J).isSymbolicLink()){let U="unknown";try{U=B00(J)}catch{}return{name:z,path:q,status:"fail",detail:`(broken symlink -> ${U})`,dangling:!0}}}catch{}return{name:z,path:q,status:"warn",detail:"(not found - run 'loki setup-skill')",dangling:!1}})}function eR(){return yG().map(($)=>({name:$.name,path:$.path.startsWith(`${OQ()}/`)?`~/${$.path.slice(OQ().length+1)}`:$.path,status:$.status,detail:$.status==="pass"?null:$.status==="fail"?"broken symlink. Fix: loki setup-skill":"not found - run loki setup-skill",required:"required"}))}async function $I(){return Promise.all(A00.map(async($)=>{return{...await rR($.jsonName,$.cmd,$.required,$.min??null),displayName:$.displayName}}))}function bG(){let $=e3(O1,"autonomy/provider-offer.sh");return MQ($)?$:null}function XI(){let $=bG();if(!$)return null;try{let X=I9("bash",[$,"providers-json"],{encoding:"utf8"});if(X.status!==0||!X.stdout)return null;let Q=JSON.parse(X.stdout);if(!Q||!Array.isArray(Q.providers))return null;return Q}catch{return null}}function QI(){let $=bG();if(!$)return"";try{let X=I9("bash",[$,"providers"],{encoding:"utf8"});if(X.status!==0||!X.stdout)return"";return X.stdout}catch{return""}}async function C00(){let X=await g5("sentrux")!==null,Q=X?await sR("sentrux"):null;return{found:X,version:Q,status:X?"pass":"warn",required:"optional"}}async function T00(){let $=(process.env.LOKI_PROOF_GPG_KEY??"").trim()!=="",X=await g5("gpg")!==null,Q=$&&X;return{enabled:Q,key_configured:$,gpg_available:X,status:Q?"pass":"warn",required:"optional"}}async function D00(){let{openSync:$,statSync:X,readSync:Q,closeSync:z,existsSync:Z}=await import("fs"),{join:K}=await import("path"),J=65536,q=process.env.LOKI_DIR??".loki",V=K(q,"memory",".errors.log"),Y=[],U=!1;try{if(Z(V)){U=!0;let H=X(V).size,W=Math.max(0,H-65536),G=H-W,N=Buffer.alloc(G),M=$(V,"r");try{Q(M,N,0,G,W)}finally{z(M)}let j=N.toString("utf-8").split(`
336
336
  `);if(W>0&&j.length>0)j=j.slice(1);j=j.map((B)=>B.trim()).filter((B)=>B.length>0),Y=j.slice(-5)}}catch{Y=[]}return{errors_log_path:U?V:null,recent_errors:Y,recent_error_count:Y.length,status:Y.length===0?"pass":"warn"}}function fG(){let $=process.env.LOKI_MODEL_CATALOG||e3(O1,"providers/model_catalog.json"),X,Q;try{X=JSON.parse(oR($,"utf-8")).updated;let z=Date.parse(`${X}T00:00:00Z`),Z=Date.parse(`${new Date().toISOString().slice(0,10)}T00:00:00Z`);if(Number.isNaN(z))throw Error("bad date");Q=Math.round((Z-z)/86400000)}catch{return{status:"warn",updated:null,age_days:null,detail:'Catalog unreadable or missing an ISO "updated" date -- cannot determine age'}}return Q>F00?{status:"warn",updated:X,age_days:Q,detail:`Last updated ${X} (${Q} days ago) -- may be missing newer models`}:{status:"pass",updated:X,age_days:Q,detail:`Last updated ${X} (${Q} days ago)`}}async function zI(){let X=(await $I()).map(({displayName:W,...G})=>G),Q=kG(),z=await C00(),Z=await T00(),K=await D00(),J=0,q=0,V=0;for(let W of X)if(W.status==="pass")J++;else if(W.status==="fail")q++;else V++;if(Q.status==="pass")J++;else if(Q.status==="fail")q++;else V++;let Y=["claude","codex","cline","aider","opencode"].some((W)=>X.find((G)=>G.command===W)?.found===!0),U={found:Y,status:Y?"pass":"fail",required:"required",detail:Y?null:"No AI provider CLI. Fix: npm install -g @anthropic-ai/claude-code"};if(Y)J++;else q++;let H=eR();for(let W of H)if(W.status==="pass")J++;else if(W.status==="fail")q++;else V++;return{loki_mode_version:L9(),checks:X,provider_availability:XI(),disk:Q,ai_provider:U,skills:H,sentrux:z,receipt_signing:Z,memory:K,model_catalog:fG(),summary:{passed:J,failed:q,warnings:V,ok:q===0}}}function R00(){try{let $=process.env.CLAUDE_CONFIG_DIR||`${OQ()}/.claude`,X=oR(`${$}/.credentials.json`,"utf8");if(!X)return!1;let Q=JSON.parse(X)?.claudeAiOauth?.expiresAt;return typeof Q==="number"&&Q>0&&Q/1000<=Date.now()/1000+60}catch{return!1}}function I00(){try{let X=(I9("claude",["auth","status"],{encoding:"utf8",timeout:5000}).stdout||"").trim();if(!X)return"";let Q=JSON.parse(X);if(Q?.loggedIn===!0)return"yes";if(Q?.loggedIn===!1)return"no";return""}catch{return""}}function a0($){switch($){case"pass":return`${$5}PASS${n}`;case"fail":return`${p0}FAIL${n}`;case"warn":return`${Z1}WARN${n}`}}function Wq($){let X=$.version?` (v${$.version})`:"",Q=$.displayName;if(!$.found){let z=$.required==="required"?"not found":$.required==="recommended"?"not found (recommended)":"not found (optional)";return` ${a0($.status)} ${Q} - ${z}`}if($.min_version&&$.version&&nR($.version,$.min_version)<0){let z=$.required==="required"?"requires":"recommended";return` ${a0($.status)} ${Q}${X} - ${z} >= ${$.min_version}`}return` ${a0($.status)} ${Q}${X}`}function Gq($,X){if(X==="pass")$.pass++;else if(X==="fail")$.fail++;else $.warn++}function w00(){process.stdout.write(`${f1}loki doctor${n} - Check system prerequisites
337
337
 
338
- `),process.stdout.write(`Usage: loki doctor [--json]
338
+ `),process.stdout.write(`Usage: loki doctor [--json] [--airgap]
339
339
 
340
340
  `),process.stdout.write(`Options:
341
341
  `),process.stdout.write(` --json Output machine-readable JSON
342
+ `),process.stdout.write(` --airgap Audit network egress: model inference, telemetry and the
343
+ `),process.stdout.write(` update check, REQUIRED or optional, and how to disable
344
+ `),process.stdout.write(` each. Exits non-zero while a required egress remains.
342
345
 
343
346
  `),process.stdout.write(`Checks: node, python3, jq, git, curl, bash version,
344
347
  `),process.stdout.write(` claude/codex CLIs, and disk space.
@@ -441,7 +444,7 @@ Start a session with: loki start <prd>`}}let Z=Y00(z);return{exitCode:0,stdout:X
441
444
  `),process.stdout.write(`Next: loki quickstart (guided first build from your idea, no PRD needed)
442
445
  `),process.stdout.write(` or loki demo (builds a sample todo app end to end) or loki start ./prd.md
443
446
  `),0}async function E00($){let X=!1;for(let Q of $)if(Q==="--json")X=!0;else if(Q==="--help"||Q==="-h")return w00(),0;else return process.stderr.write(`${p0}Unknown option: ${Q}${n}
444
- `),process.stderr.write(`Usage: loki doctor [--json]
447
+ `),process.stderr.write(`Usage: loki doctor [--json] [--airgap]
445
448
  `),1;if(X){let Q=await zI();return process.stdout.write(JSON.stringify(Q,null,2)+`
446
449
  `),Q.summary.ok?0:1}return P00()}var M00,Bq,L00,A00,F00=90;var KI=s(()=>{k1();y8();m2();e7();Jq();M00=/(\d+\.\d+(?:\.\d+)*)/;Bq={fn:SG};L00=[{name:"Claude Code",dir:".claude/skills/loki-mode",id:"claude"},{name:"Codex CLI",dir:".codex/skills/loki-mode",id:"codex"},{name:"Cline CLI",dir:".cline/skills/loki-mode",id:"cline"},{name:"Aider CLI",dir:".aider/skills/loki-mode",id:"aider"}];A00=[{displayName:"Node.js (>= 18)",jsonName:"Node.js",cmd:"node",required:"required",min:"18.0"},{displayName:"Python 3 (>= 3.8)",jsonName:"Python 3",cmd:"python3",required:"required",min:"3.8"},{displayName:"jq",jsonName:"jq",cmd:"jq",required:"required"},{displayName:"git",jsonName:"git",cmd:"git",required:"required"},{displayName:"curl",jsonName:"curl",cmd:"curl",required:"required"},{displayName:"bash (>= 4.0)",jsonName:"bash",cmd:"bash",required:"recommended",min:"4.0"},{displayName:"Bun (>= 1.3)",jsonName:"Bun",cmd:"bun",required:"recommended",min:"1.3"},{displayName:"Claude CLI",jsonName:"Claude CLI",cmd:"claude",required:"optional"},{displayName:"Codex CLI",jsonName:"Codex CLI",cmd:"codex",required:"optional"},{displayName:"Cline CLI",jsonName:"Cline CLI",cmd:"cline",required:"optional"},{displayName:"Aider CLI",jsonName:"Aider CLI",cmd:"aider",required:"optional"},{displayName:"opencode CLI",jsonName:"opencode CLI",cmd:"opencode",required:"optional"}]});var UI={};B1(UI,{writeBudgetState:()=>mG,readEfficiencyDir:()=>Mq,readBudgetState:()=>g00,parseRetryAfter:()=>c00,isRateLimited:()=>p00,checkBudgetLimitForRunner:()=>i00,checkBudgetLimit:()=>YI,calculateRateLimitBackoff:()=>l00,calculateCostFromRecords:()=>Nq,PRICING:()=>gG});import{existsSync as uG,mkdirSync as vG,readdirSync as x00,readFileSync as dG,renameSync as k00,writeFileSync as hG}from"fs";import{dirname as pG,join as p2,resolve as S00}from"path";import{fileURLToPath as y00}from"url";function b00(){try{let $=pG(y00(import.meta.url)),X=S00($,"..","..","data","model-pricing.json");if(!uG(X))return jQ;let z=JSON.parse(dG(X,"utf8")).pricing;if(!z||typeof z!=="object")return jQ;let Z={};for(let[K,J]of Object.entries(z))if(J!==null&&typeof J==="object"&&typeof J.input==="number"&&typeof J.output==="number"){let q=J;Z[K]={input:q.input,output:q.output,...typeof q.cache_read==="number"?{cache_read:q.cache_read}:{},...typeof q.cache_write==="number"?{cache_write:q.cache_write}:{}}}for(let K of Object.keys(jQ))if(!(K in Z))return jQ;return Z}catch{return jQ}}function _00($){return Math.round(($+Number.EPSILON)*1e4)/1e4}function v00($){let X=($??JI).toLowerCase();return gG[X]??gG[JI]}function Nq($){let X=0;for(let Q of $){if(typeof Q.cost_usd==="number"&&Number.isFinite(Q.cost_usd)){X+=Q.cost_usd;continue}let z=v00(Q.model),Z=typeof Q.input_tokens==="number"?Q.input_tokens:0,K=typeof Q.output_tokens==="number"?Q.output_tokens:0,J=typeof Q.cache_read_tokens==="number"?Q.cache_read_tokens:0,q=typeof Q.cache_creation_tokens==="number"?Q.cache_creation_tokens:0,V=z.cache_read??z.input,Y=z.cache_write??z.input;X+=Z/1e6*z.input+K/1e6*z.output+J/1e6*V+q/1e6*Y}return _00(X)}function Mq($){if(!uG($))return[];let X=[],Q;try{Q=x00($)}catch{return[]}for(let z of Q){if(!z.endsWith(".json"))continue;let Z=p2($,z);try{let K=dG(Z,"utf8"),J=JSON.parse(K);if(J&&typeof J==="object")X.push(J)}catch{}}return X}function h00($){if($===null||typeof $!=="object"||Array.isArray($))return!1;let X=$;if(typeof X.limit!=="number"||!Number.isFinite(X.limit))return!1;if(typeof X.budget_limit!=="number"||!Number.isFinite(X.budget_limit))return!1;if(typeof X.budget_used!=="number"||!Number.isFinite(X.budget_used))return!1;if(typeof X.exceeded!=="boolean")return!1;if(X.exceeded_at!==void 0&&typeof X.exceeded_at!=="string")return!1;if(X.created_at!==void 0&&typeof X.created_at!=="string")return!1;return!0}function g00($){let X=$??p2(h0(),"metrics","budget.json");if(!uG(X))return null;try{let Q=dG(X,"utf8"),z=JSON.parse(Q);if(!h00(z))return console.warn(`[budget] discarding malformed budget state at ${X}`),null;return z}catch{return null}}function mG($,X){let Q=X??p2(h0(),"metrics","budget.json");vG(pG(Q),{recursive:!0});let z=[];if(z.push("{"),z.push(` "limit": ${_G($.limit)},`),z.push(` "budget_limit": ${_G($.budget_limit)},`),z.push(` "budget_used": ${_G($.budget_used)},`),$.exceeded){z.push(' "exceeded": true,');let J=$.exceeded_at??VI();z.push(` "exceeded_at": "${J}"`)}else z.push(' "exceeded": false');z.push("}");let Z=z.join(`
447
450
  `)+`
@@ -1345,4 +1348,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1345
1348
  `),2}case"start":{let{runStart:z}=await Promise.resolve().then(() => (gt(),ht));return z(Q)}default:return process.stderr.write(`Unknown command: ${X}
1346
1349
  `),process.stderr.write(mt),2}}ER();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var H61=await U61(Bun.argv.slice(2));process.exit(H61);
1347
1350
 
1348
- //# debugId=CF1319BA2D6A59CC478BB2F881D9252C
1351
+ //# debugId=581D4F54CF5D0BFAC2AEC3492BD2352A
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '9.54.2'
78
+ __version__ = '9.55.0'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "loki-mode",
3
3
  "mcpName": "io.github.asklokesh/loki-mode",
4
- "version": "9.54.2",
4
+ "version": "9.55.0",
5
5
  "description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider, opencode).",
6
6
  "keywords": [
7
7
  "agent",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "loki-mode",
4
4
  "displayName": "Loki Mode",
5
- "version": "9.54.2",
5
+ "version": "9.55.0",
6
6
  "description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
7
7
  "author": {
8
8
  "name": "Autonomi",
@@ -197,3 +197,13 @@ auto_detect_provider() {
197
197
  echo ""
198
198
  return 1
199
199
  }
200
+
201
+ # loki_claude_sidecall_allowed [provider] -- 0 when an engine side-call (PRD
202
+ # enrichment, done recognition, council voters, USAGE.md refresh, quickstart
203
+ # intent) may prompt the claude CLI. Only the provider the operator chose sees
204
+ # prompts (V10 P5): allowed when that provider is claude, or on the explicit
205
+ # opt-in LOKI_ALLOW_CLAUDE_SIDECALLS=1 (exact value), which
206
+ # `loki doctor --airgap` reports as required egress to Anthropic.
207
+ loki_claude_sidecall_allowed() {
208
+ [ "${1:-${LOKI_PROVIDER:-claude}}" = "claude" ] || [ "${LOKI_ALLOW_CLAUDE_SIDECALLS:-}" = "1" ]
209
+ }
@@ -197,7 +197,7 @@ def attest(proof_path, repo_dir="."):
197
197
  axes["drift"] = _tri(
198
198
  result.get("diff_drift") is False if result.get("diff_drift") is not None
199
199
  else None,
200
- _pick(reasons, "drift unverifiable") or
200
+ _pick(reasons, "could not check drift", "drift unverifiable") or
201
201
  "the recorded diff could not be re-derived here",
202
202
  )
203
203
  if result.get("diff_drift") is True: