> **本文回答**：vjcad 的命令是一个带 `async main()` 的 class，由 `CommandDefinition + CommandRegistry.regist(cmdDef)` 注册，由 `Engine.editor.executerWithOp('CMDNAME')` 触发；命令体里可以 `getPoint / getSelections / getReal / getInteger / getString / getKeyword`，可以画预览（`Engine.drawPreviewEntity`），也可以走状态机；`EXECSTR / EXECJS` 是两条内置脚本命令（⚠️ 官方示例 05executescript.js/06executejs.js 里写的 `EXECUTESTR`/`EXECUTEJS` 是**过期名**，CommandRegistry 实际注册名没有 UTE 段）。

## 一、命令骨架

```js
class HelloCommand {
  async main() {
    writeMessage('<br/>Hello WebCAD!');
    const c = new CircleEnt([50, 50], 30);
    c.setDefaults();
    c.color = 1;
    Engine.addEntities(c);
    Engine.zoomExtents();
  }
}

const options = new CommandOptions();
options.useAutoComplete = true;

CommandRegistry.regist(new CommandDefinition(
  'HELLO',          // 命令名（大写）
  '简单示例命令',
  HelloCommand,
  options
));

await Engine.editor.executerWithOp('HELLO');
```

MCP 参考：`vjcad/example/command/01simple.js`、`vjcad/doc/command/index.md`。

## 二、按子主题挑

| 想做的 | 子主题 |
|---|---|
| 写一个完整的自定义命令（含撤销分组） | [custom-command](custom-command.md) |
| `CommandOptions` 配置项 | [options](options.md) |
| 拖动绘制预览图（弹簧线 / 红色虚线） | [preview](preview.md) |
| 多步骤命令的状态机模式 | [state-machine](state-machine.md) |
| `EXECSTR` / `Engine.editor.automate(script)` | [execute-script](execute-script.md) |

## 三、与输入系统的关系

命令体内的 `Engine.getPoint / getSelections / getReal / getInteger / getString / getKeyword` 详见 [input/](../input/overview.md)。

## 四、Top 命令开发坑

1. 没有 `Engine.startUndoRecord(...)` / `Engine.undoManager.start_undoMark(...)` + `end_undoMark()` 包裹整个命令：撤销时拆成多步。
2. 没有 `try { ... } finally { Engine.undoManager.end_undoMark(); }`：中途异常（或提前 `return`）时 `end_undoMark` 不执行，undo 栈错位。注意 Esc **不抛异常**——输入函数返回 `status === InputStatusEnum.Cancel`，需自己判断后退出；注册命令结束后框架会自动 `resetPreview()` 并捕获 `main()` 的异常，`finally` 里再 `Engine.clearPreview()` 属防御写法。
3. 用 `Engine.pcanvas.addEntity(ent)` 而不是 `Engine.addEntities(ent)`：前者**不会**自动 `added_undoMark`。
4. 命令名大小写：`new CommandDefinition('mycmd', ...)` 构造时自动 `toUpperCase()`，`CommandRegistry.item()` / 执行时也把输入转大写，所以小写注册、小写调用都能命中；仍建议按约定写大写（参见 `vjcad/example/command/01simple.js` 与 `02register.js`）。只有直接 `regist` 鸭子对象 `{name, ...}` 时名字不会被转换，必须自己写大写。


## 参考（官方文档 MCP）

- 文档：`vjcad/doc/command/index.md`、`vjcad/doc/command/api-reference.md`
- 示例：`vjcad/example/command/01simple.js`、`02register.js`、`03withpreview.js`、`04statemachine.js`、`05executescript.js`、`06executejs.js`
