Files
SecondBrain/99 System/Export/agent-tools/vault_ingest.py
T
Dmitry 3b597aad7d ada-pc: 2026-06-16 20:13:30 | 8
Affected files:
99 System/Export/agent-tools/AGENT_TOOLS.md
99 System/Export/agent-tools/vault_audit.py
99 System/Export/agent-tools/vault_common.py
99 System/Export/agent-tools/vault_frontmatter.py
99 System/Export/agent-tools/vault_index.py
99 System/Export/agent-tools/vault_ingest.py
99 System/INDEX.md
AGENTS.md
2026-06-16 20:13:30 +03:00

138 lines
4.7 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
from pathlib import Path
from vault_common import ROOT, all_records, json_print, rel, search_records, split_frontmatter, tokenize
INBOX_DIRS = [ROOT / "00 Inbox", ROOT / "01 Ideas"]
LIBRARY_HINTS = {
"ansible": "90 Library/DevOps/Ansible",
"ci": "90 Library/DevOps",
"cd": "90 Library/DevOps",
"cicd": "90 Library/DevOps",
"docker": "90 Library/Containers/Docker",
"git": "90 Library/DevOps",
"gitlab": "90 Library/DevOps",
"jenkins": "90 Library/DevOps",
"kubernetes": "90 Library/Containers/Kubernetes",
"linux": "90 Library/Linux",
"nginx": "90 Library/HomeLab/Proxy",
"rails": "90 Library/Programming/Ruby On Rails",
"ruby": "90 Library/Programming/Ruby",
"terraform": "90 Library/DevOps",
}
def read_path(path_arg: str) -> Path:
path = Path(path_arg)
if not path.is_absolute():
path = ROOT / path
return path
def scan() -> list[dict[str, object]]:
items = []
for directory in INBOX_DIRS:
if not directory.exists():
continue
for path in sorted(directory.iterdir(), key=lambda item: item.name.lower()):
if path.name == ".gitkeep" or not path.is_file():
continue
text = path.read_text(encoding="utf-8", errors="replace") if path.suffix == ".md" else ""
fm, body, _ = split_frontmatter(text)
items.append(
{
"path": rel(path),
"folder": rel(directory),
"suffix": path.suffix,
"title": fm.get("title") or path.stem,
"tags": fm.get("tags") or [],
"bytes": path.stat().st_size,
"classification": classify_text(path, body, fm),
}
)
return items
def classify_text(path: Path, body: str, frontmatter: dict[str, object]) -> dict[str, object]:
tags = frontmatter.get("tags") or []
if not isinstance(tags, list):
tags = [tags]
content = f"{path.stem}\n{' '.join(map(str, tags))}\n{body[:4000]}".lower()
tokens = tokenize(content)
idea_markers = {"идея", "idea", "business", "product", "стартап", "концепция", "задумка"}
if "01 Ideas" in rel(path) or tokens & idea_markers:
kind = "idea"
destination = "01 Ideas"
else:
kind = "library"
destination = "90 Library/Other"
for marker, folder in LIBRARY_HINTS.items():
if marker in content:
destination = folder
break
return {
"kind": kind,
"destination": destination,
"confidence": "heuristic",
}
def plan(path: Path) -> dict[str, object]:
text = path.read_text(encoding="utf-8", errors="replace")
fm, body, _ = split_frontmatter(text)
classification = classify_text(path, body, fm)
records = all_records()
terms = " ".join([path.stem, str(fm.get("title") or ""), " ".join(map(str, fm.get("tags") or []))])
similar = []
for item in search_records(records, terms, limit=30):
if item["path"] == rel(path):
continue
if item["path"].startswith(("03 Journal/", "99 System/Export/", "99 System/Cache/", "99 System/Template/")):
continue
similar.append(item)
if len(similar) == 8:
break
wikilinks = [item["name"] for item in similar[:5]]
return {
"path": rel(path),
"classification": classification,
"similar_notes": similar,
"suggested_wikilinks": wikilinks,
"write_policy": "read-only plan; agent must ask before workflow changes",
}
def main() -> int:
parser = argparse.ArgumentParser(description="Agent-facing ingest helper")
sub = parser.add_subparsers(dest="command", required=True)
sub.add_parser("scan", help="List files in 00 Inbox and 01 Ideas")
classify = sub.add_parser("classify", help="Classify one source file")
classify.add_argument("path")
plan_cmd = sub.add_parser("plan", help="Build JSON ingest plan for one source file")
plan_cmd.add_argument("path")
args = parser.parse_args()
if args.command == "scan":
json_print({"ok": True, "items": scan()})
elif args.command == "classify":
path = read_path(args.path)
text = path.read_text(encoding="utf-8", errors="replace")
fm, body, _ = split_frontmatter(text)
json_print({"ok": True, "path": rel(path), "classification": classify_text(path, body, fm)})
elif args.command == "plan":
json_print({"ok": True, "plan": plan(read_path(args.path))})
return 0
if __name__ == "__main__":
raise SystemExit(main())