prove
Per-surface verification loops for wenlan — daemon, cli, mcp, plugin, suite strength (mutation), behavior trace, weekly sweep. Routes to scripts; every check…
Build, launch, and stop the wenlan daemon (wenlan-server) for local dev and verification. Use when asked to run or restart the daemon, or before driving any surface (HTTP, CLI, MCP) against a live instance.
$ npx -y skills add 7xuanlu/wenlan --skill run-wenlan --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/run-wenlanContext preview
The summary Claude sees to decide when to auto-load this skill.
Build, launch, and stop the wenlan daemon (wenlan-server) for local dev and verification. Use when asked to run or restart the daemon, or before driving any surface (HTTP, CLI, MCP) against a live instance.
name: run-wenlan description: Build, launch, and stop the wenlan daemon (wenlan-server) for local dev and verification. Use when asked to run or restart the daemon, or before driving any surface (HTTP, CLI, MCP) against a live instance.
Build: `cargo build -p wenlan-server -p wenlan` (add `-p wenlan-mcp` for the MCP bridge).
Three ports exist and they are not interchangeable. Stopping "the isolated daemon" on the wrong one leaves a live daemon behind and reports success.
| Port | Owner | Notes | |---|---|---| | 7878 | the user's real daemon | never kill casually, never verify against | | 17878 | manual isolated instance (this skill) | the recipes below | | 17881 | `scripts/smoke-cli.sh` | `PORT=` overrides | | 17882 | `scripts/smoke-mcp.sh` | `PORT=` overrides; runs alongside the CLI smoke |
The smokes own their own ports so both can run at once. Do not repoint them at 17878 — a manual daemon and a smoke would then fight over one listener, and the smoke's ownership assertion would fail on a daemon it did not start.
Never verify against the shared prod daemon on :7878 — dev and prod share the platform data dir by default. Isolating the port and `WENLAN_DATA_DIR` is not enough: the default pages folder is `.wenlan/pages` under the OS user-home, NOT under `WENLAN_DATA_DIR`, so a capture will write into the user's real notes unless `knowledge_path` is set explicitly. Write the scratch `config.json` BEFORE the daemon starts, then read the live value back from `/api/knowledge/path` — the daemon reloads config per request, so a value written afterwards proves nothing about what was already ingested.
`WENLAN_NO_AUTOSTART=1` belongs on every harness command: without it a failed connect starts the user's registered background service.
DATA_DIR="$(mktemp -d "${TMPDIR:-/tmp}/run.XXXXXX")"
mkdir -p "$DATA_DIR/pages"
printf '{"knowledge_path":"%s"}' "$DATA_DIR/pages" >"$DATA_DIR/config.json.tmp"
mv "$DATA_DIR/config.json.tmp" "$DATA_DIR/config.json" # atomic, before spawn
WENLAN_NO_AUTOSTART=1 WENLAN_PORT=17878 WENLAN_DATA_DIR="$DATA_DIR" \
./target/debug/wenlan-server &
# ready: curl -sf --max-time 2 http://127.0.0.1:17878/api/health (poll up to ~120s)
# check: curl -sf http://127.0.0.1:17878/api/knowledge/path → must be $DATA_DIR/pages
# stop: lsof -ti :17878 | xargs kill -9Sandbox gotcha: always give mktemp a template (`"${TMPDIR:-/tmp}/x.XXXXXX"`); bare `mktemp -d` lands in a denied dir on macOS.
`TMPDIR` is unset, `lsof` is absent, `$!` is not the pid the OS knows the daemon by, and an MSYS path is not a path the native daemon can open. Each one fails quietly in a different way, so none of them is optional.
# 1. mktemp has no TMPDIR to fall back on: give it an explicit template.
DATA_DIR="$(mktemp -d "${TMPDIR:-/tmp}/run.XXXXXX")"
mkdir -p "$DATA_DIR/pages"
# 2. TWO variables, not one rewritten one. The shell keeps the MSYS spelling for
# its own `rm -rf`; the daemon is handed the native spelling. `cygpath -m`
# converts at the daemon boundary ONLY: /tmp/run.EiVYlp →
# C:/Users/<you>/AppData/Local/Temp/run.EiVYlp
NATIVE_DATA_DIR="$(cygpath -m "$DATA_DIR")"
NATIVE_PAGES_DIR="$(cygpath -m "$DATA_DIR/pages")"
printf '{"knowledge_path":"%s"}' "$NATIVE_PAGES_DIR" >"$DATA_DIR/config.json.tmp"
mv "$DATA_DIR/config.json.tmp" "$DATA_DIR/config.json"
WENLAN_NO_AUTOSTART=1 WENLAN_PORT=17878 WENLAN_DATA_DIR="$NATIVE_DATA_DIR" \
/c/wl-target/debug/wenlan-server.exe &
JOB_PID=$! # an MSYS job pid — NOT the pid netstat and tasklist report**Listener check — source the library; do not hand-roll `netstat`.** There is no `lsof`, and the obvious `netstat -ano | awk ... $4=="LISTENING"` one-liner is NOT a substitute. It has two states where the question has three, and the state it cannot express is the dangerous one:
**free**.
no matching row → reads as **free**, with the port genuinely busy.
Starting a second daemon on a port nobody measured is how two daemons end up sharing one data directory. `scripts/lib/host-process.sh` already answers this in three states, keys on the structural shape of a listening row rather than on the localised word, and validates that what it parsed really is the table. Use it, on Windows and POSIX alike:
. scripts/lib/host-process.sh probe_listener_port 17878 case "$LISTENER_PROBE_STATE" in found) echo "busy: pid $LISTENER_PROBE_PID"; exit 1 ;; none) echo "measured free" ;; unmeasured) echo "COULD NOT MEASURE port 17878 — refusing to start"; exit 2 ;; esac
Underneath is `listener_pid_for_port` (0 = found, 1 = measured free, 2 = could not measure); `probe_listener_port` is the wrapper that turns those into `LISTENER_PROBE_STATE`, because `out="$(f)"; rc=$?` aborts under `set -e` and `if out="$(f)"; then …; fi; rc=$?` reads the compound's status, which is 0. Branch on all three states every time. There is no fourth branch to add and no default that is safe to leave off.
**Resolve the real pid before killing anything.** `$!` is the MSYS job pid; the Windows pid is the `WINPID` column of `ps -W`, and the image is the `COMMAND` column read from the header's own offset — `STIME` is one token (`10:23:45`) or two (`Aug 27`), so a field-index parse shifts on every row for a process that outlived midnight. That parse also has to reject a table it cannot read instead of reporting the process absent. All of it lives in `ps_w_row_for`, the single validated `ps -W` parse in the repository:
. scripts/lib/host-process.sh rc=0 row="$(ps_w_row_for PID "$JOB_PID")" || rc=$? case "$rc" in
Wenlan is a knowledge base for the AI-native age. Your AI agents capture what they learn, Wenlan keeps it current and distills it into source-cited wiki pages you can trust
Per-surface verification loops for wenlan — daemon, cli, mcp, plugin, suite strength (mutation), behavior trace, weekly sweep. Routes to scripts; every check…
Build, launch, screenshot, and drive the wenlan-app Tauri desktop app in dev mode on macOS (WKWebView) or Windows (WebView2). Use when asked to run or start…
Drive/evidence recipe for verifying wenlan changes at their real surfaces (daemon HTTP, CLI, MCP stdio). The handle file the built-in verify protocol expects;…
Read the current Space-owned project Brief from Wenlan for Codex. With an optional topic, appends separately labeled related context from the same Space.…
Save a durable memory to Wenlan from Codex. Use proactively when the user states a preference, makes a decision, corrects you, or shares a durable fact.…