loki-mode 7.129.0 → 7.129.2
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 +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +49 -0
- package/autonomy/run.sh +66 -7
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +2 -2
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/skills/github-integration.md +57 -0
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.129.
|
|
6
|
+
# Loki Mode v7.129.2
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
408
408
|
|
|
409
409
|
---
|
|
410
410
|
|
|
411
|
-
**v7.129.
|
|
411
|
+
**v7.129.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.129.
|
|
1
|
+
7.129.2
|
package/autonomy/loki
CHANGED
|
@@ -2348,6 +2348,44 @@ cmd_start() {
|
|
|
2348
2348
|
fi
|
|
2349
2349
|
fi
|
|
2350
2350
|
|
|
2351
|
+
# Shared-checkout concurrency guard (client bug: N `loki start ... --detach`
|
|
2352
|
+
# in ONE working dir with a SHARED .loki/ collide on non-namespaced worktree
|
|
2353
|
+
# paths + branches, producing missing worktrees and log spam). Issue-mode
|
|
2354
|
+
# namespaces pids PER SESSION, so the global loki.pid check below never trips
|
|
2355
|
+
# for DIFFERENT issues -- there was no guard on concurrent runs per checkout.
|
|
2356
|
+
# Detect a LIVE sibling loki running in THIS checkout via per-run pid markers
|
|
2357
|
+
# (crash-safe: dead markers are pruned via kill -0, so no deadlock). A run
|
|
2358
|
+
# that isolates its state (a distinct LOKI_DIR, i.e. NOT the default .loki) is
|
|
2359
|
+
# exempt -- that IS the safe per-issue pattern. Escape hatch:
|
|
2360
|
+
# LOKI_ALLOW_SHARED_CHECKOUT=1.
|
|
2361
|
+
local _co_loki_dir="${LOKI_DIR:-.loki}"
|
|
2362
|
+
if [ "${LOKI_ALLOW_SHARED_CHECKOUT:-0}" != "1" ] \
|
|
2363
|
+
&& { [ -z "${LOKI_DIR:-}" ] || [ "$(cd "${LOKI_DIR}" 2>/dev/null && pwd)" = "$(cd .loki 2>/dev/null && pwd)" ]; }; then
|
|
2364
|
+
local _co_runs_dir="$_co_loki_dir/checkout-runs"
|
|
2365
|
+
mkdir -p "$_co_runs_dir" 2>/dev/null || true
|
|
2366
|
+
local _co_marker _co_pid _co_live=""
|
|
2367
|
+
for _co_marker in "$_co_runs_dir"/*; do
|
|
2368
|
+
[ -f "$_co_marker" ] || continue
|
|
2369
|
+
_co_pid=$(cat "$_co_marker" 2>/dev/null | tr -dc '0-9')
|
|
2370
|
+
if [ -n "$_co_pid" ] && [ "$_co_pid" != "$$" ] && kill -0 "$_co_pid" 2>/dev/null; then
|
|
2371
|
+
_co_live="$_co_pid"; break
|
|
2372
|
+
fi
|
|
2373
|
+
# Prune a dead marker.
|
|
2374
|
+
[ -n "$_co_pid" ] && rm -f "$_co_marker" 2>/dev/null || true
|
|
2375
|
+
done
|
|
2376
|
+
if [ -n "$_co_live" ]; then
|
|
2377
|
+
echo -e "${RED}Error: another loki run (pid $_co_live) is active in this checkout ($(pwd)).${NC}" >&2
|
|
2378
|
+
echo -e "${YELLOW}Running several issues at once from ONE checkout + shared .loki/ collides on worktrees and branches.${NC}" >&2
|
|
2379
|
+
echo -e "${YELLOW}Give each issue its own worktree AND state dir, e.g.:${NC}" >&2
|
|
2380
|
+
echo -e "${YELLOW} git worktree add ../repo-issue-N -b loki/issue-N && (cd ../repo-issue-N && LOKI_DIR=\"\$PWD/.loki\" loki start owner/repo#N --pr --detach)${NC}" >&2
|
|
2381
|
+
echo -e "${YELLOW}See 'skills/github-integration.md' (Multiple issues at once). Override with LOKI_ALLOW_SHARED_CHECKOUT=1.${NC}" >&2
|
|
2382
|
+
exit 1
|
|
2383
|
+
fi
|
|
2384
|
+
# Register our own marker for the duration of this run (best-effort;
|
|
2385
|
+
# pruned by the next start if we die, and by 'loki stop').
|
|
2386
|
+
echo "$$" > "$_co_runs_dir/$$" 2>/dev/null || true
|
|
2387
|
+
fi
|
|
2388
|
+
|
|
2351
2389
|
# v7.5.12 Gap B: Stale-PID detection. Hard-kill (Ctrl+C followed by SIGKILL or
|
|
2352
2390
|
# `loki stop`) can leave .loki/loki.pid + .loki/session.lock orphaned. The
|
|
2353
2391
|
# next `loki start` then refuses to launch -- or worse, run.sh's downstream
|
|
@@ -8019,6 +8057,16 @@ fi
|
|
|
8019
8057
|
INNER_SCRIPT_EOF
|
|
8020
8058
|
chmod +x "$run_script"
|
|
8021
8059
|
|
|
8060
|
+
# Propagate the nested-agent signal into the detached run. CLAUDECODE (set
|
|
8061
|
+
# by Claude Code / the Loki Mode skill) is NOT in the curated env list the
|
|
8062
|
+
# inner script inherits, so run.sh's nested guard would never see it once
|
|
8063
|
+
# detached. Forward it as LOKI_NESTED_AGENT (which run.sh honors) so the
|
|
8064
|
+
# testing/docs worktree sub-streams stay OFF when nested even after the
|
|
8065
|
+
# nohup fork. Already-set LOKI_NESTED_AGENT wins.
|
|
8066
|
+
local _loki_nested_pass="${LOKI_NESTED_AGENT:-}"
|
|
8067
|
+
if [ -z "$_loki_nested_pass" ] && [ -n "${CLAUDECODE:-}" ]; then
|
|
8068
|
+
_loki_nested_pass=1
|
|
8069
|
+
fi
|
|
8022
8070
|
# Pass all variables safely via environment
|
|
8023
8071
|
LOKI_RUN_DIR="$(pwd)" \
|
|
8024
8072
|
LOKI_CMD="$loki_cmd" \
|
|
@@ -8031,6 +8079,7 @@ INNER_SCRIPT_EOF
|
|
|
8031
8079
|
LOKI_AUTO_MERGE="$auto_merge" \
|
|
8032
8080
|
LOKI_PR_TITLE="${title:-Implementation for issue ${issue_ref}}" \
|
|
8033
8081
|
LOKI_ISSUE_NUMBER="${number:-}" \
|
|
8082
|
+
LOKI_NESTED_AGENT="$_loki_nested_pass" \
|
|
8034
8083
|
nohup bash "$run_script" > "$log_file" 2>&1 &
|
|
8035
8084
|
|
|
8036
8085
|
local bg_pid=$!
|
package/autonomy/run.sh
CHANGED
|
@@ -817,8 +817,27 @@ fi
|
|
|
817
817
|
PARALLEL_MODE=${LOKI_PARALLEL_MODE:-false}
|
|
818
818
|
MAX_WORKTREES=${LOKI_MAX_WORKTREES:-5}
|
|
819
819
|
MAX_PARALLEL_SESSIONS=${LOKI_MAX_PARALLEL_SESSIONS:-3}
|
|
820
|
-
|
|
821
|
-
|
|
820
|
+
# Nested-agent guard: the fixed testing/docs sub-streams each spawn a FURTHER
|
|
821
|
+
# nested `claude` session in a NON-namespaced worktree (`<project>-testing`,
|
|
822
|
+
# `<project>-docs`). When Loki itself runs inside another agent session (Claude
|
|
823
|
+
# Code sets CLAUDECODE; the Loki Mode skill, or a user driving the CLI as a
|
|
824
|
+
# background process from an agent), those nested spawns fight over the shared
|
|
825
|
+
# checkout/worktree paths and flood the log -- the reported "N issues in one dir"
|
|
826
|
+
# failure. So default the sub-streams OFF when nested; the core issue work still
|
|
827
|
+
# runs. Explicit LOKI_PARALLEL_TESTING/DOCS=true overrides (opt back in). This
|
|
828
|
+
# does NOT disable the user's own --parallel/--pr issue work, only the auxiliary
|
|
829
|
+
# testing/docs fan-out that is unsafe to nest.
|
|
830
|
+
_loki_nested_agent=false
|
|
831
|
+
if [ -n "${CLAUDECODE:-}" ] || [ -n "${LOKI_NESTED_AGENT:-}" ]; then
|
|
832
|
+
_loki_nested_agent=true
|
|
833
|
+
fi
|
|
834
|
+
if [ "$_loki_nested_agent" = "true" ]; then
|
|
835
|
+
PARALLEL_TESTING=${LOKI_PARALLEL_TESTING:-false}
|
|
836
|
+
PARALLEL_DOCS=${LOKI_PARALLEL_DOCS:-false}
|
|
837
|
+
else
|
|
838
|
+
PARALLEL_TESTING=${LOKI_PARALLEL_TESTING:-true}
|
|
839
|
+
PARALLEL_DOCS=${LOKI_PARALLEL_DOCS:-true}
|
|
840
|
+
fi
|
|
822
841
|
|
|
823
842
|
# Dynamic resource-aware session concurrency (Release 3, slice 3).
|
|
824
843
|
# DEFAULT OFF: when LOKI_DYNAMIC_CONCURRENCY is unset, effective_session_cap()
|
|
@@ -4423,6 +4442,7 @@ process_pending_merges() {
|
|
|
4423
4442
|
local failed=0
|
|
4424
4443
|
|
|
4425
4444
|
for signal_file in "$signals_dir"/MERGE_REQUESTED_*; do
|
|
4445
|
+
case "$signal_file" in *.ack) continue ;; esac
|
|
4426
4446
|
[ -f "$signal_file" ] || continue
|
|
4427
4447
|
local stream_name
|
|
4428
4448
|
stream_name=$(basename "$signal_file" | sed 's/MERGE_REQUESTED_//')
|
|
@@ -4465,12 +4485,28 @@ check_merge_queue() {
|
|
|
4465
4485
|
fi
|
|
4466
4486
|
|
|
4467
4487
|
for signal in "$signals_dir"/MERGE_REQUESTED_*; do
|
|
4488
|
+
# Skip our own acknowledgement markers (*.ack) so they are not treated
|
|
4489
|
+
# as signals themselves.
|
|
4490
|
+
case "$signal" in *.ack) continue ;; esac
|
|
4468
4491
|
if [ -f "$signal" ]; then
|
|
4469
4492
|
local feature=$(basename "$signal" | sed 's/MERGE_REQUESTED_//')
|
|
4470
|
-
log_info "Merge requested: $feature"
|
|
4471
4493
|
|
|
4472
4494
|
if [ "$AUTO_MERGE" = "true" ]; then
|
|
4495
|
+
# Auto-merge consumes the signal (merge_feature rm's it), so the
|
|
4496
|
+
# log line fires exactly once per completed feature.
|
|
4497
|
+
log_info "Merge requested: $feature"
|
|
4473
4498
|
merge_feature "$feature"
|
|
4499
|
+
else
|
|
4500
|
+
# Auto-merge is OFF (e.g. `--pr` without `--ship`): the PR is left
|
|
4501
|
+
# for a human to merge, so the signal is INTENTIONALLY not consumed.
|
|
4502
|
+
# Log ONCE (guarded by a sibling .ack marker) instead of re-logging
|
|
4503
|
+
# the same "Merge requested" line every orchestrator pass -- that
|
|
4504
|
+
# re-log was a 90-minute "looks stuck" spam while real work was
|
|
4505
|
+
# already done. The signal itself is preserved untouched.
|
|
4506
|
+
if [ ! -f "${signal}.ack" ]; then
|
|
4507
|
+
log_info "Merge requested: $feature (PR ready; auto-merge off -- merge the PR, or use --ship to auto-merge)"
|
|
4508
|
+
: > "${signal}.ack" 2>/dev/null || true
|
|
4509
|
+
fi
|
|
4474
4510
|
fi
|
|
4475
4511
|
fi
|
|
4476
4512
|
done
|
|
@@ -4585,8 +4621,9 @@ merge_feature() {
|
|
|
4585
4621
|
fi
|
|
4586
4622
|
fi
|
|
4587
4623
|
|
|
4588
|
-
# Remove signal
|
|
4589
|
-
rm -f "$TARGET_DIR/.loki/signals/MERGE_REQUESTED_$feature"
|
|
4624
|
+
# Remove signal (and its log-once .ack marker, if any)
|
|
4625
|
+
rm -f "$TARGET_DIR/.loki/signals/MERGE_REQUESTED_$feature" \
|
|
4626
|
+
"$TARGET_DIR/.loki/signals/MERGE_REQUESTED_$feature.ack"
|
|
4590
4627
|
|
|
4591
4628
|
# Remove worktree
|
|
4592
4629
|
remove_worktree "feature-$clean_feature"
|
|
@@ -10303,8 +10340,30 @@ auto_generate_docs_if_needed() {
|
|
|
10303
10340
|
# Synchronous so docs exist before the gate scores. Provider-agnostic:
|
|
10304
10341
|
# 'loki docs generate' picks the run's provider and falls back to
|
|
10305
10342
|
# template-based docs when no provider CLI is available.
|
|
10306
|
-
|
|
10307
|
-
|
|
10343
|
+
#
|
|
10344
|
+
# BOUNDED: 'loki docs generate' spawns a provider (claude) call that can hang
|
|
10345
|
+
# with no cap. This runs AFTER completion, before the detached --pr push/PR
|
|
10346
|
+
# step, so a hang here silently blocks the whole PR from ever being created
|
|
10347
|
+
# (observed: a verified build stuck ~55 min in this step, work committed but
|
|
10348
|
+
# never pushed). Wrap it in a timeout; docs are non-gating, so on timeout we
|
|
10349
|
+
# warn and continue to the gate (which scores whatever docs exist).
|
|
10350
|
+
local _doc_to="${LOKI_DOCS_TIMEOUT:-${LOKI_GATE_TIMEOUT:-300}}"
|
|
10351
|
+
local _doc_cmd=()
|
|
10352
|
+
if command -v gtimeout >/dev/null 2>&1; then
|
|
10353
|
+
_doc_cmd=(gtimeout "${_doc_to}s")
|
|
10354
|
+
elif command -v timeout >/dev/null 2>&1; then
|
|
10355
|
+
_doc_cmd=(timeout "${_doc_to}s")
|
|
10356
|
+
fi
|
|
10357
|
+
if "${_doc_cmd[@]}" "$loki_bin" docs generate "$project_dir" >/dev/null 2>&1; then
|
|
10358
|
+
:
|
|
10359
|
+
else
|
|
10360
|
+
local _doc_rc=$?
|
|
10361
|
+
if [ "$_doc_rc" -eq 124 ]; then
|
|
10362
|
+
log_warn "Auto-documentation: timed out after ${_doc_to}s (gate will score on what exists); continuing"
|
|
10363
|
+
else
|
|
10364
|
+
log_warn "Auto-documentation: generation did not complete (gate will score on what exists)"
|
|
10365
|
+
fi
|
|
10366
|
+
fi
|
|
10308
10367
|
}
|
|
10309
10368
|
|
|
10310
10369
|
# ============================================================================
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
|
|
4
4
|
|
|
5
|
-
**Version:** v7.129.
|
|
5
|
+
**Version:** v7.129.2
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -396,7 +396,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
|
|
|
396
396
|
# Run Loki Mode in Docker (Claude provider, API-key auth)
|
|
397
397
|
docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
|
|
398
398
|
-v $(pwd):/workspace -w /workspace \
|
|
399
|
-
asklokesh/loki-mode:7.129.
|
|
399
|
+
asklokesh/loki-mode:7.129.2 start ./my-spec.md
|
|
400
400
|
```
|
|
401
401
|
|
|
402
402
|
##### docker compose + .env (no host install)
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.129.
|
|
2
|
+
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.129.2";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function R($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([c1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}Error: jq is required but not installed.${J}
|
|
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)
|
|
@@ -814,4 +814,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
814
814
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
815
815
|
`),process.stderr.write(K8),2}}e1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var SZ=await NZ(Bun.argv.slice(2));process.exit(SZ);
|
|
816
816
|
|
|
817
|
-
//# debugId=
|
|
817
|
+
//# debugId=11A0089D1663FA1464756E2164756E21
|
package/mcp/__init__.py
CHANGED
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": "7.129.
|
|
4
|
+
"version": "7.129.2",
|
|
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": "7.129.
|
|
5
|
+
"version": "7.129.2",
|
|
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",
|
|
@@ -171,6 +171,63 @@ LOKI_GITHUB_IMPORT=true \
|
|
|
171
171
|
|
|
172
172
|
---
|
|
173
173
|
|
|
174
|
+
## Multiple issues at once (parallel, reliably)
|
|
175
|
+
|
|
176
|
+
There are two safe ways to work several issues; pick by whether you want them
|
|
177
|
+
concurrent.
|
|
178
|
+
|
|
179
|
+
### Sequential (simplest, one process, one checkout)
|
|
180
|
+
|
|
181
|
+
Import all open issues into one queue and let a single run drain them one at a
|
|
182
|
+
time. No worktree/state collisions because there is only one run.
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
LOKI_GITHUB_IMPORT=true loki start # drains .loki/queue sequentially
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Concurrent (one isolated run PER issue)
|
|
189
|
+
|
|
190
|
+
To run N issues at the SAME time, each run needs its OWN git worktree AND its
|
|
191
|
+
OWN state dir. `--pr`/`--worktree` isolates the git branch, but two runs in the
|
|
192
|
+
same checkout still share one `.loki/` unless you also set a distinct `LOKI_DIR`.
|
|
193
|
+
Give each issue its own checkout + `LOKI_DIR`:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
for n in 101 102 103; do
|
|
197
|
+
git worktree add "../repo-issue-$n" -b "loki/issue-$n" >/dev/null
|
|
198
|
+
( cd "../repo-issue-$n" \
|
|
199
|
+
&& LOKI_DIR="$PWD/.loki" \
|
|
200
|
+
loki start "owner/repo#$n" --pr --detach )
|
|
201
|
+
done
|
|
202
|
+
loki status --all # watch all runs
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Each run is fully isolated: separate working tree, branch, and `.loki/` state, so
|
|
206
|
+
they cannot corrupt each other's branches or worktrees.
|
|
207
|
+
|
|
208
|
+
DO NOT run several `loki start ... --detach` from the SAME directory with a shared
|
|
209
|
+
`.loki/`. The runs collide on shared (non-namespaced) worktree paths and state,
|
|
210
|
+
which produces missing branches, "claimed worktree missing", and a repeating
|
|
211
|
+
`Merge requested: testing/docs` log (the auxiliary sub-streams, not your issues).
|
|
212
|
+
|
|
213
|
+
### Inside a Claude Code session (the Loki Mode skill)
|
|
214
|
+
|
|
215
|
+
When you invoke Loki Mode from inside Claude Code, Loki detects the nested agent
|
|
216
|
+
(`CLAUDECODE`) and automatically disables its internal `testing`/`docs`
|
|
217
|
+
worktree sub-streams (they would spawn further nested `claude` sessions that
|
|
218
|
+
collide) -- your issue work still runs. To fan out across several issues from a
|
|
219
|
+
Claude Code session, prefer the per-issue-isolated loop above (each with its own
|
|
220
|
+
worktree + `LOKI_DIR`), or let Claude Code orchestrate one Loki run per issue.
|
|
221
|
+
Re-enable the sub-streams explicitly with `LOKI_PARALLEL_TESTING=true` /
|
|
222
|
+
`LOKI_PARALLEL_DOCS=true` only if you know the checkout is not shared.
|
|
223
|
+
|
|
224
|
+
Notes:
|
|
225
|
+
- `--pr` leaves the PR for you to merge (auto-merge OFF). Use `--ship` to
|
|
226
|
+
auto-merge. Either way the "Merge requested" line now logs once, not on a loop.
|
|
227
|
+
- `loki status --all` and `loki stop --all` operate across all runs/projects.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
174
231
|
## CLI Commands
|
|
175
232
|
|
|
176
233
|
```bash
|