loki-mode 7.16.1 → 7.17.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.
@@ -0,0 +1,137 @@
1
+ # R10: Agent + Template Marketplace (install-from-source)
2
+
3
+ Status: implemented in this worktree (isolated; not committed to main).
4
+ Scope: the install MECHANISM + a manifest format. There is NO hosted
5
+ central registry. This is honest install-from-source (git / local / url).
6
+ A central hub server is future work.
7
+
8
+ ## Goal
9
+
10
+ Network effects: let users install community agents and PRD templates from
11
+ a shared source, the way Claude Code installs skills/plugins. Pairs with R8
12
+ (R8 exports shareable assets; R10 installs them). Shared assets become a
13
+ network effect that keeps users in the ecosystem.
14
+
15
+ ## What already exists (verified, reused -- NOT rebuilt)
16
+
17
+ - `agents/types.json` -- a JSON ARRAY of 41 built-in agent objects. Fields
18
+ (union): `type`, `name`, `swarm`, `persona`, `focus` (list), `capabilities`.
19
+ Swarms: business, data, engineering, growth, operations, orchestration,
20
+ product, review.
21
+ - `agents/managed_registry.py` -- a DIFFERENT registry: it materializes
22
+ Managed Agent IDs via the Anthropic beta SDK, gated on managed-memory.
23
+ It treats `types.json` as read-only source of truth. NOT related to
24
+ install-from-source; no collision. We did not touch it.
25
+ - `cmd_agent()` (`autonomy/loki`) -- subcommands `list`, `info`, `run`,
26
+ `start`, `review`. Each reads `types.json` directly in inline python
27
+ blocks (5 sites).
28
+ - `templates/` -- 21 built-in PRD templates as `*.md` files (plus README).
29
+ - `cmd_init()` (`autonomy/loki`) -- `loki init --template <name>` is the
30
+ consumer of templates: it validates `<name>` against a hardcoded
31
+ `TEMPLATE_NAMES` array, resolves `$SKILL_DIR/templates/<name>.md` (or
32
+ `examples/`), and writes a `prd.md`. `--stdout` prints the body.
33
+ - `cmd_quick()` -- generates an ad-hoc PRD; does not read `templates/`.
34
+ - Bun route (`loki-ts/src/cli.ts` + `bin/loki` shim): only ~12 high-traffic
35
+ commands run on Bun; `agent` and `template` are NOT among them, so the
36
+ shim execs the bash CLI for both. Parity is automatic and identical.
37
+ - R8 asset-export bundle format: NOT present in this tree yet. R10 defines
38
+ the install side and a manifest format R8 can target. (Coordinate: R8
39
+ exports a manifest of the same shape; R10 installs it.)
40
+
41
+ ## What R10 adds (enhance-in-place)
42
+
43
+ ### New module: `agents/hub_install.py`
44
+ Single source of truth for manifest validation + install. Importable for
45
+ tests; also a small CLI (`python3 hub_install.py <cmd> [source]`) used by
46
+ the bash CLI via env-passed JSON. DATA-ONLY: it never eval/imports/runs
47
+ anything from a source. For git/url it reads the manifest file only and
48
+ never runs build hooks, npm install, make, or scripts in the tree.
49
+
50
+ ### Manifest format (JSON, `schema_version: 1`)
51
+
52
+ Agent:
53
+ ```json
54
+ {
55
+ "schema_version": 1,
56
+ "kind": "agent",
57
+ "type": "community-rust-pro",
58
+ "name": "Rust Pro",
59
+ "swarm": "engineering",
60
+ "persona": "You are a senior Rust engineer...",
61
+ "focus": ["rust", "tokio"],
62
+ "capabilities": "Rust, tokio, async"
63
+ }
64
+ ```
65
+
66
+ Template:
67
+ ```json
68
+ {
69
+ "schema_version": 1,
70
+ "kind": "template",
71
+ "name": "rust-cli",
72
+ "label": "Rust CLI Tool",
73
+ "description": "A CLI tool in Rust",
74
+ "body": "# PRD: Rust CLI\n..."
75
+ }
76
+ ```
77
+ (`body_file: "prd.md"` may be used instead of `body` for local/git sources;
78
+ the sibling markdown file is inlined, with traversal-safe name validation.)
79
+
80
+ ### Store layout (project-local, under `.loki/`)
81
+ - `.loki/agents/installed.json` -- list of installed agent manifests
82
+ - `.loki/templates/<name>.md` -- installed template body
83
+ - `.loki/templates/installed.json` -- index of installed templates
84
+
85
+ Rationale: project-local keeps installs scoped and never writes into the
86
+ read-only package `agents/` or `templates/` dirs (those are wiped on
87
+ npm/Docker upgrade and would dirty cherry-pick). A user-global `~/.loki`
88
+ store is a natural future extension; not implemented here.
89
+
90
+ ### CLI surface
91
+ - `loki agent install <source>` / `loki agent installed`
92
+ - `loki template install <source>` / `loki template list`
93
+ - `<source>` = local path | dir containing `manifest.json` | git repo URL
94
+ (cloned shallow to temp, manifest read, temp discarded) | raw http(s)
95
+ manifest URL.
96
+
97
+ ### Reader integration (avoids write-only)
98
+ - `cmd_agent list / info / run / start / review`: union built-in
99
+ `types.json` with `.loki/agents/installed.json` at read time, so installed
100
+ agents are immediately visible to the existing consumers.
101
+ - `cmd_init --template <name>`: also resolves an installed template body
102
+ (`.loki/templates/<name>.md`) and accepts the name as valid. Built-in
103
+ templates and `init --list` are unchanged.
104
+
105
+ ## Security model (no arbitrary code execution)
106
+
107
+ Validation rejects, BEFORE any write:
108
+ - Path traversal in `type` / template `name`: `..`, `/`, `\`, absolute
109
+ paths, null bytes; names must match `^[a-z0-9][a-z0-9-]{0,79}$`.
110
+ - Built-in shadowing: a manifest claiming a built-in agent `type` or a
111
+ built-in template `name` is rejected (cannot silently replace the
112
+ security-reviewer persona, etc.).
113
+ - Wrong field types, oversized fields (persona/body/focus caps), manifest
114
+ size cap, wrong `kind`, unsupported `schema_version`.
115
+ - Executable-looking fields (`postinstall`, `preinstall`, `scripts`,
116
+ `exec`, `command`, `cmd`, `hooks`, `run`, `shell`, `eval`) are IGNORED,
117
+ never run, stripped from the stored entry, and reported to the operator.
118
+ - Git clone is shallow, `--no-tags`, `GIT_TERMINAL_PROMPT=0`; only files
119
+ are read. URL fetch uses urllib (no shell), with a size cap.
120
+
121
+ ## Tests
122
+ - `tests/test_hub_install.py` -- 34 module assertions (install agent/template
123
+ local + body_file, install from file:// git repo, malformed rejection,
124
+ path-traversal rejection, built-in-shadow rejection, no-code-execution
125
+ sentinel, unknown-source rejection, merged/installed read paths).
126
+ - `tests/cli/test-hub-install.sh` -- 14 end-to-end CLI assertions through
127
+ `autonomy/loki` (install, list, agent list/info union, init --template
128
+ resolves installed, git file:// source, security rejections, no-exec
129
+ sentinel). No network.
130
+
131
+ ## Honesty / gaps
132
+ - No hosted central marketplace server. Help text and this note say so.
133
+ - Store is project-local only (user-global `~/.loki` is future).
134
+ - No uninstall/update subcommand yet (re-install replaces in place).
135
+ - R8 export side is not in this tree; R10 defines the manifest shape R8
136
+ should target.
137
+ - Dashboard UI for browsing/installing is out of scope for this pass.
@@ -0,0 +1,129 @@
1
+ # R8: Shareable Team Assets - Design Note
2
+
3
+ Status: implemented in this worktree (isolated; not committed to main).
4
+ Goal (per arc): turn individual setup into org lock-in / network effects by
5
+ making a team's invested assets EXPORTABLE and IMPORTABLE across a team or org,
6
+ so setup compounds into shared value (the skills-marketplace stickiness dynamic).
7
+
8
+ ## 1. Inventory of existing machinery (verified against real source)
9
+
10
+ Before writing anything, the existing export/import/share + memory machinery
11
+ was read in full. Findings:
12
+
13
+ | Mechanism | Location | What it actually does | Reused? |
14
+ |---|---|---|---|
15
+ | `cmd_export` | `autonomy/loki:6254` | Per-run SESSION SNAPSHOT: json / markdown / csv / timeline of one `.loki/` run. Not portable across teams, not redacted. | Reused its `_export_check_overwrite` guard. |
16
+ | `cmd_import` | `autonomy/loki:4722` | Imports GitHub ISSUES into `.loki/queue/`. Unrelated to assets. | Not applicable (different domain). |
17
+ | `cmd_share` | `autonomy/loki:25147` | Uploads a session REPORT as a GitHub gist. | Not applicable (report, not assets). |
18
+ | `cmd_memory` | `autonomy/loki:14475` | Lists/show cross-project learnings in `~/.loki/learnings/*.jsonl`. No tarball export. | Same source dir reused as an asset category. |
19
+ | `proof_redact.py` | `autonomy/lib/proof_redact.py` | Single redaction chokepoint (R1). `redact_tree(obj)`, `redact_value(s)`, `set_context()`, `RULES_VERSION`. | REUSED as-is. No second redactor written. |
20
+ | agent registry | `agents/types.json` | 41 shipped agent types. `cmd_agent` (`autonomy/loki:19509`) reads it from the install dir. | Bundled as the `agents` category. |
21
+ | PRD templates | `templates/*.md` | 21 templates; read from `$SKILL_DIR/templates` at runtime (`autonomy/loki:9781`, `8840`). | Bundled as `templates`. |
22
+ | council config | `<project>/.loki/council/*.json` | `config.json` / `state.json` (`autonomy/loki:16688`, `autonomy/run.sh:3643`). | Bundled as `council`. |
23
+ | project memory | `<project>/.loki/memory/{episodic,semantic,skills,...}` (`autonomy/run.sh:3183`) | Per-project episodic/semantic/procedural memory. | Bundled as `memory`. |
24
+ | R5 wiki | `<project>/.loki/wiki/**` (`autonomy/loki:22881`) | Cited per-project wiki. | Bundled as `wiki` (opt-in). |
25
+
26
+ Key negative finding: there is NO separate per-user "custom agent" store.
27
+ "Custom agents" == the `agents/types.json` registry. We do not invent one
28
+ (that would be the parallel-machinery trap the task forbids).
29
+
30
+ ## 2. Why `loki assets` is NOT a duplicate of `cmd_export`
31
+
32
+ - `cmd_export` = per-run session snapshot, one format at a time, not redacted,
33
+ not portable. Scope: "what happened in this run".
34
+ - `loki assets` = portable, redacted, multi-category TEAM-ASSET tarball.
35
+ Scope: "the reusable setup a team invested in", designed to travel to
36
+ another clone/org. Different scope, different artifact, different lifetime.
37
+
38
+ Concrete reuse (not a fork): `_export_check_overwrite` (overwrite guard) and
39
+ the `proof_redact` chokepoint. No new redactor, no parallel exporter.
40
+
41
+ ## 3. Asset map and the deliberate export/import asymmetry
42
+
43
+ | Category | Export source | Bundle path | Import restore root |
44
+ |---|---|---|---|
45
+ | learnings | `~/.loki/learnings/*.jsonl` | `learnings/` | `$HOME/.loki/learnings` |
46
+ | memory | `<project>/.loki/memory/**` | `memory/` | `<cwd>/.loki/memory` |
47
+ | agents | `$SKILL_DIR/agents/types.json` | `agents/` | `<cwd>/agents` |
48
+ | templates | `$SKILL_DIR/templates/*.md` | `templates/` | `<cwd>/templates` |
49
+ | council | `<project>/.loki/council/*.json` | `council/` | `<cwd>/.loki/council` |
50
+ | wiki (opt-in) | `<project>/.loki/wiki/**` | `wiki/` | `<cwd>/.loki/wiki` |
51
+
52
+ The export and import repo_root values are DELIBERATELY DIFFERENT (enforced in
53
+ `cmd_assets`, not in the Python helper):
54
+
55
+ - EXPORT reads agents/templates from the loki install (`$SKILL_DIR`), because
56
+ that is where a team's edited registry/templates live and are read at
57
+ runtime.
58
+ - IMPORT writes agents/templates under the caller's cwd (the target clone
59
+ root) BY DEFAULT, NEVER silently back into the install. Writing into
60
+ `$SKILL_DIR` would clobber the live install with redacted copies (this was
61
+ caught and fixed during development: a bash smoke test that used `$SKILL_DIR`
62
+ on import overwrote the worktree's real `agents/types.json` and templates;
63
+ default restore root changed to cwd, damage reverted).
64
+ - `loki assets import --into-install` opts back into `$SKILL_DIR` for
65
+ agents/templates only, so a GLOBAL-INSTALL user (the primary install path per
66
+ CLAUDE.md) gets them read at runtime. This is the symmetric round-trip
67
+ (export reads $SKILL_DIR, import --into-install writes $SKILL_DIR). It is
68
+ opt-in because the default cwd target is non-destructive.
69
+
70
+ memory/learnings/council/wiki restore to `$HOME/.loki` or `<cwd>/.loki` and
71
+ take effect immediately regardless of the flag.
72
+
73
+ ## 4. Redaction (single chokepoint, all file types)
74
+
75
+ `autonomy/lib/assets_bundle.py` calls `proof_redact.set_context(home, repo)`
76
+ then dispatches by extension before any byte is written into the tarball:
77
+ - `.json` -> parse -> `redact_tree` -> reserialize
78
+ - `.jsonl` -> per-line parse -> `redact_tree` -> reserialize
79
+ - `.md` / `.txt` / other -> `redact_value` on the whole string
80
+
81
+ Originals on disk are never modified (verified by test). Secrets seeded into
82
+ each of md, jsonl, and json are all stripped (verified by test + live leak
83
+ scan).
84
+
85
+ Scope of redaction (honest): proof_redact strips SECRETS / KEYS / TOKENS /
86
+ connection-string credentials / absolute home+repo PATHS, per its frozen
87
+ `RULES_VERSION`. It does NOT do general PII (emails, author names, free text).
88
+ The accurate claim for this feature is "no secrets/keys/home-paths leak," not
89
+ "no PII." The authoritative evidence is the leak-scan test
90
+ (`test_imported_assets_contain_no_secrets`), not the manifest count.
91
+
92
+ ## 5. Honest gaps / limitations
93
+
94
+ - agents/templates restore relative to cwd by default; loki reads them from its
95
+ install at runtime, so a global-install user should pass `--into-install` (or
96
+ copy the restored `agents/` + `templates/` into their install). Documented in
97
+ `--help` and the module docstring. memory/learnings/council/wiki are
98
+ effective immediately.
99
+ - Round-trip is SEMANTICALLY FAITHFUL but REFORMATTED, not byte-identical:
100
+ JSON files come back `indent=2`, and JSONL merge canonicalizes lines
101
+ (sort_keys). Content/semantics are preserved; whitespace/key-order are not.
102
+ - The export-reads-install / import-writes-cwd asymmetry lives in the bash
103
+ `cmd_assets`, not the Python helper. The Python tests pass roots explicitly
104
+ so they cover the helper mechanics; the bash asymmetry is covered by
105
+ `tests/test_assets_cli.sh` (export with SKILL_DIR=A, import from cwd=B,
106
+ assert agents land in B).
107
+ - The manifest `redactions` count covers structured (json/jsonl) redactions
108
+ only; `redact_value` (md/text) and path collapses are not counted (the
109
+ redaction still HAPPENS). The authoritative no-PII evidence is the
110
+ `test_imported_assets_contain_no_secrets` leak-scan test, not the count.
111
+ - `agents/types.json` and `templates/*.md` are repo-shipped defaults; a bundle
112
+ from a fresh checkout re-ships the stock set. Value = captured team DELTAS
113
+ travelling with the baseline.
114
+ - The placement tests assert files land at the mapped path; they do not assert
115
+ the importing loki reads them at runtime (that is the global-install gap
116
+ above).
117
+
118
+ ## 6. bash + Bun parity
119
+
120
+ `assets` is NOT in the `bin/loki` Bun allowlist (`bin/loki:119`), so it falls
121
+ through the shim's default branch to the bash CLI (`bin/loki:145`), exactly like
122
+ `bench`. Parity is free via fall-through; no Bun command is needed or added.
123
+
124
+ ## 7. Files
125
+
126
+ - `autonomy/lib/assets_bundle.py` (new) - bundler/redactor/restorer helper.
127
+ - `autonomy/loki` (modified) - `cmd_assets` + dispatch case.
128
+ - `tests/test_assets_bundle.py` (new) - 13 tests.
129
+ - `docs/R8-SHAREABLE-TEAM-ASSETS-PLAN.md` (this note).
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var I7=Object.defineProperty;var P7=($)=>$;function L7($,Q){this[$]=P7.bind(null,Q)}var g=($,Q)=>{for(var K in Q)I7($,K,{get:Q[K],enumerable:!0,configurable:!0,set:L7.bind(Q,K)})};var k=($,Q)=>()=>($&&(Q=$($=0)),Q);var z1=import.meta.require;var w$={};g(w$,{lokiDir:()=>L,homeLokiDir:()=>c1,findRepoRootForVersion:()=>u1,REPO_ROOT:()=>p});import{resolve as o,dirname as f1}from"path";import{fileURLToPath as k7}from"url";import{existsSync as P1}from"fs";import{homedir as j7}from"os";function F7(){let $=A$;for(let Q=0;Q<6;Q++){if(P1(o($,"VERSION"))&&P1(o($,"autonomy/run.sh")))return $;let K=f1($);if(K===$)break;$=K}return o(A$,"..","..","..")}function u1($){let Q=$;for(let K=0;K<6;K++){if(P1(o(Q,"VERSION"))&&P1(o(Q,"autonomy/run.sh")))return Q;let Z=f1(Q);if(Z===Q)break;Q=Z}return o($,"..","..","..")}function L(){return process.env.LOKI_DIR??o(process.cwd(),".loki")}function c1(){return o(j7(),".loki")}var A$,p;var m=k(()=>{A$=f1(k7(import.meta.url));p=F7()});import{readFileSync as R7}from"fs";import{resolve as E7,dirname as S7}from"path";import{fileURLToPath as x7}from"url";function L1(){if($1!==null)return $1;let $="7.16.1";if(typeof $==="string"&&$.length>0)return $1=$,$1;try{let Q=S7(x7(import.meta.url)),K=u1(Q);$1=R7(E7(K,"VERSION"),"utf-8").trim()}catch{$1="unknown"}return $1}var $1=null;var p1=k(()=>{m()});var P$={};g(P$,{runOrThrow:()=>N7,run:()=>F,commandVersion:()=>D7,commandExists:()=>v,ShellError:()=>l1});async function F($,Q={}){let K=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),Z,z;if(Q.timeoutMs&&Q.timeoutMs>0)Z=setTimeout(()=>{try{K.kill("SIGTERM")}catch{}z=setTimeout(()=>{try{K.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[U,X,W]=await Promise.all([new Response(K.stdout).text(),new Response(K.stderr).text(),K.exited]);return{stdout:U,stderr:X,exitCode:W}}finally{if(Z)clearTimeout(Z);if(z)clearTimeout(z)}}async function N7($,Q={}){let K=await F($,Q);if(K.exitCode!==0)throw new l1(`command failed (${K.exitCode}): ${$.join(" ")}`,K.exitCode,K.stdout,K.stderr);return K}async function v($){let Q=C7($),K=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(K.exitCode===0)return K.stdout.trim()||null;return null}function C7($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function D7($,Q="--version"){if(!await v($))return null;let Z=await F([$,Q],{timeoutMs:5000});if(Z.exitCode!==0)return null;return((Z.stdout||Z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var l1;var n=k(()=>{l1=class l1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,K,Z){super($);this.message=$;this.exitCode=Q;this.stdout=K;this.stderr=Z;this.name="ShellError"}}});function a($){return h7?"":$}var h7,O,C,I,I3,A,x,h,H;var l=k(()=>{h7=(process.env.NO_COLOR??"").length>0;O=a("\x1B[0;31m"),C=a("\x1B[0;32m"),I=a("\x1B[1;33m"),I3=a("\x1B[0;34m"),A=a("\x1B[0;36m"),x=a("\x1B[1m"),h=a("\x1B[2m"),H=a("\x1B[0m")});import{existsSync as l7}from"fs";async function X1(){if(G1!==void 0)return G1;let $="/opt/homebrew/bin/python3.12";if(l7($))return G1=$,$;let Q=await v("python3.12");if(Q)return G1=Q,Q;let K=await v("python3");return G1=K,K}async function Q1($,Q={}){let K=await X1();if(!K)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([K,"-c",$],Q)}var G1;var B1=k(()=>{n()});var D$={};g(D$,{runStatus:()=>z8});import{existsSync as b,readFileSync as H1,readdirSync as R$,statSync as E$}from"fs";import{resolve as N,basename as s7}from"path";import{homedir as r7}from"os";async function i7(){if(await v("jq"))return!0;return process.stdout.write(`${O}Error: jq is required but not installed.${H}
2
+ var I7=Object.defineProperty;var P7=($)=>$;function L7($,Q){this[$]=P7.bind(null,Q)}var g=($,Q)=>{for(var K in Q)I7($,K,{get:Q[K],enumerable:!0,configurable:!0,set:L7.bind(Q,K)})};var k=($,Q)=>()=>($&&(Q=$($=0)),Q);var z1=import.meta.require;var w$={};g(w$,{lokiDir:()=>L,homeLokiDir:()=>c1,findRepoRootForVersion:()=>u1,REPO_ROOT:()=>p});import{resolve as o,dirname as f1}from"path";import{fileURLToPath as k7}from"url";import{existsSync as P1}from"fs";import{homedir as j7}from"os";function F7(){let $=A$;for(let Q=0;Q<6;Q++){if(P1(o($,"VERSION"))&&P1(o($,"autonomy/run.sh")))return $;let K=f1($);if(K===$)break;$=K}return o(A$,"..","..","..")}function u1($){let Q=$;for(let K=0;K<6;K++){if(P1(o(Q,"VERSION"))&&P1(o(Q,"autonomy/run.sh")))return Q;let Z=f1(Q);if(Z===Q)break;Q=Z}return o($,"..","..","..")}function L(){return process.env.LOKI_DIR??o(process.cwd(),".loki")}function c1(){return o(j7(),".loki")}var A$,p;var m=k(()=>{A$=f1(k7(import.meta.url));p=F7()});import{readFileSync as R7}from"fs";import{resolve as E7,dirname as S7}from"path";import{fileURLToPath as x7}from"url";function L1(){if($1!==null)return $1;let $="7.17.0";if(typeof $==="string"&&$.length>0)return $1=$,$1;try{let Q=S7(x7(import.meta.url)),K=u1(Q);$1=R7(E7(K,"VERSION"),"utf-8").trim()}catch{$1="unknown"}return $1}var $1=null;var p1=k(()=>{m()});var P$={};g(P$,{runOrThrow:()=>N7,run:()=>F,commandVersion:()=>D7,commandExists:()=>v,ShellError:()=>l1});async function F($,Q={}){let K=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),Z,z;if(Q.timeoutMs&&Q.timeoutMs>0)Z=setTimeout(()=>{try{K.kill("SIGTERM")}catch{}z=setTimeout(()=>{try{K.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[U,X,W]=await Promise.all([new Response(K.stdout).text(),new Response(K.stderr).text(),K.exited]);return{stdout:U,stderr:X,exitCode:W}}finally{if(Z)clearTimeout(Z);if(z)clearTimeout(z)}}async function N7($,Q={}){let K=await F($,Q);if(K.exitCode!==0)throw new l1(`command failed (${K.exitCode}): ${$.join(" ")}`,K.exitCode,K.stdout,K.stderr);return K}async function v($){let Q=C7($),K=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(K.exitCode===0)return K.stdout.trim()||null;return null}function C7($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function D7($,Q="--version"){if(!await v($))return null;let Z=await F([$,Q],{timeoutMs:5000});if(Z.exitCode!==0)return null;return((Z.stdout||Z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var l1;var n=k(()=>{l1=class l1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,K,Z){super($);this.message=$;this.exitCode=Q;this.stdout=K;this.stderr=Z;this.name="ShellError"}}});function a($){return h7?"":$}var h7,O,C,I,I3,A,x,h,H;var l=k(()=>{h7=(process.env.NO_COLOR??"").length>0;O=a("\x1B[0;31m"),C=a("\x1B[0;32m"),I=a("\x1B[1;33m"),I3=a("\x1B[0;34m"),A=a("\x1B[0;36m"),x=a("\x1B[1m"),h=a("\x1B[2m"),H=a("\x1B[0m")});import{existsSync as l7}from"fs";async function X1(){if(G1!==void 0)return G1;let $="/opt/homebrew/bin/python3.12";if(l7($))return G1=$,$;let Q=await v("python3.12");if(Q)return G1=Q,Q;let K=await v("python3");return G1=K,K}async function Q1($,Q={}){let K=await X1();if(!K)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([K,"-c",$],Q)}var G1;var B1=k(()=>{n()});var D$={};g(D$,{runStatus:()=>z8});import{existsSync as b,readFileSync as H1,readdirSync as R$,statSync as E$}from"fs";import{resolve as N,basename as s7}from"path";import{homedir as r7}from"os";async function i7(){if(await v("jq"))return!0;return process.stdout.write(`${O}Error: jq is required but not installed.${H}
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)
@@ -748,4 +748,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
748
748
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
749
749
  `),process.stderr.write(w7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var H3=await U3(Bun.argv.slice(2));process.exit(H3);
750
750
 
751
- //# debugId=5AF2A03AC937F25664756E2164756E21
751
+ //# debugId=34E49D55E4F12E2764756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.16.1'
60
+ __version__ = '7.17.0'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "7.16.1",
3
+ "version": "7.17.0",
4
4
  "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).",
5
5
  "keywords": [
6
6
  "agent",