loki-mode 11.45.0 → 11.46.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.
Files changed (69) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/efficiency_cost.py +5 -3
  4. package/dashboard/__init__.py +1 -1
  5. package/docs/CLI-REFERENCE.md +1 -1
  6. package/docs/INSTALLATION.md +1 -1
  7. package/loki-ts/dist/loki.js +2 -2
  8. package/mcp/__init__.py +1 -1
  9. package/memory/embeddings.py +61 -7
  10. package/memory/vector_index.py +58 -12
  11. package/package.json +2 -2
  12. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  13. package/web-app/dist/assets/{AdminPage-HU_6BQN1.js → AdminPage-Cpk2EHRn.js} +1 -1
  14. package/web-app/dist/assets/{Avatar-CCwnon5O.js → Avatar-xfdA6HHw.js} +1 -1
  15. package/web-app/dist/assets/{Badge-n4PTb9F1.js → Badge-BxcMTvOK.js} +1 -1
  16. package/web-app/dist/assets/{Button-DMQxN8bv.js → Button-BDJQTeBq.js} +1 -1
  17. package/web-app/dist/assets/{CockpitPage-zxSAsSBG.js → CockpitPage-BRPsFxNM.js} +1 -1
  18. package/web-app/dist/assets/{ComparePage-FqhPVr_T.js → ComparePage-es4wlPFZ.js} +1 -1
  19. package/web-app/dist/assets/{ErrorBoundary-CincZs7i.js → ErrorBoundary-BGAlBzcB.js} +1 -1
  20. package/web-app/dist/assets/{EvidenceReceiptPanel-5XeluQmM.js → EvidenceReceiptPanel-CulRYQ28.js} +1 -1
  21. package/web-app/dist/assets/{GitHubIssuesPanel-BILRF_-r.js → GitHubIssuesPanel-DmDiKTZI.js} +1 -1
  22. package/web-app/dist/assets/{GitHubPRsPanel-BShR3YLY.js → GitHubPRsPanel-pibUi3FE.js} +1 -1
  23. package/web-app/dist/assets/{HomePage-CSo-i0zn.js → HomePage-BTgWVuvl.js} +2 -2
  24. package/web-app/dist/assets/{LoginPage-BTv_1cpe.js → LoginPage-CLVQjlQ-.js} +1 -1
  25. package/web-app/dist/assets/{MagicPage-DtMNDiPq.js → MagicPage-CuPYa26V.js} +1 -1
  26. package/web-app/dist/assets/{MetricsPage-CW4H8lHA.js → MetricsPage-BI01kRI4.js} +1 -1
  27. package/web-app/dist/assets/{NotFoundPage-BC5MUmut.js → NotFoundPage-BwS_zmkd.js} +1 -1
  28. package/web-app/dist/assets/{ProjectPage-Bm5Ih9uz.js → ProjectPage-pGURtMmr.js} +3 -3
  29. package/web-app/dist/assets/{ProjectsPage-DQ1NH_3h.js → ProjectsPage-Bjb4ecz5.js} +1 -1
  30. package/web-app/dist/assets/{SettingsPage-B-6P8H__.js → SettingsPage-DgkXJY8G.js} +1 -1
  31. package/web-app/dist/assets/{ShowcasePage-D3SVuFH5.js → ShowcasePage-W1kn0K8X.js} +1 -1
  32. package/web-app/dist/assets/{SystemSettingsPage-CiZeEhuG.js → SystemSettingsPage-CVtV225z.js} +1 -1
  33. package/web-app/dist/assets/{TeamsPage-BNUXKycY.js → TeamsPage-d_Yr3NNM.js} +1 -1
  34. package/web-app/dist/assets/{TemplatesPage-BT5Vrytp.js → TemplatesPage--P3OcRWw.js} +1 -1
  35. package/web-app/dist/assets/{TerminalOutput-DBK6ddQF.js → TerminalOutput-BzanuL3R.js} +1 -1
  36. package/web-app/dist/assets/{activity-BvnwlILO.js → activity-onNTo3z1.js} +1 -1
  37. package/web-app/dist/assets/{bell-_iaOZTKy.js → bell-DjZb8wxS.js} +1 -1
  38. package/web-app/dist/assets/{bot-D1fnwXba.js → bot-DC-zqs6V.js} +1 -1
  39. package/web-app/dist/assets/{check-Bo8ndDNV.js → check-_TVDwdrq.js} +1 -1
  40. package/web-app/dist/assets/{chevron-left-BndWdJHg.js → chevron-left-D8M7-5Kc.js} +1 -1
  41. package/web-app/dist/assets/{circle-alert-BYyuAk0R.js → circle-alert-HUO4WzPf.js} +1 -1
  42. package/web-app/dist/assets/{clock-L4Yo-1oq.js → clock-W2kDTGYx.js} +1 -1
  43. package/web-app/dist/assets/{cloud-C1gVTFQ2.js → cloud-CswIXQ44.js} +1 -1
  44. package/web-app/dist/assets/{code-xml-Y-TJa72C.js → code-xml-D2IospVb.js} +1 -1
  45. package/web-app/dist/assets/{database-Blj4jI9G.js → database-Dg9kxuUW.js} +1 -1
  46. package/web-app/dist/assets/{dollar-sign-DfcAvtPn.js → dollar-sign-Be49Rzh5.js} +1 -1
  47. package/web-app/dist/assets/{file-code-corner-4VQHPIIq.js → file-code-corner-BWmgmYUT.js} +1 -1
  48. package/web-app/dist/assets/{file-plus-CZyfeB4q.js → file-plus-Dv3GsHfI.js} +1 -1
  49. package/web-app/dist/assets/{globe-nkvqONVn.js → globe-mb1Su4WH.js} +1 -1
  50. package/web-app/dist/assets/{hammer-Bc0rcWkS.js → hammer-D1MoKYJh.js} +1 -1
  51. package/web-app/dist/assets/{index-CvDorHM5.js → index-CjeJkKHf.js} +3 -3
  52. package/web-app/dist/assets/{layers-C5GdsHj0.js → layers-Dw0b7-9N.js} +1 -1
  53. package/web-app/dist/assets/{loader-circle-DNgNGU-W.js → loader-circle-DZFGHRZb.js} +1 -1
  54. package/web-app/dist/assets/{lock-CzfZEv4E.js → lock-DyfnnkPv.js} +1 -1
  55. package/web-app/dist/assets/{package-CaPvBStN.js → package-CFhqaw7T.js} +1 -1
  56. package/web-app/dist/assets/{plus-BHEJacTT.js → plus-BGJd8wWM.js} +1 -1
  57. package/web-app/dist/assets/{refresh-cw-DmIYhngY.js → refresh-cw-B-eHLtrF.js} +1 -1
  58. package/web-app/dist/assets/{rotate-ccw-BRFLSzAw.js → rotate-ccw-CZDw8m--.js} +1 -1
  59. package/web-app/dist/assets/{scroll-text-tcxJua91.js → scroll-text-BfFjI8cR.js} +1 -1
  60. package/web-app/dist/assets/{server-BLdwOa83.js → server-C21JkB7U.js} +1 -1
  61. package/web-app/dist/assets/{shield-alert-B6-i4Ci9.js → shield-alert-Bojfzk9R.js} +1 -1
  62. package/web-app/dist/assets/{thumbs-up-CtzTHYX3.js → thumbs-up-BsB7OQxT.js} +1 -1
  63. package/web-app/dist/assets/{trash-2-ssQolTpk.js → trash-2-CvOAbh9v.js} +1 -1
  64. package/web-app/dist/assets/{trending-down-CW9F6Oy3.js → trending-down-_lZH3kDf.js} +1 -1
  65. package/web-app/dist/assets/{trending-up-DPnHpzG_.js → trending-up-Bqln1HBv.js} +1 -1
  66. package/web-app/dist/assets/{upload-BY5CKgt-.js → upload-CNpkqzkA.js} +1 -1
  67. package/web-app/dist/assets/{usePolling-iaevcvjA.js → usePolling-D3dvIVbM.js} +1 -1
  68. package/web-app/dist/assets/{user-9uylwqzF.js → user-BhFi92qW.js} +1 -1
  69. package/web-app/dist/index.html +1 -1
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Runs an autonomous software factory that knows what it is supposed to deliver and proves it did, with a signed receipt. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
4
4
  ---
5
5
 
6
- # Loki Mode v11.45.0
6
+ # Loki Mode v11.46.0
7
7
 
8
8
  **Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
9
9
 
