From a828de13068e5947b115222a99d044095b791d58 Mon Sep 17 00:00:00 2001 From: matevip Date: Wed, 20 May 2026 08:09:34 +0800 Subject: [PATCH] feat(skill): add skill-authoring builtin + retarget node-inspect-debugger to Electron/Vite --- .../skills/node-inspect-debugger/SKILL.md | 82 +++++---- .../resources/skills/skill-authoring/SKILL.md | 165 ++++++++++++++++++ 2 files changed, 204 insertions(+), 43 deletions(-) create mode 100644 mateclaw-server/src/main/resources/skills/skill-authoring/SKILL.md diff --git a/mateclaw-server/src/main/resources/skills/node-inspect-debugger/SKILL.md b/mateclaw-server/src/main/resources/skills/node-inspect-debugger/SKILL.md index 57c6f10e..fcbc8bd2 100644 --- a/mateclaw-server/src/main/resources/skills/node-inspect-debugger/SKILL.md +++ b/mateclaw-server/src/main/resources/skills/node-inspect-debugger/SKILL.md @@ -1,14 +1,15 @@ --- name: node-inspect-debugger description: Debug Node.js via --inspect + Chrome DevTools Protocol CLI. -version: 1.0.0 +version: 1.1.0 tags: - debugging - nodejs - node-inspect - cdp - breakpoints -- ui-tui +- electron +- vite author: ported --- # Node.js Inspect Debugger @@ -24,15 +25,17 @@ Two tools, pick one: **Prefer `node inspect` first.** It's always available and the REPL is fast. +In this repo the Node.js surfaces are the front-end packages — `mateclaw-ui`, `mateclaw-webchat`, and the Electron desktop app `mateclaw-desktop`. The Spring Boot backend is a JVM process and is not a target for this skill. + ## When to Use -- A Node test fails and you need to see intermediate state -- ui-tui crashes or behaves wrong and you want to inspect React/Ink state pre-render -- tui_gateway child processes (`_SlashWorker`, PTY bridge workers) misbehave +- A Node-based build or packaging step (a Vite build, an `electron-builder` hook, a `scripts/` helper) fails and you need to see intermediate state +- The Electron desktop **main process** (`mateclaw-desktop`) crashes, hangs on startup, or mishandles the bundled Java backend child process +- A Vite dev server or a build plugin behaves wrong and `console.log` can't reach the value - You need to inspect a value in a closure that `console.log` can't reach without patching - Perf: attach to a running process to capture a CPU profile or heap snapshot -**Don't use for:** things `console.log` solves in under a minute. Breakpoint-driven debugging is heavier; use it when the payoff is real. +**Don't use for:** things `console.log` solves in under a minute. Breakpoint-driven debugging is heavier; use it when the payoff is real. The Electron **renderer** is a Chromium page, not a Node target — debug it with the window's built-in DevTools, not `node inspect`. ## Quick Reference: `node inspect` REPL @@ -72,7 +75,7 @@ The `debug>` prompt accepts: ## Attaching to a Running Process -When the process is already running (e.g. a long-lived dev server or the TUI gateway): +When the process is already running (e.g. a Vite dev server, or the Electron main process): ```bash # 1. Send SIGUSR1 to enable the inspector on an existing process @@ -152,7 +155,7 @@ const CDP = require('chrome-remote-interface'); // Set a breakpoint by URL regex + line await Debugger.setBreakpointByUrl({ - urlRegex: '.*app\\.tsx$', + urlRegex: '.*dist-electron/main/index\\.js$', lineNumber: 119, // 0-indexed columnNumber: 0, }); @@ -167,74 +170,67 @@ Run it: node /tmp/cdp-debug.js ``` -the agent-specific note: `chrome-remote-interface` is NOT in `ui-tui/package.json`. Install it to a throwaway location if you don't want to dirty the project: +`chrome-remote-interface` is not a dependency of any package in this repo. Install it to a throwaway location so you don't dirty a project's `package.json`: ```bash mkdir -p /tmp/cdp-tools && cd /tmp/cdp-tools && npm i chrome-remote-interface NODE_PATH=/tmp/cdp-tools/node_modules node /tmp/cdp-debug.js ``` -## Debugging the agent ui-tui +## Debugging the Electron Desktop App -The TUI is built Ink + tsx. Two common scenarios: +`mateclaw-desktop` is an Electron app. The **main process** is a Node process — `electron/main/index.ts`, compiled by Vite to `dist-electron/main/index.js` (the `main` field in `package.json`). It spawns the Java backend as a child process. The **renderer** is a Chromium `BrowserWindow` — debug that with the window's DevTools, not this skill. -### Debugging a single Ink component under dev +### Launch the main process paused -`ui-tui/package.json` has `npm run dev` (tsx --watch). Add `--inspect-brk` by running tsx directly: +Electron forwards `--inspect` / `--inspect-brk` to its main process. Build the Electron output first so there is a `dist-electron/` to run: ```bash -cd /path/to/your/project/ui-tui -npm run build # produce dist/ once so transpile isn't needed on first load -node --inspect-brk dist/entry.js +cd mateclaw-desktop +npm run build # produces dist/ and dist-electron/ +npx electron --inspect-brk=9229 . # Electron starts, paused on the main process first line # In another terminal: -node inspect -p +node inspect ws://127.0.0.1:9229/ ``` Then inside `debug>`: ``` -sb('dist/app.js', 220) # or wherever the suspect render is +sb('dist-electron/main/index.js', 220) # e.g. the suspect line in window/backend setup cont ``` -When it pauses, `repl` → inspect `props`, state refs, `useInput` handler values, etc. +When it pauses, `repl` → inspect `mainWindow`, `javaProcess`, `BACKEND_PORT`, the updater state, etc. -### Debugging a running `the agent --tui` +### Attach to an already-running desktop app -The TUI spawns Node from the Python CLI. Easiest path: +The Electron main process is the one launched without a `--type=` flag (renderer/GPU/utility processes carry `--type=`): ```bash -# 1. Launch TUI -the agent --tui & -TUI_PID=$(pgrep -f 'ui-tui/dist/entry' | head -1) +# Find the main process PID (the entry without --type=) +ps aux | grep -i 'mateclaw-desktop' | grep -v -- '--type=' -# 2. Enable inspector on that Node PID -kill -SIGUSR1 "$TUI_PID" +# Enable the inspector on it +kill -SIGUSR1 -# 3. Find the WS URL +# Find the WS URL and attach curl -s http://127.0.0.1:9229/json/list | jq -r '.[0].webSocketDebuggerUrl' - -# 4. Attach node inspect ws://127.0.0.1:9229/ ``` -Interacting with the TUI (typing in its window) continues to advance execution; your debugger can pause it on a breakpoint at any `sb(...)`. +The Java backend that the main process spawns is a JVM, not a Node target — it will not appear in `/json/list`. To debug that, use the JVM's own remote-debug flags, not this skill. -### Debugging `_SlashWorker` / PTY child processes +## Debugging a Vite Dev Server -Those are Python, not Node — use the `python-debugpy` skill for them. Only Node portions (Ink UI, tui_gateway client, tsx-run tests under `ui-tui/`) use this skill. - -## Running Vitest Tests Under the Debugger +`mateclaw-ui`, `mateclaw-webchat`, and `mateclaw-desktop` all run `vite` for `dev`. To step through Vite config or a build plugin, run Vite's binary under the inspector instead of the `pnpm dev` wrapper: ```bash -cd /path/to/your/project/ui-tui -# Run a single test file paused on entry -node --inspect-brk ./node_modules/vitest/vitest.mjs run --no-file-parallelism src/app/foo.test.tsx +cd mateclaw-ui +node --inspect-brk ./node_modules/vite/bin/vite.js +# In another terminal: node inspect -p , then sb('vite.config.ts', N), cont ``` -In another terminal: `node inspect -p `, then `sb('src/app/foo.tsx', 42)`, `cont`. - -Use `--no-file-parallelism` (vitest) or `--runInBand` (jest) so only one worker exists — debugging a pool is painful. +This pauses inside the Node process that loads `vite.config.ts` and runs plugin hooks. The browser-side Vue code it serves is not reachable here — that runs in the browser and is debugged with browser DevTools. ## Heap Snapshots & CPU Profiles (Non-interactive) @@ -261,7 +257,7 @@ require('fs').writeFileSync('/tmp/heap.heapsnapshot', chunks.join('')); ## Common Pitfalls -1. **Wrong line numbers in TS source.** Breakpoints hit the emitted JS, not the `.ts`. Either (a) break in the built `dist/*.js`, or (b) enable sourcemaps (`node --enable-source-maps`) and use `sb('src/app.tsx', N)` — but only with CDP clients that follow sourcemaps. `node inspect` CLI does not. +1. **Wrong line numbers in TS source.** Breakpoints hit the emitted JS, not the `.ts`. Either (a) break in the built file (`dist-electron/main/index.js`), or (b) enable sourcemaps (`node --enable-source-maps`) and use `sb('electron/main/index.ts', N)` — but only with CDP clients that follow sourcemaps. The `node inspect` CLI does not. 2. **`--inspect` vs `--inspect-brk`.** `--inspect` starts the inspector but doesn't pause; your script races past your first breakpoint if you attach too late. Use `--inspect-brk` when you need to set breakpoints before any code runs. @@ -270,11 +266,11 @@ require('fs').writeFileSync('/tmp/heap.heapsnapshot', chunks.join('')); curl -s http://127.0.0.1:9229/json/list # lists all inspectable targets on the host ``` -4. **Child processes.** `--inspect` on a parent does NOT inspect its children. Use `NODE_OPTIONS='--inspect-brk' node parent.js` to propagate to every child; be aware they all need unique ports (Node auto-increments when `NODE_OPTIONS='--inspect'` is inherited). +4. **Child processes.** `--inspect` on a parent does NOT inspect its children. Electron itself is multi-process, and the desktop main process additionally spawns the Java backend. Use `NODE_OPTIONS='--inspect-brk' node parent.js` to propagate to every Node child; be aware they all need unique ports (Node auto-increments when `NODE_OPTIONS='--inspect'` is inherited). 5. **Background kills.** If you `Ctrl+C` out of `node inspect` while the target is paused, the target stays paused. Either `cont` first, or `kill` the target explicitly. -6. **Running `node inspect` through an agent terminal.** It's a PTY-friendly REPL. In the agent, launch it with `terminal(pty=true)` or `background=true` + `process(action='submit', data='...')`. Non-PTY foreground mode will work for one-shot commands but not for interactive stepping. +6. **Running `node inspect` through the agent's shell tool.** The `execute_shell_command` tool is one-shot and non-interactive — it cannot drive the interactive `debug>` REPL. For interactive stepping, run `node inspect` in a real terminal yourself. For agent-driven debugging, prefer the scripted CDP driver above: it is fully non-interactive and runs fine as a single `execute_shell_command` call. 7. **Security.** `--inspect=0.0.0.0:9229` exposes arbitrary code execution. Always bind to `127.0.0.1` (the default) unless you have an isolated network. diff --git a/mateclaw-server/src/main/resources/skills/skill-authoring/SKILL.md b/mateclaw-server/src/main/resources/skills/skill-authoring/SKILL.md new file mode 100644 index 00000000..42088883 --- /dev/null +++ b/mateclaw-server/src/main/resources/skills/skill-authoring/SKILL.md @@ -0,0 +1,165 @@ +--- +name: skill-authoring +description: 'Author SKILL.md skills: frontmatter, validator limits, structure.' +version: 1.0.0 +tags: +- skills +- authoring +- skill-md +- conventions +- meta +author: ported +--- +# Authoring MateClaw Skills + +## Overview + +A skill is a `SKILL.md` file — YAML frontmatter plus a markdown body of reusable instructions. There are two places a SKILL.md can live, and they have different creation paths: + +1. **Builtin (in-repo):** `mateclaw-server/src/main/resources/skills//SKILL.md` — committed, shipped inside the server JAR. On every startup `BuiltinSkillSeedService` scans `classpath*:skills/*/SKILL.md`, parses each frontmatter, and upserts a row into `mate_skill` keyed by `name`. The SKILL.md is the single source of truth — no SQL seed entry is required. +2. **Custom (runtime):** created by an agent or user through the `skill_manage` tool. Stored as a `mate_skill` row with `skill_type=custom` and exported to the workspace at `~/.mateclaw/skills//`. Not committed; lives per-installation. + +This skill covers both. Note that `skill_manage` does NOT write into the in-repo `skills/` tree — builtin skills are authored by writing the file directly and restarting. + +## When to Use + +- You're adding a reusable workflow that should ship with MateClaw → builtin. +- You're editing an existing builtin skill under `mateclaw-server/src/main/resources/skills/`. +- An agent finished a complex task and wants to persist the approach → custom, via `skill_manage`. +- You're reviewing a SKILL.md for correct frontmatter and structure. + +**Don't use for:** recording a one-off tip discovered while *using* a skill (that belongs in `record_lesson` / a per-skill LESSONS.md) or cross-skill memory notes (`remember`). This skill is about writing the skill document itself. + +## Required Frontmatter + +The frontmatter is parsed by `SkillFrontmatterParser`: a regex (`^---\s*\n(.*?)\n---\s*\n(.*)$`) splits the fenced block, then SnakeYAML loads it as a mapping. Hard requirements: + +- Starts with `---` as the **first bytes** — no leading blank line, no BOM. +- A closing `---` line follows, then the body. The body must be non-empty. +- The block between the fences parses as a YAML mapping. +- `name` is present — it is the upsert key. `BuiltinSkillSeedService` skips any SKILL.md with no `name`. +- `description` is present — a single line. + +If the frontmatter regex fails to match, the parser treats the whole file as body with an empty `name`, and a builtin skill is silently skipped at seed time. A loadable skill ALWAYS has well-formed frontmatter. + +## Size & Naming Limits + +- **Skill content:** ≤ 100,000 chars (`MAX_CONTENT_CHARS`, ~25k tokens) — enforced by `skill_manage` for custom skills. Builtin skills aren't hard-checked but should obey the same ceiling. +- **Name:** must match `^[a-z0-9][a-z0-9._-]{0,63}$` — lowercase letters and digits plus `-` `_` `.`, starting with a letter or digit, ≤ 64 chars. `skill_manage` lowercases the name before validating. +- **Description:** keep it to one line. Peer skills run 40-70 chars — a tight trigger phrase, not a paragraph. +- **Peer skills** in `resources/skills/` sit at 6-15k chars. Aim for that range; past ~20k, split detail into `references/*.md`. + +## Peer-Matched Frontmatter + +Every shipped skill follows this shape: + +```yaml +--- +name: my-skill-name +description: 'One line: what it does and when it fires.' +version: 1.0.0 +tags: +- short +- descriptive +- tags +author: ported +--- +``` + +Fields `BuiltinSkillSeedService` projects onto the `mate_skill` row: + +| Field | Effect | Default if absent | +|---|---|---| +| `name` | upsert key, skill identity | — (required) | +| `description` | shown in skill lists | empty | +| `version` | `mate_skill.version` | `1.0.0` | +| `icon` | emoji, or a `/skill-assets/...` path | `🛠️` | +| `author` | attribution | `MateClaw` | +| `tags` | YAML list or CSV string | skill name | +| `nameZh` / `nameEn` | bilingual display names | none | +| `optional: true` | seeds the skill **disabled** — user opts in from the Skills page | `false` (enabled) | +| `dependencies.tools` | required tool ids → `config_json.requiredTools` | none | +| `platforms` | e.g. `[linux, macos, windows]` | none | + +`version` / `author` / `tags` are not validator-enforced, but every peer carries them — omitting makes the skill look half-finished. Use `optional: true` for heavyweight skills (paid CLI dependencies, external OAuth, niche integrations) so they ship dark and the user activates them deliberately. + +## Skill Structure + +Shipped skills follow roughly: + +``` +# + +## Overview — one or two paragraphs: what and why. +## When to Use — bulleted triggers, plus a "Don't use for:" counter-trigger. +## <Topic sections> — quick-reference tables, exact commands, concrete recipes + (mvn test, paths under mateclaw-server/, etc.). +## Common Pitfalls — numbered mistakes paired with their fixes. +## Verification Checklist — checkbox list of post-action checks. +``` + +Not every section is mandatory, but `Overview` + `When to Use` + an actionable body + `Common Pitfalls` is the minimum for the skill to read like a peer. + +## Directory Placement + +``` +mateclaw-server/src/main/resources/skills/<skill-name>/SKILL.md +``` + +The `skills/` tree is **flat** — no category subdirectories. The seed glob `classpath*:skills/*/SKILL.md` matches exactly one level deep, so a skill nested under a category directory would never be scanned. The directory name SHOULD equal the frontmatter `name`. Supporting files go in `references/` and `scripts/` subdirectories (see below). + +## Builtin Workflow (in-repo) + +1. **Survey peers:** `ls mateclaw-server/src/main/resources/skills/` and read 2-3 SKILL.md files close to your topic — match tone and structure. +2. **Create** `skills/<name>/SKILL.md` with the file tools. +3. **Validate** that the frontmatter parses — see the checklist below. +4. **Restart the server.** `BuiltinSkillSeedService` seeds the new row only at startup; a running server will not see it. The service also skips re-seeding when no SKILL.md's size/mtime changed, so rebuilding the JAR is what makes a change land. +5. **Commit** the new `skills/<name>/` directory. No SQL seed change is needed — the SKILL.md is the source of truth and obsoletes per-skill `INSERT INTO mate_skill`. + +## Custom Workflow (skill_manage) + +Agents and users create runtime skills with the `skill_manage` tool — actions `create | edit | patch | delete`: + +- `create` — a new skill from full SKILL.md content. Rejects a duplicate name. +- `edit` — a full-content rewrite of a custom skill. +- `patch` — find-and-replace one section (`oldText` → `newText`). +- `delete` — uninstall (logical delete plus workspace archive). + +Notes: + +- Every write is **security-scanned** (`SkillSecurityService`) before saving — dangerous patterns are rejected with the reason. Builtin SKILL.md files are NOT scanned; they are trusted committed source. +- `edit` / `patch` / `delete` **refuse builtin skills** ("cannot edit builtin skill"). To change a builtin skill, edit the resource file and restart. +- A custom skill is live immediately — the tool re-runs the resolver pipeline — so no restart is needed. + +## Supporting Files + +Beyond `SKILL.md`, a skill directory may carry: + +- `references/*.md` — long-form material the body links to. Use this to keep SKILL.md under ~20k chars. +- `scripts/*` — executable helpers a skill invokes. +- `templates/`, `assets/` — used by some bundled skills (HTML templates, images, etc.). + +`SkillFileAccessPolicy` only resolves runtime paths under `references/` and `scripts/`, and rejects `..` traversal or absolute paths — keep runtime-read files in those two directories. + +## Common Pitfalls + +1. **Leading whitespace before `---`.** The frontmatter regex anchors on `^---`; a blank line or BOM makes the whole file parse as body with an empty `name`, and a builtin skill is silently skipped. +2. **Expecting a running server to see a new builtin skill.** `BuiltinSkillSeedService` seeds only at startup. Restart — or, for a quick iteration, create a custom skill via `skill_manage`, which is live immediately. +3. **Trying to `skill_manage edit` a builtin skill.** It is refused. Builtin skills are committed source — edit the file and restart. +4. **Adding an `INSERT INTO mate_skill` for a new builtin skill.** Unnecessary and discouraged — the SKILL.md is the source of truth and the seed service upserts by `name`. +5. **Generic description.** "Debug things" is weak. A peer description names the *trigger* — "4-phase root cause debugging: understand bugs before fixing." beats "Debug things." +6. **Naming an external project or internal RFC in the skill body.** Describe the function objectively. Shipped content states *what* it does, not where the idea came from — `author: ported` is the neutral attribution for an adapted skill. +7. **Skill content over 100k chars.** `skill_manage` rejects it outright; split detail into `references/`. +8. **Mismatched directory and `name`.** The upsert keys on the frontmatter `name`, but a directory that disagrees confuses everyone reading the tree. Keep them equal. + +## Verification Checklist + +- [ ] File at `mateclaw-server/src/main/resources/skills/<name>/SKILL.md` (builtin); the directory name equals the frontmatter `name` +- [ ] Frontmatter starts at byte 0 with `---`, closes with a `---` line, and the body is non-empty +- [ ] `name` matches `^[a-z0-9][a-z0-9._-]{0,63}$`; `description` is a single line +- [ ] `version`, `tags`, `author` present (peer-matched shape) +- [ ] Total file ≤ 100,000 chars (aim 6-15k; split into `references/` past ~20k) +- [ ] Structure: `# Title` → `## Overview` → `## When to Use` → actionable body → `## Common Pitfalls` → `## Verification Checklist` +- [ ] No external project names or RFC numbers in the body +- [ ] Builtin: server restarted so `BuiltinSkillSeedService` seeds the row; the new `skills/<name>/` directory is committed +- [ ] Custom: created via `skill_manage`, security scan reported PASSED