from __future__ import annotations

import json
from pathlib import Path

def run(out_path: Path) -> int:
    try:
        import codeeval_pro  # type: ignore  # noqa: F401
    except Exception:
        out_path.write_text("", encoding="utf-8")
        print(json.dumps({"ok": False, "status": "skipped", "reason": "CodeEval-Pro not installed", "out": str(out_path)}))
        return 0
    with out_path.open("w", encoding="utf-8") as f:
        f.write(json.dumps({"suite": "humaneval_pro", "status": "plugin_stub"}) + "\n")
    return 0


from __future__ import annotations

import json
from pathlib import Path

def run(out_path: Path) -> int:
    try:
        import codeeval_pro  # type: ignore  # noqa: F401
    except Exception:
        out_path.write_text("", encoding="utf-8")
        print(json.dumps({"ok": False, "status": "skipped", "reason": "CodeEval-Pro not installed", "out": str(out_path)}))
        return 0
    with out_path.open("w", encoding="utf-8") as f:
        f.write(json.dumps({"suite": "mbpp_pro", "status": "plugin_stub"}) + "\n")
    return 0


from __future__ import annotations

import json
from pathlib import Path

def run(out_path: Path) -> int:
    try:
        import bigcodebench  # type: ignore  # noqa: F401
    except Exception:
        out_path.write_text("", encoding="utf-8")
        print(json.dumps({"ok": False, "status": "skipped", "reason": "bigcodebench not installed", "out": str(out_path)}))
        return 0
    # Placeholder integration: write marker row
    with out_path.open("w", encoding="utf-8") as f:
        f.write(json.dumps({"suite": "bigcodebench", "status": "plugin_stub"}) + "\n")
    return 0


from __future__ import annotations

import json
from pathlib import Path

def run(out_path: Path) -> int:
    try:
        import livecodebench  # type: ignore  # noqa: F401
    except Exception:
        out_path.write_text("", encoding="utf-8")
        print(json.dumps({"ok": False, "status": "skipped", "reason": "livecodebench not installed", "out": str(out_path)}))
        return 0
    with out_path.open("w", encoding="utf-8") as f:
        f.write(json.dumps({"suite": "livecodebench", "status": "plugin_stub"}) + "\n")
    return 0


from __future__ import annotations
import logging

import argparse
import json
import subprocess
from pathlib import Path

def main() -> int:
	ap = argparse.ArgumentParser(description="Check docs drift by diffing tracked files")
	ap.add_argument("--path", default="docs", help="Docs folder to check")
	args = ap.parse_args()

	# Use git to detect working tree changes
	try:
		out = subprocess.check_output(["git", "status", "--porcelain"], stderr=subprocess.STDOUT).decode("utf-8", errors="ignore")
	except Exception as e:
		print(json.dumps({"ok": False, "error": f"git status failed: {e}"}))
		return 2

	# Consider any changes inside docs/ or README.md as drift
	lines = [ln.strip() for ln in out.splitlines() if ln.strip()]
	drift = [ln for ln in lines if (f" {args.path}/" in ln or ln.endswith(" README.md"))]
	ok = len(drift) == 0
	print(json.dumps({"ok": ok, "changed": drift}))
	return 0 if ok else 2

if __name__ == "__main__":
	raise SystemExit(main())


from __future__ import annotations

import logging
import argparse
import json
import os
import shutil
import subprocess
from pathlib import Path
from typing import Dict, Any

def write(path: Path, text: str) -> None:
	path.parent.mkdir(parents=True, exist_ok=True)
	path.write_text(text, encoding="utf-8")

def run_cli(cmd: list[str]) -> str:
	try:
		out = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
		return out.decode("utf-8", errors="ignore")
	except Exception as e:
		return f"ERROR: {e}"

def render_quickstart(root: Path, docs_root: Path) -> None:
	content = (
		"# Quickstart\n\n"
		"```bash\n"
		"make dev.up\n"
		"make docs.open\n"
		"```\n"
	)
	write(docs_root / "getting-started" / "quickstart.md", content)

def render_dev_env(root: Path, docs_root: Path) -> None:
	tool_versions = []
	for name in (".tool-versions", "uv.lock", "package-lock.json", "requirements.txt"):
		p = root / name
		if p.exists():
			tool_versions.append(f"- {name}")
	content = "# Dev Environment\n\n" + "\n".join(tool_versions) + "\n"
	write(docs_root / "getting-started" / "dev-env.md", content)

def render_cli_reference(root: Path, docs_root: Path) -> None:
	# Snapshot top-level Make help and python entry points as crude CLI docs
	make_help = run_cli(["make", "-C", str(root), "-n", "help"]) if shutil.which("make") else ""
	content = "# CLI Reference\n\n" + "\n\n" + "````\n" + make_help + "\n````\n"
	write(docs_root / "reference" / "cli" / "index.md", content)

def render_cheatsheet(root: Path, docs_root: Path) -> None:
	lines = [
		"# Commands Cheat Sheet",
		"",
		"- Build index: `make -C agi_dw tools.code-index`",
		"- Risk: `make -C agi_dw tools.code-graph && make -C agi_dw plan.risk`",
		"- Pillars+Docs: `make -C agi_dw docs.pipeline`",
		"- Evidence: `make -C agi_dw pr.bundle`",
		"- Release: `make -C agi_dw rel.prepare && make -C agi_dw rel.publish`",
		"- SLO: `make -C agi_dw slo.report && make -C agi_dw docs.gen`",
		"- SBOM/Audit: `make -C agi_dw deps.sbom && make -C agi_dw deps.audit`",
		"- Benchmarks: `make -C agi_dw bench.run.humaneval && make -C agi_dw bench.run.mbpp`",
	]
	write(docs_root / "reference" / "cheatsheet.md", "\n".join(lines) + "\n")

def render_whats_new(root: Path, docs_root: Path) -> None:
    lines = [
        "# What's New",
        "",
        "## New Make Targets",
        "- `pr.bundle` (evidence bundler; depends on docs.pipeline)",
        "- `rel.prepare`, `rel.publish`, `rel.rollback` (release orchestrator)",
        "- `obs.inject` (observability codemod)",
        "- `deps.sbom`, `deps.audit` (supply chain lane)",
        "- `tools.ir.snapshot` (IR snapshot scaffold)",
        "- `bench.smoke.code` (humaneval/mbpp smoke + gate)",
        "- `benchinfra.pipeline` (export + validate bench-infra tasks)",
        "- `ci.gate.consolidated` (consolidated tests/types/lint + pillars)",
        "",
        "## Pillars & Risk",
        "- `plan_risk.json` enriched with score/components/caps; `policies.yaml` under data/sandbox/config",
        "- SLO reporter consumes telemetry; budgets enforced from policies",
        "",
        "## Utilities",
        "- Window compiler emits prompt_pack.json",
        "- Thin generation adapters: name/api synthesis and PR narrative",
        "",
        "Artifacts are written under `data/sandbox/tmp/` and `artifacts/`.",
    ]
    write(docs_root / "updates" / "whats-new.md", "\n".join(lines) + "\n")

def render_modules_index(root: Path, docs_root: Path) -> None:
	idx_path = root / "data" / "sandbox" / "tmp" / "index.json"
	if not idx_path.exists():
		return
	try:
		idx = json.loads(idx_path.read_text(encoding="utf-8"))
	except Exception:
		idx = {}
	entries = idx.get("entries", []) or []
	lines = ["# Modules Index", "", "| File | Symbols |", "| --- | --- |"]
	for it in entries:
		file = it.get("file")
		defs = it.get("defs", []) or []
		symbols = ", ".join(sorted({d.get("name") for d in defs if d.get("name")}))
		lines.append(f"| {file} | {symbols} |")
	write(docs_root / "reference" / "modules" / "index.md", "\n".join(lines) + "\n")

def render_ownership_map(root: Path, docs_root: Path) -> None:
	codeowners = root / "CODEOWNERS"
	lines = ["# Ownership Map", "", "| Pattern | Owners |", "| --- | --- |"]
	if codeowners.exists():
		for ln in codeowners.read_text(encoding="utf-8", errors="ignore").splitlines():
			s = ln.strip()
			if not s or s.startswith("#"):
				continue
			parts = s.split()
			if len(parts) >= 2:
				pat = parts[0]
				owners = ", ".join(parts[1:])
				lines.append(f"| {pat} | {owners} |")
	write(docs_root / "architecture" / "ownership-map.md", "\n".join(lines) + "\n")

def render_readme(root: Path) -> None:
	# Intentionally no-op: do not modify repo root; narrative lives outside sandbox when authored.
	return

def render_pillar_reports(root: Path, docs_root: Path) -> None:
	reports_dir = docs_root / "reports"
	reports_dir.mkdir(parents=True, exist_ok=True)

	def load_json_safe(p: Path):
		try:
			return json.loads(p.read_text(encoding="utf-8")) if p.exists() else None
		except Exception:
			return None

	base_tmp = root / "data" / "sandbox" / "tmp"

	# Planning
	pi = load_json_safe(base_tmp / "plan_impact.json")
	pr = load_json_safe(base_tmp / "plan_risk.json")
	pa = load_json_safe(base_tmp / "plan_api_diff.json")
	if pi:
		lines = ["# Plan Impact", "", "Top modules (by symbols):", "", "| Path | Symbols | Owners |", "| --- | --- | --- |"]
		for it in (pi.get("impact", {}).get("top_modules", []) or [])[:50]:
			owners = ", ".join(it.get("owners", []) or [])
			lines.append(f"| {it.get('path','')} | {it.get('symbols',0)} | {owners} |")
		write(reports_dir / "plan_impact.md", "\n".join(lines) + "\n")
	if pr:
		lines = ["# Plan Risk", "", "Highest coupling (approx):", "", "| Path | Score |", "| --- | --- |"]
		for it in pr.get("risk", []) or []:
			lines.append(f"| {it.get('path','')} | {it.get('score',0)} |")
		write(reports_dir / "plan_risk.md", "\n".join(lines) + "\n")
	if pa:
		lines = ["# API Diff", "", "Breaking: ", "", "Additive:", "", "Deprecations:"]
		write(reports_dir / "plan_api_diff.md", "\n".join(lines) + "\n")

	# Dependencies
	sbom = load_json_safe(base_tmp / "sbom.json")
	dep_a = load_json_safe(base_tmp / "deps_audit.json")
	dep_u = load_json_safe(base_tmp / "deps_upgrade.json")
	if sbom:
		mans = ", ".join(sbom.get("sbom", {}).get("manifests", []) or [])
		write(reports_dir / "deps_sbom.md", f"# SBOM\n\nManifests: {mans}\n")
	if dep_a:
		write(reports_dir / "deps_audit.md", f"# Dependency Audit\n\nFindings: {len(dep_a.get('findings', []))}\n")
	if dep_u:
		write(reports_dir / "deps_upgrade.md", f"# Dependency Upgrades\n\nPlanned: {len(dep_u.get('planned', []))}\n")

	# Observability
	obs = load_json_safe(base_tmp / "obs_check.json")
	slo = load_json_safe(base_tmp / "slo_report.json")
	eb = load_json_safe(base_tmp / "error_budget.json")
	if obs:
		p = obs.get("presence", {}) or {}
		write(reports_dir / "obs_check.md", f"# Observability Check\n\n{json.dumps(p, indent=2)}\n")
	if slo:
		write(reports_dir / "slo_report.md", f"# SLO Report\n\n{json.dumps(slo.get('slo', {}), indent=2)}\n")
	if eb:
		write(reports_dir / "error_budget.md", f"# Error Budget\n\n{json.dumps(eb.get('error_budget', {}), indent=2)}\n")

	# Learning
	th = load_json_safe(base_tmp / "trace_harvest.json")
	td = load_json_safe(base_tmp / "trace_dataset_update.json")
	pt = load_json_safe(base_tmp / "pattern_update.json")
	if th:
		write(reports_dir / "trace_harvest.md", f"# Trace Harvest\n\nHarvested: {th.get('harvested', 0)}\n")
	if td:
		write(reports_dir / "trace_dataset_update.md", f"# Trace Dataset Update\n\nUpdated: {bool(td.get('updated', False))}\n")
	if pt:
		write(reports_dir / "pattern_update.md", f"# Pattern Update\n\nCount: {len(pt.get('patterns', []))}\n")

	# Training (SFT/IL/Heads)
	sft_manifest = load_json_safe(root / "data" / "sandbox" / "sft" / "manifest.json")
	il_manifest = load_json_safe(root / "data" / "sandbox" / "il_traces" / "manifest.json")
	heads = load_json_safe(base_tmp / "heads_tuning.json")
	if sft_manifest:
		write(reports_dir / "training_sft.md", f"# SFT Curation\n\n{json.dumps(sft_manifest, indent=2)}\n")
	if il_manifest:
		write(reports_dir / "training_il_traces.md", f"# IL Traces\n\n{json.dumps(il_manifest, indent=2)}\n")
	if heads:
		write(reports_dir / "training_heads.md", f"# Heads Tuning\n\n{json.dumps(heads, indent=2)}\n")

