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.
- package/README.md +1 -53
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +989 -0
- package/autonomy/run.sh +106 -2
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/mcp/server.py +177 -0
- package/package.json +1 -1
- package/references/mcp-integration.md +59 -0
- package/skills/00-index.md +9 -0
- package/skills/documentation.md +123 -0
- package/skills/quality-gates.md +25 -1
- package/web-app/dist/assets/{AdminPage-Cwqm_kDg.js → AdminPage-D4QSV6Zi.js} +1 -1
- package/web-app/dist/assets/{Avatar-BgcFY2E5.js → Avatar-88MlpLO5.js} +1 -1
- package/web-app/dist/assets/{Badge-DeFGfZLB.js → Badge-DbGjLr4i.js} +1 -1
- package/web-app/dist/assets/{Button-Dg1EkPtN.js → Button-sp_FVGZj.js} +1 -1
- package/web-app/dist/assets/{ComparePage-D-wvMVP2.js → ComparePage-p2ENnfa7.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-B_Jm7CmJ.js → GitHubIssuesPanel-DBbBTG9w.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-B5i8Q99N.js → GitHubPRsPanel-Bi_yrcAE.js} +1 -1
- package/web-app/dist/assets/{HomePage-CUDTdntY.js → HomePage-BB83YPiX.js} +1 -1
- package/web-app/dist/assets/{LoginPage-BobwVXx5.js → LoginPage-BXUudCJ9.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-DmM--20B.js → MetricsPage-CX0Ahy-_.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-6_gjeogD.js → NotFoundPage-C4JqatEk.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-C3R2wbFt.js → ProjectPage-t5J2XAJT.js} +46 -46
- package/web-app/dist/assets/{ProjectsPage-DQr06iBk.js → ProjectsPage-Bzpz1clk.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-eROlGKB9.js → SettingsPage-y_yl8FvH.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-DEA5tT_R.js → ShowcasePage-B7d6pzMq.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-C_rIbgZg.js → SystemSettingsPage-C4tR33KU.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-DZGoYZnD.js → TeamsPage-DIOCfZIP.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-DVblWpbO.js → TemplatesPage-DlKyapXX.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-DnESY9zk.js → TerminalOutput-Czg-ZC2k.js} +1 -1
- package/web-app/dist/assets/{activity-COLsZyo1.js → activity-h1wU9a0L.js} +1 -1
- package/web-app/dist/assets/{bell-BjLe9xXk.js → bell-Bu8lsWOp.js} +1 -1
- package/web-app/dist/assets/{bot-Dz62aBIi.js → bot-rWO7KjkQ.js} +1 -1
- package/web-app/dist/assets/{check-BQPQjkH4.js → check-BWp8L5Cy.js} +1 -1
- package/web-app/dist/assets/{chevron-left-BVvOVUQ8.js → chevron-left-Bw4I1yGm.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DqoLW238.js → circle-alert-C37PKXiC.js} +1 -1
- package/web-app/dist/assets/{clock-Cn9fFUva.js → clock-DDScLol4.js} +1 -1
- package/web-app/dist/assets/{cloud-COJxbgUu.js → cloud-DaYKPLaM.js} +1 -1
- package/web-app/dist/assets/{copy-DOn0hVgy.js → copy-DKIRv0VK.js} +1 -1
- package/web-app/dist/assets/{database-Bj3Llvnk.js → database-CYZBHz51.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-BcmncygQ.js → dollar-sign-CydJu0kl.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-BysxoSyu.js → file-code-corner-DqZ9gpdv.js} +1 -1
- package/web-app/dist/assets/{file-plus-Dwi1MqSS.js → file-plus-CzeFJWp3.js} +1 -1
- package/web-app/dist/assets/{folder-open-WzXNCq2x.js → folder-open-4YWk08dP.js} +1 -1
- package/web-app/dist/assets/{git-commit-horizontal-D85UUfNF.js → git-commit-horizontal-wbqFPNID.js} +1 -1
- package/web-app/dist/assets/{globe-CegT0VaL.js → globe-Cby-g5Yb.js} +1 -1
- package/web-app/dist/assets/{hammer-ZGKvu_xy.js → hammer-BNScgGdp.js} +1 -1
- package/web-app/dist/assets/{index-_2iPl2nX.js → index-6Z4B0I6r.js} +74 -74
- package/web-app/dist/assets/{layers-B40lki5j.js → layers-XfssQc5V.js} +1 -1
- package/web-app/dist/assets/{lightbulb-CFSoqUsV.js → lightbulb-EhnzRw7M.js} +1 -1
- package/web-app/dist/assets/{loader-circle-womi7Brk.js → loader-circle-BA0QIVGA.js} +1 -1
- package/web-app/dist/assets/{lock-CEWBb_SL.js → lock-BABtHe6K.js} +1 -1
- package/web-app/dist/assets/{mail-DimGrYf8.js → mail-Dokiey5S.js} +1 -1
- package/web-app/dist/assets/{package-DysIuuIJ.js → package-DbJyS1Ft.js} +1 -1
- package/web-app/dist/assets/{plus-DG1hW27_.js → plus-BcAN8Kaj.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-X31ig0S6.js → refresh-cw-B3dG1-Sb.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CBXooICo.js → rotate-ccw-Cs1Phctm.js} +1 -1
- package/web-app/dist/assets/{save-DQVrWTjZ.js → save-DsrNCZrP.js} +1 -1
- package/web-app/dist/assets/{server-BYAMALfa.js → server-CpN2GX4G.js} +1 -1
- package/web-app/dist/assets/{shield-alert-B3G7vLiW.js → shield-alert-CKJ1pzCz.js} +1 -1
- package/web-app/dist/assets/{trash-2-ChVunC-C.js → trash-2-C9vZqTqw.js} +1 -1
- package/web-app/dist/assets/{trending-down-DgsuOE2t.js → trending-down-BNLTrF5P.js} +1 -1
- package/web-app/dist/assets/{trending-up-CZHZsGeN.js → trending-up-DmFIdVOc.js} +1 -1
- package/web-app/dist/assets/{usePolling-ns_dFCVn.js → usePolling-vUlY-o6P.js} +1 -1
- package/web-app/dist/assets/{user-FQUrWHhF.js → user-Dh00W8De.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/web-app/server.py +196 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./index-
|
|
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};
|
package/web-app/dist/index.html
CHANGED
|
@@ -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-
|
|
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()
|