@@ -476,4 +476,4 @@ Implemented, planned and deprecated items, with the version each landed in: `ski
476
476
 
477
477
  ---
478
478
 
479
- **v11.45.0 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
479
+ **v11.46.0 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 11.45.0
1
+ 11.46.0
@@ -393,8 +393,8 @@ def price_from_tokens(model, input_tokens, output_tokens,
393
393
  cache_read_tokens=0, prices=None, prices_path=None):
394
394
  """Compute a USD cost from raw token counts and the shared price table.
395
395
 
396
- Returns a float (rounded to 6 decimals) when the model is present in the
397
- price table, else None. None means "cannot price" (honest), never 0.0.
396
+ Returns a float (rounded to 6 decimals, times inference_geo_multiplier())
397
+ when the model is present in the price table, else None. None means "cannot price" (honest), never 0.0.
398
398
 
399
399
  Pricing is per million tokens (mtok). cache_read is priced separately from
400
400
  fresh input tokens. Used for tools that expose token counts but not a
@@ -420,4 +420,6 @@ def price_from_tokens(model, input_tokens, output_tokens,
420
420
  + (ot / 1_000_000.0) * out_rate
421
421
  + (ct / 1_000_000.0) * cache_rate
422
422
  )
423
- return round(usd, 6)
423
+ # LOKI_INFERENCE_GEO: same multiplier as the budget gate and dashboard. Only
424
+ # token-estimated cost reaches here; a provider-reported cost_usd never does.
425
+ return round(usd * inference_geo_multiplier(), 6)
@@ -7,6 +7,6 @@ Modules:
7
7
  control: Session control helpers (status, atomic writes, origin check)
8
8
  """
9
9
 
10
- __version__ = "11.45.0"
10
+ __version__ = "11.46.0"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -1,6 +1,6 @@
1
1
  # CLI reference
2
2
 
3
- Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.45.0. Do not edit by hand.
3
+ Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.46.0. Do not edit by hand.
4
4
  Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for the full inventory.
5
5
 
6
6
  | Command | Description |
@@ -2,7 +2,7 @@
2
2
 
3
3
  Loki Mode, by [Autonomi](https://www.autonomi.dev/), turns a spec or an issue into a verified pull request with a receipt you can check. This page covers installing it, a first run, and the options around it.
4
4
 
5
- **Version:** v11.45.0
5
+ **Version:** v11.46.0
6
6
 
7
7
  **Engine note:** `loki start` is the primary command. `loki start`, `loki "<task>"`, `loki owner/repo#N` and `loki quick` all run the Loki 10 engine, whatever the flag order or input form (PRD file, issue ref, task or one-word brief). The previous loop runs only on `--engine legacy`, `LOKI_ENGINE=legacy`, or for one call when a flag Loki 10 cannot honor is present. See the [user guide](v10/GUIDE.md) and the [5-minute quickstart](QUICKSTART.md).