def main() -> int:
	ap = argparse.ArgumentParser(description="Build deterministic docs and render templates")
	root = Path(__file__).resolve().parents[2]
	ap.add_argument("--out", default=str(root / "data" / "sandbox" / "docs"))
	args = ap.parse_args()

	docs_root = Path(args.out)
	docs_root.mkdir(parents=True, exist_ok=True)

	# Deterministic prerequisites: code index
	idx_out = root / "data" / "sandbox" / "tmp" / "index.json"
	idx_out.parent.mkdir(parents=True, exist_ok=True)
	try:
		subprocess.check_call(["python", str(root / "tools" / "code_index.py"), "--root", str(root), "--out", str(idx_out)])
	except Exception:
		pass

	# Render deterministic surfaces
	render_quickstart(root, docs_root)
	render_dev_env(root, docs_root)
	render_cli_reference(root, docs_root)
	render_cheatsheet(root, docs_root)
	render_whats_new(root, docs_root)
	render_modules_index(root, docs_root)
	render_ownership_map(root, docs_root)
	render_pillar_reports(root, docs_root)
	render_readme(root)

	print(json.dumps({"ok": True, "out": str(docs_root)}))
	return 0

if __name__ == "__main__":
	raise SystemExit(main())


write

from __future__ import annotations

import logging
import argparse
import json
import os
import shutil
import subprocess
from pathlib import Path
from typing import Dict, Any

def write(path: Path, text: str) -> None:
	path.parent.mkdir(parents=True, exist_ok=True)
	path.write_text(text, encoding="utf-8")

def run_cli(cmd: list[str]) -> str:
	try:
		out = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
		return out.decode("utf-8", errors="ignore")
	except Exception as e:
		return f"ERROR: {e}"

def render_quickstart(root: Path, docs_root: Path) -> None:
	content = (
		"# Quickstart\n\n"
		"```bash\n"
		"make dev.up\n"
		"make docs.open\n"
		"```\n"
	)
	write(docs_root / "getting-started" / "quickstart.md", content)


from __future__ import annotations

import logging
import argparse
import json
import os
import shutil
import subprocess
from pathlib import Path
from typing import Dict, Any

def write(path: Path, text: str) -> None:
	path.parent.mkdir(parents=True, exist_ok=True)
	path.write_text(text, encoding="utf-8")

def run_cli(cmd: list[str]) -> str:
	try:
		out = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
		return out.decode("utf-8", errors="ignore")
	except Exception as e:
		return f"ERROR: {e}"

def render_quickstart(root: Path, docs_root: Path) -> None:
	content = (
		"# Quickstart\n\n"
		"```bash\n"
		"make dev.up\n"
		"make docs.open\n"
		"```\n"
	)
	write(docs_root / "getting-started" / "quickstart.md", content)

def render_dev_env(root: Path, docs_root: Path) -> None:
	tool_versions = []
	for name in (".tool-versions", "uv.lock", "package-lock.json", "requirements.txt"):
		p = root / name
		if p.exists():
			tool_versions.append(f"- {name}")
	content = "# Dev Environment\n\n" + "\n".join(tool_versions) + "\n"


import os
import shutil
import subprocess
from pathlib import Path
from typing import Dict, Any

def write(path: Path, text: str) -> None:
	path.parent.mkdir(parents=True, exist_ok=True)
	path.write_text(text, encoding="utf-8")

def run_cli(cmd: list[str]) -> str:
	try:
		out = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
		return out.decode("utf-8", errors="ignore")
	except Exception as e:
		return f"ERROR: {e}"

def render_quickstart(root: Path, docs_root: Path) -> None:
	content = (
		"# Quickstart\n\n"
		"```bash\n"
		"make dev.up\n"
		"make docs.open\n"
		"```\n"
	)
	write(docs_root / "getting-started" / "quickstart.md", content)

def render_dev_env(root: Path, docs_root: Path) -> None:
	tool_versions = []
	for name in (".tool-versions", "uv.lock", "package-lock.json", "requirements.txt"):
		p = root / name
		if p.exists():
			tool_versions.append(f"- {name}")
	content = "# Dev Environment\n\n" + "\n".join(tool_versions) + "\n"
	write(docs_root / "getting-started" / "dev-env.md", content)

def render_cli_reference(root: Path, docs_root: Path) -> None:
	# Snapshot top-level Make help and python entry points as crude CLI docs
	make_help = run_cli(["make", "-C", str(root), "-n", "help"]) if shutil.which("make") else ""
	content = "# CLI Reference\n\n" + "\n\n" + "````\n" + make_help + "\n````\n"
	write(docs_root / "reference" / "cli" / "index.md", content)

def render_cheatsheet(root: Path, docs_root: Path) -> None:


def run_cli(cmd: list[str]) -> str:
	try:
		out = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
		return out.decode("utf-8", errors="ignore")
	except Exception as e:
		return f"ERROR: {e}"

def render_quickstart(root: Path, docs_root: Path) -> None:
	content = (
		"# Quickstart\n\n"
		"```bash\n"
		"make dev.up\n"
		"make docs.open\n"
		"```\n"
	)
	write(docs_root / "getting-started" / "quickstart.md", content)

def render_dev_env(root: Path, docs_root: Path) -> None:
	tool_versions = []
	for name in (".tool-versions", "uv.lock", "package-lock.json", "requirements.txt"):
		p = root / name
		if p.exists():
			tool_versions.append(f"- {name}")
	content = "# Dev Environment\n\n" + "\n".join(tool_versions) + "\n"
	write(docs_root / "getting-started" / "dev-env.md", content)

def render_cli_reference(root: Path, docs_root: Path) -> None:
	# Snapshot top-level Make help and python entry points as crude CLI docs
	make_help = run_cli(["make", "-C", str(root), "-n", "help"]) if shutil.which("make") else ""
	content = "# CLI Reference\n\n" + "\n\n" + "````\n" + make_help + "\n````\n"
	write(docs_root / "reference" / "cli" / "index.md", content)

