Skill flagged — suspicious patterns detected

ClawHub Security flagged this skill as suspicious. Review the scan results before using.

SiYuan Note

v1.0.3

SiYuan Note (思源笔记) API client - Complete notebook, document and block management

0· 299·1 current·1 all-time

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for weiwei2027/siyuan.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "SiYuan Note" (weiwei2027/siyuan) from ClawHub.
Skill page: https://clawhub.ai/weiwei2027/siyuan
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Required binaries: python3
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Bare skill slug

openclaw skills install siyuan

ClawHub CLI

Package manager switcher

npx clawhub@latest install siyuan
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
Name/description match the code and instructions: this is a local HTTP API client for SiYuan Note. Required binary is python3 and the client expects an API token in a local config file. No unrelated credentials, services, or binaries are requested.
Instruction Scope
SKILL.md and the included CLI tools instruct the agent to talk to a SiYuan HTTP API (default http://127.0.0.1:6806), read config.yaml under the skill workspace, and perform notebook/document/block operations. The instructions and code only read the declared config path and interact with the SiYuan API; writing exported Markdown files to disk is expected for export functionality.
Install Mechanism
There is no external install spec (instruction-only). Code files are bundled with the skill. No remote downloads, package installs, or archive extractions are performed by the skill itself.
Credentials
No environment variables or unrelated credentials are requested. The API token is stored in a config.yaml file under the skill workspace (~/.openclaw/workspace/skills/siyuan/config.yaml) which is appropriate for a local client. The code does not attempt to access other system credentials or config paths.
Persistence & Privilege
The skill does not request always:true and does not modify other skills or system-wide settings. It runs on demand (user-invocable) and can be invoked autonomously by the agent per platform defaults — this is normal for skills of this type.
Scan Findings in Context
[unicode-control-chars] unexpected: A pre-scan flagged unicode control characters in SKILL.md. The repository's changelog claims this was checked and Chinese/UTF-8 characters are normal; this is likely a false positive (or innocuous invisible chars in metadata). Still, inspect SKILL.md and other docs for hidden/control characters before trusting or redistributing the token-containing config file.
Assessment
This skill appears to do exactly what it claims: it is a local API client for SiYuan Note and only needs python3 plus your SiYuan API token stored in a config.yaml under the skill's workspace. Before installing: 1) Verify you actually run SiYuan locally and that base_url points to 127.0.0.1 (or a host you trust). 2) Inspect SKILL.md and bundled code for any unexpected network endpoints (the code defaults to local host only). 3) Store your API token in the config.yaml file mentioned, and avoid putting the token into shared or public places. 4) The scanner found possible hidden unicode control characters in SKILL.md — open the file in a text editor to confirm there are no suspicious invisible instructions. 5) Because the registry lists the source as unknown, if you want higher assurance compare the included code to the official SiYuan client/repo linked in the README/Homepage and prefer installing from an upstream repo you trust. If you are uncertain, run the tools in a restricted or sandboxed environment.
!
config.example.yaml:10
Install source points to URL shortener or raw IP.
About static analysis
These patterns were detected by automated regex scanning. They may be normal for skills that integrate with external APIs. Check the VirusTotal and OpenClaw results above for context-aware analysis.

Like a lobster shell, security has layers — review code before you run it.

Runtime requirements

🧠 Clawdis
Binspython3
latestvk97er8hamfkn0g0m6xvry3rdyx8388jp
299downloads
0stars
5versions
Updated 22h ago
v1.0.3
MIT-0

SiYuan Note (思源笔记)

A clean API client for SiYuan Note, providing access to your notes via the local HTTP API.

ClawHub: https://clawhub.ai/weiwei2027/siyuan
Install: clawhub install siyuan
Version: 1.0.1

Prerequisites

  • SiYuan running with API enabled (Settings → About → API)
  • API token from SiYuan settings

Configuration

  1. Get your API token from SiYuan: Settings → About → API → Copy Token

  2. Create/edit ~/.openclaw/workspace/skills/siyuan/config.yaml:

siyuan:
  base_url: "http://127.0.0.1:6806"  # Check SiYuan settings for actual port
  token: "your-api-token-here"       # Paste your token here
  timeout: 30
  retry: 3

Note: SiYuan may use different ports on restart (default 6806, but could be 34669, etc.). Check the current port in SiYuan settings.

Python Client API

Initialize Client

from siyuan_client import SiYuanClient

# Use config.yaml settings
client = SiYuanClient()

# Or explicit configuration
client = SiYuanClient(
    base_url="http://127.0.0.1:6806",
    token="your-token"
)

System Operations

# Get system version
version = client.system_version()
print(f"SiYuan v{version}")

# Get current time
timestamp = client.current_time()

Notebook Operations

# List all notebooks
notebooks = client.list_notebooks()
for nb in notebooks:
    print(f"{nb['name']}: {nb['id']}")

# Create new notebook
new_nb = client.create_notebook("我的新项目")

# Open/close notebook (load/unload from memory)
client.open_notebook("notebook-id")
client.close_notebook("notebook-id")

# Rename notebook
client.rename_notebook("notebook-id", "新名称")

# Get/set notebook configuration
conf = client.get_notebook_conf("notebook-id")
client.set_notebook_conf("notebook-id", {"dailyNoteSavePath": "/daily"})

# Remove notebook
client.remove_notebook("notebook-id")

Document Operations

# Export document as Markdown
result = client.export_md_content("doc-id")
print(result['hPath'])      # Human-readable path
print(result['content'])    # Markdown content

# Create new document
new_doc = client.create_doc_with_md(
    notebook_id="notebook-id",
    path="folder/document-name",  # Supports nested paths
    markdown="# Title\n\nContent here"
)

# Rename document
client.rename_doc("notebook-id", "/old-path", "新标题")
client.rename_doc_by_id("doc-id", "新标题")

# Get document paths
hpath = client.get_hpath_by_id("doc-id")  # Human-readable path
path_info = client.get_path_by_id("doc-id")  # Storage path
ids = client.get_ids_by_hpath("notebook-id", "/人类可读路径")

# Move documents by ID
client.move_docs_by_id(
    doc_ids=["doc-id-1", "doc-id-2"],
    to_id="target-notebook-or-doc-id"
)

# Move documents by path
client.move_docs(
    from_paths=["/path/to/doc1.sy", "/path/to/doc2.sy"],
    to_notebook="target-notebook-id",
    to_path="/subfolder"
)

# Remove document by notebook and path
client.remove_doc("notebook-id", "/path/to/doc.sy")

# Remove document by ID
client.remove_doc_by_id("doc-id")

Block Operations

# Insert blocks at specific position
blocks = client.insert_block(
    data_type="markdown",
    data="## New Section\n\nSome content",
    parent_id="doc-id",      # Optional: parent block/document
    previous_id="block-id",  # Optional: insert after this block
    next_id="block-id"       # Optional: insert before this block
)

# Prepend to beginning of document
blocks = client.prepend_block(
    data_type="markdown",
    data="# Title\n",
    parent_id="doc-id"
)

# Append to end of document
blocks = client.append_block(
    data_type="markdown",
    data="\n---\nFooter here",
    parent_id="doc-id"
)

# Update block content
client.update_block(
    data_type="markdown",
    data="Updated content",
    block_id="block-id"
)

# Delete block
client.delete_block("block-id")

# Move block
client.move_block(
    block_id="block-id",
    previous_id="target-block-id",  # Optional: insert after this block
    parent_id="parent-block-id"     # Optional: set parent (at least one required)
)

# Fold/unfold (collapse/expand) blocks
client.fold_block("block-id")
client.unfold_block("block-id")

# Transfer block references
client.transfer_block_ref(
    from_id="source-block-id",
    to_id="target-block-id",
    ref_ids=["ref-1", "ref-2"]  # Optional: specific refs to transfer
)

# Get block in Kramdown format
kramdown = client.get_block_kramdown("block-id")

# Get child blocks
children = client.get_child_blocks("container-block-id")
for child in children:
    print(f"{child['type']}: {child['content'][:50]}")

Block Attributes

# Set custom attributes on a block
client.set_block_attrs("block-id", {
    "custom-key": "value",
    "custom-priority": "high",
    "custom-status": "done"
})

# Get all attributes of a block
attrs = client.get_block_attrs("block-id")
print(attrs.get("custom-key"))

Assets

# Upload asset files
result = client.upload_asset(
    file_paths=["/path/to/image.png", "/path/to/doc.pdf"],
    assets_dir_path="/assets/"
)
print(result['succMap'])  # Successfully uploaded files
print(result['errFiles'])  # Failed uploads

SQL Operations

# Execute SQL query
results = client.query_sql("""
    SELECT * FROM blocks 
    WHERE type = 'd' 
    ORDER BY updated DESC 
    LIMIT 10
""")

# Flush SQLite transaction to disk
client.flush_transaction()

Templates

# Render a template file
result = client.render_template(
    doc_id="doc-id",
    template_path="/data/templates/daily.md"
)
print(result['content'])

# Render Sprig template string
output = client.render_sprig('/daily note/{{now | date "2006/01"}}/{{now | date "2006-01-02"}}')

File Operations

# Read file content
content = client.get_file("/data/20210808180117-6v0mkxr/20200923234011-ieuun1p.sy")

# Create directory
client.put_file("/data/new-folder", is_dir=True)

# Upload file
with open("local-file.txt", "rb") as f:
    client.put_file("/data/new-folder/file.txt", file_content=f.read())

# List directory
files = client.read_dir("/data/20210808180117-6v0mkxr")
for f in files:
    print(f"{'[DIR]' if f['isDir'] else '[FILE]'} {f['name']}")

# Rename/move file
client.rename_file("/data/old-name.sy", "/data/new-name.sy")

# Remove file
client.remove_file("/data/unwanted-file.sy")

Export

# Export document as Markdown
result = client.export_md_content("doc-id")
print(result['hPath'])      # Human-readable path
print(result['content'])    # Markdown content

# Export multiple files/folders as zip
zip_path = client.export_resources(
    paths=["/conf/appearance/boot", "/conf/appearance/langs"],
    name="my-export"
)
print(f"Exported to: {zip_path}")

Conversion

# Run Pandoc conversion
# 1. Put input file
client.put_file("/temp/convert/pandoc/mydir/input.epub", file_content=epub_bytes)

# 2. Run conversion
work_dir = client.pandoc("mydir", ["--to", "markdown_strict", "input.epub", "-o", "output.md"])

# 3. Get output file
output = client.get_file("/temp/convert/pandoc/mydir/output.md")

Notifications

# Push message to SiYuan UI
msg_id = client.push_msg("Hello from API!")

# Push error message
err_id = client.push_err_msg("Something went wrong!", timeout=10000)

Network

# Forward HTTP request through SiYuan proxy
response = client.forward_proxy(
    url="https://api.example.com/data",
    method="GET",
    headers=[{"Authorization": "Bearer token"}]
)
print(response['body'])
print(response['status'])

System

# Get boot progress
progress = client.boot_progress()
print(f"Boot: {progress['progress']}% - {progress['details']}")

# Get system version
version = client.system_version()
print(f"SiYuan v{version}")

# Get current time (milliseconds)
timestamp = client.current_time()

SQL Query

# Execute SQL query (read-only recommended)
results = client.query_sql("""
    SELECT * FROM blocks 
    WHERE content LIKE '%关键词%'
    ORDER BY updated DESC 
    LIMIT 10
""")
for block in results:
    print(f"{block['content'][:100]}...")

CLI Tools

All tools are located in tools/ directory and depend on siyuan_client.py.

List

# List all notebooks
python3 tools/list.py --notebooks

# List documents in a notebook
python3 tools/list.py --docs "notebook-id"

# Output as JSON
python3 tools/list.py -n -j

Read

# Read document content
python3 tools/read.py 20240602141622-l7ou7t7

# Save to file
python3 tools/read.py 20240602141622-l7ou7t7 -o ~/doc.md

# Show document metadata
python3 tools/read.py 20240602141622-l7ou7t7 --info

Search

# Search by keyword
python3 tools/search.py "keyword"

# Limit results
python3 tools/search.py "keyword" -l 50

# Raw SQL query
python3 tools/search.py "SELECT * FROM blocks WHERE type='d' LIMIT 10" --sql

Export

# Export all notebooks
python3 tools/export.py -o ~/backup/

# Export specific notebook
python3 tools/export.py -n "工作" -o ~/backup/

# Export single document
python3 tools/export.py -d 20240602141622-l7ou7t7 -o ~/doc.md

Create

# Create notebook
python3 tools/create.py --notebook "New Project"

# Create document
python3 tools/create.py --doc notebook-id /readme "# Hello\n\nWorld"

# Create with nested path
python3 tools/create.py --doc notebook-id /folder/doc "## Title\nContent"

Delete

# Delete notebook
python3 tools/delete.py --notebook notebook-id

# Delete document
python3 tools/delete.py --doc doc-id

# Delete block
python3 tools/delete.py --block block-id

# Skip confirmation
python3 tools/delete.py --doc doc-id --yes

Move

# Move single document
python3 tools/move.py --doc doc-id --to-notebook target-nb-id

# Move multiple documents
python3 tools/move.py --docs id1 id2 id3 --to-notebook target-nb-id

# Move by path
python3 tools/move.py --from-paths /doc1.sy /doc2.sy --to-nb target-nb --to-path /folder/

Update

# Update a block
python3 tools/update.py --block block-id --markdown "New content"

# Append to document
python3 tools/update.py --append doc-id --markdown "\n\nFooter"

# Prepend to document
python3 tools/update.py --prepend doc-id --markdown "# Header\n"

# Insert block
python3 tools/update.py --insert "New paragraph" --parent doc-id

Safety Features

  • ✅ All write operations are logged
  • ✅ Automatic retry with exponential backoff
  • ✅ Connection health checks
  • ✅ Comprehensive error handling
  • ✅ Read-only by default for queries

Troubleshooting

Connection Refused

  1. Check if SiYuan is running
  2. Verify API is enabled in Settings → About → API
  3. Check the correct port in config.yaml

Authentication Failed

  1. Get fresh token from SiYuan: Settings → About → API
  2. Update config.yaml with new token

Port Changes

SiYuan may use different ports on restart. Check current port:

ss -tlnp | grep SiYuan

Then update config.yaml accordingly.

API Reference

Changelog

v1.0.0 (2026-03-20)

  • Added complete API client with automatic retry
  • Added 8 CLI tools for all common operations
  • Added bilingual documentation (Chinese/English)
  • Added configuration file support
  • Production-ready with comprehensive error handling

v0.5.0 (2026-03-18)

  • Initial release

Comments

Loading comments...