ltcai 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/tools.py CHANGED
@@ -17,7 +17,7 @@ import tempfile
17
17
  import json
18
18
  from html.parser import HTMLParser
19
19
  from pathlib import Path
20
- from typing import Any, Dict, List, Optional
20
+ from typing import Any, Callable, Dict, List, Optional
21
21
 
22
22
  _PLATFORM = platform.system() # "Darwin" | "Windows" | "Linux"
23
23
 
@@ -1435,118 +1435,90 @@ def git_show(revision: str = "HEAD", cwd: Optional[str] = None) -> Dict[str, Any
1435
1435
  return _run_git(["show", "--stat", "--oneline", "--decorate", revision], cwd)
1436
1436
 
1437
1437
 
1438
+ def _h_create_xlsx(args: Dict[str, Any]) -> Dict[str, Any]:
1439
+ rows = args.get("rows", [])
1440
+ if isinstance(rows, str):
1441
+ rows = json.loads(rows)
1442
+ return create_xlsx(rows, args.get("filename", "spreadsheet.xlsx"), args.get("sheet_name", "Sheet1"))
1443
+
1444
+
1445
+ def _h_create_pptx(args: Dict[str, Any]) -> Dict[str, Any]:
1446
+ slides = args.get("slides", [])
1447
+ if isinstance(slides, str):
1448
+ slides = json.loads(slides)
1449
+ return create_pptx(args.get("title", ""), slides, args.get("filename", "presentation.pptx"))
1450
+
1451
+
1452
+ # ── Tool registry: the single source of truth for name → invocation ───────────
1453
+ # Each entry binds the args dict to a tool function. ``execute_tool`` is a
1454
+ # lookup over this table — adding a tool means adding one entry here, not
1455
+ # editing an if/elif chain. server.py's governance map and catalog brief are
1456
+ # checked against ``registered_tools()`` so the three never silently drift.
1457
+ TOOL_HANDLERS: Dict[str, Callable[[Dict[str, Any]], Dict[str, Any]]] = {
1458
+ # filesystem
1459
+ "list_dir": lambda a: list_dir(a.get("path", ".")),
1460
+ "workspace_tree": lambda a: workspace_tree(a.get("path", "."), a.get("max_depth", 3)),
1461
+ "read_file": lambda a: read_file(a["path"], offset=a.get("offset", 0), limit=a.get("limit", 0), line_numbers=a.get("line_numbers", True)),
1462
+ "write_file": lambda a: write_file(a["path"], a.get("content", "")),
1463
+ "edit_file": lambda a: edit_file(a["path"], a["old_string"], a["new_string"], replace_all=bool(a.get("replace_all", False))),
1464
+ "grep": lambda a: grep(a["pattern"], path=a.get("path", "."), glob=a.get("glob"), max_results=a.get("max_results", 50), case_insensitive=bool(a.get("case_insensitive", False)), context_lines=a.get("context_lines", 0)),
1465
+ "search_files": lambda a: search_files(a["query"], a.get("path", "."), a.get("max_results", 20)),
1466
+ "inspect_html": lambda a: inspect_html(a["path"]),
1467
+ "preview_url": lambda a: preview_url(a.get("path", "index.html")),
1468
+ # planning
1469
+ "todo_read": lambda a: todo_read(),
1470
+ "todo_write": lambda a: todo_write(a.get("todos") or []),
1471
+ # documents
1472
+ "create_docx": lambda a: create_docx(a.get("title", ""), a.get("body", ""), a.get("filename", "document.docx")),
1473
+ "create_xlsx": _h_create_xlsx,
1474
+ "create_pptx": _h_create_pptx,
1475
+ "create_pdf": lambda a: create_pdf(a.get("title", ""), a.get("body", ""), a.get("filename", "document.pdf")),
1476
+ "create_web_project": lambda a: create_web_project(a.get("path", ""), a.get("framework", "react"), a.get("template", "vite")),
1477
+ # local filesystem
1478
+ "local_list": lambda a: local_list(a["path"]),
1479
+ "local_read": lambda a: local_read(a["path"]),
1480
+ "local_write": lambda a: local_write(a["path"], a.get("content", "")),
1481
+ "read_document": lambda a: read_document(a["path"]),
1482
+ "network_status": lambda a: network_status(),
1483
+ # computer use
1484
+ "computer_screenshot": lambda a: computer_screenshot(),
1485
+ "computer_open_app": lambda a: computer_open_app(a.get("app", "Google Chrome")),
1486
+ "computer_open_url": lambda a: computer_open_url(a["url"], a.get("app", "Google Chrome")),
1487
+ "computer_click": lambda a: computer_click(a.get("x", 0), a.get("y", 0), a.get("button", "left"), a.get("double", False)),
1488
+ "computer_type": lambda a: computer_type(a["text"], a.get("interval", 0.04)),
1489
+ "computer_key": lambda a: computer_key(a["key"]),
1490
+ "computer_scroll": lambda a: computer_scroll(a.get("x", 0), a.get("y", 0), a.get("direction", "down"), a.get("clicks", 3)),
1491
+ "computer_move": lambda a: computer_move(a.get("x", 0), a.get("y", 0)),
1492
+ "computer_drag": lambda a: computer_drag(a.get("x1", 0), a.get("y1", 0), a.get("x2", 0), a.get("y2", 0)),
1493
+ "computer_status": lambda a: computer_status(),
1494
+ "chrome_status": lambda a: desktop_bridge_status(),
1495
+ "computer_use_status": lambda a: desktop_bridge_status(),
1496
+ # knowledge / obsidian
1497
+ "knowledge_save": lambda a: knowledge_save(a["content"], a.get("folder", "00_Raw"), a.get("title")),
1498
+ "knowledge_search": lambda a: knowledge_search(a["query"], a.get("max_results", 5)),
1499
+ "knowledge_tree": lambda a: knowledge_tree(),
1500
+ "obsidian_save": lambda a: obsidian_save(a["content"], a.get("folder", "00_Raw"), a.get("title")),
1501
+ "obsidian_search": lambda a: obsidian_search(a["query"], a.get("max_results", 5)),
1502
+ "obsidian_tree": lambda a: obsidian_tree(),
1503
+ # git (read-only)
1504
+ "git_status": lambda a: git_status(a.get("cwd")),
1505
+ "git_diff": lambda a: git_diff(a.get("path"), a.get("cwd")),
1506
+ "git_log": lambda a: git_log(a.get("max_count", 5), a.get("cwd")),
1507
+ "git_show": lambda a: git_show(a.get("revision", "HEAD"), a.get("cwd")),
1508
+ # exec
1509
+ "run_command": lambda a: run_command(a["command"], a.get("cwd")),
1510
+ "build_project": lambda a: build_project(a.get("cwd"), a.get("script", "build")),
1511
+ "deploy_project": lambda a: deploy_project(a.get("cwd"), a.get("script", "deploy")),
1512
+ }
1513
+
1514
+
1515
+ def registered_tools() -> frozenset:
1516
+ """Names dispatchable through ``execute_tool`` — the seam other modules verify against."""
1517
+ return frozenset(TOOL_HANDLERS)
1518
+
1519
+
1438
1520
  def execute_tool(action: str, args: Dict[str, Any]) -> Dict[str, Any]:
1439
- if action == "list_dir":
1440
- return list_dir(args.get("path", "."))
1441
- if action == "workspace_tree":
1442
- return workspace_tree(args.get("path", "."), args.get("max_depth", 3))
1443
- if action == "read_file":
1444
- return read_file(
1445
- args["path"],
1446
- offset=args.get("offset", 0),
1447
- limit=args.get("limit", 0),
1448
- line_numbers=args.get("line_numbers", True),
1449
- )
1450
- if action == "write_file":
1451
- return write_file(args["path"], args.get("content", ""))
1452
- if action == "edit_file":
1453
- return edit_file(
1454
- args["path"],
1455
- args["old_string"],
1456
- args["new_string"],
1457
- replace_all=bool(args.get("replace_all", False)),
1458
- )
1459
- if action == "grep":
1460
- return grep(
1461
- args["pattern"],
1462
- path=args.get("path", "."),
1463
- glob=args.get("glob"),
1464
- max_results=args.get("max_results", 50),
1465
- case_insensitive=bool(args.get("case_insensitive", False)),
1466
- context_lines=args.get("context_lines", 0),
1467
- )
1468
- if action == "search_files":
1469
- return search_files(args["query"], args.get("path", "."), args.get("max_results", 20))
1470
- if action == "todo_read":
1471
- return todo_read()
1472
- if action == "todo_write":
1473
- return todo_write(args.get("todos") or [])
1474
- if action == "inspect_html":
1475
- return inspect_html(args["path"])
1476
- if action == "preview_url":
1477
- return preview_url(args.get("path", "index.html"))
1478
- if action == "create_docx":
1479
- return create_docx(args.get("title", ""), args.get("body", ""), args.get("filename", "document.docx"))
1480
- if action == "create_xlsx":
1481
- rows = args.get("rows", [])
1482
- if isinstance(rows, str):
1483
- rows = json.loads(rows)
1484
- return create_xlsx(rows, args.get("filename", "spreadsheet.xlsx"), args.get("sheet_name", "Sheet1"))
1485
- if action == "create_pptx":
1486
- slides = args.get("slides", [])
1487
- if isinstance(slides, str):
1488
- slides = json.loads(slides)
1489
- return create_pptx(args.get("title", ""), slides, args.get("filename", "presentation.pptx"))
1490
- if action == "create_pdf":
1491
- return create_pdf(args.get("title", ""), args.get("body", ""), args.get("filename", "document.pdf"))
1492
- if action == "create_web_project":
1493
- return create_web_project(args.get("path", ""), args.get("framework", "react"), args.get("template", "vite"))
1494
- if action == "local_list":
1495
- return local_list(args["path"])
1496
- if action == "local_read":
1497
- return local_read(args["path"])
1498
- if action == "local_write":
1499
- return local_write(args["path"], args.get("content", ""))
1500
- if action == "read_document":
1501
- return read_document(args["path"])
1502
- if action == "network_status":
1503
- return network_status()
1504
- if action == "computer_screenshot":
1505
- return computer_screenshot()
1506
- if action == "computer_open_app":
1507
- return computer_open_app(args.get("app", "Google Chrome"))
1508
- if action == "computer_open_url":
1509
- return computer_open_url(args["url"], args.get("app", "Google Chrome"))
1510
- if action == "computer_click":
1511
- return computer_click(args.get("x", 0), args.get("y", 0), args.get("button", "left"), args.get("double", False))
1512
- if action == "computer_type":
1513
- return computer_type(args["text"], args.get("interval", 0.04))
1514
- if action == "computer_key":
1515
- return computer_key(args["key"])
1516
- if action == "computer_scroll":
1517
- return computer_scroll(args.get("x", 0), args.get("y", 0), args.get("direction", "down"), args.get("clicks", 3))
1518
- if action == "computer_move":
1519
- return computer_move(args.get("x", 0), args.get("y", 0))
1520
- if action == "computer_drag":
1521
- return computer_drag(args.get("x1", 0), args.get("y1", 0), args.get("x2", 0), args.get("y2", 0))
1522
- if action == "computer_status":
1523
- return computer_status()
1524
- if action in {"chrome_status", "computer_use_status"}:
1525
- return desktop_bridge_status()
1526
- if action == "knowledge_save":
1527
- return knowledge_save(args["content"], args.get("folder", "00_Raw"), args.get("title"))
1528
- if action == "knowledge_search":
1529
- return knowledge_search(args["query"], args.get("max_results", 5))
1530
- if action == "knowledge_tree":
1531
- return knowledge_tree()
1532
- if action == "obsidian_save":
1533
- return obsidian_save(args["content"], args.get("folder", "00_Raw"), args.get("title"))
1534
- if action == "obsidian_search":
1535
- return obsidian_search(args["query"], args.get("max_results", 5))
1536
- if action == "obsidian_tree":
1537
- return obsidian_tree()
1538
- if action == "git_status":
1539
- return git_status(args.get("cwd"))
1540
- if action == "git_diff":
1541
- return git_diff(args.get("path"), args.get("cwd"))
1542
- if action == "git_log":
1543
- return git_log(args.get("max_count", 5), args.get("cwd"))
1544
- if action == "git_show":
1545
- return git_show(args.get("revision", "HEAD"), args.get("cwd"))
1546
- if action == "run_command":
1547
- return run_command(args["command"], args.get("cwd"))
1548
- if action == "build_project":
1549
- return build_project(args.get("cwd"), args.get("script", "build"))
1550
- if action == "deploy_project":
1551
- return deploy_project(args.get("cwd"), args.get("script", "deploy"))
1552
- raise ToolError(f"Unknown action: {action}")
1521
+ handler = TOOL_HANDLERS.get(action)
1522
+ if handler is None:
1523
+ raise ToolError(f"Unknown action: {action}")
1524
+ return handler(args)