loki-mode 7.7.6 → 7.7.7

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/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.7.6
6
+ # Loki Mode v7.7.7
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -381,4 +381,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
381
381
 
382
382
  ---
383
383
 
384
- **v7.7.6 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
384
+ **v7.7.7 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.7.6
1
+ 7.7.7
package/autonomy/loki CHANGED
@@ -14259,20 +14259,25 @@ except Exception as e:
14259
14259
  # .loki/metrics/efficiency/iter-*.json. Unify: prefer the
14260
14260
  # canonical kpis source; fall back to the legacy file for
14261
14261
  # backward compat with pre-v7.6.6 sessions.
14262
+ # v7.7.7 fix: also check iteration-*.json (the canonical filename)
14263
+ # in addition to iter-*.json. Use a portable glob check (no compgen)
14264
+ # so the function works in any POSIX shell, not just bash.
14262
14265
  local _eff_dir=".loki/metrics/efficiency"
14263
14266
  local _legacy_file=".loki/memory/token_economics.json"
14264
14267
  local _have_iters=0
14265
14268
  if [ -d "$_eff_dir" ]; then
14266
- if compgen -G "$_eff_dir/iter-*.json" >/dev/null 2>&1; then
14269
+ if ls "$_eff_dir"/iteration-*.json >/dev/null 2>&1 || ls "$_eff_dir"/iter-*.json >/dev/null 2>&1; then
14267
14270
  _have_iters=1
14268
14271
  fi
14269
14272
  fi
14270
14273
  if [ "$_have_iters" -eq 1 ]; then
14271
14274
  PYTHONPATH="${SKILL_DIR}${PYTHONPATH:+:$PYTHONPATH}" python3 - <<'PYEOF' 2>/dev/null
14272
14275
  import json, glob, os, sys
