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 +2 -2
- package/VERSION +1 -1
- package/autonomy/run.sh +99 -12
- package/dashboard/__init__.py +1 -1
- package/dashboard/static/index.html +14 -0
- 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/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.
|
|
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.
|
|
325
|
+
**v7.4.17 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.4.
|
|
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
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
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
|
-
|
|
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:
|
package/dashboard/__init__.py
CHANGED
|
@@ -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>
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -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.
|
|
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=
|
|
342
|
+
//# debugId=097D97714E32434B64756E2164756E21
|
package/mcp/__init__.py
CHANGED