loki-mode 7.17.0 → 7.17.1

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: Autonomous spec-to-product system. Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product via the RARV-C closure loop, with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.17.0
6
+ # Loki Mode v7.17.1
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -383,4 +383,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
383
383
 
384
384
  ---
385
385
 
386
- **v7.17.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
386
+ **v7.17.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.17.0
1
+ 7.17.1
@@ -307,7 +307,16 @@ def _collect_spec(loki_dir, target_dir):
307
307
  brief = _read_text(prd_path)
308
308
  else:
309
309
  gen = os.path.join(loki_dir, "generated-prd.md")
310
- if os.path.isfile(gen):
310
+ # Raw one-liner from `loki start "<brief>"` (zero-config first run). The
311
+ # brief path writes the typed brief here; showing it verbatim is a
312
+ # stronger, more honest proof artifact than the synthesized PRD or a
313
+ # "No brief recorded" fallback. Checked before generated-prd.md because a
314
+ # brief run never produces generated-prd.md (it writes brief-prd-$$.md).
315
+ raw_brief = os.path.join(loki_dir, "state", "brief.txt")
316
+ if os.path.isfile(raw_brief):
317
+ source = "brief"
318
+ brief = _read_text(raw_brief)
319
+ elif os.path.isfile(gen):
311
320
  source = gen
312
321
  brief = _read_text(gen)
313
322
  else:
@@ -213,12 +213,16 @@ def _render_md(section):
213
213
 
214
214
  def main(argv=None):
215
215
  ap = argparse.ArgumentParser(description="Generate a cited project wiki.")
216
+ ap.add_argument("path", nargs="?", default=None,
217
+ help="project root (positional; same as --root)")
216
218
  ap.add_argument("--root", default=".", help="project root")
217
219
  ap.add_argument("--force", action="store_true", help="regenerate even if unchanged")
218
220
  ap.add_argument("--quiet", action="store_true")
219
221
  args = ap.parse_args(argv)
220
222
 
221
- root = Path(args.root).resolve()
223
+ # A positional path (documented as `loki wiki generate [path]`) takes
224
+ # precedence over --root when both are given; otherwise fall back to --root.
225
+ root = Path(args.path if args.path is not None else args.root).resolve()
222
226
  if not root.is_dir():
223
227
  print("error: not a directory: %s" % root, file=sys.stderr)
224
228
  return 2