8
8
 
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var _b0=Object.create;var{getPrototypeOf:kb0,defineProperty:CK,getOwnPropertyNames:zo,getOwnPropertyDescriptor:xb0}=Object,Wo=Object.prototype.hasOwnProperty;function Ko(Z){return this[Z]}var Sb0,yb0,bb0=(Z,$,J)=>{var X=Z!=null&&typeof Z==="object";if(X){var Y=$?Sb0??=new WeakMap:yb0??=new WeakMap,Q=Y.get(Z);if(Q)return Q}J=Z!=null?_b0(kb0(Z)):{};let z=$||!Z||!Z.__esModule?CK(J,"default",{value:Z,enumerable:!0}):J;for(let W of zo(Z))if(!Wo.call(z,W))CK(z,W,{get:Ko.bind(Z,W),enumerable:!0});if(X)Y.set(Z,z);return z},vb0=(Z)=>{var $=(Qo??=new WeakMap).get(Z),J;if($)return $;if($=CK({},"__esModule",{value:!0}),Z&&typeof Z==="object"||typeof Z==="function"){for(var X of zo(Z))if(!Wo.call($,X))CK($,X,{get:Ko.bind(Z,X),enumerable:!(J=xb0(Z,X))||J.enumerable})}return Qo.set(Z,$),$},Qo,lN=(Z,$)=>()=>($||Z(($={exports:{}}).exports,$),$.exports);var hb0=(Z)=>Z;function fb0(Z,$){this[Z]=hb0.bind(null,$)}var A0=(Z,$)=>{for(var J in $)CK(Z,J,{get:$[J],enumerable:!0,configurable:!0,set:fb0.bind($,J)})};var S=(Z,$)=>()=>(Z&&($=Z(Z=0)),$);var s9=import.meta.require;var Go={};A0(Go,{lokiDir:()=>P0,homeLokiDir:()=>B2,findRepoRootForVersion:()=>FK,REPO_ROOT:()=>C0});import{resolve as CZ,dirname as $E}from"path";import{fileURLToPath as gb0}from"url";import{existsSync as aN}from"fs";import{homedir as mb0}from"os";function ub0(){let Z=Vo;for(let $=0;$<6;$++){if(aN(CZ(Z,"VERSION"))&&aN(CZ(Z,"autonomy/run.sh")))return Z;let J=$E(Z);if(J===Z)break;Z=J}return CZ(Vo,"..","..","..")}function FK(Z){let $=Z;for(let J=0;J<6;J++){if(aN(CZ($,"VERSION"))&&aN(CZ($,"autonomy/run.sh")))return $;let X=$E($);if(X===$)break;$=X}return CZ(Z,"..","..","..")}function P0(){return process.env.LOKI_DIR??CZ(process.cwd(),".loki")}function B2(){return CZ(mb0(),".loki")}var Vo,C0;var n0=S(()=>{Vo=$E(gb0(import.meta.url));C0=ub0()});import{readFileSync as pb0}from"fs";import{resolve as cb0,dirname as db0}from"path";import{fileURLToPath as lb0}from"url";function q6(){if(w1!==null)return w1;let Z="11.45.0";if(typeof Z==="string"&&Z.length>0)return w1=Z,w1;try{let $=db0(lb0(import.meta.url)),J=FK($);w1=pb0(cb0(J,"VERSION"),"utf-8").trim()}catch{w1="unknown"}return w1}var w1=null;var FZ=S(()=>{n0()});import{mkdirSync as XE,readFileSync as iN,realpathSync as ab0,statSync as ib0,writeFileSync as YE}from"fs";import{dirname as YY,join as JE,resolve as Ho}from"path";function tb0(){return Ho(B2(),"cache","update-check.json")}function IZ(Z){let $=/^(\d+)\.(\d+)\.(\d+)$/.exec(Z.trim());if(!$)return null;return[Number($[1]),Number($[2]),Number($[3])]}function IK(Z,$){let J=IZ(Z),X=IZ($);if(!J||!X)return!1;let[Y,Q,z]=J,[W,K,V]=X;if(Y!==W)return Y>W;if(Q!==K)return Q>K;return z>V}function qo(){let Z=(process.env.LOKI_NO_UPDATE_CHECK??"").trim().toLowerCase();if(Z==="1"||Z==="true"||Z==="yes")return!0;if(process.env.CI)return!0;if(!process.stdout.isTTY)return!0;return!1}function Uo(Z){try{let $=iN(Z,"utf-8"),J=JSON.parse($);if(typeof J.checkedAt==="number"&&typeof J.latest==="string"&&IZ(J.latest)!==null)return{checkedAt:J.checkedAt,latest:J.latest}}catch{}return null}function eb0(Z){try{let $=JSON.parse(iN(Z,"utf-8")).failedAt;if(typeof $==="number")return $}catch{}return null}function Zv0(Z,$){let J=Uo(Z);try{XE(YY(Z),{recursive:!0}),YE(Z,JSON.stringify({...J??{},failedAt:$}),"utf-8")}catch{}}function $v0(Z,$){try{XE(YY(Z),{recursive:!0}),YE(Z,JSON.stringify($),"utf-8")}catch{}}async function Jv0(){try{let Z=await fetch(ob0,{signal:AbortSignal.timeout(sb0),headers:{accept:"application/json"}});if(!Z.ok)return null;let $=await Z.json();if(typeof $.version==="string"&&IZ($.version)!==null)return $.version}catch{}return null}async function Xv0(Z=Date.now(),$=Jv0,J=tb0()){let X=Uo(J);if(X&&Z-X.checkedAt<nb0)return X.latest;let Y=eb0(J);if(Y!==null&&Z-Y>=0&&Z-Y<rb0)return null;let Q=await $();if(Q===null)return Zv0(J,Z),null;return $v0(J,{checkedAt:Z,latest:Q}),Q}function Yv0(Z,$=process.env){if(IZ(Z)===null)return null;let X=$.PATH;if(!X)return null;let Y=new Set;for(let Q of X.split(":")){if(!Q)continue;let z=JE(Q,"loki"),W;try{ib0(z),W=ab0(z)}catch{continue}if(Y.has(W))continue;Y.add(W);try{let K=JE(YY(YY(W)),"package.json"),V=JSON.parse(iN(K,"utf8")).version;if(typeof V!=="string")continue;if(IK(V,Z))return{path:z,version:V}}catch{continue}}return null}function Qv0(){return Ho(B2(),"cache","last-seen-version.json")}function zv0(Z,$={}){try{if(qo())return!1;if(IZ(Z)===null)return!1;let J=$.seenFile??Qv0(),X=null;try{let Q=JSON.parse(iN(J,"utf-8")).version;if(typeof Q==="string"&&IZ(Q)!==null)X=Q}catch{}if(X!==null&&!IK(Z,X))return!1;try{XE(YY(J),{recursive:!0}),YE(J,JSON.stringify({version:Z}),"utf-8")}catch{return!1}if(X===null)return!1;return($.write??((Q)=>process.stderr.write(Q)))(`Loki Mode upgraded ${X} -> ${Z}. What is new: https://github.com/asklokesh/loki-mode/releases/tag/v${Z}
2
+ var _b0=Object.create;var{getPrototypeOf:kb0,defineProperty:CK,getOwnPropertyNames:zo,getOwnPropertyDescriptor:xb0}=Object,Wo=Object.prototype.hasOwnProperty;function Ko(Z){return this[Z]}var Sb0,yb0,bb0=(Z,$,J)=>{var X=Z!=null&&typeof Z==="object";if(X){var Y=$?Sb0??=new WeakMap:yb0??=new WeakMap,Q=Y.get(Z);if(Q)return Q}J=Z!=null?_b0(kb0(Z)):{};let z=$||!Z||!Z.__esModule?CK(J,"default",{value:Z,enumerable:!0}):J;for(let W of zo(Z))if(!Wo.call(z,W))CK(z,W,{get:Ko.bind(Z,W),enumerable:!0});if(X)Y.set(Z,z);return z},vb0=(Z)=>{var $=(Qo??=new WeakMap).get(Z),J;if($)return $;if($=CK({},"__esModule",{value:!0}),Z&&typeof Z==="object"||typeof Z==="function"){for(var X of zo(Z))if(!Wo.call($,X))CK($,X,{get:Ko.bind(Z,X),enumerable:!(J=xb0(Z,X))||J.enumerable})}return Qo.set(Z,$),$},Qo,lN=(Z,$)=>()=>($||Z(($={exports:{}}).exports,$),$.exports);var hb0=(Z)=>Z;function fb0(Z,$){this[Z]=hb0.bind(null,$)}var A0=(Z,$)=>{for(var J in $)CK(Z,J,{get:$[J],enumerable:!0,configurable:!0,set:fb0.bind($,J)})};var S=(Z,$)=>()=>(Z&&($=Z(Z=0)),$);var s9=import.meta.require;var Go={};A0(Go,{lokiDir:()=>P0,homeLokiDir:()=>B2,findRepoRootForVersion:()=>FK,REPO_ROOT:()=>C0});import{resolve as CZ,dirname as $E}from"path";import{fileURLToPath as gb0}from"url";import{existsSync as aN}from"fs";import{homedir as mb0}from"os";function ub0(){let Z=Vo;for(let $=0;$<6;$++){if(aN(CZ(Z,"VERSION"))&&aN(CZ(Z,"autonomy/run.sh")))return Z;let J=$E(Z);if(J===Z)break;Z=J}return CZ(Vo,"..","..","..")}function FK(Z){let $=Z;for(let J=0;J<6;J++){if(aN(CZ($,"VERSION"))&&aN(CZ($,"autonomy/run.sh")))return $;let X=$E($);if(X===$)break;$=X}return CZ(Z,"..","..","..")}function P0(){return process.env.LOKI_DIR??CZ(process.cwd(),".loki")}function B2(){return CZ(mb0(),".loki")}var Vo,C0;var n0=S(()=>{Vo=$E(gb0(import.meta.url));C0=ub0()});import{readFileSync as pb0}from"fs";import{resolve as cb0,dirname as db0}from"path";import{fileURLToPath as lb0}from"url";function q6(){if(w1!==null)return w1;let Z="11.46.0";if(typeof Z==="string"&&Z.length>0)return w1=Z,w1;try{let $=db0(lb0(import.meta.url)),J=FK($);w1=pb0(cb0(J,"VERSION"),"utf-8").trim()}catch{w1="unknown"}return w1}var w1=null;var FZ=S(()=>{n0()});import{mkdirSync as XE,readFileSync as iN,realpathSync as ab0,statSync as ib0,writeFileSync as YE}from"fs";import{dirname as YY,join as JE,resolve as Ho}from"path";function tb0(){return Ho(B2(),"cache","update-check.json")}function IZ(Z){let $=/^(\d+)\.(\d+)\.(\d+)$/.exec(Z.trim());if(!$)return null;return[Number($[1]),Number($[2]),Number($[3])]}function IK(Z,$){let J=IZ(Z),X=IZ($);if(!J||!X)return!1;let[Y,Q,z]=J,[W,K,V]=X;if(Y!==W)return Y>W;if(Q!==K)return Q>K;return z>V}function qo(){let Z=(process.env.LOKI_NO_UPDATE_CHECK??"").trim().toLowerCase();if(Z==="1"||Z==="true"||Z==="yes")return!0;if(process.env.CI)return!0;if(!process.stdout.isTTY)return!0;return!1}function Uo(Z){try{let $=iN(Z,"utf-8"),J=JSON.parse($);if(typeof J.checkedAt==="number"&&typeof J.latest==="string"&&IZ(J.latest)!==null)return{checkedAt:J.checkedAt,latest:J.latest}}catch{}return null}function eb0(Z){try{let $=JSON.parse(iN(Z,"utf-8")).failedAt;if(typeof $==="number")return $}catch{}return null}function Zv0(Z,$){let J=Uo(Z);try{XE(YY(Z),{recursive:!0}),YE(Z,JSON.stringify({...J??{},failedAt:$}),"utf-8")}catch{}}function $v0(Z,$){try{XE(YY(Z),{recursive:!0}),YE(Z,JSON.stringify($),"utf-8")}catch{}}async function Jv0(){try{let Z=await fetch(ob0,{signal:AbortSignal.timeout(sb0),headers:{accept:"application/json"}});if(!Z.ok)return null;let $=await Z.json();if(typeof $.version==="string"&&IZ($.version)!==null)return $.version}catch{}return null}async function Xv0(Z=Date.now(),$=Jv0,J=tb0()){let X=Uo(J);if(X&&Z-X.checkedAt<nb0)return X.latest;let Y=eb0(J);if(Y!==null&&Z-Y>=0&&Z-Y<rb0)return null;let Q=await $();if(Q===null)return Zv0(J,Z),null;return $v0(J,{checkedAt:Z,latest:Q}),Q}function Yv0(Z,$=process.env){if(IZ(Z)===null)return null;let X=$.PATH;if(!X)return null;let Y=new Set;for(let Q of X.split(":")){if(!Q)continue;let z=JE(Q,"loki"),W;try{ib0(z),W=ab0(z)}catch{continue}if(Y.has(W))continue;Y.add(W);try{let K=JE(YY(YY(W)),"package.json"),V=JSON.parse(iN(K,"utf8")).version;if(typeof V!=="string")continue;if(IK(V,Z))return{path:z,version:V}}catch{continue}}return null}function Qv0(){return Ho(B2(),"cache","last-seen-version.json")}function zv0(Z,$={}){try{if(qo())return!1;if(IZ(Z)===null)return!1;let J=$.seenFile??Qv0(),X=null;try{let Q=JSON.parse(iN(J,"utf-8")).version;if(typeof Q==="string"&&IZ(Q)!==null)X=Q}catch{}if(X!==null&&!IK(Z,X))return!1;try{XE(YY(J),{recursive:!0}),YE(J,JSON.stringify({version:Z}),"utf-8")}catch{return!1}if(X===null)return!1;return($.write??((Q)=>process.stderr.write(Q)))(`Loki Mode upgraded ${X} -> ${Z}. What is new: https://github.com/asklokesh/loki-mode/releases/tag/v${Z}
3
3
  `),!0}catch{return!1}}function Wv0(Z){let $=Z.replace(/\\/g,"/");if($.includes("/Cellar/"))return"brew upgrade loki-mode";if(/\/pnpm\//.test($)||$.includes("/.pnpm/"))return"pnpm add -g loki-mode (or npm i -g loki-mode)";if($.includes("/yarn/global/")||$.includes("/.yarn/"))return"yarn global add loki-mode (or npm i -g loki-mode)";if($.includes("/.bun/"))return"bun install -g loki-mode (or npm i -g loki-mode)";if($.includes("/lib/node_modules/"))return"npm i -g loki-mode (or bun install -g loki-mode)";if($.includes("/homebrew/"))return"brew upgrade loki-mode";return"bun install -g loki-mode (or npm i -g loki-mode)"}async function Bo(Z,$={}){try{if(qo())return;if(IZ(Z)===null)return;let J=$.write??((Q)=>process.stderr.write(Q));zv0(Z,{write:J,...$.cacheFile?{seenFile:JE(YY($.cacheFile),"last-seen-version.json")}:{}});let X=Yv0(Z,$.env??process.env);if(X!==null){J(`Loki Mode ${X.version} is installed but not the one running (you are running ${Z}).
4
4
  Another loki earlier on PATH is winning. Newer copy: ${X.path}
5
5
  Run \`which -a loki\` to see the order, then remove or re-point the earlier entry.
@@ -3263,4 +3263,4 @@ Unknown commands exit 2 with a suggestion; run 'loki help' for the full list.
3263
3263
  `);{let{suggestCommand:X}=await Promise.resolve().then(() => (Z4(),no)),Y=$===void 0?void 0:X($);if(Y)process.stderr.write(`Did you mean 'loki ${Y}'?
3264
3264
  `)}return process.stderr.write(Ib0),2}}CF0();var Tb0=()=>globalThis[Symbol.for("loki.ownsStopSignals")]===!0;process.on("SIGINT",()=>{if(!Tb0())process.exit(130)});process.on("SIGTERM",()=>{if(!Tb0())process.exit(143)});var RU9=await PU9(Bun.argv.slice(2));process.exit(RU9);
