为 OpenClaw 创建 Minitally Skill

2026/05/07

OpenClaw Skill 适合固化可重复的 Agent 行为。Minitally Skill 可以只暴露一个清晰动作:把用户确认过的消费信息写入 Minitally。

实际落地有两种方式:

  1. 环境能安装 Node.js 和 CLI 时,直接走 Minitally CLI。
  2. 环境不能安装 CLI 时,走一个 Python 脚本。

两种方式都从环境变量读取 MINITALLY_API_KEYMINITALLY_API_BASE

Skill 结构

可以创建这样的目录:

minitally-record-expense/
  SKILL.md
  scripts/
    add_expense.py

SKILL.md 保持短而严格:

# Minitally Record Expense

Use this skill when the user asks to record a spending or income item in Minitally.

Before writing:
- Confirm the amount.
- Identify whether it is expense or income.
- Preserve the user's original note.
- Ask if the amount, currency, or budget-exclusion status is unclear.

Preferred path:
- If `minitally` CLI is available, call it.
- Otherwise run `python3 scripts/add_expense.py`.

Never print `MINITALLY_API_KEY`.

CLI 方式

如果 Skill 运行环境能执行 CLI,命令保持简单:

minitally expense add --amount 38 \
  --note "OpenClaw skill lunch test" \
  --merchant "Noodle House" \
  --request-id "openclaw-skill-20260507-001"

当前源码版本可以用:

pnpm --filter @minitally/cli dev expense add --amount 38 \
  --note "OpenClaw skill lunch test" \
  --merchant "Noodle House" \
  --request-id "openclaw-skill-20260507-001"

Python 脚本兜底

脚本只用 Python 标准库,适合限制较多、无法安装 CLI 的环境:

#!/usr/bin/env python3
import json
import os
import sys
import urllib.error
import urllib.request


def arg_value(name, default=None):
    if name not in sys.argv:
        return default
    index = sys.argv.index(name)
    if index + 1 >= len(sys.argv):
        return default
    return sys.argv[index + 1]


api_key = os.environ.get("MINITALLY_API_KEY")
base_url = os.environ.get("MINITALLY_API_BASE", "https://mn.hekmon.com").rstrip("/")

if not api_key:
    raise SystemExit("MINITALLY_API_KEY is required")

payload = {
    "amount": float(arg_value("--amount", "0")),
    "type": arg_value("--type", "expense"),
    "note": arg_value("--note", ""),
    "counterparty": arg_value("--counterparty", arg_value("--merchant", "")),
    "requestId": arg_value("--request-id", ""),
}

if "--exclude-from-budget" in sys.argv:
    payload["excludeFromBudget"] = True

request = urllib.request.Request(
    f"{base_url}/api/v1/expenses",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "accept": "application/json",
        "content-type": "application/json",
        "authorization": f"Bearer {api_key}",
        "x-minitally-agent-channel": "cli",
    },
    method="POST",
)

try:
    with urllib.request.urlopen(request, timeout=20) as response:
        print(response.read().decode("utf-8"))
except urllib.error.HTTPError as error:
    print(error.read().decode("utf-8"), file=sys.stderr)
    raise SystemExit(error.code)

在 Skill 中调用:

python3 scripts/add_expense.py \
  --amount 38 \
  --note "OpenClaw skill lunch test" \
  --merchant "Noodle House" \
  --request-id "openclaw-skill-20260507-001"

执行规则

  • 每个 OpenClaw workspace 使用单独的 API Key。
  • 每次写入都设置 request id。
  • 用户说“把刚才那笔记一下”时,如果上下文里可能已经写过,先搜索再创建。
  • 只有用户明确说报销、代垫、转账类或不计入预算时,才使用 --exclude-from-budget
管理员

管理员