loki-mode 7.7.1 → 7.7.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/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.1
6
+ # Loki Mode v7.7.3
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.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
384
+ **v7.7.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.7.1
1
+ 7.7.3
package/autonomy/loki CHANGED
@@ -2285,7 +2285,8 @@ if not os.path.isdir(loki_dir):
2285
2285
  result['status'] = 'inactive'
2286
2286
  result['phase'] = None
2287
2287
  result['iteration'] = 0
2288
- result['provider'] = env_provider
2288
+ result['provider'] = env_provider if os.environ.get('LOKI_PROVIDER', '') else 'claude'
2289
+ result['provider_source'] = 'env' if os.environ.get('LOKI_PROVIDER', '') else 'default'
2289
2290
  result['dashboard_url'] = None
2290
2291
  result['pid'] = None
2291
2292
  result['elapsed_time'] = 0
@@ -2343,13 +2344,27 @@ else:
2343
2344
  result['phase'] = None
2344
2345
  result['iteration'] = 0
2345
2346
 
2346
- # Provider
2347
+ # Provider + provider_source (v7.7.2 B-5 clarity: surface WHY this
2348
+ # value won the resolution race: 'saved' vs 'env' vs 'default').
2347
2349
  provider_file = os.path.join(loki_dir, 'state', 'provider')
2348
2350
  if os.path.isfile(provider_file):
2349
- with open(provider_file) as f:
2350
- result['provider'] = f.read().strip()
2351
+ try:
2352
+ with open(provider_file) as f:
2353
+ saved = f.read().strip()
2354
+ except OSError:
2355
+ saved = ''
2351
2356
  else:
2357
+ saved = ''
2358
+ env_set = os.environ.get('LOKI_PROVIDER', '') != ''
2359
+ if saved:
2360
+ result['provider'] = saved
2361
+ result['provider_source'] = 'saved'
2362
+ elif env_set:
2352
2363
  result['provider'] = env_provider
2364
+ result['provider_source'] = 'env'
2365
+ else:
2366
+ result['provider'] = 'claude'
2367
+ result['provider_source'] = 'default'
2353
2368
 
2354
2369
  # PID
2355
2370
  pid_file = os.path.join(loki_dir, 'loki.pid')
@@ -2817,6 +2832,15 @@ cmd_provider() {
2817
2832
  echo " info Show provider details"
2818
2833
  echo " models Show resolved model configuration for all providers"
2819
2834
  echo ""
2835
+ echo "Precedence (v7.7.2 -- highest wins):"
2836
+ echo " 1. loki start --provider NAME CLI flag (per-invocation)"
2837
+ echo " 2. .loki/state/provider saved value (per-project)"
2838
+ echo " 3. LOKI_PROVIDER env var shell session default"
2839
+ echo " 4. claude built-in default"
2840
+ echo ""
2841
+ echo " Note: 'loki status' reflects the SAVED provider, not the env var."
2842
+ echo " Run 'loki provider set <name>' to make a project-level choice persist."
2843
+ echo ""
2820
2844
  echo "Examples:"
2821
2845
  echo " loki provider show"
2822
2846
  echo " loki provider set claude"
package/autonomy/run.sh CHANGED
@@ -8714,6 +8714,66 @@ except Exception as e:
8714
8714
  PYEOF
8715
8715
  }
8716
8716
 
8717
+ # v7.7.3 F-3 fix: intelligent USAGE.md regeneration. Called at session end
8718
+ # (after completion-promise fulfilled). Reads the FINAL project state
8719
+ # (file tree + package manifests + recent commits) and asks Claude
8720
+ # (haiku tier) to emit a USAGE.md tailored to the actual stack.
8721
+ #
8722
+ # Best-effort: any failure (no provider, network, parse) returns silently
8723
+ # without disrupting completion. Costs ~$0.01-0.05 per session (one
8724
+ # haiku call). Set LOKI_INTELLIGENT_USAGE=0 to skip entirely.
8725
+ _intelligent_usage_regen() {
8726
+ local target_dir="${TARGET_DIR:-.}"
8727
+ local usage_path="$target_dir/USAGE.md"
8728
+ # Find a working `claude` binary; if absent, bail silently.
8729
+ if ! command -v claude >/dev/null 2>&1; then
8730
+ return 0
8731
+ fi
8732
+ # Snapshot project state. Keep it small (< ~4 KiB) so the prompt
8733
+ # stays cache-stable across sessions.
8734
+ local _tree _manifests _commits _state_prompt
8735
+ _tree=$(cd "$target_dir" && find . -maxdepth 3 -type f \
8736
+ -not -path './node_modules/*' -not -path './.loki/*' \
8737
+ -not -path './.git/*' -not -path './venv/*' -not -path './.venv/*' \
8738
+ -not -path './dist/*' -not -path './build/*' 2>/dev/null | head -30)
8739
+ # Capture package manifests inline so the model sees real scripts.
8740
+ _manifests=""
8741
+ for f in package.json requirements.txt pyproject.toml Cargo.toml go.mod composer.json Gemfile; do
8742
+ if [ -f "$target_dir/$f" ]; then
8743
+ _manifests="${_manifests}=== $f ===\n$(head -50 "$target_dir/$f" 2>/dev/null)\n\n"
8744
+ fi
8745
+ done
8746
+ _commits=$(cd "$target_dir" && git log --oneline -10 2>/dev/null || true)
8747
+
8748
+ log_info "Regenerating USAGE.md from final project state (intelligent mode)..."
8749
+ local _ic_prompt="You are writing a USAGE.md for the project below. Detect the stack from the manifest files; emit a concise (under 100 lines) Markdown doc with sections: ## Prerequisites, ## Install, ## Start, ## Verify (2-3 copy-paste curl/browser/CLI commands with expected output), ## Stop. Use the ACTUAL command names from package.json scripts or pyproject entry points -- never generic placeholders. Output ONLY the Markdown body (no code-fence wrapper, no preamble).
8750
+
8751
+ === Project tree (max 30 files, 3 levels deep) ===
8752
+ ${_tree}
8753
+
8754
+ === Manifest files ===
8755
+ ${_manifests}
8756
+
8757
+ === Last 10 commits ===
8758
+ ${_commits}"
8759
+
8760
+ # Use haiku for cheap, fast generation. --dangerously-skip-permissions
8761
+ # because this is a one-shot non-interactive call.
8762
+ local _ic_out
8763
+ _ic_out=$(printf '%s' "$_ic_prompt" \
8764
+ | timeout 60 claude --dangerously-skip-permissions --model haiku -p - 2>/dev/null \
8765
+ | head -200)
8766
+ # Sanity check: response must look like Markdown (starts with # or ##).
8767
+ if [ -z "$_ic_out" ] || ! printf '%s' "$_ic_out" | head -1 | grep -qE '^#'; then
8768
+ log_info "Intelligent USAGE regen returned non-Markdown or empty; keeping existing USAGE.md."
8769
+ return 0
8770
+ fi
8771
+ printf '%s\n' "$_ic_out" > "$usage_path"
8772
+ log_info "USAGE.md regenerated intelligently from final project state -> $usage_path"
8773
+ return 0
8774
+ }
8775
+
8776
+
8717
8777
  # Magic Modules COMPOUND: record successful component patterns (v6.77.0)