3265
3265
 
3266
- //# debugId=FBB0F5AECCA41820CF9A5698FDD58BDA
3266
+ //# debugId=B9C6D431B8F4B35F0F13D1D88F070B41
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '11.45.0'
78
+ __version__ = '11.46.0'
@@ -86,6 +86,11 @@ class EmbeddingConfig:
86
86
 
87
87
  # Dimension settings
88
88
  dimension: int = 384 # Default for MiniLM-L6-v2
89
+ # Matryoshka-style truncation: keep the leading N components, then
90
+ # re-normalize. None keeps the provider's native dimension.
91
+ output_dimension: Optional[int] = None
92
+ # Storage dtype for a VectorIndex built from this config.
93
+ dtype: str = "float32"
89
94
 
90
95
  # API keys (loaded from env if not provided)
91
96
  openai_api_key: Optional[str] = None
@@ -175,6 +180,13 @@ class EmbeddingConfig:
175
180
  if self.model in self.MODEL_DIMENSIONS:
176
181
  self.dimension = self.MODEL_DIMENSIONS[self.model]
177
182
 
183
+ if self.output_dimension is not None and self.output_dimension < 1:
184
+ raise ValueError(
185
+ f"output_dimension must be a positive integer, got {self.output_dimension!r}"
186
+ )
187
+ if self.dtype not in ("float32", "float16"):
188
+ raise ValueError(f"dtype must be float32 or float16, got {self.dtype!r}")
189
+
178
190
  @classmethod
179
191
  def from_env(cls) -> "EmbeddingConfig":
180
192
  """Create config from environment variables."""
@@ -184,6 +196,9 @@ class EmbeddingConfig:
184
196
  openai_api_key=os.environ.get("OPENAI_API_KEY"),
185
197
  cohere_api_key=os.environ.get("COHERE_API_KEY"),
186
198
  voyage_api_key=os.environ.get("VOYAGE_API_KEY"),
199
+ output_dimension=int(os.environ["LOKI_EMBEDDING_OUTPUT_DIMENSION"])
200
+ if os.environ.get("LOKI_EMBEDDING_OUTPUT_DIMENSION") else None,
201
+ dtype=os.environ.get("LOKI_EMBEDDING_DTYPE", "float32"),
187
202
  chunking_strategy=os.environ.get("LOKI_EMBEDDING_CHUNKING", "semantic"),
188
203
  include_context=os.environ.get("LOKI_EMBEDDING_CONTEXT", "true").lower() == "true",
189
204
  )
@@ -203,6 +218,8 @@ class EmbeddingConfig:
203
218
  model=data.get("model"),
204
219
  fallback_providers=data.get("fallback_providers", ["local"]),
205
220
  dimension=data.get("dimension", 384),
221
+ output_dimension=data.get("output_dimension"),
222
+ dtype=data.get("dtype", "float32"),
206
223
  chunking_strategy=data.get("chunking_strategy", "semantic"),
207
224
  max_chunk_size=data.get("max_chunk_size", 512),
208
225
  chunk_overlap=data.get("chunk_overlap", 50),
@@ -222,6 +239,8 @@ class EmbeddingConfig:
222
239
  "model": self.model,
223
240
  "fallback_providers": self.fallback_providers,
224
241
  "dimension": self.dimension,
242
+ "output_dimension": self.output_dimension,
243
+ "dtype": self.dtype,
225
244
  "chunking_strategy": self.chunking_strategy,
226
245
  "max_chunk_size": self.max_chunk_size,
227
246
  "chunk_overlap": self.chunk_overlap,
@@ -469,6 +488,10 @@ def _check_input_type(input_type: str) -> str:
469
488
  class BaseEmbeddingProvider(ABC):
470
489
  """Base class for embedding providers."""
471
490
 
491
+ # True when the provider returns unit-length vectors, so a VectorIndex can
492
+ # rank by dot product instead of cosine. Subclasses opt in explicitly.
493
+ normalized: bool = False
494
+
472
495
  @abstractmethod
473
496
  def embed(self, text: str, input_type: str = "document") -> np.ndarray:
474
497
  """Generate embedding for a single text.
@@ -507,6 +530,8 @@ class LocalEmbeddingProvider(BaseEmbeddingProvider):
507
530
  Falls back to TF-IDF when sentence-transformers is not available.
508
531
  """
509
532
 
533
+ normalized = True
534
+
510
535
  def __init__(self, model_name: str = "all-MiniLM-L6-v2", dimension: int = 384):
511
536
  self.model_name = model_name
512
537
  self.dimension = dimension
@@ -647,6 +672,8 @@ class LocalEmbeddingProvider(BaseEmbeddingProvider):
647
672
  class OpenAIEmbeddingProvider(BaseEmbeddingProvider):
648
673
  """OpenAI embedding provider using text-embedding-3 models."""
649
674
 
