loki-mode 7.28.2 → 7.30.0
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 +4 -3
- package/SKILL.md +8 -2
- package/VERSION +1 -1
- package/autonomy/loki +285 -26
- package/autonomy/mcp-launch.sh +282 -0
- package/autonomy/provider-offer.sh +249 -0
- package/autonomy/quickstart.sh +584 -0
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +10 -1
- package/docs/competitive/emergence-others-analysis.md +1 -1
- package/docs/competitive/replit-lovable-analysis.md +2 -2
- package/loki-ts/dist/loki.js +208 -208
- package/mcp/__init__.py +1 -1
- package/mcp/server.py +186 -35
- package/package.json +1 -1
- package/templates/simple-todo-app.md +3 -0
package/README.md
CHANGED
|
@@ -28,13 +28,14 @@
|
|
|
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
|
- **Standalone verification: `loki verify`** -- Run Loki's deterministic gates (build, tests, static analysis, secret scan, dependency audit) against any branch or PR diff, including code written by other agents or humans. CI-ready exit codes (0 VERIFIED, 1 CONCERNS, 2 BLOCKED), machine-readable evidence at `.loki/verify/evidence.json`. Inconclusive evidence is never reported as VERIFIED (v7.27.0).
|
|
30
30
|
- **Living spec and pre-build interrogation** -- `loki spec` locks a spec and detects drift deterministically (`spec.lock`, `drift-report.json`, and a `SPEC_DRIFT` finding in `loki verify` with CI exit codes), so you can tell when the build diverges from what was agreed. `loki grill` runs a Devil's-Advocate interrogation of the spec before you build, surfacing gaps and contradictions early (v7.28.0).
|
|
31
|
+
- **Guided first build: `loki quickstart`** -- four quick questions (setup check, one-line idea, template pick, plan review) and your build starts; pressing Enter through every step builds the sample Todo app. The plan step quotes the real cost/time estimate before anything is spent, and `loki demo` now confirms its estimate the same way. If no AI provider CLI is installed, Loki offers to install Claude Code (consent-gated, interactive terminals only) (v7.29.0).
|
|
31
32
|
- **Live App Preview** -- The dashboard embeds the locally-running app in an iframe so you can interact with it immediately during a build. Use `loki preview` (alias `loki open`) to print the URL and open it in your browser. Local-first: no hosted service, no vendor lock (v7.24.0).
|
|
32
33
|
- **Compose-first fullstack** -- When a spec needs more than one service (web + database + cache) Loki generates a 12-factor `docker-compose.yml` with healthchecks, `depends_on` wiring, env-var config, and a `.env.example`. The Live App Preview surfaces the web service URL (not a database port), and health reflects the web service's Docker healthcheck so a crashed app shows as crashed even when the database stays up. Single-service apps stay on a plain run command. All local-first, no hosted service (v7.26.0).
|
|
33
34
|
- **Intelligent `loki start`** -- For interactive foreground runs the dashboard auto-opens in the browser (cross-platform; skipped in CI, SSH-without-TTY, and piped runs; opt out with `LOKI_NO_AUTO_OPEN=1`). The completion summary shows "Your app is live at <url>" so you know exactly where to try what Loki just built. The autonomous loop passes Claude Code's `--effort`, `--max-budget-usd`, and `--fallback-model` on every iteration (each gated on CLI support and individual opt-out env vars) for better long-run unattended execution (v7.25.0).
|
|
34
35
|
- **Cross-project memory** -- Episodic/semantic/procedural memory with vector search; knowledge learned on one project surfaces on the next (v5.15.0+, see `memory/engine.py`)
|
|
35
36
|
- **Self-hosted and private** -- Your keys, your infrastructure, no data leaves your network
|
|
36
37
|
- **Legacy system healing** -- `loki heal` archaeology/stabilize/isolate/modernize/validate phases (v6.67.0, see `skills/healing.md`)
|
|
37
|
-
- **MCP server** -- 34 tools (including ChromaDB code search) plus 3 resources and 2 prompts (`mcp/server.py`, with
|
|
38
|
+
- **MCP server** -- 34 tools (including ChromaDB code search) plus 3 resources and 2 prompts (`mcp/server.py`, with magic tools registered from `mcp/magic_tools.py` and the managed-memory tool from `mcp/managed_tools.py`). Of the 34, 33 are always available; `loki_memory_redact` is registered but only succeeds when `LOKI_MANAGED_AGENTS=true` and `LOKI_MANAGED_MEMORY=true`. Launch with `loki mcp` (bootstraps the Python MCP SDK on first run).
|
|
38
39
|
- **Full-stack output** -- Source code, tests, Docker Compose stacks (multi-service with healthchecks), CI/CD pipelines, audit logs
|
|
39
40
|
- **Provider-agnostic** -- runs on Claude, Codex, Cline, or Aider with automatic failover (`loki-ts/src/runner/providers.ts`); no vendor lock-in. Gemini CLI deprecated v7.5.18; Antigravity CLI coming soon.
|
|
40
41
|
- **Open source** -- Free for personal, internal, and academic use.
|
|
@@ -101,7 +102,7 @@ loki quick "build a landing page with a signup form"
|
|
|
101
102
|
|--------|---------|-------|
|
|
102
103
|
| **Bun (recommended)** | `bun install -g loki-mode` | Fastest startup for CLI commands. |
|
|
103
104
|
| **Homebrew** | `brew tap asklokesh/tap && brew install loki-mode` | Auto-installs Bun as a dep |
|
|
104
|
-
| **Docker** | `docker pull asklokesh/loki-mode:7.
|
|
105
|
+
| **Docker** | `docker pull asklokesh/loki-mode:7.30.0 && docker run --rm asklokesh/loki-mode:7.30.0 start prd.md` | Bun pre-installed in image |
|
|
105
106
|
| **npm (compat)** | `npm install -g loki-mode` | Works without Bun (bash fallback). Migrate any time with `loki self-update --to bun`. |
|
|
106
107
|
|
|
107
108
|
**Upgrading:**
|
|
@@ -161,7 +162,7 @@ The next major release sunsets the Bash runtime entirely. There is no firm calen
|
|
|
161
162
|
| Method | Command |
|
|
162
163
|
|--------|---------|
|
|
163
164
|
| **Homebrew** | `brew tap asklokesh/tap && brew install loki-mode` |
|
|
164
|
-
| **Docker** | `docker pull asklokesh/loki-mode:7.
|
|
165
|
+
| **Docker** | `docker pull asklokesh/loki-mode:7.30.0` |
|
|
165
166
|
| **Inside Claude Code** | `claude --dangerously-skip-permissions` then type "Loki Mode" |
|
|
166
167
|
| **Git clone** | `git clone https://github.com/asklokesh/loki-mode.git` |
|
|
167
168
|
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 11 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.
|
|
6
|
+
# Loki Mode v7.30.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -342,6 +342,12 @@ Two completion-trust features extend the verification gates. Full details in `sk
|
|
|
342
342
|
- **Held-out spec evals:** ~25% of checklist items (deterministic `sha256(id)` order, `N >= 4`) are reserved into `.loki/checklist/held-out.json` and excluded from the build prompt feed; the completion council blocks if a held-out item fails. Opt out with `LOKI_HELDOUT_GATE=0`. Honest limit: this guards the prompt feed, not a sandbox; the reservation file is on disk and an agent with filesystem access can read it.
|
|
343
343
|
- **Inconclusive-baseline disclosure:** when the evidence gate cannot establish a diff baseline (`no_git_repo` / `no_run_start_sha`) it writes `.loki/state/evidence-inconclusive.json` and `COMPLETION.txt` carries an honest "not independently verified" line. It never blocks non-git projects; red tests still block.
|
|
344
344
|
|
|
345
|
+
## First-run UX (v7.29.0)
|
|
346
|
+
|
|
347
|
+
- **`loki quickstart`:** guided 4-step first build (setup check, one-line idea, offline template match, plan review with real estimator figures); Enter-through-everything builds the sample Todo app; non-TTY/CI exits 2 with an automation hint.
|
|
348
|
+
- **Provider install offer:** when no provider CLI is found, doctor and the start/demo/quick/quickstart pre-flight offer to install Claude Code. Consent-gated on an interactive TTY only; the single command executed is printed first; auth handoff via `claude auth login` with readiness confirmed by `claude auth status`. Opt out: `LOKI_NO_INSTALL_OFFER=1`.
|
|
349
|
+
- **`loki demo` cost confirm:** the estimate always prints before spending; `--yes` skips the prompt, never the estimate. `LOKI_COMPLEXITY` is honored by `loki plan` with an honest forced-tier note.
|
|
350
|
+
|
|
345
351
|
---
|
|
346
352
|
|
|
347
353
|
## Concurrency and Security Hardening (v7.5.7 - v7.5.13)
|
|
@@ -392,4 +398,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
392
398
|
|
|
393
399
|
---
|
|
394
400
|
|
|
395
|
-
**v7.
|
|
401
|
+
**v7.30.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.30.0
|
package/autonomy/loki
CHANGED
|
@@ -68,6 +68,24 @@ if [ -f "$_LOKI_SCRIPT_DIR/crash.sh" ]; then
|
|
|
68
68
|
source "$_LOKI_SCRIPT_DIR/crash.sh"
|
|
69
69
|
fi
|
|
70
70
|
|
|
71
|
+
# Provider install offer (v7.29.0): shared, self-contained helper providing
|
|
72
|
+
# detect_any_provider, offer_provider_install, and provider_offer_gate. Sourced
|
|
73
|
+
# here for the bash route; doctor.ts invokes the same file via child_process so
|
|
74
|
+
# the offer copy never drifts between routes. Self-guarded against double-source.
|
|
75
|
+
if [ -f "$_LOKI_SCRIPT_DIR/provider-offer.sh" ]; then
|
|
76
|
+
# shellcheck source=provider-offer.sh
|
|
77
|
+
source "$_LOKI_SCRIPT_DIR/provider-offer.sh"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Quickstart guided interview (v7.29.0): provides cmd_quickstart and its
|
|
81
|
+
# deterministic offline template matcher. Functions-only file (no top-level
|
|
82
|
+
# command), sourced here so the dispatch case can call cmd_quickstart. Composes
|
|
83
|
+
# the provider offer (above), show_prd_plan, and cmd_start. Self-guarded.
|
|
84
|
+
if [ -f "$_LOKI_SCRIPT_DIR/quickstart.sh" ]; then
|
|
85
|
+
# shellcheck source=quickstart.sh
|
|
86
|
+
source "$_LOKI_SCRIPT_DIR/quickstart.sh"
|
|
87
|
+
fi
|
|
88
|
+
|
|
71
89
|
# Resolve the script's real path (handles symlinks)
|
|
72
90
|
resolve_script_path() {
|
|
73
91
|
local script="$1"
|
|
@@ -522,6 +540,7 @@ show_help() {
|
|
|
522
540
|
echo " quick \"task\" Quick single-task mode (lightweight, 3 iterations max)"
|
|
523
541
|
echo " monitor [path] Monitor Docker Compose services with auto-fix (v6.67.0)"
|
|
524
542
|
echo " demo Build a sample todo app end to end (real run)"
|
|
543
|
+
echo " quickstart [idea] Guided first build: setup, idea, template, plan, go"
|
|
525
544
|
echo " init [name] Project scaffolding with 21 PRD templates"
|
|
526
545
|
echo " issue <url|num> (deprecated) Use 'loki start <issue-ref>' instead"
|
|
527
546
|
echo " watch [prd] Auto-rerun on PRD file changes (v6.33.0)"
|
|
@@ -683,6 +702,13 @@ show_help() {
|
|
|
683
702
|
show_landing() {
|
|
684
703
|
local version
|
|
685
704
|
version=$(get_version)
|
|
705
|
+
# v7.30.0 (item 5): strip color when stdout is not a TTY (piped/redirected)
|
|
706
|
+
# in addition to the global NO_COLOR honoring above, so captured landing
|
|
707
|
+
# output is clean. Local overrides only; the rest of the CLI is untouched.
|
|
708
|
+
local BOLD="$BOLD" CYAN="$CYAN" NC="$NC"
|
|
709
|
+
if [ ! -t 1 ]; then
|
|
710
|
+
BOLD=''; CYAN=''; NC=''
|
|
711
|
+
fi
|
|
686
712
|
echo -e "${BOLD}Loki Mode v$version${NC} - the spec-driven builder that verifies its own work."
|
|
687
713
|
echo ""
|
|
688
714
|
echo -e "First time here? ${CYAN}loki doctor${NC} checks your setup (an AI provider CLI is required)."
|
|
@@ -690,6 +716,7 @@ show_landing() {
|
|
|
690
716
|
echo "Get started:"
|
|
691
717
|
echo -e " ${CYAN}loki start ./prd.md${NC} Build from a spec (PRD file, GitHub issue, or no arg)"
|
|
692
718
|
echo -e " ${CYAN}loki demo${NC} Build a sample todo app end to end (real run)"
|
|
719
|
+
echo -e " ${CYAN}loki web${NC} Open the visual builder to input a spec and watch agents build"
|
|
693
720
|
echo -e " ${CYAN}loki dashboard start${NC} Start the live run monitor (then: loki dashboard open)"
|
|
694
721
|
echo ""
|
|
695
722
|
echo -e "Need help? ${CYAN}loki help${NC} lists every command."
|
|
@@ -1260,6 +1287,17 @@ cmd_start() {
|
|
|
1260
1287
|
esac
|
|
1261
1288
|
done
|
|
1262
1289
|
|
|
1290
|
+
# v7.29.0: provider pre-flight gate. If no provider CLI is installed, offer
|
|
1291
|
+
# to install one (TTY) or print honest manual instructions and exit 2
|
|
1292
|
+
# (non-TTY/CI), BEFORE any spend or runner entry. This moves what used to be
|
|
1293
|
+
# an opaque deep-runner failure to a friendly, actionable top-of-run gate.
|
|
1294
|
+
# --help already returned inside the arg loop, so we never gate help.
|
|
1295
|
+
if declare -f provider_offer_gate >/dev/null 2>&1; then
|
|
1296
|
+
if ! provider_offer_gate; then
|
|
1297
|
+
exit 2
|
|
1298
|
+
fi
|
|
1299
|
+
fi
|
|
1300
|
+
|
|
1263
1301
|
# Clear any stale raw-brief marker from a PRIOR run before we decide the
|
|
1264
1302
|
# mode of THIS run. Only the brief branch (below) re-writes it. Without this,
|
|
1265
1303
|
# a brief run leaves .loki/state/brief.txt behind and a later non-brief run
|
|
@@ -2286,10 +2324,18 @@ PYDASH
|
|
|
2286
2324
|
# v7.7.30: --all preserves the legacy machine-wide kill. It runs even when
|
|
2287
2325
|
# the current folder has no live session (the "clean everything" use case),
|
|
2288
2326
|
# reaping every folder's orphaned loki-run-* temp script (SIGTERM, SIGKILL).
|
|
2327
|
+
#
|
|
2328
|
+
# The kill pattern is "loki-run-" for real users and is NOT user-overridable
|
|
2329
|
+
# at the CLI -- that is the documented machine-wide behavior. LOKI_STOP_ALL_PATTERN
|
|
2330
|
+
# exists ONLY so the test suite can exercise this exact code path against its
|
|
2331
|
+
# own uniquely-marked fake runners without SIGKILLing an unrelated live
|
|
2332
|
+
# loki-run-* on the same machine (e.g. a long SWE-bench instance). It defaults
|
|
2333
|
+
# to "loki-run-" so user-facing semantics are unchanged when unset.
|
|
2289
2334
|
if [ "$stop_all" = true ]; then
|
|
2290
|
-
|
|
2335
|
+
local _stop_all_pat="${LOKI_STOP_ALL_PATTERN:-loki-run-}"
|
|
2336
|
+
pkill -f "$_stop_all_pat" 2>/dev/null || true
|
|
2291
2337
|
sleep 0.5
|
|
2292
|
-
pkill -9 -f "
|
|
2338
|
+
pkill -9 -f "$_stop_all_pat" 2>/dev/null || true
|
|
2293
2339
|
echo -e "${RED}--all: signalled all loki-run-* processes on this machine.${NC}"
|
|
2294
2340
|
fi
|
|
2295
2341
|
}
|
|
@@ -4698,8 +4744,10 @@ cmd_web_stop() {
|
|
|
4698
4744
|
rm -f "$pids_file" 2>/dev/null || true
|
|
4699
4745
|
fi
|
|
4700
4746
|
|
|
4701
|
-
#
|
|
4702
|
-
#
|
|
4747
|
+
# Companion cleanup: stop the dashboard server that the web UI runs
|
|
4748
|
+
# alongside. Scoped to the dashboard, NOT to every loki process: the
|
|
4749
|
+
# documented intent of "loki web stop" is to stop the Purple Lab web UI
|
|
4750
|
+
# session, not to reap unrelated builds.
|
|
4703
4751
|
|
|
4704
4752
|
# Kill any dashboard server (by process name and by port)
|
|
4705
4753
|
local dash_port="${LOKI_DASHBOARD_PORT:-57374}"
|
|
@@ -4724,21 +4772,14 @@ cmd_web_stop() {
|
|
|
4724
4772
|
kill -0 "$dash_port_pid" 2>/dev/null && kill -9 "$dash_port_pid" 2>/dev/null || true
|
|
4725
4773
|
fi
|
|
4726
4774
|
|
|
4727
|
-
#
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
for rpid in $run_pids; do
|
|
4736
|
-
kill -0 "$rpid" 2>/dev/null && kill -9 "$rpid" 2>/dev/null || true
|
|
4737
|
-
done
|
|
4738
|
-
if [ "$stopped" = true ]; then
|
|
4739
|
-
echo "Background processes cleaned up"
|
|
4740
|
-
fi
|
|
4741
|
-
fi
|
|
4775
|
+
# FIX-563 (v7.30.0): do NOT blanket-kill loki-run-* orchestrators here.
|
|
4776
|
+
# The prior unscoped `pgrep -f "loki-run-..."` reaped EVERY orchestrator on
|
|
4777
|
+
# the machine, including foreign `loki start` sessions launched from other
|
|
4778
|
+
# terminals/CWDs that the web UI never owned. Purple Lab's own build
|
|
4779
|
+
# processes are already reaped authoritatively above via child-pids.json
|
|
4780
|
+
# (the only PIDs this session actually started). Foreign builds survive,
|
|
4781
|
+
# mirroring the cwd-scoped dashboard-stop pattern (FIX rationale: a user
|
|
4782
|
+
# invoking "loki web stop" expects the web UI gone, not their other builds).
|
|
4742
4783
|
|
|
4743
4784
|
# Clean up all PID files globally
|
|
4744
4785
|
rm -f "${LOKI_DIR}/dashboard/dashboard.pid" 2>/dev/null || true
|
|
@@ -7728,15 +7769,19 @@ cmd_doctor() {
|
|
|
7728
7769
|
doctor_check "Cline CLI" cline optional || true
|
|
7729
7770
|
doctor_check "Aider CLI" aider optional || true
|
|
7730
7771
|
|
|
7731
|
-
# Check if at least one provider is installed
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
command -v "$_dp" &>/dev/null && _any_provider=true && break
|
|
7735
|
-
done
|
|
7736
|
-
if ! $_any_provider; then
|
|
7772
|
+
# Check if at least one provider is installed (detect_any_provider is the
|
|
7773
|
+
# shared helper from provider-offer.sh, extracted from this exact loop).
|
|
7774
|
+
if ! detect_any_provider; then
|
|
7737
7775
|
echo -e " ${RED}FAIL${NC} No AI provider CLI installed -- at least one is required"
|
|
7738
7776
|
echo -e " ${YELLOW}Install: npm install -g @anthropic-ai/claude-code${NC}"
|
|
7739
7777
|
fail_count=$((fail_count + 1))
|
|
7778
|
+
# v7.29.0: on a TTY (non-json), append the consent-gated install offer.
|
|
7779
|
+
# In report mode the helper is a no-op on non-TTY/CI, so the doctor
|
|
7780
|
+
# output stays byte-identical for the bun-parity matrix. --json never
|
|
7781
|
+
# reaches here (cmd_doctor dispatches to cmd_doctor_json earlier).
|
|
7782
|
+
if declare -f offer_provider_install >/dev/null 2>&1; then
|
|
7783
|
+
offer_provider_install report || true
|
|
7784
|
+
fi
|
|
7740
7785
|
fi
|
|
7741
7786
|
echo ""
|
|
7742
7787
|
|
|
@@ -9030,6 +9075,77 @@ cmd_sandbox() {
|
|
|
9030
9075
|
exec "$SANDBOX_SH" "$subcommand" "$@"
|
|
9031
9076
|
}
|
|
9032
9077
|
|
|
9078
|
+
# v7.29.0 (feature #4): print the SIMPLE-tier cost/time/iteration estimate for
|
|
9079
|
+
# the demo PRD, parsed from the single estimator source (show_prd_plan JSON).
|
|
9080
|
+
# Because `loki demo` always runs `loki start --simple` (which exports
|
|
9081
|
+
# LOKI_COMPLEXITY=simple), the estimate is computed with LOKI_COMPLEXITY=simple
|
|
9082
|
+
# so the quoted figures are the ones the demo will actually incur -- honest by
|
|
9083
|
+
# construction (the keystone fix makes show_prd_plan honor LOKI_COMPLEXITY).
|
|
9084
|
+
# Returns 0 when a real estimate was printed, 1 when the estimator failed (the
|
|
9085
|
+
# caller then falls back to a no-number confirm). Never fabricates a number.
|
|
9086
|
+
emit_demo_estimate() {
|
|
9087
|
+
local prd_path="$1"
|
|
9088
|
+
# v7.30.0 (item 5): the demo estimate always prints before spending, incl.
|
|
9089
|
+
# non-TTY (--dry-run / --yes piped), so gate color on a TTY here too (the
|
|
9090
|
+
# global vars are only blanked on NO_COLOR). Mirrors provider-offer.sh and
|
|
9091
|
+
# quickstart.sh. Local overrides only.
|
|
9092
|
+
local BOLD="$BOLD" YELLOW="$YELLOW" DIM="$DIM" NC="$NC"
|
|
9093
|
+
if [ ! -t 1 ]; then
|
|
9094
|
+
BOLD=''; YELLOW=''; DIM=''; NC=''
|
|
9095
|
+
fi
|
|
9096
|
+
local plan_json=""
|
|
9097
|
+
plan_json=$(LOKI_COMPLEXITY=simple show_prd_plan "$prd_path" "true" "false" 2>/dev/null) || plan_json=""
|
|
9098
|
+
|
|
9099
|
+
if [ -z "$plan_json" ]; then
|
|
9100
|
+
echo -e "${YELLOW}Could not compute a cost estimate (the estimator did not return a result).${NC}"
|
|
9101
|
+
return 1
|
|
9102
|
+
fi
|
|
9103
|
+
|
|
9104
|
+
local parsed
|
|
9105
|
+
parsed=$(printf '%s' "$plan_json" | python3 -c "
|
|
9106
|
+
import json, sys
|
|
9107
|
+
try:
|
|
9108
|
+
d = json.load(sys.stdin)
|
|
9109
|
+
except Exception:
|
|
9110
|
+
sys.exit(1)
|
|
9111
|
+
cost = d.get('cost', {}).get('total_usd')
|
|
9112
|
+
time_est = d.get('time', {}).get('estimated')
|
|
9113
|
+
iters = d.get('iterations', {}).get('estimated')
|
|
9114
|
+
rng = d.get('iterations', {}).get('range', [])
|
|
9115
|
+
tier = d.get('complexity', {}).get('tier', 'simple')
|
|
9116
|
+
if cost is None or time_est is None or iters is None:
|
|
9117
|
+
sys.exit(1)
|
|
9118
|
+
rng_str = ''
|
|
9119
|
+
if isinstance(rng, list) and len(rng) == 2:
|
|
9120
|
+
rng_str = ' (range {}-{})'.format(rng[0], rng[1])
|
|
9121
|
+
print(tier.upper())
|
|
9122
|
+
print('{:.2f}'.format(float(cost)))
|
|
9123
|
+
print(time_est)
|
|
9124
|
+
print('{}{}'.format(iters, rng_str))
|
|
9125
|
+
" 2>/dev/null) || parsed=""
|
|
9126
|
+
|
|
9127
|
+
if [ -z "$parsed" ]; then
|
|
9128
|
+
echo -e "${YELLOW}Could not compute a cost estimate (the estimator did not return a result).${NC}"
|
|
9129
|
+
return 1
|
|
9130
|
+
fi
|
|
9131
|
+
|
|
9132
|
+
local tier_u cost_u time_u iter_u
|
|
9133
|
+
tier_u=$(printf '%s' "$parsed" | sed -n '1p')
|
|
9134
|
+
cost_u=$(printf '%s' "$parsed" | sed -n '2p')
|
|
9135
|
+
time_u=$(printf '%s' "$parsed" | sed -n '3p')
|
|
9136
|
+
iter_u=$(printf '%s' "$parsed" | sed -n '4p')
|
|
9137
|
+
|
|
9138
|
+
echo -e "${BOLD}Estimate (${tier_u} tier, the path this demo actually runs):${NC}"
|
|
9139
|
+
printf ' Cost: ~$%s\n' "$cost_u"
|
|
9140
|
+
echo " Time: ~$time_u"
|
|
9141
|
+
echo " Iterations: $iter_u"
|
|
9142
|
+
echo ""
|
|
9143
|
+
echo -e "${DIM}This is an estimate. Actual usage depends on PRD complexity,"
|
|
9144
|
+
echo -e "code review cycles, and test failures.${NC}"
|
|
9145
|
+
echo ""
|
|
9146
|
+
return 0
|
|
9147
|
+
}
|
|
9148
|
+
|
|
9033
9149
|
# Demo mode - build a real project from a bundled PRD template
|
|
9034
9150
|
cmd_demo() {
|
|
9035
9151
|
# Handle --help
|
|
@@ -9055,6 +9171,16 @@ cmd_demo() {
|
|
|
9055
9171
|
return 0
|
|
9056
9172
|
fi
|
|
9057
9173
|
|
|
9174
|
+
# v7.30.0 (item 5): demo emits user-facing color (header, dry-run block,
|
|
9175
|
+
# cancel/refuse messages) on paths that always run even when piped/non-TTY,
|
|
9176
|
+
# so gate color on a TTY here (the global vars are only blanked on NO_COLOR).
|
|
9177
|
+
# Placed AFTER the --help early return so help output is untouched. Local
|
|
9178
|
+
# overrides only; mirrors provider-offer.sh / quickstart.sh.
|
|
9179
|
+
local BOLD="$BOLD" CYAN="$CYAN" YELLOW="$YELLOW" GREEN="$GREEN" DIM="$DIM" RED="$RED" NC="$NC"
|
|
9180
|
+
if [ ! -t 1 ]; then
|
|
9181
|
+
BOLD=''; CYAN=''; YELLOW=''; GREEN=''; DIM=''; RED=''; NC=''
|
|
9182
|
+
fi
|
|
9183
|
+
|
|
9058
9184
|
local version
|
|
9059
9185
|
version=$(get_version)
|
|
9060
9186
|
local demo_prd="$SKILL_DIR/templates/simple-todo-app.md"
|
|
@@ -9062,10 +9188,21 @@ cmd_demo() {
|
|
|
9062
9188
|
local provider=""
|
|
9063
9189
|
local dry_run=false
|
|
9064
9190
|
local start_args=()
|
|
9191
|
+
# v7.29.0 (feature #4): demo now shows the FORCED-simple cost estimate
|
|
9192
|
+
# before spending and asks for confirmation. --yes / LOKI_ASSUME_YES skip
|
|
9193
|
+
# the prompt but still print the estimate (the spend is never hidden).
|
|
9194
|
+
local assume_yes=false
|
|
9195
|
+
if [ "${LOKI_ASSUME_YES:-}" = "1" ] || [ "${LOKI_ASSUME_YES:-}" = "true" ]; then
|
|
9196
|
+
assume_yes=true
|
|
9197
|
+
fi
|
|
9065
9198
|
|
|
9066
9199
|
# Parse arguments
|
|
9067
9200
|
while [[ $# -gt 0 ]]; do
|
|
9068
9201
|
case "$1" in
|
|
9202
|
+
--yes|-y)
|
|
9203
|
+
assume_yes=true
|
|
9204
|
+
shift
|
|
9205
|
+
;;
|
|
9069
9206
|
--dir)
|
|
9070
9207
|
if [[ -z "${2:-}" ]]; then
|
|
9071
9208
|
echo -e "${RED}Error: --dir requires a path${NC}"
|
|
@@ -9106,6 +9243,17 @@ cmd_demo() {
|
|
|
9106
9243
|
esac
|
|
9107
9244
|
done
|
|
9108
9245
|
|
|
9246
|
+
# v7.29.0: provider pre-flight gate (skipped for --dry-run, which never
|
|
9247
|
+
# spends). On a TTY, offer to install; on non-TTY/CI, print honest manual
|
|
9248
|
+
# instructions and exit 2 before any spend. Detect-first, so the nested
|
|
9249
|
+
# cmd_start gate below no-ops once a provider is present. --help already
|
|
9250
|
+
# returned at the top of cmd_demo.
|
|
9251
|
+
if [ "$dry_run" != true ] && declare -f provider_offer_gate >/dev/null 2>&1; then
|
|
9252
|
+
if ! provider_offer_gate; then
|
|
9253
|
+
exit 2
|
|
9254
|
+
fi
|
|
9255
|
+
fi
|
|
9256
|
+
|
|
9109
9257
|
# Fall back to examples/ if templates/ doesn't exist
|
|
9110
9258
|
if [ ! -f "$demo_prd" ]; then
|
|
9111
9259
|
demo_prd="$SKILL_DIR/examples/simple-todo-app.md"
|
|
@@ -9146,6 +9294,9 @@ cmd_demo() {
|
|
|
9146
9294
|
fi
|
|
9147
9295
|
|
|
9148
9296
|
if [ "$dry_run" = true ]; then
|
|
9297
|
+
# v7.29.0: dry-run is now a genuine cost preview -- show the estimate,
|
|
9298
|
+
# no prompt, no spend.
|
|
9299
|
+
emit_demo_estimate "$demo_dir/prd.md" || true
|
|
9149
9300
|
echo -e "${YELLOW}[dry-run] Would run:${NC}"
|
|
9150
9301
|
echo " cd $demo_dir"
|
|
9151
9302
|
echo -n " loki start prd.md --simple --yes"
|
|
@@ -9156,6 +9307,52 @@ cmd_demo() {
|
|
|
9156
9307
|
return 0
|
|
9157
9308
|
fi
|
|
9158
9309
|
|
|
9310
|
+
# v7.29.0 (feature #4): cost confirm before any spend. The estimate ALWAYS
|
|
9311
|
+
# prints (even with --yes), so the spend is never hidden. The prompt is
|
|
9312
|
+
# shown only on a TTY without --yes; non-TTY without --yes refuses (exit 2)
|
|
9313
|
+
# rather than hanging on read -- the project's "never hang in CI" idiom.
|
|
9314
|
+
local estimate_ok=true
|
|
9315
|
+
emit_demo_estimate "$demo_dir/prd.md" || estimate_ok=false
|
|
9316
|
+
|
|
9317
|
+
# Interactive only when stdin is a real TTY and CI is not forcing
|
|
9318
|
+
# non-interactive (matches the project's first-run gate semantics).
|
|
9319
|
+
local demo_interactive=true
|
|
9320
|
+
if [ ! -t 0 ] || [ -n "${CI:-}" ]; then
|
|
9321
|
+
demo_interactive=false
|
|
9322
|
+
fi
|
|
9323
|
+
|
|
9324
|
+
if [ "$assume_yes" != true ]; then
|
|
9325
|
+
if [ "$demo_interactive" = true ]; then
|
|
9326
|
+
local demo_answer=""
|
|
9327
|
+
if [ "$estimate_ok" = true ]; then
|
|
9328
|
+
# Default YES: Enter proceeds.
|
|
9329
|
+
echo -n "Build the Todo app now? [Y/n] "
|
|
9330
|
+
read -r demo_answer </dev/tty 2>/dev/null || demo_answer=""
|
|
9331
|
+
if [[ -n "$demo_answer" && ! "$demo_answer" =~ ^[Yy] ]]; then
|
|
9332
|
+
echo ""
|
|
9333
|
+
echo -e "Cancelled. Nothing was spent. The demo PRD is at:"
|
|
9334
|
+
echo -e " ${CYAN}$demo_dir/prd.md${NC}"
|
|
9335
|
+
echo -e "Run 'loki plan ${demo_dir}/prd.md' for the full breakdown."
|
|
9336
|
+
return 0
|
|
9337
|
+
fi
|
|
9338
|
+
else
|
|
9339
|
+
# No honest number available: default NO (the safe direction).
|
|
9340
|
+
echo -n "Proceed with the demo build anyway? [y/N] "
|
|
9341
|
+
read -r demo_answer </dev/tty 2>/dev/null || demo_answer=""
|
|
9342
|
+
if [[ ! "$demo_answer" =~ ^[Yy] ]]; then
|
|
9343
|
+
echo ""
|
|
9344
|
+
echo -e "Cancelled. Nothing was spent. The demo PRD is at:"
|
|
9345
|
+
echo -e " ${CYAN}$demo_dir/prd.md${NC}"
|
|
9346
|
+
return 0
|
|
9347
|
+
fi
|
|
9348
|
+
fi
|
|
9349
|
+
else
|
|
9350
|
+
# Non-TTY without --yes: never hang; refuse with an honest message.
|
|
9351
|
+
echo "demo needs confirmation; re-run with --yes to proceed non-interactively" >&2
|
|
9352
|
+
return 2
|
|
9353
|
+
fi
|
|
9354
|
+
fi
|
|
9355
|
+
|
|
9159
9356
|
echo -e "${DIM}Starting autonomous build...${NC}"
|
|
9160
9357
|
echo -e "${DIM}(Press Ctrl+C to stop at any time)${NC}"
|
|
9161
9358
|
echo ""
|
|
@@ -9380,6 +9577,15 @@ cmd_quick() {
|
|
|
9380
9577
|
exit 2
|
|
9381
9578
|
fi
|
|
9382
9579
|
|
|
9580
|
+
# v7.29.0: provider pre-flight gate. Offer install on a TTY; print honest
|
|
9581
|
+
# manual instructions and exit 2 on non-TTY/CI, before any spend. --help and
|
|
9582
|
+
# the no-args usage path already returned/exited above.
|
|
9583
|
+
if declare -f provider_offer_gate >/dev/null 2>&1; then
|
|
9584
|
+
if ! provider_offer_gate; then
|
|
9585
|
+
exit 2
|
|
9586
|
+
fi
|
|
9587
|
+
fi
|
|
9588
|
+
|
|
9383
9589
|
local task_desc="$*"
|
|
9384
9590
|
local version=$(get_version)
|
|
9385
9591
|
local max_iter="${LOKI_MAX_ITERATIONS:-3}"
|
|
@@ -11451,6 +11657,28 @@ cmd_grill() {
|
|
|
11451
11657
|
return $?
|
|
11452
11658
|
}
|
|
11453
11659
|
|
|
11660
|
+
# ---------------------------------------------------------------------------
|
|
11661
|
+
# loki mcp - launch the MCP (Model Context Protocol) server
|
|
11662
|
+
#
|
|
11663
|
+
# Thin dispatcher that sources autonomy/mcp-launch.sh and delegates to
|
|
11664
|
+
# mcp_launch_main(). The launcher checks for python3 + the MCP SDK and, when
|
|
11665
|
+
# the SDK is missing, offers a consent-gated bootstrap into a project-local
|
|
11666
|
+
# virtualenv (.loki/mcp-venv) before exec'ing the server over stdio. This
|
|
11667
|
+
# closes the fresh-npm-consumer gap where the only bin was `loki` and the
|
|
11668
|
+
# Python MCP dependencies were never installed (task 562).
|
|
11669
|
+
# ---------------------------------------------------------------------------
|
|
11670
|
+
cmd_mcp() {
|
|
11671
|
+
local mcp_mod="$_LOKI_SCRIPT_DIR/mcp-launch.sh"
|
|
11672
|
+
if [ ! -f "$mcp_mod" ]; then
|
|
11673
|
+
echo -e "${RED}Error: mcp launcher not found at $mcp_mod${NC}" >&2
|
|
11674
|
+
return 3
|
|
11675
|
+
fi
|
|
11676
|
+
# shellcheck source=/dev/null
|
|
11677
|
+
source "$mcp_mod"
|
|
11678
|
+
mcp_launch_main "$@"
|
|
11679
|
+
return $?
|
|
11680
|
+
}
|
|
11681
|
+
|
|
11454
11682
|
cmd_heal_help() {
|
|
11455
11683
|
echo -e "${BOLD}loki heal${NC} - Legacy system healing (v6.67.0)"
|
|
11456
11684
|
echo ""
|
|
@@ -12833,6 +13061,20 @@ show_verbose = sys.argv[3] == 'true'
|
|
|
12833
13061
|
session_model_env = (os.environ.get('LOKI_SESSION_MODEL', 'sonnet') or 'sonnet').strip().lower()
|
|
12834
13062
|
legacy_tier_switching = (os.environ.get('LOKI_LEGACY_TIER_SWITCHING', 'false') or 'false').strip().lower() == 'true'
|
|
12835
13063
|
|
|
13064
|
+
# v7.29.0: Honor LOKI_COMPLEXITY -- the SAME env var the runner honors at
|
|
13065
|
+
# run.sh:920 -- so a forced-tier run (e.g. 'loki demo' / 'loki start --simple'
|
|
13066
|
+
# which export LOKI_COMPLEXITY=simple) quotes the tier it will actually run,
|
|
13067
|
+
# not the content-derived tier. Without this the estimate diverges from
|
|
13068
|
+
# execution (audit finding #9). Runner vocabulary is auto|simple|standard|complex;
|
|
13069
|
+
# the estimator's internal tiers are simple|moderate|complex|enterprise, so
|
|
13070
|
+
# 'standard' aliases to 'moderate' (the 6-12 iteration analog). 'auto', empty,
|
|
13071
|
+
# and any unrecognized value are ignored (content tier is used) -- honest by
|
|
13072
|
+
# construction. The override is applied AFTER the content score-to-tier map.
|
|
13073
|
+
_forced_complexity_raw = (os.environ.get('LOKI_COMPLEXITY', '') or '').strip().lower()
|
|
13074
|
+
forced_complexity = {'standard': 'moderate'}.get(_forced_complexity_raw, _forced_complexity_raw)
|
|
13075
|
+
if forced_complexity not in ('simple', 'moderate', 'complex', 'enterprise'):
|
|
13076
|
+
forced_complexity = ''
|
|
13077
|
+
|
|
12836
13078
|
# Map session model name to tier key used in tokens_per_tier below.
|
|
12837
13079
|
# Unknown models fall through to 'development' (Sonnet) as a safe default.
|
|
12838
13080
|
_session_tier_map = {
|
|
@@ -12985,6 +13227,14 @@ elif complexity_score <= 5:
|
|
|
12985
13227
|
else:
|
|
12986
13228
|
complexity = 'enterprise'
|
|
12987
13229
|
|
|
13230
|
+
# v7.29.0: apply the forced-tier override AFTER the content score-to-tier map,
|
|
13231
|
+
# so it is not clobbered by the assignment above. Everything downstream
|
|
13232
|
+
# (iterations, tokens, cost, time) derives from the complexity variable, so the
|
|
13233
|
+
# whole estimate becomes consistent with what the run will actually do.
|
|
13234
|
+
if forced_complexity:
|
|
13235
|
+
complexity = forced_complexity
|
|
13236
|
+
complexity_reasons.insert(0, 'Complexity forced via LOKI_COMPLEXITY=' + _forced_complexity_raw)
|
|
13237
|
+
|
|
12988
13238
|
# --- Estimate iterations ---
|
|
12989
13239
|
iteration_map = {
|
|
12990
13240
|
'simple': (3, 5),
|
|
@@ -13215,7 +13465,10 @@ if ui_count > 0:
|
|
|
13215
13465
|
color = {'simple': GREEN, 'moderate': YELLOW, 'complex': RED, 'enterprise': RED}
|
|
13216
13466
|
cx_color = color.get(complexity, NC)
|
|
13217
13467
|
print(f'\n{CYAN}Complexity{NC}')
|
|
13218
|
-
|
|
13468
|
+
if forced_complexity:
|
|
13469
|
+
print(f' Tier: {cx_color}{BOLD}{complexity.upper()}{NC} (forced via LOKI_COMPLEXITY={_forced_complexity_raw})')
|
|
13470
|
+
else:
|
|
13471
|
+
print(f' Tier: {cx_color}{BOLD}{complexity.upper()}{NC} (score: {complexity_score})')
|
|
13219
13472
|
for reason in complexity_reasons:
|
|
13220
13473
|
print(f' {DIM}- {reason}{NC}')
|
|
13221
13474
|
|
|
@@ -13446,6 +13699,9 @@ main() {
|
|
|
13446
13699
|
demo)
|
|
13447
13700
|
cmd_demo "$@"
|
|
13448
13701
|
;;
|
|
13702
|
+
quickstart)
|
|
13703
|
+
cmd_quickstart "$@"
|
|
13704
|
+
;;
|
|
13449
13705
|
welcome)
|
|
13450
13706
|
cmd_welcome "$@"
|
|
13451
13707
|
;;
|
|
@@ -13609,6 +13865,9 @@ main() {
|
|
|
13609
13865
|
grill)
|
|
13610
13866
|
cmd_grill "$@"
|
|
13611
13867
|
;;
|
|
13868
|
+
mcp)
|
|
13869
|
+
cmd_mcp "$@"
|
|
13870
|
+
;;
|
|
13612
13871
|
migrate)
|
|
13613
13872
|
cmd_migrate "$@"
|
|
13614
13873
|
;;
|