loki-mode 7.5.17 → 7.5.27
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/README.md +10 -9
- package/SKILL.md +14 -14
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +26 -3
- package/autonomy/lib/claude-flags.sh +132 -0
- package/autonomy/lib/mcp-config.sh +160 -0
- package/autonomy/lib/project-graph.sh +675 -0
- package/autonomy/lib/voter-agents.sh +356 -0
- package/autonomy/loki +61 -96
- package/autonomy/run.sh +95 -186
- package/bin/loki +10 -0
- package/dashboard/__init__.py +1 -1
- package/dashboard/requirements.txt +13 -8
- package/dashboard/server.py +33 -15
- package/dashboard/static/index.html +298 -299
- package/docs/INSTALLATION.md +54 -21
- package/docs/retrospectives/v7.5.15-fleet-postmortem.md +325 -0
- package/docs/retrospectives/v7.5.15-honesty-audit.md +136 -0
- package/docs/retrospectives/v7.5.15-llm-failure-modes.md +49 -0
- package/loki-ts/data/finding-schema.json +74 -0
- package/loki-ts/data/model-pricing.json +12 -0
- package/loki-ts/dist/loki.js +109 -108
- package/mcp/__init__.py +1 -1
- package/mcp/lsp_proxy.py +713 -0
- package/mcp/requirements.txt +9 -3
- package/mcp/tests/__init__.py +0 -0
- package/mcp/tests/test_lsp_proxy.py +377 -0
- package/memory/app_graph.py +153 -0
- package/memory/storage.py +6 -1
- package/memory/tests/test_app_graph.py +134 -0
- package/package.json +4 -3
- package/providers/claude.sh +115 -4
- package/providers/codex.sh +2 -2
- package/providers/loader.sh +4 -4
- package/providers/model_catalog.json +0 -9
- package/providers/models.sh +1 -2
- package/references/multi-provider.md +26 -35
- package/references/prompt-repetition.md +1 -1
- package/references/quality-control.md +1 -1
- package/skills/00-index.md +3 -3
- package/skills/model-selection.md +11 -14
- package/skills/providers.md +17 -57
- package/skills/quality-gates.md +2 -2
- package/skills/troubleshooting.md +1 -1
- package/src/integrations/github/action-handler.js +3 -2
- package/src/protocols/tools/start-project.js +1 -1
- package/providers/gemini.sh +0 -343
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Tests for memory/app_graph.py + LOKI_MEMORY_BASE_PATH override.
|
|
2
|
+
|
|
3
|
+
Phase F cross-project context. Verifies:
|
|
4
|
+
- AppGraph.from_env returns None when LOKI_PROJECT_GRAPH_ROOT is unset
|
|
5
|
+
- AppGraph.from_env builds an instance when the three env vars are set
|
|
6
|
+
- MemoryStorage honors LOKI_MEMORY_BASE_PATH and writes under the shared dir
|
|
7
|
+
- Two MemoryStorage instances pointed at the same LOKI_MEMORY_BASE_PATH
|
|
8
|
+
see each other's episodes (the shared-memory contract)
|
|
9
|
+
|
|
10
|
+
Each test snapshots and restores os.environ so suite ordering is stable.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import shutil
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
import unittest
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from unittest.mock import patch
|
|
20
|
+
|
|
21
|
+
# Allow `python3 -m unittest memory.tests.test_app_graph` from the repo root.
|
|
22
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
|
23
|
+
|
|
24
|
+
from memory.app_graph import AppGraph
|
|
25
|
+
from memory.storage import MemoryStorage
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TestAppGraphFromEnv(unittest.TestCase):
|
|
29
|
+
"""Cover AppGraph.from_env contract."""
|
|
30
|
+
|
|
31
|
+
def test_returns_none_when_root_unset(self) -> None:
|
|
32
|
+
# Strip every LOKI_PROJECT_GRAPH_* var so we are sure of the input.
|
|
33
|
+
env_clean = {
|
|
34
|
+
k: v
|
|
35
|
+
for k, v in os.environ.items()
|
|
36
|
+
if not k.startswith("LOKI_PROJECT_GRAPH_")
|
|
37
|
+
}
|
|
38
|
+
with patch.dict(os.environ, env_clean, clear=True):
|
|
39
|
+
self.assertIsNone(AppGraph.from_env())
|
|
40
|
+
|
|
41
|
+
def test_returns_instance_when_env_set(self) -> None:
|
|
42
|
+
tmp = tempfile.mkdtemp(prefix="loki-appgraph-")
|
|
43
|
+
try:
|
|
44
|
+
api = Path(tmp) / "api"
|
|
45
|
+
web = Path(tmp) / "web"
|
|
46
|
+
api.mkdir()
|
|
47
|
+
web.mkdir()
|
|
48
|
+
env = {
|
|
49
|
+
"LOKI_PROJECT_GRAPH_ROOT": tmp,
|
|
50
|
+
"LOKI_PROJECT_GRAPH_APP_ID": "demo-app",
|
|
51
|
+
"LOKI_PROJECT_GRAPH_MEMBERS": f"{api}:{web}",
|
|
52
|
+
}
|
|
53
|
+
with patch.dict(os.environ, env, clear=False):
|
|
54
|
+
graph = AppGraph.from_env()
|
|
55
|
+
self.assertIsNotNone(graph)
|
|
56
|
+
assert graph is not None # type narrow for mypy/pyright
|
|
57
|
+
self.assertEqual(graph.app_id, "demo-app")
|
|
58
|
+
self.assertEqual(graph.root, Path(tmp))
|
|
59
|
+
members = graph.get_members()
|
|
60
|
+
self.assertEqual(len(members), 2)
|
|
61
|
+
# Compare basenames to avoid macOS /var vs /private/var symlink
|
|
62
|
+
# noise from Path.resolve(). Members are passed through env as
|
|
63
|
+
# raw strings, not canonicalized.
|
|
64
|
+
member_names = {m.name for m in members}
|
|
65
|
+
self.assertEqual(member_names, {"api", "web"})
|
|
66
|
+
finally:
|
|
67
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TestMemoryBasePathOverride(unittest.TestCase):
|
|
71
|
+
"""LOKI_MEMORY_BASE_PATH must redirect MemoryStorage writes."""
|
|
72
|
+
|
|
73
|
+
def setUp(self) -> None:
|
|
74
|
+
self.tmp = tempfile.mkdtemp(prefix="loki-shared-mem-")
|
|
75
|
+
self.local_a = tempfile.mkdtemp(prefix="loki-member-a-")
|
|
76
|
+
self.local_b = tempfile.mkdtemp(prefix="loki-member-b-")
|
|
77
|
+
|
|
78
|
+
def tearDown(self) -> None:
|
|
79
|
+
for p in (self.tmp, self.local_a, self.local_b):
|
|
80
|
+
shutil.rmtree(p, ignore_errors=True)
|
|
81
|
+
|
|
82
|
+
def test_writes_under_shared_dir(self) -> None:
|
|
83
|
+
shared = os.path.join(self.tmp, "memory")
|
|
84
|
+
# Caller asks for a local path; env override redirects to the shared dir.
|
|
85
|
+
local_path = os.path.join(self.local_a, ".loki", "memory")
|
|
86
|
+
with patch.dict(os.environ, {"LOKI_MEMORY_BASE_PATH": shared}):
|
|
87
|
+
store = MemoryStorage(base_path=local_path)
|
|
88
|
+
self.assertEqual(str(store.root_path), shared)
|
|
89
|
+
# The shared dir should now have the standard subdirs created.
|
|
90
|
+
self.assertTrue(Path(shared, "episodic").is_dir())
|
|
91
|
+
|
|
92
|
+
def test_two_stores_share_episodes(self) -> None:
|
|
93
|
+
shared = os.path.join(self.tmp, "memory")
|
|
94
|
+
# Both members point at the same shared dir via env.
|
|
95
|
+
with patch.dict(os.environ, {"LOKI_MEMORY_BASE_PATH": shared}):
|
|
96
|
+
store_a = MemoryStorage(
|
|
97
|
+
base_path=os.path.join(self.local_a, ".loki", "memory")
|
|
98
|
+
)
|
|
99
|
+
store_b = MemoryStorage(
|
|
100
|
+
base_path=os.path.join(self.local_b, ".loki", "memory")
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Write an episode via store_a using the public API. Schema differs
|
|
104
|
+
# across builds, so the safest cross-version probe is to drop a JSON
|
|
105
|
+
# file into the canonical episodic/<date>/ tree and confirm store_b
|
|
106
|
+
# can see it via direct enumeration.
|
|
107
|
+
from datetime import datetime, timezone
|
|
108
|
+
import json
|
|
109
|
+
|
|
110
|
+
date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
111
|
+
date_dir = Path(shared) / "episodic" / date_str
|
|
112
|
+
date_dir.mkdir(parents=True, exist_ok=True)
|
|
113
|
+
ep_path = date_dir / "episode-shared-test.json"
|
|
114
|
+
ep_path.write_text(json.dumps({"id": "shared-test", "outcome": "success"}))
|
|
115
|
+
|
|
116
|
+
# store_b should see the file because both point at the same root.
|
|
117
|
+
seen = list((Path(store_b.root_path) / "episodic" / date_str).glob("*.json"))
|
|
118
|
+
self.assertTrue(
|
|
119
|
+
any(p.name == "episode-shared-test.json" for p in seen),
|
|
120
|
+
f"store_b did not see store_a's episode under {shared}",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def test_env_unset_falls_back_to_base_path(self) -> None:
|
|
124
|
+
# Backward-compat probe: without the override, MemoryStorage must
|
|
125
|
+
# write under the caller-provided base_path (original behavior).
|
|
126
|
+
local_path = os.path.join(self.local_a, ".loki", "memory")
|
|
127
|
+
env_clean = {k: v for k, v in os.environ.items() if k != "LOKI_MEMORY_BASE_PATH"}
|
|
128
|
+
with patch.dict(os.environ, env_clean, clear=True):
|
|
129
|
+
store = MemoryStorage(base_path=local_path)
|
|
130
|
+
self.assertEqual(str(store.root_path), local_path)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
unittest.main()
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
|
-
"version": "7.5.
|
|
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.
|
|
3
|
+
"version": "7.5.27",
|
|
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",
|
|
7
7
|
"agent-orchestration",
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"bin/",
|
|
74
74
|
"bin/loki",
|
|
75
75
|
"loki-ts/dist/",
|
|
76
|
+
"loki-ts/data/",
|
|
76
77
|
"loki-ts/package.json",
|
|
77
78
|
"api/",
|
|
78
79
|
"events/",
|
|
@@ -124,7 +125,7 @@
|
|
|
124
125
|
"@opentelemetry/exporter-trace-otlp-http": "^0.57.0"
|
|
125
126
|
},
|
|
126
127
|
"overrides": {
|
|
127
|
-
"protobufjs": ">=
|
|
128
|
+
"protobufjs": ">=8.4.2"
|
|
128
129
|
},
|
|
129
130
|
"devDependencies": {
|
|
130
131
|
"@types/node": "^25.2.0",
|
package/providers/claude.sh
CHANGED
|
@@ -109,11 +109,107 @@ provider_version() {
|
|
|
109
109
|
claude --version 2>/dev/null | head -1
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
#
|
|
112
|
+
# Source the v7.5.19 Phase B claude-flags helper (idempotent).
|
|
113
|
+
# shellcheck source=../autonomy/lib/claude-flags.sh
|
|
114
|
+
_loki_claude_flags_helper="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/autonomy/lib/claude-flags.sh"
|
|
115
|
+
if [ -f "$_loki_claude_flags_helper" ]; then
|
|
116
|
+
# shellcheck disable=SC1090
|
|
117
|
+
. "$_loki_claude_flags_helper"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# Source the v7.5.22 Phase D mcp-config helper (idempotent).
|
|
121
|
+
# shellcheck source=../autonomy/lib/mcp-config.sh
|
|
122
|
+
_loki_mcp_config_helper="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/autonomy/lib/mcp-config.sh"
|
|
123
|
+
if [ -f "$_loki_mcp_config_helper" ]; then
|
|
124
|
+
# shellcheck disable=SC1090
|
|
125
|
+
. "$_loki_mcp_config_helper"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Build the auto-derived flag array. Caller passes tier + complexity + primary model.
|
|
129
|
+
# Values that the helper returns empty are dropped (no flag emitted).
|
|
130
|
+
# Honors loki_claude_flag_supported() so we never pass a flag the installed CLI lacks.
|
|
131
|
+
_loki_build_claude_auto_flags() {
|
|
132
|
+
local tier="${1:-development}"
|
|
133
|
+
local complexity="${2:-${LOKI_COMPLEXITY:-standard}}"
|
|
134
|
+
local primary="${3:-}"
|
|
135
|
+
_LOKI_CLAUDE_AUTO_FLAGS=()
|
|
136
|
+
|
|
137
|
+
# --effort: default-on derived from tier + complexity.
|
|
138
|
+
if type loki_effort_for_tier >/dev/null 2>&1 && loki_claude_flag_supported "--effort"; then
|
|
139
|
+
local effort
|
|
140
|
+
effort=$(loki_effort_for_tier "$tier" "$complexity")
|
|
141
|
+
if [ -n "$effort" ]; then
|
|
142
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--effort" "$effort")
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# --max-budget-usd: derived from LOKI_BUDGET_LIMIT minus spend.
|
|
147
|
+
if type loki_remaining_budget >/dev/null 2>&1 && loki_claude_flag_supported "--max-budget-usd"; then
|
|
148
|
+
local rem
|
|
149
|
+
rem=$(loki_remaining_budget)
|
|
150
|
+
if [ -n "$rem" ]; then
|
|
151
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--max-budget-usd" "$rem")
|
|
152
|
+
fi
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# --fallback-model: derived from primary model alias.
|
|
156
|
+
if [ -n "$primary" ] && type loki_fallback_for_primary >/dev/null 2>&1 && loki_claude_flag_supported "--fallback-model"; then
|
|
157
|
+
local fb
|
|
158
|
+
fb=$(loki_fallback_for_primary "$primary")
|
|
159
|
+
if [ -n "$fb" ]; then
|
|
160
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--fallback-model" "$fb")
|
|
161
|
+
fi
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# --exclude-dynamic-system-prompt-sections (Phase E, v7.5.20).
|
|
165
|
+
# Move per-machine sections (cwd, env, memory paths, git status) from the
|
|
166
|
+
# system prompt into the first user message. Improves cross-user prompt-cache
|
|
167
|
+
# reuse. Boolean flag: pass it OR do not, no value. Default ON when supported.
|
|
168
|
+
# Suppress with LOKI_DYNAMIC_PROMPT_SECTIONS=keep (for users who want the
|
|
169
|
+
# old behavior; the only opt-out lever for this flag).
|
|
170
|
+
if [ "${LOKI_DYNAMIC_PROMPT_SECTIONS:-auto}" != "keep" ] \
|
|
171
|
+
&& loki_claude_flag_supported "--exclude-dynamic-system-prompt-sections"; then
|
|
172
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--exclude-dynamic-system-prompt-sections")
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# --mcp-config (Phase D, v7.5.22). Variadic flag (Commander `<configs...>`):
|
|
176
|
+
# Claude expects SEPARATE argv elements per path, not one space-joined
|
|
177
|
+
# value. Per Dev-C parity concern -- spread each path as its own argv
|
|
178
|
+
# element so bash matches Bun's shape exactly. Loki bundle first, optional
|
|
179
|
+
# user overlay (~/.claude/mcp.json) second. Skip entirely if the bundle
|
|
180
|
+
# cannot be written so we never pass malformed paths.
|
|
181
|
+
if type loki_mcp_config_argv >/dev/null 2>&1 \
|
|
182
|
+
&& loki_claude_flag_supported "--mcp-config"; then
|
|
183
|
+
local _mcp_argv
|
|
184
|
+
if _mcp_argv=$(loki_mcp_config_argv) && [ -n "$_mcp_argv" ]; then
|
|
185
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--mcp-config")
|
|
186
|
+
# Split space-separated path list into individual argv elements.
|
|
187
|
+
# Both paths come from Loki-controlled writers (loki_mcp_config_path)
|
|
188
|
+
# or HOME expansion -- whitespace in paths is not supported here.
|
|
189
|
+
local _mcp_path
|
|
190
|
+
for _mcp_path in $_mcp_argv; do
|
|
191
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("$_mcp_path")
|
|
192
|
+
done
|
|
193
|
+
fi
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# --include-hook-events (Phase D, v7.5.22). Boolean flag; only valid
|
|
197
|
+
# when --output-format=stream-json (which the claude branch in
|
|
198
|
+
# autonomy/run.sh always uses). Default-on; opt out with
|
|
199
|
+
# LOKI_HOOK_EVENTS=off.
|
|
200
|
+
if [ "${LOKI_HOOK_EVENTS:-on}" != "off" ] \
|
|
201
|
+
&& loki_claude_flag_supported "--include-hook-events"; then
|
|
202
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--include-hook-events")
|
|
203
|
+
fi
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# Invocation function (basic, no tier).
|
|
207
|
+
# Auto-flags use development tier defaults.
|
|
113
208
|
provider_invoke() {
|
|
114
209
|
local prompt="$1"
|
|
115
210
|
shift
|
|
116
|
-
|
|
211
|
+
_loki_build_claude_auto_flags "development" "${LOKI_COMPLEXITY:-standard}" ""
|
|
212
|
+
claude --dangerously-skip-permissions "${_LOKI_CLAUDE_AUTO_FLAGS[@]}" -p "$prompt" "$@"
|
|
117
213
|
}
|
|
118
214
|
|
|
119
215
|
# Model tier to Task tool model parameter value
|
|
@@ -185,15 +281,30 @@ resolve_model_for_tier() {
|
|
|
185
281
|
esac
|
|
186
282
|
fi
|
|
187
283
|
|
|
284
|
+
# Phase I (v7.5.25): when ANTHROPIC_BASE_URL is set, the user is routing
|
|
285
|
+
# Claude Code to an alt-provider (OpenRouter, Ollama, LiteLLM, self-hosted).
|
|
286
|
+
# The alt-provider may not recognize the opus/sonnet/haiku aliases that
|
|
287
|
+
# only Anthropic resolves. Let the user override the resolved model name
|
|
288
|
+
# via LOKI_MODEL_OVERRIDE; it wins over all tier mapping. Bash and Bun
|
|
289
|
+
# routes honor the same env var.
|
|
290
|
+
if [ -n "${ANTHROPIC_BASE_URL:-}" ] && [ -n "${LOKI_MODEL_OVERRIDE:-}" ]; then
|
|
291
|
+
model="$LOKI_MODEL_OVERRIDE"
|
|
292
|
+
fi
|
|
293
|
+
|
|
188
294
|
echo "$model"
|
|
189
295
|
}
|
|
190
296
|
|
|
191
|
-
# Tier-aware invocation (values are already aliases like opus/sonnet/haiku)
|
|
297
|
+
# Tier-aware invocation (values are already aliases like opus/sonnet/haiku).
|
|
298
|
+
# v7.5.19 Phase B: auto-derive --effort, --max-budget-usd, --fallback-model from existing Loki state.
|
|
299
|
+
# v7.5.25 Phase I: ANTHROPIC_BASE_URL is passed through unchanged (Claude Code
|
|
300
|
+
# reads it natively; we never strip or rewrite it). LOKI_MODEL_OVERRIDE wins
|
|
301
|
+
# over tier-resolved model when an alt-provider endpoint is configured.
|
|
192
302
|
provider_invoke_with_tier() {
|
|
193
303
|
local tier="$1"
|
|
194
304
|
local prompt="$2"
|
|
195
305
|
shift 2
|
|
196
306
|
local model
|
|
197
307
|
model=$(resolve_model_for_tier "$tier")
|
|
198
|
-
|
|
308
|
+
_loki_build_claude_auto_flags "$tier" "${LOKI_COMPLEXITY:-standard}" "$model"
|
|
309
|
+
claude --dangerously-skip-permissions --model "$model" "${_LOKI_CLAUDE_AUTO_FLAGS[@]}" -p "$prompt" "$@"
|
|
199
310
|
}
|
package/providers/codex.sh
CHANGED
|
@@ -53,8 +53,8 @@ PROVIDER_MAX_PARALLEL=1
|
|
|
53
53
|
CODEX_DEFAULT_MODEL="gpt-5.3-codex"
|
|
54
54
|
|
|
55
55
|
# Known valid Codex model prefixes for validation (BUG-PROV-002 fix)
|
|
56
|
-
# Generic LOKI_MODEL_* may contain Claude
|
|
57
|
-
# "
|
|
56
|
+
# Generic LOKI_MODEL_* may contain Claude model aliases (e.g. "opus", "sonnet",
|
|
57
|
+
# "haiku") which are invalid for Codex. Validate before accepting.
|
|
58
58
|
CODEX_KNOWN_MODELS=("gpt-" "o1-" "o3-" "o4-" "codex-" "ft:gpt-")
|
|
59
59
|
|
|
60
60
|
_codex_validate_model() {
|
package/providers/loader.sh
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
PROVIDERS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
6
|
|
|
7
7
|
# List of supported providers
|
|
8
|
-
SUPPORTED_PROVIDERS=("claude" "codex" "
|
|
8
|
+
SUPPORTED_PROVIDERS=("claude" "codex" "cline" "aider")
|
|
9
9
|
|
|
10
10
|
# Default provider
|
|
11
11
|
DEFAULT_PROVIDER="claude"
|
|
@@ -171,10 +171,10 @@ print_capability_matrix() {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
# Auto-detect best available provider
|
|
174
|
-
# BUG-PROV-007 fix: includes all
|
|
175
|
-
# Priority: Claude (Tier 1, full) > Cline (Tier 2, near-full) > Codex/
|
|
174
|
+
# BUG-PROV-007 fix: includes all 4 supported providers in priority order
|
|
175
|
+
# Priority: Claude (Tier 1, full) > Cline (Tier 2, near-full) > Codex/Aider (Tier 3, degraded)
|
|
176
176
|
auto_detect_provider() {
|
|
177
|
-
for p in claude cline codex
|
|
177
|
+
for p in claude cline codex aider; do
|
|
178
178
|
if check_provider_installed "$p"; then
|
|
179
179
|
echo "$p"
|
|
180
180
|
return 0
|
|
@@ -48,15 +48,6 @@
|
|
|
48
48
|
],
|
|
49
49
|
"notes": "Codex uses a single model with effort level (xhigh/high/low) for tier differentiation"
|
|
50
50
|
},
|
|
51
|
-
"gemini": {
|
|
52
|
-
"latest_planning": "gemini-3-pro-preview",
|
|
53
|
-
"latest_development": "gemini-3-pro-preview",
|
|
54
|
-
"latest_fast": "gemini-3-flash-preview",
|
|
55
|
-
"models": [
|
|
56
|
-
{ "id": "gemini-3-pro-preview", "tier": "planning", "context_window": 1000000 },
|
|
57
|
-
{ "id": "gemini-3-flash-preview", "tier": "fast", "context_window": 1000000 }
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
51
|
"cline": {
|
|
61
52
|
"latest_planning": "claude-opus-4-7",
|
|
62
53
|
"latest_development": "claude-opus-4-7",
|
package/providers/models.sh
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
# Usage:
|
|
10
10
|
# source providers/models.sh
|
|
11
11
|
# model=$(loki_latest_model claude planning) # -> claude-opus-4-7
|
|
12
|
-
# model=$(loki_latest_model gemini fast) # -> gemini-3-flash-preview
|
|
13
12
|
#
|
|
14
13
|
# Env override order: LOKI_<PROVIDER>_MODEL_<TIER> > LOKI_<PROVIDER>_MODEL > catalog latest.
|
|
15
14
|
|
|
@@ -18,7 +17,7 @@ _LOKI_MODELS_SH_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
|
18
17
|
LOKI_MODEL_CATALOG="${LOKI_MODEL_CATALOG:-$_LOKI_MODELS_SH_DIR/model_catalog.json}"
|
|
19
18
|
|
|
20
19
|
# Return the "latest_<tier>" id for a provider from the catalog.
|
|
21
|
-
# Args: $1 provider (claude|codex|
|
|
20
|
+
# Args: $1 provider (claude|codex|cline|aider)
|
|
22
21
|
# $2 tier (planning|development|fast)
|
|
23
22
|
loki_latest_model() {
|
|
24
23
|
local provider="${1:-claude}"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Multi-Provider Architecture Reference
|
|
2
2
|
|
|
3
|
-
> **Version:** 5.25.0 | **Status:** Production | **Last Updated:** 2026-
|
|
3
|
+
> **Version:** 5.25.0 | **Status:** Production | **Last Updated:** 2026-05-22
|
|
4
4
|
|
|
5
|
-
Loki Mode supports
|
|
5
|
+
Loki Mode supports four AI CLI providers with a unified abstraction layer. This document provides detailed technical reference for the multi-provider system.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -12,7 +12,8 @@ Loki Mode supports three AI CLI providers with a unified abstraction layer. This
|
|
|
12
12
|
|----------|-----|--------|----------|
|
|
13
13
|
| **Claude Code** | `claude` | Full | Subagents, Parallel, Task Tool, MCP |
|
|
14
14
|
| **OpenAI Codex** | `codex` | Degraded | Sequential, Effort Parameter, MCP (basic) |
|
|
15
|
-
| **
|
|
15
|
+
| **Cline CLI** | `cline` | Near-Full (Tier 2) | Subagents, MCP, 12+ providers |
|
|
16
|
+
| **Aider** | `aider` | Degraded | Sequential, 18+ providers |
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
@@ -24,7 +25,8 @@ Located in `providers/` directory:
|
|
|
24
25
|
providers/
|
|
25
26
|
claude.sh # Full-featured provider
|
|
26
27
|
codex.sh # Degraded mode, effort parameter
|
|
27
|
-
|
|
28
|
+
cline.sh # Near-full mode, 12+ providers (Tier 2)
|
|
29
|
+
aider.sh # Degraded mode, 18+ providers
|
|
28
30
|
loader.sh # Provider loader utility
|
|
29
31
|
```
|
|
30
32
|
|
|
@@ -71,7 +73,7 @@ PROVIDER_MAX_OUTPUT_TOKENS=128000
|
|
|
71
73
|
|
|
72
74
|
#### Degraded Mode
|
|
73
75
|
```bash
|
|
74
|
-
PROVIDER_DEGRADED=false # true for Codex/
|
|
76
|
+
PROVIDER_DEGRADED=false # true for Codex/Aider
|
|
75
77
|
PROVIDER_DEGRADED_REASONS=() # Array of limitation descriptions
|
|
76
78
|
```
|
|
77
79
|
|
|
@@ -81,21 +83,21 @@ PROVIDER_DEGRADED_REASONS=() # Array of limitation descriptions
|
|
|
81
83
|
|
|
82
84
|
Loki Mode uses abstract tiers that map to provider-specific configurations:
|
|
83
85
|
|
|
84
|
-
| Abstract Tier | Purpose | Claude | Codex |
|
|
85
|
-
|
|
86
|
-
| `planning` | Architecture, PRD analysis | opus | xhigh effort |
|
|
87
|
-
| `development` | Implementation, tests | sonnet | high effort |
|
|
88
|
-
| `fast` | Simple tasks, docs | haiku | low effort |
|
|
86
|
+
| Abstract Tier | Purpose | Claude | Codex |
|
|
87
|
+
|---------------|---------|--------|-------|
|
|
88
|
+
| `planning` | Architecture, PRD analysis | opus | xhigh effort |
|
|
89
|
+
| `development` | Implementation, tests | sonnet | high effort |
|
|
90
|
+
| `fast` | Simple tasks, docs | haiku | low effort |
|
|
89
91
|
|
|
90
92
|
### Tier Selection by RARV Phase
|
|
91
93
|
|
|
92
94
|
```
|
|
93
95
|
RARV Phase -> Abstract Tier -> Provider-Specific
|
|
94
|
-
|
|
95
|
-
REASON -> planning -> opus/xhigh
|
|
96
|
-
ACT -> development -> sonnet/high
|
|
97
|
-
REFLECT -> development -> sonnet/high
|
|
98
|
-
VERIFY -> fast -> haiku/low
|
|
96
|
+
-----------------------------------------------------
|
|
97
|
+
REASON -> planning -> opus/xhigh
|
|
98
|
+
ACT -> development -> sonnet/high
|
|
99
|
+
REFLECT -> development -> sonnet/high
|
|
100
|
+
VERIFY -> fast -> haiku/low
|
|
99
101
|
```
|
|
100
102
|
|
|
101
103
|
---
|
|
@@ -112,7 +114,7 @@ load_provider "claude" # Returns 0 on success, 1 on failure
|
|
|
112
114
|
validate_provider "codex" # Returns 0 if valid
|
|
113
115
|
|
|
114
116
|
# Check if provider CLI is installed
|
|
115
|
-
check_provider_installed "
|
|
117
|
+
check_provider_installed "cline" # Returns 0 if installed
|
|
116
118
|
|
|
117
119
|
# Auto-detect first available provider
|
|
118
120
|
auto_detect_provider # Echoes provider name or returns 1
|
|
@@ -169,13 +171,12 @@ provider_get_tier_param "development" # Returns: opus/sonnet/haiku or xhigh/hig
|
|
|
169
171
|
|
|
170
172
|
## Degraded Mode Behavior
|
|
171
173
|
|
|
172
|
-
When running with Codex or
|
|
174
|
+
When running with Codex or Aider:
|
|
173
175
|
|
|
174
176
|
1. **RARV Cycle executes sequentially** - No parallel agents
|
|
175
177
|
2. **Task tool calls are skipped** - Main thread handles all work
|
|
176
178
|
3. **Model tier maps to provider configuration:**
|
|
177
179
|
- Codex: `CODEX_MODEL_REASONING_EFFORT` environment variable
|
|
178
|
-
- Gemini: `~/.gemini/settings.json` thinkingMode
|
|
179
180
|
4. **Quality gates run sequentially** - No 3-reviewer parallel review
|
|
180
181
|
5. **Git worktree parallelism disabled** - `--parallel` flag has no effect
|
|
181
182
|
|
|
@@ -249,7 +250,7 @@ calculate_rate_limit_backoff # Uses PROVIDER_RATE_LIMIT_RPM
|
|
|
249
250
|
```bash
|
|
250
251
|
# Via CLI flag
|
|
251
252
|
./autonomy/run.sh --provider codex ./prd.md
|
|
252
|
-
loki start --provider
|
|
253
|
+
loki start --provider cline ./prd.md
|
|
253
254
|
|
|
254
255
|
# Via environment variable
|
|
255
256
|
export LOKI_PROVIDER=codex
|
|
@@ -264,15 +265,16 @@ export LOKI_PROVIDER=codex
|
|
|
264
265
|
$ loki start --help
|
|
265
266
|
|
|
266
267
|
Provider Options:
|
|
267
|
-
--provider PROVIDER Select AI provider (claude, codex,
|
|
268
|
+
--provider PROVIDER Select AI provider (claude, codex, cline, aider)
|
|
268
269
|
Default: claude (auto-detect if not installed)
|
|
269
270
|
|
|
270
271
|
Provider Capability Matrix:
|
|
271
272
|
Provider Features Parallel Task Tool MCP
|
|
272
|
-
|
|
273
|
+
--------------------------------------------------
|
|
273
274
|
claude Full Yes (10) Yes Yes
|
|
274
275
|
codex Degraded No No Basic
|
|
275
|
-
|
|
276
|
+
cline Near-Full No Yes Yes
|
|
277
|
+
aider Degraded No No No
|
|
276
278
|
```
|
|
277
279
|
|
|
278
280
|
---
|
|
@@ -285,19 +287,8 @@ All CLI flags have been verified against actual CLI help output:
|
|
|
285
287
|
|----------|------|------------------|-------|
|
|
286
288
|
| Claude | `--dangerously-skip-permissions` | v2.1.34 | Autonomous mode |
|
|
287
289
|
| Codex | `--full-auto` | v0.98.0 | Recommended; legacy: `exec --dangerously-bypass-approvals-and-sandbox` |
|
|
288
|
-
|
|
|
289
|
-
|
|
290
|
-
### Gemini Note
|
|
291
|
-
|
|
292
|
-
The `-p` prompt flag is deprecated in Gemini CLI v0.27.3. Loki Mode uses positional prompts instead:
|
|
293
|
-
|
|
294
|
-
```bash
|
|
295
|
-
# Correct (v5.1.0+)
|
|
296
|
-
gemini --approval-mode=yolo "$prompt"
|
|
297
|
-
|
|
298
|
-
# Deprecated (do not use)
|
|
299
|
-
gemini --yolo -p "$prompt"
|
|
300
|
-
```
|
|
290
|
+
| Cline | `--auto-approve` | latest | Autonomous mode |
|
|
291
|
+
| Aider | `--yes-always` | latest | Autonomous mode |
|
|
301
292
|
|
|
302
293
|
---
|
|
303
294
|
|
|
@@ -75,7 +75,7 @@ Task(
|
|
|
75
75
|
|
|
76
76
|
| Model | Task | Baseline | 2x Repetition | 3x Repetition |
|
|
77
77
|
|-------|------|----------|---------------|---------------|
|
|
78
|
-
|
|
|
78
|
+
| Google Flash-Lite 2.0 | NameIndex | 21.33% | 97.33% | 98.67% |
|
|
79
79
|
| GPT-4o | NameIndex | 56.67% | 86.67% | 90.00% |
|
|
80
80
|
| Claude 3 Sonnet | NameIndex | 48.00% | 82.67% | 85.33% |
|
|
81
81
|
| Deepseek V3 | NameIndex | 62.67% | 88.00% | 91.33% |
|
|
@@ -137,7 +137,7 @@ See `references/openai-patterns.md` for full guardrails implementation.
|
|
|
137
137
|
|
|
138
138
|
## Override Council (v7.5.4)
|
|
139
139
|
|
|
140
|
-
The override council closes the RARV-C cycle with real provider judges (Claude/Codex
|
|
140
|
+
The override council closes the RARV-C cycle with real provider judges (Claude/Codex) rather than scripted heuristics. Each judge reviews the iteration outcome independently and casts a binding ALLOW or BLOCK vote. A unanimous ALLOW closes the iteration; any BLOCK reopens REASON with the judge's rationale appended to CONTINUITY.md. See Phase 1 RARV-C closure in `references/core-workflow.md`.
|
|
141
141
|
|
|
142
142
|
---
|
|
143
143
|
|
package/skills/00-index.md
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
| Parallel features, git worktrees | `parallel-workflows.md` |
|
|
28
28
|
| Scale patterns (50+ agents) | `parallel-workflows.md` + `references/cursor-learnings.md` |
|
|
29
29
|
| GitHub issues, PRs, syncing | `github-integration.md` |
|
|
30
|
-
| Multi-provider (Codex,
|
|
30
|
+
| Multi-provider (Codex, Cline, Aider) | `providers.md` |
|
|
31
31
|
| OpenSpec delta context, brownfield modifications | `openspec-integration.md` |
|
|
32
32
|
| MiroFish market validation, `--mirofish` flag | `mirofish-integration.md` |
|
|
33
33
|
| Writing/updating documentation, `loki docs` | `documentation.md` |
|
|
@@ -158,9 +158,9 @@
|
|
|
158
158
|
- Full reference: `references/legacy-healing-patterns.md`
|
|
159
159
|
|
|
160
160
|
### providers.md (v5.0.0)
|
|
161
|
-
**When:** Using non-Claude providers (Codex,
|
|
161
|
+
**When:** Using non-Claude providers (Codex, Cline, Aider), understanding degraded mode
|
|
162
162
|
- Provider comparison matrix
|
|
163
|
-
- Claude (full features) vs Codex/
|
|
163
|
+
- Claude (full features) vs Codex/Cline/Aider (degraded mode)
|
|
164
164
|
- Provider selection via CLI flag or environment variable
|
|
165
165
|
- Model tier mapping (planning/development/fast)
|
|
166
166
|
- Degraded mode limitations and behavior
|
|
@@ -26,7 +26,6 @@ Loki Mode supports five AI providers. Claude has full features; all others run i
|
|
|
26
26
|
|----------|---------------|----------|----------|
|
|
27
27
|
| **Claude Code** | Yes | No | `--provider claude` (default) |
|
|
28
28
|
| **OpenAI Codex CLI** | No | Yes | `--provider codex` |
|
|
29
|
-
| **Google Gemini CLI** | No | Yes | `--provider gemini` |
|
|
30
29
|
| **Cline CLI** | No | Yes | `--provider cline` |
|
|
31
30
|
| **Aider** | No | Yes | `--provider aider` |
|
|
32
31
|
|
|
@@ -42,11 +41,11 @@ Loki Mode supports five AI providers. Claude has full features; all others run i
|
|
|
42
41
|
|
|
43
42
|
**Default (v5.3.0):** Haiku disabled for quality. All tasks use Opus or Sonnet.
|
|
44
43
|
|
|
45
|
-
| Tier | Purpose | Claude (default) | Claude (--allow-haiku) | Codex |
|
|
46
|
-
|
|
47
|
-
| **planning** | PRD analysis, architecture, system design | opus | opus | effort=xhigh |
|
|
48
|
-
| **development** | Feature implementation, complex bugs, tests | opus | sonnet | effort=high |
|
|
49
|
-
| **fast** | Unit tests, docs, linting, simple tasks | sonnet | haiku | effort=low |
|
|
44
|
+
| Tier | Purpose | Claude (default) | Claude (--allow-haiku) | Codex |
|
|
45
|
+
|------|---------|------------------|------------------------|-------|
|
|
46
|
+
| **planning** | PRD analysis, architecture, system design | opus | opus | effort=xhigh |
|
|
47
|
+
| **development** | Feature implementation, complex bugs, tests | opus | sonnet | effort=high |
|
|
48
|
+
| **fast** | Unit tests, docs, linting, simple tasks | sonnet | haiku | effort=low |
|
|
50
49
|
|
|
51
50
|
### Enabling Haiku
|
|
52
51
|
|
|
@@ -80,11 +79,10 @@ When Haiku is enabled:
|
|
|
80
79
|
|
|
81
80
|
**Claude-specific model names:** opus, sonnet, haiku (haiku requires --allow-haiku flag)
|
|
82
81
|
**Codex effort levels:** xhigh, high, medium, low
|
|
83
|
-
**Gemini thinking levels:** high, medium, low
|
|
84
82
|
|
|
85
83
|
## Task Tool Examples (Claude Only)
|
|
86
84
|
|
|
87
|
-
**NOTE:** Task tool is Claude-specific. Codex and
|
|
85
|
+
**NOTE:** Task tool is Claude-specific. Codex, Cline, and Aider run in degraded mode without subagents.
|
|
88
86
|
|
|
89
87
|
```python
|
|
90
88
|
# Planning tier (opus) for Bootstrap, Discovery, Architecture
|
|
@@ -109,7 +107,7 @@ if [ "${PROVIDER_HAS_TASK_TOOL:-false}" = "true" ]; then
|
|
|
109
107
|
# Claude: Use Task tool with parallel agents
|
|
110
108
|
Task(model="haiku", description="Run tests", prompt="...")
|
|
111
109
|
else
|
|
112
|
-
# Codex/
|
|
110
|
+
# Codex/Cline/Aider: Run sequentially without subagents
|
|
113
111
|
# Execute RARV cycle in main thread
|
|
114
112
|
fi
|
|
115
113
|
```
|
|
@@ -136,7 +134,7 @@ fi
|
|
|
136
134
|
|
|
137
135
|
## Parallelization Strategy (Claude Only)
|
|
138
136
|
|
|
139
|
-
**NOTE:** Parallelization requires Task tool, which is Claude-specific. Codex and
|
|
137
|
+
**NOTE:** Parallelization requires Task tool, which is Claude-specific. Codex, Cline, and Aider run sequentially.
|
|
140
138
|
|
|
141
139
|
```python
|
|
142
140
|
# Claude: Launch 10+ Haiku agents in parallel for unit test suite
|
|
@@ -145,7 +143,7 @@ for test_file in test_files:
|
|
|
145
143
|
description=f"Run unit tests: {test_file}",
|
|
146
144
|
run_in_background=True)
|
|
147
145
|
|
|
148
|
-
# Codex/
|
|
146
|
+
# Codex/Cline/Aider: Run tests sequentially (no parallelization)
|
|
149
147
|
for test_file in test_files:
|
|
150
148
|
run_test(test_file) # Sequential execution
|
|
151
149
|
```
|
|
@@ -441,12 +439,11 @@ Task(
|
|
|
441
439
|
)
|
|
442
440
|
```
|
|
443
441
|
|
|
444
|
-
For Codex
|
|
442
|
+
For Codex (degraded mode):
|
|
445
443
|
```python
|
|
446
|
-
# Map tiers to effort
|
|
444
|
+
# Map tiers to effort levels
|
|
447
445
|
TIER_MAPPING = {
|
|
448
446
|
"codex": {"HIGH": "xhigh", "MEDIUM": "high", "LOW": "low"},
|
|
449
|
-
"gemini": {"HIGH": "high", "MEDIUM": "medium", "LOW": "low"},
|
|
450
447
|
}
|
|
451
448
|
effort_level = TIER_MAPPING[provider][determine_tier(task)]
|
|
452
449
|
```
|