loki-mode 7.12.0 → 7.14.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/SKILL.md +4 -2
- package/VERSION +1 -1
- package/autonomy/lib/wiki-ask.py +137 -0
- package/autonomy/lib/wiki-generator.py +322 -0
- package/autonomy/lib/wiki_index.py +258 -0
- package/autonomy/lib/wiki_llm.py +140 -0
- package/autonomy/loki +304 -11
- package/autonomy/run.sh +62 -12
- package/bin/loki +1 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +202 -0
- package/dashboard/static/index.html +405 -329
- package/docs/INSTALLATION.md +1 -1
- package/docs/R5-AUTO-WIKI-DESIGN.md +137 -0
- package/docs/R6-ROLLBACK-CHECKPOINT-PLAN.md +107 -0
- package/loki-ts/dist/loki.js +245 -206
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
package/docs/INSTALLATION.md
CHANGED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# R5: Auto-wiki + Cited Codebase Q&A (Loki's DeepWiki) -- Design Note
|
|
2
|
+
|
|
3
|
+
Status: implemented in worktree (do not commit to main). Target release: R5 of the
|
|
4
|
+
competitive-stickiness arc. NO version bump in this worktree.
|
|
5
|
+
|
|
6
|
+
## Goal
|
|
7
|
+
|
|
8
|
+
A persistent, queryable per-project wiki generated from the codebase, surfaced in
|
|
9
|
+
the dashboard, with cited answers. Loki's answer to Devin DeepWiki. Sections cite
|
|
10
|
+
the real source files they were built from; `loki wiki ask` returns a grounded,
|
|
11
|
+
cited answer (citations = `file:line`). Generation is incremental: it skips when
|
|
12
|
+
the codebase has not changed (reuses the R1 codebase-signature idea).
|
|
13
|
+
|
|
14
|
+
## What already exists (verified against source, 2026-06-03)
|
|
15
|
+
|
|
16
|
+
| Asset | File | Reused? |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| `loki docs generate` (LLM-written README/ARCHITECTURE/...) | `autonomy/loki:20577` (`cmd_docs`) | Patterns reused: `_docs_scan_project`, `_docs_build_context`, `_docs_invoke_provider`, `_docs_write_manifest`. Not the command itself -- docs has no citations and no Q&A. |
|
|
19
|
+
| Proof-of-run generator (Python core, thin CLI readers) | `autonomy/lib/proof-generator.py`, bash `cmd_proof`, `loki-ts/src/commands/proof.ts` | Architecture precedent reused exactly: Python core does the heavy work; bash + Bun are thin readers; dashboard exposes read APIs. |
|
|
20
|
+
| PII redaction | `autonomy/lib/proof_redact.py` (`redact_tree`, `_redact_paths`) | Reused: wiki output is normalized to repo-relative paths and passed through the redactor so no `/Users/<name>/...` leaks. |
|
|
21
|
+
| Org knowledge graph | `memory/knowledge_graph.py` (`OrganizationKnowledgeGraph`) | Token-overlap scoring idea reused (`_tokenize` / `query_patterns`). NOT a codebase index -- it aggregates `.loki/memory/semantic/*.json` patterns across projects, keyed on `~/.loki/knowledge`. See "Honest reuse" below. |
|
|
22
|
+
| Memory retrieval | `memory/retrieval.py` (`MemoryRetrieval`) | Inspected. It retrieves memory entries (episodic/semantic/procedural), NOT source code. Not a code indexer. Not reused for code retrieval. |
|
|
23
|
+
| Dashboard read-API + traversal-safety | `dashboard/server.py:7191` (`_safe_proof_run_dir`) | Pattern reused for the wiki section/path param (`_safe_wiki_section`). |
|
|
24
|
+
| Dashboard web components | `dashboard-ui/components/*.js` (Web Components) | New `loki-wiki-browser.js` follows the same `LokiElement` convention; registered in `index.js`. |
|
|
25
|
+
|
|
26
|
+
### Honest reuse statement
|
|
27
|
+
|
|
28
|
+
The task says "reuse memory/knowledge_graph.py and memory/retrieval.py." Both were
|
|
29
|
+
read. Neither is a *codebase* index: `knowledge_graph.py` aggregates cross-project
|
|
30
|
+
*memory patterns* (`.loki/memory/semantic`), and `retrieval.py` retrieves *memory
|
|
31
|
+
entries*, not source files. There was no existing per-file code index to query for
|
|
32
|
+
grounded citations. So R5 adds a new lightweight, dependency-free code index
|
|
33
|
+
(`autonomy/lib/wiki_index.py`) and reuses the parts that genuinely fit: the
|
|
34
|
+
token-overlap retrieval scoring (ported from `knowledge_graph._tokenize` /
|
|
35
|
+
`query_patterns`), the docs scanner, the proof manifest/signature idea, and the
|
|
36
|
+
redactor. Reuse is stated where real; not fabricated to satisfy a constraint.
|
|
37
|
+
|
|
38
|
+
ChromaDB (`tools/index-codebase.py`, MEMORY.md) is an OPTIONAL future backend. The
|
|
39
|
+
core deliberately does NOT depend on it: it needs Docker + python3.12 and is not
|
|
40
|
+
CI-safe. Default retrieval is deterministic and dependency-light.
|
|
41
|
+
|
|
42
|
+
## The grounding contract (the part that must be right)
|
|
43
|
+
|
|
44
|
+
Fabricated citations are made structurally impossible, not merely prompt-discouraged:
|
|
45
|
+
|
|
46
|
+
1. **Index**: `wiki_index.py` scans source files (git-tracked when available, else a
|
|
47
|
+
filtered `find`), splits each into line-anchored chunks
|
|
48
|
+
`{file, start_line, end_line, text}` where `file` is REPO-RELATIVE.
|
|
49
|
+
2. **Retrieve** (`ask`): deterministic token-overlap scoring (no LLM, no network)
|
|
50
|
+
selects the top-K chunks for the question. Each is a record we own.
|
|
51
|
+
3. **Prompt**: the LLM sees NUMBERED chunks `[1]..[K]` and is told to cite by chunk
|
|
52
|
+
index only (`[1]`, `[2]`). It never emits raw paths.
|
|
53
|
+
4. **Map + validate**: indices in the answer are mapped back to `{file, start_line}`
|
|
54
|
+
from the retrieval records. Every citation is then validated against the
|
|
55
|
+
filesystem (file exists AND start_line <= file length). Non-resolving citations
|
|
56
|
+
are DROPPED. The LLM can only reference chunks we supplied, and only ones that
|
|
57
|
+
resolve on disk -- so a fabricated citation cannot survive.
|
|
58
|
+
5. **generate**: per-section "sources" are CODE-DERIVED (the files the scanner read,
|
|
59
|
+
the real def/class line numbers from a grep parse), not LLM-emitted. The LLM
|
|
60
|
+
writes prose; the citation list comes from the index.
|
|
61
|
+
|
|
62
|
+
If the LLM is unavailable (CI, no provider), `ask` returns an EXTRACTIVE answer
|
|
63
|
+
(the top chunk snippets with their real citations) and `generate` writes a
|
|
64
|
+
template-based wiki whose citations are still the real scanned files. No fabrication
|
|
65
|
+
in any path.
|
|
66
|
+
|
|
67
|
+
## Mocking the LLM in CI
|
|
68
|
+
|
|
69
|
+
The Python core reads `LOKI_WIKI_LLM_STUB`:
|
|
70
|
+
- unset -> call the real provider via the same path `_docs_invoke_provider` uses
|
|
71
|
+
(`claude -p` etc.), OR fall back to extractive/template if no provider on PATH.
|
|
72
|
+
- set to a file path -> read the stubbed completion from that file.
|
|
73
|
+
- set to any other value -> use it literally as the completion.
|
|
74
|
+
|
|
75
|
+
Tests set `LOKI_WIKI_LLM_STUB` so CI makes ZERO paid calls. This mirrors how the
|
|
76
|
+
proof tests fake `gh`/`open` via PATH and env.
|
|
77
|
+
|
|
78
|
+
## Storage layout (per project, generated)
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
.loki/wiki/
|
|
82
|
+
wiki.json # structured: sections[], each with title, body, citations[]
|
|
83
|
+
index.md # human-readable rendered wiki (overview + module list)
|
|
84
|
+
architecture.md # rendered architecture section
|
|
85
|
+
modules.md # rendered key-modules section
|
|
86
|
+
data-flow.md # rendered data-flow section
|
|
87
|
+
wiki-manifest.json # signature (git sha + per-file content hash), generated_at
|
|
88
|
+
code-index.json # the chunk index (file, start_line, end_line, tokens)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
NOTE: `.loki/wiki/` (this deliverable, per-project, generated, gitignored) is a
|
|
92
|
+
DIFFERENT namespace from the repo-root `wiki/` (the GitHub wiki in the release
|
|
93
|
+
workflow). R5 never touches the latter.
|
|
94
|
+
|
|
95
|
+
## Incremental regeneration (R1 signature idea)
|
|
96
|
+
|
|
97
|
+
`wiki-manifest.json` stores a `signature` = sha256 over (git HEAD sha + sorted list
|
|
98
|
+
of `path:content-hash` for every scanned source file). `loki wiki generate` computes
|
|
99
|
+
the current signature; if it equals the stored one, it SKIPS regeneration and prints
|
|
100
|
+
"up to date" (unless `--force`). This is the same cheap-incremental idea as the docs
|
|
101
|
+
manifest and the R1 codebase signature.
|
|
102
|
+
|
|
103
|
+
## Command surface
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
loki wiki generate [path] [--force] # build/refresh .loki/wiki/ (incremental)
|
|
107
|
+
loki wiki show [section] # print rendered wiki (or one section)
|
|
108
|
+
loki wiki ask "<question>" # grounded, cited answer (file:line)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Build surface (mirrors the proof precedent)
|
|
112
|
+
|
|
113
|
+
- `autonomy/lib/wiki_index.py` -- scan + chunk + token-overlap retrieve + signature
|
|
114
|
+
(importable module; underscore name).
|
|
115
|
+
- `autonomy/lib/wiki-generator.py` -- generate wiki.json + rendered md (LLM or
|
|
116
|
+
template), citations code-derived; subprocess-invoked (hyphen in name, like
|
|
117
|
+
proof-generator.py).
|
|
118
|
+
- `autonomy/lib/wiki-ask.py` -- retrieve K chunks, prompt (stub-aware), map + validate
|
|
119
|
+
citations, print grounded answer. Subprocess-invoked.
|
|
120
|
+
- bash `cmd_wiki` in `autonomy/loki` (generate|show|ask) -- thin dispatcher to Python.
|
|
121
|
+
- Bun `loki-ts/src/commands/wiki.ts` -- native `show` (reads `.loki/wiki/`); `generate`
|
|
122
|
+
and `ask` delegate to the bash/Python core (heavy work, provider). Added to the
|
|
123
|
+
`bin/loki` allowlist and `cli.ts` switch.
|
|
124
|
+
- Dashboard: `GET /api/wiki` (list sections + manifest), `GET /api/wiki/{section}`,
|
|
125
|
+
`POST /api/wiki/ask` -- all traversal-safe; web component `loki-wiki-browser.js`.
|
|
126
|
+
|
|
127
|
+
## Tests
|
|
128
|
+
|
|
129
|
+
- `tests/test_wiki_index.py` -- chunking is line-accurate; retrieval is deterministic;
|
|
130
|
+
signature stable + changes on edit; repo-relative paths only.
|
|
131
|
+
- `tests/test_wiki_generator.py` -- generate on a fixture repo; citations point to REAL
|
|
132
|
+
files; incremental skip when unchanged; LLM stubbed; no absolute paths (no PII).
|
|
133
|
+
- `tests/test_wiki_ask.py` -- `ask` returns grounded answer; every citation resolves on
|
|
134
|
+
disk; a stub that emits a bogus `[99]` index is dropped (anti-fabrication).
|
|
135
|
+
- `tests/cli/test-wiki-command.sh` -- bash route generate/show/ask on a fixture, stubbed.
|
|
136
|
+
- `loki-ts/tests/commands/wiki.test.ts` -- Bun `show` parity with the rendered md.
|
|
137
|
+
- `tests/dashboard/test_wiki_routes.py` -- API list/get/ask + traversal rejection.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# R6: 1-Click Rollback + Checkpoint UX (Design Note)
|
|
2
|
+
|
|
3
|
+
Status: implemented in this worktree (not committed to main). For integrator cherry-pick.
|
|
4
|
+
Goal: make Loki's existing checkpoint/rollback infra EXCELLENT and OBVIOUS so users
|
|
5
|
+
run autonomous work boldly, knowing a mistake is one action from undone.
|
|
6
|
+
|
|
7
|
+
## 1. Verified existing code (read, not assumed)
|
|
8
|
+
|
|
9
|
+
### Checkpoint writers (THREE divergent formats, one shared index.jsonl)
|
|
10
|
+
|
|
11
|
+
| Writer | ID format | Files captured | Source |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| `create_checkpoint()` (automatic, per iteration) | `cp-{iter}-{epoch}` | `state/orchestrator.json`, `autonomy-state.json`, `queue/{pending,completed,in-progress,current-task}.json` | `autonomy/run.sh:7373` |
|
|
14
|
+
| `cmd_checkpoint create` (manual CLI) | `cp-{ts}` | `session.json`, `dashboard-state.json`, `queue/`, `memory/`, `metrics/`, `council/` (recursive copy) | `autonomy/loki:16122` |
|
|
15
|
+
| `POST /api/checkpoints` (dashboard) | `chk-{ts}` | `dashboard-state.json`, `session.json`, `queue/` | `dashboard/server.py:5085` |
|
|
16
|
+
|
|
17
|
+
All three append to `.loki/state/checkpoints/index.jsonl` (field names differ; the
|
|
18
|
+
dashboard `GET /api/checkpoints` normalizes them).
|
|
19
|
+
|
|
20
|
+
### Restore (rollback) paths
|
|
21
|
+
|
|
22
|
+
| Path | Restores | Source |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `rollback_to_checkpoint()` (internal, run.sh) | state + queue json (NOT autonomy-state) | `autonomy/run.sh:7473` |
|
|
25
|
+
| `loki checkpoint rollback <id>` (bash CLI) | glob-restores whatever is in the cp dir | `autonomy/loki:16263` |
|
|
26
|
+
| `loki rollback to|latest <id>` (Bun) | the 5 RESTORE_FILES | `loki-ts/src/commands/rollback.ts` + `loki-ts/src/runner/checkpoint.ts:632` |
|
|
27
|
+
|
|
28
|
+
### Bun runner checkpoint API (`loki-ts/src/runner/checkpoint.ts`, 700 lines)
|
|
29
|
+
- `createCheckpoint`, `listCheckpoints`, `readCheckpoint`, `rollbackToCheckpoint` (planner),
|
|
30
|
+
`executeRollback` (copier). Byte-for-byte parity with bash `create_checkpoint`.
|
|
31
|
+
- `metadata.json` keys are pinned by `loki-ts/tests/runner/checkpoint.test.ts:62-91`
|
|
32
|
+
(`Object.keys(m).sort()`) -- they MUST NOT change.
|
|
33
|
+
|
|
34
|
+
### Dashboard
|
|
35
|
+
- `GET /api/checkpoints`, `GET /api/checkpoints/{id}`, `POST /api/checkpoints` exist.
|
|
36
|
+
- The UI component `dashboard-ui/components/loki-checkpoint-viewer.js` (601 lines) ALREADY
|
|
37
|
+
renders list + create + rollback-with-two-step-confirm and POSTs to
|
|
38
|
+
`POST /api/checkpoints/{id}/rollback` -- but that endpoint **did not exist**. The
|
|
39
|
+
dashboard rollback button was DEAD.
|
|
40
|
+
|
|
41
|
+
## 2. Three decisions (verified facts, not assumptions)
|
|
42
|
+
|
|
43
|
+
### Decision A: what "truly undo an iteration" restores
|
|
44
|
+
Verified fact: Loki does NOT commit per iteration (`grep "git commit" autonomy/run.sh`
|
|
45
|
+
finds only merge-conflict `git add`, no per-iteration commit). Therefore the captured
|
|
46
|
+
`git_sha` is HEAD (the last commit), and `git reset --hard <git_sha>` would discard the
|
|
47
|
+
iteration's work PLUS anything since the last commit -- it cannot reconstruct a specific
|
|
48
|
+
iteration's working tree. The pre-existing printed hint `git reset --hard <git_sha>`
|
|
49
|
+
(run.sh:7541, loki:16314) was therefore actively MISLEADING.
|
|
50
|
+
|
|
51
|
+
Resolution: capture a real working-tree snapshot at checkpoint time via `git stash create`
|
|
52
|
+
(captures tracked changes without disturbing the tree), then ANCHOR it with
|
|
53
|
+
`git update-ref refs/loki/cp/<id> <sha>` so `git gc` cannot prune the dangling commit.
|
|
54
|
+
The snapshot sha is written to a SIDECAR file `worktree-snapshot.txt` in the checkpoint
|
|
55
|
+
dir (NOT metadata.json, to preserve byte parity). Restore of code is opt-in
|
|
56
|
+
(`loki rollback to <id> --code`) because it overwrites tracked files; by default the
|
|
57
|
+
durable, correct recovery command is printed: `git stash apply refs/loki/cp/<id>`.
|
|
58
|
+
|
|
59
|
+
State + `.loki/CONTINUITY.md` (the iteration/conversation handoff context) are
|
|
60
|
+
auto-restored -- this is the always-on "undo the iteration's state + context".
|
|
61
|
+
|
|
62
|
+
Honest gap: `git stash create` captures tracked changes only; it does NOT capture
|
|
63
|
+
untracked or ignored files. Files the iteration newly ADDED and never committed are not in
|
|
64
|
+
the snapshot, and an apply will not delete them. Documented, not hidden.
|
|
65
|
+
|
|
66
|
+
### Decision B: re-undoability invariant
|
|
67
|
+
Every restore path FORCES a pre-rollback snapshot of current state before overwriting, so
|
|
68
|
+
a rollback is itself trivially undoable. The Bun `executePlan`, the dashboard endpoint, and
|
|
69
|
+
bash `cmd_rollback` all create a forced checkpoint first (bash `create_checkpoint`
|
|
70
|
+
early-returns on a clean tree, so the new code path forces it). The human dashboard path
|
|
71
|
+
ALSO keeps the existing two-step confirm. Autonomous paths never block on a prompt.
|
|
72
|
+
|
|
73
|
+
### Decision C: do not add a 4th format, do not unify the 3
|
|
74
|
+
Unifying would break the byte-for-byte parity test; a 4th compounds the problem. The new
|
|
75
|
+
dashboard restore endpoint GLOB-restores whatever the checkpoint dir contains (mirrors bash
|
|
76
|
+
`cmd_checkpoint rollback`), so it works for all three writers. New fields go in sidecars.
|
|
77
|
+
|
|
78
|
+
## 3. Changes (parity-organized)
|
|
79
|
+
|
|
80
|
+
- **bash `autonomy/loki`**: add top-level `rollback)` dispatch + `cmd_rollback`
|
|
81
|
+
(`list|show|to|latest`, `--code` flag). `to|latest` delegate to the existing
|
|
82
|
+
`cmd_checkpoint rollback` restore body after forcing a pre-rollback snapshot, then
|
|
83
|
+
optionally apply the anchored code snapshot. Prominent "you can undo this" output.
|
|
84
|
+
- **bash `autonomy/run.sh`**: `create_checkpoint` also copies `CONTINUITY.md`, creates +
|
|
85
|
+
anchors the worktree snapshot (sidecar), echoes the checkpoint id; `rollback_to_checkpoint`
|
|
86
|
+
also restores `CONTINUITY.md` and forces the pre-rollback snapshot. The per-iteration call
|
|
87
|
+
site emits a prominent "Checkpoint <id> created -- undo with `loki rollback to <id>`".
|
|
88
|
+
The misleading `git reset --hard` hint replaced with `git stash apply refs/loki/cp/<id>`.
|
|
89
|
+
- **Bun `loki-ts/src/runner/checkpoint.ts`**: copy `CONTINUITY.md` into the checkpoint
|
|
90
|
+
(additive, parity-safe), add `CONTINUITY.md` to RESTORE_FILES, expose
|
|
91
|
+
`executeRollbackWithSnapshot` that forces a pre-rollback snapshot before restoring.
|
|
92
|
+
metadata.json keys unchanged.
|
|
93
|
+
- **Bun `loki-ts/src/commands/rollback.ts`**: `executePlan` forces a pre-rollback snapshot
|
|
94
|
+
before restoring; HELP text made honest about state+context restore and the printed code
|
|
95
|
+
command.
|
|
96
|
+
- **dashboard `dashboard/server.py`**: add `POST /api/checkpoints/{id}/rollback`
|
|
97
|
+
(`require_scope("control")`, `_sanitize_checkpoint_id`, forced pre-snapshot, glob-restore).
|
|
98
|
+
Un-deads the existing UI button.
|
|
99
|
+
|
|
100
|
+
## 4. Tests (extend existing suites, no parallels)
|
|
101
|
+
- `loki-ts/tests/runner/checkpoint.test.ts`: CONTINUITY round-trip restore + forced
|
|
102
|
+
pre-rollback snapshot.
|
|
103
|
+
- `loki-ts/tests/commands/rollback.test.ts`: `to` forces a pre-rollback snapshot (re-undo).
|
|
104
|
+
- `tests/test-checkpoint-cli.sh`: top-level `loki rollback list|to|latest`, restore actually
|
|
105
|
+
reverts, pre-rollback snapshot exists.
|
|
106
|
+
- `dashboard/tests/test_rollback_endpoint.py`: POST rollback reverts, scope, 404.
|
|
107
|
+
No paid model calls anywhere.
|