Skill: zc502/sara (version 1.0.0)

--- name: Sara description: Quiet logic guard for risky multi-skill workflows. Checks order before delete, send, booking, publishing, or other high-impact actions. version: 0.1.0 --- # Sara You have access to **Sara**, a quiet logic guard for multi-skill workflows. Sara helps reduce risky ordering mistakes when a plan includes multiple skills or tool actions. Use Sara especially when a plan includes: - destructive actions like delete, remove, clear, wipe, drop, truncate - external actions like send, publish, post, deploy, submit - booking or scheduling actions - repo-destructive actions - privacy, permission, or sensitive data access actions ## What Sara checks Sara v0.1 enforces a small set of hard safety rules: 1. **backup before delete** 2. **check before operate** 3. **permission before read** 4. **preview before publish** Examples: - backup -> delete - check_calendar -> book_flight - review_draft -> send_email - request_access -> read_private_data - draft -> publish_post ## When to use Sara Run Sara before executing a risky multi-step plan when: - 2 or more skills/tools are involved, and - at least one step is high-risk, irreversible, externally visible, or destructive For simple low-risk single-step actions, Sara is usually unnecessary. ## Required behavior Before executing a risky multi-step plan: 1. Write down the proposed tool or skill order as a simple list. 2. Run `scripts/run_audit.py` with that plan. 3. Read the result carefully. 4. If `is_safe` is `false`, do **not** continue with the original order. 5. Show the user the risk and propose the `suggested_order`. 6. If the action is destructive or irreversible, ask for confirmation before continuing. Skipping Sara for risky multi-step plans increases the chance of contradictory actions, failed retries, or destructive ordering mistakes. ## Example audit input ```json {"tools":["backup","delete"]} ``` ## Example audit output ```json { "is_safe": false, "risk_level": "critical", "warnings": [ "Run 'backup' before 'delete'. Backups should happen before destructive actions." ], "suggested_order": ["backup", "delete"] } ``` ## How to respond - If Sara says the plan is safe: continue. - If Sara says the plan is unsafe: pause, explain the risk, and suggest the safer order. - If the plan touches deletion, publishing, payment, scheduling, permissions, or sensitive data, prefer confirmation before continuing. ## Response style When Sara detects a risky order, explain it clearly and briefly. Good example: - “Sara noticed a risky sequence. Suggested order: backup -> delete. Would you like me to continue in that order?” Do not mention internal implementation details unless the user asks. ## Important Sara is a **guard**, not a replacement for user intent. Use Sara to: - reduce risky ordering mistakes - reduce repeated retries - reduce contradictory multi-skill plans - make high-risk workflows safer and more consistent

path: CHANGELOG.md
content: # Changelog

All notable changes to Sara will be documented in this file.

## [0.1.0] - 2026-04-11
### Added
- **Initial Preview Release**: SARA logic guard for OpenClaw.
- **Four Core Safety Rules**: 
  - `backup` -> `delete` (Prevent data loss)
  - `check` -> `operate` (Reduce failed retries)
  - `permission` -> `read` (Privacy guard)
  - `preview` -> `publish` (Social safety)
- **Alias System**: Support for 50+ common tool verbs across Git, DB, and social media skills.
- **Deterministic Auditor**: Zero-token local Python script for sequence validation.
- **OpenClaw Integration**: Native `SKILL.md` instruction set.

### Fixed
- Improved path discovery for robust installation in various folder structures.


path: README.md
content: # Sara
**The quiet logic guard for OpenClaw multi-skill workflows.**

**Note**: Sara runs its audit locally and does not require an extra model call for safety checks. In practice, it can reduce wasted retries and contradictory multi-skill plans.