def render_cheatsheet(root: Path, docs_root: Path) -> None:
	lines = [
		"# Commands Cheat Sheet",
		"",
		"- Build index: `make -C agi_dw tools.code-index`",
		"- Risk: `make -C agi_dw tools.code-graph && make -C agi_dw plan.risk`",
		"- Pillars+Docs: `make -C agi_dw docs.pipeline`",
		"- Evidence: `make -C agi_dw pr.bundle`",
		"- Release: `make -C agi_dw rel.prepare && make -C agi_dw rel.publish`",
		"- SLO: `make -C agi_dw slo.report && make -C agi_dw docs.gen`",
		"- SBOM/Audit: `make -C agi_dw deps.sbom && make -C agi_dw deps.audit`",


	content = (
		"# Quickstart\n\n"
		"```bash\n"
		"make dev.up\n"
		"make docs.open\n"
		"```\n"
	)
	write(docs_root / "getting-started" / "quickstart.md", content)

def render_dev_env(root: Path, docs_root: Path) -> None:
	tool_versions = []
	for name in (".tool-versions", "uv.lock", "package-lock.json", "requirements.txt"):
		p = root / name
		if p.exists():
			tool_versions.append(f"- {name}")
	content = "# Dev Environment\n\n" + "\n".join(tool_versions) + "\n"
	write(docs_root / "getting-started" / "dev-env.md", content)

def render_cli_reference(root: Path, docs_root: Path) -> None:
	# Snapshot top-level Make help and python entry points as crude CLI docs
	make_help = run_cli(["make", "-C", str(root), "-n", "help"]) if shutil.which("make") else ""
	content = "# CLI Reference\n\n" + "\n\n" + "````\n" + make_help + "\n````\n"
	write(docs_root / "reference" / "cli" / "index.md", content)

def render_cheatsheet(root: Path, docs_root: Path) -> None:
	lines = [
		"# Commands Cheat Sheet",
		"",
		"- Build index: `make -C agi_dw tools.code-index`",
		"- Risk: `make -C agi_dw tools.code-graph && make -C agi_dw plan.risk`",
		"- Pillars+Docs: `make -C agi_dw docs.pipeline`",
		"- Evidence: `make -C agi_dw pr.bundle`",
		"- Release: `make -C agi_dw rel.prepare && make -C agi_dw rel.publish`",
		"- SLO: `make -C agi_dw slo.report && make -C agi_dw docs.gen`",
		"- SBOM/Audit: `make -C agi_dw deps.sbom && make -C agi_dw deps.audit`",
		"- Benchmarks: `make -C agi_dw bench.run.humaneval && make -C agi_dw bench.run.mbpp`",
	]
	write(docs_root / "reference" / "cheatsheet.md", "\n".join(lines) + "\n")

def render_whats_new(root: Path, docs_root: Path) -> None:
    lines = [


	write(docs_root / "getting-started" / "quickstart.md", content)

def render_dev_env(root: Path, docs_root: Path) -> None:
	tool_versions = []
	for name in (".tool-versions", "uv.lock", "package-lock.json", "requirements.txt"):
		p = root / name
		if p.exists():
			tool_versions.append(f"- {name}")
	content = "# Dev Environment\n\n" + "\n".join(tool_versions) + "\n"
	write(docs_root / "getting-started" / "dev-env.md", content)

def render_cli_reference(root: Path, docs_root: Path) -> None:
	# Snapshot top-level Make help and python entry points as crude CLI docs
	make_help = run_cli(["make", "-C", str(root), "-n", "help"]) if shutil.which("make") else ""
	content = "# CLI Reference\n\n" + "\n\n" + "````\n" + make_help + "\n````\n"
	write(docs_root / "reference" / "cli" / "index.md", content)

def render_cheatsheet(root: Path, docs_root: Path) -> None:
	lines = [
		"# Commands Cheat Sheet",
		"",
		"- Build index: `make -C agi_dw tools.code-index`",
		"- Risk: `make -C agi_dw tools.code-graph && make -C agi_dw plan.risk`",
		"- Pillars+Docs: `make -C agi_dw docs.pipeline`",
		"- Evidence: `make -C agi_dw pr.bundle`",
		"- Release: `make -C agi_dw rel.prepare && make -C agi_dw rel.publish`",
		"- SLO: `make -C agi_dw slo.report && make -C agi_dw docs.gen`",
		"- SBOM/Audit: `make -C agi_dw deps.sbom && make -C agi_dw deps.audit`",
		"- Benchmarks: `make -C agi_dw bench.run.humaneval && make -C agi_dw bench.run.mbpp`",
	]
	write(docs_root / "reference" / "cheatsheet.md", "\n".join(lines) + "\n")

def render_whats_new(root: Path, docs_root: Path) -> None:
    lines = [
        "# What's New",
        "",
        "## New Make Targets",
        "- `pr.bundle` (evidence bundler; depends on docs.pipeline)",
        "- `rel.prepare`, `rel.publish`, `rel.rollback` (release orchestrator)",
        "- `obs.inject` (observability codemod)",
        "- `deps.sbom`, `deps.audit` (supply chain lane)",
        "- `tools.ir.snapshot` (IR snapshot scaffold)",
        "- `bench.smoke.code` (humaneval/mbpp smoke + gate)",
        "- `benchinfra.pipeline` (export + validate bench-infra tasks)",
        "- `ci.gate.consolidated` (consolidated tests/types/lint + pillars)",
        "",
        "## Pillars & Risk",
        "- `plan_risk.json` enriched with score/components/caps; `policies.yaml` under data/sandbox/config",
        "- SLO reporter consumes telemetry; budgets enforced from policies",
        "",


	content = "# CLI Reference\n\n" + "\n\n" + "````\n" + make_help + "\n````\n"
	write(docs_root / "reference" / "cli" / "index.md", content)

def render_cheatsheet(root: Path, docs_root: Path) -> None:
	lines = [
		"# Commands Cheat Sheet",
		"",
		"- Build index: `make -C agi_dw tools.code-index`",
		"- Risk: `make -C agi_dw tools.code-graph && make -C agi_dw plan.risk`",
		"- Pillars+Docs: `make -C agi_dw docs.pipeline`",
		"- Evidence: `make -C agi_dw pr.bundle`",
		"- Release: `make -C agi_dw rel.prepare && make -C agi_dw rel.publish`",
		"- SLO: `make -C agi_dw slo.report && make -C agi_dw docs.gen`",
		"- SBOM/Audit: `make -C agi_dw deps.sbom && make -C agi_dw deps.audit`",
		"- Benchmarks: `make -C agi_dw bench.run.humaneval && make -C agi_dw bench.run.mbpp`",
	]
	write(docs_root / "reference" / "cheatsheet.md", "\n".join(lines) + "\n")

def render_whats_new(root: Path, docs_root: Path) -> None:
    lines = [
        "# What's New",
        "",
        "## New Make Targets",
        "- `pr.bundle` (evidence bundler; depends on docs.pipeline)",
        "- `rel.prepare`, `rel.publish`, `rel.rollback` (release orchestrator)",
        "- `obs.inject` (observability codemod)",
        "- `deps.sbom`, `deps.audit` (supply chain lane)",
        "- `tools.ir.snapshot` (IR snapshot scaffold)",
        "- `bench.smoke.code` (humaneval/mbpp smoke + gate)",
        "- `benchinfra.pipeline` (export + validate bench-infra tasks)",
        "- `ci.gate.consolidated` (consolidated tests/types/lint + pillars)",
        "",
        "## Pillars & Risk",
        "- `plan_risk.json` enriched with score/components/caps; `policies.yaml` under data/sandbox/config",
        "- SLO reporter consumes telemetry; budgets enforced from policies",
        "",
        "## Utilities",
        "- Window compiler emits prompt_pack.json",
        "- Thin generation adapters: name/api synthesis and PR narrative",
        "",
        "Artifacts are written under `data/sandbox/tmp/` and `artifacts/`.",
    ]
    write(docs_root / "updates" / "whats-new.md", "\n".join(lines) + "\n")

def render_modules_index(root: Path, docs_root: Path) -> None:
	idx_path = root / "data" / "sandbox" / "tmp" / "index.json"
	if not idx_path.exists():
		return
	try:
		idx = json.loads(idx_path.read_text(encoding="utf-8"))
	except Exception:
		idx = {}
	entries = idx.get("entries", []) or []
	lines = ["# Modules Index", "", "| File | Symbols |", "| --- | --- |"]
	for it in entries:
		file = it.get("file")
		defs = it.get("defs", []) or []
		symbols = ", ".join(sorted({d.get("name") for d in defs if d.get("name")}))
		lines.append(f"| {file} | {symbols} |")
	write(docs_root / "reference" / "modules" / "index.md", "\n".join(lines) + "\n")


        "- `obs.inject` (observability codemod)",
        "- `deps.sbom`, `deps.audit` (supply chain lane)",
        "- `tools.ir.snapshot` (IR snapshot scaffold)",
        "- `bench.smoke.code` (humaneval/mbpp smoke + gate)",
        "- `benchinfra.pipeline` (export + validate bench-infra tasks)",
        "- `ci.gate.consolidated` (consolidated tests/types/lint + pillars)",
        "",
        "## Pillars & Risk",
        "- `plan_risk.json` enriched with score/components/caps; `policies.yaml` under data/sandbox/config",
        "- SLO reporter consumes telemetry; budgets enforced from policies",
        "",
        "## Utilities",
        "- Window compiler emits prompt_pack.json",
        "- Thin generation adapters: name/api synthesis and PR narrative",
        "",
        "Artifacts are written under `data/sandbox/tmp/` and `artifacts/`.",
    ]
    write(docs_root / "updates" / "whats-new.md", "\n".join(lines) + "\n")

def render_modules_index(root: Path, docs_root: Path) -> None:
	idx_path = root / "data" / "sandbox" / "tmp" / "index.json"
	if not idx_path.exists():
		return
	try:
		idx = json.loads(idx_path.read_text(encoding="utf-8"))
	except Exception:
		idx = {}
	entries = idx.get("entries", []) or []
	lines = ["# Modules Index", "", "| File | Symbols |", "| --- | --- |"]
	for it in entries:
		file = it.get("file")
		defs = it.get("defs", []) or []
		symbols = ", ".join(sorted({d.get("name") for d in defs if d.get("name")}))
		lines.append(f"| {file} | {symbols} |")
	write(docs_root / "reference" / "modules" / "index.md", "\n".join(lines) + "\n")

def render_ownership_map(root: Path, docs_root: Path) -> None:
	codeowners = root / "CODEOWNERS"
	lines = ["# Ownership Map", "", "| Pattern | Owners |", "| --- | --- |"]
	if codeowners.exists():
		for ln in codeowners.read_text(encoding="utf-8", errors="ignore").splitlines():
			s = ln.strip()
			if not s or s.startswith("#"):
				continue
			parts = s.split()
			if len(parts) >= 2:
				pat = parts[0]
				owners = ", ".join(parts[1:])
				lines.append(f"| {pat} | {owners} |")
	write(docs_root / "architecture" / "ownership-map.md", "\n".join(lines) + "\n")

def render_readme(root: Path) -> None:
	# Intentionally no-op: do not modify repo root; narrative lives outside sandbox when authored.


def render_modules_index(root: Path, docs_root: Path) -> None:
	idx_path = root / "data" / "sandbox" / "tmp" / "index.json"
	if not idx_path.exists():
		return
	try:
		idx = json.loads(idx_path.read_text(encoding="utf-8"))
	except Exception:
		idx = {}
	entries = idx.get("entries", []) or []
	lines = ["# Modules Index", "", "| File | Symbols |", "| --- | --- |"]
	for it in entries:
		file = it.get("file")
		defs = it.get("defs", []) or []
		symbols = ", ".join(sorted({d.get("name") for d in defs if d.get("name")}))
		lines.append(f"| {file} | {symbols} |")
	write(docs_root / "reference" / "modules" / "index.md", "\n".join(lines) + "\n")

def render_ownership_map(root: Path, docs_root: Path) -> None:
	codeowners = root / "CODEOWNERS"
	lines = ["# Ownership Map", "", "| Pattern | Owners |", "| --- | --- |"]
	if codeowners.exists():
		for ln in codeowners.read_text(encoding="utf-8", errors="ignore").splitlines():
			s = ln.strip()
			if not s or s.startswith("#"):
				continue
			parts = s.split()
			if len(parts) >= 2:
				pat = parts[0]
				owners = ", ".join(parts[1:])
				lines.append(f"| {pat} | {owners} |")
	write(docs_root / "architecture" / "ownership-map.md", "\n".join(lines) + "\n")

def render_readme(root: Path) -> None:
	# Intentionally no-op: do not modify repo root; narrative lives outside sandbox when authored.
	return

def render_pillar_reports(root: Path, docs_root: Path) -> None:
	reports_dir = docs_root / "reports"
	reports_dir.mkdir(parents=True, exist_ok=True)

	def load_json_safe(p: Path):
		try:
			return json.loads(p.read_text(encoding="utf-8")) if p.exists() else None
		except Exception:
			return None

	base_tmp = root / "data" / "sandbox" / "tmp"

	# Planning


		lines.append(f"| {file} | {symbols} |")
	write(docs_root / "reference" / "modules" / "index.md", "\n".join(lines) + "\n")

def render_ownership_map(root: Path, docs_root: Path) -> None:
	codeowners = root / "CODEOWNERS"
	lines = ["# Ownership Map", "", "| Pattern | Owners |", "| --- | --- |"]
	if codeowners.exists():
		for ln in codeowners.read_text(encoding="utf-8", errors="ignore").splitlines():
			s = ln.strip()
			if not s or s.startswith("#"):
				continue
			parts = s.split()
			if len(parts) >= 2:
				pat = parts[0]
				owners = ", ".join(parts[1:])
				lines.append(f"| {pat} | {owners} |")
	write(docs_root / "architecture" / "ownership-map.md", "\n".join(lines) + "\n")

def render_readme(root: Path) -> None:
	# Intentionally no-op: do not modify repo root; narrative lives outside sandbox when authored.
	return

def render_pillar_reports(root: Path, docs_root: Path) -> None:
	reports_dir = docs_root / "reports"
	reports_dir.mkdir(parents=True, exist_ok=True)

	def load_json_safe(p: Path):
		try:
			return json.loads(p.read_text(encoding="utf-8")) if p.exists() else None
		except Exception:
			return None

	base_tmp = root / "data" / "sandbox" / "tmp"

	# Planning
	pi = load_json_safe(base_tmp / "plan_impact.json")
	pr = load_json_safe(base_tmp / "plan_risk.json")
	pa = load_json_safe(base_tmp / "plan_api_diff.json")
	if pi:
		lines = ["# Plan Impact", "", "Top modules (by symbols):", "", "| Path | Symbols | Owners |", "| --- | --- | --- |"]


	codeowners = root / "CODEOWNERS"
	lines = ["# Ownership Map", "", "| Pattern | Owners |", "| --- | --- |"]
	if codeowners.exists():
		for ln in codeowners.read_text(encoding="utf-8", errors="ignore").splitlines():
			s = ln.strip()
			if not s or s.startswith("#"):
				continue
			parts = s.split()
			if len(parts) >= 2:
				pat = parts[0]
				owners = ", ".join(parts[1:])
				lines.append(f"| {pat} | {owners} |")
	write(docs_root / "architecture" / "ownership-map.md", "\n".join(lines) + "\n")

def render_readme(root: Path) -> None:
	# Intentionally no-op: do not modify repo root; narrative lives outside sandbox when authored.
	return

def render_pillar_reports(root: Path, docs_root: Path) -> None:
	reports_dir = docs_root / "reports"
	reports_dir.mkdir(parents=True, exist_ok=True)

	def load_json_safe(p: Path):
		try:
			return json.loads(p.read_text(encoding="utf-8")) if p.exists() else None
		except Exception:
			return None

	base_tmp = root / "data" / "sandbox" / "tmp"

	# Planning
	pi = load_json_safe(base_tmp / "plan_impact.json")
	pr = load_json_safe(base_tmp / "plan_risk.json")
	pa = load_json_safe(base_tmp / "plan_api_diff.json")
	if pi:
		lines = ["# Plan Impact", "", "Top modules (by symbols):", "", "| Path | Symbols | Owners |", "| --- | --- | --- |"]
		for it in (pi.get("impact", {}).get("top_modules", []) or [])[:50]:
			owners = ", ".join(it.get("owners", []) or [])
			lines.append(f"| {it.get('path','')} | {it.get('symbols',0)} | {owners} |")
		write(reports_dir / "plan_impact.md", "\n".join(lines) + "\n")
	if pr:
		lines = ["# Plan Risk", "", "Highest coupling (approx):", "", "| Path | Score |", "| --- | --- |"]
		for it in pr.get("risk", []) or []:
			lines.append(f"| {it.get('path','')} | {it.get('score',0)} |")
		write(reports_dir / "plan_risk.md", "\n".join(lines) + "\n")
	if pa:
		lines = ["# API Diff", "", "Breaking: ", "", "Additive:", "", "Deprecations:"]
		write(reports_dir / "plan_api_diff.md", "\n".join(lines) + "\n")

	# Dependencies
	sbom = load_json_safe(base_tmp / "sbom.json")
	dep_a = load_json_safe(base_tmp / "deps_audit.json")
	dep_u = load_json_safe(base_tmp / "deps_upgrade.json")
	if sbom:
		mans = ", ".join(sbom.get("sbom", {}).get("manifests", []) or [])
		write(reports_dir / "deps_sbom.md", f"# SBOM\n\nManifests: {mans}\n")
	if dep_a:
		write(reports_dir / "deps_audit.md", f"# Dependency Audit\n\nFindings: {len(dep_a.get('findings', []))}\n")
	if dep_u:
		write(reports_dir / "deps_upgrade.md", f"# Dependency Upgrades\n\nPlanned: {len(dep_u.get('planned', []))}\n")

	# Observability
	obs = load_json_safe(base_tmp / "obs_check.json")
	slo = load_json_safe(base_tmp / "slo_report.json")
	eb = load_json_safe(base_tmp / "error_budget.json")
	if obs:
		p = obs.get("presence", {}) or {}
		write(reports_dir / "obs_check.md", f"# Observability Check\n\n{json.dumps(p, indent=2)}\n")
	if slo:
		write(reports_dir / "slo_report.md", f"# SLO Report\n\n{json.dumps(slo.get('slo', {}), indent=2)}\n")
	if eb:
		write(reports_dir / "error_budget.md", f"# Error Budget\n\n{json.dumps(eb.get('error_budget', {}), indent=2)}\n")

	# Learning
	th = load_json_safe(base_tmp / "trace_harvest.json")
	td = load_json_safe(base_tmp / "trace_dataset_update.json")
	pt = load_json_safe(base_tmp / "pattern_update.json")
	if th:
		write(reports_dir / "trace_harvest.md", f"# Trace Harvest\n\nHarvested: {th.get('harvested', 0)}\n")
	if td:
		write(reports_dir / "trace_dataset_update.md", f"# Trace Dataset Update\n\nUpdated: {bool(td.get('updated', False))}\n")
	if pt:
		write(reports_dir / "pattern_update.md", f"# Pattern Update\n\nCount: {len(pt.get('patterns', []))}\n")

	# Training (SFT/IL/Heads)
	sft_manifest = load_json_safe(root / "data" / "sandbox" / "sft" / "manifest.json")
	il_manifest = load_json_safe(root / "data" / "sandbox" / "il_traces" / "manifest.json")
	heads = load_json_safe(base_tmp / "heads_tuning.json")
	if sft_manifest:
		write(reports_dir / "training_sft.md", f"# SFT Curation\n\n{json.dumps(sft_manifest, indent=2)}\n")
	if il_manifest:
		write(reports_dir / "training_il_traces.md", f"# IL Traces\n\n{json.dumps(il_manifest, indent=2)}\n")
	if heads:
		write(reports_dir / "training_heads.md", f"# Heads Tuning\n\n{json.dumps(heads, indent=2)}\n")

def main() -> int:
	ap = argparse.ArgumentParser(description="Build deterministic docs and render templates")
	root = Path(__file__).resolve().parents[2]
	ap.add_argument("--out", default=str(root / "data" / "sandbox" / "docs"))
	args = ap.parse_args()

	docs_root = Path(args.out)
	docs_root.mkdir(parents=True, exist_ok=True)

	# Deterministic prerequisites: code index
	idx_out = root / "data" / "sandbox" / "tmp" / "index.json"
	idx_out.parent.mkdir(parents=True, exist_ok=True)
	try:
		subprocess.check_call(["python", str(root / "tools" / "code_index.py"), "--root", str(root), "--out", str(idx_out)])
	except Exception:
		pass

	# Render deterministic surfaces


main

	pt = load_json_safe(base_tmp / "pattern_update.json")
	if th:
		write(reports_dir / "trace_harvest.md", f"# Trace Harvest\n\nHarvested: {th.get('harvested', 0)}\n")
	if td:
		write(reports_dir / "trace_dataset_update.md", f"# Trace Dataset Update\n\nUpdated: {bool(td.get('updated', False))}\n")
	if pt:
		write(reports_dir / "pattern_update.md", f"# Pattern Update\n\nCount: {len(pt.get('patterns', []))}\n")

	# Training (SFT/IL/Heads)
	sft_manifest = load_json_safe(root / "data" / "sandbox" / "sft" / "manifest.json")
	il_manifest = load_json_safe(root / "data" / "sandbox" / "il_traces" / "manifest.json")
	heads = load_json_safe(base_tmp / "heads_tuning.json")
	if sft_manifest:
		write(reports_dir / "training_sft.md", f"# SFT Curation\n\n{json.dumps(sft_manifest, indent=2)}\n")
	if il_manifest:
		write(reports_dir / "training_il_traces.md", f"# IL Traces\n\n{json.dumps(il_manifest, indent=2)}\n")
	if heads:
		write(reports_dir / "training_heads.md", f"# Heads Tuning\n\n{json.dumps(heads, indent=2)}\n")

def main() -> int:
	ap = argparse.ArgumentParser(description="Build deterministic docs and render templates")
	root = Path(__file__).resolve().parents[2]
	ap.add_argument("--out", default=str(root / "data" / "sandbox" / "docs"))
	args = ap.parse_args()

	docs_root = Path(args.out)
	docs_root.mkdir(parents=True, exist_ok=True)

	# Deterministic prerequisites: code index
	idx_out = root / "data" / "sandbox" / "tmp" / "index.json"
	idx_out.parent.mkdir(parents=True, exist_ok=True)
	try:
		subprocess.check_call(["python", str(root / "tools" / "code_index.py"), "--root", str(root), "--out", str(idx_out)])
	except Exception:
		pass

	# Render deterministic surfaces
	render_quickstart(root, docs_root)
	render_dev_env(root, docs_root)
	render_cli_reference(root, docs_root)
	render_cheatsheet(root, docs_root)
	render_whats_new(root, docs_root)
	render_modules_index(root, docs_root)
	render_ownership_map(root, docs_root)
	render_pillar_reports(root, docs_root)
	render_readme(root)

	print(json.dumps({"ok": True, "out": str(docs_root)}))
	return 0

if __name__ == "__main__":
	raise SystemExit(main())


			s = ln.strip()
			if not s or s.startswith("#"):
				continue
			parts = s.split()
			if len(parts) >= 2:
				pat = parts[0]
				owners = ", ".join(parts[1:])
				lines.append(f"| {pat} | {owners} |")
	write(docs_root / "architecture" / "ownership-map.md", "\n".join(lines) + "\n")

def render_readme(root: Path) -> None:
	# Intentionally no-op: do not modify repo root; narrative lives outside sandbox when authored.
	return

def render_pillar_reports(root: Path, docs_root: Path) -> None:
	reports_dir = docs_root / "reports"
	reports_dir.mkdir(parents=True, exist_ok=True)

	def load_json_safe(p: Path):
		try:
			return json.loads(p.read_text(encoding="utf-8")) if p.exists() else None
		except Exception:
			return None

	base_tmp = root / "data" / "sandbox" / "tmp"

	# Planning
	pi = load_json_safe(base_tmp / "plan_impact.json")
	pr = load_json_safe(base_tmp / "plan_risk.json")
	pa = load_json_safe(base_tmp / "plan_api_diff.json")
	if pi:
		lines = ["# Plan Impact", "", "Top modules (by symbols):", "", "| Path | Symbols | Owners |", "| --- | --- | --- |"]
		for it in (pi.get("impact", {}).get("top_modules", []) or [])[:50]:
			owners = ", ".join(it.get("owners", []) or [])
			lines.append(f"| {it.get('path','')} | {it.get('symbols',0)} | {owners} |")
		write(reports_dir / "plan_impact.md", "\n".join(lines) + "\n")
	if pr:
		lines = ["# Plan Risk", "", "Highest coupling (approx):", "", "| Path | Score |", "| --- | --- |"]
		for it in pr.get("risk", []) or []:
			lines.append(f"| {it.get('path','')} | {it.get('score',0)} |")
		write(reports_dir / "plan_risk.md", "\n".join(lines) + "\n")
	if pa:
		lines = ["# API Diff", "", "Breaking: ", "", "Additive:", "", "Deprecations:"]


from __future__ import annotations
import logging

"""
Generate an index of repository documentation (READMEs/ADRs) with titles and short summaries,
grouped by top-level directories for quick discovery.
"""

import argparse
import json
from pathlib import Path
from typing import Any, Dict, List, Tuple

def summarize_markdown(path: Path) -> Dict[str, Any]:
	try:
		text = path.read_text(encoding="utf-8")
	except Exception:
		return {"path": str(path), "title": path.name, "lines": 0, "summary": ""}
	lines = text.splitlines()
	title = path.stem
	summary = ""
	# First non-empty heading or first meaningful line
	for ln in lines:
		s = ln.strip()
		if not s:
			continue
		if s.startswith("#"):
			title = s.lstrip("#").strip()
			continue
		summary = s
		break
	return {"path": str(path), "title": title, "lines": len(lines), "summary": summary[:200]}

def collect_docs(root: Path, include_patterns: List[str]) -> List[Path]:
	docs: List[Path] = []
	for pat in include_patterns:
		for p in root.rglob(pat):
			if any(seg.startswith(".") for seg in p.parts):
				continue
			if p.is_file():
				docs.append(p)
	# Sort by path for stability
	return sorted(docs, key=lambda p: str(p).lower())

def write_json(items: List[Dict[str, Any]], out_path: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	out_path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")

def write_markdown(items: List[Dict[str, Any]], out_path: Path, repo_root: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	lines: List[str] = []
	lines.append("## Docs Index")
	lines.append("")
	# Group by top-level directory
	groups: Dict[str, List[Dict[str, Any]]] = {}
	for it in items:
		p = Path(it["path"]).resolve()
		rel = p.relative_to(repo_root)
		top = rel.parts[0] if len(rel.parts) > 1 else rel.parent.as_posix()
		groups.setdefault(top, []).append({**it, "rel": rel.as_posix()})
	for top in sorted(groups.keys()):
		lines.append(f"### {top}")
		lines.append("")
		for it in sorted(groups[top], key=lambda x: x["rel"]):
			desc = f" — {it['summary']}" if it.get("summary") else ""
			lines.append(f"- `{it['rel']}`: {it['title']}{desc}")
		lines.append("")
	out_path.write_text("\n".join(lines), encoding="utf-8")

def main() -> int:
	ap = argparse.ArgumentParser()
	repo_root_default = Path(__file__).resolve().parents[1]
	ap.add_argument("--root", default=str(repo_root_default))
	ap.add_argument("--patterns", nargs="*", default=[
		"README.md",
		"docs/**/*.md",
		"core/**/README.md",
		"bench/README.md",
		"runtimes/**/README.md",
		"eval/**/README.md",
		"scripts/README.md",
	])
	ap.add_argument("--out-json", default=str(repo_root_default / "data" / "sandbox" / "tmp" / "docs_index.json"))
	ap.add_argument("--out-md", default=str(repo_root_default / "docs" / "docs_index.md"))
	args = ap.parse_args()

	root = Path(args.root)
	pats = list(args.patterns)
	# Expand simple globs ourselves where possible
	docs_paths: List[Path] = []
	for pat in pats:
		docs_paths.extend(collect_docs(root, [pat]))
	# De-duplicate
	seen = set()
	uniq_paths = []
	for p in docs_paths:
		rp = str(p.resolve())
		if rp in seen:
			continue
		seen.add(rp)
		uniq_paths.append(p)

	items = [summarize_markdown(p) for p in uniq_paths]
	write_json(items, Path(args.out_json))
	write_markdown(items, Path(args.out_md), repo_root=root.resolve())
	print(json.dumps({"ok": True, "count": len(items), "json": args.out_json, "md": args.out_md}))
	return 0

if __name__ == "__main__":
	raise SystemExit(main())


from __future__ import annotations
import logging

"""
Generate an index of repository documentation (READMEs/ADRs) with titles and short summaries,
grouped by top-level directories for quick discovery.
"""

import argparse
import json
from pathlib import Path
from typing import Any, Dict, List, Tuple

def summarize_markdown(path: Path) -> Dict[str, Any]:
	try:
		text = path.read_text(encoding="utf-8")
	except Exception:
		return {"path": str(path), "title": path.name, "lines": 0, "summary": ""}
	lines = text.splitlines()
	title = path.stem
	summary = ""
	# First non-empty heading or first meaningful line
	for ln in lines:
		s = ln.strip()
		if not s:
			continue
		if s.startswith("#"):
			title = s.lstrip("#").strip()
			continue
		summary = s
		break
	return {"path": str(path), "title": title, "lines": len(lines), "summary": summary[:200]}

def collect_docs(root: Path, include_patterns: List[str]) -> List[Path]:
	docs: List[Path] = []
	for pat in include_patterns:
		for p in root.rglob(pat):
			if any(seg.startswith(".") for seg in p.parts):
				continue
			if p.is_file():
				docs.append(p)
	# Sort by path for stability
	return sorted(docs, key=lambda p: str(p).lower())

def write_json(items: List[Dict[str, Any]], out_path: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	out_path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")

def write_markdown(items: List[Dict[str, Any]], out_path: Path, repo_root: Path) -> None:


	try:
		text = path.read_text(encoding="utf-8")
	except Exception:
		return {"path": str(path), "title": path.name, "lines": 0, "summary": ""}
	lines = text.splitlines()
	title = path.stem
	summary = ""
	# First non-empty heading or first meaningful line
	for ln in lines:
		s = ln.strip()
		if not s:
			continue
		if s.startswith("#"):
			title = s.lstrip("#").strip()
			continue
		summary = s
		break
	return {"path": str(path), "title": title, "lines": len(lines), "summary": summary[:200]}

def collect_docs(root: Path, include_patterns: List[str]) -> List[Path]:
	docs: List[Path] = []
	for pat in include_patterns:
		for p in root.rglob(pat):
			if any(seg.startswith(".") for seg in p.parts):
				continue
			if p.is_file():
				docs.append(p)
	# Sort by path for stability
	return sorted(docs, key=lambda p: str(p).lower())

def write_json(items: List[Dict[str, Any]], out_path: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	out_path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")

def write_markdown(items: List[Dict[str, Any]], out_path: Path, repo_root: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	lines: List[str] = []
	lines.append("## Docs Index")
	lines.append("")
	# Group by top-level directory
	groups: Dict[str, List[Dict[str, Any]]] = {}
	for it in items:
		p = Path(it["path"]).resolve()
		rel = p.relative_to(repo_root)
		top = rel.parts[0] if len(rel.parts) > 1 else rel.parent.as_posix()
		groups.setdefault(top, []).append({**it, "rel": rel.as_posix()})
	for top in sorted(groups.keys()):


		if s.startswith("#"):
			title = s.lstrip("#").strip()
			continue
		summary = s
		break
	return {"path": str(path), "title": title, "lines": len(lines), "summary": summary[:200]}

def collect_docs(root: Path, include_patterns: List[str]) -> List[Path]:
	docs: List[Path] = []
	for pat in include_patterns:
		for p in root.rglob(pat):
			if any(seg.startswith(".") for seg in p.parts):
				continue
			if p.is_file():
				docs.append(p)
	# Sort by path for stability
	return sorted(docs, key=lambda p: str(p).lower())

def write_json(items: List[Dict[str, Any]], out_path: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	out_path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")

def write_markdown(items: List[Dict[str, Any]], out_path: Path, repo_root: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	lines: List[str] = []
	lines.append("## Docs Index")
	lines.append("")
	# Group by top-level directory
	groups: Dict[str, List[Dict[str, Any]]] = {}
	for it in items:
		p = Path(it["path"]).resolve()
		rel = p.relative_to(repo_root)
		top = rel.parts[0] if len(rel.parts) > 1 else rel.parent.as_posix()
		groups.setdefault(top, []).append({**it, "rel": rel.as_posix()})
	for top in sorted(groups.keys()):
		lines.append(f"### {top}")
		lines.append("")
		for it in sorted(groups[top], key=lambda x: x["rel"]):
			desc = f" — {it['summary']}" if it.get("summary") else ""
			lines.append(f"- `{it['rel']}`: {it['title']}{desc}")


	return {"path": str(path), "title": title, "lines": len(lines), "summary": summary[:200]}

def collect_docs(root: Path, include_patterns: List[str]) -> List[Path]:
	docs: List[Path] = []
	for pat in include_patterns:
		for p in root.rglob(pat):
			if any(seg.startswith(".") for seg in p.parts):
				continue
			if p.is_file():
				docs.append(p)
	# Sort by path for stability
	return sorted(docs, key=lambda p: str(p).lower())

def write_json(items: List[Dict[str, Any]], out_path: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	out_path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")

def write_markdown(items: List[Dict[str, Any]], out_path: Path, repo_root: Path) -> None:
	out_path.parent.mkdir(parents=True, exist_ok=True)
	lines: List[str] = []
	lines.append("## Docs Index")
	lines.append("")
	# Group by top-level directory
	groups: Dict[str, List[Dict[str, Any]]] = {}
	for it in items:
		p = Path(it["path"]).resolve()
		rel = p.relative_to(repo_root)
		top = rel.parts[0] if len(rel.parts) > 1 else rel.parent.as_posix()
		groups.setdefault(top, []).append({**it, "rel": rel.as_posix()})
	for top in sorted(groups.keys()):
		lines.append(f"### {top}")
		lines.append("")
		for it in sorted(groups[top], key=lambda x: x["rel"]):
			desc = f" — {it['summary']}" if it.get("summary") else ""
			lines.append(f"- `{it['rel']}`: {it['title']}{desc}")
		lines.append("")
	out_path.write_text("\n".join(lines), encoding="utf-8")

def main() -> int:
	ap = argparse.ArgumentParser()
	repo_root_default = Path(__file__).resolve().parents[1]
	ap.add_argument("--root", default=str(repo_root_default))
	ap.add_argument("--patterns", nargs="*", default=[
		"README.md",
		"docs/**/*.md",
		"core/**/README.md",
		"bench/README.md",
		"runtimes/**/README.md",
		"eval/**/README.md",
		"scripts/README.md",
	])
	ap.add_argument("--out-json", default=str(repo_root_default / "data" / "sandbox" / "tmp" / "docs_index.json"))
	ap.add_argument("--out-md", default=str(repo_root_default / "docs" / "docs_index.md"))
	args = ap.parse_args()

	root = Path(args.root)


	lines: List[str] = []
	lines.append("## Docs Index")
	lines.append("")
	# Group by top-level directory
	groups: Dict[str, List[Dict[str, Any]]] = {}
	for it in items:
		p = Path(it["path"]).resolve()
		rel = p.relative_to(repo_root)
		top = rel.parts[0] if len(rel.parts) > 1 else rel.parent.as_posix()
		groups.setdefault(top, []).append({**it, "rel": rel.as_posix()})
	for top in sorted(groups.keys()):
		lines.append(f"### {top}")
		lines.append("")
		for it in sorted(groups[top], key=lambda x: x["rel"]):
			desc = f" — {it['summary']}" if it.get("summary") else ""
			lines.append(f"- `{it['rel']}`: {it['title']}{desc}")
		lines.append("")
	out_path.write_text("\n".join(lines), encoding="utf-8")

def main() -> int:
	ap = argparse.ArgumentParser()
	repo_root_default = Path(__file__).resolve().parents[1]
	ap.add_argument("--root", default=str(repo_root_default))
	ap.add_argument("--patterns", nargs="*", default=[
		"README.md",
		"docs/**/*.md",
		"core/**/README.md",
		"bench/README.md",
		"runtimes/**/README.md",
		"eval/**/README.md",
		"scripts/README.md",
	])
	ap.add_argument("--out-json", default=str(repo_root_default / "data" / "sandbox" / "tmp" / "docs_index.json"))
	ap.add_argument("--out-md", default=str(repo_root_default / "docs" / "docs_index.md"))
	args = ap.parse_args()

	root = Path(args.root)
	pats = list(args.patterns)
	# Expand simple globs ourselves where possible
	docs_paths: List[Path] = []
	for pat in pats:
		docs_paths.extend(collect_docs(root, [pat]))
	# De-duplicate
	seen = set()
	uniq_paths = []
	for p in docs_paths:
		rp = str(p.resolve())
		if rp in seen:
			continue
		seen.add(rp)
		uniq_paths.append(p)

	items = [summarize_markdown(p) for p in uniq_paths]
	write_json(items, Path(args.out_json))
	write_markdown(items, Path(args.out_md), repo_root=root.resolve())
	print(json.dumps({"ok": True, "count": len(items), "json": args.out_json, "md": args.out_md}))
	return 0

if __name__ == "__main__":
	raise SystemExit(main())


from __future__ import annotations
import logging

import argparse
from pathlib import Path
import json

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[2]
	ap.add_argument("--root", default=str(root))
	ap.add_argument("--out", default=str(root / "data" / "docs" / "design.md"))
	args = ap.parse_args()

	proj = Path(args.root)
	lines: list[str] = []
	lines.append(f"# Design Overview\n")
	lines.append(f"Repo: {proj.resolve()}\n")
	# Basic directory structure
	try:
		lines.append("## Directories\n")
		for p in sorted([p for p in proj.iterdir() if p.is_dir()]):
			if p.name.startswith('.'):
				continue
			lines.append(f"- {p.name}/\n")
	except Exception:
		pass
	# Dependencies snapshot
	try:
		req = proj / "requirements.txt"
		if req.exists():
			lines.append("\n## Dependencies\n")
			for ln in req.read_text(encoding="utf-8").splitlines()[:200]:
				lines.append(f"- {ln}\n")
	except Exception:
		pass
	# Simple module inventory
	try:
		lines.append("\n## Python Modules (top-level)\n")
		for p in sorted(proj.glob("**/*.py"))[:400]:
			rel = p.relative_to(proj).as_posix()
			if rel.startswith(".git/") or "/__pycache__/" in rel:
				continue
			lines.append(f"- {rel}\n")
	except Exception:
		pass
	# Write out
	outp = Path(args.out)
	outp.parent.mkdir(parents=True, exist_ok=True)
	outp.write_text("".join(lines), encoding="utf-8")
	print(json.dumps({"ok": True, "out": str(outp)}))
	return 0

if __name__ == "__main__":
	raise SystemExit(main())


import logging
import argparse
import json
from pathlib import Path

def answer_query(doc_text: str, query: str) -> str:
	l = query.lower()
	if "ignore case" in l:
		return "Use -i flag, e.g., grep -i 'error' file.txt"
	if "line number" in l or "line numbers" in l:
		return "Use -n flag, e.g., grep -n 'TODO' src/*.py"
	return ""

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[1]
	ap.add_argument("--tasks", default=str(root / "data" / "docs" / "tasks.jsonl"))
	ap.add_argument("--docs", default=str(root / "data" / "docs"))
	ap.add_argument("--out", default=str(root / "data" / "benchmarks" / "docs_results.jsonl"))
	args = ap.parse_args()

	docs_dir = Path(args.docs)
	outp = Path(args.out)
	outp.parent.mkdir(parents=True, exist_ok=True)
	n = 0
	ok = 0
	with outp.open("w", encoding="utf-8") as w:
		with Path(args.tasks).open("r", encoding="utf-8") as f:
			for line in f:
				line = line.strip()
				if not line:
					continue
				try:
					task = json.loads(line)
				except Exception:
					continue
				doc_path = docs_dir / str(task.get("doc", ""))
				query = str(task.get("query", ""))
				expect = str(task.get("expect", ""))
				try:
					doc_text = doc_path.read_text(encoding="utf-8")
				except Exception:
					doc_text = ""
				pred = answer_query(doc_text, query)
				ok_i = (expect.lower() in pred.lower()) if pred else False
				w.write(json.dumps({"id": task.get("id"), "ok": bool(ok_i), "pred": pred, "expect": expect}, ensure_ascii=False) + "\n")
				n += 1
				ok += 1 if ok_i else 0
	print(json.dumps({"ok": bool(ok == n and n > 0), "total": int(n), "ok_n": int(ok), "out": str(outp)}))
	return 0

if __name__ == "__main__":
	import sys
	sys.exit(main())


import logging
import argparse
import json
from pathlib import Path

def answer_query(doc_text: str, query: str) -> str:
	l = query.lower()
	if "ignore case" in l:
		return "Use -i flag, e.g., grep -i 'error' file.txt"
	if "line number" in l or "line numbers" in l:
		return "Use -n flag, e.g., grep -n 'TODO' src/*.py"
	return ""

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[1]
	ap.add_argument("--tasks", default=str(root / "data" / "docs" / "tasks.jsonl"))
	ap.add_argument("--docs", default=str(root / "data" / "docs"))
	ap.add_argument("--out", default=str(root / "data" / "benchmarks" / "docs_results.jsonl"))
	args = ap.parse_args()

	docs_dir = Path(args.docs)
	outp = Path(args.out)
	outp.parent.mkdir(parents=True, exist_ok=True)
	n = 0
	ok = 0
	with outp.open("w", encoding="utf-8") as w:
		with Path(args.tasks).open("r", encoding="utf-8") as f:
			for line in f:
				line = line.strip()
				if not line:


#!/usr/bin/env python3
import logging
import argparse
import json
import os
import resource
import signal
import sys
import tempfile
import time
from pathlib import Path

def parse_args():
	ap = argparse.ArgumentParser(description="Sandboxed Python execution for code + tests (very lightweight)")
	ap.add_argument("--code", required=True, help="Path to file containing solution code or literal code if --literal")
	ap.add_argument("--tests", required=True, help="Path to file containing tests (assert statements)")
	ap.add_argument("--timeout", type=int, default=10)
	ap.add_argument("--memmb", type=int, default=256)
	ap.add_argument("--literal", action="store_true", help="Treat --code/--tests values as literal content instead of file paths")
	ap.add_argument("--coverage", action="store_true", help="If set, attempt to compute statement coverage percent with coverage.py")
	return ap.parse_args()

def set_limits(mem_mb: int):
	# CPU time limit is enforced by outer timeout; set address space/memory limit
	bytes_limit = mem_mb * 1024 * 1024
	try:
		resource.setrlimit(resource.RLIMIT_AS, (bytes_limit, bytes_limit))
	except Exception:
		pass
	try:
		resource.setrlimit(resource.RLIMIT_DATA, (bytes_limit, bytes_limit))
	except Exception:
		pass

def run_in_sandbox(code_text: str, test_text: str, timeout_sec: int, mem_mb: int, with_coverage: bool = False) -> dict:
	with tempfile.TemporaryDirectory() as tmp:
		tmp_path = Path(tmp)
		(tmp_path / "solution.py").write_text(code_text, encoding="utf-8")
		test_driver = (
			"import sys, json\n"
			"from solution import *\n"
			"success=True\n"
			"try:\n"
			"    # user-provided asserts\n"
			"    exec(\"" + test_text.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "\\\"") + "\")\n"
			"except Exception as e:\n"
			"    success=False\n"
			"    err=str(e)\n"
			"result={'success':success}\n"
			"if not success:\n"
			"    result['error']=err\n"
			"print(json.dumps(result))\n"
		)
		(tmp_path / "test_runner.py").write_text(test_driver, encoding="utf-8")

		# If coverage is requested, run via coverage subprocess directly
		if with_coverage:
			try:
				import subprocess
				# Run test under coverage
				p = subprocess.run([sys.executable, "-m", "coverage", "run", str(tmp_path / "test_runner.py")], capture_output=True, text=True, timeout=timeout_sec)
				cov = None
				try:
					# Generate JSON and parse percent_covered if available
					subprocess.run([sys.executable, "-m", "coverage", "json", "-o", str(tmp_path / "coverage.json")], capture_output=True, text=True, timeout=5)
					import json as _json
					cov_obj = _json.loads((tmp_path / "coverage.json").read_text(encoding="utf-8"))
					totals = cov_obj.get("totals") or {}
					cov = float(totals.get("percent_covered", totals.get("percent_covered_display", 0.0)))
				except Exception:
					cov = None
				ok = p.returncode == 0
				out = (p.stdout or "").strip().splitlines()
				last = out[-1] if out else ""
				try:
					res = json.loads(last) if last.startswith("{") else {"success": ok}
				except Exception:
					res = {"success": ok}
				if cov is not None:
					res["coverage"] = cov
				return res
			except Exception as e:
				return {"success": False, "error": str(e)}

		pid = os.fork()
		if pid == 0:
			# Child: apply limits and exec
			try:
				set_limits(mem_mb)
				os.chdir(tmp_path)
				# Drop env that could be risky
				for k in list(os.environ.keys()):
					if k.upper().startswith("PYTHON") or k.upper().startswith("LD_"):
						os.environ.pop(k, None)
				os.execv(sys.executable, [sys.executable, "test_runner.py"])
			except Exception as e:
				print(json.dumps({"success": False, "error": str(e)}))
				os._exit(0)
		else:
			start = time.time()
			out = b""
			err = b""
			while True:
				pid_done, status = os.waitpid(pid, os.WNOHANG)
				if pid_done == pid:
					break
				if time.time() - start > timeout_sec:
					try:
						os.kill(pid, signal.SIGKILL)
					except Exception:
						pass
					return {"success": False, "timeout": True}
				time.sleep(0.01)
			# Read result from stdout file? We printed JSON to stdout; but we didn't capture pipes here.
			# As a simple workaround, re-run test_runner capturing output in a safe way within limits.
			# Fallback simple execution
			try:
				import subprocess
				p = subprocess.run([sys.executable, str(tmp_path / "test_runner.py")], capture_output=True, text=True, timeout=timeout_sec)
				if p.returncode == 0 and p.stdout.strip():
					return json.loads(p.stdout.strip().splitlines()[-1])
				return {"success": p.returncode == 0, "stdout": p.stdout[-500:], "stderr": p.stderr[-500:]}
			except Exception as e:
				return {"success": False, "error": str(e)}

def main() -> int:
	args = parse_args()
	code_text = Path(args.code).read_text(encoding="utf-8") if not args.literal else args.code
	test_text = Path(args.tests).read_text(encoding="utf-8") if not args.literal else args.tests
	res = run_in_sandbox(code_text, test_text, args.timeout, args.memmb, bool(args.coverage))
	print(json.dumps(res))
	return 0 if bool(res.get("success")) else 1

if __name__ == "__main__":
	raise SystemExit(main())


#!/usr/bin/env python3
import logging
import argparse
import json
import os
import resource
import signal
import sys
import tempfile
import time
from pathlib import Path

def parse_args():
	ap = argparse.ArgumentParser(description="Sandboxed Python execution for code + tests (very lightweight)")
	ap.add_argument("--code", required=True, help="Path to file containing solution code or literal code if --literal")
	ap.add_argument("--tests", required=True, help="Path to file containing tests (assert statements)")
	ap.add_argument("--timeout", type=int, default=10)
	ap.add_argument("--memmb", type=int, default=256)
	ap.add_argument("--literal", action="store_true", help="Treat --code/--tests values as literal content instead of file paths")
	ap.add_argument("--coverage", action="store_true", help="If set, attempt to compute statement coverage percent with coverage.py")
	return ap.parse_args()

def set_limits(mem_mb: int):
	# CPU time limit is enforced by outer timeout; set address space/memory limit
	bytes_limit = mem_mb * 1024 * 1024
	try:
		resource.setrlimit(resource.RLIMIT_AS, (bytes_limit, bytes_limit))
	except Exception:
		pass
	try:
		resource.setrlimit(resource.RLIMIT_DATA, (bytes_limit, bytes_limit))
	except Exception:
		pass

def run_in_sandbox(code_text: str, test_text: str, timeout_sec: int, mem_mb: int, with_coverage: bool = False) -> dict:
	with tempfile.TemporaryDirectory() as tmp:
		tmp_path = Path(tmp)
		(tmp_path / "solution.py").write_text(code_text, encoding="utf-8")
		test_driver = (


import os
import resource
import signal
import sys
import tempfile
import time
from pathlib import Path

def parse_args():
	ap = argparse.ArgumentParser(description="Sandboxed Python execution for code + tests (very lightweight)")
	ap.add_argument("--code", required=True, help="Path to file containing solution code or literal code if --literal")
	ap.add_argument("--tests", required=True, help="Path to file containing tests (assert statements)")
	ap.add_argument("--timeout", type=int, default=10)
	ap.add_argument("--memmb", type=int, default=256)
	ap.add_argument("--literal", action="store_true", help="Treat --code/--tests values as literal content instead of file paths")
	ap.add_argument("--coverage", action="store_true", help="If set, attempt to compute statement coverage percent with coverage.py")
	return ap.parse_args()

def set_limits(mem_mb: int):
	# CPU time limit is enforced by outer timeout; set address space/memory limit
	bytes_limit = mem_mb * 1024 * 1024
	try:
		resource.setrlimit(resource.RLIMIT_AS, (bytes_limit, bytes_limit))
	except Exception:
		pass
	try:
		resource.setrlimit(resource.RLIMIT_DATA, (bytes_limit, bytes_limit))
	except Exception:
		pass

def run_in_sandbox(code_text: str, test_text: str, timeout_sec: int, mem_mb: int, with_coverage: bool = False) -> dict:
	with tempfile.TemporaryDirectory() as tmp:
		tmp_path = Path(tmp)
		(tmp_path / "solution.py").write_text(code_text, encoding="utf-8")
		test_driver = (
			"import sys, json\n"
			"from solution import *\n"
			"success=True\n"
			"try:\n"
			"    # user-provided asserts\n"
			"    exec(\"" + test_text.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "\\\"") + "\")\n"
			"except Exception as e:\n"
			"    success=False\n"
			"    err=str(e)\n"
			"result={'success':success}\n"
			"if not success:\n"
			"    result['error']=err\n"
			"print(json.dumps(result))\n"


	ap.add_argument("--timeout", type=int, default=10)
	ap.add_argument("--memmb", type=int, default=256)
	ap.add_argument("--literal", action="store_true", help="Treat --code/--tests values as literal content instead of file paths")
	ap.add_argument("--coverage", action="store_true", help="If set, attempt to compute statement coverage percent with coverage.py")
	return ap.parse_args()

def set_limits(mem_mb: int):
	# CPU time limit is enforced by outer timeout; set address space/memory limit
	bytes_limit = mem_mb * 1024 * 1024
	try:
		resource.setrlimit(resource.RLIMIT_AS, (bytes_limit, bytes_limit))
	except Exception:
		pass
	try:
		resource.setrlimit(resource.RLIMIT_DATA, (bytes_limit, bytes_limit))
	except Exception:
		pass

def run_in_sandbox(code_text: str, test_text: str, timeout_sec: int, mem_mb: int, with_coverage: bool = False) -> dict:
	with tempfile.TemporaryDirectory() as tmp:
		tmp_path = Path(tmp)
		(tmp_path / "solution.py").write_text(code_text, encoding="utf-8")
		test_driver = (
			"import sys, json\n"
			"from solution import *\n"
			"success=True\n"
			"try:\n"
			"    # user-provided asserts\n"
			"    exec(\"" + test_text.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "\\\"") + "\")\n"
			"except Exception as e:\n"
			"    success=False\n"
			"    err=str(e)\n"
			"result={'success':success}\n"
			"if not success:\n"
			"    result['error']=err\n"
			"print(json.dumps(result))\n"
		)
		(tmp_path / "test_runner.py").write_text(test_driver, encoding="utf-8")

		# If coverage is requested, run via coverage subprocess directly
		if with_coverage:
			try:
				import subprocess
				# Run test under coverage
				p = subprocess.run([sys.executable, "-m", "coverage", "run", str(tmp_path / "test_runner.py")], capture_output=True, text=True, timeout=timeout_sec)
				cov = None
				try:
					# Generate JSON and parse percent_covered if available
					subprocess.run([sys.executable, "-m", "coverage", "json", "-o", str(tmp_path / "coverage.json")], capture_output=True, text=True, timeout=5)
					import json as _json
					cov_obj = _json.loads((tmp_path / "coverage.json").read_text(encoding="utf-8"))
					totals = cov_obj.get("totals") or {}
					cov = float(totals.get("percent_covered", totals.get("percent_covered_display", 0.0)))
				except Exception:
					cov = None
				ok = p.returncode == 0
				out = (p.stdout or "").strip().splitlines()
				last = out[-1] if out else ""
				try:
					res = json.loads(last) if last.startswith("{") else {"success": ok}
				except Exception:
					res = {"success": ok}
				if cov is not None:
					res["coverage"] = cov
				return res
			except Exception as e:
				return {"success": False, "error": str(e)}

		pid = os.fork()
		if pid == 0:
			# Child: apply limits and exec
			try:
				set_limits(mem_mb)
				os.chdir(tmp_path)
				# Drop env that could be risky
				for k in list(os.environ.keys()):
					if k.upper().startswith("PYTHON") or k.upper().startswith("LD_"):
						os.environ.pop(k, None)
				os.execv(sys.executable, [sys.executable, "test_runner.py"])
			except Exception as e:
				print(json.dumps({"success": False, "error": str(e)}))
				os._exit(0)
		else:
			start = time.time()
			out = b""
			err = b""
			while True:
				pid_done, status = os.waitpid(pid, os.WNOHANG)
				if pid_done == pid:
					break
				if time.time() - start > timeout_sec:
					try:
						os.kill(pid, signal.SIGKILL)
					except Exception:
						pass
					return {"success": False, "timeout": True}
				time.sleep(0.01)
			# Read result from stdout file? We printed JSON to stdout; but we didn't capture pipes here.
			# As a simple workaround, re-run test_runner capturing output in a safe way within limits.
			# Fallback simple execution
			try:
				import subprocess
				p = subprocess.run([sys.executable, str(tmp_path / "test_runner.py")], capture_output=True, text=True, timeout=timeout_sec)
				if p.returncode == 0 and p.stdout.strip():
					return json.loads(p.stdout.strip().splitlines()[-1])
				return {"success": p.returncode == 0, "stdout": p.stdout[-500:], "stderr": p.stderr[-500:]}
			except Exception as e:
				return {"success": False, "error": str(e)}

def main() -> int:
	args = parse_args()
	code_text = Path(args.code).read_text(encoding="utf-8") if not args.literal else args.code
	test_text = Path(args.tests).read_text(encoding="utf-8") if not args.literal else args.tests
	res = run_in_sandbox(code_text, test_text, args.timeout, args.memmb, bool(args.coverage))
	print(json.dumps(res))
	return 0 if bool(res.get("success")) else 1

if __name__ == "__main__":
	raise SystemExit(main())


				if time.time() - start > timeout_sec:
					try:
						os.kill(pid, signal.SIGKILL)
					except Exception:
						pass
					return {"success": False, "timeout": True}
				time.sleep(0.01)
			# Read result from stdout file? We printed JSON to stdout; but we didn't capture pipes here.
			# As a simple workaround, re-run test_runner capturing output in a safe way within limits.
			# Fallback simple execution
			try:
				import subprocess
				p = subprocess.run([sys.executable, str(tmp_path / "test_runner.py")], capture_output=True, text=True, timeout=timeout_sec)
				if p.returncode == 0 and p.stdout.strip():
					return json.loads(p.stdout.strip().splitlines()[-1])
				return {"success": p.returncode == 0, "stdout": p.stdout[-500:], "stderr": p.stderr[-500:]}
			except Exception as e:
				return {"success": False, "error": str(e)}

def main() -> int:
	args = parse_args()
	code_text = Path(args.code).read_text(encoding="utf-8") if not args.literal else args.code
	test_text = Path(args.tests).read_text(encoding="utf-8") if not args.literal else args.tests
	res = run_in_sandbox(code_text, test_text, args.timeout, args.memmb, bool(args.coverage))
	print(json.dumps(res))
	return 0 if bool(res.get("success")) else 1

if __name__ == "__main__":
	raise SystemExit(main())


from __future__ import annotations
import logging

import argparse
import json
import statistics
import subprocess
import sys
import time
from pathlib import Path
from typing import Any, Dict, List

def _run(cmd: List[str], cwd: str | None = None, timeout: int = 1800) -> Dict[str, Any]:
	t0 = time.perf_counter()
	try:
		p = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout)
		dt = time.perf_counter() - t0
		return {
			"ok": (p.returncode == 0),
			"returncode": int(p.returncode),
			"stdout": p.stdout,
			"stderr": p.stderr,
			"elapsed_sec": float(round(dt, 3)),
		}
	except subprocess.TimeoutExpired as e:
		dt = time.perf_counter() - t0
		return {
			"ok": False,
			"returncode": 124,
			"stdout": getattr(e, "stdout", "") or "",
			"stderr": getattr(e, "stderr", "") or "timeout",
			"elapsed_sec": float(round(dt, 3)),
		}

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[2]
	ap.add_argument("--repos", nargs="*", default=[] , help="List of repo paths: local:/abs/path form is supported")
	ap.add_argument("--repos-file", default="", help="Path to a file containing newline-separated repo specs")
	ap.add_argument("--llm-model", default="meta-llama/Llama-3.2-3B")
	ap.add_argument("--setup", action="store_true")
	ap.add_argument("--sr-threshold", type=float, default=0.6, help="Success rate threshold")
	ap.add_argument("--p90-threshold", type=float, default=600.0, help="p90 elapsed seconds threshold")
	ap.add_argument("--timeout", type=int, default=1200)
	ap.add_argument("--out", default=str(root / "data" / "ci" / "devloop_batch.json"))
	args, extra = ap.parse_known_args()

	runners: List[Dict[str, Any]] = []
	sr_hits = 0
	elapsed: List[float] = []
	outp = Path(args.out)
	outp.parent.mkdir(parents=True, exist_ok=True)

	repos = list(args.repos or [])
	# Load repos from file if provided
	if str(getattr(args, "repos_file", "") or "").strip():
		rf = Path(args.repos_file)
		if rf.exists():
			for line in rf.read_text(encoding="utf-8").splitlines():
				s = line.strip()
				if s and not s.startswith("#"):
					repos.append(s)
	if not repos:
		print(json.dumps({"ok": False, "error": "no repos provided"}))
		return 2

	for repo in repos:
		cmd = [
			sys.executable,
			str(root / "scripts" / "loops" / "run_loop_dev.py"),
			"--repo", repo,
			"--llm-model", args.llm_model,
			"--out", str(root / "data" / "traces" / "dev_loop.ci.jsonl"),
		]
		if args.setup:
			cmd.append("--setup")
		if extra:
			cmd.extend(list(extra))
		res = _run(cmd, cwd=str(root), timeout=int(args.timeout))
		ok = bool(res.get("ok"))
		runners.append({"repo": repo, **res, "ok": ok})
		sr_hits += int(ok)
		elapsed.append(float(res.get("elapsed_sec", 0.0) or 0.0))

	sr = float(sr_hits / max(1, len(runners)))
	p90 = float(sorted(elapsed)[int(min(len(elapsed) - 1, max(0, round(0.9 * (len(elapsed) - 1)))))]) if elapsed else 0.0
	summary = {"n": int(len(runners)), "sr": float(round(sr, 4)), "p90_sec": float(round(p90, 3))}
	pack = {"summary": summary, "runs": runners}
	outp.write_text(json.dumps(pack, ensure_ascii=False, indent=2), encoding="utf-8")
	print(json.dumps({"ok": True, **summary, "out": str(outp)}))

	ok_all = (sr >= float(args.sr_threshold)) and (p90 <= float(args.p90_threshold))
	return 0 if ok_all else 3

if __name__ == "__main__":
	raise SystemExit(main())


from __future__ import annotations
import logging

import argparse
import json
import statistics
import subprocess
import sys
import time
from pathlib import Path
from typing import Any, Dict, List

def _run(cmd: List[str], cwd: str | None = None, timeout: int = 1800) -> Dict[str, Any]:
	t0 = time.perf_counter()
	try:
		p = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout)
		dt = time.perf_counter() - t0
		return {
			"ok": (p.returncode == 0),
			"returncode": int(p.returncode),
			"stdout": p.stdout,
			"stderr": p.stderr,
			"elapsed_sec": float(round(dt, 3)),
		}
	except subprocess.TimeoutExpired as e:
		dt = time.perf_counter() - t0
		return {
			"ok": False,
			"returncode": 124,
			"stdout": getattr(e, "stdout", "") or "",
			"stderr": getattr(e, "stderr", "") or "timeout",
			"elapsed_sec": float(round(dt, 3)),
		}

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[2]
	ap.add_argument("--repos", nargs="*", default=[] , help="List of repo paths: local:/abs/path form is supported")
	ap.add_argument("--repos-file", default="", help="Path to a file containing newline-separated repo specs")
	ap.add_argument("--llm-model", default="meta-llama/Llama-3.2-3B")
	ap.add_argument("--setup", action="store_true")
	ap.add_argument("--sr-threshold", type=float, default=0.6, help="Success rate threshold")
	ap.add_argument("--p90-threshold", type=float, default=600.0, help="p90 elapsed seconds threshold")
	ap.add_argument("--timeout", type=int, default=1200)
	ap.add_argument("--out", default=str(root / "data" / "ci" / "devloop_batch.json"))
	args, extra = ap.parse_known_args()

	runners: List[Dict[str, Any]] = []
	sr_hits = 0
	elapsed: List[float] = []
	outp = Path(args.out)
	outp.parent.mkdir(parents=True, exist_ok=True)


		p = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout)
		dt = time.perf_counter() - t0
		return {
			"ok": (p.returncode == 0),
			"returncode": int(p.returncode),
			"stdout": p.stdout,
			"stderr": p.stderr,
			"elapsed_sec": float(round(dt, 3)),
		}
	except subprocess.TimeoutExpired as e:
		dt = time.perf_counter() - t0
		return {
			"ok": False,
			"returncode": 124,
			"stdout": getattr(e, "stdout", "") or "",
			"stderr": getattr(e, "stderr", "") or "timeout",
			"elapsed_sec": float(round(dt, 3)),
		}

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[2]
	ap.add_argument("--repos", nargs="*", default=[] , help="List of repo paths: local:/abs/path form is supported")
	ap.add_argument("--repos-file", default="", help="Path to a file containing newline-separated repo specs")
	ap.add_argument("--llm-model", default="meta-llama/Llama-3.2-3B")
	ap.add_argument("--setup", action="store_true")
	ap.add_argument("--sr-threshold", type=float, default=0.6, help="Success rate threshold")
	ap.add_argument("--p90-threshold", type=float, default=600.0, help="p90 elapsed seconds threshold")
	ap.add_argument("--timeout", type=int, default=1200)
	ap.add_argument("--out", default=str(root / "data" / "ci" / "devloop_batch.json"))
	args, extra = ap.parse_known_args()

	runners: List[Dict[str, Any]] = []
	sr_hits = 0
	elapsed: List[float] = []
	outp = Path(args.out)
	outp.parent.mkdir(parents=True, exist_ok=True)

	repos = list(args.repos or [])
	# Load repos from file if provided
	if str(getattr(args, "repos_file", "") or "").strip():
		rf = Path(args.repos_file)
		if rf.exists():
			for line in rf.read_text(encoding="utf-8").splitlines():
				s = line.strip()
				if s and not s.startswith("#"):
					repos.append(s)
	if not repos:
		print(json.dumps({"ok": False, "error": "no repos provided"}))
		return 2

	for repo in repos:
		cmd = [
			sys.executable,
			str(root / "scripts" / "loops" / "run_loop_dev.py"),
			"--repo", repo,
			"--llm-model", args.llm_model,
			"--out", str(root / "data" / "traces" / "dev_loop.ci.jsonl"),
		]
		if args.setup:
			cmd.append("--setup")
		if extra:
			cmd.extend(list(extra))
		res = _run(cmd, cwd=str(root), timeout=int(args.timeout))
		ok = bool(res.get("ok"))
		runners.append({"repo": repo, **res, "ok": ok})
		sr_hits += int(ok)
		elapsed.append(float(res.get("elapsed_sec", 0.0) or 0.0))

	sr = float(sr_hits / max(1, len(runners)))
	p90 = float(sorted(elapsed)[int(min(len(elapsed) - 1, max(0, round(0.9 * (len(elapsed) - 1)))))]) if elapsed else 0.0
	summary = {"n": int(len(runners)), "sr": float(round(sr, 4)), "p90_sec": float(round(p90, 3))}
	pack = {"summary": summary, "runs": runners}
	outp.write_text(json.dumps(pack, ensure_ascii=False, indent=2), encoding="utf-8")
	print(json.dumps({"ok": True, **summary, "out": str(outp)}))

	ok_all = (sr >= float(args.sr_threshold)) and (p90 <= float(args.p90_threshold))
	return 0 if ok_all else 3

if __name__ == "__main__":
	raise SystemExit(main())


import logging
import argparse
import subprocess
import sys
import time
from pathlib import Path

def _run_cmd(cmd):
	proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
	try:
		for line in iter(proc.stdout.readline, ""):
			if not line:
				break
			print(line.rstrip())
		proc.wait()
		return proc.returncode
	except KeyboardInterrupt:
		try:
			proc.terminate()
		except Exception:
			pass
		raise

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[1]
	ap.add_argument("--mode", choices=["oscli", "webdom", "both"], default="oscli")
	ap.add_argument("--hf-model", default="meta-llama/Llama-3.2-3B")
	ap.add_argument("--timeout", type=int, default=60)
	ap.add_argument("--sleep", type=int, default=2, help="Seconds to sleep between iterations")
	ap.add_argument("--cycles", type=int, default=-1, help="Number of iterations (-1 = infinite)")
	ap.add_argument("--use-memory", action="store_true")
	ap.add_argument("--rebuild-memory-every", type=int, default=0, help="Rebuild memory every N iterations (0=never)")
	# OS/CLI specifics
	ap.add_argument("--oscli-task", choices=["count_lines", "grep_error"], default="count_lines")
	ap.add_argument("--router", action="store_true", help="Use router actuator for OS/CLI")
	ap.add_argument("--log-router", action="store_true")
	ap.add_argument("--wm-prior", action="store_true")
	ap.add_argument("--calibrate-verifier", action="store_true")
	# Web DOM specifics
	ap.add_argument("--dom-url", default="https://example.com")
	ap.add_argument("--dom-selector", default="h1")
	ap.add_argument("--dom-auto-t5", action="store_true")
	args = ap.parse_args()

	iter_idx = 0
	try:
		while True:
			if args.cycles > 0 and iter_idx >= args.cycles:
				break
			# Optional memory rebuild
			if args.use-memory and args.rebuild_memory_every and (iter_idx % max(1, int(args.rebuild_memory_every)) == 0):
				print("[dev] Rebuilding episodic memory index...")
				code = _run_cmd([sys.executable, str(root / "scripts" / "build_memory.py")])
				if code != 0:
					print("[dev] build_memory returned non-zero", file=sys.stderr)
			# Run OS/CLI loop
			if args.mode in ("oscli", "both"):
				cmd = [
					sys.executable,
					str(root / "scripts" / "run_loop_oscli.py"),
					"--planner-backend", "hf",
					"--verifier-backend", "hf",
					"--planner-model", args.hf_model,
					"--verifier-model", args.hf_model,
					"--timeout", str(int(args.timeout)),
					"--task", args.oscli_task,
				]
				if args.use-memory:
					cmd.append("--use-memory")
				if args.router:
					cmd.extend(["--actuator", "router", "--learned-router"])
				if args.log-router:
					cmd.append("--log-router")
				if args.wm_prior:
					cmd.append("--wm-prior")
				if args.calibrate_verifier:
					cmd.append("--calibrate-verifier")
				print("[dev] Running OS/CLI loop:", " ".join(cmd))
				_ = _run_cmd(cmd)
			# Run Web DOM loop
			if args.mode in ("webdom", "both"):
				cmd = [
					sys.executable,
					str(root / "scripts" / "run_loop_webdom.py"),
					"--planner-backend", "hf",
					"--verifier-backend", "hf",
					"--planner-model", args.hf_model,
					"--verifier-model", args.hf_model,
					"--timeout", str(int(args.timeout)),
					"--url", args.dom_url,
					"--selector", args.dom_selector,
				]
				if args.use-memory:
					cmd.append("--use-memory")
				if args.dom_auto_t5:
					cmd.append("--auto-t5")
				print("[dev] Running Web DOM loop:", " ".join(cmd))
				_ = _run_cmd(cmd)
			iter_idx += 1
			if args.sleep > 0:
				time.sleep(float(args.sleep))
		return 0
	except KeyboardInterrupt:
		print("[dev] Stopped by user")
		return 0

if __name__ == "__main__":
	raise SystemExit(main())


import logging
import argparse
import subprocess
import sys
import time
from pathlib import Path

def _run_cmd(cmd):
	proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
	try:
		for line in iter(proc.stdout.readline, ""):
			if not line:
				break
			print(line.rstrip())
		proc.wait()
		return proc.returncode
	except KeyboardInterrupt:
		try:
			proc.terminate()
		except Exception:
			pass
		raise

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[1]
	ap.add_argument("--mode", choices=["oscli", "webdom", "both"], default="oscli")
	ap.add_argument("--hf-model", default="meta-llama/Llama-3.2-3B")
	ap.add_argument("--timeout", type=int, default=60)
	ap.add_argument("--sleep", type=int, default=2, help="Seconds to sleep between iterations")
	ap.add_argument("--cycles", type=int, default=-1, help="Number of iterations (-1 = infinite)")
	ap.add_argument("--use-memory", action="store_true")
	ap.add_argument("--rebuild-memory-every", type=int, default=0, help="Rebuild memory every N iterations (0=never)")
	# OS/CLI specifics
	ap.add_argument("--oscli-task", choices=["count_lines", "grep_error"], default="count_lines")
	ap.add_argument("--router", action="store_true", help="Use router actuator for OS/CLI")
	ap.add_argument("--log-router", action="store_true")
	ap.add_argument("--wm-prior", action="store_true")
	ap.add_argument("--calibrate-verifier", action="store_true")
	# Web DOM specifics
	ap.add_argument("--dom-url", default="https://example.com")


from pathlib import Path

def _run_cmd(cmd):
	proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
	try:
		for line in iter(proc.stdout.readline, ""):
			if not line:
				break
			print(line.rstrip())
		proc.wait()
		return proc.returncode
	except KeyboardInterrupt:
		try:
			proc.terminate()
		except Exception:
			pass
		raise

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[1]
	ap.add_argument("--mode", choices=["oscli", "webdom", "both"], default="oscli")
	ap.add_argument("--hf-model", default="meta-llama/Llama-3.2-3B")
	ap.add_argument("--timeout", type=int, default=60)
	ap.add_argument("--sleep", type=int, default=2, help="Seconds to sleep between iterations")
	ap.add_argument("--cycles", type=int, default=-1, help="Number of iterations (-1 = infinite)")
	ap.add_argument("--use-memory", action="store_true")
	ap.add_argument("--rebuild-memory-every", type=int, default=0, help="Rebuild memory every N iterations (0=never)")
	# OS/CLI specifics
	ap.add_argument("--oscli-task", choices=["count_lines", "grep_error"], default="count_lines")
	ap.add_argument("--router", action="store_true", help="Use router actuator for OS/CLI")
	ap.add_argument("--log-router", action="store_true")
	ap.add_argument("--wm-prior", action="store_true")
	ap.add_argument("--calibrate-verifier", action="store_true")
	# Web DOM specifics
	ap.add_argument("--dom-url", default="https://example.com")
	ap.add_argument("--dom-selector", default="h1")
	ap.add_argument("--dom-auto-t5", action="store_true")
	args = ap.parse_args()

	iter_idx = 0
	try:
		while True:
			if args.cycles > 0 and iter_idx >= args.cycles:
				break
			# Optional memory rebuild
			if args.use-memory and args.rebuild_memory_every and (iter_idx % max(1, int(args.rebuild_memory_every)) == 0):
				print("[dev] Rebuilding episodic memory index...")
				code = _run_cmd([sys.executable, str(root / "scripts" / "build_memory.py")])
				if code != 0:
					print("[dev] build_memory returned non-zero", file=sys.stderr)
			# Run OS/CLI loop
			if args.mode in ("oscli", "both"):
				cmd = [
					sys.executable,
					str(root / "scripts" / "run_loop_oscli.py"),
					"--planner-backend", "hf",
					"--verifier-backend", "hf",
					"--planner-model", args.hf_model,
					"--verifier-model", args.hf_model,
					"--timeout", str(int(args.timeout)),
					"--task", args.oscli_task,
				]
				if args.use-memory:
					cmd.append("--use-memory")
				if args.router:
					cmd.extend(["--actuator", "router", "--learned-router"])
				if args.log-router:
					cmd.append("--log-router")
				if args.wm_prior:
					cmd.append("--wm-prior")
				if args.calibrate_verifier:
					cmd.append("--calibrate-verifier")
				print("[dev] Running OS/CLI loop:", " ".join(cmd))
				_ = _run_cmd(cmd)
			# Run Web DOM loop
			if args.mode in ("webdom", "both"):
				cmd = [
					sys.executable,
					str(root / "scripts" / "run_loop_webdom.py"),
					"--planner-backend", "hf",
					"--verifier-backend", "hf",
					"--planner-model", args.hf_model,
					"--verifier-model", args.hf_model,
					"--timeout", str(int(args.timeout)),
					"--url", args.dom_url,
					"--selector", args.dom_selector,
				]
				if args.use-memory:
					cmd.append("--use-memory")
				if args.dom_auto_t5:
					cmd.append("--auto-t5")
				print("[dev] Running Web DOM loop:", " ".join(cmd))
				_ = _run_cmd(cmd)
			iter_idx += 1
			if args.sleep > 0:
				time.sleep(float(args.sleep))
		return 0
	except KeyboardInterrupt:
		print("[dev] Stopped by user")
		return 0

if __name__ == "__main__":
	raise SystemExit(main())


from __future__ import annotations
import logging

import argparse
import json
import subprocess
from pathlib import Path

def git(cmd: list[str]) -> str:
	return subprocess.check_output(["git", *cmd], text=True)

def main() -> int:
	ap = argparse.ArgumentParser(description="Export DRY refactor dataset from git diffs")
	ap.add_argument("--out", default="/data/agiattempt/agi_dw/data/dry_refactors.jsonl")
	ap.add_argument("--since", default="HEAD~20", help="git rev range start (e.g., HEAD~20)")
	ap.add_argument("--until", default="HEAD", help="git rev range end (e.g., HEAD)")
	args = ap.parse_args()

	root = Path(__file__).resolve().parents[2]
	rng = f"{args.since}..{args.until}"
	files = git(["diff", "--name-only", rng]).splitlines()
	outp = Path(args.out)
	outp.parent.mkdir(parents=True, exist_ok=True)
	with outp.open("w", encoding="utf-8") as f:
		for p in files:
			if not p.endswith(".py"):
				continue
			# Only include files available in both revisions
			try:
				git(["cat-file", "-e", f"{args.since}:{p}"])  # verify exists
				git(["cat-file", "-e", f"{args.until}:{p}"])  # verify exists
				before = git(["show", f"{args.since}:{p}"])
				after = git(["show", f"{args.until}:{p}"])
			except Exception:
				continue
			record = {
				"path": p,
				"before_text": before,
				"after_text": after,
				"rationale": "DRY refactor: centralize helpers, use common flags",
				"helpers_used": [
					"ensure_safe_env",
					"strip_fences",
					"precheck_code",
					"retry_with_backoff",
					"add_common_bench_args",
				],
				"tags": ["dry", "bench_utils", "refactor"],
			}
			f.write(json.dumps(record) + "\n")
	print(json.dumps({"ok": True, "out": str(outp)}))
	return 0

if __name__ == "__main__":
	raise SystemExit(main())


git

from __future__ import annotations
import logging

import argparse
import json
import subprocess
from pathlib import Path

def git(cmd: list[str]) -> str:
	return subprocess.check_output(["git", *cmd], text=True)

def main() -> int:
	ap = argparse.ArgumentParser(description="Export DRY refactor dataset from git diffs")
	ap.add_argument("--out", default="/data/agiattempt/agi_dw/data/dry_refactors.jsonl")
	ap.add_argument("--since", default="HEAD~20", help="git rev range start (e.g., HEAD~20)")
	ap.add_argument("--until", default="HEAD", help="git rev range end (e.g., HEAD)")
	args = ap.parse_args()

	root = Path(__file__).resolve().parents[2]
	rng = f"{args.since}..{args.until}"
	files = git(["diff", "--name-only", rng]).splitlines()
	outp = Path(args.out)
	outp.parent.mkdir(parents=True, exist_ok=True)
	with outp.open("w", encoding="utf-8") as f:
		for p in files:
			if not p.endswith(".py"):
				continue
			# Only include files available in both revisions
			try:


import logging
import argparse
import json
import os
import re
import subprocess
import shlex
import shutil
from pathlib import Path

from agi_dw.tools.git import GitTool
from agi_dw.tools.test_runner import TestRunner

def run(cmd: list[str], cwd: Path, timeout: int = 900, env: dict | None = None) -> subprocess.CompletedProcess:
	return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, timeout=timeout, env=env)

def maybe_install_requirements(repo_dir: Path, timeout: int = 900) -> None:
	req = repo_dir / "requirements.txt"
	if req.exists():
		try:
			run(["python3", "-m", "pip", "install", "-r", str(req)], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass
	dev_req = repo_dir / "requirements-dev.txt"
	if dev_req.exists():
		try:
			run(["python3", "-m", "pip", "install", "-r", str(dev_req)], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass

def maybe_install_editable(repo_dir: Path, timeout: int = 900) -> None:
	# If it's a Python package (pyproject.toml or setup.py), try editable install
	if (repo_dir / "pyproject.toml").exists() or (repo_dir / "setup.py").exists():
		try:
			run(["python3", "-m", "pip", "install", "-e", "."], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass

def maybe_install_test_extras(repo_dir: Path, timeout: int = 900) -> None:
	# Best-effort install of common extras to satisfy tests
	if (repo_dir / "pyproject.toml").exists() or (repo_dir / "setup.cfg").exists() or (repo_dir / "setup.py").exists():
		for extra in ("tests", "test", "dev"):
			try:
				run(["python3", "-m", "pip", "install", f".[{extra}]"], cwd=repo_dir, timeout=timeout)
			except Exception:
				continue

def ensure_pytest(repo_dir: Path, timeout: int = 900) -> None:
	chk = run(["python3", "-c", "import pytest; print(pytest.__version__)"] , cwd=repo_dir, timeout=timeout)
	if chk.returncode != 0:
		try:
			run(["python3", "-m", "pip", "install", "pytest"], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass

def maybe_npm_install(repo_dir: Path, timeout: int = 900) -> bool:
	if (repo_dir / "package.json").exists():
		try:
			# prefer ci if lockfile present
			if (repo_dir / "package-lock.json").exists():
				run(["npm", "ci"], cwd=repo_dir, timeout=timeout)
			else:
				run(["npm", "install"], cwd=repo_dir, timeout=timeout)
			return True
		except Exception:
			return False
	return False

def tail(s: str, n: int = 200) -> str:
	lines = s.strip().splitlines()
	return "\n".join(lines[-n:])

def main() -> int:
	ap = argparse.ArgumentParser()
	root = Path(__file__).resolve().parents[1]
	ap.add_argument("--repo", required=True)
	ap.add_argument("--branch", default=None)
	ap.add_argument("--dir", default=str(root / "data" / "sandbox" / "dev_repo"))
	ap.add_argument("--pytest-args", default="")
	args = ap.parse_args()

	repo_root = Path(args.dir)
	repo_root.parent.mkdir(parents=True, exist_ok=True)
	git = GitTool(str(repo_root))

	# Populate repo dir
	if not any(repo_root.iterdir()) and args.repo.startswith("local:"):
		src = Path(args.repo.split("local:", 1)[1]).resolve()
		try:
			shutil.copytree(src, repo_root, dirs_exist_ok=True)
		except Exception as e:
			print(json.dumps({"stage": "copy", "ok": False, "error": str(e)}))
			return 1
	elif not any(repo_root.iterdir()) and not args.repo.startswith("local:"):
		cl = git.clone(args.repo, str(repo_root))
		if cl.returncode != 0:
			print(json.dumps({"stage": "clone", "ok": False, "stderr": cl.stderr}))
			return 1

	# Checkout branch if requested
	if args.branch:
		co = git.checkout(args.branch)
		if co.returncode != 0:
			print(json.dumps({"stage": "checkout", "ok": False, "stderr": co.stderr}))
			return 1

	# Install dependencies
	maybe_install_requirements(repo_root)
	maybe_install_editable(repo_root)
	maybe_install_test_extras(repo_root)
	used_npm = maybe_npm_install(repo_root)

	# Run tests
	if used_npm:
		# Prefer npm test if JS project
		p = run(["npm", "test", "--silent"], cwd=repo_root, timeout=1200)
		ok = (p.returncode == 0)
		print(json.dumps({"stage": "npm test", "ok": ok, "returncode": p.returncode, "stderr_tail": tail(p.stderr), "stdout_tail": tail(p.stdout)}))
		return 0 if ok else 1
	else:
		ensure_pytest(repo_root)
		# Ensure local repo is preferred on import
		env = os.environ.copy()
		env["PYTHONPATH"] = str(repo_root) + (":" + env["PYTHONPATH"] if env.get("PYTHONPATH") else "")
		pytest_cmd = ["pytest", "-q"]
		if args.pytest_args:
			pytest_cmd += shlex.split(args.pytest_args)
		else:
			pytest_cmd += ["-k", "not validators and not version_info and not make and not benchmark and not slow"]
		p = run(pytest_cmd, cwd=repo_root, timeout=1200, env=env)
		if p.returncode != 0:
			# One retry: attempt to install missing module if ModuleNotFoundError is present
			m = re.search(r"ModuleNotFoundError: No module named '([^']+)'", p.stdout or "")
			if m:
				missing = m.group(1)
				try:
					run(["python3", "-m", "pip", "install", missing], cwd=repo_root, timeout=600)
					p = run(pytest_cmd, cwd=repo_root, timeout=1200, env=env)
				except Exception:
					pass
		ok = (p.returncode == 0)
		print(json.dumps({
			"stage": "pytest",
			"ok": ok,
			"returncode": p.returncode,
			"stderr_tail": tail(p.stderr),
			"stdout_tail": tail(p.stdout),
		}))
		return 0 if ok else 1

if __name__ == "__main__":
	raise SystemExit(main())


run

import logging
import argparse
import json
import os
import re
import subprocess
import shlex
import shutil
from pathlib import Path

from agi_dw.tools.git import GitTool
from agi_dw.tools.test_runner import TestRunner

def run(cmd: list[str], cwd: Path, timeout: int = 900, env: dict | None = None) -> subprocess.CompletedProcess:
	return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, timeout=timeout, env=env)

def maybe_install_requirements(repo_dir: Path, timeout: int = 900) -> None:
	req = repo_dir / "requirements.txt"
	if req.exists():
		try:
			run(["python3", "-m", "pip", "install", "-r", str(req)], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass
	dev_req = repo_dir / "requirements-dev.txt"
	if dev_req.exists():
		try:
			run(["python3", "-m", "pip", "install", "-r", str(dev_req)], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass

def maybe_install_editable(repo_dir: Path, timeout: int = 900) -> None:
	# If it's a Python package (pyproject.toml or setup.py), try editable install
	if (repo_dir / "pyproject.toml").exists() or (repo_dir / "setup.py").exists():


import logging
import argparse
import json
import os
import re
import subprocess
import shlex
import shutil
from pathlib import Path

from agi_dw.tools.git import GitTool
from agi_dw.tools.test_runner import TestRunner

def run(cmd: list[str], cwd: Path, timeout: int = 900, env: dict | None = None) -> subprocess.CompletedProcess:
	return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, timeout=timeout, env=env)

def maybe_install_requirements(repo_dir: Path, timeout: int = 900) -> None:
	req = repo_dir / "requirements.txt"
	if req.exists():
		try:
			run(["python3", "-m", "pip", "install", "-r", str(req)], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass
	dev_req = repo_dir / "requirements-dev.txt"
	if dev_req.exists():
		try:
			run(["python3", "-m", "pip", "install", "-r", str(dev_req)], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass

def maybe_install_editable(repo_dir: Path, timeout: int = 900) -> None:
	# If it's a Python package (pyproject.toml or setup.py), try editable install
	if (repo_dir / "pyproject.toml").exists() or (repo_dir / "setup.py").exists():
		try:
			run(["python3", "-m", "pip", "install", "-e", "."], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass

def maybe_install_test_extras(repo_dir: Path, timeout: int = 900) -> None:
	# Best-effort install of common extras to satisfy tests
	if (repo_dir / "pyproject.toml").exists() or (repo_dir / "setup.cfg").exists() or (repo_dir / "setup.py").exists():
		for extra in ("tests", "test", "dev"):
			try:
				run(["python3", "-m", "pip", "install", f".[{extra}]"], cwd=repo_dir, timeout=timeout)
			except Exception:
				continue


def run(cmd: list[str], cwd: Path, timeout: int = 900, env: dict | None = None) -> subprocess.CompletedProcess:
	return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, timeout=timeout, env=env)

def maybe_install_requirements(repo_dir: Path, timeout: int = 900) -> None:
	req = repo_dir / "requirements.txt"
	if req.exists():
		try:
			run(["python3", "-m", "pip", "install", "-r", str(req)], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass
	dev_req = repo_dir / "requirements-dev.txt"
	if dev_req.exists():
		try:
			run(["python3", "-m", "pip", "install", "-r", str(dev_req)], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass

def maybe_install_editable(repo_dir: Path, timeout: int = 900) -> None:
	# If it's a Python package (pyproject.toml or setup.py), try editable install
	if (repo_dir / "pyproject.toml").exists() or (repo_dir / "setup.py").exists():
		try:
			run(["python3", "-m", "pip", "install", "-e", "."], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass

def maybe_install_test_extras(repo_dir: Path, timeout: int = 900) -> None:
	# Best-effort install of common extras to satisfy tests
	if (repo_dir / "pyproject.toml").exists() or (repo_dir / "setup.cfg").exists() or (repo_dir / "setup.py").exists():
		for extra in ("tests", "test", "dev"):
			try:
				run(["python3", "-m", "pip", "install", f".[{extra}]"], cwd=repo_dir, timeout=timeout)
			except Exception:
				continue

def ensure_pytest(repo_dir: Path, timeout: int = 900) -> None:
	chk = run(["python3", "-c", "import pytest; print(pytest.__version__)"] , cwd=repo_dir, timeout=timeout)
	if chk.returncode != 0:
		try:
			run(["python3", "-m", "pip", "install", "pytest"], cwd=repo_dir, timeout=timeout)
		except Exception:
			pass