loki-mode 7.38.0 → 7.39.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/.claude-plugin/marketplace.json +28 -0
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/claude-flags.sh +8 -4
- package/autonomy/loki +11 -7
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +3 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +28 -0
- package/plugins/loki-mode/.mcp.json +11 -0
- package/plugins/loki-mode/README.md +88 -0
- package/plugins/loki-mode/commands/loki-grill.md +47 -0
- package/plugins/loki-mode/commands/loki-spec-status.md +48 -0
- package/plugins/loki-mode/commands/loki-verify.md +38 -0
- package/plugins/loki-mode/hooks/hooks.json +15 -0
- package/plugins/loki-mode/scripts/loki-guard.sh +103 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-marketplace.json",
|
|
3
|
+
"name": "loki-mode",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "Autonomi"
|
|
6
|
+
},
|
|
7
|
+
"description": "Autonomi marketplace: Loki Mode, the autonomous spec-to-product build system with a built-in trust layer.",
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "loki-mode",
|
|
11
|
+
"source": "./plugins/loki-mode",
|
|
12
|
+
"description": "Autonomous spec-to-product build system with a built-in trust layer. Ships Loki's spec-hardening (grill), living-spec drift detection, and deterministic PR verification commands, plus the Loki MCP server (memory, task queue, code search, build management). Requires the loki-mode CLI on PATH (npm install -g loki-mode or brew install).",
|
|
13
|
+
"homepage": "https://github.com/asklokesh/loki-mode",
|
|
14
|
+
"repository": "https://github.com/asklokesh/loki-mode",
|
|
15
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
16
|
+
"category": "automation",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"autonomous",
|
|
19
|
+
"agent",
|
|
20
|
+
"spec-driven",
|
|
21
|
+
"verification",
|
|
22
|
+
"code-review",
|
|
23
|
+
"mcp",
|
|
24
|
+
"loki"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
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.39.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -398,4 +398,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
398
398
|
|
|
399
399
|
---
|
|
400
400
|
|
|
401
|
-
**v7.
|
|
401
|
+
**v7.39.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.39.0
|
|
@@ -261,14 +261,18 @@ loki_review_guard_denylist() {
|
|
|
261
261
|
# the dangerous forms. echo>/sed -i/python -c style writes are not enumerable and
|
|
262
262
|
# remain possible; the real net is commit-before-agent-wave (see CLAUDE.md).
|
|
263
263
|
#
|
|
264
|
-
# DEFAULT
|
|
265
|
-
#
|
|
266
|
-
#
|
|
264
|
+
# DEFAULT ON (safety-additive least-privilege; opt OUT with LOKI_REVIEW_ALLOWLIST=0).
|
|
265
|
+
# Flipped default-on because deny precedence (verified live) means the denylist
|
|
266
|
+
# still hard-blocks every mutation form while this allowlist only narrows the
|
|
267
|
+
# reviewer surface to read/inspect tools -- pure safety win, no surprise spend, no
|
|
268
|
+
# egress. The escape hatch (LOKI_REVIEW_ALLOWLIST=0) restores the prior off state.
|
|
269
|
+
# Gated on CLI support so an older claude degrades gracefully (emits nothing).
|
|
270
|
+
# Predicate + token so call sites append uniformly:
|
|
267
271
|
# if loki_review_allowlist_enabled; then
|
|
268
272
|
# argv+=("--allowedTools" "$(loki_review_allowlist)")
|
|
269
273
|
# fi
|
|
270
274
|
loki_review_allowlist_enabled() {
|
|
271
|
-
[ "${LOKI_REVIEW_ALLOWLIST:-
|
|
275
|
+
[ "${LOKI_REVIEW_ALLOWLIST:-1}" = "0" ] && return 1
|
|
272
276
|
loki_claude_flag_supported "--allowedTools"
|
|
273
277
|
}
|
|
274
278
|
loki_review_allowlist() {
|
package/autonomy/loki
CHANGED
|
@@ -649,13 +649,18 @@ show_help() {
|
|
|
649
649
|
# do NOT appear here; they live in the collapsed footer below and in
|
|
650
650
|
# `loki help aliases`. The full long-tail surface is still dispatchable and
|
|
651
651
|
# documented per-command via `loki <command> --help`.
|
|
652
|
+
#
|
|
653
|
+
# v7.39.0: trimmed three lower-traffic canonical entries off the front page
|
|
654
|
+
# to hold the lean target after `ultracode` (v7.38.0) joined Verify/trust:
|
|
655
|
+
# grill (pre-build spec interrogation, advanced), spec (living-spec drift,
|
|
656
|
+
# advanced), and cleanup (orphaned-process recovery, rare). They remain
|
|
657
|
+
# fully canonical (NOT aliases), dispatchable, and listed in the "More
|
|
658
|
+
# commands" footer below + documented via `loki <command> --help`.
|
|
652
659
|
echo "Commands:"
|
|
653
660
|
echo ""
|
|
654
661
|
echo "Build:"
|
|
655
662
|
echo " start [SPEC] Start a build (PRD file, GitHub issue, or no arg)"
|
|
656
663
|
echo " plan <PRD-file> Dry-run analysis: complexity, cost, execution plan"
|
|
657
|
-
echo " grill [SPEC] Devil's-advocate spec interrogation before you build"
|
|
658
|
-
echo " spec [cmd] Living spec: lock|status|sync (drift detection)"
|
|
659
664
|
echo " quickstart [idea] Guided first build: setup, idea, template, plan, go"
|
|
660
665
|
echo ""
|
|
661
666
|
echo "Session:"
|
|
@@ -663,7 +668,6 @@ show_help() {
|
|
|
663
668
|
echo " stop Stop execution immediately"
|
|
664
669
|
echo " pause Pause after current session"
|
|
665
670
|
echo " resume Resume paused execution"
|
|
666
|
-
echo " cleanup Kill orphaned processes from crashed sessions"
|
|
667
671
|
echo ""
|
|
668
672
|
echo "Verify / trust:"
|
|
669
673
|
echo " verify [base] Deterministic PR verification (CI-gate exit codes)"
|
|
@@ -692,10 +696,10 @@ show_help() {
|
|
|
692
696
|
echo " version Show version"
|
|
693
697
|
echo " help Show this help ('loki help aliases' for old names)"
|
|
694
698
|
echo ""
|
|
695
|
-
echo "More commands (init, watch, demo, web, api,
|
|
696
|
-
echo "import, council, proof, audit, agent, template, magic,
|
|
697
|
-
echo "test, bench, secrets, telemetry, crash, worktree,
|
|
698
|
-
echo "remote, ...) are dispatchable and documented via"
|
|
699
|
+
echo "More commands (grill, spec, cleanup, init, watch, demo, web, api,"
|
|
700
|
+
echo "logs, github, import, council, proof, audit, agent, template, magic,"
|
|
701
|
+
echo "docs, wiki, ci, test, bench, secrets, telemetry, crash, worktree,"
|
|
702
|
+
echo "failover, monitor, remote, ...) are dispatchable and documented via"
|
|
699
703
|
echo "'loki <command> --help'."
|
|
700
704
|
echo ""
|
|
701
705
|
echo "Aliases (deprecated): older command names still work; they print a"
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
|
|
4
4
|
|
|
5
|
-
**Version:** v7.
|
|
5
|
+
**Version:** v7.39.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null,Q)}var h=($,Q)=>{for(var Z in Q)n6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:s6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var K$=import.meta.require;var S1={};h(S1,{lokiDir:()=>P,homeLokiDir:()=>o$,findRepoRootForVersion:()=>d$,REPO_ROOT:()=>m});import{resolve as n,dirname as l$}from"path";import{fileURLToPath as t6}from"url";import{existsSync as P$}from"fs";import{homedir as r6}from"os";function i6(){let $=N1;for(let Q=0;Q<6;Q++){if(P$(n($,"VERSION"))&&P$(n($,"autonomy/run.sh")))return $;let Z=l$($);if(Z===$)break;$=Z}return n(N1,"..","..","..")}function d$($){let Q=$;for(let Z=0;Z<6;Z++){if(P$(n(Q,"VERSION"))&&P$(n(Q,"autonomy/run.sh")))return Q;let z=l$(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o$(){return n(r6(),".loki")}var N1,m;var C=L(()=>{N1=l$(t6(import.meta.url));m=i6()});import{readFileSync as e6}from"fs";import{resolve as $Q,dirname as QQ}from"path";import{fileURLToPath as ZQ}from"url";function F$(){if($$!==null)return $$;let $="7.
|
|
2
|
+
var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null,Q)}var h=($,Q)=>{for(var Z in Q)n6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:s6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var K$=import.meta.require;var S1={};h(S1,{lokiDir:()=>P,homeLokiDir:()=>o$,findRepoRootForVersion:()=>d$,REPO_ROOT:()=>m});import{resolve as n,dirname as l$}from"path";import{fileURLToPath as t6}from"url";import{existsSync as P$}from"fs";import{homedir as r6}from"os";function i6(){let $=N1;for(let Q=0;Q<6;Q++){if(P$(n($,"VERSION"))&&P$(n($,"autonomy/run.sh")))return $;let Z=l$($);if(Z===$)break;$=Z}return n(N1,"..","..","..")}function d$($){let Q=$;for(let Z=0;Z<6;Z++){if(P$(n(Q,"VERSION"))&&P$(n(Q,"autonomy/run.sh")))return Q;let z=l$(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o$(){return n(r6(),".loki")}var N1,m;var C=L(()=>{N1=l$(t6(import.meta.url));m=i6()});import{readFileSync as e6}from"fs";import{resolve as $Q,dirname as QQ}from"path";import{fileURLToPath as ZQ}from"url";function F$(){if($$!==null)return $$;let $="7.39.0";if(typeof $==="string"&&$.length>0)return $$=$,$$;try{let Q=QQ(ZQ(import.meta.url)),Z=d$(Q);$$=e6($Q(Z,"VERSION"),"utf-8").trim()}catch{$$="unknown"}return $$}var $$=null;var n$=L(()=>{C()});var C1={};h(C1,{runOrThrow:()=>zQ,run:()=>j,commandVersion:()=>KQ,commandExists:()=>f,ShellError:()=>a$});async function j($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[W,K,U]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:W,stderr:K,exitCode:U}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function zQ($,Q={}){let Z=await j($,Q);if(Z.exitCode!==0)throw new a$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=XQ($),Z=await j(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function XQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function KQ($,Q="--version"){if(!await f($))return null;let z=await j([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var a$;var d=L(()=>{a$=class a$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function a($){return WQ?"":$}var WQ,T,S,I,TZ,w,R,y,q;var c=L(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=a("\x1B[0;31m"),S=a("\x1B[0;32m"),I=a("\x1B[1;33m"),TZ=a("\x1B[0;34m"),w=a("\x1B[0;36m"),R=a("\x1B[1m"),y=a("\x1B[2m"),q=a("\x1B[0m")});import{existsSync as TQ}from"fs";async function Q$(){if(B$!==void 0)return B$;let $="/opt/homebrew/bin/python3.12";if(TQ($))return B$=$,$;let Q=await f("python3.12");if(Q)return B$=Q,Q;let Z=await f("python3");return B$=Z,Z}async function Z$($,Q={}){let Z=await Q$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return j([Z,"-c",$],Q)}var B$;var W$=L(()=>{d()});var t1={};h(t1,{runStatus:()=>gQ});import{existsSync as v,readFileSync as U$,readdirSync as l1,statSync as d1}from"fs";import{resolve as D,basename as xQ}from"path";import{homedir as NQ}from"os";async function DQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${q}
|
|
3
3
|
`),process.stdout.write(`Install with:
|
|
4
4
|
`),process.stdout.write(` brew install jq (macOS)
|
|
5
5
|
`),process.stdout.write(` apt install jq (Debian/Ubuntu)
|
|
@@ -789,4 +789,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
789
789
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
790
790
|
`),process.stderr.write(o6),2}}p1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var ZZ=await QZ(Bun.argv.slice(2));process.exit(ZZ);
|
|
791
791
|
|
|
792
|
-
//# debugId=
|
|
792
|
+
//# debugId=0053955F19A92CF864756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
3
|
"mcpName": "io.github.asklokesh/loki-mode",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.39.0",
|
|
5
5
|
"description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 11 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -67,6 +67,8 @@
|
|
|
67
67
|
"VERSION",
|
|
68
68
|
"assets/",
|
|
69
69
|
"tools/",
|
|
70
|
+
"plugins/",
|
|
71
|
+
".claude-plugin/marketplace.json",
|
|
70
72
|
"autonomy/",
|
|
71
73
|
"providers/",
|
|
72
74
|
"agents/",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "loki-mode",
|
|
4
|
+
"displayName": "Loki Mode",
|
|
5
|
+
"version": "7.39.0",
|
|
6
|
+
"description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 11 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Autonomi",
|
|
9
|
+
"url": "https://www.autonomi.dev/"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/asklokesh/loki-mode",
|
|
12
|
+
"repository": "https://github.com/asklokesh/loki-mode",
|
|
13
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"autonomous",
|
|
16
|
+
"agent",
|
|
17
|
+
"spec-driven",
|
|
18
|
+
"verification",
|
|
19
|
+
"code-review",
|
|
20
|
+
"mcp",
|
|
21
|
+
"loki"
|
|
22
|
+
],
|
|
23
|
+
"commands": [
|
|
24
|
+
"./commands/"
|
|
25
|
+
],
|
|
26
|
+
"mcpServers": "./.mcp.json",
|
|
27
|
+
"hooks": "./hooks/hooks.json"
|
|
28
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Loki Mode plugin for Claude Code
|
|
2
|
+
|
|
3
|
+
Loki Mode is the autonomous spec-to-product build system with a built-in trust
|
|
4
|
+
layer (RARV-C closure loop, 11 quality gates, completion council). This plugin
|
|
5
|
+
brings Loki's spec-hardening, drift-detection, and deterministic PR verification
|
|
6
|
+
into Claude Code as slash commands, and wires up the Loki MCP server.
|
|
7
|
+
|
|
8
|
+
Homepage: https://github.com/asklokesh/loki-mode
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
The plugin is published through the Autonomi marketplace, which lives in this
|
|
13
|
+
same repository.
|
|
14
|
+
|
|
15
|
+
1. Add the marketplace (one time):
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/plugin marketplace add asklokesh/loki-mode
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
2. Install the plugin:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
/plugin install loki-mode@loki-mode
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That is it. The commands below become available immediately.
|
|
28
|
+
|
|
29
|
+
## What you get
|
|
30
|
+
|
|
31
|
+
Slash commands (namespaced as `loki-mode:<command>`):
|
|
32
|
+
|
|
33
|
+
- `loki-mode:loki-grill` - interrogate a spec with Loki's Devil's-Advocate
|
|
34
|
+
grill before building, and summarize the hardest questions it surfaces.
|
|
35
|
+
- `loki-mode:loki-spec-status` - check whether the spec has drifted from its
|
|
36
|
+
lock using deterministic living-spec drift detection.
|
|
37
|
+
- `loki-mode:loki-verify` - run Loki's deterministic PR verification on the
|
|
38
|
+
current change and summarize the evidence verdict.
|
|
39
|
+
|
|
40
|
+
MCP server (`loki-mode`): exposes Loki's tools (memory, task queue, code
|
|
41
|
+
search, build management) to Claude Code.
|
|
42
|
+
|
|
43
|
+
## Requirement: the loki-mode CLI must be installed
|
|
44
|
+
|
|
45
|
+
The slash commands run `loki ...` subcommands, and the MCP server is launched
|
|
46
|
+
via `loki mcp`. Both require the `loki` binary to be on your PATH. Install it
|
|
47
|
+
once:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
npm install -g loki-mode
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
or with Homebrew:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
brew install asklokesh/tap/loki-mode
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The plugin ships the commands, MCP wiring, and an optional guard hook. It does
|
|
60
|
+
not bundle the Loki runtime itself, because a marketplace plugin is copied into
|
|
61
|
+
an isolated cache and cannot reach the rest of the repository. The CLI on PATH
|
|
62
|
+
is what provides the runtime.
|
|
63
|
+
|
|
64
|
+
On the MCP server's first launch, if the Python MCP SDK is not present, the
|
|
65
|
+
bundled config sets `LOKI_MCP_AUTO_BOOTSTRAP=1` as written, in-advance consent
|
|
66
|
+
so the server can create a project-local virtualenv at `.loki/mcp-venv` and
|
|
67
|
+
install its dependencies non-interactively. Remove that env var from
|
|
68
|
+
`.mcp.json` if you prefer to bootstrap manually (run `loki mcp` once in a
|
|
69
|
+
terminal and follow the printed instructions).
|
|
70
|
+
|
|
71
|
+
## Optional Bash guard hook (off by default)
|
|
72
|
+
|
|
73
|
+
The plugin ships a `PreToolUse` hook for the Bash tool that is a no-op unless
|
|
74
|
+
you opt in. Set `LOKI_GUARD=1` in your environment to enable it. When enabled it
|
|
75
|
+
blocks a small set of clearly destructive Bash commands that have bitten Loki
|
|
76
|
+
runs before:
|
|
77
|
+
|
|
78
|
+
- `rm -rf` on `/tmp/loki-*` while a live Loki run may be staging files there
|
|
79
|
+
- `rm -rf` of a filesystem or `$HOME` root
|
|
80
|
+
- `git add -A` / `git add .` (Loki convention: stage files individually)
|
|
81
|
+
|
|
82
|
+
With `LOKI_GUARD` unset the hook always allows the command through, so it never
|
|
83
|
+
interferes unless you ask it to.
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
SEE LICENSE IN LICENSE (BUSL-1.1, source-available). See the LICENSE file at the
|
|
88
|
+
repository root.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Interrogate a spec with Loki's Devil's-Advocate grill before building, and summarize the hardest questions it surfaces.
|
|
3
|
+
argument-hint: "[spec-path] (default: prd.md / .loki/generated-prd.md)"
|
|
4
|
+
allowed-tools: Bash(loki grill:*), Read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Harden a spec before any code is written. Loki's grill invokes the provider
|
|
8
|
+
once with a Devil's-Advocate prompt to surface the 10-15 hardest questions that
|
|
9
|
+
expose ambiguities, missing acceptance criteria, unstated assumptions, and
|
|
10
|
+
security/scale blind spots. A grilled spec is a better Reason input to the
|
|
11
|
+
RARV-C loop.
|
|
12
|
+
|
|
13
|
+
Steps:
|
|
14
|
+
|
|
15
|
+
1. Run the interrogation on the spec ($ARGUMENTS, or the default resolution
|
|
16
|
+
prd.md / .loki/generated-prd.md if empty):
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
loki grill $ARGUMENTS
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
It writes `.loki/grill/report.md`. It requires a provider CLI and fails
|
|
23
|
+
cleanly (exit 3) when none is available: it never fabricates questions.
|
|
24
|
+
|
|
25
|
+
2. Read `.loki/grill/report.md` and present the findings to the user grouped by
|
|
26
|
+
category:
|
|
27
|
+
- Ambiguities and missing acceptance criteria
|
|
28
|
+
- Unstated assumptions
|
|
29
|
+
- Security blind spots
|
|
30
|
+
- Scale and reliability blind spots
|
|
31
|
+
|
|
32
|
+
3. For each hard question, suggest a concrete way to resolve it in the spec
|
|
33
|
+
(a precise acceptance criterion, an explicit assumption made explicit, a
|
|
34
|
+
security control, a stated limit). Do not silently answer them yourself;
|
|
35
|
+
the point is to harden the human's intent.
|
|
36
|
+
|
|
37
|
+
4. If the user wants the questions embedded in the spec for the record, offer:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
loki grill --apply $ARGUMENTS
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
which appends a "Grill findings" section to the spec file. Ask before
|
|
44
|
+
modifying the spec.
|
|
45
|
+
|
|
46
|
+
Report only what the grill produced. If the provider was unavailable, say so
|
|
47
|
+
plainly and do not invent questions.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Check whether the spec has drifted from its lock using Loki's living-spec drift detection, and summarize the report.
|
|
3
|
+
argument-hint: "[spec-path] (default: prd.md / .loki/generated-prd.md)"
|
|
4
|
+
allowed-tools: Bash(loki spec:*), Read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Check whether the spec is still true: the spec is the contract; Loki keeps it
|
|
8
|
+
true. This runs deterministic drift detection (no LLM cost) comparing the
|
|
9
|
+
current spec against its lock.
|
|
10
|
+
|
|
11
|
+
Steps:
|
|
12
|
+
|
|
13
|
+
1. If there is no lock yet, the status command will say so. In that case, offer
|
|
14
|
+
to create one:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
loki spec lock $ARGUMENTS
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The lock (`.loki/spec/spec.lock`) is a deterministic map of spec
|
|
21
|
+
requirements (checklist items and headings) to content hashes, plus repo
|
|
22
|
+
HEAD at lock time.
|
|
23
|
+
|
|
24
|
+
2. Run the drift check:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
loki spec status $ARGUMENTS
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Exit 0 means in sync (SPEC-TRUE); exit 1 means drift detected
|
|
31
|
+
(SPEC-DRIFTED). It writes `.loki/spec/drift-report.json`.
|
|
32
|
+
|
|
33
|
+
3. Read `.loki/spec/drift-report.json` and summarize for the user:
|
|
34
|
+
- The verdict: SPEC-TRUE or SPEC-DRIFTED.
|
|
35
|
+
- Counts of ADDED, REMOVED, and CHANGED requirements, then list each one.
|
|
36
|
+
- Whether code changed since the locked HEAD (files, insertions, deletions).
|
|
37
|
+
|
|
38
|
+
4. If drifted, explain the choice clearly:
|
|
39
|
+
- If the code is the source of truth and the spec should follow, the human
|
|
40
|
+
updates the spec, then runs `loki spec sync $ARGUMENTS` to re-lock.
|
|
41
|
+
- If the spec is correct and the code lags, the change set is incomplete.
|
|
42
|
+
|
|
43
|
+
This MVP never auto-rewrites the spec. Re-locking via `loki spec sync` is an
|
|
44
|
+
explicit human action after review. Do not run `sync` automatically; ask
|
|
45
|
+
first.
|
|
46
|
+
|
|
47
|
+
Report only what the drift report shows. Do not infer requirements that are not
|
|
48
|
+
in the spec.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run Loki's deterministic PR verification on the current change and summarize the evidence verdict.
|
|
3
|
+
argument-hint: "[base-ref] (default: main)"
|
|
4
|
+
allowed-tools: Bash(loki verify:*), Read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Run Loki's Autonomi Verify on the current working tree and report the verdict
|
|
8
|
+
with its evidence, not just an opinion in chat. The differentiator is the
|
|
9
|
+
auditable artifact: a verdict that refuses to silently pass on inconclusive
|
|
10
|
+
evidence.
|
|
11
|
+
|
|
12
|
+
Steps:
|
|
13
|
+
|
|
14
|
+
1. Run the verifier against the base ref ($ARGUMENTS, or `main` if empty):
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
loki verify $ARGUMENTS
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It computes the PR-style delta merge-base(base, HEAD)..HEAD and runs
|
|
21
|
+
deterministic gates (build, tests, static analysis, secret scan, dependency
|
|
22
|
+
audit, and spec drift when a spec lock exists). Exit codes:
|
|
23
|
+
0 VERIFIED, 1 CONCERNS, 2 BLOCKED, 3 verifier error.
|
|
24
|
+
|
|
25
|
+
2. Read the evidence artifacts it wrote:
|
|
26
|
+
- `.loki/verify/evidence.json` (machine-readable: schema, gates, findings)
|
|
27
|
+
- `.loki/verify/report.md` (human verdict + findings table)
|
|
28
|
+
|
|
29
|
+
3. Summarize for the user:
|
|
30
|
+
- The verdict (VERIFIED / CONCERNS / BLOCKED) and exit code.
|
|
31
|
+
- Each gate and its status (pass / fail / inconclusive / skipped).
|
|
32
|
+
- Every finding: severity, category, file:line, and whether it is blocking.
|
|
33
|
+
- If the verdict is CONCERNS or BLOCKED, list exactly what to fix.
|
|
34
|
+
|
|
35
|
+
Be honest about inconclusive evidence: an inconclusive gate (for example a test
|
|
36
|
+
runner that could not run) is never upgraded to VERIFIED. If the diff is empty,
|
|
37
|
+
the verdict is CONCERNS (nothing to verify), not VERIFIED. Do not claim the
|
|
38
|
+
change is verified unless the evidence says VERIFIED.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Loki Mode opt-in Bash guard (PreToolUse hook).
|
|
3
|
+
#
|
|
4
|
+
# DEFAULT: OFF. This hook is a no-op pass-through unless the user explicitly
|
|
5
|
+
# opts in by setting LOKI_GUARD=1 (or true/yes/on, case-insensitive) in their
|
|
6
|
+
# environment. With the guard disabled the hook always exits 0 immediately, so
|
|
7
|
+
# it never interferes with a user's Bash tool calls.
|
|
8
|
+
#
|
|
9
|
+
# When enabled, it inspects the proposed Bash command (read from the PreToolUse
|
|
10
|
+
# event JSON on stdin) and blocks a small set of clearly destructive patterns
|
|
11
|
+
# that have bitten Loki runs in the past:
|
|
12
|
+
# - rm -rf on /tmp/loki-* globs while a live run may be staging there
|
|
13
|
+
# - rm -rf of / or $HOME roots
|
|
14
|
+
# - git add -A / git add . (Loki convention: stage files individually)
|
|
15
|
+
#
|
|
16
|
+
# Blocking contract (Claude Code hooks): to deny a tool call, emit a JSON object
|
|
17
|
+
# on stdout with permissionDecision "deny" and a reason, and exit 0. Anything
|
|
18
|
+
# else (empty object, exit 0) allows the call. We never hard-fail the hook so a
|
|
19
|
+
# parsing hiccup can never wedge the session.
|
|
20
|
+
#
|
|
21
|
+
# This script depends only on bash builtins plus python3 (already required by
|
|
22
|
+
# Loki) for robust JSON parsing. If python3 is missing it degrades to allow.
|
|
23
|
+
|
|
24
|
+
set -u
|
|
25
|
+
|
|
26
|
+
# 1. Opt-in gate. Unset / empty / anything-not-truthy => pass through.
|
|
27
|
+
guard_on=0
|
|
28
|
+
case "$(printf '%s' "${LOKI_GUARD:-}" | tr '[:upper:]' '[:lower:]')" in
|
|
29
|
+
1 | true | yes | on | y) guard_on=1 ;;
|
|
30
|
+
*) guard_on=0 ;;
|
|
31
|
+
esac
|
|
32
|
+
|
|
33
|
+
if [ "$guard_on" -ne 1 ]; then
|
|
34
|
+
# Disabled: allow without comment.
|
|
35
|
+
printf '{}'
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# 2. Read the event JSON from stdin (PreToolUse provides tool_input.command).
|
|
40
|
+
event="$(cat 2>/dev/null || true)"
|
|
41
|
+
|
|
42
|
+
# 3. Extract the proposed command. Prefer python3; degrade to allow on any error.
|
|
43
|
+
cmd=""
|
|
44
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
45
|
+
cmd="$(printf '%s' "$event" | python3 -c '
|
|
46
|
+
import sys, json
|
|
47
|
+
try:
|
|
48
|
+
e = json.load(sys.stdin)
|
|
49
|
+
except Exception:
|
|
50
|
+
sys.exit(0)
|
|
51
|
+
ti = e.get("tool_input") or {}
|
|
52
|
+
c = ti.get("command")
|
|
53
|
+
if isinstance(c, str):
|
|
54
|
+
sys.stdout.write(c)
|
|
55
|
+
' 2>/dev/null || true)"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# No command parsed => allow.
|
|
59
|
+
if [ -z "$cmd" ]; then
|
|
60
|
+
printf '{}'
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
deny() {
|
|
65
|
+
# $1 = reason. Emit a deny decision and exit 0 (the hook itself succeeded).
|
|
66
|
+
reason="$1"
|
|
67
|
+
printf '%s' "$reason" | python3 -c '
|
|
68
|
+
import sys, json
|
|
69
|
+
reason = sys.stdin.read()
|
|
70
|
+
print(json.dumps({
|
|
71
|
+
"hookSpecificOutput": {
|
|
72
|
+
"hookEventName": "PreToolUse",
|
|
73
|
+
"permissionDecision": "deny",
|
|
74
|
+
"permissionDecisionReason": reason
|
|
75
|
+
}
|
|
76
|
+
}))
|
|
77
|
+
'
|
|
78
|
+
exit 0
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# 4. Destructive-pattern checks (only reached when guard is ON).
|
|
82
|
+
|
|
83
|
+
# 4a. rm -rf targeting /tmp/loki-* while a live run may be staging there.
|
|
84
|
+
if printf '%s' "$cmd" | grep -Eq 'rm[[:space:]]+(-[A-Za-z]*r[A-Za-z]*f|-[A-Za-z]*f[A-Za-z]*r|-rf|-fr)[[:space:]].*/tmp/loki-'; then
|
|
85
|
+
if command -v pgrep >/dev/null 2>&1 && pgrep -f 'loki-run-' >/dev/null 2>&1; then
|
|
86
|
+
deny "LOKI_GUARD: refusing rm -rf on /tmp/loki-* while a live loki run is staging there (pgrep -f loki-run- matched). Scope cleanup to known-dead paths, or stop the run first."
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# 4b. rm -rf of filesystem root or HOME root.
|
|
91
|
+
# shellcheck disable=SC2016 # the regex matches the literal text $HOME in the command, no expansion intended
|
|
92
|
+
if printf '%s' "$cmd" | grep -Eq 'rm[[:space:]]+-[A-Za-z]*[rf][A-Za-z]*[[:space:]]+(-[A-Za-z]+[[:space:]]+)*(/|/\*|"\$HOME"|\$HOME|~)([[:space:]]|$)'; then
|
|
93
|
+
deny "LOKI_GUARD: refusing rm -rf of a filesystem or HOME root. This is almost certainly a mistake."
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# 4c. git add -A / git add . (Loki convention: stage files individually).
|
|
97
|
+
if printf '%s' "$cmd" | grep -Eq 'git[[:space:]]+add[[:space:]]+(-A|--all|\.)([[:space:]]|$)'; then
|
|
98
|
+
deny "LOKI_GUARD: 'git add -A' / 'git add .' is blocked by Loki convention. Stage files individually by name."
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# 5. Nothing matched => allow.
|
|
102
|
+
printf '{}'
|
|
103
|
+
exit 0
|