8718
8778
  # Called at end of each iteration to capture generated/updated components
8719
8779
  # as semantic memory patterns via magic.core.memory_bridge.
@@ -11544,6 +11604,16 @@ if __name__ == "__main__":
11544
11604
  log_header "TASK COMPLETION CLAIMED (via loki_complete_task)"
11545
11605
  fi
11546
11606
  log_info "Explicit completion signal detected."
11607
+ # v7.7.3 F-3 fix: intelligent USAGE.md regeneration. The static
11608
+ # USAGE_DOC_INSTRUCTION in build_prompt gets the agent to write
11609
+ # SOMETHING; this hook re-runs a cheap model call with the FINAL
11610
+ # project state to refine that output (or write it if missing).
11611
+ # Default-on per the "no user flag" mandate; set
11612
+ # LOKI_INTELLIGENT_USAGE=0 to disable. Best-effort: failures
11613
+ # never block completion.
11614
+ if [ "${LOKI_INTELLIGENT_USAGE:-1}" != "0" ]; then
11615
+ _intelligent_usage_regen 2>/dev/null || true
11616
+ fi
11547
11617
  # Run memory consolidation on successful completion
11548
11618
  log_info "Running memory consolidation..."
11549
11619
  run_memory_consolidation
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.7.1"
10
+ __version__ = "7.7.3"
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.1
5
+ **Version:** v7.7.3
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.1";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.3";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)
@@ -76,7 +76,8 @@ if not os.path.isdir(loki_dir):
76
76
  result['status'] = 'inactive'
77
77
  result['phase'] = None
78
78
  result['iteration'] = 0
79
- result['provider'] = env_provider
79
+ result['provider'] = env_provider if os.environ.get('LOKI_PROVIDER', '') else 'claude'
80
+ result['provider_source'] = 'env' if os.environ.get('LOKI_PROVIDER', '') else 'default'
80
81
  result['dashboard_url'] = None
81
82
  result['pid'] = None
82
83
  result['elapsed_time'] = 0
@@ -134,13 +135,26 @@ else:
134
135
  result['phase'] = None
135
136
  result['iteration'] = 0
136
137
 
137
- # Provider
138
+ # Provider + provider_source (v7.7.2 B-5 clarity, parity with bash)
138
139
  provider_file = os.path.join(loki_dir, 'state', 'provider')
139
140
  if os.path.isfile(provider_file):
140
- with open(provider_file) as f:
141
- result['provider'] = f.read().strip()
141
+ try:
142
+ with open(provider_file) as f:
143
+ saved = f.read().strip()
144
+ except OSError:
145
+ saved = ''
142
146
  else:
147
+ saved = ''
148
+ env_set = os.environ.get('LOKI_PROVIDER', '') != ''
149
+ if saved:
150
+ result['provider'] = saved
151
+ result['provider_source'] = 'saved'
152
+ elif env_set:
143
153
  result['provider'] = env_provider
154
+ result['provider_source'] = 'env'
155
+ else:
156
+ result['provider'] = 'claude'
157
+ result['provider_source'] = 'default'
144
158
 
145
159
  # PID
146
160
  pid_file = os.path.join(loki_dir, 'loki.pid')
@@ -536,4 +550,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
536
550
  `),2}default:return process.stderr.write(`Unknown command: ${$}
537
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);
538
552
 
539
- //# debugId=5030DC06E8E8ED4064756E2164756E21
553
+ //# debugId=B6208839B2FD587864756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.7.1'
60
+ __version__ = '7.7.3'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "7.7.1",
3
+ "version": "7.7.3",
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",