14273
- files = sorted(glob.glob('.loki/metrics/efficiency/iter-*.json'))
14276
+ # v7.7.7 fix: canonical filename is `iteration-N.json`, NOT `iter-N.json`.
14277
+ # We glob both for compat with any older runs.
14278
+ files = sorted(set(glob.glob('.loki/metrics/efficiency/iteration-*.json') + glob.glob('.loki/metrics/efficiency/iter-*.json')))
14274
14279
  totals = {
14275
- 'source': '.loki/metrics/efficiency/iter-*.json',
14280
+ 'source': '.loki/metrics/efficiency/iteration-*.json',
14276
14281
  'iterations': len(files),
14277
14282
  'total_input_tokens': 0,
14278
14283
  'total_output_tokens': 0,
package/autonomy/run.sh CHANGED
@@ -8826,35 +8826,87 @@ auto_capture_episode() {
8826
8826
  # rolled the new files into HEAD. Now diff against the iteration-start
8827
8827
  # SHA captured at the top of the retry loop. Falls back to HEAD~1 if the
8828
8828
  # start SHA env is unset (older direct callers).
8829
+ # v7.7.7 fix: previously only captured files when target_dir was a git
8830
+ # repo. Real-user test on /tmp/loki-validate (no git init) produced
8831
+ # `files_modified: []` because git rev-parse failed silently and the
8832
+ # fallback also required git. Now: detect git-vs-non-git up front, and
8833
+ # for non-git dirs use a `find` snapshot diff against the timestamp
8834
+ # captured when loki created .loki/ (initialized_at). Skips standard
8835
+ # noise dirs (.loki, node_modules, .git, venv, .venv, dist, build).
8829
8836
  local files_modified=""
8830
8837
  local _diff_base="${_LOKI_ITER_START_SHA:-}"
8831
- if [ -z "$_diff_base" ]; then
8832
- _diff_base=$(cd "$target_dir" && git rev-parse HEAD~1 2>/dev/null || echo "")
8833
- fi
8834
- if [ -n "$_diff_base" ]; then
8835
- files_modified=$(cd "$target_dir" && git diff --name-only "$_diff_base" HEAD 2>/dev/null | head -50 | tr '\n' '|' || true)
8836
- # Also include unstaged changes (in case auto-commit didn't run)
8837
- local _unstaged
8838
- _unstaged=$(cd "$target_dir" && git diff --name-only HEAD 2>/dev/null | head -20 | tr '\n' '|' || true)
8839
- if [ -n "$_unstaged" ]; then
8840
- files_modified="${files_modified}${_unstaged}"
8838
+ local _is_git=0
8839
+ if (cd "$target_dir" && git rev-parse --is-inside-work-tree >/dev/null 2>&1); then
8840
+ _is_git=1
8841
+ fi
8842
+ if [ "$_is_git" -eq 1 ]; then
8843
+ if [ -z "$_diff_base" ]; then
8844
+ _diff_base=$(cd "$target_dir" && git rev-parse HEAD~1 2>/dev/null || echo "")
8845
+ fi
8846
+ if [ -n "$_diff_base" ]; then
8847
+ files_modified=$(cd "$target_dir" && git diff --name-only "$_diff_base" HEAD 2>/dev/null | head -50 | tr '\n' '|' || true)
8848
+ # Also include unstaged changes (in case auto-commit didn't run)
8849
+ local _unstaged
8850
+ _unstaged=$(cd "$target_dir" && git diff --name-only HEAD 2>/dev/null | head -20 | tr '\n' '|' || true)
8851
+ if [ -n "$_unstaged" ]; then
8852
+ files_modified="${files_modified}${_unstaged}"
8853
+ fi
8854
+ else
8855
+ # Git repo but no prior commit (e.g. fresh init) -- list untracked.
8856
+ files_modified=$(cd "$target_dir" && git ls-files --others --exclude-standard 2>/dev/null | head -50 | tr '\n' '|' || true)
8841
8857
  fi
8842
8858
  else
8843
- # No git history yet (initial commit) -- list all tracked + untracked.
8844
- files_modified=$(cd "$target_dir" && git ls-files --others --exclude-standard 2>/dev/null | head -50 | tr '\n' '|' || true)
8859
+ # NOT a git repo: snapshot diff via find. Use .loki/ mtime as the
8860
+ # iteration-start reference (loki creates .loki/ on session start).
8861
+ # `-newer` on directory mtime gives a rough but useful set of files
8862
+ # modified DURING this session. Skip noise dirs.
8863
+ local _ref_file="${target_dir}/.loki/state/orchestrator.json"
8864
+ if [ ! -f "$_ref_file" ]; then
8865
+ _ref_file="${target_dir}/.loki"
8866
+ fi
8867
+ if [ -e "$_ref_file" ]; then
8868
+ files_modified=$(cd "$target_dir" && find . -type f -newer "$_ref_file" \
8869
+ -not -path './.loki/*' \
8870
+ -not -path './node_modules/*' \
8871
+ -not -path './.git/*' \
8872
+ -not -path './venv/*' \
8873
+ -not -path './.venv/*' \
8874
+ -not -path './dist/*' \
8875
+ -not -path './build/*' \
8876
+ 2>/dev/null | head -50 | sed 's|^\./||' | tr '\n' '|' || true)
8877
+ fi
8878
+ # Belt-and-suspenders: if find returned nothing, fall back to a
8879
+ # plain listing of non-noise files (every visible file, capped at 50).
8880
+ if [ -z "$files_modified" ]; then
8881
+ files_modified=$(cd "$target_dir" && find . -maxdepth 3 -type f \
8882
+ -not -path './.loki/*' \
8883
+ -not -path './node_modules/*' \
8884
+ -not -path './.git/*' \
8885
+ 2>/dev/null | head -50 | sed 's|^\./||' | tr '\n' '|' || true)
8886
+ fi
8845
8887
  fi
8846
8888
 
8847
8889
  # Collect last git commit if any
8848
8890
  local git_commit=""
8849
8891
  git_commit=$(cd "$target_dir" && git rev-parse --short HEAD 2>/dev/null || true)
8850
8892
 
8851
- # v7.6.4 B-3a fix: aggregate per-iteration efficiency metrics into the
8852
- # episode so `tokens_used` / `cost_usd` / `input_tokens` / `output_tokens`
8853
- # are no longer always 0. Source: `.loki/metrics/efficiency/iter-N.json`
8854
- # (same source `loki kpis` reads from).
8855
- local _iter_metrics_file="$target_dir/.loki/metrics/efficiency/iter-${iteration}.json"
8893
+ # v7.6.4 B-3a fix + v7.7.7 filename fix: the actual filename is
8894
+ # `iteration-N.json` (not `iter-N.json` as v7.6.4 erroneously assumed).
8895
+ # Real-user test on /tmp/loki-validate showed `iteration-1.json` in
8896
+ # .loki/metrics/efficiency/. We now check both the canonical name and
8897
+ # the legacy `iter-N.json` for backward compat with any older runs.
8898
+ local _iter_metrics_file=""
8899
+ for _candidate in \
8900
+ "$target_dir/.loki/metrics/efficiency/iteration-${iteration}.json" \
8901
+ "$target_dir/.loki/metrics/efficiency/iter-${iteration}.json" \
8902
+ ; do
8903
+ if [ -f "$_candidate" ]; then
8904
+ _iter_metrics_file="$_candidate"
8905
+ break
8906
+ fi
8907
+ done
8856
8908
  local _iter_tokens_in=0 _iter_tokens_out=0 _iter_cost=0
8857
- if [ -f "$_iter_metrics_file" ]; then
8909
+ if [ -n "$_iter_metrics_file" ]; then
8858
8910
  _iter_tokens_in=$(python3 -c "import json; d=json.load(open('$_iter_metrics_file')); print(int(d.get('input_tokens', 0) or 0))" 2>/dev/null || echo 0)
8859
8911
  _iter_tokens_out=$(python3 -c "import json; d=json.load(open('$_iter_metrics_file')); print(int(d.get('output_tokens', 0) or 0))" 2>/dev/null || echo 0)
8860
8912
  _iter_cost=$(python3 -c "import json; d=json.load(open('$_iter_metrics_file')); print(float(d.get('cost_usd', 0) or 0))" 2>/dev/null || echo 0)
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.7.6"
10
+ __version__ = "7.7.7"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.7.6
5
+ **Version:** v7.7.7
6
6
 
7
7
  ---
8
8
 
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var b=(K,$)=>{for(var z in $)_7(K,z,{get:$[z],enumerable:!0,configurable:!0,set:P7.bind($,z)})};var R=(K,$)=>()=>(K&&($=K(K=0)),$);var V1=import.meta.require;var e1={};b(e1,{lokiDir:()=>P,homeLokiDir:()=>k1,findRepoRootForVersion:()=>N1,REPO_ROOT:()=>p});import{resolve as u,dirname as S1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(u(K,"VERSION"))&&J1(u(K,"autonomy/run.sh")))return K;let z=S1(K);if(z===K)break;K=z}return u(i1,"..","..","..")}function N1(K){let $=K;for(let z=0;z<6;z++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let Q=S1($);if(Q===$)break;$=Q}return u(K,"..","..","..")}function P(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var y=R(()=>{i1=S1(L7(import.meta.url));p=E7()});import{readFileSync as x7}from"fs";import{resolve as F7,dirname as w7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(n!==null)return n;let K="7.7.6";if(typeof K==="string"&&K.length>0)return n=K,n;try{let $=w7(S7(import.meta.url)),z=N1($);n=x7(F7(z,"VERSION"),"utf-8").trim()}catch{n="unknown"}return n}var n=null;var D1=R(()=>{y()});var $0={};b($0,{runOrThrow:()=>N7,run:()=>S,commandVersion:()=>D7,commandExists:()=>D,ShellError:()=>C1});async function S(K,$={}){let z=Bun.spawn({cmd:[...K],stdout:"pipe",stderr:"pipe",env:$.env?{...process.env,...$.env}:process.env,cwd:$.cwd}),Q,X;if($.timeoutMs&&$.timeoutMs>0)Q=setTimeout(()=>{try{z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{z.kill("SIGKILL")}catch{}},2000)},$.timeoutMs);try{let[H,Z,q]=await Promise.all([new Response(z.stdout).text(),new Response(z.stderr).text(),z.exited]);return{stdout:H,stderr:Z,exitCode:q}}finally{if(Q)clearTimeout(Q);if(X)clearTimeout(X)}}async function N7(K,$={}){let z=await S(K,$);if(z.exitCode!==0)throw new C1(`command failed (${z.exitCode}): ${K.join(" ")}`,z.exitCode,z.stdout,z.stderr);return z}async function D(K){let $=k7(K),z=await S(["sh","-c",`command -v ${$}`],{timeoutMs:5000});if(z.exitCode===0)return z.stdout.trim()||null;return null}function k7(K){if(!/^[A-Za-z0-9._/-]+$/.test(K))throw Error(`refused to shell-escape suspect token: ${K}`);return K}async function D7(K,$="--version"){if(!await D(K))return null;let Q=await S([K,$],{timeoutMs:5000});if(Q.exitCode!==0)return null;return((Q.stdout||Q.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var C1;var c=R(()=>{C1=class C1 extends Error{message;exitCode;stdout;stderr;constructor(K,$,z,Q){super(K);this.message=K;this.exitCode=$;this.stdout=z;this.stderr=Q;this.name="ShellError"}}});function l(K){return C7?"":K}var C7,E,C,x,O6,O,k,F,W;var a=R(()=>{C7=(process.env.NO_COLOR??"").length>0;E=l("\x1B[0;31m"),C=l("\x1B[0;32m"),x=l("\x1B[1;33m"),O6=l("\x1B[0;34m"),O=l("\x1B[0;36m"),k=l("\x1B[1m"),F=l("\x1B[2m"),W=l("\x1B[0m")});import{existsSync as c7}from"fs";async function t(){if(z1!==void 0)return z1;let K="/opt/homebrew/bin/python3.12";if(c7(K))return z1=K,K;let $=await D("python3.12");if($)return z1=$,$;let z=await D("python3");return z1=z,z}async function s(K,$={}){let z=await t();if(!z)return{stdout:"",stderr:"python3 not found",exitCode:127};return S([z,"-c",K],$)}var z1;var Q1=R(()=>{c()});var G0={};b(G0,{runStatus:()=>z5});import{existsSync as N,readFileSync as Z1,readdirSync as H0,statSync as W0}from"fs";import{resolve as w,basename as a7}from"path";async function r7(){if(await D("jq"))return!0;return process.stdout.write(`${E}Error: jq is required but not installed.${W}
2
+ var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var b=(K,$)=>{for(var z in $)_7(K,z,{get:$[z],enumerable:!0,configurable:!0,set:P7.bind($,z)})};var R=(K,$)=>()=>(K&&($=K(K=0)),$);var V1=import.meta.require;var e1={};b(e1,{lokiDir:()=>P,homeLokiDir:()=>k1,findRepoRootForVersion:()=>N1,REPO_ROOT:()=>p});import{resolve as u,dirname as S1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(u(K,"VERSION"))&&J1(u(K,"autonomy/run.sh")))return K;let z=S1(K);if(z===K)break;K=z}return u(i1,"..","..","..")}function N1(K){let $=K;for(let z=0;z<6;z++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let Q=S1($);if(Q===$)break;$=Q}return u(K,"..","..","..")}function P(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var y=R(()=>{i1=S1(L7(import.meta.url));p=E7()});import{readFileSync as x7}from"fs";import{resolve as F7,dirname as w7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(n!==null)return n;let K="7.7.7";if(typeof K==="string"&&K.length>0)return n=K,n;try{let $=w7(S7(import.meta.url)),z=N1($);n=x7(F7(z,"VERSION"),"utf-8").trim()}catch{n="unknown"}return n}var n=null;var D1=R(()=>{y()});var $0={};b($0,{runOrThrow:()=>N7,run:()=>S,commandVersion:()=>D7,commandExists:()=>D,ShellError:()=>C1});async function S(K,$={}){let z=Bun.spawn({cmd:[...K],stdout:"pipe",stderr:"pipe",env:$.env?{...process.env,...$.env}:process.env,cwd:$.cwd}),Q,X;if($.timeoutMs&&$.timeoutMs>0)Q=setTimeout(()=>{try{z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{z.kill("SIGKILL")}catch{}},2000)},$.timeoutMs);try{let[H,Z,q]=await Promise.all([new Response(z.stdout).text(),new Response(z.stderr).text(),z.exited]);return{stdout:H,stderr:Z,exitCode:q}}finally{if(Q)clearTimeout(Q);if(X)clearTimeout(X)}}async function N7(K,$={}){let z=await S(K,$);if(z.exitCode!==0)throw new C1(`command failed (${z.exitCode}): ${K.join(" ")}`,z.exitCode,z.stdout,z.stderr);return z}async function D(K){let $=k7(K),z=await S(["sh","-c",`command -v ${$}`],{timeoutMs:5000});if(z.exitCode===0)return z.stdout.trim()||null;return null}function k7(K){if(!/^[A-Za-z0-9._/-]+$/.test(K))throw Error(`refused to shell-escape suspect token: ${K}`);return K}async function D7(K,$="--version"){if(!await D(K))return null;let Q=await S([K,$],{timeoutMs:5000});if(Q.exitCode!==0)return null;return((Q.stdout||Q.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var C1;var c=R(()=>{C1=class C1 extends Error{message;exitCode;stdout;stderr;constructor(K,$,z,Q){super(K);this.message=K;this.exitCode=$;this.stdout=z;this.stderr=Q;this.name="ShellError"}}});function l(K){return C7?"":K}var C7,E,C,x,O6,O,k,F,W;var a=R(()=>{C7=(process.env.NO_COLOR??"").length>0;E=l("\x1B[0;31m"),C=l("\x1B[0;32m"),x=l("\x1B[1;33m"),O6=l("\x1B[0;34m"),O=l("\x1B[0;36m"),k=l("\x1B[1m"),F=l("\x1B[2m"),W=l("\x1B[0m")});import{existsSync as c7}from"fs";async function t(){if(z1!==void 0)return z1;let K="/opt/homebrew/bin/python3.12";if(c7(K))return z1=K,K;let $=await D("python3.12");if($)return z1=$,$;let z=await D("python3");return z1=z,z}async function s(K,$={}){let z=await t();if(!z)return{stdout:"",stderr:"python3 not found",exitCode:127};return S([z,"-c",K],$)}var z1;var Q1=R(()=>{c()});var G0={};b(G0,{runStatus:()=>z5});import{existsSync as N,readFileSync as Z1,readdirSync as H0,statSync as W0}from"fs";import{resolve as w,basename as a7}from"path";async function r7(){if(await D("jq"))return!0;return process.stdout.write(`${E}Error: jq is required but not installed.${W}
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)
@@ -550,4 +550,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
550
550
  `),2}default:return process.stderr.write(`Unknown command: ${$}
551
551
  `),process.stderr.write(j7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var z6=await $6(Bun.argv.slice(2));process.exit(z6);
552
552
 
553
- //# debugId=496D49A157EA8A9064756E2164756E21
553
+ //# debugId=A58DDE9430ACC6E964756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.7.6'
60
+ __version__ = '7.7.7'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "7.7.6",
3
+ "version": "7.7.7",
4
4
  "description": "Loki Mode by Autonomi. Multi-agent autonomous SDLC framework. Spec to deployed app: PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief. 4 AI providers (Claude Code, OpenAI Codex, Cline, Aider). 11 quality gates.",
5
5
  "keywords": [
6
6
  "agent",