package/autonomy/loki CHANGED
@@ -1220,6 +1220,13 @@ cmd_start() {
1220
1220
  esac
1221
1221
  done
1222
1222
 
1223
+ # Clear any stale raw-brief marker from a PRIOR run before we decide the
1224
+ # mode of THIS run. Only the brief branch (below) re-writes it. Without this,
1225
+ # a brief run leaves .loki/state/brief.txt behind and a later non-brief run
1226
+ # (PRD or codebase-analysis) would have its proof mislabel source="brief"
1227
+ # with the old one-liner, since proof-generator reads brief.txt first.
1228
+ rm -f "$LOKI_DIR/state/brief.txt" 2>/dev/null || true
1229
+
1223
1230
  # v6.84.0: Unified dispatch based on explicit flags or auto-detection
1224
1231
  # Precedence: --brief > --issue > --prd > positional auto-detect >
1225
1232
  # LOKI_PRD_FILE env
@@ -1292,6 +1299,15 @@ cmd_start() {
1292
1299
  synthesize_brief_prd "$brief_prd" "$brief_text"
1293
1300
  prd_file="$brief_prd"
1294
1301
 
1302
+ # Persist the raw one-liner so the proof-of-run can show the actual brief
1303
+ # the user typed ("Built and verified from: <brief>") instead of falling
1304
+ # back to "codebase-analysis / No brief recorded". The synthesized PRD is
1305
+ # kept distinct (brief-prd-$$.md); the raw brief is the stronger, more
1306
+ # honest shareable artifact. proof-generator.py::_collect_spec reads this
1307
+ # as a fallback when no PRD_PATH / generated-prd.md is present.
1308
+ mkdir -p "$LOKI_DIR/state" 2>/dev/null || true
1309
+ printf '%s' "$brief_text" > "$LOKI_DIR/state/brief.txt" 2>/dev/null || true
1310
+
1295
1311
  # Apply the shared lightweight profile and flag the TTFV first-run path.
1296
1312
  # The signal value ("brief") drives the end-of-run wording in run.sh so
1297
1313
  # the message matches what actually ran (lightweight, council off).
@@ -25755,19 +25771,38 @@ _loki_gist_upload() {
25755
25771
  local desc="$2"
25756
25772
  local visibility="$3"
25757
25773
 
25758
- local gist_url
25759
- gist_url=$(gh gist create "$file" --desc "$desc" $visibility 2>&1)
25760
- local gist_exit=$?
25774
+ # gh prints progress ("- Creating gist...", "Created public gist...") on
25775
+ # STDERR and the final gist URL on STDOUT. Capture them separately so the
25776
+ # URL we expose and print is clean: folding stderr into stdout pollutes the
25777
+ # "Shared:" line and the ready-to-post hook with multi-line gh chatter.
25778
+ local gist_out gist_err gist_exit
25779
+ local errfile
25780
+ # Fall back to /dev/null if mktemp fails (disk full / unwritable /tmp) so the
25781
+ # redirect target is always a real path -- never an empty string (2>"" is a
25782
+ # bash error). We lose the captured stderr text in that rare case, not the
25783
+ # clean URL parse.
25784
+ errfile=$(mktemp "/tmp/loki-gist-err-XXXXXX" 2>/dev/null) || errfile=/dev/null
25785
+ gist_out=$(gh gist create "$file" --desc "$desc" $visibility 2>"$errfile")
25786
+ gist_exit=$?
25787
+ gist_err=$(cat "$errfile" 2>/dev/null)
25788
+ [ "$errfile" != "/dev/null" ] && rm -f "$errfile"
25761
25789
 
25762
25790
  # Cleanup temp file
25763
25791
  rm -f "$file"
25764
25792
 
25765
25793
  if [ $gist_exit -ne 0 ]; then
25766
25794
  echo -e "${RED}Failed to create gist${NC}"
25767
- echo "$gist_url"
25795
+ [ -n "$gist_err" ] && echo "$gist_err"
25796
+ [ -n "$gist_out" ] && echo "$gist_out"
25768
25797
  exit 1
25769
25798
  fi
25770
25799
 
25800
+ # The URL is the gist permalink on stdout. Extract just the URL line in
25801
+ # case any non-URL text slips into stdout on some gh versions.
25802
+ local gist_url
25803
+ gist_url=$(printf '%s\n' "$gist_out" | grep -Eo 'https://gist\.github\.com/[^[:space:]]+' | head -1)
25804
+ [ -z "$gist_url" ] && gist_url="$gist_out"
25805
+
25771
25806
  # Expose the URL to callers (e.g. cmd_proof prints a ready-to-post hook
25772
25807
  # after this returns). The shared "Shared:" line stays unchanged.
25773
25808
  LOKI_LAST_GIST_URL="$gist_url"
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.17.0"
10
+ __version__ = "7.17.1"
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.17.0
5
+ **Version:** v7.17.1
6
6
 
7
7
  ---
8
8
 
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var I7=Object.defineProperty;var P7=($)=>$;function L7($,Q){this[$]=P7.bind(null,Q)}var g=($,Q)=>{for(var K in Q)I7($,K,{get:Q[K],enumerable:!0,configurable:!0,set:L7.bind(Q,K)})};var k=($,Q)=>()=>($&&(Q=$($=0)),Q);var z1=import.meta.require;var w$={};g(w$,{lokiDir:()=>L,homeLokiDir:()=>c1,findRepoRootForVersion:()=>u1,REPO_ROOT:()=>p});import{resolve as o,dirname as f1}from"path";import{fileURLToPath as k7}from"url";import{existsSync as P1}from"fs";import{homedir as j7}from"os";function F7(){let $=A$;for(let Q=0;Q<6;Q++){if(P1(o($,"VERSION"))&&P1(o($,"autonomy/run.sh")))return $;let K=f1($);if(K===$)break;$=K}return o(A$,"..","..","..")}function u1($){let Q=$;for(let K=0;K<6;K++){if(P1(o(Q,"VERSION"))&&P1(o(Q,"autonomy/run.sh")))return Q;let Z=f1(Q);if(Z===Q)break;Q=Z}return o($,"..","..","..")}function L(){return process.env.LOKI_DIR??o(process.cwd(),".loki")}function c1(){return o(j7(),".loki")}var A$,p;var m=k(()=>{A$=f1(k7(import.meta.url));p=F7()});import{readFileSync as R7}from"fs";import{resolve as E7,dirname as S7}from"path";import{fileURLToPath as x7}from"url";function L1(){if($1!==null)return $1;let $="7.17.0";if(typeof $==="string"&&$.length>0)return $1=$,$1;try{let Q=S7(x7(import.meta.url)),K=u1(Q);$1=R7(E7(K,"VERSION"),"utf-8").trim()}catch{$1="unknown"}return $1}var $1=null;var p1=k(()=>{m()});var P$={};g(P$,{runOrThrow:()=>N7,run:()=>F,commandVersion:()=>D7,commandExists:()=>v,ShellError:()=>l1});async function F($,Q={}){let K=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),Z,z;if(Q.timeoutMs&&Q.timeoutMs>0)Z=setTimeout(()=>{try{K.kill("SIGTERM")}catch{}z=setTimeout(()=>{try{K.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[U,X,W]=await Promise.all([new Response(K.stdout).text(),new Response(K.stderr).text(),K.exited]);return{stdout:U,stderr:X,exitCode:W}}finally{if(Z)clearTimeout(Z);if(z)clearTimeout(z)}}async function N7($,Q={}){let K=await F($,Q);if(K.exitCode!==0)throw new l1(`command failed (${K.exitCode}): ${$.join(" ")}`,K.exitCode,K.stdout,K.stderr);return K}async function v($){let Q=C7($),K=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(K.exitCode===0)return K.stdout.trim()||null;return null}function C7($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function D7($,Q="--version"){if(!await v($))return null;let Z=await F([$,Q],{timeoutMs:5000});if(Z.exitCode!==0)return null;return((Z.stdout||Z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var l1;var n=k(()=>{l1=class l1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,K,Z){super($);this.message=$;this.exitCode=Q;this.stdout=K;this.stderr=Z;this.name="ShellError"}}});function a($){return h7?"":$}var h7,O,C,I,I3,A,x,h,H;var l=k(()=>{h7=(process.env.NO_COLOR??"").length>0;O=a("\x1B[0;31m"),C=a("\x1B[0;32m"),I=a("\x1B[1;33m"),I3=a("\x1B[0;34m"),A=a("\x1B[0;36m"),x=a("\x1B[1m"),h=a("\x1B[2m"),H=a("\x1B[0m")});import{existsSync as l7}from"fs";async function X1(){if(G1!==void 0)return G1;let $="/opt/homebrew/bin/python3.12";if(l7($))return G1=$,$;let Q=await v("python3.12");if(Q)return G1=Q,Q;let K=await v("python3");return G1=K,K}async function Q1($,Q={}){let K=await X1();if(!K)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([K,"-c",$],Q)}var G1;var B1=k(()=>{n()});var D$={};g(D$,{runStatus:()=>z8});import{existsSync as b,readFileSync as H1,readdirSync as R$,statSync as E$}from"fs";import{resolve as N,basename as s7}from"path";import{homedir as r7}from"os";async function i7(){if(await v("jq"))return!0;return process.stdout.write(`${O}Error: jq is required but not installed.${H}
2
+ var I7=Object.defineProperty;var P7=($)=>$;function L7($,Q){this[$]=P7.bind(null,Q)}var g=($,Q)=>{for(var K in Q)I7($,K,{get:Q[K],enumerable:!0,configurable:!0,set:L7.bind(Q,K)})};var k=($,Q)=>()=>($&&(Q=$($=0)),Q);var z1=import.meta.require;var w$={};g(w$,{lokiDir:()=>L,homeLokiDir:()=>c1,findRepoRootForVersion:()=>u1,REPO_ROOT:()=>p});import{resolve as o,dirname as f1}from"path";import{fileURLToPath as k7}from"url";import{existsSync as P1}from"fs";import{homedir as j7}from"os";function F7(){let $=A$;for(let Q=0;Q<6;Q++){if(P1(o($,"VERSION"))&&P1(o($,"autonomy/run.sh")))return $;let K=f1($);if(K===$)break;$=K}return o(A$,"..","..","..")}function u1($){let Q=$;for(let K=0;K<6;K++){if(P1(o(Q,"VERSION"))&&P1(o(Q,"autonomy/run.sh")))return Q;let Z=f1(Q);if(Z===Q)break;Q=Z}return o($,"..","..","..")}function L(){return process.env.LOKI_DIR??o(process.cwd(),".loki")}function c1(){return o(j7(),".loki")}var A$,p;var m=k(()=>{A$=f1(k7(import.meta.url));p=F7()});import{readFileSync as R7}from"fs";import{resolve as E7,dirname as S7}from"path";import{fileURLToPath as x7}from"url";function L1(){if($1!==null)return $1;let $="7.17.1";if(typeof $==="string"&&$.length>0)return $1=$,$1;try{let Q=S7(x7(import.meta.url)),K=u1(Q);$1=R7(E7(K,"VERSION"),"utf-8").trim()}catch{$1="unknown"}return $1}var $1=null;var p1=k(()=>{m()});var P$={};g(P$,{runOrThrow:()=>N7,run:()=>F,commandVersion:()=>D7,commandExists:()=>v,ShellError:()=>l1});async function F($,Q={}){let K=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),Z,z;if(Q.timeoutMs&&Q.timeoutMs>0)Z=setTimeout(()=>{try{K.kill("SIGTERM")}catch{}z=setTimeout(()=>{try{K.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[U,X,W]=await Promise.all([new Response(K.stdout).text(),new Response(K.stderr).text(),K.exited]);return{stdout:U,stderr:X,exitCode:W}}finally{if(Z)clearTimeout(Z);if(z)clearTimeout(z)}}async function N7($,Q={}){let K=await F($,Q);if(K.exitCode!==0)throw new l1(`command failed (${K.exitCode}): ${$.join(" ")}`,K.exitCode,K.stdout,K.stderr);return K}async function v($){let Q=C7($),K=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(K.exitCode===0)return K.stdout.trim()||null;return null}function C7($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function D7($,Q="--version"){if(!await v($))return null;let Z=await F([$,Q],{timeoutMs:5000});if(Z.exitCode!==0)return null;return((Z.stdout||Z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var l1;var n=k(()=>{l1=class l1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,K,Z){super($);this.message=$;this.exitCode=Q;this.stdout=K;this.stderr=Z;this.name="ShellError"}}});function a($){return h7?"":$}var h7,O,C,I,I3,A,x,h,H;var l=k(()=>{h7=(process.env.NO_COLOR??"").length>0;O=a("\x1B[0;31m"),C=a("\x1B[0;32m"),I=a("\x1B[1;33m"),I3=a("\x1B[0;34m"),A=a("\x1B[0;36m"),x=a("\x1B[1m"),h=a("\x1B[2m"),H=a("\x1B[0m")});import{existsSync as l7}from"fs";async function X1(){if(G1!==void 0)return G1;let $="/opt/homebrew/bin/python3.12";if(l7($))return G1=$,$;let Q=await v("python3.12");if(Q)return G1=Q,Q;let K=await v("python3");return G1=K,K}async function Q1($,Q={}){let K=await X1();if(!K)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([K,"-c",$],Q)}var G1;var B1=k(()=>{n()});var D$={};g(D$,{runStatus:()=>z8});import{existsSync as b,readFileSync as H1,readdirSync as R$,statSync as E$}from"fs";import{resolve as N,basename as s7}from"path";import{homedir as r7}from"os";async function i7(){if(await v("jq"))return!0;return process.stdout.write(`${O}Error: jq is required but not installed.${H}
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)
@@ -748,4 +748,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
748
748
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
749
749
  `),process.stderr.write(w7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var H3=await U3(Bun.argv.slice(2));process.exit(H3);
750
750
 
751
- //# debugId=34E49D55E4F12E2764756E2164756E21
751
+ //# debugId=F5A8D252FFE962D764756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.17.0'
60
+ __version__ = '7.17.1'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "7.17.0",
3
+ "version": "7.17.1",
4
4
  "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 11 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
5
5
  "keywords": [
6
6
  "agent",