loki-mode 6.74.6 → 6.75.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.
Files changed (70) hide show
  1. package/README.md +1 -53
  2. package/SKILL.md +2 -2
  3. package/VERSION +1 -1
  4. package/autonomy/loki +989 -0
  5. package/autonomy/run.sh +106 -2
  6. package/dashboard/__init__.py +1 -1
  7. package/docs/INSTALLATION.md +1 -1
  8. package/mcp/__init__.py +1 -1
  9. package/mcp/server.py +177 -0
  10. package/package.json +1 -1
  11. package/references/mcp-integration.md +59 -0
  12. package/skills/00-index.md +9 -0
  13. package/skills/documentation.md +123 -0
  14. package/skills/quality-gates.md +25 -1
  15. package/web-app/dist/assets/{AdminPage-Cwqm_kDg.js → AdminPage-D4QSV6Zi.js} +1 -1
  16. package/web-app/dist/assets/{Avatar-BgcFY2E5.js → Avatar-88MlpLO5.js} +1 -1
  17. package/web-app/dist/assets/{Badge-DeFGfZLB.js → Badge-DbGjLr4i.js} +1 -1
  18. package/web-app/dist/assets/{Button-Dg1EkPtN.js → Button-sp_FVGZj.js} +1 -1
  19. package/web-app/dist/assets/{ComparePage-D-wvMVP2.js → ComparePage-p2ENnfa7.js} +1 -1
  20. package/web-app/dist/assets/{GitHubIssuesPanel-B_Jm7CmJ.js → GitHubIssuesPanel-DBbBTG9w.js} +1 -1
  21. package/web-app/dist/assets/{GitHubPRsPanel-B5i8Q99N.js → GitHubPRsPanel-Bi_yrcAE.js} +1 -1
  22. package/web-app/dist/assets/{HomePage-CUDTdntY.js → HomePage-BB83YPiX.js} +1 -1
  23. package/web-app/dist/assets/{LoginPage-BobwVXx5.js → LoginPage-BXUudCJ9.js} +1 -1
  24. package/web-app/dist/assets/{MetricsPage-DmM--20B.js → MetricsPage-CX0Ahy-_.js} +1 -1
  25. package/web-app/dist/assets/{NotFoundPage-6_gjeogD.js → NotFoundPage-C4JqatEk.js} +1 -1
  26. package/web-app/dist/assets/{ProjectPage-C3R2wbFt.js → ProjectPage-t5J2XAJT.js} +46 -46
  27. package/web-app/dist/assets/{ProjectsPage-DQr06iBk.js → ProjectsPage-Bzpz1clk.js} +1 -1
  28. package/web-app/dist/assets/{SettingsPage-eROlGKB9.js → SettingsPage-y_yl8FvH.js} +1 -1
  29. package/web-app/dist/assets/{ShowcasePage-DEA5tT_R.js → ShowcasePage-B7d6pzMq.js} +1 -1
  30. package/web-app/dist/assets/{SystemSettingsPage-C_rIbgZg.js → SystemSettingsPage-C4tR33KU.js} +1 -1
  31. package/web-app/dist/assets/{TeamsPage-DZGoYZnD.js → TeamsPage-DIOCfZIP.js} +1 -1
  32. package/web-app/dist/assets/{TemplatesPage-DVblWpbO.js → TemplatesPage-DlKyapXX.js} +1 -1
  33. package/web-app/dist/assets/{TerminalOutput-DnESY9zk.js → TerminalOutput-Czg-ZC2k.js} +1 -1
  34. package/web-app/dist/assets/{activity-COLsZyo1.js → activity-h1wU9a0L.js} +1 -1
  35. package/web-app/dist/assets/{bell-BjLe9xXk.js → bell-Bu8lsWOp.js} +1 -1
  36. package/web-app/dist/assets/{bot-Dz62aBIi.js → bot-rWO7KjkQ.js} +1 -1
  37. package/web-app/dist/assets/{check-BQPQjkH4.js → check-BWp8L5Cy.js} +1 -1
  38. package/web-app/dist/assets/{chevron-left-BVvOVUQ8.js → chevron-left-Bw4I1yGm.js} +1 -1
  39. package/web-app/dist/assets/{circle-alert-DqoLW238.js → circle-alert-C37PKXiC.js} +1 -1
  40. package/web-app/dist/assets/{clock-Cn9fFUva.js → clock-DDScLol4.js} +1 -1
  41. package/web-app/dist/assets/{cloud-COJxbgUu.js → cloud-DaYKPLaM.js} +1 -1
  42. package/web-app/dist/assets/{copy-DOn0hVgy.js → copy-DKIRv0VK.js} +1 -1
  43. package/web-app/dist/assets/{database-Bj3Llvnk.js → database-CYZBHz51.js} +1 -1
  44. package/web-app/dist/assets/{dollar-sign-BcmncygQ.js → dollar-sign-CydJu0kl.js} +1 -1
  45. package/web-app/dist/assets/{file-code-corner-BysxoSyu.js → file-code-corner-DqZ9gpdv.js} +1 -1
  46. package/web-app/dist/assets/{file-plus-Dwi1MqSS.js → file-plus-CzeFJWp3.js} +1 -1
  47. package/web-app/dist/assets/{folder-open-WzXNCq2x.js → folder-open-4YWk08dP.js} +1 -1
  48. package/web-app/dist/assets/{git-commit-horizontal-D85UUfNF.js → git-commit-horizontal-wbqFPNID.js} +1 -1
  49. package/web-app/dist/assets/{globe-CegT0VaL.js → globe-Cby-g5Yb.js} +1 -1
  50. package/web-app/dist/assets/{hammer-ZGKvu_xy.js → hammer-BNScgGdp.js} +1 -1
  51. package/web-app/dist/assets/{index-_2iPl2nX.js → index-6Z4B0I6r.js} +74 -74
  52. package/web-app/dist/assets/{layers-B40lki5j.js → layers-XfssQc5V.js} +1 -1
  53. package/web-app/dist/assets/{lightbulb-CFSoqUsV.js → lightbulb-EhnzRw7M.js} +1 -1
  54. package/web-app/dist/assets/{loader-circle-womi7Brk.js → loader-circle-BA0QIVGA.js} +1 -1
  55. package/web-app/dist/assets/{lock-CEWBb_SL.js → lock-BABtHe6K.js} +1 -1
  56. package/web-app/dist/assets/{mail-DimGrYf8.js → mail-Dokiey5S.js} +1 -1
  57. package/web-app/dist/assets/{package-DysIuuIJ.js → package-DbJyS1Ft.js} +1 -1
  58. package/web-app/dist/assets/{plus-DG1hW27_.js → plus-BcAN8Kaj.js} +1 -1
  59. package/web-app/dist/assets/{refresh-cw-X31ig0S6.js → refresh-cw-B3dG1-Sb.js} +1 -1
  60. package/web-app/dist/assets/{rotate-ccw-CBXooICo.js → rotate-ccw-Cs1Phctm.js} +1 -1
  61. package/web-app/dist/assets/{save-DQVrWTjZ.js → save-DsrNCZrP.js} +1 -1
  62. package/web-app/dist/assets/{server-BYAMALfa.js → server-CpN2GX4G.js} +1 -1
  63. package/web-app/dist/assets/{shield-alert-B3G7vLiW.js → shield-alert-CKJ1pzCz.js} +1 -1
  64. package/web-app/dist/assets/{trash-2-ChVunC-C.js → trash-2-C9vZqTqw.js} +1 -1
  65. package/web-app/dist/assets/{trending-down-DgsuOE2t.js → trending-down-BNLTrF5P.js} +1 -1
  66. package/web-app/dist/assets/{trending-up-CZHZsGeN.js → trending-up-DmFIdVOc.js} +1 -1
  67. package/web-app/dist/assets/{usePolling-ns_dFCVn.js → usePolling-vUlY-o6P.js} +1 -1
  68. package/web-app/dist/assets/{user-FQUrWHhF.js → user-Dh00W8De.js} +1 -1
  69. package/web-app/dist/index.html +1 -1
  70. package/web-app/server.py +196 -0
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-_2iPl2nX.js";/**
1
+ import{c as a}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c}from"./index-_2iPl2nX.js";/**
1
+ import{c}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c}from"./index-_2iPl2nX.js";/**
1
+ import{c}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c}from"./index-_2iPl2nX.js";/**
1
+ import{c}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-_2iPl2nX.js";/**
1
+ import{c as e}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-_2iPl2nX.js";/**
1
+ import{c as a}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-_2iPl2nX.js";/**
1
+ import{c as e}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-_2iPl2nX.js";/**
1
+ import{c as e}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-_2iPl2nX.js";/**
1
+ import{c as e}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-_2iPl2nX.js";/**
1
+ import{c as a}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-_2iPl2nX.js";/**
1
+ import{c as e}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-_2iPl2nX.js";/**
1
+ import{c as e}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-_2iPl2nX.js";/**
1
+ import{c as a}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as n}from"./index-_2iPl2nX.js";/**
1
+ import{c as n}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as e}from"./index-_2iPl2nX.js";/**
1
+ import{c as e}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as e}from"./index-_2iPl2nX.js";function m(s,u=2e3,a=!0){const[c,l]=e.useState(null),[f,o]=e.useState(null),[i,d]=e.useState(!0),r=e.useRef(!0),n=e.useCallback(async()=>{try{const t=await s();r.current&&(l(t),o(null))}catch(t){r.current&&o(t instanceof Error?t.message:"Unknown error")}finally{r.current&&d(!1)}},[s]);return e.useEffect(()=>{if(r.current=!0,!a)return;n();const t=setInterval(n,u);return()=>{r.current=!1,clearInterval(t)}},[n,u,a]),{data:c,error:f,loading:i,refresh:n}}export{m as u};
1
+ import{r as e}from"./index-6Z4B0I6r.js";function m(s,u=2e3,a=!0){const[c,l]=e.useState(null),[f,o]=e.useState(null),[i,d]=e.useState(!0),r=e.useRef(!0),n=e.useCallback(async()=>{try{const t=await s();r.current&&(l(t),o(null))}catch(t){r.current&&o(t instanceof Error?t.message:"Unknown error")}finally{r.current&&d(!1)}},[s]);return e.useEffect(()=>{if(r.current=!0,!a)return;n();const t=setInterval(n,u);return()=>{r.current=!1,clearInterval(t)}},[n,u,a]),{data:c,error:f,loading:i,refresh:n}}export{m as u};
@@ -1,4 +1,4 @@
1
- import{c}from"./index-_2iPl2nX.js";/**
1
+ import{c}from"./index-6Z4B0I6r.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -8,7 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
10
  <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
11
- <script type="module" crossorigin src="/assets/index-_2iPl2nX.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-6Z4B0I6r.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-CVM4A1Fw.css">
13
13
  </head>
14
14
  <body class="bg-background text-ink font-sans antialiased">
package/web-app/server.py CHANGED
@@ -7040,6 +7040,14 @@ async def github_import_repo(session_id: str, req: GitHubImportRequest) -> JSONR
7040
7040
  )
7041
7041
  default_branch = branch_result.stdout.strip() if branch_result.returncode == 0 else req.branch
7042
7042
 
7043
+ # After successful clone, trigger doc generation in background
7044
+ try:
7045
+ loki_cli = _find_loki_cli()
7046
+ if loki_cli:
7047
+ asyncio.create_task(_run_doc_generation(str(target), loki_cli))
7048
+ except Exception:
7049
+ logger.debug("Auto-doc generation skipped")
7050
+
7043
7051
  return JSONResponse(content={
7044
7052
  "success": True,
7045
7053
  "files_count": files_count,
@@ -8322,5 +8330,193 @@ async def github_actions_cancel_run(session_id: str, run_id: int) -> JSONRespons
8322
8330
  return JSONResponse(status_code=500, content={"error": f"Failed to cancel run: {exc}"})
8323
8331
 
8324
8332
 
8333
+ # ---------------------------------------------------------------------------
8334
+ # Documentation generation endpoints
8335
+ # ---------------------------------------------------------------------------
8336
+
8337
+
8338
+ async def _run_doc_generation(project_dir: str, loki_cli: str) -> None:
8339
+ """Run loki docs generate in background after repo import."""
8340
+ loop = asyncio.get_running_loop()
8341
+ try:
8342
+ await loop.run_in_executor(None, lambda: subprocess.run(
8343
+ [loki_cli, "docs", "generate"],
8344
+ cwd=project_dir,
8345
+ capture_output=True,
8346
+ timeout=300,
8347
+ ))
8348
+ logger.info("Auto-generated documentation for %s", project_dir)
8349
+ except Exception as e:
8350
+ logger.warning("Doc generation failed: %s", e)
8351
+
8352
+
8353
+ @app.post("/api/sessions/{session_id}/docs/generate")
8354
+ async def docs_generate(session_id: str) -> JSONResponse:
8355
+ """Trigger loki docs generate in the session's project directory."""
8356
+ target, err = _validate_session_and_find_dir(session_id)
8357
+ if err:
8358
+ return err
8359
+
8360
+ _cleanup_chat_tasks()
8361
+ task = ChatTask()
8362
+ _chat_tasks[task.id] = task
8363
+
8364
+ async def run_docs() -> None:
8365
+ loki = _find_loki_cli()
8366
+ if loki is None:
8367
+ task.output_lines = ["loki CLI not found"]
8368
+ task.returncode = 1
8369
+ task.complete = True
8370
+ return
8371
+ proc: Optional[subprocess.Popen] = None
8372
+ try:
8373
+ proc = subprocess.Popen(
8374
+ [loki, "docs", "generate"],
8375
+ stdout=subprocess.PIPE,
8376
+ stderr=subprocess.STDOUT,
8377
+ stdin=subprocess.DEVNULL,
8378
+ text=True,
8379
+ cwd=str(target),
8380
+ start_new_session=True,
8381
+ )
8382
+ task.process = proc
8383
+ _track_child_pid(proc.pid)
8384
+ loop = asyncio.get_running_loop()
8385
+
8386
+ def _read() -> None:
8387
+ assert proc.stdout is not None
8388
+ for raw_line in proc.stdout:
8389
+ if task.cancelled:
8390
+ break
8391
+ clean = re.sub(r'\x1b\[[0-9;]*[a-zA-Z]', '', raw_line.rstrip("\n"))
8392
+ stripped = clean.strip()
8393
+ if not stripped:
8394
+ continue
8395
+ task.output_lines.append(stripped)
8396
+ proc.wait()
8397
+
8398
+ await loop.run_in_executor(None, _read)
8399
+ task.returncode = proc.returncode or 0
8400
+ except Exception as exc:
8401
+ task.output_lines.append(f"Doc generation failed: {exc}")
8402
+ task.returncode = 1
8403
+ finally:
8404
+ task.complete = True
8405
+ if proc and proc.pid:
8406
+ _untrack_child_pid(proc.pid)
8407
+
8408
+ asyncio.create_task(run_docs())
8409
+ return JSONResponse(content={"task_id": task.id, "message": "Documentation generation started"})
8410
+
8411
+
8412
+ @app.get("/api/sessions/{session_id}/docs/status")
8413
+ async def docs_status(session_id: str) -> JSONResponse:
8414
+ """Read .loki/docs/docs-manifest.json from the project directory."""
8415
+ target, err = _validate_session_and_find_dir(session_id)
8416
+ if err:
8417
+ return err
8418
+
8419
+ docs_dir = target / ".loki" / "docs"
8420
+ manifest_path = docs_dir / "docs-manifest.json"
8421
+
8422
+ if not manifest_path.exists():
8423
+ return JSONResponse(content={
8424
+ "has_docs": False,
8425
+ "coverage_pct": 0,
8426
+ "doc_files": [],
8427
+ "last_generated": None,
8428
+ "commits_behind": 0,
8429
+ })
8430
+
8431
+ try:
8432
+ manifest = json.loads(manifest_path.read_text())
8433
+ except (json.JSONDecodeError, OSError):
8434
+ return JSONResponse(content={
8435
+ "has_docs": False,
8436
+ "coverage_pct": 0,
8437
+ "doc_files": [],
8438
+ "last_generated": None,
8439
+ "commits_behind": 0,
8440
+ })
8441
+
8442
+ # List doc files
8443
+ doc_files = []
8444
+ if docs_dir.exists():
8445
+ doc_files = [
8446
+ f.name for f in docs_dir.iterdir()
8447
+ if f.is_file() and f.suffix in (".md", ".txt", ".html") and f.name != "docs-manifest.json"
8448
+ ]
8449
+
8450
+ # Count commits behind (compare HEAD to last_generated commit if available)
8451
+ commits_behind = 0
8452
+ last_commit = manifest.get("last_commit")
8453
+ if last_commit:
8454
+ try:
8455
+ result = subprocess.run(
8456
+ ["git", "rev-list", "--count", f"{last_commit}..HEAD"],
8457
+ cwd=str(target),
8458
+ capture_output=True, text=True, timeout=10,
8459
+ )
8460
+ if result.returncode == 0:
8461
+ commits_behind = int(result.stdout.strip())
8462
+ except (subprocess.TimeoutExpired, ValueError, OSError):
8463
+ pass
8464
+
8465
+ return JSONResponse(content={
8466
+ "has_docs": True,
8467
+ "coverage_pct": manifest.get("coverage_pct", 0),
8468
+ "doc_files": doc_files,
8469
+ "last_generated": manifest.get("last_generated"),
8470
+ "commits_behind": commits_behind,
8471
+ })
8472
+
8473
+
8474
+ @app.get("/api/sessions/{session_id}/docs/files")
8475
+ async def docs_list_files(session_id: str) -> JSONResponse:
8476
+ """List all generated doc files in .loki/docs/."""
8477
+ target, err = _validate_session_and_find_dir(session_id)
8478
+ if err:
8479
+ return err
8480
+
8481
+ docs_dir = target / ".loki" / "docs"
8482
+ if not docs_dir.exists():
8483
+ return JSONResponse(content={"files": []})
8484
+
8485
+ files = []
8486
+ for f in sorted(docs_dir.iterdir()):
8487
+ if f.is_file() and f.name != "docs-manifest.json":
8488
+ stat = f.stat()
8489
+ files.append({
8490
+ "name": f.name,
8491
+ "size": stat.st_size,
8492
+ "generated_at": datetime.fromtimestamp(stat.st_mtime).isoformat(),
8493
+ })
8494
+
8495
+ return JSONResponse(content={"files": files})
8496
+
8497
+
8498
+ @app.get("/api/sessions/{session_id}/docs/files/{filename}")
8499
+ async def docs_get_file(session_id: str, filename: str) -> Response:
8500
+ """Return the content of a specific generated doc file."""
8501
+ target, err = _validate_session_and_find_dir(session_id)
8502
+ if err:
8503
+ return err
8504
+
8505
+ # Sanitize filename to prevent directory traversal
8506
+ if "/" in filename or "\\" in filename or ".." in filename:
8507
+ return JSONResponse(status_code=400, content={"error": "Invalid filename"})
8508
+
8509
+ doc_path = target / ".loki" / "docs" / filename
8510
+ if not doc_path.exists() or not doc_path.is_file():
8511
+ return JSONResponse(status_code=404, content={"error": "Doc file not found"})
8512
+
8513
+ try:
8514
+ content = doc_path.read_text()
8515
+ except OSError:
8516
+ return JSONResponse(status_code=500, content={"error": "Cannot read doc file"})
8517
+
8518
+ return Response(content=content, media_type="text/markdown")
8519
+
8520
+
8325
8521
  if __name__ == "__main__":
8326
8522
  main()