loki-mode 11.8.1 → 11.10.1
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/autonomy/lib/backlog.py +15 -3
- package/autonomy/lib/workspace.py +95 -4
- package/autonomy/loki +189 -108
- package/autonomy/trigger-server.py +302 -4
- package/bin/loki +1 -1
- package/completions/_loki +34 -1
- package/completions/loki.bash +10 -3
- package/dashboard/__init__.py +1 -1
- package/docs/ARCHITECTURE.md +0 -1
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/COMPONENTS.md +1 -1
- package/docs/DEPLOY-PLAN.md +13 -3
- package/docs/INSTALLATION.md +1 -1
- package/docs/WORKSPACES.md +1 -1
- package/docs/dev/architecture-reference.md +1 -1
- package/docs/sentry-intake.md +15 -1
- package/docs/v10/D61-SPEED.md +2 -0
- package/docs/v10/PROGRESS.md +25 -1
- package/loki-ts/dist/loki.js +1016 -946
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/ask-tools-server.js +13 -4
- package/packages/control-plane/dist/server.js +457 -279
- package/packages/control-plane/src/server/diffstat.ts +10 -2
- package/packages/control-plane/src/server/legacy/routes-control.ts +8 -4
- package/packages/control-plane/src/server/routes/checkpoints.ts +11 -2
- package/packages/control-plane/ui/dist/assets/index-u8cd6TRQ.js +190 -0
- package/packages/control-plane/ui/dist/index.html +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-BnIg9ilA.js → AdminPage-D3PgG7SH.js} +1 -1
- package/web-app/dist/assets/{Avatar-0BTVsQ2A.js → Avatar-Ct77hvlG.js} +1 -1
- package/web-app/dist/assets/{Badge-Bbq8CXgn.js → Badge-Cg_gZaJF.js} +1 -1
- package/web-app/dist/assets/{Button-CKD4n0ld.js → Button-i9r2G84n.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-DJhLNLAz.js → CockpitPage-C3UyLXyp.js} +1 -1
- package/web-app/dist/assets/{ComparePage-Di2akuII.js → ComparePage-B1dXHga_.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-9lhsYG-z.js → ErrorBoundary-CQ_xREw0.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CQDGXvDr.js → EvidenceReceiptPanel-DwUxd_Xi.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-Y_w2k9Ej.js → GitHubIssuesPanel-C0jAnuAd.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-pMSQedCv.js → GitHubPRsPanel-B4pNXJMz.js} +1 -1
- package/web-app/dist/assets/{HomePage-CIAkiGdt.js → HomePage-BC9UTS1l.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BES4Pk6k.js → LoginPage-CnpdwK8F.js} +1 -1
- package/web-app/dist/assets/{MagicPage-B154fMhi.js → MagicPage-Bj0lZuNp.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-BcNyaiUO.js → MetricsPage-yyw3VnfX.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-D28FqYxG.js → NotFoundPage-LMxkJP15.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-C_O3u8Jx.js → ProjectPage-2x0MRkNu.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-uqdahuPA.js → ProjectsPage-A8J8o1gj.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DLvVA9OZ.js → SettingsPage-fwvOVcRb.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-BAZP8vsm.js → ShowcasePage-DqI4sUwk.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-DPYZkLWh.js → SystemSettingsPage-CyytfNo_.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-BgSyb5Pd.js → TeamsPage-BTTe76W1.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-D_Jh5VU1.js → TemplatesPage-CIp3OTlL.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-4v8poDKb.js → TerminalOutput-dX5L4ky-.js} +1 -1
- package/web-app/dist/assets/{activity-CE2b6kdw.js → activity-uHXr2AaF.js} +1 -1
- package/web-app/dist/assets/{bell-CxJDlLSR.js → bell-D6RbxRMo.js} +1 -1
- package/web-app/dist/assets/{bot-C2WuK4zw.js → bot-D31w2Mu6.js} +1 -1
- package/web-app/dist/assets/{check-81epVldw.js → check-CoKJksbr.js} +1 -1
- package/web-app/dist/assets/{chevron-left-B6EXh_3C.js → chevron-left-Bjx7oX7s.js} +1 -1
- package/web-app/dist/assets/{circle-alert-0iqoo-iy.js → circle-alert-DOAh8Jmi.js} +1 -1
- package/web-app/dist/assets/{clock-CFrBpNUp.js → clock-DgEXPNWH.js} +1 -1
- package/web-app/dist/assets/{cloud-BBSFhUT_.js → cloud-0dXJFzBv.js} +1 -1
- package/web-app/dist/assets/{code-xml-BCU2V1rq.js → code-xml-DytQ_bq7.js} +1 -1
- package/web-app/dist/assets/{database-D2hvO52K.js → database-Qyjryb72.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CXFA9b6T.js → dollar-sign-BzhTVy1e.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CqTCEEfS.js → file-code-corner-DTdSOALw.js} +1 -1
- package/web-app/dist/assets/{file-plus-DYFbkGbs.js → file-plus-VdjOG1RK.js} +1 -1
- package/web-app/dist/assets/{globe-DpYHLobk.js → globe-BhxNOfyM.js} +1 -1
- package/web-app/dist/assets/{hammer-CZ97SEYo.js → hammer-MQzrw8ee.js} +1 -1
- package/web-app/dist/assets/{index-DzswgpRo.js → index-DaxrMbHf.js} +3 -3
- package/web-app/dist/assets/{layers-2Tq0edx4.js → layers-C7nusTP-.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BFP4kUX-.js → loader-circle-B1qBkgOY.js} +1 -1
- package/web-app/dist/assets/{lock-DkKdZH4d.js → lock-BdkOxK2O.js} +1 -1
- package/web-app/dist/assets/{package-ChOGD8yf.js → package-RVyK96Zn.js} +1 -1
- package/web-app/dist/assets/{plus-hggB1o6b.js → plus-ntmwFRrp.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-DYwVLOeF.js → refresh-cw-BJ39b_PP.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CUdY506q.js → rotate-ccw-uPx_-3vz.js} +1 -1
- package/web-app/dist/assets/{scroll-text-Wvidz5uN.js → scroll-text-D9qvXkj-.js} +1 -1
- package/web-app/dist/assets/{server-O11QmkCN.js → server-gs4xmD8Y.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DXXx27uV.js → shield-alert-O2_YffGI.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DF8ivs3A.js → thumbs-up-B3RVBhQx.js} +1 -1
- package/web-app/dist/assets/{trash-2-LA6PW0Nf.js → trash-2-DEUt7qhq.js} +1 -1
- package/web-app/dist/assets/{trending-down-Bw_vLKHT.js → trending-down-oyTns4UZ.js} +1 -1
- package/web-app/dist/assets/{trending-up-tX8motha.js → trending-up-CdpZK0Oc.js} +1 -1
- package/web-app/dist/assets/{upload-CxtTWJd2.js → upload-D9sbjjC8.js} +1 -1
- package/web-app/dist/assets/{usePolling-D-P5Iofq.js → usePolling-6ognrnyV.js} +1 -1
- package/web-app/dist/assets/{user-dKTcuUFq.js → user-CBKc8QoI.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/memory/rag_injector.py +0 -161
- package/packages/control-plane/ui/dist/assets/index-DbLzr87u.js +0 -190
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: An autonomous software factory that knows what it is supposed to deliver, and proves it did. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v11.
|
|
6
|
+
# Loki Mode v11.10.1
|
|
7
7
|
|
|
8
8
|
**Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
|
|
9
9
|
|
|
@@ -483,4 +483,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
483
483
|
|
|
484
484
|
---
|
|
485
485
|
|
|
486
|
-
**v11.
|
|
486
|
+
**v11.10.1 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
11.
|
|
1
|
+
11.10.1
|
package/autonomy/lib/backlog.py
CHANGED
|
@@ -196,6 +196,7 @@ def load_dag(path, group):
|
|
|
196
196
|
raise ValueError("unit %s task text starts with '-' and would be read as an option" % uid)
|
|
197
197
|
units.append({"n": n, "id": uid, "deps": deps[uid], "write_set": list(u.get("writeSet") or []),
|
|
198
198
|
"task": task, "done": u.get("done") is True,
|
|
199
|
+
"base": str(u.get("base") or ""),
|
|
199
200
|
"spec": u["spec"] if isinstance(u.get("spec"), str) and os.path.isabs(u["spec"]) else ""})
|
|
200
201
|
return group, units
|
|
201
202
|
|
|
@@ -265,6 +266,17 @@ def main(argv):
|
|
|
265
266
|
return die("this checkout's origin is %r, not %s; cd into a checkout of %s"
|
|
266
267
|
% (origin_slug(top) or "unknown", a.repo, a.repo))
|
|
267
268
|
|
|
269
|
+
for u in dag_units:
|
|
270
|
+
b = u["base"]
|
|
271
|
+
if not b or re.match(r"^[0-9a-f]{7,64}$", b):
|
|
272
|
+
continue
|
|
273
|
+
# A ref-name base is resolved to a SHA; an unresolvable one fails loudly, never falls back to origin/HEAD.
|
|
274
|
+
r = git("rev-parse", "--verify", "-q", "--end-of-options", b + "^{commit}", cwd=top) if not b.startswith("-") else None
|
|
275
|
+
sha = r.stdout.strip() if r is not None and r.returncode == 0 else ""
|
|
276
|
+
if not re.match(r"^[0-9a-f]{40,64}$", sha):
|
|
277
|
+
return die("unit %s base %r is not a commit SHA or a resolvable ref" % (u["id"], b))
|
|
278
|
+
u["base"] = sha
|
|
279
|
+
|
|
268
280
|
try:
|
|
269
281
|
issues = ([{"number": n, "title": ""} for n in numbers] if numbers
|
|
270
282
|
else list_issues(a.repo, a.label, env) if (a.all or a.label) else [])
|
|
@@ -315,7 +327,7 @@ def main(argv):
|
|
|
315
327
|
"deps": [key_of[d] for d in u["deps"]],
|
|
316
328
|
"env": {"LOKI_GROUP_ID": group, "LOKI_UNIT_ID": u["id"], "LOKI_UNIT_N": str(u["n"]),
|
|
317
329
|
"LOKI_UNIT_DEPS": ",".join(u["deps"]), "LOKI_UNIT_WRITE_SET": "\n".join(u["write_set"])},
|
|
318
|
-
"done": u["done"]}
|
|
330
|
+
"done": u["done"], "base": u["base"]}
|
|
319
331
|
if u["spec"]: # frozen group acceptance for unit_mode (wall.ts)
|
|
320
332
|
spec[k]["env"].update(LOKI_UNIT_SPEC=u["spec"], LOKI_SPEED="1")
|
|
321
333
|
state = {k: {"status": "queued", "ok": None, "cost": None, "ran": False} for k in spec}
|
|
@@ -393,13 +405,13 @@ def main(argv):
|
|
|
393
405
|
# Shared worktree prep (D51-B05): per-base lock, clean start point, deps copied.
|
|
394
406
|
git("branch", "-D", s["branch"], cwd=top)
|
|
395
407
|
try:
|
|
396
|
-
worktree_prep.prepare_worktree(top, wt, s["branch"])
|
|
408
|
+
worktree_prep.prepare_worktree(top, wt, s["branch"], base=s.get("base") or None)
|
|
397
409
|
r = None
|
|
398
410
|
except (RuntimeError, subprocess.CalledProcessError) as e:
|
|
399
411
|
msg = getattr(e, "stderr", None) or str(e)
|
|
400
412
|
r = subprocess.CompletedProcess([], 1, "", msg)
|
|
401
413
|
else:
|
|
402
|
-
r = git("worktree", "add", "-B", s["branch"], wt, "HEAD", cwd=top)
|
|
414
|
+
r = git("worktree", "add", "-B", s["branch"], wt, s.get("base") or "HEAD", cwd=top)
|
|
403
415
|
if r is not None and r.returncode != 0:
|
|
404
416
|
err = (r.stderr.strip().splitlines() or ["git error"])[-1]
|
|
405
417
|
state[k].update(status="FAILED: worktree: " + redact(err, token), ok=False)
|
|
@@ -585,6 +585,85 @@ def _dep_tokens(root):
|
|
|
585
585
|
return toks
|
|
586
586
|
|
|
587
587
|
|
|
588
|
+
_SRC_EXT = (".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go")
|
|
589
|
+
_SKIP_DIRS = {"node_modules", ".git", "dist", "build", "vendor", "venv", ".venv", "__pycache__", ".loki"}
|
|
590
|
+
_SCAN_MAX_FILES = 400
|
|
591
|
+
_SCAN_MAX_BYTES = 262144
|
|
592
|
+
_JS_SPEC = re.compile(r"""(?:from\s*|require\s*\(\s*|import\s*\(?\s*)["']([^"']+)["']""")
|
|
593
|
+
_PY_IMPORT = re.compile(r"^\s*(?:from\s+([A-Za-z_][\w.]*)\s+import\b|import\s+([A-Za-z_][\w.]*))")
|
|
594
|
+
_GO_IMPORT = re.compile(r'^\s*(?:import\s+)?(?:[\w.]+\s+)?"([^"]+)"\s*$')
|
|
595
|
+
_SPEC_KEY = re.compile(r"""["']?(?:inputSpec|input|target)["']?\s*[:=]\s*["']?([^"',\s}]+)""")
|
|
596
|
+
_GEN_CONFIG = re.compile(
|
|
597
|
+
r"^(openapitools\.json|openapi-generator.*\.(?:ya?ml|json)|orval\.config\.\w+|\.orvalrc(?:\.\w+)?)$")
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def _spec_hits(path, ext):
|
|
601
|
+
"""Yield (line number, imported specifier) for one source file."""
|
|
602
|
+
text = _read(path)
|
|
603
|
+
if text is None or len(text) > _SCAN_MAX_BYTES:
|
|
604
|
+
return
|
|
605
|
+
for n, line in enumerate(text.splitlines(), 1):
|
|
606
|
+
if ext == ".py":
|
|
607
|
+
m = _PY_IMPORT.match(line)
|
|
608
|
+
if m:
|
|
609
|
+
yield n, (m.group(1) or m.group(2)).lower().replace("_", "-")
|
|
610
|
+
elif ext == ".go":
|
|
611
|
+
m = _GO_IMPORT.match(line)
|
|
612
|
+
if m:
|
|
613
|
+
yield n, m.group(1).lower()
|
|
614
|
+
else:
|
|
615
|
+
for m in _JS_SPEC.finditer(line):
|
|
616
|
+
yield n, m.group(1).lower()
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def _import_evidence(root, ids):
|
|
620
|
+
"""First file:line per sibling whose package/module name a source file imports (bounded scan)."""
|
|
621
|
+
found, seen = {}, 0
|
|
622
|
+
for dirpath, dirs, files in os.walk(root):
|
|
623
|
+
dirs[:] = sorted(d for d in dirs if d not in _SKIP_DIRS)
|
|
624
|
+
for fn in sorted(files):
|
|
625
|
+
ext = os.path.splitext(fn)[1]
|
|
626
|
+
if ext not in _SRC_EXT:
|
|
627
|
+
continue
|
|
628
|
+
seen += 1
|
|
629
|
+
if seen > _SCAN_MAX_FILES:
|
|
630
|
+
return found
|
|
631
|
+
path = os.path.join(dirpath, fn)
|
|
632
|
+
for n, spec in _spec_hits(path, ext):
|
|
633
|
+
if spec.startswith("."):
|
|
634
|
+
continue
|
|
635
|
+
for o, oids in ids.items():
|
|
636
|
+
if o not in found and any(spec == i or spec.startswith(i + "/") or spec.startswith(i + ".")
|
|
637
|
+
for i in oids):
|
|
638
|
+
found[o] = "%s:%d (imports %s)" % (os.path.relpath(path, root), n, spec)
|
|
639
|
+
return found
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def _openapi_evidence(root, roots):
|
|
643
|
+
"""file:line per sibling whose checkout holds the spec a client generator config points at."""
|
|
644
|
+
found = {}
|
|
645
|
+
try:
|
|
646
|
+
names = sorted(os.listdir(root))
|
|
647
|
+
except OSError:
|
|
648
|
+
return found
|
|
649
|
+
for fn in names:
|
|
650
|
+
path = os.path.join(root, fn)
|
|
651
|
+
if not _GEN_CONFIG.match(fn) or not os.path.isfile(path):
|
|
652
|
+
continue
|
|
653
|
+
text = _read(path) or ""
|
|
654
|
+
for n, line in enumerate(text.splitlines(), 1):
|
|
655
|
+
for m in _SPEC_KEY.finditer(line):
|
|
656
|
+
val = m.group(1)
|
|
657
|
+
if "://" in val:
|
|
658
|
+
continue
|
|
659
|
+
target = os.path.realpath(os.path.join(root, val))
|
|
660
|
+
for o, oroot in roots.items():
|
|
661
|
+
if oroot and o not in found and os.path.realpath(oroot) != os.path.realpath(root) and \
|
|
662
|
+
(target + os.sep).startswith(os.path.realpath(oroot) + os.sep):
|
|
663
|
+
found[o] = "%s:%d (spec %s)" % (fn, n, val)
|
|
664
|
+
return found
|
|
665
|
+
|
|
666
|
+
|
|
588
667
|
def infer(name, workspaces, say=say):
|
|
589
668
|
"""Print suggested `after:` edges from manifests; never writes loki.yaml."""
|
|
590
669
|
ws = workspaces.get(name)
|
|
@@ -598,7 +677,7 @@ def infer(name, workspaces, say=say):
|
|
|
598
677
|
os.path.expanduser("~"), ".loki", "repos", _slug(e["repo"]))
|
|
599
678
|
roots[e["repo"]] = root if os.path.isdir(root) else None
|
|
600
679
|
ids = {e["repo"]: _identities(e, roots[e["repo"]]) if roots[e["repo"]] else set() for e in entries}
|
|
601
|
-
proposed = {}
|
|
680
|
+
proposed, evidence = {}, {}
|
|
602
681
|
for e in entries:
|
|
603
682
|
repo = e["repo"]
|
|
604
683
|
if not roots[repo]:
|
|
@@ -607,12 +686,21 @@ def infer(name, workspaces, say=say):
|
|
|
607
686
|
continue
|
|
608
687
|
toks = _dep_tokens(roots[repo])
|
|
609
688
|
deps = list(e.get("after") or [])
|
|
610
|
-
for
|
|
611
|
-
|
|
612
|
-
|
|
689
|
+
others = {x["repo"]: ids[x["repo"]] for x in entries if x["repo"] != repo}
|
|
690
|
+
imp = _import_evidence(roots[repo], others)
|
|
691
|
+
api = _openapi_evidence(roots[repo], {o: roots[o] for o in others})
|
|
692
|
+
for o in others:
|
|
693
|
+
if o in deps:
|
|
613
694
|
continue
|
|
614
695
|
if any(t == i or t.startswith(i + "/") for t in toks for i in ids[o]):
|
|
615
696
|
deps.append(o)
|
|
697
|
+
evidence[(repo, o)] = "manifest dependency"
|
|
698
|
+
elif o in api:
|
|
699
|
+
deps.append(o)
|
|
700
|
+
evidence[(repo, o)] = api[o]
|
|
701
|
+
elif o in imp:
|
|
702
|
+
deps.append(o)
|
|
703
|
+
evidence[(repo, o)] = imp[o]
|
|
616
704
|
proposed[repo] = deps
|
|
617
705
|
try:
|
|
618
706
|
order_repos({"repos": [{"repo": e["repo"], "after": proposed[e["repo"]]} for e in entries]})
|
|
@@ -623,6 +711,9 @@ def infer(name, workspaces, say=say):
|
|
|
623
711
|
for e in entries:
|
|
624
712
|
say("%s" % e["repo"])
|
|
625
713
|
say(" after: [%s]" % ", ".join(proposed[e["repo"]]))
|
|
714
|
+
for d in proposed[e["repo"]]:
|
|
715
|
+
if (e["repo"], d) in evidence:
|
|
716
|
+
say(" %s: %s" % (d, evidence[(e["repo"], d)]))
|
|
626
717
|
return 0
|
|
627
718
|
|
|
628
719
|
|