675
+ normalized = True
676
+
650
677
  def __init__(
651
678
  self,
652
679
  api_key: Optional[str] = None,
@@ -810,6 +837,8 @@ class VoyageEmbeddingProvider(BaseEmbeddingProvider):
810
837
  never selects it.
811
838
  """
812
839
 
840
+ normalized = True
841
+
813
842
  def __init__(
814
843
  self,
815
844
  api_key: Optional[str] = None,
@@ -1004,7 +1033,7 @@ class EmbeddingEngine:
1004
1033
  if provider.is_available():
1005
1034
  self._primary_provider = provider
1006
1035
  self._current_provider_name = self.config.provider
1007
- self.dimension = provider.get_dimension()
1036
+ self.dimension = self._effective_dimension(provider.get_dimension())
1008
1037
  logger.info(f"Using {self.config.provider} embedding provider")
1009
1038
  else:
1010
1039
  logger.warning(f"{self.config.provider} provider not available, using fallback")
@@ -1022,7 +1051,7 @@ class EmbeddingEngine:
1022
1051
  self._primary_provider = provider
1023
1052
  self._current_provider_name = fallback
1024
1053
  self._using_fallback = True
1025
- self.dimension = provider.get_dimension()
1054
+ self.dimension = self._effective_dimension(provider.get_dimension())
1026
1055
  self._metrics["fallback_count"] += 1
1027
1056
  logger.info(f"Switched to fallback provider: {fallback}")
1028
1057
  return
@@ -1031,7 +1060,7 @@ class EmbeddingEngine:
1031
1060
  self._primary_provider = self._providers["local"]
1032
1061
  self._current_provider_name = "local"
1033
1062
  self._using_fallback = True
1034
- self.dimension = self._primary_provider.get_dimension()
1063
+ self.dimension = self._effective_dimension(self._primary_provider.get_dimension())
1035
1064
 
1036
1065
  def _get_cache_key(
1037
1066
  self, text: str, provider: Optional[str] = None, input_type: str = "document"
@@ -1043,6 +1072,31 @@ class EmbeddingEngine:
1043
1072
  content = f"{provider_name}:{self.model_name}:{text}{suffix}"
1044
1073
  return hashlib.md5(content.encode('utf-8')).hexdigest()
1045
1074
 
1075
+ def _effective_dimension(self, native: int) -> int:
1076
+ """Provider dimension after optional output_dimension truncation."""
1077
+ target = self.config.output_dimension
1078
+ return native if target is None else min(native, target)
1079
+
1080
+ def _prepare(self, embedding: np.ndarray) -> np.ndarray:
1081
+ """Truncate to output_dimension (if set), then L2 normalize."""
1082
+ target = self.config.output_dimension
1083
+ if target is not None and embedding.shape[-1] > target:
1084
+ embedding = embedding[..., :target]
1085
+ return self._normalize(embedding)
1086
+
1087
+ def vector_index_kwargs(self) -> Dict[str, Any]:
1088
+ """Constructor kwargs for a VectorIndex matching this engine's output.
1089
+
1090
+ ``normalized`` follows the active provider's declaration; a provider
1091
+ that does not declare it gets a cosine-ranking index. VectorIndex
1092
+ re-normalizes anything off unit length when the flag is set.
1093
+ """
1094
+ return {
1095
+ "dimension": self.dimension,
1096
+ "normalized": bool(getattr(self._primary_provider, "normalized", False)),
1097
+ "dtype": self.config.dtype,
1098
+ }
1099
+
1046
1100
  def _normalize(self, embedding: np.ndarray) -> np.ndarray:
1047
1101
  """L2 normalize an embedding vector."""
1048
1102
  if embedding.ndim == 1:
@@ -1130,7 +1184,7 @@ class EmbeddingEngine:
1130
1184
  embedding = np.average(chunk_embeddings, axis=0, weights=weights)
1131
1185
 
1132
1186
  # Normalize
1133
- embedding = self._normalize(embedding)
1187
+ embedding = self._prepare(embedding)
1134
1188
 
1135
1189
  # Track metrics
1136
1190
  provider_name = self._current_provider_name
@@ -1153,7 +1207,7 @@ class EmbeddingEngine:
1153
1207
  weights = np.array([len(c) for c in chunks], dtype=np.float32)
1154
1208
  weights = weights / weights.sum()
1155
1209
  embedding = np.average(chunk_embeddings, axis=0, weights=weights)
1156
- embedding = self._normalize(embedding)
1210
+ embedding = self._prepare(embedding)
1157
1211
  # The cache key computed above used the pre-fallback provider name.
1158
1212
  # _use_fallback() switched the provider, so recompute the key to
1159
1213
  # reflect the provider that actually produced this vector. Without
@@ -1248,7 +1302,7 @@ class EmbeddingEngine:
1248
1302
  if texts_to_compute:
1249
1303
  try:
1250
1304
  new_embeddings = self._primary_provider.embed_batch(texts_to_compute)
1251
- new_embeddings = self._normalize(new_embeddings)
1305
+ new_embeddings = self._prepare(new_embeddings)
1252
1306
 
1253
1307
  # Track metrics
1254
1308
  provider_name = self._current_provider_name
@@ -1260,7 +1314,7 @@ class EmbeddingEngine:
1260
1314
  logger.warning(f"Primary provider failed: {e}, trying fallback")
1261
1315
  self._use_fallback()
1262
1316
  new_embeddings = self._primary_provider.embed_batch(texts_to_compute)
1263
- new_embeddings = self._normalize(new_embeddings)
1317
+ new_embeddings = self._prepare(new_embeddings)
1264
1318
 
1265
1319
  # Update cache
1266
1320
  if self.config.cache_enabled:
@@ -10,6 +10,7 @@ memory system's embedding-based search capabilities.
10
10
  from __future__ import annotations
11
11
 
12
12
  import json
13
+ import math
13
14
  import os
14
15
  from typing import Callable, Dict, List, Optional, Tuple
15
16
 
@@ -35,13 +36,26 @@ class VectorIndex:
35
36
  metadata: List of metadata dictionaries for each vector.
36
37
  """
37
38
 
38
- def __init__(self, dimension: int = 384):
39
+ SUPPORTED_DTYPES = ("float32", "float16")
40
+
41
+ def __init__(
42
+ self,
43
+ dimension: int = 384,
44
+ normalized: bool = False,
45
+ dtype: str = "float32",
46
+ ):
39
47
  """
40
48
  Initialize a new vector index.
41
49
 
42
50
  Args:
43
51
  dimension: The dimensionality of vectors. Default is 384
44
52
  which matches MiniLM embedding size.
53
+ normalized: True when every vector added is already unit length
54
+ (a provider that declares ``normalized``). Search then
55
+ ranks by plain dot product, which equals cosine for unit
56
+ vectors, and skips the per-search normalized copy.
57
+ dtype: Storage dtype for vectors, "float32" (default) or
58
+ "float16". Scoring always runs in float32.
45
59
 
46
60
  Raises:
47
61
  ImportError: If numpy is not installed. Every operation in this
@@ -56,13 +70,32 @@ class VectorIndex:
56
70
  "numpy is required for the vector index. "
57
71
  "Install it with: pip install numpy"
58
72
  )
73
+ if dtype not in self.SUPPORTED_DTYPES:
74
+ raise ValueError(
75
+ f"dtype must be one of {self.SUPPORTED_DTYPES}, got {dtype!r}"
76
+ )
59
77
  self.dimension = dimension
78
+ self.assume_normalized = normalized
79
+ self.dtype = dtype
60
80
  self.embeddings: List[np.ndarray] = []
61
81
  self.ids: List[str] = []
62
82
  self.metadata: List[Dict] = []
63
83
  self._normalized: bool = False
64
84
  self._id_to_index: Dict[str, int] = {}
65
85
 
86
+ def _store(self, embedding: np.ndarray) -> np.ndarray:
87
+ """Cast a vector to the storage dtype. A normalized index holds unit
88
+ vectors only: anything off unit length is re-normalized in float32
89
+ first, so a caller's non-unit vector can never inflate dot-product
90
+ scores. A zero or non-finite vector is stored as is, never divided."""
91
+ vec = np.asarray(embedding)
92
+ if self.assume_normalized:
93
+ vec = vec.astype(np.float32)
94
+ norm = float(np.linalg.norm(vec.astype(np.float64)))
95
+ if math.isfinite(norm) and norm > 0 and abs(norm - 1.0) > 1e-6:
96
+ vec = vec / norm
97
+ return vec.astype(self.dtype)
98
+
66
99
  def add(
67
100
  self,
68
101
  id: str,
@@ -95,7 +128,7 @@ class VectorIndex:
95
128
  return
96
129
 
97
130
  # Add new entry
98
- self.embeddings.append(embedding.astype(np.float32))
131
+ self.embeddings.append(self._store(embedding))
99
132
  self.ids.append(id)
100
133
  self.metadata.append(metadata or {})
101
134
  self._id_to_index[id] = len(self.ids) - 1
@@ -154,7 +187,8 @@ class VectorIndex:
154
187
  """
155
188
  Find the top-k most similar vectors to the query.
156
189
 
157
- Uses cosine similarity for ranking.
190
+ Uses cosine similarity for ranking (a plain dot product when the
191
+ index was created with normalized=True).
158
192
 
159
193
  Args:
160
194
  query: The query vector to search for.
@@ -179,15 +213,17 @@ class VectorIndex:
179
213
  f"index dimension {self.dimension}"
180
214
  )
181
215
 
182
- # Ensure vectors are normalized for cosine similarity
183
- if not self._normalized:
184
- self._normalize_vectors()
185
-
186
216
  # Normalize query
187
217
  query_norm = query / (np.linalg.norm(query) + 1e-10)
188
218
 
189
- # Stack normalized embeddings into matrix for efficient computation
190
- embeddings_matrix = np.vstack(self._normalized_embeddings)
219
+ if self.assume_normalized:
220
+ # Unit-length vectors: dot product equals cosine, no copy needed.
221
+ embeddings_matrix = np.vstack(self.embeddings).astype(np.float32)
222
+ else:
223
+ # Ensure vectors are normalized for cosine similarity
224
+ if not self._normalized:
225
+ self._normalize_vectors()
226
+ embeddings_matrix = np.vstack(self._normalized_embeddings).astype(np.float32)
191
227
 
192
228
  # Compute cosine similarities
193
229
  similarities = np.dot(embeddings_matrix, query_norm)
@@ -264,7 +300,7 @@ class VectorIndex:
264
300
  f"Embedding dimension {embedding.shape[0]} does not match "
265
301
  f"index dimension {self.dimension}"
266
302
  )
267
- self.embeddings[index] = embedding.astype(np.float32)
303
+ self.embeddings[index] = self._store(embedding)
268
304
  self._normalized = False
269
305
 
270
306
  if metadata is not None:
@@ -321,6 +357,9 @@ class VectorIndex:
321
357
  "metadata": self.metadata,
322
358
  "dimension": self.dimension
323
359
  }
360
+ if self.assume_normalized:
361
+ # Written only when set, so a default index keeps its byte layout.
362
+ sidecar_data["normalized"] = True
324
363
 
325
364
  import tempfile
326
365
  json_path = f"{path}.json"
@@ -371,9 +410,16 @@ class VectorIndex:
371
410
  self.ids = sidecar_data["ids"]
372
411
  self.metadata = sidecar_data["metadata"]
373
412
 
374
- # Convert embeddings matrix to list
413
+ # A file saved by a normalized index, or an index that claims
414
+ # normalized, ranks by dot product: validate by re-normalizing.
415
+ if sidecar_data.get("normalized") is True:
416
+ self.assume_normalized = True
417
+
418
+ # Convert embeddings matrix to list, cast to this index's dtype
419
+ # (and unit length when normalized) so a float16 file does not leave
420
+ # a float32 index half precision and an old non-unit file scores right.
375
421
  if embeddings_matrix.shape[0] > 0:
376
- self.embeddings = [embeddings_matrix[i] for i in range(embeddings_matrix.shape[0])]
422
+ self.embeddings = [self._store(embeddings_matrix[i]) for i in range(embeddings_matrix.shape[0])]
377
423
  else:
378
424
  self.embeddings = []
