loki-mode 7.5.17 → 7.5.28
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 +685 -0
- package/autonomy/lib/voter-agents.sh +356 -0
- package/autonomy/loki +108 -111
- package/autonomy/run.sh +95 -186
- package/bin/loki +12 -1
- 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 +198 -172
- 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
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
- **Truly autonomous** -- Describe what you want, walk away, come back to working code with tests
|
|
28
28
|
- **Production quality built in** -- 11 quality gates (`skills/quality-gates.md`), blind 3-reviewer code review (`run.sh:run_code_review()`), anti-sycophancy checks
|
|
29
29
|
- **Self-hosted and private** -- Your keys, your infrastructure, no data leaves your network
|
|
30
|
-
- **
|
|
30
|
+
- **4 active AI providers** -- Claude, Codex, Cline, Aider with automatic failover (`loki-ts/src/runner/providers.ts`). Gemini CLI deprecated v7.5.18; Antigravity CLI coming soon.
|
|
31
31
|
- **Legacy system healing** -- `loki heal` archaeology/stabilize/isolate/modernize/validate phases (v6.67.0, see `skills/healing.md`)
|
|
32
32
|
- **Memory system** -- Episodic/semantic/procedural with vector search (v5.15.0, see `memory/engine.py`)
|
|
33
33
|
- **MCP server** -- 15 tools including ChromaDB code search (`mcp/server.py`)
|
|
@@ -304,15 +304,16 @@ Loki Mode is the only platform that is fully self-hosted, open source, and inclu
|
|
|
304
304
|
|
|
305
305
|
## Multi-Provider Support
|
|
306
306
|
|
|
307
|
-
| Provider | Autonomous Flag | Parallel Agents | Install |
|
|
308
|
-
|
|
309
|
-
| **Claude Code** | `--dangerously-skip-permissions` | Yes (10+) | `npm i -g @anthropic-ai/claude-code` |
|
|
310
|
-
| **Codex CLI** | `--full-auto` | Sequential | `npm i -g @openai/codex` |
|
|
311
|
-
| **
|
|
312
|
-
| **
|
|
313
|
-
| **
|
|
307
|
+
| Provider | Status | Autonomous Flag | Parallel Agents | Install |
|
|
308
|
+
|----------|--------|:-:|:-:|---------|
|
|
309
|
+
| **Claude Code** | Active (Tier 1) | `--dangerously-skip-permissions` | Yes (10+) | `npm i -g @anthropic-ai/claude-code` |
|
|
310
|
+
| **Codex CLI** | Active (Tier 3) | `--full-auto` | Sequential | `npm i -g @openai/codex` |
|
|
311
|
+
| **Cline CLI** | Active (Tier 2) | `--auto-approve` | Sequential | `npm i -g @anthropic-ai/cline` |
|
|
312
|
+
| **Aider** | Active (Tier 3) | `--yes-always` | Sequential | `pip install aider-chat` |
|
|
313
|
+
| **Google Gemini CLI** | DEPRECATED v7.5.18 | -- | -- | Upstream deprecated; runtime removed. `LOKI_PROVIDER=gemini` exits with migration message. |
|
|
314
|
+
| **Anthropic Antigravity CLI** | Coming soon | -- | -- | Integration planned. |
|
|
314
315
|
|
|
315
|
-
Claude gets full features (subagents, parallelization, MCP, Task tool). Other providers run sequentially. Auto-failover switches providers when rate-limited. See [Provider Guide](skills/providers.md).
|
|
316
|
+
Claude gets full features (subagents, parallelization, MCP, Task tool). Other active providers run sequentially. Auto-failover switches providers when rate-limited. See [Provider Guide](skills/providers.md).
|
|
316
317
|
|
|
317
318
|
---
|
|
318
319
|
|
package/SKILL.md
CHANGED
|
@@ -3,13 +3,13 @@ name: loki-mode
|
|
|
3
3
|
description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.5.
|
|
6
|
+
# Loki Mode v7.5.28
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
10
10
|
**Spec in, product out.** A "spec" is whatever describes the work: a Markdown PRD, a GitHub issue, an OpenAPI doc, a Jira ticket -- a PRD is one form of spec.
|
|
11
11
|
|
|
12
|
-
**Multi-provider (stable since v5.0.0):** Claude/Codex/
|
|
12
|
+
**Multi-provider (stable since v5.0.0):** Claude/Codex/Cline/Aider with abstract model tiers and degraded mode for non-Claude providers. See `skills/providers.md`. **Current track (v7.5.x):** Phase 1 RARV-C closure -- real provider judges, gate-failure flock, synthetic PRD e2e, status `--json`, dead code cleanup.
|
|
13
13
|
|
|
14
14
|
**Runtime migration in progress:** A bash-to-Bun migration is underway on the `feat/bun-migration` branch. The first phase (shipped in v7.3.0) routes a small set of read-only commands -- `version`, `status`, `stats`, `doctor`, `provider show/list`, `memory list/index` -- through a Bun runtime via `bin/loki`. Every other command remains on the Bash runtime (`autonomy/loki`). Rollback is available with `LOKI_LEGACY_BASH=1`. See `UPGRADING.md` and `docs/architecture/ADR-001-runtime-migration.md` for the full plan.
|
|
15
15
|
|
|
@@ -93,17 +93,17 @@ These rules guide autonomous operation. Test results and code quality always tak
|
|
|
93
93
|
|
|
94
94
|
**Default since v5.3.0 (reaffirmed in v7.5.13):** Haiku disabled for quality. Use `--allow-haiku` or `LOKI_ALLOW_HAIKU=true` to enable.
|
|
95
95
|
|
|
96
|
-
| Task Type | Tier | Claude (default) | Claude (--allow-haiku) | Codex (GPT-5.3) |
|
|
97
|
-
|
|
98
|
-
| Spec analysis, architecture, system design | **planning** | opus | opus | effort=xhigh |
|
|
99
|
-
| Feature implementation, complex bugs | **development** | opus | sonnet | effort=high |
|
|
100
|
-
| Code review (planned: 3 parallel reviewers) | **development** | opus | sonnet | effort=high |
|
|
101
|
-
| Integration tests, E2E, deployment | **development** | opus | sonnet | effort=high |
|
|
102
|
-
| Unit tests, linting, docs, simple fixes | **fast** | sonnet | haiku | effort=low |
|
|
96
|
+
| Task Type | Tier | Claude (default) | Claude (--allow-haiku) | Codex (GPT-5.3) |
|
|
97
|
+
|-----------|------|------------------|------------------------|------------------|
|
|
98
|
+
| Spec analysis, architecture, system design | **planning** | opus | opus | effort=xhigh |
|
|
99
|
+
| Feature implementation, complex bugs | **development** | opus | sonnet | effort=high |
|
|
100
|
+
| Code review (planned: 3 parallel reviewers) | **development** | opus | sonnet | effort=high |
|
|
101
|
+
| Integration tests, E2E, deployment | **development** | opus | sonnet | effort=high |
|
|
102
|
+
| Unit tests, linting, docs, simple fixes | **fast** | sonnet | haiku | effort=low |
|
|
103
103
|
|
|
104
104
|
**Parallelization rule (Claude only):** Launch up to 10 agents simultaneously for independent tasks.
|
|
105
105
|
|
|
106
|
-
**Degraded mode (Codex/
|
|
106
|
+
**Degraded mode (Codex/Cline/Aider):** No parallel agents or Task tool. Codex has MCP support. Runs RARV cycle sequentially. See `skills/model-selection.md`.
|
|
107
107
|
|
|
108
108
|
**Git worktree parallelism:** For true parallel feature development, use `--parallel` flag with run.sh. See `skills/parallel-workflows.md`.
|
|
109
109
|
|
|
@@ -210,7 +210,6 @@ loki start --issue 123 # Explicit issue mode (overrides de
|
|
|
210
210
|
# With provider selection (supports .md and .json PRDs)
|
|
211
211
|
loki start --provider claude ./prd.md # Default, full features
|
|
212
212
|
loki start --provider codex ./prd.json # GPT-5.3 Codex, degraded mode
|
|
213
|
-
loki start --provider gemini ./prd.md # Gemini 3 Pro, degraded mode
|
|
214
213
|
loki start --provider cline ./prd.md # Cline CLI, degraded mode
|
|
215
214
|
loki start --provider aider ./prd.md # Aider (18+ providers), degraded mode
|
|
216
215
|
|
|
@@ -225,9 +224,10 @@ loki start 123 --ship # Issue -> PR -> auto-merge
|
|
|
225
224
|
**Provider capabilities:**
|
|
226
225
|
- **Claude**: Opus 4.6, 1M context (beta), 128K output, adaptive thinking, agent teams, full features (Task tool, parallel agents, MCP)
|
|
227
226
|
- **Codex**: GPT-5.3, 400K context, 128K output, MCP support, --full-auto mode, degraded (sequential only, no Task tool)
|
|
228
|
-
- **Gemini**: Degraded mode (sequential only, no Task tool, 1M context)
|
|
229
227
|
- **Cline**: Multi-provider CLI, degraded mode (sequential only, no Task tool)
|
|
230
228
|
- **Aider**: 18+ provider backends, degraded mode (sequential only, no Task tool)
|
|
229
|
+
- **Google Gemini CLI**: DEPRECATED starting v7.5.18 (upstream deprecated; runtime removed)
|
|
230
|
+
- **Anthropic Antigravity CLI**: Coming soon
|
|
231
231
|
|
|
232
232
|
---
|
|
233
233
|
|
|
@@ -350,7 +350,7 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
350
350
|
|
|
351
351
|
| Feature | Added | Notes |
|
|
352
352
|
|---------|-------|-------|
|
|
353
|
-
| Multi-provider support (
|
|
353
|
+
| Multi-provider support (4 providers) | v5.0.0 | claude, codex, cline, aider -- see `providers/` |
|
|
354
354
|
| CONTINUITY.md working memory | v5.35.0 | Auto-managed by run.sh, updated each iteration |
|
|
355
355
|
| Quality gates 3-reviewer system | v5.35.0 | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
356
356
|
| Memory System (episodic/semantic/procedural) | v5.15.0 | Full implementation in `memory/` |
|
|
@@ -381,4 +381,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
381
381
|
|
|
382
382
|
---
|
|
383
383
|
|
|
384
|
-
**v7.5.
|
|
384
|
+
**v7.5.28 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.5.
|
|
1
|
+
7.5.28
|
|
@@ -1518,9 +1518,32 @@ council_evaluate() {
|
|
|
1518
1518
|
# Compute threshold using the same ceiling(2/3) formula as council_vote and council_aggregate_votes
|
|
1519
1519
|
local _eval_threshold=$(( (COUNCIL_SIZE * 2 + 2) / 3 ))
|
|
1520
1520
|
|
|
1521
|
-
# Step 1: Aggregate votes from all members
|
|
1522
|
-
|
|
1523
|
-
|
|
1521
|
+
# Step 1: Aggregate votes from all members.
|
|
1522
|
+
# Phase C (v7.5.20): try the Claude `--agents <json>` + `--json-schema`
|
|
1523
|
+
# dispatch first. When the locally installed Claude CLI supports both flags
|
|
1524
|
+
# AND the call returns parseable findings, the helper writes both per-voter
|
|
1525
|
+
# verdict files AND the round-N.json shape consumed by the existing
|
|
1526
|
+
# transcript writer / aggregator readers downstream. On any failure
|
|
1527
|
+
# (unsupported flags, missing binary, parse error, etc.) it returns 1 and
|
|
1528
|
+
# we fall through to the existing heuristic council_aggregate_votes path.
|
|
1529
|
+
local aggregate_result=""
|
|
1530
|
+
local _va_helper
|
|
1531
|
+
_va_helper="$(dirname "${BASH_SOURCE[0]}")/lib/voter-agents.sh"
|
|
1532
|
+
if [ -f "$_va_helper" ]; then
|
|
1533
|
+
# shellcheck disable=SC1090
|
|
1534
|
+
. "$_va_helper" 2>/dev/null || true
|
|
1535
|
+
if declare -f loki_council_dispatch_agents >/dev/null 2>&1; then
|
|
1536
|
+
if loki_council_dispatch_agents "$ITERATION_COUNT" "${COUNCIL_PRD_PATH:-}"; then
|
|
1537
|
+
local _va_round_file="$COUNCIL_STATE_DIR/votes/round-${ITERATION_COUNT}.json"
|
|
1538
|
+
if [ -f "$_va_round_file" ]; then
|
|
1539
|
+
aggregate_result=$(_RF="$_va_round_file" python3 -c "import json, os; print(json.load(open(os.environ['_RF'])).get('verdict', 'CONTINUE'))" 2>/dev/null || echo "")
|
|
1540
|
+
fi
|
|
1541
|
+
fi
|
|
1542
|
+
fi
|
|
1543
|
+
fi
|
|
1544
|
+
if [ -z "$aggregate_result" ]; then
|
|
1545
|
+
aggregate_result=$(council_aggregate_votes)
|
|
1546
|
+
fi
|
|
1524
1547
|
|
|
1525
1548
|
if [ "$aggregate_result" = "COMPLETE" ]; then
|
|
1526
1549
|
# Step 2: Check if unanimous -- compare complete_count to COUNCIL_SIZE
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# autonomy/lib/claude-flags.sh -- Phase B (v7.5.19) helpers.
|
|
3
|
+
#
|
|
4
|
+
# Compute Claude Code CLI flag values automatically from existing Loki state.
|
|
5
|
+
# No new env vars introduced (per binding constraint: nothing user-facing changes).
|
|
6
|
+
# Every value derives from RARV tier, complexity score, and budget config.
|
|
7
|
+
#
|
|
8
|
+
# Public API (all functions are pure: they read env + filesystem, write stdout).
|
|
9
|
+
# loki_effort_for_tier <tier> [complexity] -- emit one of: low|medium|high|xhigh|max
|
|
10
|
+
# loki_remaining_budget -- emit dollar amount remaining as plain int/float, OR empty when unlimited
|
|
11
|
+
# loki_fallback_for_primary <model> -- emit fallback model alias (opus->sonnet, sonnet->haiku if allowed), OR empty when no fallback applies
|
|
12
|
+
# loki_claude_flag_supported <flag> -- 0 if `claude --help` lists the flag, 1 otherwise
|
|
13
|
+
#
|
|
14
|
+
# All functions are side-effect free. The caller (providers/claude.sh, loki-ts) decides whether to pass the result.
|
|
15
|
+
|
|
16
|
+
# Guard against double-source.
|
|
17
|
+
if [ "${__LOKI_CLAUDE_FLAGS_SH_LOADED:-0}" = "1" ]; then
|
|
18
|
+
return 0 2>/dev/null || true
|
|
19
|
+
fi
|
|
20
|
+
__LOKI_CLAUDE_FLAGS_SH_LOADED=1
|
|
21
|
+
|
|
22
|
+
# ---------- Effort tier mapping ----------
|
|
23
|
+
# RARV tier names: planning (xhigh), development (high), fast (medium).
|
|
24
|
+
# Complexity shift: if the project is "complex", bump each tier one notch up.
|
|
25
|
+
# Honors LOKI_ALLOW_HAIKU=true semantics for the "fast" tier (medium stays medium; haiku is a separate model concept).
|
|
26
|
+
loki_effort_for_tier() {
|
|
27
|
+
local tier="${1:-development}"
|
|
28
|
+
local complexity="${2:-${LOKI_COMPLEXITY:-standard}}"
|
|
29
|
+
local effort=""
|
|
30
|
+
case "$tier" in
|
|
31
|
+
planning) effort="xhigh" ;;
|
|
32
|
+
development) effort="high" ;;
|
|
33
|
+
fast) effort="medium" ;;
|
|
34
|
+
# Capability aliases used elsewhere in the codebase.
|
|
35
|
+
best) effort="xhigh" ;;
|
|
36
|
+
balanced) effort="high" ;;
|
|
37
|
+
cheap) effort="low" ;;
|
|
38
|
+
*) effort="high" ;;
|
|
39
|
+
esac
|
|
40
|
+
# Complex projects get one bump up. "max" is the ceiling and only for explicit user override (not auto-bumped to from xhigh).
|
|
41
|
+
if [ "$complexity" = "complex" ]; then
|
|
42
|
+
case "$effort" in
|
|
43
|
+
low) effort="medium" ;;
|
|
44
|
+
medium) effort="high" ;;
|
|
45
|
+
high) effort="xhigh" ;;
|
|
46
|
+
# xhigh stays xhigh; max not auto-reached.
|
|
47
|
+
esac
|
|
48
|
+
fi
|
|
49
|
+
printf '%s' "$effort"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# ---------- Remaining budget ----------
|
|
53
|
+
# Compute remaining budget = LOKI_BUDGET_LIMIT - cumulative spend so far.
|
|
54
|
+
# Spend lives in .loki/metrics/budget.json under "current_spend" (per autonomy/run.sh:8167+).
|
|
55
|
+
# Emit empty string when budget is unlimited (LOKI_BUDGET_LIMIT unset or 0).
|
|
56
|
+
# Emit empty when remaining <= 0 (caller decides what to do; we never emit 0).
|
|
57
|
+
loki_remaining_budget() {
|
|
58
|
+
local limit="${LOKI_BUDGET_LIMIT:-}"
|
|
59
|
+
# Empty or zero limit means no cap; emit nothing.
|
|
60
|
+
if [ -z "$limit" ] || [ "$limit" = "0" ] || [ "$limit" = "0.0" ] || [ "$limit" = "0.00" ]; then
|
|
61
|
+
return 0
|
|
62
|
+
fi
|
|
63
|
+
local budget_file="${TARGET_DIR:-.}/.loki/metrics/budget.json"
|
|
64
|
+
local spend="0"
|
|
65
|
+
if [ -f "$budget_file" ]; then
|
|
66
|
+
spend=$(python3 -c "
|
|
67
|
+
import json, sys
|
|
68
|
+
try:
|
|
69
|
+
with open('$budget_file') as f:
|
|
70
|
+
d = json.load(f)
|
|
71
|
+
v = d.get('current_spend', 0)
|
|
72
|
+
print(float(v))
|
|
73
|
+
except Exception:
|
|
74
|
+
print(0)
|
|
75
|
+
" 2>/dev/null)
|
|
76
|
+
fi
|
|
77
|
+
# Compute remaining via python3 (bash floats are unreliable across awk/bc variations).
|
|
78
|
+
python3 -c "
|
|
79
|
+
import sys
|
|
80
|
+
try:
|
|
81
|
+
limit = float('$limit')
|
|
82
|
+
spend = float('$spend')
|
|
83
|
+
rem = limit - spend
|
|
84
|
+
# Strictly positive; otherwise emit nothing (caller decides whether to bail or warn).
|
|
85
|
+
if rem > 0:
|
|
86
|
+
# Round to 2 decimal places for the CLI.
|
|
87
|
+
print(f'{rem:.2f}')
|
|
88
|
+
except Exception:
|
|
89
|
+
pass
|
|
90
|
+
" 2>/dev/null
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# ---------- Fallback model ----------
|
|
94
|
+
# Map primary model alias to a sensible fallback for `--fallback-model`.
|
|
95
|
+
# opus -> sonnet (always safe; sonnet is cheaper + similar capability)
|
|
96
|
+
# sonnet -> haiku ONLY when LOKI_ALLOW_HAIKU=true; else emit nothing
|
|
97
|
+
# haiku -> nothing (already at the bottom)
|
|
98
|
+
# Anything else (specific dated IDs, alt-provider names) -> nothing (no auto-fallback we can reason about)
|
|
99
|
+
loki_fallback_for_primary() {
|
|
100
|
+
local primary="${1:-}"
|
|
101
|
+
case "$primary" in
|
|
102
|
+
opus) printf '%s' "sonnet" ;;
|
|
103
|
+
sonnet)
|
|
104
|
+
if [ "${LOKI_ALLOW_HAIKU:-false}" = "true" ]; then
|
|
105
|
+
printf '%s' "haiku"
|
|
106
|
+
fi
|
|
107
|
+
;;
|
|
108
|
+
# No fallback for haiku or other model names.
|
|
109
|
+
*) ;;
|
|
110
|
+
esac
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# ---------- Flag-support detection ----------
|
|
114
|
+
# Returns 0 if `claude --help` lists the named flag, 1 otherwise.
|
|
115
|
+
# Cached per-process so we do not shell out N times per iteration.
|
|
116
|
+
loki_claude_flag_supported() {
|
|
117
|
+
local flag="${1:-}"
|
|
118
|
+
[ -z "$flag" ] && return 1
|
|
119
|
+
# Per-process cache: __LOKI_CLAUDE_HELP_CACHE populated on first call.
|
|
120
|
+
if [ -z "${__LOKI_CLAUDE_HELP_CACHE:-}" ]; then
|
|
121
|
+
if command -v claude >/dev/null 2>&1; then
|
|
122
|
+
__LOKI_CLAUDE_HELP_CACHE=$(claude --help 2>&1 || true)
|
|
123
|
+
else
|
|
124
|
+
__LOKI_CLAUDE_HELP_CACHE="__no_claude__"
|
|
125
|
+
fi
|
|
126
|
+
export __LOKI_CLAUDE_HELP_CACHE
|
|
127
|
+
fi
|
|
128
|
+
case "$__LOKI_CLAUDE_HELP_CACHE" in
|
|
129
|
+
*"$flag"*) return 0 ;;
|
|
130
|
+
*) return 1 ;;
|
|
131
|
+
esac
|
|
132
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# autonomy/lib/mcp-config.sh -- Phase D (v7.5.22) helpers.
|
|
3
|
+
#
|
|
4
|
+
# Compute MCP-config paths for the Claude Code CLI `--mcp-config` flag.
|
|
5
|
+
# The flag is variadic per Commander (`--mcp-config <configs...>`), which
|
|
6
|
+
# means Claude expects each path as a SEPARATE argv element following the
|
|
7
|
+
# flag. The helper here emits a space-separated string for ergonomics; the
|
|
8
|
+
# caller in `providers/claude.sh::_loki_build_claude_auto_flags` splits on
|
|
9
|
+
# whitespace and pushes each path as its own array entry, so the variadic
|
|
10
|
+
# shape is honored on the wire. Paths must not contain whitespace (both the
|
|
11
|
+
# Loki bundle path and the `$HOME`-rooted overlay path are whitespace-safe
|
|
12
|
+
# under normal use; the helper does not quote inside the joined string).
|
|
13
|
+
#
|
|
14
|
+
# Public API (all functions read env + filesystem, write stdout):
|
|
15
|
+
# loki_mcp_config_path -- emit absolute path to .loki/mcp-config.json.
|
|
16
|
+
# Re-emits (overwrites) the bundle each call.
|
|
17
|
+
# Returns 0 on success, 1 only if the dir
|
|
18
|
+
# cannot be created or the file cannot be
|
|
19
|
+
# written.
|
|
20
|
+
# loki_user_mcp_config_path -- emit absolute path to ~/.claude/mcp.json
|
|
21
|
+
# if present + readable, else empty. Always
|
|
22
|
+
# returns 0.
|
|
23
|
+
# loki_mcp_config_argv -- emit one space-separated string of paths
|
|
24
|
+
# (Loki bundle first, optional user overlay
|
|
25
|
+
# second). Caller MUST split on whitespace
|
|
26
|
+
# and pass each path as a separate argv
|
|
27
|
+
# element to satisfy Claude's variadic flag.
|
|
28
|
+
# Returns 0 on success, 1 if the bundle
|
|
29
|
+
# emission fails.
|
|
30
|
+
#
|
|
31
|
+
# No side effects beyond writing .loki/mcp-config.json (idempotent;
|
|
32
|
+
# regenerated each call).
|
|
33
|
+
|
|
34
|
+
# Guard against double-source.
|
|
35
|
+
if [ "${__LOKI_MCP_CONFIG_SH_LOADED:-0}" = "1" ]; then
|
|
36
|
+
return 0 2>/dev/null || true
|
|
37
|
+
fi
|
|
38
|
+
__LOKI_MCP_CONFIG_SH_LOADED=1
|
|
39
|
+
|
|
40
|
+
# ---------- Loki MCP bundle path ----------
|
|
41
|
+
# Emits the absolute path to .loki/mcp-config.json (TARGET_DIR-relative).
|
|
42
|
+
# Bundle includes the always-on `loki-mode` server. Phase G (v7.5.24):
|
|
43
|
+
# when any supported LSP binary (typescript-language-server, pylsp, gopls,
|
|
44
|
+
# rust-analyzer) is on PATH, the bundle also gets a single `lsp-proxy`
|
|
45
|
+
# entry that fans out to per-language servers at runtime. Detection uses
|
|
46
|
+
# `command -v` only; absence is a normal state, not an error.
|
|
47
|
+
#
|
|
48
|
+
# Idempotent-write: the new bundle bytes are compared against the existing
|
|
49
|
+
# file (`cmp -s`); the file is only rewritten when content differs. This
|
|
50
|
+
# preserves mtime across calls when nothing changed, which matters for the
|
|
51
|
+
# Phase G LSP-detection regression test (no LSP -> no bundle churn).
|
|
52
|
+
#
|
|
53
|
+
# The bundle mirrors the repo's .mcp.json `loki-mode` entry: a single
|
|
54
|
+
# stdio MCP server backed by `python3 -m mcp.server`. Caller may extend
|
|
55
|
+
# this in the future without API breakage; consumers should treat the
|
|
56
|
+
# bundle as opaque JSON.
|
|
57
|
+
loki_mcp_config_path() {
|
|
58
|
+
local base="${TARGET_DIR:-.}"
|
|
59
|
+
local mcp_dir="${base}/.loki"
|
|
60
|
+
local mcp_path="${mcp_dir}/mcp-config.json"
|
|
61
|
+
|
|
62
|
+
# Resolve to absolute path early so callers always get a stable value
|
|
63
|
+
# even if cwd changes later in the iteration.
|
|
64
|
+
if ! mkdir -p "$mcp_dir" 2>/dev/null; then
|
|
65
|
+
return 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Phase G: detect supported LSP binaries on PATH. The lsp-proxy server
|
|
69
|
+
# routes by language at runtime, so we register it once when ANY of the
|
|
70
|
+
# supported binaries is present (multiple binaries -> still one entry).
|
|
71
|
+
local lsp_detected=0
|
|
72
|
+
local lsp_bin
|
|
73
|
+
for lsp_bin in typescript-language-server pylsp gopls rust-analyzer; do
|
|
74
|
+
if command -v "$lsp_bin" >/dev/null 2>&1; then
|
|
75
|
+
lsp_detected=1
|
|
76
|
+
break
|
|
77
|
+
fi
|
|
78
|
+
done
|
|
79
|
+
|
|
80
|
+
# Build the bundle to a temp file, then compare bytes before rewriting.
|
|
81
|
+
# This makes the helper idempotent: identical bundle bytes -> no write,
|
|
82
|
+
# mtime stable. python3 is used so JSON encoding is canonical.
|
|
83
|
+
local tmp_bundle
|
|
84
|
+
tmp_bundle="${mcp_path}.tmp.$$"
|
|
85
|
+
if ! _MCP_OUT="$tmp_bundle" _MCP_LSP="$lsp_detected" python3 -c "
|
|
86
|
+
import json, os
|
|
87
|
+
out = os.environ['_MCP_OUT']
|
|
88
|
+
lsp = os.environ.get('_MCP_LSP', '0') == '1'
|
|
89
|
+
servers = {
|
|
90
|
+
'loki-mode': {
|
|
91
|
+
'command': 'python3',
|
|
92
|
+
'args': ['-m', 'mcp.server'],
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
if lsp:
|
|
96
|
+
servers['lsp-proxy'] = {
|
|
97
|
+
'command': 'python3',
|
|
98
|
+
'args': ['-m', 'mcp.lsp_proxy'],
|
|
99
|
+
}
|
|
100
|
+
bundle = {'mcpServers': servers}
|
|
101
|
+
with open(out, 'w') as f:
|
|
102
|
+
json.dump(bundle, f, indent=2)
|
|
103
|
+
" 2>/dev/null; then
|
|
104
|
+
rm -f "$tmp_bundle" 2>/dev/null
|
|
105
|
+
return 1
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Idempotent write: only replace the file when bytes differ. cmp -s
|
|
109
|
+
# exits 0 when identical, so we keep the existing file in that case.
|
|
110
|
+
if [ -f "$mcp_path" ] && cmp -s "$tmp_bundle" "$mcp_path" 2>/dev/null; then
|
|
111
|
+
rm -f "$tmp_bundle" 2>/dev/null
|
|
112
|
+
else
|
|
113
|
+
mv -f "$tmp_bundle" "$mcp_path" 2>/dev/null || {
|
|
114
|
+
rm -f "$tmp_bundle" 2>/dev/null
|
|
115
|
+
return 1
|
|
116
|
+
}
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Emit absolute path -- python3 handles realpath portably.
|
|
120
|
+
_MCP_OUT="$mcp_path" python3 -c "
|
|
121
|
+
import os, sys
|
|
122
|
+
print(os.path.abspath(os.environ['_MCP_OUT']))
|
|
123
|
+
" 2>/dev/null
|
|
124
|
+
return 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# ---------- User overlay path ----------
|
|
128
|
+
# Echoes ~/.claude/mcp.json if it exists and is readable, else empty.
|
|
129
|
+
# Always returns 0 -- a missing overlay is a normal state, not an error.
|
|
130
|
+
loki_user_mcp_config_path() {
|
|
131
|
+
local user_path="${HOME}/.claude/mcp.json"
|
|
132
|
+
if [ -f "$user_path" ] && [ -r "$user_path" ]; then
|
|
133
|
+
printf '%s' "$user_path"
|
|
134
|
+
fi
|
|
135
|
+
return 0
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# ---------- Combined --mcp-config argv value ----------
|
|
139
|
+
# Emits a single space-separated string of paths. The caller in
|
|
140
|
+
# `providers/claude.sh` splits on whitespace and pushes each path as its own
|
|
141
|
+
# argv element so Claude's variadic `--mcp-config <configs...>` receives
|
|
142
|
+
# separate argv entries (not one joined value). Loki bundle first, then
|
|
143
|
+
# user overlay if present. Paths must not contain whitespace.
|
|
144
|
+
#
|
|
145
|
+
# Returns 1 if the Loki bundle cannot be emitted (the caller should then
|
|
146
|
+
# skip the flag entirely rather than pass a malformed value).
|
|
147
|
+
loki_mcp_config_argv() {
|
|
148
|
+
local loki_path user_path
|
|
149
|
+
loki_path=$(loki_mcp_config_path) || return 1
|
|
150
|
+
if [ -z "$loki_path" ]; then
|
|
151
|
+
return 1
|
|
152
|
+
fi
|
|
153
|
+
user_path=$(loki_user_mcp_config_path)
|
|
154
|
+
if [ -n "$user_path" ]; then
|
|
155
|
+
printf '%s %s' "$loki_path" "$user_path"
|
|
156
|
+
else
|
|
157
|
+
printf '%s' "$loki_path"
|
|
158
|
+
fi
|
|
159
|
+
return 0
|
|
160
|
+
}
|