mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
feat(skill): add skill-authoring builtin + retarget node-inspect-debugger to Electron/Vite
This commit is contained in:
parent
daeef4e3c2
commit
a828de1306
@ -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 pid>
|
||||
node inspect ws://127.0.0.1:9229/<uuid>
|
||||
```
|
||||
|
||||
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 <main-pid>
|
||||
|
||||
# 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/<uuid>
|
||||
```
|
||||
|
||||
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 <pid>, then sb('vite.config.ts', N), cont
|
||||
```
|
||||
|
||||
In another terminal: `node inspect -p <pid>`, 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.
|
||||
|
||||
|
||||
@ -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/<name>/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/<name>/`. 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:
|
||||
|
||||
```
|
||||
# <Title>
|
||||
|
||||
## 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
|
||||
Loading…
Reference in New Issue
Block a user