loki-mode 7.7.26 → 7.7.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/assets/lokesh_brand_full.png +0 -0
- package/assets/publisher-icon-128.png +0 -0
- package/assets/welcome/welcome.html +358 -0
- package/autonomy/completion-council.sh +10 -1
- package/autonomy/hooks/migration-hooks.sh +4 -1
- package/autonomy/loki +95 -0
- package/autonomy/run.sh +14 -2
- package/bin/postinstall.js +3 -0
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/WELCOME-OPENER-PLAN.md +106 -0
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/memory/retrieval.py +49 -0
- package/package.json +2 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-to-product system. Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product via the RARV-C closure loop, with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.7.
|
|
6
|
+
# Loki Mode v7.7.28
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -381,4 +381,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
381
381
|
|
|
382
382
|
---
|
|
383
383
|
|
|
384
|
-
**v7.7.
|
|
384
|
+
**v7.7.28 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.7.
|
|
1
|
+
7.7.28
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<meta name="theme-color" content="#553DE9">
|
|
7
|
+
<title>Welcome to Loki Mode</title>
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
11
|
+
<style>
|
|
12
|
+
:root {
|
|
13
|
+
--loki-bg-primary: #FAFAF7;
|
|
14
|
+
--loki-bg-secondary: #F2F0EB;
|
|
15
|
+
--loki-bg-card: rgba(255, 255, 255, 0.72);
|
|
16
|
+
--loki-text-primary: #1A1614;
|
|
17
|
+
--loki-text-secondary: #4A4640;
|
|
18
|
+
--loki-text-muted: #8A857C;
|
|
19
|
+
--loki-accent: #553DE9;
|
|
20
|
+
--loki-accent-hover: #4432c4;
|
|
21
|
+
--loki-accent-glow: rgba(85, 61, 233, 0.15);
|
|
22
|
+
--loki-border: rgba(0, 0, 0, 0.08);
|
|
23
|
+
--loki-success: #1AAF95;
|
|
24
|
+
--loki-glass-bg: rgba(255, 255, 255, 0.55);
|
|
25
|
+
--loki-glass-border: rgba(255, 255, 255, 0.35);
|
|
26
|
+
--loki-glass-shadow: 0 4px 24px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
27
|
+
--loki-transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
28
|
+
}
|
|
29
|
+
@media (prefers-color-scheme: dark) {
|
|
30
|
+
:root {
|
|
31
|
+
--loki-bg-primary: #0F0B1A;
|
|
32
|
+
--loki-bg-secondary: #150F24;
|
|
33
|
+
--loki-bg-card: rgba(30, 21, 51, 0.72);
|
|
34
|
+
--loki-text-primary: #F0ECF8;
|
|
35
|
+
--loki-text-secondary: #B8B0C8;
|
|
36
|
+
--loki-text-muted: #8A82A0;
|
|
37
|
+
--loki-border: rgba(255, 255, 255, 0.10);
|
|
38
|
+
--loki-glass-bg: rgba(30, 21, 51, 0.55);
|
|
39
|
+
--loki-glass-border: rgba(255, 255, 255, 0.10);
|
|
40
|
+
--loki-glass-shadow: 0 4px 24px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
44
|
+
html, body { height: 100%; }
|
|
45
|
+
body {
|
|
46
|
+
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
47
|
+
background: var(--loki-bg-primary);
|
|
48
|
+
color: var(--loki-text-primary);
|
|
49
|
+
line-height: 1.55;
|
|
50
|
+
-webkit-font-smoothing: antialiased;
|
|
51
|
+
background-image: radial-gradient(circle at 15% 10%, var(--loki-accent-glow), transparent 40%);
|
|
52
|
+
}
|
|
53
|
+
.wrap { max-width: 880px; margin: 0 auto; padding: 56px 24px 72px; }
|
|
54
|
+
.brand { display: flex; align-items: center; gap: 10px; margin-bottom: 36px; }
|
|
55
|
+
.brand-mark {
|
|
56
|
+
width: 34px; height: 34px; border-radius: 9px;
|
|
57
|
+
background: linear-gradient(135deg, var(--loki-accent), #7B63FF);
|
|
58
|
+
display: flex; align-items: center; justify-content: center;
|
|
59
|
+
color: #fff; font-family: 'DM Serif Display', serif; font-size: 20px;
|
|
60
|
+
}
|
|
61
|
+
.brand-name { font-family: 'DM Serif Display', serif; font-size: 1.25rem; }
|
|
62
|
+
.brand-by { color: var(--loki-text-muted); font-size: 0.8rem; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
63
|
+
h1 { font-family: 'DM Serif Display', Georgia, serif; font-size: 2.6rem; font-weight: 400; line-height: 1.1; margin-bottom: 14px; letter-spacing: -0.01em; }
|
|
64
|
+
.lede { font-size: 1.12rem; color: var(--loki-text-secondary); max-width: 600px; margin-bottom: 8px; }
|
|
65
|
+
.ver { font-family: 'JetBrains Mono', monospace; font-size: 0.8rem; color: var(--loki-text-muted); margin-bottom: 36px; }
|
|
66
|
+
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; margin-bottom: 40px; }
|
|
67
|
+
.card {
|
|
68
|
+
background: var(--loki-glass-bg);
|
|
69
|
+
border: 1px solid var(--loki-glass-border);
|
|
70
|
+
box-shadow: var(--loki-glass-shadow);
|
|
71
|
+
border-radius: 12px; padding: 18px 18px 20px;
|
|
72
|
+
backdrop-filter: blur(8px);
|
|
73
|
+
}
|
|
74
|
+
.card h3 { font-family: 'DM Serif Display', serif; font-size: 1.05rem; font-weight: 400; margin-bottom: 6px; }
|
|
75
|
+
.card p { font-size: 0.88rem; color: var(--loki-text-secondary); }
|
|
76
|
+
.card .tag { display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--loki-accent); margin-bottom: 10px; }
|
|
77
|
+
.form-card {
|
|
78
|
+
background: var(--loki-bg-card);
|
|
79
|
+
border: 1px solid var(--loki-border);
|
|
80
|
+
border-radius: 14px; padding: 26px; margin-bottom: 18px;
|
|
81
|
+
box-shadow: var(--loki-glass-shadow);
|
|
82
|
+
}
|
|
83
|
+
.form-card h2 { font-family: 'DM Serif Display', serif; font-weight: 400; font-size: 1.4rem; margin-bottom: 6px; }
|
|
84
|
+
.form-sub { color: var(--loki-text-muted); font-size: 0.9rem; margin-bottom: 22px; }
|
|
85
|
+
.field { margin-bottom: 18px; }
|
|
86
|
+
.field label { display: block; font-size: 0.82rem; font-weight: 500; margin-bottom: 7px; color: var(--loki-text-secondary); }
|
|
87
|
+
select {
|
|
88
|
+
width: 100%; padding: 10px 12px; font-family: inherit; font-size: 0.92rem;
|
|
89
|
+
color: var(--loki-text-primary); background: var(--loki-bg-primary);
|
|
90
|
+
border: 1px solid var(--loki-border); border-radius: 8px; appearance: none;
|
|
91
|
+
cursor: pointer; transition: border-color var(--loki-transition);
|
|
92
|
+
}
|
|
93
|
+
select:focus { outline: none; border-color: var(--loki-accent); box-shadow: 0 0 0 3px var(--loki-accent-glow); }
|
|
94
|
+
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
95
|
+
.chip {
|
|
96
|
+
font-size: 0.84rem; padding: 7px 13px; border-radius: 999px;
|
|
97
|
+
border: 1px solid var(--loki-border); background: var(--loki-bg-primary);
|
|
98
|
+
color: var(--loki-text-secondary); cursor: pointer; user-select: none;
|
|
99
|
+
transition: all var(--loki-transition);
|
|
100
|
+
}
|
|
101
|
+
.chip[aria-pressed="true"] {
|
|
102
|
+
background: var(--loki-accent); color: #fff; border-color: var(--loki-accent);
|
|
103
|
+
}
|
|
104
|
+
.btn {
|
|
105
|
+
width: 100%; padding: 13px; font-family: inherit; font-size: 0.95rem; font-weight: 600;
|
|
106
|
+
color: #fff; background: var(--loki-accent); border: none; border-radius: 9px;
|
|
107
|
+
cursor: pointer; transition: background var(--loki-transition); margin-top: 6px;
|
|
108
|
+
}
|
|
109
|
+
.btn:hover { background: var(--loki-accent-hover); }
|
|
110
|
+
.btn:disabled { opacity: 0.55; cursor: not-allowed; }
|
|
111
|
+
.consent { font-size: 0.76rem; color: var(--loki-text-muted); margin-top: 14px; line-height: 1.5; }
|
|
112
|
+
.consent code { font-family: 'JetBrains Mono', monospace; font-size: 0.72rem; background: var(--loki-bg-secondary); padding: 1px 5px; border-radius: 4px; }
|
|
113
|
+
.cta-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
|
|
114
|
+
.cta {
|
|
115
|
+
display: inline-flex; align-items: center; gap: 7px; text-decoration: none;
|
|
116
|
+
font-size: 0.9rem; font-weight: 500; padding: 10px 16px; border-radius: 8px;
|
|
117
|
+
border: 1px solid var(--loki-border); color: var(--loki-text-primary);
|
|
118
|
+
background: var(--loki-bg-card); transition: all var(--loki-transition);
|
|
119
|
+
}
|
|
120
|
+
.cta:hover { border-color: var(--loki-accent); color: var(--loki-accent); }
|
|
121
|
+
.cta.primary { background: var(--loki-accent); color: #fff; border-color: var(--loki-accent); }
|
|
122
|
+
.cta.primary:hover { background: var(--loki-accent-hover); color: #fff; }
|
|
123
|
+
.quickstart {
|
|
124
|
+
font-family: 'JetBrains Mono', monospace; font-size: 0.85rem;
|
|
125
|
+
background: var(--loki-bg-secondary); border: 1px solid var(--loki-border);
|
|
126
|
+
border-radius: 8px; padding: 9px 13px; color: var(--loki-text-primary);
|
|
127
|
+
}
|
|
128
|
+
.research { margin-top: 34px; font-size: 0.76rem; color: var(--loki-text-muted); }
|
|
129
|
+
.research .rchips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
|
130
|
+
.rchip { font-family: 'JetBrains Mono', monospace; font-size: 0.68rem; padding: 3px 8px; border: 1px solid var(--loki-border); border-radius: 6px; }
|
|
131
|
+
.thanks { display: none; text-align: center; padding: 18px 0; }
|
|
132
|
+
.thanks.show { display: block; }
|
|
133
|
+
.thanks .check { width: 44px; height: 44px; border-radius: 50%; background: var(--loki-success); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 22px; margin-bottom: 12px; }
|
|
134
|
+
.disabled-note { display: none; font-size: 0.85rem; color: var(--loki-text-muted); background: var(--loki-bg-secondary); border: 1px solid var(--loki-border); border-radius: 8px; padding: 12px 14px; }
|
|
135
|
+
.disabled-note.show { display: block; }
|
|
136
|
+
footer { margin-top: 40px; font-size: 0.78rem; color: var(--loki-text-muted); }
|
|
137
|
+
footer a { color: var(--loki-accent); text-decoration: none; }
|
|
138
|
+
</style>
|
|
139
|
+
</head>
|
|
140
|
+
<body>
|
|
141
|
+
<div class="wrap">
|
|
142
|
+
<div class="brand">
|
|
143
|
+
<div class="brand-mark">L</div>
|
|
144
|
+
<div>
|
|
145
|
+
<div class="brand-name">Loki Mode</div>
|
|
146
|
+
<div class="brand-by">Powered by Autonomi</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<h1>Describe it. Walk away.<br>Get working, tested software.</h1>
|
|
151
|
+
<p class="lede">Loki takes a spec (a PRD, a GitHub issue, or a one-line brief) all the way to a deployed product, running its Reason-Act-Reflect-Verify-Close loop until the work is actually done, not just attempted.</p>
|
|
152
|
+
<div class="ver" id="ver">loki-mode</div>
|
|
153
|
+
|
|
154
|
+
<div class="cards">
|
|
155
|
+
<div class="card">
|
|
156
|
+
<span class="tag">Verified, not just generated</span>
|
|
157
|
+
<h3>Closure loop + council</h3>
|
|
158
|
+
<p>Every change passes a RARV-C cycle and a unanimous multi-reviewer council before it counts as done. A quality bar you can audit.</p>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="card">
|
|
161
|
+
<span class="tag">Compounds over time</span>
|
|
162
|
+
<h3>Cross-project memory</h3>
|
|
163
|
+
<p>Lessons learned on one project surface on the next, so your agents stop repeating the same mistakes across your whole codebase.</p>
|
|
164
|
+
</div>
|
|
165
|
+
<div class="card">
|
|
166
|
+
<span class="tag">Yours to run</span>
|
|
167
|
+
<h3>Provider-agnostic and private</h3>
|
|
168
|
+
<p>Runs on Claude, Codex, Cline, or Aider. Your keys, your infrastructure, no vendor lock-in.</p>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div class="form-card" id="formCard">
|
|
173
|
+
<h2>Tell us who you are</h2>
|
|
174
|
+
<p class="form-sub">Optional, takes 10 seconds, and helps us build the right thing.</p>
|
|
175
|
+
|
|
176
|
+
<div class="field">
|
|
177
|
+
<label for="role">Your role</label>
|
|
178
|
+
<select id="role">
|
|
179
|
+
<option value="">Select...</option>
|
|
180
|
+
<option>Founder / CEO</option>
|
|
181
|
+
<option>Engineering leader</option>
|
|
182
|
+
<option>Software engineer</option>
|
|
183
|
+
<option>Product manager</option>
|
|
184
|
+
<option>Designer</option>
|
|
185
|
+
<option>Student / learner</option>
|
|
186
|
+
<option>Researcher</option>
|
|
187
|
+
<option>Other</option>
|
|
188
|
+
</select>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div class="field">
|
|
192
|
+
<label for="company">Company size</label>
|
|
193
|
+
<select id="company">
|
|
194
|
+
<option value="">Select...</option>
|
|
195
|
+
<option>Just me</option>
|
|
196
|
+
<option>2-10</option>
|
|
197
|
+
<option>11-50</option>
|
|
198
|
+
<option>51-200</option>
|
|
199
|
+
<option>201-1000</option>
|
|
200
|
+
<option>1000+</option>
|
|
201
|
+
</select>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div class="field">
|
|
205
|
+
<label>Tools you use today</label>
|
|
206
|
+
<div class="chips" id="tools">
|
|
207
|
+
<span class="chip" role="button" tabindex="0" aria-pressed="false">Claude Code</span>
|
|
208
|
+
<span class="chip" role="button" tabindex="0" aria-pressed="false">Cursor</span>
|
|
209
|
+
<span class="chip" role="button" tabindex="0" aria-pressed="false">OpenAI Codex</span>
|
|
210
|
+
<span class="chip" role="button" tabindex="0" aria-pressed="false">GitHub Copilot</span>
|
|
211
|
+
<span class="chip" role="button" tabindex="0" aria-pressed="false">Cline</span>
|
|
212
|
+
<span class="chip" role="button" tabindex="0" aria-pressed="false">Aider</span>
|
|
213
|
+
<span class="chip" role="button" tabindex="0" aria-pressed="false">Replit</span>
|
|
214
|
+
<span class="chip" role="button" tabindex="0" aria-pressed="false">Other</span>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<button class="btn" id="submitBtn">Send and get started</button>
|
|
219
|
+
<p class="consent">
|
|
220
|
+
Sends <strong>anonymous</strong> usage analytics (your role, company size, and tools)
|
|
221
|
+
to help us improve the product. We <strong>never</strong> collect your prompts, PRDs, or
|
|
222
|
+
code. Opt out anytime with <code>LOKI_TELEMETRY_DISABLED=true</code> or <code>DO_NOT_TRACK=1</code>.
|
|
223
|
+
</p>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<div class="disabled-note" id="disabledNote">
|
|
227
|
+
Analytics are turned off for this session (LOKI_TELEMETRY_DISABLED / DO_NOT_TRACK).
|
|
228
|
+
Nothing will be sent. Welcome aboard regardless.
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div class="thanks" id="thanks">
|
|
232
|
+
<div class="check">✓</div>
|
|
233
|
+
<h2 style="font-family:'DM Serif Display',serif;font-weight:400;">Thanks, you're all set.</h2>
|
|
234
|
+
<p style="color:var(--loki-text-secondary);margin-top:6px;">Point Loki at a spec and let it run.</p>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<div class="cta-row">
|
|
238
|
+
<a class="cta primary" href="https://www.autonomi.dev/docs" target="_blank" rel="noopener">Read the docs</a>
|
|
239
|
+
<span class="quickstart">loki start ./prd.md</span>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div class="research">
|
|
243
|
+
Built on published research from Anthropic, Google DeepMind, OpenAI, and NVIDIA.
|
|
244
|
+
<div class="rchips">
|
|
245
|
+
<span class="rchip">Constitutional AI</span>
|
|
246
|
+
<span class="rchip">Scalable Oversight via Debate</span>
|
|
247
|
+
<span class="rchip">Agents SDK guardrails</span>
|
|
248
|
+
<span class="rchip">ToolOrchestra efficiency</span>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<footer>
|
|
253
|
+
Loki Mode by <a href="https://www.autonomi.dev/" target="_blank" rel="noopener">Autonomi</a>.
|
|
254
|
+
Questions? <a href="https://www.autonomi.dev/docs" target="_blank" rel="noopener">autonomi.dev/docs</a>
|
|
255
|
+
</footer>
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<script>
|
|
259
|
+
(function () {
|
|
260
|
+
"use strict";
|
|
261
|
+
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
262
|
+
var POSTHOG_KEY = "phc_ya0vGBru41AJWtGNfZZ8H9W4yjoZy4KON0nnayS7s87";
|
|
263
|
+
|
|
264
|
+
// Read params the CLI may pass: ?telemetry=off, ?v=<version>, ?key=<override>, ?did=<distinct_id>
|
|
265
|
+
var params = new URLSearchParams(window.location.search);
|
|
266
|
+
var telemetryOff = params.get("telemetry") === "off";
|
|
267
|
+
var version = params.get("v") || "loki-mode";
|
|
268
|
+
if (params.get("key")) { POSTHOG_KEY = params.get("key"); }
|
|
269
|
+
var verEl = document.getElementById("ver");
|
|
270
|
+
if (verEl) { verEl.textContent = version; }
|
|
271
|
+
|
|
272
|
+
// distinct_id: prefer the id the CLI injects (the existing anonymous
|
|
273
|
+
// ~/.loki-telemetry-id), else a browser-local random id. Never an email
|
|
274
|
+
// or real name (the form has no such field).
|
|
275
|
+
function distinctId() {
|
|
276
|
+
var injected = params.get("did");
|
|
277
|
+
if (injected) { return injected; }
|
|
278
|
+
try {
|
|
279
|
+
var k = "loki_welcome_did";
|
|
280
|
+
var v = localStorage.getItem(k);
|
|
281
|
+
if (!v) {
|
|
282
|
+
v = (window.crypto && crypto.randomUUID) ? crypto.randomUUID()
|
|
283
|
+
: "web-" + Date.now() + "-" + Math.random().toString(36).slice(2);
|
|
284
|
+
localStorage.setItem(k, v);
|
|
285
|
+
}
|
|
286
|
+
return v;
|
|
287
|
+
} catch (e) {
|
|
288
|
+
return "web-anon";
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
var formCard = document.getElementById("formCard");
|
|
293
|
+
var disabledNote = document.getElementById("disabledNote");
|
|
294
|
+
var thanks = document.getElementById("thanks");
|
|
295
|
+
var submitBtn = document.getElementById("submitBtn");
|
|
296
|
+
|
|
297
|
+
// Opt-out: hide the form and return before any handler binds, so a
|
|
298
|
+
// capture can never fire. No network call happens on load either.
|
|
299
|
+
if (telemetryOff) {
|
|
300
|
+
if (formCard) { formCard.style.display = "none"; }
|
|
301
|
+
if (disabledNote) { disabledNote.classList.add("show"); }
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Tool chips toggle (local UI state only).
|
|
306
|
+
var toolEls = Array.prototype.slice.call(document.querySelectorAll("#tools .chip"));
|
|
307
|
+
function toggleChip(el) {
|
|
308
|
+
var on = el.getAttribute("aria-pressed") === "true";
|
|
309
|
+
el.setAttribute("aria-pressed", on ? "false" : "true");
|
|
310
|
+
}
|
|
311
|
+
toolEls.forEach(function (el) {
|
|
312
|
+
el.addEventListener("click", function () { toggleChip(el); });
|
|
313
|
+
el.addEventListener("keydown", function (e) {
|
|
314
|
+
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggleChip(el); }
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// The ONLY network call in the page happens here, on explicit click.
|
|
319
|
+
submitBtn.addEventListener("click", function () {
|
|
320
|
+
var role = document.getElementById("role").value || "unspecified";
|
|
321
|
+
var company = document.getElementById("company").value || "unspecified";
|
|
322
|
+
var tools = toolEls
|
|
323
|
+
.filter(function (el) { return el.getAttribute("aria-pressed") === "true"; })
|
|
324
|
+
.map(function (el) { return el.textContent.trim(); });
|
|
325
|
+
|
|
326
|
+
var payload = {
|
|
327
|
+
api_key: POSTHOG_KEY,
|
|
328
|
+
event: "welcome_profile",
|
|
329
|
+
distinct_id: distinctId(),
|
|
330
|
+
properties: {
|
|
331
|
+
role: role,
|
|
332
|
+
company_size: company,
|
|
333
|
+
tools: tools,
|
|
334
|
+
source: "welcome_opener",
|
|
335
|
+
loki_version: version
|
|
336
|
+
// Deliberately NO prompt, PRD, code, file path, cwd, hostname,
|
|
337
|
+
// project name, email, or real name. Anonymous fields only.
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
submitBtn.disabled = true;
|
|
342
|
+
submitBtn.textContent = "Sending...";
|
|
343
|
+
|
|
344
|
+
fetch(POSTHOG_HOST + "/capture/", {
|
|
345
|
+
method: "POST",
|
|
346
|
+
headers: { "Content-Type": "application/json" },
|
|
347
|
+
body: JSON.stringify(payload),
|
|
348
|
+
keepalive: true
|
|
349
|
+
}).catch(function () { /* fire-and-forget; never block the user */ })
|
|
350
|
+
.finally(function () {
|
|
351
|
+
if (formCard) { formCard.style.display = "none"; }
|
|
352
|
+
if (thanks) { thanks.classList.add("show"); }
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
})();
|
|
356
|
+
</script>
|
|
357
|
+
</body>
|
|
358
|
+
</html>
|
|
@@ -1199,7 +1199,16 @@ council_evaluate_member() {
|
|
|
1199
1199
|
for test_log in "$loki_dir"/logs/test-*.log "$loki_dir"/logs/*test*.log; do
|
|
1200
1200
|
if [ -f "$test_log" ]; then
|
|
1201
1201
|
local fail_count
|
|
1202
|
-
|
|
1202
|
+
# Detect REAL test failures, not any line containing "error".
|
|
1203
|
+
# The old blanket grep "(FAIL|ERROR|failed|error:)" counted
|
|
1204
|
+
# benign lines ("0 errors", "0 failed", a test named
|
|
1205
|
+
# test_error_handling, "no errors found"), which forced CONTINUE
|
|
1206
|
+
# forever on a fully-passing suite that merely mentions "error".
|
|
1207
|
+
# Match actual failure signals and exclude the zero-count forms.
|
|
1208
|
+
fail_count=$(grep -ciE \
|
|
1209
|
+
'([1-9][0-9]*[[:space:]]+(failed|errors?)|^FAILED|[[:space:]]FAILED[[:space:]]|tests? failed|assertionerror|traceback \(most recent)' \
|
|
1210
|
+
"$test_log" 2>/dev/null | tr -dc '0-9')
|
|
1211
|
+
fail_count=${fail_count:-0}
|
|
1203
1212
|
test_failures=$((test_failures + fail_count))
|
|
1204
1213
|
fi
|
|
1205
1214
|
done
|
|
@@ -362,7 +362,10 @@ import json, sys
|
|
|
362
362
|
from datetime import datetime
|
|
363
363
|
with open(sys.argv[1]) as f:
|
|
364
364
|
data = json.load(f)
|
|
365
|
-
|
|
365
|
+
# setdefault (not get): get('modes', []) appended to a THROWAWAY list when
|
|
366
|
+
# the key was missing, so the failure mode was silently lost on a fresh
|
|
367
|
+
# failure-modes.json. setdefault binds the list into data before appending.
|
|
368
|
+
data.setdefault('modes', []).append({
|
|
366
369
|
'mode_id': 'heal-fail-' + datetime.now().strftime('%Y%m%dT%H%M%S'),
|
|
367
370
|
'trigger': 'healing_modification',
|
|
368
371
|
'file': sys.argv[2],
|
package/autonomy/loki
CHANGED
|
@@ -700,6 +700,10 @@ detect_arg_type() {
|
|
|
700
700
|
|
|
701
701
|
# Start Loki Mode
|
|
702
702
|
cmd_start() {
|
|
703
|
+
# First-run welcome (once): open the branded welcome page on the very
|
|
704
|
+
# first start. Writes ~/.loki/.welcomed and never repeats; non-blocking,
|
|
705
|
+
# and never auto-opens a browser in CI or non-interactive shells.
|
|
706
|
+
cmd_welcome_maybe_firstrun 2>/dev/null || true
|
|
703
707
|
local args=()
|
|
704
708
|
local prd_file=""
|
|
705
709
|
local provider=""
|
|
@@ -3837,6 +3841,94 @@ cmd_dashboard_open() {
|
|
|
3837
3841
|
fi
|
|
3838
3842
|
}
|
|
3839
3843
|
|
|
3844
|
+
# Welcome opener (the "magic opener"). Shows a branded welcome page in the
|
|
3845
|
+
# browser, or a terminal welcome when no browser is available (headless,
|
|
3846
|
+
# Docker, CI). Honors telemetry opt-out (LOKI_TELEMETRY_DISABLED / DO_NOT_TRACK):
|
|
3847
|
+
# when opted out, the page is loaded with ?telemetry=off so its form is
|
|
3848
|
+
# disabled and no analytics are ever sent.
|
|
3849
|
+
WELCOME_MARKER="${HOME}/.loki/.welcomed"
|
|
3850
|
+
|
|
3851
|
+
_loki_telemetry_off() {
|
|
3852
|
+
[ "${LOKI_TELEMETRY_DISABLED:-}" = "true" ] && return 0
|
|
3853
|
+
[ "${DO_NOT_TRACK:-}" = "1" ] && return 0
|
|
3854
|
+
return 1
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
cmd_welcome_terminal() {
|
|
3858
|
+
local ver="${1:-}"
|
|
3859
|
+
echo ""
|
|
3860
|
+
echo -e " ${BOLD}Loki Mode${NC} ${DIM}by Autonomi${NC}${ver:+ ${DIM}v${ver}${NC}}"
|
|
3861
|
+
echo ""
|
|
3862
|
+
echo -e " ${BOLD}Describe it. Walk away. Get working, tested software.${NC}"
|
|
3863
|
+
echo ""
|
|
3864
|
+
echo -e " Loki takes a spec (PRD, GitHub issue, or one-line brief) to a deployed"
|
|
3865
|
+
echo -e " product via the RARV-C closure loop, until the work is actually done."
|
|
3866
|
+
echo ""
|
|
3867
|
+
echo -e " ${CYAN}Closure loop + council${NC} Every change is verified by a unanimous council."
|
|
3868
|
+
echo -e " ${CYAN}Cross-project memory${NC} Lessons compound, so agents stop repeating mistakes."
|
|
3869
|
+
echo -e " ${CYAN}Provider-agnostic${NC} Claude, Codex, Cline, or Aider. Your keys, your infra."
|
|
3870
|
+
echo ""
|
|
3871
|
+
echo -e " Quick start: ${BOLD}loki start ./prd.md${NC}"
|
|
3872
|
+
echo -e " Docs: ${BOLD}https://www.autonomi.dev/docs${NC}"
|
|
3873
|
+
echo ""
|
|
3874
|
+
if _loki_telemetry_off; then
|
|
3875
|
+
echo -e " ${DIM}Analytics are off for this session. Nothing is sent.${NC}"
|
|
3876
|
+
else
|
|
3877
|
+
echo -e " ${DIM}Anonymous usage analytics help us improve the product. We never${NC}"
|
|
3878
|
+
echo -e " ${DIM}collect prompts, PRDs, or code. Opt out: LOKI_TELEMETRY_DISABLED=true${NC}"
|
|
3879
|
+
fi
|
|
3880
|
+
echo ""
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
cmd_welcome() {
|
|
3884
|
+
local ver=""
|
|
3885
|
+
[ -f "${SKILL_DIR}/VERSION" ] && ver="$(cat "${SKILL_DIR}/VERSION" 2>/dev/null | tr -d '[:space:]')"
|
|
3886
|
+
|
|
3887
|
+
local welcome_file="${SKILL_DIR}/assets/welcome/welcome.html"
|
|
3888
|
+
|
|
3889
|
+
# Build the file URL with params: version, opt-out, and the existing
|
|
3890
|
+
# anonymous telemetry distinct-id (so browser + install share one id).
|
|
3891
|
+
local q="v=${ver}"
|
|
3892
|
+
if _loki_telemetry_off; then
|
|
3893
|
+
q="${q}&telemetry=off"
|
|
3894
|
+
else
|
|
3895
|
+
local idfile="${HOME}/.loki-telemetry-id"
|
|
3896
|
+
[ -f "$idfile" ] && q="${q}&did=$(cat "$idfile" 2>/dev/null | tr -d '[:space:]')"
|
|
3897
|
+
fi
|
|
3898
|
+
|
|
3899
|
+
# Headless / Docker / CI / no-browser: print the terminal welcome only.
|
|
3900
|
+
local has_browser=""
|
|
3901
|
+
if command -v open >/dev/null 2>&1; then has_browser="open"
|
|
3902
|
+
elif command -v xdg-open >/dev/null 2>&1; then has_browser="xdg-open"
|
|
3903
|
+
elif command -v start >/dev/null 2>&1; then has_browser="start"
|
|
3904
|
+
fi
|
|
3905
|
+
|
|
3906
|
+
if [ -z "$has_browser" ] || [ ! -f "$welcome_file" ] || [ -n "${CI:-}" ] || [ ! -t 1 ]; then
|
|
3907
|
+
cmd_welcome_terminal "$ver"
|
|
3908
|
+
[ -f "$welcome_file" ] && echo -e " ${DIM}Full welcome page: ${welcome_file}${NC}" && echo ""
|
|
3909
|
+
return 0
|
|
3910
|
+
fi
|
|
3911
|
+
|
|
3912
|
+
echo -e "${GREEN}Opening the Loki Mode welcome page in your browser...${NC}"
|
|
3913
|
+
"$has_browser" "file://${welcome_file}?${q}" >/dev/null 2>&1 || cmd_welcome_terminal "$ver"
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
# First-run hook: open the welcome once, then never again. Browser only when
|
|
3917
|
+
# interactive + not CI + not opted out; otherwise nothing (no noisy auto-print
|
|
3918
|
+
# during a real run). Marker lives at ~/.loki/.welcomed.
|
|
3919
|
+
cmd_welcome_maybe_firstrun() {
|
|
3920
|
+
[ -f "$WELCOME_MARKER" ] && return 0
|
|
3921
|
+
mkdir -p "$(dirname "$WELCOME_MARKER")" 2>/dev/null || true
|
|
3922
|
+
: > "$WELCOME_MARKER" 2>/dev/null || true
|
|
3923
|
+
# Only auto-open on an interactive, non-CI terminal. cmd_welcome itself
|
|
3924
|
+
# re-checks for a browser and falls back to the terminal welcome, so we
|
|
3925
|
+
# gate purely on interactivity + CI here (grouped to avoid &&/|| precedence
|
|
3926
|
+
# ambiguity). Backgrounded so it never delays the start.
|
|
3927
|
+
if [ -t 1 ] && [ -z "${CI:-}" ]; then
|
|
3928
|
+
(cmd_welcome >/dev/null 2>&1 &) || true
|
|
3929
|
+
fi
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3840
3932
|
# Web app management
|
|
3841
3933
|
# Purple Lab -- standalone product web UI for Loki Mode.
|
|
3842
3934
|
# Runs on port 57375 (separate from dashboard at 57374).
|
|
@@ -12505,6 +12597,9 @@ main() {
|
|
|
12505
12597
|
demo)
|
|
12506
12598
|
cmd_demo "$@"
|
|
12507
12599
|
;;
|
|
12600
|
+
welcome)
|
|
12601
|
+
cmd_welcome "$@"
|
|
12602
|
+
;;
|
|
12508
12603
|
init)
|
|
12509
12604
|
cmd_init "$@"
|
|
12510
12605
|
;;
|
package/autonomy/run.sh
CHANGED
|
@@ -8710,7 +8710,11 @@ try:
|
|
|
8710
8710
|
from memory.engine import MemoryEngine
|
|
8711
8711
|
from memory.schemas import EpisodeTrace
|
|
8712
8712
|
from datetime import datetime, timezone
|
|
8713
|
-
|
|
8713
|
+
# base_path= is required: MemoryEngine.__init__(self, storage=None, base_path=...)
|
|
8714
|
+
# takes `storage` first, so a bare positional path was assigned to
|
|
8715
|
+
# self.storage and engine.initialize() crashed on str.ensure_directory,
|
|
8716
|
+
# silently dropping every store_episode_trace into the except handler.
|
|
8717
|
+
engine = MemoryEngine(base_path=f'{target_dir}/.loki/memory')
|
|
8714
8718
|
engine.initialize()
|
|
8715
8719
|
trace = EpisodeTrace.create(
|
|
8716
8720
|
task_id=task_id,
|
|
@@ -9071,7 +9075,15 @@ try:
|
|
|
9071
9075
|
importance = float(getattr(trace, 'importance', 0.0) or 0.0)
|
|
9072
9076
|
except (TypeError, ValueError):
|
|
9073
9077
|
importance = 0.0
|
|
9074
|
-
|
|
9078
|
+
# Reconstruct the ACTUAL on-disk path. storage.save_episode writes to
|
|
9079
|
+
# episodic/<YYYY-MM-DD>/task-<id>.json (date from the trace timestamp),
|
|
9080
|
+
# NOT episodic/<id>.json. The old flat path never existed, so the
|
|
9081
|
+
# importance shadow-write guard in bash never fired.
|
|
9082
|
+
_ts = getattr(trace, 'timestamp', '') or ''
|
|
9083
|
+
_date_str = str(_ts)[:10] if _ts else __import__('datetime').datetime.now(
|
|
9084
|
+
__import__('datetime').timezone.utc).strftime('%Y-%m-%d')
|
|
9085
|
+
episode_file = (Path(f'{target_dir}/.loki/memory/episodic')
|
|
9086
|
+
/ _date_str / f'task-{trace.id}.json')
|
|
9075
9087
|
if path_out_file:
|
|
9076
9088
|
try:
|
|
9077
9089
|
with open(path_out_file, 'w', encoding='utf-8') as f:
|
package/bin/postinstall.js
CHANGED
|
@@ -173,8 +173,11 @@ console.log(' loki start --provider codex Start with OpenAI Codex');
|
|
|
173
173
|
console.log(' loki start --provider gemini Start with Google Gemini');
|
|
174
174
|
console.log(' loki status Check status');
|
|
175
175
|
console.log(' loki doctor Verify installation');
|
|
176
|
+
console.log(' loki welcome Quick tour, docs, and setup');
|
|
176
177
|
console.log(' loki --help Show all commands');
|
|
177
178
|
console.log('');
|
|
179
|
+
console.log('New here? Run `loki welcome` for a 30-second tour.');
|
|
180
|
+
console.log('');
|
|
178
181
|
|
|
179
182
|
// Anonymous install telemetry (fire-and-forget, silent)
|
|
180
183
|
try {
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Welcome Opener Plan (the "magic opener")
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
A polished welcome experience shown when people install the loki CLI or
|
|
5
|
+
run the Docker image. It (1) introduces Loki/Autonomi with the dashboard's
|
|
6
|
+
design language, (2) subtly highlights the research backing + memory
|
|
7
|
+
compounding moat, (3) offers a simple opt-in form collecting anonymous
|
|
8
|
+
role / business / tools, transmitted to PostHog on click, and (4) links to
|
|
9
|
+
autonomi.dev/docs.
|
|
10
|
+
|
|
11
|
+
## Hard constraints (from the user + CLAUDE.md)
|
|
12
|
+
- Anonymous usage analytics for PRODUCT IMPROVEMENT ONLY. NEVER transmit
|
|
13
|
+
prompts, PRDs, code, or any project content. Disclosed, not covert.
|
|
14
|
+
- Reuse the EXISTING PostHog contract already in the codebase:
|
|
15
|
+
- host `https://us.i.posthog.com`, path `/capture/`
|
|
16
|
+
- public ingest key `phc_ya0vGBru41AJWtGNfZZ8H9W4yjoZy4KON0nnayS7s87`
|
|
17
|
+
(same key postinstall.js + autonomy/telemetry.sh already use; public
|
|
18
|
+
ingest keys are safe in client code), overridable by LOKI_POSTHOG_KEY.
|
|
19
|
+
- Honor opt-out: LOKI_TELEMETRY_DISABLED=true and DO_NOT_TRACK=1.
|
|
20
|
+
- Design must match the loki dashboard: accent #553DE9, light bg #FAFAF7 /
|
|
21
|
+
dark #0F0B1A, text #1A1614 / #F0ECF8, fonts DM Serif Display (headings),
|
|
22
|
+
Inter (body), JetBrains Mono (mono). Glass cards. Light + dark.
|
|
23
|
+
- No emojis, no em dashes anywhere.
|
|
24
|
+
|
|
25
|
+
## Components
|
|
26
|
+
|
|
27
|
+
### 1. web-app-less static page: `assets/welcome/welcome.html`
|
|
28
|
+
Self-contained single HTML file (inline CSS + JS, Google Fonts link), so
|
|
29
|
+
it works opened directly from disk OR served. Sections:
|
|
30
|
+
- Hero: "Loki Mode by Autonomi" + tagline "Describe it. Walk away. Get
|
|
31
|
+
working, tested software." + version (templated at serve time or left
|
|
32
|
+
generic).
|
|
33
|
+
- Three subtle highlight cards: (a) RARV-C closure + unanimous council
|
|
34
|
+
("finishes the work, verified"), (b) Cross-project memory that compounds
|
|
35
|
+
("your agents stop repeating mistakes across projects"), (c) Research
|
|
36
|
+
backing (Anthropic Constitutional AI / DeepMind debate / OpenAI Agents
|
|
37
|
+
SDK / NVIDIA ToolOrchestra) shown as small footnote-style chips, not
|
|
38
|
+
loud claims.
|
|
39
|
+
- Opt-in form: role (select), company size (select), primary tools
|
|
40
|
+
(multi-select chips: Claude Code, Cursor, Codex, Copilot, Cline, Aider,
|
|
41
|
+
other). A single "Send + get started" button. Consent line directly
|
|
42
|
+
under it: "Sends anonymous usage analytics (your role, company size,
|
|
43
|
+
tools) to help us improve the product. We never collect your prompts,
|
|
44
|
+
PRDs, or code. Opt out anytime with LOKI_TELEMETRY_DISABLED=true."
|
|
45
|
+
- CTA row: "Read the docs" -> https://www.autonomi.dev/docs ; "Quick
|
|
46
|
+
start" copyable `loki start ./prd.md`.
|
|
47
|
+
- On submit: POST to us.i.posthog.com/capture/ with event
|
|
48
|
+
`welcome_profile`, distinct_id = the existing ~/.loki-telemetry-id (or a
|
|
49
|
+
fresh uuid in browser localStorage if not injected), properties =
|
|
50
|
+
{role, company_size, tools, source:'welcome_opener', loki_version}.
|
|
51
|
+
Then show a thank-you state + the docs link. Submission is the ONLY
|
|
52
|
+
network call; nothing is sent on page load.
|
|
53
|
+
- Respect opt-out: if the page is served with ?telemetry=off (the CLI
|
|
54
|
+
passes this when LOKI_TELEMETRY_DISABLED/DO_NOT_TRACK is set), the form
|
|
55
|
+
is replaced with a "analytics disabled" note and the submit is a no-op.
|
|
56
|
+
|
|
57
|
+
### 2. CLI command: `loki welcome`
|
|
58
|
+
- Mirrors cmd_dashboard_open idiom. Resolves the welcome.html path inside
|
|
59
|
+
the package, opens it with `open`/`xdg-open`. If no browser opener
|
|
60
|
+
(headless/Docker), prints the terminal fallback (see #4) + the file path
|
|
61
|
+
+ the autonomi.dev/docs URL.
|
|
62
|
+
- Passes ?telemetry=off when opt-out env is set.
|
|
63
|
+
- Dispatch: add `welcome) cmd_welcome "$@" ;;` near the `demo)` case
|
|
64
|
+
(~autonomy/loki:12505) and a help line.
|
|
65
|
+
|
|
66
|
+
### 3. First-run auto-open (once)
|
|
67
|
+
- Marker file: `~/.loki/.welcomed`. On first `loki start` (and at end of
|
|
68
|
+
postinstall when a browser is available + not CI + not opt-out), if the
|
|
69
|
+
marker is absent: open the welcome page once, then write the marker.
|
|
70
|
+
Never auto-open again. CI/non-TTY/Docker never auto-open a browser; they
|
|
71
|
+
only print the terminal welcome.
|
|
72
|
+
- postinstall.js already prints an install summary; append a one-line
|
|
73
|
+
"Run `loki welcome` for a quick tour" pointer (no auto browser launch in
|
|
74
|
+
postinstall to avoid surprising npm installs).
|
|
75
|
+
|
|
76
|
+
### 4. Terminal fallback
|
|
77
|
+
- A clean ASCII/ANSI welcome (loki accent color) printed when no browser:
|
|
78
|
+
product one-liner, the 3 highlights as one line each, the docs URL, the
|
|
79
|
+
quick-start command, and the consent/opt-out line. No network call from
|
|
80
|
+
the terminal fallback (the form is browser-only; terminal just informs).
|
|
81
|
+
|
|
82
|
+
## Files
|
|
83
|
+
- ADD `assets/welcome/welcome.html`
|
|
84
|
+
- ADD `assets/welcome/welcome.terminal.sh` (sourced helper that prints the
|
|
85
|
+
terminal welcome) OR inline in cmd_welcome.
|
|
86
|
+
- EDIT `autonomy/loki`: cmd_welcome + dispatch + help + first-run hook in
|
|
87
|
+
cmd_start.
|
|
88
|
+
- EDIT `bin/postinstall.js`: add the "Run `loki welcome`" pointer line.
|
|
89
|
+
- EDIT `package.json` "files": add `assets/`.
|
|
90
|
+
- EDIT `Dockerfile`, `Dockerfile.sandbox`: COPY assets/.
|
|
91
|
+
- ADD `tests/test-welcome-opener.sh`.
|
|
92
|
+
- Version bump 14 locations + CHANGELOG.
|
|
93
|
+
|
|
94
|
+
## Privacy test matrix (must pass)
|
|
95
|
+
- Page load makes ZERO network calls (only submit does).
|
|
96
|
+
- Submit payload contains ONLY {role, company_size, tools, source,
|
|
97
|
+
loki_version, distinct_id}; never any file/prompt/PRD content.
|
|
98
|
+
- LOKI_TELEMETRY_DISABLED=true and DO_NOT_TRACK=1 each => form disabled,
|
|
99
|
+
no capture.
|
|
100
|
+
- `loki welcome` works headless (terminal fallback, no browser error).
|
|
101
|
+
- First-run marker opens once and not again.
|
|
102
|
+
|
|
103
|
+
## Honesty
|
|
104
|
+
Highlights cite real, documented research (already in README Research
|
|
105
|
+
Foundation) and frame the memory moat as "retrieval/compounding," NOT a
|
|
106
|
+
claimed task-success number (consistent with prior honesty fixes).
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var v=(K,$)=>{for(var Q in $)_7(K,Q,{get:$[Q],enumerable:!0,configurable:!0,set:P7.bind($,Q)})};var R=(K,$)=>()=>(K&&($=K(K=0)),$);var t=import.meta.require;var e1={};v(e1,{lokiDir:()=>P,homeLokiDir:()=>k1,findRepoRootForVersion:()=>N1,REPO_ROOT:()=>p});import{resolve as u,dirname as S1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(u(K,"VERSION"))&&J1(u(K,"autonomy/run.sh")))return K;let Q=S1(K);if(Q===K)break;K=Q}return u(i1,"..","..","..")}function N1(K){let $=K;for(let Q=0;Q<6;Q++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let X=S1($);if(X===$)break;$=X}return u(K,"..","..","..")}function P(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var g=R(()=>{i1=S1(L7(import.meta.url));p=E7()});import{readFileSync as F7}from"fs";import{resolve as w7,dirname as x7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(o!==null)return o;let K="7.7.
|
|
2
|
+
var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var v=(K,$)=>{for(var Q in $)_7(K,Q,{get:$[Q],enumerable:!0,configurable:!0,set:P7.bind($,Q)})};var R=(K,$)=>()=>(K&&($=K(K=0)),$);var t=import.meta.require;var e1={};v(e1,{lokiDir:()=>P,homeLokiDir:()=>k1,findRepoRootForVersion:()=>N1,REPO_ROOT:()=>p});import{resolve as u,dirname as S1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(u(K,"VERSION"))&&J1(u(K,"autonomy/run.sh")))return K;let Q=S1(K);if(Q===K)break;K=Q}return u(i1,"..","..","..")}function N1(K){let $=K;for(let Q=0;Q<6;Q++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let X=S1($);if(X===$)break;$=X}return u(K,"..","..","..")}function P(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var g=R(()=>{i1=S1(L7(import.meta.url));p=E7()});import{readFileSync as F7}from"fs";import{resolve as w7,dirname as x7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(o!==null)return o;let K="7.7.28";if(typeof K==="string"&&K.length>0)return o=K,o;try{let $=x7(S7(import.meta.url)),Q=N1($);o=F7(w7(Q,"VERSION"),"utf-8").trim()}catch{o="unknown"}return o}var o=null;var D1=R(()=>{g()});var $0={};v($0,{runOrThrow:()=>N7,run:()=>k,commandVersion:()=>D7,commandExists:()=>h,ShellError:()=>C1});async function k(K,$={}){let Q=Bun.spawn({cmd:[...K],stdout:"pipe",stderr:"pipe",env:$.env?{...process.env,...$.env}:process.env,cwd:$.cwd}),X,Z;if($.timeoutMs&&$.timeoutMs>0)X=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},$.timeoutMs);try{let[W,z,q]=await Promise.all([new Response(Q.stdout).text(),new Response(Q.stderr).text(),Q.exited]);return{stdout:W,stderr:z,exitCode:q}}finally{if(X)clearTimeout(X);if(Z)clearTimeout(Z)}}async function N7(K,$={}){let Q=await k(K,$);if(Q.exitCode!==0)throw new C1(`command failed (${Q.exitCode}): ${K.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function h(K){let $=k7(K),Q=await k(["sh","-c",`command -v ${$}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function k7(K){if(!/^[A-Za-z0-9._/-]+$/.test(K))throw Error(`refused to shell-escape suspect token: ${K}`);return K}async function D7(K,$="--version"){if(!await h(K))return null;let X=await k([K,$],{timeoutMs:5000});if(X.exitCode!==0)return null;return((X.stdout||X.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var C1;var n=R(()=>{C1=class C1 extends Error{message;exitCode;stdout;stderr;constructor(K,$,Q,X){super(K);this.message=K;this.exitCode=$;this.stdout=Q;this.stderr=X;this.name="ShellError"}}});function c(K){return C7?"":K}var C7,E,b,F,T6,O,D,w,H;var a=R(()=>{C7=(process.env.NO_COLOR??"").length>0;E=c("\x1B[0;31m"),b=c("\x1B[0;32m"),F=c("\x1B[1;33m"),T6=c("\x1B[0;34m"),O=c("\x1B[0;36m"),D=c("\x1B[1m"),w=c("\x1B[2m"),H=c("\x1B[0m")});import{existsSync as c7}from"fs";async function i(){if(X1!==void 0)return X1;let K="/opt/homebrew/bin/python3.12";if(c7(K))return X1=K,K;let $=await h("python3.12");if($)return X1=$,$;let Q=await h("python3");return X1=Q,Q}async function s(K,$={}){let Q=await i();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return k([Q,"-c",K],$)}var X1;var Z1=R(()=>{n()});var G0={};v(G0,{runStatus:()=>Q5});import{existsSync as N,readFileSync as W1,readdirSync as W0,statSync as H0}from"fs";import{resolve as x,basename as a7}from"path";async function r7(){if(await h("jq"))return!0;return process.stdout.write(`${E}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)
|
|
@@ -585,4 +585,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
585
585
|
`),2}default:return process.stderr.write(`Unknown command: ${$}
|
|
586
586
|
`),process.stderr.write(j7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var X6=await Q6(Bun.argv.slice(2));process.exit(X6);
|
|
587
587
|
|
|
588
|
-
//# debugId=
|
|
588
|
+
//# debugId=FB1EB87ADC30E73E64756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/memory/retrieval.py
CHANGED
|
@@ -1558,6 +1558,35 @@ class MemoryRetrieval:
|
|
|
1558
1558
|
if self._belongs_to_namespace(anti_copy):
|
|
1559
1559
|
results.append(anti_copy)
|
|
1560
1560
|
|
|
1561
|
+
# Consolidation writes anti-patterns as SemanticPattern objects with
|
|
1562
|
+
# category="anti-pattern" into semantic/patterns.json (not the legacy
|
|
1563
|
+
# anti-patterns.json above), with fields incorrect_approach /
|
|
1564
|
+
# description / correct_approach. Without this bridge, consolidated
|
|
1565
|
+
# anti-patterns were never retrievable. Map them onto the same
|
|
1566
|
+
# what_fails / why / prevention scoring shape.
|
|
1567
|
+
patterns_data = self.storage.read_json("semantic/patterns.json") or {}
|
|
1568
|
+
for pat in patterns_data.get("patterns", []):
|
|
1569
|
+
if pat.get("category") != "anti-pattern":
|
|
1570
|
+
continue
|
|
1571
|
+
what_fails = (pat.get("incorrect_approach", "")
|
|
1572
|
+
or pat.get("pattern", "")).lower()
|
|
1573
|
+
why = pat.get("description", "").lower()
|
|
1574
|
+
prevention = pat.get("correct_approach", "").lower()
|
|
1575
|
+
|
|
1576
|
+
score = sum(2 for kw in keywords if kw in what_fails)
|
|
1577
|
+
score += sum(1 for kw in keywords if kw in why)
|
|
1578
|
+
score += sum(1 for kw in keywords if kw in prevention)
|
|
1579
|
+
|
|
1580
|
+
if score > 0:
|
|
1581
|
+
anti_copy = dict(pat)
|
|
1582
|
+
anti_copy["_score"] = score
|
|
1583
|
+
anti_copy["_source"] = "anti_patterns"
|
|
1584
|
+
anti_copy.setdefault("what_fails", pat.get("incorrect_approach", "") or pat.get("pattern", ""))
|
|
1585
|
+
anti_copy.setdefault("why", pat.get("description", ""))
|
|
1586
|
+
anti_copy.setdefault("prevention", pat.get("correct_approach", ""))
|
|
1587
|
+
if self._belongs_to_namespace(anti_copy):
|
|
1588
|
+
results.append(anti_copy)
|
|
1589
|
+
|
|
1561
1590
|
return results
|
|
1562
1591
|
|
|
1563
1592
|
def _build_episodic_index(self) -> None:
|
|
@@ -1654,3 +1683,23 @@ class MemoryRetrieval:
|
|
|
1654
1683
|
# Add to index with ID
|
|
1655
1684
|
item_id = anti.get("id", anti.get("source", f"anti-{hash(text) % 10000}"))
|
|
1656
1685
|
index.add(item_id, embedding, anti)
|
|
1686
|
+
|
|
1687
|
+
# Parity with the keyword path: consolidation writes anti-patterns as
|
|
1688
|
+
# category="anti-pattern" entries in semantic/patterns.json, not the
|
|
1689
|
+
# legacy anti-patterns.json above. Bridge those into the vector index
|
|
1690
|
+
# too so embedding-based retrieval sees consolidated anti-patterns.
|
|
1691
|
+
patterns_data = self.storage.read_json("semantic/patterns.json") or {}
|
|
1692
|
+
for pat in patterns_data.get("patterns", []):
|
|
1693
|
+
if pat.get("category") != "anti-pattern":
|
|
1694
|
+
continue
|
|
1695
|
+
what_fails = pat.get("incorrect_approach", "") or pat.get("pattern", "")
|
|
1696
|
+
why = pat.get("description", "")
|
|
1697
|
+
prevention = pat.get("correct_approach", "")
|
|
1698
|
+
text = f"{what_fails} {why} {prevention}"
|
|
1699
|
+
embedding = self.embedding_engine.embed(text)
|
|
1700
|
+
item_id = pat.get("id", f"anti-{hash(text) % 10000}")
|
|
1701
|
+
bridged = dict(pat)
|
|
1702
|
+
bridged.setdefault("what_fails", what_fails)
|
|
1703
|
+
bridged.setdefault("why", why)
|
|
1704
|
+
bridged.setdefault("prevention", prevention)
|
|
1705
|
+
index.add(item_id, embedding, bridged)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
|
-
"version": "7.7.
|
|
3
|
+
"version": "7.7.28",
|
|
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",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"files": [
|
|
65
65
|
"SKILL.md",
|
|
66
66
|
"VERSION",
|
|
67
|
+
"assets/",
|
|
67
68
|
"tools/",
|
|
68
69
|
"autonomy/",
|
|
69
70
|
"providers/",
|