diff --git a/README.md b/README.md index 678729a0..9f286ef5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ ## Table of Contents +- [What's new (2026-06-19) — Process-Doc (SOP) Generator](#whats-new-2026-06-19--process-doc-sop-generator) - [What's new (2026-06-19) — Heal Analytics & Secret Scan](#whats-new-2026-06-19--heal-analytics--secret-scan) - [What's new (2026-06-19) — CI Annotations & Clipboard History](#whats-new-2026-06-19--ci-annotations--clipboard-history) - [What's new (2026-06-19) — Resilience Primitives](#whats-new-2026-06-19--resilience-primitives) @@ -80,6 +81,12 @@ --- +## What's new (2026-06-19) — Process-Doc (SOP) Generator + +Turn an action list into a step-by-step SOP. Full reference: [`docs/source/Eng/doc/new_features/v28_features_doc.rst`](docs/source/Eng/doc/new_features/v28_features_doc.rst). + +- **`generate_sop` / `write_sop`** (`AC_generate_sop`, `ac_generate_sop`): map a recorded/authored action list to numbered, human-readable steps + an HTML document (UiPath Task-Capture deliverable); content HTML-escaped, unknown commands degrade gracefully. + ## What's new (2026-06-19) — Heal Analytics & Secret Scan Two pure-stdlib audit/analysis tools. Full reference: [`docs/source/Eng/doc/new_features/v27_features_doc.rst`](docs/source/Eng/doc/new_features/v27_features_doc.rst). diff --git a/README/README_zh-CN.md b/README/README_zh-CN.md index 2d1aeaac..9d5eb4dc 100644 --- a/README/README_zh-CN.md +++ b/README/README_zh-CN.md @@ -12,6 +12,7 @@ ## 目录 +- [本次更新 (2026-06-19) — 流程文档(SOP)生成器](#本次更新-2026-06-19--流程文档sop生成器) - [本次更新 (2026-06-19) — 修复分析与机密扫描](#本次更新-2026-06-19--修复分析与机密扫描) - [本次更新 (2026-06-19) — CI 注解与剪贴板历史](#本次更新-2026-06-19--ci-注解与剪贴板历史) - [本次更新 (2026-06-19) — 韧性原语](#本次更新-2026-06-19--韧性原语) @@ -79,6 +80,12 @@ --- +## 本次更新 (2026-06-19) — 流程文档(SOP)生成器 + +把动作列表转成逐步 SOP。完整参考:[`docs/source/Zh/doc/new_features/v28_features_doc.rst`](../docs/source/Zh/doc/new_features/v28_features_doc.rst)。 + +- **`generate_sop` / `write_sop`**(`AC_generate_sop`、`ac_generate_sop`):把录制/编写的动作列表映射成编号、人类可读步骤 + HTML 文档(UiPath Task-Capture 产出);内容 HTML 转义,未知指令优雅降级。 + ## 本次更新 (2026-06-19) — 修复分析与机密扫描 两项纯标准库的审计/分析工具。完整参考:[`docs/source/Zh/doc/new_features/v27_features_doc.rst`](../docs/source/Zh/doc/new_features/v27_features_doc.rst)。 diff --git a/README/README_zh-TW.md b/README/README_zh-TW.md index ed48e0fe..8113b876 100644 --- a/README/README_zh-TW.md +++ b/README/README_zh-TW.md @@ -12,6 +12,7 @@ ## 目錄 +- [本次更新 (2026-06-19) — 流程文件(SOP)產生器](#本次更新-2026-06-19--流程文件sop產生器) - [本次更新 (2026-06-19) — 修復分析與機密掃描](#本次更新-2026-06-19--修復分析與機密掃描) - [本次更新 (2026-06-19) — CI 註解與剪貼簿歷史](#本次更新-2026-06-19--ci-註解與剪貼簿歷史) - [本次更新 (2026-06-19) — 韌性原語](#本次更新-2026-06-19--韌性原語) @@ -79,6 +80,12 @@ --- +## 本次更新 (2026-06-19) — 流程文件(SOP)產生器 + +把動作清單轉成逐步 SOP。完整參考:[`docs/source/Zh/doc/new_features/v28_features_doc.rst`](../docs/source/Zh/doc/new_features/v28_features_doc.rst)。 + +- **`generate_sop` / `write_sop`**(`AC_generate_sop`、`ac_generate_sop`):把錄製/編寫的動作清單對應成編號、人類可讀步驟 + HTML 文件(UiPath Task-Capture 產出);內容 HTML 轉義,未知指令優雅降級。 + ## 本次更新 (2026-06-19) — 修復分析與機密掃描 兩項純標準庫的稽核/分析工具。完整參考:[`docs/source/Zh/doc/new_features/v27_features_doc.rst`](../docs/source/Zh/doc/new_features/v27_features_doc.rst)。 diff --git a/docs/source/Eng/doc/new_features/v28_features_doc.rst b/docs/source/Eng/doc/new_features/v28_features_doc.rst new file mode 100644 index 00000000..0ca1564f --- /dev/null +++ b/docs/source/Eng/doc/new_features/v28_features_doc.rst @@ -0,0 +1,32 @@ +================================================== +New Features (2026-06-19) — Process-Doc (SOP) Generator +================================================== + +Turn a recorded / authored action list into a numbered, human-readable +**standard operating procedure** — a structured step list plus an HTML +rendering (the UiPath Task-Capture deliverable) for runbooks and review. +Pure standard library; full stack. + +.. contents:: + :local: + :depth: 2 + + +Usage +===== + +:: + + from je_auto_control import generate_sop, write_sop + + doc = generate_sop(actions, title="Invoice Login") + doc["steps"] # [{n, command, description, args}, ...] + doc["html"] # full HTML document (content escaped) + write_sop(actions, "procedure.html", title="Invoice Login") + +Each action is mapped to a human verb phrase (``AC_write`` → "Type text", +``AC_click_mouse`` → "Click the mouse", …) with the most descriptive +argument appended; unknown commands fall back to a readable form of the +name. User content is HTML-escaped. Exposed as ``AC_generate_sop`` / +``ac_generate_sop`` (writes a file when ``path`` is given, else returns the +structured document). diff --git a/docs/source/Eng/eng_index.rst b/docs/source/Eng/eng_index.rst index be1f4ede..00be320e 100644 --- a/docs/source/Eng/eng_index.rst +++ b/docs/source/Eng/eng_index.rst @@ -50,6 +50,7 @@ Comprehensive guides for all AutoControl features. doc/new_features/v25_features_doc doc/new_features/v26_features_doc doc/new_features/v27_features_doc + doc/new_features/v28_features_doc doc/ocr_backends/ocr_backends_doc doc/observability/observability_doc doc/operations_layer/operations_layer_doc diff --git a/docs/source/Zh/doc/new_features/v28_features_doc.rst b/docs/source/Zh/doc/new_features/v28_features_doc.rst new file mode 100644 index 00000000..9c0b7d90 --- /dev/null +++ b/docs/source/Zh/doc/new_features/v28_features_doc.rst @@ -0,0 +1,30 @@ +================================================== +新功能 (2026-06-19) — 流程文件(SOP)產生器 +================================================== + +把錄製 / 編寫的動作清單轉成編號、人類可讀的**標準作業程序(SOP)**—— +結構化步驟清單加上 HTML 呈現(UiPath Task-Capture 的產出),供 runbook +與審閱使用。純標準庫;走完整五層。 + +.. contents:: + :local: + :depth: 2 + + +用法 +==== + +:: + + from je_auto_control import generate_sop, write_sop + + doc = generate_sop(actions, title="Invoice Login") + doc["steps"] # [{n, command, description, args}, ...] + doc["html"] # 完整 HTML 文件(內容已轉義) + write_sop(actions, "procedure.html", title="Invoice Login") + +每個動作會對應到人類動詞片語(``AC_write`` → 「Type text」、 +``AC_click_mouse`` → 「Click the mouse」…),並附上最具描述性的引數; +未知指令則退回為可讀的名稱形式。使用者內容會做 HTML 轉義。對應 +``AC_generate_sop`` / ``ac_generate_sop``(給 ``path`` 時寫檔,否則回傳 +結構化文件)。 diff --git a/docs/source/Zh/zh_index.rst b/docs/source/Zh/zh_index.rst index 8fa72888..d063404a 100644 --- a/docs/source/Zh/zh_index.rst +++ b/docs/source/Zh/zh_index.rst @@ -50,6 +50,7 @@ AutoControl 所有功能的完整使用指南。 doc/new_features/v25_features_doc doc/new_features/v26_features_doc doc/new_features/v27_features_doc + doc/new_features/v28_features_doc doc/ocr_backends/ocr_backends_doc doc/observability/observability_doc doc/operations_layer/operations_layer_doc diff --git a/je_auto_control/__init__.py b/je_auto_control/__init__.py index 4df3bb6e..484155e7 100644 --- a/je_auto_control/__init__.py +++ b/je_auto_control/__init__.py @@ -191,6 +191,10 @@ # Self-heal analytics + action-secrets scanning (audit/analysis) from je_auto_control.utils.heal_analytics import analyze_heal_log, heal_stats from je_auto_control.utils.secrets_scan import scan_secrets +# Process-documentation (SOP) generator from an action list +from je_auto_control.utils.process_doc import ( + describe_step, generate_sop, write_sop, +) # Background popup/interrupt watchdog (unattended automation) from je_auto_control.utils.watchdog import ( PopupWatchdog, WatchdogRule, default_popup_watchdog, @@ -623,6 +627,7 @@ def start_autocontrol_gui(*args, **kwargs): "emit_annotations", "format_annotation", "ClipboardHistory", "default_clipboard_history", "analyze_heal_log", "heal_stats", "scan_secrets", + "describe_step", "generate_sop", "write_sop", # MCP server "AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt", "MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool", diff --git a/je_auto_control/gui/script_builder/command_schema.py b/je_auto_control/gui/script_builder/command_schema.py index bbb376e7..02bb56f4 100644 --- a/je_auto_control/gui/script_builder/command_schema.py +++ b/je_auto_control/gui/script_builder/command_schema.py @@ -667,6 +667,16 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None: _add_resilience_specs(specs) _add_devex_specs(specs) _add_audit_specs(specs) + specs.append(CommandSpec( + "AC_generate_sop", "Report", "Generate SOP Document", + fields=( + FieldSpec("title", FieldType.STRING, optional=True, + default="Automation Procedure"), + FieldSpec("path", FieldType.FILE_PATH, optional=True), + ), + description="Build a step-by-step SOP (HTML) from 'actions' (JSON " + "view).", + )) def _add_audit_specs(specs: List[CommandSpec]) -> None: diff --git a/je_auto_control/utils/executor/action_executor.py b/je_auto_control/utils/executor/action_executor.py index 44135e67..0e27f033 100644 --- a/je_auto_control/utils/executor/action_executor.py +++ b/je_auto_control/utils/executor/action_executor.py @@ -2863,6 +2863,15 @@ def _scan_secrets(data: Any) -> Dict[str, Any]: return {"findings": scan_secrets(data)} +def _generate_sop(actions: List[Any], title: str = "Automation Procedure", + path: Optional[str] = None) -> Dict[str, Any]: + """Adapter: build (or write) a step-by-step SOP from an action list.""" + from je_auto_control.utils.process_doc import generate_sop, write_sop + if path: + return {"path": write_sop(actions, path, title=title)} + return generate_sop(actions, title=title) + + class Executor: """ Executor @@ -3090,6 +3099,7 @@ def __init__(self): "AC_clip_history_stop": _clip_history_stop, "AC_heal_stats": _heal_stats, "AC_scan_secrets": _scan_secrets, + "AC_generate_sop": _generate_sop, "AC_a11y_record_start": _a11y_record_start, "AC_a11y_record_stop": _a11y_record_stop, "AC_a11y_record_events": _a11y_record_events, diff --git a/je_auto_control/utils/mcp_server/tools/_factories.py b/je_auto_control/utils/mcp_server/tools/_factories.py index 007883b9..57afe694 100644 --- a/je_auto_control/utils/mcp_server/tools/_factories.py +++ b/je_auto_control/utils/mcp_server/tools/_factories.py @@ -2502,6 +2502,25 @@ def audit_analysis_tools() -> List[MCPTool]: ] +def process_doc_tools() -> List[MCPTool]: + return [ + MCPTool( + name="ac_generate_sop", + description=("Generate a step-by-step SOP document from an action " + "list (numbered steps + HTML, the Task-Capture " + "deliverable). Writes to 'path' when given, else " + "returns the structured doc."), + input_schema=schema({ + "actions": {"type": "array"}, + "title": {"type": "string"}, + "path": {"type": "string"}}, + required=["actions"]), + handler=h.generate_sop, + annotations=SIDE_EFFECT_ONLY, + ), + ] + + def unattended_tools() -> List[MCPTool]: return [ MCPTool( @@ -3559,6 +3578,7 @@ def media_assert_tools() -> List[MCPTool]: checkpoint_tools, set_of_marks_tools, screen_state_tools, input_macro_tools, resilience_tools, ci_annotation_tools, clipboard_history_tools, audit_analysis_tools, + process_doc_tools, screen_record_tools, process_and_shell_tools, remote_desktop_tools, gamepad_tools, usb_passthrough_tools, assertion_tools, data_source_tools, diff --git a/je_auto_control/utils/mcp_server/tools/_handlers.py b/je_auto_control/utils/mcp_server/tools/_handlers.py index 02be8716..0a99de2c 100644 --- a/je_auto_control/utils/mcp_server/tools/_handlers.py +++ b/je_auto_control/utils/mcp_server/tools/_handlers.py @@ -1221,6 +1221,14 @@ def scan_secrets(data): return {"findings": _scan(data)} +def generate_sop(actions, title="Automation Procedure", path=None): + from je_auto_control.utils.process_doc import generate_sop as _gen + from je_auto_control.utils.process_doc import write_sop as _write + if path: + return {"path": _write(actions, path, title=title)} + return _gen(actions, title=title) + + def vlm_locate(description: str, screen_region: Optional[List[int]] = None, model: Optional[str] = None) -> Optional[List[int]]: diff --git a/je_auto_control/utils/process_doc/__init__.py b/je_auto_control/utils/process_doc/__init__.py new file mode 100644 index 00000000..625c3c26 --- /dev/null +++ b/je_auto_control/utils/process_doc/__init__.py @@ -0,0 +1,6 @@ +"""Generate a step-by-step SOP document from a recorded action list.""" +from je_auto_control.utils.process_doc.process_doc import ( + describe_step, generate_sop, write_sop, +) + +__all__ = ["describe_step", "generate_sop", "write_sop"] diff --git a/je_auto_control/utils/process_doc/process_doc.py b/je_auto_control/utils/process_doc/process_doc.py new file mode 100644 index 00000000..bc0a9b2c --- /dev/null +++ b/je_auto_control/utils/process_doc/process_doc.py @@ -0,0 +1,79 @@ +"""Generate a step-by-step SOP document from an action list. + +AutoControl records actions but doesn't *document* them. This turns a +recorded / authored action list into a numbered, human-readable +standard-operating-procedure — a structured step list plus an HTML +rendering (the UiPath Task-Capture deliverable) — for runbooks and review. + +Pure standard library (``html`` escaping); imports no ``PySide6``. +""" +import html +from pathlib import Path +from typing import Any, Dict, List + +# Command -> human verb phrase. +_VERBS = { + "AC_click_mouse": "Click the mouse", + "AC_press_mouse": "Press the mouse button", + "AC_release_mouse": "Release the mouse button", + "AC_set_mouse_position": "Move the mouse", + "AC_mouse_scroll": "Scroll the wheel", + "AC_type_keyboard": "Press a key", + "AC_press_keyboard_key": "Hold a key", + "AC_release_keyboard_key": "Release a key", + "AC_write": "Type text", + "AC_hotkey": "Press a hotkey", + "AC_screenshot": "Take a screenshot", + "AC_locate_and_click": "Find an image and click it", + "AC_locate_image_center": "Locate an image", + "AC_click_text": "Click on text", + "AC_set_var": "Set a variable", +} +# Per-command arg whose value is the most descriptive detail. +_DETAIL_KEYS = ("write_string", "text", "keycode", "key_code_list", "image", + "name", "value", "x") + + +def describe_step(command: str, args: Dict[str, Any]) -> str: + """Return a human-readable description for one action.""" + base = _VERBS.get(command, + command.replace("AC_", "").replace("_", " ").strip() + or "Action") + for key in _DETAIL_KEYS: + if key in args and args[key] not in (None, ""): + return f"{base} ({key}: {args[key]})" + return base + + +def generate_sop(actions: List[Any], *, + title: str = "Automation Procedure") -> Dict[str, Any]: + """Return a structured SOP for ``actions`` plus an HTML rendering.""" + steps: List[Dict[str, Any]] = [] + for index, action in enumerate(actions, start=1): + command = action[0] if action and isinstance(action[0], str) else "?" + args = (action[1] if len(action) > 1 and isinstance(action[1], dict) + else {}) + steps.append({"n": index, "command": command, + "description": describe_step(command, args), + "args": args}) + return {"title": title, "step_count": len(steps), "steps": steps, + "html": _render_html(title, steps)} + + +def _render_html(title: str, steps: List[Dict[str, Any]]) -> str: + items = "\n".join( + f"