loki-mode 8.3.1 → 8.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ _The free, source-available autonomous coding agent by [Autonomi](https://www.au
15
15
 
16
16
  [Website](https://www.autonomi.dev/) | [Documentation](wiki/Home.md) | [Installation](docs/INSTALLATION.md) | [Changelog](CHANGELOG.md) | [Purple Lab -- deprecated v7.44.0](#purple-lab)
17
17
 
18
- **Current release: v8.3.1**
18
+ **Current release: v8.3.3**
19
19
 
20
20
  </div>
21
21
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 8.3.1
1
+ 8.3.3
@@ -174,17 +174,30 @@ _qs_score_templates() {
174
174
  local tdir; tdir="$(_qs_templates_dir)"
175
175
  local brief_lc; brief_lc=$(printf '%s' "$brief" | tr '[:upper:]' '[:lower:]')
176
176
 
177
- declare -A scores
177
+ # BASH 3.2 COMPATIBLE. This used `declare -A scores`, a bash 4 associative
178
+ # array. macOS ships bash 3.2.57 as /bin/bash (frozen since 2007, GPLv2,
179
+ # and Apple will not update it), so on the stock shell of the most common
180
+ # developer platform this function printed
181
+ # declare: -A: invalid option
182
+ # and returned ZERO templates. The function body parses fine, which is why
183
+ # sourcing looked healthy and the failure only appeared when CALLED --
184
+ # and the test harness runs under homebrew bash 5, so nothing caught it.
185
+ # `loki quickstart` is the guided first build we point new users at, so
186
+ # this was a first-run failure on Macs.
187
+ #
188
+ # Scores are kept in a flat "name<TAB>score" list instead. Same semantics,
189
+ # portable to 3.2, and the final sort was already doing the ordering work.
190
+ local scores=""
178
191
  local name f
179
192
  while IFS= read -r f; do
180
193
  [ -z "$f" ] && continue
181
194
  name=$(basename "$f" .md)
182
195
  [ "$name" = "README" ] && continue
183
- scores["$name"]=0
196
+ scores="${scores}${name}\t0\n"
184
197
  done < <(ls "$tdir"/*.md 2>/dev/null)
185
198
 
186
199
  # No templates resolvable: fall back to the guaranteed default only.
187
- if [ "${#scores[@]}" -eq 0 ]; then
200
+ if [ -z "$scores" ]; then
188
201
  printf 'simple-todo-app\n'
189
202
  return 0
190
203
  fi
@@ -192,37 +205,52 @@ _qs_score_templates() {
192
205
  local -a tokens
193
206
  read -ra tokens <<< "$(printf '%s' "$brief_lc" | tr -cs 'a-z0-9' ' ')"
194
207
 
195
- local tok kw tmpl wt
208
+ # Build the additive score deltas, then fold them in once with awk. Doing
209
+ # the arithmetic in awk rather than a bash re-write loop keeps this O(n) and
210
+ # avoids quoting a growing string repeatedly.
211
+ local deltas="" tok kw tmpl wt
196
212
  for tok in "${tokens[@]}"; do
197
213
  [ -z "$tok" ] && continue
198
214
  _qs_is_stopword "$tok" && continue
199
- for name in "${!scores[@]}"; do
215
+ # +2 per template whose hyphenated name contains the token.
216
+ while IFS=$'\t' read -r name _; do
217
+ [ -z "$name" ] && continue
200
218
  case "-$name-" in
201
- *"-$tok-"*) scores["$name"]=$(( ${scores["$name"]} + 2 ));;
219
+ *"-$tok-"*) deltas="${deltas}${name}\t2\n";;
202
220
  esac
203
- done
221
+ done < <(printf '%b' "$scores")
222
+ # Curated keyword weights.
204
223
  while IFS=: read -r kw tmpl wt; do
205
224
  [ -z "$kw" ] && continue
206
225
  [ -z "$wt" ] && wt=3
207
- if [ "$tok" = "$kw" ] && [ -n "${scores[$tmpl]+x}" ]; then
208
- scores["$tmpl"]=$(( ${scores["$tmpl"]} + wt ))
226
+ if [ "$tok" = "$kw" ]; then
227
+ deltas="${deltas}${tmpl}\t${wt}\n"
209
228
  fi
210
229
  done < <(_qs_keyword_map)
211
230
  done
212
231
 
213
- # Guaranteed default baseline.
214
- if [ -n "${scores[simple-todo-app]+x}" ]; then
215
- scores["simple-todo-app"]=$(( ${scores["simple-todo-app"]} + 1 ))
216
- fi
217
-
218
- # Emit "score<TAB>priority<TAB>name"; sort by score desc, priority asc
219
- # (simple-todo-app=0 wins ties), then name asc for full determinism.
220
- local prio
221
- for name in "${!scores[@]}"; do
222
- prio=1
223
- [ "$name" = "simple-todo-app" ] && prio=0
224
- printf '%s\t%s\t%s\n' "${scores[$name]}" "$prio" "$name"
225
- done | sort -t$'\t' -k1,1nr -k2,2n -k3,3 | head -3 | cut -f3
232
+ # Guaranteed default baseline: simple-todo-app gets +1 and wins exact ties.
233
+ deltas="${deltas}simple-todo-app\t1\n"
234
+
235
+ # Fold: keep only names that are REAL templates (a keyword map entry naming
236
+ # a template that does not exist must not invent one), sum the deltas, then
237
+ # sort by score desc, priority asc (simple-todo-app=0 wins ties), name asc.
238
+ printf '%b' "$scores" > /dev/null # (no-op guard: scores is always non-empty here)
239
+ {
240
+ printf '%b' "$scores"
241
+ printf '%b' "$deltas"
242
+ } | awk -F'\t' '
243
+ NF < 2 { next }
244
+ # First pass marker: names present in the base list are valid templates.
245
+ { sum[$1] += $2; if (!($1 in seen) && $2 == 0) seen[$1] = 1 }
246
+ END {
247
+ for (n in sum) {
248
+ if (!(n in seen)) continue
249
+ prio = (n == "simple-todo-app") ? 0 : 1
250
+ printf "%d\t%d\t%s\n", sum[n], prio, n
251
+ }
252
+ }
253
+ ' | sort -t"$(printf '\t')" -k1,1nr -k2,2n -k3,3 | head -3 | cut -f3
226
254
  }
227
255
 
228
256
  # _qs_template_summary <name>: a short one-line description for the picker.
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "8.3.1"
10
+ __version__ = "8.3.3"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var S_=Object.create;var{getPrototypeOf:y_,defineProperty:oK,getOwnPropertyNames:b_}=Object;var __=Object.prototype.hasOwnProperty;function f_(Z){return this[Z]}var h_,v_,g_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?h_??=new WeakMap:v_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?S_(y_(Z)):{};let K=X||!Z||!Z.__esModule?oK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of b_(Z))if(!__.call(K,$))oK(K,$,{get:f_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var VQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var m_=(Z)=>Z;function u_(Z,X){this[Z]=m_.bind(null,X)}var c0=(Z,X)=>{for(var Q in X)oK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:u_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var t0=import.meta.require;var IO={};c0(IO,{lokiDir:()=>j0,homeLokiDir:()=>I4,findRepoRootForVersion:()=>tK,REPO_ROOT:()=>Q8});import{resolve as s7,dirname as rK}from"path";import{fileURLToPath as p_}from"url";import{existsSync as qQ}from"fs";import{homedir as d_}from"os";function c_(){let Z=EO;for(let X=0;X<6;X++){if(qQ(s7(Z,"VERSION"))&&qQ(s7(Z,"autonomy/run.sh")))return Z;let Q=rK(Z);if(Q===Z)break;Z=Q}return s7(EO,"..","..","..")}function tK(Z){let X=Z;for(let Q=0;Q<6;Q++){if(qQ(s7(X,"VERSION"))&&qQ(s7(X,"autonomy/run.sh")))return X;let Y=rK(X);if(Y===X)break;X=Y}return s7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??s7(process.cwd(),".loki")}function I4(){return s7(d_(),".loki")}var EO,Q8;var G8=p(()=>{EO=rK(p_(import.meta.url));Q8=c_()});import{readFileSync as l_}from"fs";import{resolve as i_,dirname as a_}from"path";import{fileURLToPath as s_}from"url";function _3(){if(f5!==null)return f5;let Z="8.3.1";if(typeof Z==="string"&&Z.length>0)return f5=Z,f5;try{let X=a_(s_(import.meta.url)),Q=tK(X);f5=l_(i_(Q,"VERSION"),"utf-8").trim()}catch{f5="unknown"}return f5}var f5=null;var GQ=p(()=>{G8()});var xO={};c0(xO,{runOrThrow:()=>qf,run:()=>E0,readStreamCapped:()=>HQ,commandVersion:()=>Hf,commandExists:()=>X9,ShellError:()=>eK,MAX_STDOUT_BYTES:()=>kO});async function HQ(Z,X=kO){let Q=Z.getReader(),Y=new TextDecoder,J="",z=0;try{while(z<X){let{done:K,value:$}=await Q.read();if(K)break;if(!$)continue;if(z+=$.byteLength,z>X){let W=$.byteLength-(z-X);J+=Y.decode($.subarray(0,W),{stream:!0});break}J+=Y.decode($,{stream:!0})}J+=Y.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return J}async function E0(Z,X={}){let Q=Bun.spawn({cmd:[...Z],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),Y,J;if(X.timeoutMs&&X.timeoutMs>0)Y=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}J=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[z,K,$]=await Promise.all([HQ(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:z,stderr:K,exitCode:$}}finally{if(Y)clearTimeout(Y);if(J)clearTimeout(J)}}async function qf(Z,X={}){let Q=await E0(Z,X);if(Q.exitCode!==0)throw new eK(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function X9(Z){let X=Gf(Z),Q=await E0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Gf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function Hf(Z,X="--version"){if(!await X9(Z))return null;let Y=await E0([Z,X],{timeoutMs:5000});if(Y.exitCode!==0)return null;return((Y.stdout||Y.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var kO=16777216,eK;var k9=p(()=>{eK=class eK extends Error{message;exitCode;stdout;stderr;constructor(Z,X,Q,Y){super(Z);this.message=Z;this.exitCode=X;this.stdout=Q;this.stderr=Y;this.name="ShellError"}}});function n7(Z){return Uf?"":Z}var Uf,M0,k8,l0,dW0,a0,H8,Q9,g;var S6=p(()=>{Uf=(process.env.NO_COLOR??"").length>0;M0=n7("\x1B[0;31m"),k8=n7("\x1B[0;32m"),l0=n7("\x1B[1;33m"),dW0=n7("\x1B[0;34m"),a0=n7("\x1B[0;36m"),H8=n7("\x1B[1m"),Q9=n7("\x1B[2m"),g=n7("\x1B[0m")});import{existsSync as Ff}from"fs";async function F7(){if(R4!==void 0)return R4;let Z="/opt/homebrew/bin/python3.12";if(Ff(Z))return R4=Z,Z;let X=await X9("python3.12");if(X)return R4=X,X;let Q=await X9("python3");return R4=Q,Q}async function D7(Z,X={}){let Q=await F7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return E0([Q,"-c",Z],X)}var R4;var o7=p(()=>{k9()});var rO={};c0(rO,{runStatus:()=>tf});import{existsSync as Y9,readFileSync as h3,readdirSync as dO,statSync as cO}from"fs";import{resolve as h8,basename as pf}from"path";import{homedir as df}from"os";function lO(Z){let X=Math.trunc(Z);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 iO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*BQ/X);if(J>BQ)J=BQ;let z=BQ-J,K=k8;if(Y>=80)K=M0;else if(Y>=50)K=l0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=lO(Z),V=lO(X);return` ${H8}${Q}${g} ${K}[${$}]${g} ${Y}% (${W} / ${V})`}async function lf(){if(await X9("jq"))return!0;return process.stdout.write(`${M0}Error: jq is required but not installed.${g}
2
+ var S_=Object.create;var{getPrototypeOf:y_,defineProperty:oK,getOwnPropertyNames:b_}=Object;var __=Object.prototype.hasOwnProperty;function f_(Z){return this[Z]}var h_,v_,g_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?h_??=new WeakMap:v_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?S_(y_(Z)):{};let K=X||!Z||!Z.__esModule?oK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of b_(Z))if(!__.call(K,$))oK(K,$,{get:f_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var VQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var m_=(Z)=>Z;function u_(Z,X){this[Z]=m_.bind(null,X)}var c0=(Z,X)=>{for(var Q in X)oK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:u_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var t0=import.meta.require;var IO={};c0(IO,{lokiDir:()=>j0,homeLokiDir:()=>I4,findRepoRootForVersion:()=>tK,REPO_ROOT:()=>Q8});import{resolve as s7,dirname as rK}from"path";import{fileURLToPath as p_}from"url";import{existsSync as qQ}from"fs";import{homedir as d_}from"os";function c_(){let Z=EO;for(let X=0;X<6;X++){if(qQ(s7(Z,"VERSION"))&&qQ(s7(Z,"autonomy/run.sh")))return Z;let Q=rK(Z);if(Q===Z)break;Z=Q}return s7(EO,"..","..","..")}function tK(Z){let X=Z;for(let Q=0;Q<6;Q++){if(qQ(s7(X,"VERSION"))&&qQ(s7(X,"autonomy/run.sh")))return X;let Y=rK(X);if(Y===X)break;X=Y}return s7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??s7(process.cwd(),".loki")}function I4(){return s7(d_(),".loki")}var EO,Q8;var G8=p(()=>{EO=rK(p_(import.meta.url));Q8=c_()});import{readFileSync as l_}from"fs";import{resolve as i_,dirname as a_}from"path";import{fileURLToPath as s_}from"url";function _3(){if(f5!==null)return f5;let Z="8.3.3";if(typeof Z==="string"&&Z.length>0)return f5=Z,f5;try{let X=a_(s_(import.meta.url)),Q=tK(X);f5=l_(i_(Q,"VERSION"),"utf-8").trim()}catch{f5="unknown"}return f5}var f5=null;var GQ=p(()=>{G8()});var xO={};c0(xO,{runOrThrow:()=>qf,run:()=>E0,readStreamCapped:()=>HQ,commandVersion:()=>Hf,commandExists:()=>X9,ShellError:()=>eK,MAX_STDOUT_BYTES:()=>kO});async function HQ(Z,X=kO){let Q=Z.getReader(),Y=new TextDecoder,J="",z=0;try{while(z<X){let{done:K,value:$}=await Q.read();if(K)break;if(!$)continue;if(z+=$.byteLength,z>X){let W=$.byteLength-(z-X);J+=Y.decode($.subarray(0,W),{stream:!0});break}J+=Y.decode($,{stream:!0})}J+=Y.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return J}async function E0(Z,X={}){let Q=Bun.spawn({cmd:[...Z],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),Y,J;if(X.timeoutMs&&X.timeoutMs>0)Y=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}J=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[z,K,$]=await Promise.all([HQ(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:z,stderr:K,exitCode:$}}finally{if(Y)clearTimeout(Y);if(J)clearTimeout(J)}}async function qf(Z,X={}){let Q=await E0(Z,X);if(Q.exitCode!==0)throw new eK(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function X9(Z){let X=Gf(Z),Q=await E0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Gf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function Hf(Z,X="--version"){if(!await X9(Z))return null;let Y=await E0([Z,X],{timeoutMs:5000});if(Y.exitCode!==0)return null;return((Y.stdout||Y.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var kO=16777216,eK;var k9=p(()=>{eK=class eK extends Error{message;exitCode;stdout;stderr;constructor(Z,X,Q,Y){super(Z);this.message=Z;this.exitCode=X;this.stdout=Q;this.stderr=Y;this.name="ShellError"}}});function n7(Z){return Uf?"":Z}var Uf,M0,k8,l0,dW0,a0,H8,Q9,g;var S6=p(()=>{Uf=(process.env.NO_COLOR??"").length>0;M0=n7("\x1B[0;31m"),k8=n7("\x1B[0;32m"),l0=n7("\x1B[1;33m"),dW0=n7("\x1B[0;34m"),a0=n7("\x1B[0;36m"),H8=n7("\x1B[1m"),Q9=n7("\x1B[2m"),g=n7("\x1B[0m")});import{existsSync as Ff}from"fs";async function F7(){if(R4!==void 0)return R4;let Z="/opt/homebrew/bin/python3.12";if(Ff(Z))return R4=Z,Z;let X=await X9("python3.12");if(X)return R4=X,X;let Q=await X9("python3");return R4=Q,Q}async function D7(Z,X={}){let Q=await F7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return E0([Q,"-c",Z],X)}var R4;var o7=p(()=>{k9()});var rO={};c0(rO,{runStatus:()=>tf});import{existsSync as Y9,readFileSync as h3,readdirSync as dO,statSync as cO}from"fs";import{resolve as h8,basename as pf}from"path";import{homedir as df}from"os";function lO(Z){let X=Math.trunc(Z);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 iO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*BQ/X);if(J>BQ)J=BQ;let z=BQ-J,K=k8;if(Y>=80)K=M0;else if(Y>=50)K=l0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=lO(Z),V=lO(X);return` ${H8}${Q}${g} ${K}[${$}]${g} ${Y}% (${W} / ${V})`}async function lf(){if(await X9("jq"))return!0;return process.stdout.write(`${M0}Error: jq is required but not installed.${g}
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)
@@ -1206,4 +1206,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1206
1206
  `),2}case"start":{let{runStart:Y}=await Promise.resolve().then(() => (R_(),P_));return Y(Q)}default:return process.stderr.write(`Unknown command: ${X}
1207
1207
  `),process.stderr.write(k_),2}}uO();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var DW0=await FW0(Bun.argv.slice(2));process.exit(DW0);
1208
1208
 
1209
- //# debugId=56EE84E87CC3B43564756E2164756E21
1209
+ //# debugId=0387E93FA065D70464756E2164756E21
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": "8.3.1",
4
+ "version": "8.3.3",
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).",
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": "8.3.1",
5
+ "version": "8.3.3",
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",