379
425
 
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": "11.45.0",
4
+ "version": "11.46.0",
5
5
  "description": "An autonomous software factory that knows what it is supposed to deliver, and proves it did.",
6
6
  "keywords": [
7
7
  "agent",
@@ -160,5 +160,5 @@
160
160
  "dependencies": {
161
161
  "@resvg/resvg-wasm": "^2.6.2"
162
162
  },
163
- "gitHead": "334540289bfd9f05d830e17ac92c014ccdce4c4f"
163
+ "gitHead": "426ac88090247c972081b0c6bf078068c21f8e4e"
164
164
  }
@@ -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": "11.45.0",
5
+ "version": "11.46.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",
@@ -1,4 +1,4 @@
1
- import{c as L,r as x,j as e,w as re,Z as ne,m as le,h as ie,p as P,F as ye,U as I,X as ce,L as de,x as oe,M as Ae,T as Z,S as we,l as xe,k as me,y as Ce,E as Fe,a0 as Se,a as U,a2 as he}from"./index-CvDorHM5.js";import{B as C}from"./Button-DMQxN8bv.js";import{T as $e}from"./trending-up-DPnHpzG_.js";import{C as z}from"./clock-L4Yo-1oq.js";import{A as Be,U as ue,M as De}from"./Avatar-CCwnon5O.js";import{A as J}from"./activity-BvnwlILO.js";import{R as pe}from"./refresh-cw-DmIYhngY.js";import{G as ze}from"./globe-nkvqONVn.js";import{C as Le}from"./chevron-left-BndWdJHg.js";import{C as je}from"./check-Bo8ndDNV.js";import{D as Y}from"./dollar-sign-DfcAvtPn.js";import{U as Te}from"./user-9uylwqzF.js";import{D as Me}from"./database-Blj4jI9G.js";import{L as Ue}from"./lock-CzfZEv4E.js";import{S as Re}from"./scroll-text-tcxJua91.js";import{S as Ie}from"./shield-alert-B6-i4Ci9.js";/**
1
+ import{c as L,r as x,j as e,w as re,Z as ne,m as le,h as ie,p as P,F as ye,U as I,X as ce,L as de,x as oe,M as Ae,T as Z,S as we,l as xe,k as me,y as Ce,E as Fe,a0 as Se,a as U,a2 as he}from"./index-CjeJkKHf.js";import{B as C}from"./Button-BDJQTeBq.js";import{T as $e}from"./trending-up-Bqln1HBv.js";import{C as z}from"./clock-W2kDTGYx.js";import{A as Be,U as ue,M as De}from"./Avatar-xfdA6HHw.js";import{A as J}from"./activity-onNTo3z1.js";import{R as pe}from"./refresh-cw-B-eHLtrF.js";import{G as ze}from"./globe-mb1Su4WH.js";import{C as Le}from"./chevron-left-D8M7-5Kc.js";import{C as je}from"./check-_TVDwdrq.js";import{D as Y}from"./dollar-sign-Be49Rzh5.js";import{U as Te}from"./user-BhFi92qW.js";import{D as Me}from"./database-Dg9kxuUW.js";import{L as Ue}from"./lock-DyfnnkPv.js";import{S as Re}from"./scroll-text-BfFjI8cR.js";import{S as Ie}from"./shield-alert-Bojfzk9R.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as l,j as s}from"./index-CvDorHM5.js";/**
1
+ import{c as l,j as s}from"./index-CjeJkKHf.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{j as e}from"./index-CvDorHM5.js";import{C as r}from"./clock-L4Yo-1oq.js";import{C as a}from"./circle-alert-BYyuAk0R.js";import{C as l}from"./check-Bo8ndDNV.js";const i={completed:"bg-[#1FC5A8]/10 text-[#1FC5A8]",running:"bg-[#553DE9]/10 text-[#553DE9]",failed:"bg-[#C45B5B]/10 text-[#C45B5B]",started:"bg-[#D4A03C]/10 text-[#D4A03C]",empty:"bg-[#F8F4F0] text-[#6B6960]",version:"bg-[#553DE9]/10 text-[#553DE9]"};function o({status:t}){switch(t){case"completed":return e.jsx(l,{size:12});case"running":return e.jsxs("span",{className:"relative flex h-2 w-2",children:[e.jsx("span",{className:"animate-ping motion-reduce:animate-none absolute inline-flex h-full w-full rounded-full bg-current opacity-75"}),e.jsx("span",{className:"relative inline-flex rounded-full h-2 w-2 bg-current"})]});case"failed":return e.jsx(a,{size:12});case"started":return e.jsx(r,{size:12});default:return null}}function f({status:t,children:n,className:s=""}){return e.jsxs("span",{className:["inline-flex items-center gap-1 rounded-btn px-2.5 py-0.5 text-xs font-semibold",i[t],s].filter(Boolean).join(" "),children:[e.jsx(o,{status:t}),n]})}export{f as B};
1
+ import{j as e}from"./index-CjeJkKHf.js";import{C as r}from"./clock-W2kDTGYx.js";import{C as a}from"./circle-alert-HUO4WzPf.js";import{C as l}from"./check-_TVDwdrq.js";const i={completed:"bg-[#1FC5A8]/10 text-[#1FC5A8]",running:"bg-[#553DE9]/10 text-[#553DE9]",failed:"bg-[#C45B5B]/10 text-[#C45B5B]",started:"bg-[#D4A03C]/10 text-[#D4A03C]",empty:"bg-[#F8F4F0] text-[#6B6960]",version:"bg-[#553DE9]/10 text-[#553DE9]"};function o({status:t}){switch(t){case"completed":return e.jsx(l,{size:12});case"running":return e.jsxs("span",{className:"relative flex h-2 w-2",children:[e.jsx("span",{className:"animate-ping motion-reduce:animate-none absolute inline-flex h-full w-full rounded-full bg-current opacity-75"}),e.jsx("span",{className:"relative inline-flex rounded-full h-2 w-2 bg-current"})]});case"failed":return e.jsx(a,{size:12});case"started":return e.jsx(r,{size:12});default:return null}}function f({status:t,children:n,className:s=""}){return e.jsxs("span",{className:["inline-flex items-center gap-1 rounded-btn px-2.5 py-0.5 text-xs font-semibold",i[t],s].filter(Boolean).join(" "),children:[e.jsx(o,{status:t}),n]})}export{f as B};
@@ -1 +1 @@
1
- import{r as p,j as t}from"./index-CvDorHM5.js";const m={primary:"bg-[#553DE9] text-white hover:bg-[#4432c4] shadow-button rounded-btn",secondary:"border border-[#553DE9] text-[#553DE9] hover:bg-[#E8E4FD] bg-transparent rounded-btn",ghost:"text-[#36342E] hover:bg-[#F8F4F0] rounded-btn",danger:"bg-[#C45B5B]/10 text-[#C45B5B] border border-[#C45B5B]/20 hover:bg-[#C45B5B]/20 rounded-btn"},b={sm:"px-3 py-1.5 text-xs",md:"px-4 py-2 text-sm",lg:"px-6 py-3 text-base"},u={sm:14,md:16,lg:18};function h({size:e}){return t.jsxs("svg",{className:"animate-spin",width:e,height:e,viewBox:"0 0 24 24",fill:"none",children:[t.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),t.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]})}const B=p.forwardRef(({variant:e="primary",size:o="md",icon:n,iconRight:i,loading:r=!1,disabled:a,className:c="",children:l,...x},d)=>{const s=u[o];return t.jsxs("button",{ref:d,disabled:a||r,className:["inline-flex items-center justify-center gap-2 font-medium transition-colors",m[e],b[o],(a||r)&&"opacity-60 cursor-not-allowed",c].filter(Boolean).join(" "),...x,children:[r?t.jsx(h,{size:s}):n?t.jsx(n,{size:s}):null,l,i&&!r&&t.jsx(i,{size:s})]})});B.displayName="Button";export{B};
1
+ import{r as p,j as t}from"./index-CjeJkKHf.js";const m={primary:"bg-[#553DE9] text-white hover:bg-[#4432c4] shadow-button rounded-btn",secondary:"border border-[#553DE9] text-[#553DE9] hover:bg-[#E8E4FD] bg-transparent rounded-btn",ghost:"text-[#36342E] hover:bg-[#F8F4F0] rounded-btn",danger:"bg-[#C45B5B]/10 text-[#C45B5B] border border-[#C45B5B]/20 hover:bg-[#C45B5B]/20 rounded-btn"},b={sm:"px-3 py-1.5 text-xs",md:"px-4 py-2 text-sm",lg:"px-6 py-3 text-base"},u={sm:14,md:16,lg:18};function h({size:e}){return t.jsxs("svg",{className:"animate-spin",width:e,height:e,viewBox:"0 0 24 24",fill:"none",children:[t.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),t.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]})}const B=p.forwardRef(({variant:e="primary",size:o="md",icon:n,iconRight:i,loading:r=!1,disabled:a,className:c="",children:l,...x},d)=>{const s=u[o];return t.jsxs("button",{ref:d,disabled:a||r,className:["inline-flex items-center justify-center gap-2 font-medium transition-colors",m[e],b[o],(a||r)&&"opacity-60 cursor-not-allowed",c].filter(Boolean).join(" "),...x,children:[r?t.jsx(h,{size:s}):n?t.jsx(n,{size:s}):null,l,i&&!r&&t.jsx(i,{size:s})]})});B.displayName="Button";export{B};
@@ -1,4 +1,4 @@
1
- import{c as $,r as l,j as e,G as we,Q as Se,F as Ce,h as Re,T as U,Z as Ee,a as S,V as Me,S as $e,Y as Te,X as Pe,P as ie,i as Ae,g as ze,C as Ie,l as Le,N as I,J as _e,K as De,O as Fe,u as Oe}from"./index-CvDorHM5.js";import{P as K,S as He,E as Be}from"./ErrorBoundary-CincZs7i.js";import{C as qe,F as Ge,M as Ue,H as Ke,a as We,G as Ve,T as Qe}from"./thumbs-up-CtzTHYX3.js";import{C as oe}from"./check-Bo8ndDNV.js";import{C as Ye}from"./clock-L4Yo-1oq.js";import{D as Xe}from"./dollar-sign-DfcAvtPn.js";import{U as Ze}from"./upload-BY5CKgt-.js";import{L as V}from"./loader-circle-DNgNGU-W.js";/**
1
+ import{c as $,r as l,j as e,G as we,Q as Se,F as Ce,h as Re,T as U,Z as Ee,a as S,V as Me,S as $e,Y as Te,X as Pe,P as ie,i as Ae,g as ze,C as Ie,l as Le,N as I,J as _e,K as De,O as Fe,u as Oe}from"./index-CjeJkKHf.js";import{P as K,S as He,E as Be}from"./ErrorBoundary-BGAlBzcB.js";import{C as qe,F as Ge,M as Ue,H as Ke,a as We,G as Ve,T as Qe}from"./thumbs-up-BsB7OQxT.js";import{C as oe}from"./check-_TVDwdrq.js";import{C as Ye}from"./clock-W2kDTGYx.js";import{D as Xe}from"./dollar-sign-Be49Rzh5.js";import{U as Ze}from"./upload-CNpkqzkA.js";import{L as V}from"./loader-circle-DZFGHRZb.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as o,j as e}from"./index-CvDorHM5.js";function f({value:r,prefix:t="",suffix:s=""}){const[u,n]=o.useState(0),c=o.useRef(0);return o.useEffect(()=>{const a=c.current,h=r-a;if(h===0)return;const i=600,d=performance.now();function l(b){const v=b-d,m=Math.min(v/i,1),N=1-Math.pow(1-m,3),p=Math.round(a+h*N);n(p),c.current=p,m<1&&requestAnimationFrame(l)}requestAnimationFrame(l)},[r]),e.jsxs("span",{className:"tabular-nums",children:[t,u.toLocaleString(),s]})}function g(){const[r,t]=o.useState(5),[s,u]=o.useState(30),[n,c]=o.useState(75),a=.4,i=Math.round(r*s*a*4.33),d=Math.round(i*n);return e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-6 shadow-sm",children:[e.jsx("h3",{className:"text-lg font-bold text-[#36342E] mb-1",children:"ROI Calculator"}),e.jsx("p",{className:"text-sm text-[#6B6960] mb-6",children:"Estimate how much your team could save with autonomous development."}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Team size"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[r," developers"]})]}),e.jsx("input",{type:"range",min:1,max:50,value:r,onChange:l=>t(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"1"}),e.jsx("span",{children:"50"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Hours coding per week"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[s,"h"]})]}),e.jsx("input",{type:"range",min:5,max:60,value:s,onChange:l=>u(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"5h"}),e.jsx("span",{children:"60h"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Average hourly rate"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:["$",n,"/hr"]})]}),e.jsx("input",{type:"range",min:25,max:250,step:5,value:n,onChange:l=>c(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"$25"}),e.jsx("span",{children:"$250"})]})]})]}),e.jsxs("div",{className:"mt-6 pt-6 border-t border-[#ECEAE3]",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 text-center",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#553DE9]",children:e.jsx(f,{value:i,suffix:"h"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. hours saved / month"})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#1FC5A8]",children:e.jsx(f,{value:d,prefix:"$"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. cost saved / month"})]})]}),e.jsxs("div",{className:"mt-4 p-3 rounded-lg bg-[#553DE9]/5 border border-[#553DE9]/10",children:[e.jsxs("p",{className:"text-sm text-[#36342E] text-center",children:["If Loki Mode saved ",Math.round(a*100),"% of coding time, your team could save"," ",e.jsxs("span",{className:"font-bold text-[#553DE9]",children:[i.toLocaleString()," hours"]})," ","and"," ",e.jsxs("span",{className:"font-bold text-[#1FC5A8]",children:["$",d.toLocaleString()]})," ","per month."]}),e.jsxs("p",{className:"mt-2 text-xs text-[#6B6960] text-center",children:["The ",Math.round(a*100),"% is an assumption, not a measurement. Model and API spend is not included."]})]})]})]})}const j=[{feature:"Claude (Opus/Sonnet/Haiku)",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!0},{feature:"GPT-4o / Codex",category:"AI Models",lokiMode:!0,boltNew:!0,replit:!0,lovable:!1},{feature:"5-provider auto-failover",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"8 automated quality gates",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Blind 3-reviewer code review",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Anti-sycophancy checks",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Automated test generation",category:"Quality",lokiMode:!0,boltNew:!1,replit:"Partial",lovable:!1},{feature:"Docker container generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"CI/CD pipeline generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"One-click cloud deploy",category:"Deployment",lokiMode:"Planned",boltNew:!0,replit:!0,lovable:!0},{feature:"Self-hosted (your keys)",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Free tier available",category:"Cost",lokiMode:"Source-available (BUSL-1.1)",boltNew:!0,replit:!0,lovable:!0},{feature:"No vendor lock-in",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"SSO / OIDC authentication",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"RBAC access control",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Audit logging",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Air-gapped deployment",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1}];function y(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M20 6L9 17l-5-5",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function w(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#C45B5B]/40",children:e.jsx("path",{d:"M18 6L6 18M6 6l12 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function x({value:r}){return typeof r=="string"?e.jsx("span",{className:"text-xs text-[#6B6960] font-medium",children:r}):r?e.jsx(y,{}):e.jsx(w,{})}const k=[{title:"Full Autonomy",description:"Describe what you want. Loki Mode handles planning, coding, testing, and verification automatically.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#553DE9]",children:e.jsx("path",{d:"M13 2L3 14h9l-1 8 10-12h-9l1-8z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Production Quality",description:"8 quality gates, blind code review, and anti-sycophancy checks ensure code that actually works.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Your Infrastructure",description:"Self-hosted, source-available (BUSL-1.1), no data leaves your network. Use your own API keys across 5 providers.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#2F71E3]",children:[e.jsx("rect",{x:"2",y:"2",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("rect",{x:"2",y:"14",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("circle",{cx:"6",cy:"6",r:"1",fill:"currentColor"}),e.jsx("circle",{cx:"6",cy:"18",r:"1",fill:"currentColor"})]})},{title:"Built for Teams",description:"Enterprise auth, RBAC, audit logs, and parallel workflows for teams of any size.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#E93D82]",children:[e.jsx("path",{d:"M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("circle",{cx:"9",cy:"7",r:"4",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})}];function C(){const r=[...new Set(j.map(t=>t.category))];return e.jsx("div",{className:"min-h-screen bg-[#FAF9F6]",children:e.jsxs("div",{className:"max-w-6xl mx-auto px-6 py-8",children:[e.jsxs("div",{className:"mb-8",children:[e.jsx("h1",{className:"text-2xl font-bold text-[#36342E]",children:"Compare"}),e.jsx("p",{className:"text-[#6B6960] mt-1",children:"See how Loki Mode stacks up against other AI development platforms."})]}),e.jsx("div",{className:"bg-white border border-[#ECEAE3] rounded-xl overflow-hidden shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[#ECEAE3]",children:[e.jsx("th",{className:"text-left px-4 py-3 font-medium text-[#6B6960] w-[240px]",children:"Feature"}),e.jsx("th",{className:"text-center px-4 py-3 font-bold text-[#553DE9] bg-[#553DE9]/5 w-[130px]",children:"Loki Mode"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"bolt.new"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Replit"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Lovable"})]})}),e.jsx("tbody",{children:r.map(t=>e.jsxs(e.Fragment,{children:[e.jsx("tr",{className:"bg-[#FAF9F6]",children:e.jsx("td",{colSpan:5,className:"px-4 py-2 text-xs font-bold text-[#939084] uppercase tracking-wider",children:t})},`cat-${t}`),j.filter(s=>s.category===t).map(s=>e.jsxs("tr",{className:"border-b border-[#ECEAE3] last:border-b-0",children:[e.jsx("td",{className:"px-4 py-2.5 text-[#36342E]",children:s.feature}),e.jsx("td",{className:"px-4 py-2.5 text-center bg-[#553DE9]/5",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lokiMode})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.boltNew})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.replit})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lovable})})})]},s.feature))]}))})]})})}),e.jsxs("div",{className:"mt-12",children:[e.jsx("h2",{className:"text-xl font-bold text-[#36342E] mb-6",children:"Why developers choose Loki Mode"}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-5",children:k.map(t=>e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-5 shadow-sm hover:shadow-md transition-shadow",children:[e.jsx("div",{className:"mb-3",children:t.icon}),e.jsx("h3",{className:"text-base font-bold text-[#36342E] mb-1",children:t.title}),e.jsx("p",{className:"text-sm text-[#6B6960]",children:t.description})]},t.title))})]}),e.jsx("div",{className:"mt-12 max-w-xl mx-auto",children:e.jsx(g,{})})]})})}export{C as default};
1
+ import{r as o,j as e}from"./index-CjeJkKHf.js";function f({value:r,prefix:t="",suffix:s=""}){const[u,n]=o.useState(0),c=o.useRef(0);return o.useEffect(()=>{const a=c.current,h=r-a;if(h===0)return;const i=600,d=performance.now();function l(b){const v=b-d,m=Math.min(v/i,1),N=1-Math.pow(1-m,3),p=Math.round(a+h*N);n(p),c.current=p,m<1&&requestAnimationFrame(l)}requestAnimationFrame(l)},[r]),e.jsxs("span",{className:"tabular-nums",children:[t,u.toLocaleString(),s]})}function g(){const[r,t]=o.useState(5),[s,u]=o.useState(30),[n,c]=o.useState(75),a=.4,i=Math.round(r*s*a*4.33),d=Math.round(i*n);return e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-6 shadow-sm",children:[e.jsx("h3",{className:"text-lg font-bold text-[#36342E] mb-1",children:"ROI Calculator"}),e.jsx("p",{className:"text-sm text-[#6B6960] mb-6",children:"Estimate how much your team could save with autonomous development."}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Team size"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[r," developers"]})]}),e.jsx("input",{type:"range",min:1,max:50,value:r,onChange:l=>t(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"1"}),e.jsx("span",{children:"50"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Hours coding per week"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[s,"h"]})]}),e.jsx("input",{type:"range",min:5,max:60,value:s,onChange:l=>u(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"5h"}),e.jsx("span",{children:"60h"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Average hourly rate"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:["$",n,"/hr"]})]}),e.jsx("input",{type:"range",min:25,max:250,step:5,value:n,onChange:l=>c(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"$25"}),e.jsx("span",{children:"$250"})]})]})]}),e.jsxs("div",{className:"mt-6 pt-6 border-t border-[#ECEAE3]",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 text-center",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#553DE9]",children:e.jsx(f,{value:i,suffix:"h"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. hours saved / month"})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#1FC5A8]",children:e.jsx(f,{value:d,prefix:"$"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. cost saved / month"})]})]}),e.jsxs("div",{className:"mt-4 p-3 rounded-lg bg-[#553DE9]/5 border border-[#553DE9]/10",children:[e.jsxs("p",{className:"text-sm text-[#36342E] text-center",children:["If Loki Mode saved ",Math.round(a*100),"% of coding time, your team could save"," ",e.jsxs("span",{className:"font-bold text-[#553DE9]",children:[i.toLocaleString()," hours"]})," ","and"," ",e.jsxs("span",{className:"font-bold text-[#1FC5A8]",children:["$",d.toLocaleString()]})," ","per month."]}),e.jsxs("p",{className:"mt-2 text-xs text-[#6B6960] text-center",children:["The ",Math.round(a*100),"% is an assumption, not a measurement. Model and API spend is not included."]})]})]})]})}const j=[{feature:"Claude (Opus/Sonnet/Haiku)",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!0},{feature:"GPT-4o / Codex",category:"AI Models",lokiMode:!0,boltNew:!0,replit:!0,lovable:!1},{feature:"5-provider auto-failover",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"8 automated quality gates",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Blind 3-reviewer code review",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Anti-sycophancy checks",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Automated test generation",category:"Quality",lokiMode:!0,boltNew:!1,replit:"Partial",lovable:!1},{feature:"Docker container generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"CI/CD pipeline generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"One-click cloud deploy",category:"Deployment",lokiMode:"Planned",boltNew:!0,replit:!0,lovable:!0},{feature:"Self-hosted (your keys)",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Free tier available",category:"Cost",lokiMode:"Source-available (BUSL-1.1)",boltNew:!0,replit:!0,lovable:!0},{feature:"No vendor lock-in",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"SSO / OIDC authentication",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"RBAC access control",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Audit logging",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Air-gapped deployment",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1}];function y(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M20 6L9 17l-5-5",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function w(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#C45B5B]/40",children:e.jsx("path",{d:"M18 6L6 18M6 6l12 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function x({value:r}){return typeof r=="string"?e.jsx("span",{className:"text-xs text-[#6B6960] font-medium",children:r}):r?e.jsx(y,{}):e.jsx(w,{})}const k=[{title:"Full Autonomy",description:"Describe what you want. Loki Mode handles planning, coding, testing, and verification automatically.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#553DE9]",children:e.jsx("path",{d:"M13 2L3 14h9l-1 8 10-12h-9l1-8z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Production Quality",description:"8 quality gates, blind code review, and anti-sycophancy checks ensure code that actually works.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Your Infrastructure",description:"Self-hosted, source-available (BUSL-1.1), no data leaves your network. Use your own API keys across 5 providers.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#2F71E3]",children:[e.jsx("rect",{x:"2",y:"2",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("rect",{x:"2",y:"14",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("circle",{cx:"6",cy:"6",r:"1",fill:"currentColor"}),e.jsx("circle",{cx:"6",cy:"18",r:"1",fill:"currentColor"})]})},{title:"Built for Teams",description:"Enterprise auth, RBAC, audit logs, and parallel workflows for teams of any size.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#E93D82]",children:[e.jsx("path",{d:"M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("circle",{cx:"9",cy:"7",r:"4",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})}];function C(){const r=[...new Set(j.map(t=>t.category))];return e.jsx("div",{className:"min-h-screen bg-[#FAF9F6]",children:e.jsxs("div",{className:"max-w-6xl mx-auto px-6 py-8",children:[e.jsxs("div",{className:"mb-8",children:[e.jsx("h1",{className:"text-2xl font-bold text-[#36342E]",children:"Compare"}),e.jsx("p",{className:"text-[#6B6960] mt-1",children:"See how Loki Mode stacks up against other AI development platforms."})]}),e.jsx("div",{className:"bg-white border border-[#ECEAE3] rounded-xl overflow-hidden shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[#ECEAE3]",children:[e.jsx("th",{className:"text-left px-4 py-3 font-medium text-[#6B6960] w-[240px]",children:"Feature"}),e.jsx("th",{className:"text-center px-4 py-3 font-bold text-[#553DE9] bg-[#553DE9]/5 w-[130px]",children:"Loki Mode"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"bolt.new"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Replit"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Lovable"})]})}),e.jsx("tbody",{children:r.map(t=>e.jsxs(e.Fragment,{children:[e.jsx("tr",{className:"bg-[#FAF9F6]",children:e.jsx("td",{colSpan:5,className:"px-4 py-2 text-xs font-bold text-[#939084] uppercase tracking-wider",children:t})},`cat-${t}`),j.filter(s=>s.category===t).map(s=>e.jsxs("tr",{className:"border-b border-[#ECEAE3] last:border-b-0",children:[e.jsx("td",{className:"px-4 py-2.5 text-[#36342E]",children:s.feature}),e.jsx("td",{className:"px-4 py-2.5 text-center bg-[#553DE9]/5",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lokiMode})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.boltNew})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.replit})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lovable})})})]},s.feature))]}))})]})})}),e.jsxs("div",{className:"mt-12",children:[e.jsx("h2",{className:"text-xl font-bold text-[#36342E] mb-6",children:"Why developers choose Loki Mode"}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-5",children:k.map(t=>e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-5 shadow-sm hover:shadow-md transition-shadow",children:[e.jsx("div",{className:"mb-3",children:t.icon}),e.jsx("h3",{className:"text-base font-bold text-[#36342E] mb-1",children:t.title}),e.jsx("p",{className:"text-sm text-[#6B6960]",children:t.description})]},t.title))})]}),e.jsx("div",{className:"mt-12 max-w-xl mx-auto",children:e.jsx(g,{})})]})})}export{C as default};
@@ -1,4 +1,4 @@
1
- import{c as o,r as a,j as e,T as n,h as i,p as c}from"./index-CvDorHM5.js";/**
1
+ import{c as o,r as a,j as e,T as n,h as i,p as c}from"./index-CjeJkKHf.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.