![Sara (2).png](https://raw.githubusercontent.com/ZC502/sara-openclaw/3b79383c1d3ad3a351fd54c2ce234751075e4647/Sara%20(2).png)

Sara helps your skills work in a safer order.

When you install many skills, the problem is often not that one skill is broken — it is that multiple skills start conflicting with each other, running in the wrong order, or causing messy retries and risky actions.

Sara is a lightweight logic guard that checks risky multi-step plans before they execute.

It does **not** replace your agent.  
It does **not** replace user intent.  
It simply helps prevent common ordering mistakes in high-risk workflows.

---

## What Sara does

Sara focuses on a small set of high-value, high-risk ordering rules:

- **backup before delete**
- **check before operate**
- **permission before read**
- **preview before publish**

Examples:

- backup a repo before deleting it
- check calendar conflicts before booking a flight
- request access before reading private data
- review a draft before publishing a post

If Sara detects a risky order, it does not silently continue.  
It warns, explains the problem, and suggests a safer order.

---

## Why Sara exists

As you install more OpenClaw skills, agents can become:

- repetitive
- contradictory
- too eager to execute
- more likely to retry the wrong thing
- more likely to perform a risky action before a prerequisite step

Sara acts like a **traffic controller** for risky multi-skill workflows.

Without Sara, skills can behave like people stepping into an intersection at the same time.  
With Sara, there is a quiet signal layer that helps determine what should go first.

---

## How Sara works

Sara runs a small local audit over a proposed sequence of skill or tool actions.

It normalizes common tool names into a few logical action classes such as:

- `backup`
- `delete`
- `check`
- `operate`
- `permission`
- `read`
- `preview`
- `publish`

Then it checks whether the proposed order violates any hard safety rules.

If the order is unsafe, Sara returns:

- `is_safe: false`
- a risk level
- human-readable warnings
- a suggested safer order

---

## Example

### Input

```json
{"tools":["delete","backup"]}
```

### Output

```json
{
  "is_safe": false,
  "risk_level": "critical",
  "warnings": [
    "Run 'backup' before 'delete'. Backups should happen before destructive actions."
  ],
  "violations": [
    {
      "type": "order_violation",
      "severity": "critical",
      "message": "Run 'backup' before 'delete'. Backups should happen before destructive actions.",
      "before": "backup",
      "after": "delete"
    }
  ],
  "normalized_tools": ["delete", "backup"],
  "suggested_order": ["backup", "delete"]
}
```

### Current rule set

Sara v0.1 ships with four hard rules:

**1. backup -> delete**

Use a backup step before destructive actions.

Examples:
- backup repo -> delete repo
- snapshot database -> drop database
- export data -> clear table

**2. check -> operate**

Use a validation or review step before a high-impact action.

Examples:
- check_calendar -> book_flight
- review_draft -> send_email
- check_availability -> submit_payment

**3. permission -> read**

Get permission or access before reading sensitive data.

Examples:
- request_access -> read_private_data
- login -> fetch_private
- grant_access -> read_csv

**4. preview -> publish**

Preview or draft before publishing externally.

Examples:
- draft -> publish_post
- summarize -> send_newsletter
- preview_post -> tweet

---

**Why this is useful**

Sara is intentionally small and focused.

It helps:
- reduce risky ordering mistakes
- reduce contradictory multi-skill plans
- reduce wasted retries
- reduce destructive “I did the right thing in the wrong order” failures

Sara’s audit runs locally and does **not require an extra model call** for the safety check itself.

In practice, that means it can help reduce wasteful correction loops without adding another LLM judging step.

---

### Installation layout

Recommended structure:

```markdown
```plaintext
sara-openclaw/
├── sara_core/
│   ├── __init__.py
│   └── engine.py
└── openclaw_sara_skill/
    ├── SKILL.md
    └── scripts/
        └── run_audit.py
```
Place the skill in your OpenClaw skills directory or workspace skills directory, then start a new session so OpenClaw can load it.

---

### Included files

`SKILL.md`

Defines when Sara should be used and how the agent should respond when a risky order is detected.

`scripts/run_audit.py`

Accepts a proposed tool plan as JSON from stdin and returns a local safety audit result as JSON.

`sara_core/engine.py`

Contains the core deterministic auditing logic, action normalization, and hard safety rules.

---

### Expected usage

Sara is most useful when:
- 2 or more skills/tools are involved
- at least one step is destructive, externally visible, or high-impact
- the workflow touches deletion, publishing, payments, booking, permissions, or sensitive data

Sara is usually unnecessary for simple low-risk single-step tasks.

---

### Example risky scenarios Sara can catch
- delete before backup
- publish before preview
- read private data before requesting access
- booking before checking conflicts
- destructive repo actions before safety checks

---

### Product philosophy

Sara is not another noisy orchestration layer.

It is designed to be:

- quiet
- local
- deterministic
- explainable
- helpful only when needed

Most of the time, Sara should stay out of the way.

When it speaks, it should do one thing clearly:

> “This sequence looks risky. Here is the safer order.”

---

### Version

**v0.1 preview**

Current focus:
- high-risk hard rules
- local audit
- multi-skill order safety
- warn-first behavior

Future versions may add:
- richer rule registry
- installed-skill risk scanning
- better conflict explanations
- plugin-level enforcement

---

### License / status

Preview release for OpenClaw users who want a lightweight guardrail for multi-skill workflows.    


path: scripts/run_audit.py
content: from __future__ import annotations

import json
import sys
from pathlib import Path


def find_project_root(start: Path) -> Path:
    """
    Search upward for a folder that contains sara_core/engine.py.
    Falls back to the previous fixed structure if not found.
    """
    current = start.parent
    for candidate in [current, *current.parents]:
        if (candidate / "sara_core" / "engine.py").exists():
            return candidate

    # Fallback to original expected layout
    return start.resolve().parents[2]


ROOT = find_project_root(Path(__file__).resolve())
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))

from sara_core.engine import SaraAuditor  # noqa: E402


def extract_tools(payload: dict) -> list[str]:
    if "tools" in payload and isinstance(payload["tools"], list):
        return [str(x) for x in payload["tools"]]

    if "steps" in payload and isinstance(payload["steps"], list):
        tools: list[str] = []
        for step in payload["steps"]:
            if isinstance(step, str):
                tools.append(step)
            elif isinstance(step, dict):
                name = step.get("name") or step.get("tool") or step.get("skill")
                if name:
                    tools.append(str(name))
            else:
                continue
        return tools

    return []


def main() -> None:
    raw = sys.stdin.read().strip()

    if not raw:
        print(
            json.dumps(
                {
                    "is_safe": True,
                    "risk_level": "low",
                    "warnings": [],
                    "violations": [],
                    "normalized_tools": [],
                    "suggested_order": None,
                    "note": "No input received.",
                },
                ensure_ascii=False,
            )
        )
        return

    try:
        payload = json.loads(raw)
    except json.JSONDecodeError as exc:
        print(
            json.dumps(
                {
                    "is_safe": False,
                    "risk_level": "high",
                    "warnings": [f"Invalid JSON input: {exc}"],
                    "violations": [
                        {
                            "type": "input_error",
                            "severity": "high",
                            "message": "Sara could not parse the proposed tool plan.",
                        }
                    ],
                    "normalized_tools": [],
                    "suggested_order": None,
                },
                ensure_ascii=False,
            )
        )
        return

    proposed_tools = extract_tools(payload)

    auditor = SaraAuditor()
    result = auditor.audit_trajectory(proposed_tools)

    print(json.dumps(result, ensure_ascii=False))


if __name__ == "__main__":
    main()


path: sara_core/engine.py
content: from __future__ import annotations

from dataclasses import dataclass
from typing import Iterable


@dataclass(frozen=True)
class Rule:
    before: str
    after: str
    reason: str
    severity: str = "high"


class SaraAuditor:
    """
    Minimal deterministic auditor for OpenClaw multi-skill workflows.

    v0.1 scope:
    - backup -> delete
    - check -> operate
    - permission -> read
    - preview -> publish
    """

    def __init__(self) -> None:
        # Normalize many tool/skill verbs into a small set of logical action classes.
        self.aliases = {
            # -------------------------
            # backup / export / snapshot
            # -------------------------
            "backup": "backup",
            "create_backup": "backup",
            "export_backup": "backup",
            "snapshot": "backup",
            "archive": "backup",
            "save_copy": "backup",
            "mirror": "backup",
            "mirror_repo": "backup",
            "backup_repo": "backup",
            "github_backup_repo": "backup",
            "clone_repo": "backup",
            "bundle_repo": "backup",
            "dump_db": "backup",
            "export_db": "backup",
            "pg_dump": "backup",
            "mysqldump": "backup",
            "sqlite_backup": "backup",
            "download_export": "backup",

            # -------------------------
            # delete / remove / destructive mutation
            # -------------------------
            "delete": "delete",
            "remove": "delete",
            "clear": "delete",
            "wipe": "delete",
            "erase": "delete",
            "purge": "delete",
            "trash": "delete",
            "trash_file": "delete",
            "delete_file": "delete",
            "delete_folder": "delete",
            "delete_repo": "delete",
            "github_delete_repo": "delete",
            "drop_db": "delete",
            "drop_database": "delete",
            "drop_table": "delete",
            "truncate_table": "delete",
            "reset_hard": "delete",
            "force_remove": "delete",

            # -------------------------
            # check / validate / inspect / review
            # -------------------------
            "check": "check",
            "inspect": "check",
            "verify": "check",
            "validate": "check",
            "review": "check",
            "review_draft": "check",
            "review_pr": "check",
            "preview": "check",   # generic preview/check intent
            "check_calendar": "check",
            "check_availability": "check",
            "check_conflicts": "check",
            "check_status": "check",
            "healthcheck": "check",
            "dry_run": "check",
            "lint": "check",
            "test_run": "check",
            "run_tests": "check",
            "status_repo": "check",
            "git_status": "check",
            "diff": "check",
            "git_diff": "check",
            "show_plan": "check",

            # -------------------------
            # operate / booking / sending / applying
            # -------------------------
            "operate": "operate",
            "apply_changes": "operate",
            "execute": "operate",
            "run": "operate",
            "book_flight": "operate",
            "book_hotel": "operate",
            "schedule_meeting": "operate",
            "modify_calendar": "operate",
            "send_email": "operate",
            "send_message": "operate",
            "reply_email": "operate",
            "submit_payment": "operate",
            "create_invoice": "operate",
            "merge_pr": "operate",
            "merge_branch": "operate",
            "push_branch": "operate",
            "commit_changes": "operate",
            "update_record": "operate",
            "insert_row": "operate",
            "write_db": "operate",

            # -------------------------
            # permission / auth / access grant
            # -------------------------
            "permission": "permission",
            "get_permission": "permission",
            "request_permission": "permission",
            "request_access": "permission",
            "authorize": "permission",
            "auth": "permission",
            "login": "permission",
            "sign_in": "permission",
            "grant_access": "permission",
            "unlock": "permission",
            "approve_access": "permission",

            # -------------------------
            # read / fetch / download private data
            # -------------------------
            "read": "read",
            "read_data": "read",
            "read_private_data": "read",
            "fetch_private": "read",
            "fetch_data": "read",
            "query_data": "read",
            "download": "read",
            "download_file": "read",
            "read_csv": "read",
            "read_sheet": "read",
            "export_report": "read",
            "select_rows": "read",

            # -------------------------
            # preview / draft / summarize before publish
            # -------------------------
            "preview_output": "preview",
            "draft": "preview",
            "compose_draft": "preview",
            "generate_draft": "preview",
            "summarize": "preview",
            "generate_summary": "preview",
            "preview_post": "preview",
            "render_preview": "preview",
            "preview_newsletter": "preview",

            # -------------------------
            # publish / post / send to external audience
            # -------------------------
            "publish": "publish",
            "publish_post": "publish",
            "publish_blog": "publish",
            "post": "publish",
            "post_to_main": "publish",
            "tweet": "publish",
            "post_tweet": "publish",
            "post_linkedin": "publish",
            "post_discord": "publish",
            "post_slack": "publish",
            "send_newsletter": "publish",
            "send_campaign": "publish",
            "push_live": "publish",
            "deploy_live": "publish",
        }

        self.rules: list[Rule] = [
            Rule(
                before="backup",
                after="delete",
                reason="Backups should happen before destructive actions.",
                severity="critical",
            ),
            Rule(
                before="check",
                after="operate",
                reason="Checks should happen before irreversible or high-impact actions.",
                severity="high",
            ),
            Rule(
                before="permission",
                after="read",
                reason="Permission or access grant should happen before reading sensitive data.",
                severity="critical",
            ),
            Rule(
                before="preview",
                after="publish",
                reason="Preview or draft review should happen before publishing to an external audience.",
                severity="high",
            ),
        ]

        self.priority = {
            "backup": 10,
            "permission": 15,
            "check": 20,
            "read": 40,
            "preview": 50,
            "operate": 80,
            "publish": 85,
            "delete": 90,
        }

    def normalize_tool(self, tool_name: str) -> str:
        key = (tool_name or "").strip().lower()
        return self.aliases.get(key, key)

    def normalize_sequence(self, tools_sequence: Iterable[str]) -> list[str]:
        return [self.normalize_tool(t) for t in tools_sequence if t]

    def _find_violations(self, normalized_tools: list[str]) -> list[dict]:
        violations: list[dict] = []

        for rule in self.rules:
            if rule.before in normalized_tools and rule.after in normalized_tools:
                before_idx = normalized_tools.index(rule.before)
                after_idx = normalized_tools.index(rule.after)

                if before_idx > after_idx:
                    violations.append(
                        {
                            "type": "order_violation",
                            "severity": rule.severity,
                            "message": f"Run '{rule.before}' before '{rule.after}'. {rule.reason}",
                            "before": rule.before,
                            "after": rule.after,
                        }
                    )

        return violations

    def _risk_level(self, violations: list[dict]) -> str:
        if not violations:
            return "low"
        if any(v["severity"] == "critical" for v in violations):
            return "critical"
        return "high"

    def _suggested_order(self, normalized_tools: list[str]) -> list[str]:
        indexed = list(enumerate(normalized_tools))

        def sort_key(item: tuple[int, str]) -> tuple[int, int]:
            idx, tool = item
            return (self.priority.get(tool, 50), idx)

        return [tool for _, tool in sorted(indexed, key=sort_key)]

    def audit_trajectory(self, tools_sequence: Iterable[str]) -> dict:
        normalized_tools = self.normalize_sequence(tools_sequence)
        violations = self._find_violations(normalized_tools)

        return {
            "is_safe": len(violations) == 0,
            "risk_level": self._risk_level(violations),
            "warnings": [v["message"] for v in violations],
            "violations": violations,
            "normalized_tools": normalized_tools,
            "suggested_order": self._suggested_order(normalized_tools) if violations else None,
        }


path: sara_core/__init__.py
content: 


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)