loki-mode 7.4.15 → 7.4.17

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 PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.4.15
6
+ # Loki Mode v7.4.17
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -322,4 +322,4 @@ The following features are documented in skill modules but not yet fully automat
322
322
  | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
323
323
  | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
324
324
 
325
- **v7.4.15 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
325
+ **v7.4.17 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.4.15
1
+ 7.4.17
package/autonomy/run.sh CHANGED
@@ -3049,7 +3049,16 @@ init_loki_dir() {
3049
3049
  fi
3050
3050
  fi
3051
3051
  if [ "$can_cleanup" = "true" ]; then
3052
+ # v7.4.16: extended stale-signal cleanup. Pre-v7.4.16 only
3053
+ # PAUSE / STOP / HUMAN_INPUT.md were cleaned -- but
3054
+ # PAUSE_AT_CHECKPOINT, PAUSED.md, and COMPLETED were added
3055
+ # to the signal-file family later without updating this
3056
+ # cleanup. A stale PAUSE_AT_CHECKPOINT from a prior session
3057
+ # (created by Ctrl+C in checkpoint mode) caused fresh
3058
+ # `loki start` to pause immediately when PRD-driven mode
3059
+ # auto-switched to checkpoint. User-reported regression.
3052
3060
  rm -f .loki/PAUSE .loki/STOP .loki/HUMAN_INPUT.md 2>/dev/null
3061
+ rm -f .loki/PAUSE_AT_CHECKPOINT .loki/PAUSED.md .loki/COMPLETED 2>/dev/null
3053
3062
  rm -f .loki/loki.pid 2>/dev/null
3054
3063
  rm -f .loki/session.lock 2>/dev/null
3055
3064
  fi
@@ -5786,17 +5795,33 @@ enforce_test_coverage() {
5786
5795
  fi
5787
5796
  fi
5788
5797
 
5789
- # Python
5798
+ # Python.
5799
+ # v7.4.17: only fire pytest when there is actually a Python project
5800
+ # to test. Pre-v7.4.17 the gate fired on the mere existence of a
5801
+ # `tests/` directory -- which a JS-only project (e.g. `tests/foo.test.js`)
5802
+ # commonly has. pytest then collected 0 tests and the gate reported
5803
+ # FAILED, derailing the next iteration with a fake "fix the tests"
5804
+ # injection. User reported this exact regression in v7.4.15 quick mode.
5790
5805
  if [ "$test_runner" = "none" ]; then
5791
- if [ -f "${TARGET_DIR:-.}/setup.py" ] || [ -f "${TARGET_DIR:-.}/pyproject.toml" ] || \
5792
- [ -d "${TARGET_DIR:-.}/tests" ]; then
5793
- if command -v pytest &>/dev/null; then
5794
- test_runner="pytest"
5795
- local output
5796
- output=$(cd "${TARGET_DIR:-.}" && pytest --tb=short 2>&1) || test_passed=false
5797
- details="pytest: $(echo "$output" | tail -5 | tr '\n' ' ')"
5806
+ local has_python_project=false
5807
+ if [ -f "${TARGET_DIR:-.}/setup.py" ] || [ -f "${TARGET_DIR:-.}/pyproject.toml" ] \
5808
+ || [ -f "${TARGET_DIR:-.}/setup.cfg" ] || [ -f "${TARGET_DIR:-.}/pytest.ini" ] \
5809
+ || [ -f "${TARGET_DIR:-.}/conftest.py" ]; then
5810
+ has_python_project=true
5811
+ elif [ -d "${TARGET_DIR:-.}/tests" ]; then
5812
+ # Confirm tests/ actually has Python test files.
5813
+ if find "${TARGET_DIR:-.}/tests" -maxdepth 3 -type f \
5814
+ \( -name 'test_*.py' -o -name '*_test.py' -o -name 'conftest.py' \) \
5815
+ -print -quit 2>/dev/null | grep -q .; then
5816
+ has_python_project=true
5798
5817
  fi
5799
5818
  fi
5819
+ if [ "$has_python_project" = "true" ] && command -v pytest &>/dev/null; then
5820
+ test_runner="pytest"
5821
+ local output
5822
+ output=$(cd "${TARGET_DIR:-.}" && pytest --tb=short 2>&1) || test_passed=false
5823
+ details="pytest: $(echo "$output" | tail -5 | tr '\n' ' ')"
5824
+ fi
5800
5825
  fi
5801
5826
 
5802
5827
  # Go
@@ -8032,9 +8057,40 @@ except Exception:
8032
8057
  # structured completion claim. When the signal exists, we read it, log the
8033
8058
  # structured event, and consume (remove) the file. Returns 0 on detection.
8034
8059
  #
8060
+ # v7.4.17: also accepts a file-based fallback at .loki/signals/
8061
+ # COMPLETION_REQUESTED -- the LLM can `touch` this file directly when the
8062
+ # MCP tool isn't surfaced in its environment (e.g., harness limitations,
8063
+ # Codex CLI, Gemini CLI). User reproduction: the LLM said "the
8064
+ # loki_complete_task MCP tool isn't loaded in this environment" and
8065
+ # tried to signal completion via state files; we now honor that.
8066
+ #
8035
8067
  # Output on stdout: the JSON payload (for callers that want to log it).
8036
8068
  check_task_completion_signal() {
8037
8069
  local signal_file=".loki/signals/TASK_COMPLETION_CLAIMED"
8070
+ local fallback_file=".loki/signals/COMPLETION_REQUESTED"
8071
+
8072
+ # Prefer the structured MCP-tool signal if present.
8073
+ if [ ! -f "$signal_file" ] && [ -f "$fallback_file" ]; then
8074
+ # Fallback path: synthesize a minimal payload from the optional
8075
+ # contents of COMPLETION_REQUESTED (LLM may have written a
8076
+ # statement; if not, use a generic one).
8077
+ local fb_content
8078
+ fb_content=$(cat "$fallback_file" 2>/dev/null || echo "")
8079
+ local fb_statement="${fb_content:-All PRD requirements implemented and tests passing}"
8080
+ # Build minimal JSON payload
8081
+ signal_file="$fallback_file"
8082
+ # Write the synthesized payload back into the signal file so the
8083
+ # rest of this function can read it uniformly.
8084
+ python3 -c "
8085
+ import json, sys
8086
+ print(json.dumps({
8087
+ 'statement': sys.argv[1][:1000],
8088
+ 'evidence': 'file-based completion via COMPLETION_REQUESTED fallback',
8089
+ 'confidence': 'medium',
8090
+ 'source': 'completion_requested_file_fallback'
8091
+ }))" "$fb_statement" > "$fallback_file" 2>/dev/null || echo '{}' > "$fallback_file"
8092
+ fi
8093
+
8038
8094
  if [ ! -f "$signal_file" ]; then
8039
8095
  return 1
8040
8096
  fi
@@ -8938,10 +8994,16 @@ build_prompt() {
8938
8994
  # (defined in mcp/server.py) with completion_statement + evidence fields,
8939
8995
  # instead of emitting a prose completion string.
8940
8996
  local completion_instruction=""
8997
+ # v7.4.17: explicit fallback path. The loki_complete_task MCP tool is
8998
+ # not always surfaced in the LLM's environment (Codex CLI, Gemini CLI,
8999
+ # certain Claude Code harness configs). When unavailable, the LLM
9000
+ # should `touch .loki/signals/COMPLETION_REQUESTED` instead -- the
9001
+ # runner's check_task_completion_signal honors that file as a
9002
+ # synthesized completion claim with confidence=medium.
8941
9003
  if [ -n "$COMPLETION_PROMISE" ]; then
8942
- completion_instruction="COMPLETION_PROMISE: [$COMPLETION_PROMISE]. When all PRD requirements are implemented, tests pass, and the PRD checklist is at or near 100%, invoke the loki_complete_task MCP tool with your completion_statement and evidence (cite tests that passed, checklist items verified, files created/modified). Do NOT emit a completion string in prose -- use the tool call."
9004
+ completion_instruction="COMPLETION_PROMISE: [$COMPLETION_PROMISE]. When all PRD requirements are implemented, tests pass, and the PRD checklist is at or near 100%, invoke the loki_complete_task MCP tool with your completion_statement and evidence (cite tests that passed, checklist items verified, files created/modified). Do NOT emit a completion string in prose -- use the tool call. FALLBACK: if the loki_complete_task tool is not available in your environment, instead run \`touch .loki/signals/COMPLETION_REQUESTED\` (optionally write a one-line statement to that file via \`echo 'statement' > .loki/signals/COMPLETION_REQUESTED\`); the runner detects this file and treats it as a completion claim."
8943
9005
  else
8944
- completion_instruction="NO COMPLETION PROMISE SET. Continue finding improvements. The Completion Council will evaluate your progress periodically. Iteration $iteration of max $MAX_ITERATIONS. If you do decide the task is complete, invoke the loki_complete_task MCP tool with a structured statement and evidence rather than emitting prose."
9006
+ completion_instruction="NO COMPLETION PROMISE SET. Continue finding improvements. The Completion Council will evaluate your progress periodically. Iteration $iteration of max $MAX_ITERATIONS. If you do decide the task is complete, invoke the loki_complete_task MCP tool with a structured statement and evidence rather than emitting prose. FALLBACK if that tool is unavailable: \`touch .loki/signals/COMPLETION_REQUESTED\`."
8945
9007
  fi
8946
9008
 
8947
9009
  # Core autonomous instructions - NO questions, NO waiting, NEVER say done
@@ -10620,11 +10682,36 @@ def process_stream():
10620
10682
  save_agents()
10621
10683
  print(f"\n{MAGENTA}[Agent Spawned: {agent_type}]{NC} {description}", flush=True)
10622
10684
 
10623
- # Track TodoWrite for task updates
10685
+ # Track TodoWrite for task updates.
10686
+ # v7.4.17: enrich the entry so the dashboard task-detail
10687
+ # modal has more than a one-liner. TodoWrite items are
10688
+ # internal LLM scratch (not PRD-derived work) so they
10689
+ # do not have acceptance_criteria or user stories, but
10690
+ # we surface the activeForm and a source tag.
10691
+ # Note: this whole block is inside a python3 -u -c
10692
+ # single-quoted shell string -- avoid apostrophes.
10624
10693
  elif tool == "TodoWrite":
10625
10694
  todos = tool_input.get("todos", [])
10626
10695
  in_progress = [t for t in todos if t.get("status") == "in_progress"]
10627
- save_in_progress([{"id": f"todo-{i}", "type": "todo", "payload": {"action": t.get("content", "")}} for i, t in enumerate(in_progress)])
10696
+ enriched = []
10697
+ for i, t in enumerate(in_progress):
10698
+ content = t.get("content", "")
10699
+ active_form = t.get("activeForm", "") or content
10700
+ enriched.append({
10701
+ "id": f"todo-{i}",
10702
+ "type": "todo",
10703
+ "title": content,
10704
+ "description": (
10705
+ "Internal task tracked by the agent TodoWrite tool. "
10706
+ "This is LLM scratch, not a PRD-derived work item, so "
10707
+ "it has no acceptance criteria or user story. "
10708
+ "Active form: " + active_form
10709
+ ),
10710
+ "source": "claude_code_todowrite",
10711
+ "priority": "medium",
10712
+ "payload": {"action": content, "activeForm": active_form},
10713
+ })
10714
+ save_in_progress(enriched)
10628
10715
  print(f"\n{CYAN}[Tool: {tool}]{NC} {len(todos)} items", flush=True)
10629
10716
 
10630
10717
  else:
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.4.15"
10
+ __version__ = "7.4.17"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -1539,6 +1539,20 @@ var LokiDashboard=(()=>{var xt=Object.defineProperty;var Qt=Object.getOwnPropert
1539
1539
  </div>
1540
1540
  `:""}
1541
1541
 
1542
+ ${t.user_story?`
1543
+ <div class="modal-section">
1544
+ <h3 class="modal-section-title">User Story</h3>
1545
+ <div class="modal-prose">${this._escapeHtml(t.user_story)}</div>
1546
+ </div>
1547
+ `:""}
1548
+
1549
+ ${t.source?`
1550
+ <div class="modal-section">
1551
+ <h3 class="modal-section-title">Source</h3>
1552
+ <div class="modal-prose"><code>${this._escapeHtml(t.source)}</code></div>
1553
+ </div>
1554
+ `:""}
1555
+
1542
1556
  ${t.type?`
1543
1557
  <div class="modal-footer">
1544
1558
  <span class="task-type">${this._escapeHtml(t.type)}</span>
@@ -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.4.15
5
+ **Version:** v7.4.17
6
6
 
7
7
  ---
8
8
 
@@ -94,7 +94,7 @@ loki setup-skill
94
94
  ## PyPI / Python SDK
95
95
 
96
96
  **The `loki` CLI is NOT available via `pip install loki-mode`.** PyPI hosts only the
97
- Python REST client SDK at `loki-mode-sdk` (v7.4.15+). The dashboard, MCP server,
97
+ Python REST client SDK at `loki-mode-sdk` (v7.4.17+). The dashboard, MCP server,
98
98
  and orchestrator components ship via npm, Docker, and Homebrew only.
99
99
 
100
100
  ```bash
@@ -263,7 +263,7 @@ Start a session with: loki start <prd>`}}let Z=m0(X);return{exitCode:0,stdout:Q?
263
263
  `),0}async function $3(z){let Q=!1;for(let $ of z)if($==="--json")Q=!0;else if($==="--help"||$==="-h")return e0(),0;else return process.stderr.write(`${S}Unknown option: ${$}${K}
264
264
  `),process.stderr.write(`Usage: loki doctor [--json]
265
265
  `),1;if(Q){let $=await i1();return process.stdout.write(JSON.stringify($,null,2)+`
266
- `),0}return z3()}var r0,s0,i0;var z0=g(()=>{m();Q1();p();r0=/(\d+\.\d+(?:\.\d+)*)/;s0=[{name:"Claude Code",dir:".claude/skills/loki-mode"},{name:"Codex CLI",dir:".codex/skills/loki-mode"},{name:"Gemini CLI",dir:".gemini/skills/loki-mode"},{name:"Cline CLI",dir:".cline/skills/loki-mode"},{name:"Aider CLI",dir:".aider/skills/loki-mode"}];i0=[{displayName:"Node.js (>= 18)",jsonName:"Node.js",cmd:"node",required:"required",min:"18.0"},{displayName:"Python 3 (>= 3.8)",jsonName:"Python 3",cmd:"python3",required:"required",min:"3.8"},{displayName:"jq",jsonName:"jq",cmd:"jq",required:"required"},{displayName:"git",jsonName:"git",cmd:"git",required:"required"},{displayName:"curl",jsonName:"curl",cmd:"curl",required:"required"},{displayName:"bash (>= 4.0)",jsonName:"bash",cmd:"bash",required:"recommended",min:"4.0"},{displayName:"Bun (>= 1.3)",jsonName:"Bun",cmd:"bun",required:"recommended",min:"1.3"},{displayName:"Claude CLI",jsonName:"Claude CLI",cmd:"claude",required:"optional"},{displayName:"Codex CLI",jsonName:"Codex CLI",cmd:"codex",required:"optional"},{displayName:"Gemini CLI",jsonName:"Gemini CLI",cmd:"gemini",required:"optional"},{displayName:"Cline CLI",jsonName:"Cline CLI",cmd:"cline",required:"optional"},{displayName:"Aider CLI",jsonName:"Aider CLI",cmd:"aider",required:"optional"}]});h();import{readFileSync as U0}from"fs";import{resolve as V0,dirname as q0}from"path";import{fileURLToPath as G0}from"url";var v=null;function k1(){if(v!==null)return v;let z="7.4.15";if(typeof z==="string"&&z.length>0)return v=z,v;try{let Q=q0(G0(import.meta.url)),$=U1(Q);v=U0(V0($,"VERSION"),"utf-8").trim()}catch{v="unknown"}return v}function R1(){return process.stdout.write(`Loki Mode v${k1()}
266
+ `),0}return z3()}var r0,s0,i0;var z0=g(()=>{m();Q1();p();r0=/(\d+\.\d+(?:\.\d+)*)/;s0=[{name:"Claude Code",dir:".claude/skills/loki-mode"},{name:"Codex CLI",dir:".codex/skills/loki-mode"},{name:"Gemini CLI",dir:".gemini/skills/loki-mode"},{name:"Cline CLI",dir:".cline/skills/loki-mode"},{name:"Aider CLI",dir:".aider/skills/loki-mode"}];i0=[{displayName:"Node.js (>= 18)",jsonName:"Node.js",cmd:"node",required:"required",min:"18.0"},{displayName:"Python 3 (>= 3.8)",jsonName:"Python 3",cmd:"python3",required:"required",min:"3.8"},{displayName:"jq",jsonName:"jq",cmd:"jq",required:"required"},{displayName:"git",jsonName:"git",cmd:"git",required:"required"},{displayName:"curl",jsonName:"curl",cmd:"curl",required:"required"},{displayName:"bash (>= 4.0)",jsonName:"bash",cmd:"bash",required:"recommended",min:"4.0"},{displayName:"Bun (>= 1.3)",jsonName:"Bun",cmd:"bun",required:"recommended",min:"1.3"},{displayName:"Claude CLI",jsonName:"Claude CLI",cmd:"claude",required:"optional"},{displayName:"Codex CLI",jsonName:"Codex CLI",cmd:"codex",required:"optional"},{displayName:"Gemini CLI",jsonName:"Gemini CLI",cmd:"gemini",required:"optional"},{displayName:"Cline CLI",jsonName:"Cline CLI",cmd:"cline",required:"optional"},{displayName:"Aider CLI",jsonName:"Aider CLI",cmd:"aider",required:"optional"}]});h();import{readFileSync as U0}from"fs";import{resolve as V0,dirname as q0}from"path";import{fileURLToPath as G0}from"url";var v=null;function k1(){if(v!==null)return v;let z="7.4.17";if(typeof z==="string"&&z.length>0)return v=z,v;try{let Q=q0(G0(import.meta.url)),$=U1(Q);v=U0(V0($,"VERSION"),"utf-8").trim()}catch{v="unknown"}return v}function R1(){return process.stdout.write(`Loki Mode v${k1()}
267
267
  `),0}m();p();h();import{readFileSync as A0,existsSync as O0}from"fs";import{resolve as _0}from"path";var T0=["claude","codex","gemini","cline","aider"];function D1(){let z=_0(b(),"state","provider");if(!O0(z))return"";try{return A0(z,"utf-8").trim()}catch{return""}}function I0(z,Q){return z||Q||process.env.LOKI_PROVIDER||"claude"}function w0(z){let Q=D1(),$=I0(z,Q);switch(process.stdout.write(`${x}Current Provider${K}
268
268
  `),process.stdout.write(`
269
269
  `),process.stdout.write(`${G}Provider:${K} ${$}
@@ -339,4 +339,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
339
339
  `;async function Q3(z){let Q=z[0],$=z.slice(1);switch(Q){case void 0:case"help":case"--help":case"-h":return process.stdout.write($0),0;case"version":case"--version":case"-v":return R1();case"provider":return E1($);case"memory":return N1($);case"status":{let{runStatus:X}=await Promise.resolve().then(() => (m1(),v1));return X($)}case"stats":{let{runStats:X}=await Promise.resolve().then(() => (d1(),l1));return X($)}case"doctor":{let{runDoctor:X}=await Promise.resolve().then(() => (z0(),e1));return X($)}default:return process.stderr.write(`Unknown command: ${Q}
340
340
  `),process.stderr.write($0),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var X3=await Q3(Bun.argv.slice(2));process.exit(X3);
341
341
 
342
- //# debugId=DCDC9080F2D734BC64756E2164756E21
342
+ //# debugId=097D97714E32434B64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.4.15'
60
+ __version__ = '7.4.17'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "7.4.15",
3
+ "version": "7.4.17",
4
4
  "description": "Loki Mode by Autonomi - Multi-agent autonomous startup system for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "keywords": [
6
6
  "agent",