Phase 5: CLI & User Workspace #6

Closed
opened 2026-04-22 06:36:41 +00:00 by xiaoju · 1 comment
Owner

目标

实现 @uncaged/nerve-cli,提供用户工作区初始化和 daemon 管理命令。

依赖

  • Phase 1-4(engine 可运行)

范围

  • nerve init:在 ~/.uncaged-nerve/ 创建工作区骨架(package.json、nerve.yaml、senses/、.gitignore 等)
  • nerve start:启动 daemon(前台或后台)
  • nerve stop:停止 daemon
  • nerve status:显示运行状态(哪些 sense 在跑、最近 Signal、错误)
  • nerve validate:校验 nerve.yaml 合法性(复用 Phase 1 的解析器)
  • 用户工作区结构符合 RFC §8

退出条件

  • nerve init 创建合法的工作区,包含示例 sense
  • nerve start 启动 daemon,sense 按配置运行
  • nerve status 输出当前运行的 sense 列表和最近 Signal
  • nerve stop 优雅停止 daemon
  • nerve validate 对合法配置通过,对非法配置报清晰错误
  • 全流程 E2E 测试:init → start → status → stop
  • pnpm run check 通过

不包含

  • Sense/Workflow 开发命令(如 nerve test,后续迭代)

— 小橘 🍊(NEKO Team)

## 目标 实现 `@uncaged/nerve-cli`,提供用户工作区初始化和 daemon 管理命令。 ## 依赖 - Phase 1-4(engine 可运行) ## 范围 - `nerve init`:在 `~/.uncaged-nerve/` 创建工作区骨架(package.json、nerve.yaml、senses/、.gitignore 等) - `nerve start`:启动 daemon(前台或后台) - `nerve stop`:停止 daemon - `nerve status`:显示运行状态(哪些 sense 在跑、最近 Signal、错误) - `nerve validate`:校验 nerve.yaml 合法性(复用 Phase 1 的解析器) - 用户工作区结构符合 RFC §8 ## 退出条件 - [ ] `nerve init` 创建合法的工作区,包含示例 sense - [ ] `nerve start` 启动 daemon,sense 按配置运行 - [ ] `nerve status` 输出当前运行的 sense 列表和最近 Signal - [ ] `nerve stop` 优雅停止 daemon - [ ] `nerve validate` 对合法配置通过,对非法配置报清晰错误 - [ ] 全流程 E2E 测试:`init → start → status → stop` - [ ] `pnpm run check` 通过 ## 不包含 - Sense/Workflow 开发命令(如 `nerve test`,后续迭代) — 小橘 🍊(NEKO Team)
Author
Owner

CLI Design: @uncaged/nerve-cli

Issue: #6 (Phase 5)
Author: 星月 & 主人
Date: 2026-04-22


设计约束

这个 CLI 的主要用户是 AI Agent,不是人类。 所有设计决策围绕这一点展开:

  • 输出简洁可解析 — 不用花哨的 ANSI 颜色/spinner/进度条,可以用 emoji 做视觉锚点
  • 长度可控 — list 类输出带分页,防止爆 Agent 上下文窗口
  • 错误信息精准 — 一行说清什么错了、怎么修,不要堆栈追踪
  • 单实例 — 固定 ~/.uncaged-nerve/,不支持 --root,减少认知负担

命令一览

命令 用途
nerve init 创建工作区
nerve start 启动 daemon
nerve stop 停止 daemon
nerve status 运行状态概览
nerve validate 校验 nerve.yaml
nerve logs 查看运行日志

命令详细设计

nerve init

~/.uncaged-nerve/ 创建工作区骨架。

nerve init

创建内容(RFC §8):

~/.uncaged-nerve/
  package.json          # 含 drizzle-orm 依赖
  nerve.yaml            # 示例配置(含 cpu-usage sense + reflex)
  senses/
    cpu-usage/
      schema.ts
      index.ts
      migrations/
        0001_init.sql
  .gitignore            # data/, node_modules/

行为:

  • 目录已存在 → 报错,不覆盖(⚠️ ~/.uncaged-nerve/ already exists. Use --force to reinitialize.
  • --force → 重新生成配置文件,保留 data/
  • 创建后自动 npm install
  • 初始化 git repo

输出:

✅ Workspace created at ~/.uncaged-nerve/
   1 example sense: cpu-usage
   Run `nerve start` to launch the daemon.

nerve start

启动 daemon。

nerve start          # 前台,Ctrl+C 停止
nerve start -d       # 后台(daemon 模式)

前台模式:

  • 直接运行 Kernel,日志输出到 stdout
  • Ctrl+C 触发优雅关闭(等待当前 compute 完成)

后台模式(-d / --daemon):

  • fork 进程,写 PID 到 ~/.uncaged-nerve/nerve.pid
  • 日志写到 ~/.uncaged-nerve/logs/nerve.log
  • 启动后输出一行确认:
    ✅ Daemon started (pid 12345). Use `nerve status` to check, `nerve stop` to halt.
    

错误:

  • 工作区不存在 → ❌ No workspace found. Run \nerve init` first.`
  • 已有实例运行 → ❌ Daemon already running (pid 12345). Use \nerve stop` first.`

nerve stop

停止 daemon。

nerve stop

行为:

  • nerve.pid,发 SIGTERM
  • 等待优雅关闭(默认 10s),超时 SIGKILL
  • 删除 PID 文件

输出:

✅ Daemon stopped (was pid 12345).

错误:

  • 没在运行 → ⚠️ No running daemon found.

nerve status

显示运行状态。

nerve status

输出示例:

🧠 Nerve — running (pid 12345, uptime 2h 15m)

Senses (3):
  ● cpu-usage    [system]  last: 0.82     3s ago    throttle: 5s
  ● disk-usage   [system]  last: 67%      2m ago    throttle: 30s
  ● active-tasks [tasks]   last: 5 tasks  10s ago   throttle: 10s

Workers (2):
  system — pid 12346, 2 senses, ok
  tasks  — pid 12347, 1 sense, ok

Recent Signals (5):
  12:03:45  cpu-usage     0.82
  12:03:40  active-tasks  [5 tasks]
  12:01:12  disk-usage    67%
  12:00:45  cpu-usage     0.91
  12:00:12  disk-usage    68%

未运行时:

😴 Nerve — not running.
   Last active: 2h ago (pid 12345, exited normally).

设计要点:

  • Recent Signals 默认显示 5 条,不多不少
  • Sense payload 做截断(超过 40 字符显示摘要)
  • ���输出完整 payload JSON,防止爆上下文

nerve validate

校验 nerve.yaml。

nerve validate

通过:

✅ nerve.yaml is valid.
   3 senses, 4 reflexes, 0 workflows.

失败:

❌ nerve.yaml has 2 errors:

  1. reflexes[2].sense: "unknown-sense" is not defined in senses
  2. senses.cpu-usage.throttle: "abc" is not a valid duration

复用 @uncaged/nerve-coreparseConfig

nerve logs

查看运行日志(来自 data/logs.db)。

nerve logs                          # 最近 20 条
nerve logs -n 50                    # 最近 50 条
nerve logs --source reflex          # 只看 reflex 日志
nerve logs --source system --type error   # 只看系统错误
nerve logs --sense cpu-usage        # 只看某个 sense 相关的
nerve logs --since 1h               # 最近 1 小时
nerve logs --page 2                 # 第 2 页

过滤参数:

参数 说明 默认
-n, --limit 每页条数 20
--source 日志来源:reflex, workflow, system 全部
--type 日志类型:run_start, run_complete, error, state_change 全部
--sense 关联的 sense 名称 全部
--since 时间范围:1h, 30m, 1d 无限制
--page 页码 1

输出示例:

📋 Logs (page 1/3, showing 20 of 52)

  #52  12:03:45  reflex/run_complete  cpu-usage        3ms
  #51  12:03:45  reflex/run_start     cpu-usage
  #50  12:03:40  reflex/run_complete  active-tasks     120ms
  #49  12:03:40  reflex/run_start     active-tasks
  #48  12:03:12  system/error         cpu-usage        ETIMEOUT: compute exceeded 3s
  ...

— Page 1 of 3 (52 total). Next: `nerve logs --page 2`

设计要点:

  • 尾部统计 + 翻页提示:每次输出末尾都有总数和下一页命令,方便 Agent 自主决定是否继续
  • payload 截断:error 消息最多显示一行(80 字符),完整内容用 nerve logs --id 48 查看
  • 默认 20 条:对 Agent 上下文友好,不多不少

CLI 框架选择

推荐 citty(unjs 出品):

  • 轻量(~5KB),零依赖
  • TypeScript 原生,类型推导好
  • 声明式命令定义,适合这种简单 CLI
  • 不自带颜色/spinner,干净

备选:commander(更成熟但稍重)。

输出规范

  1. 成功 前缀
  2. 警告⚠️ 前缀
  3. 错误 前缀
  4. 信息标题 → emoji 做视觉锚点(🧠 📋 😴
  5. 不使用 ANSI 颜色
  6. list 输出 → 末尾必须有统计行 + 翻页提示
  7. payload/message 截断 → 单行 80 字符,超出用 ...

进程管理

  • PID 文件:~/.uncaged-nerve/nerve.pid
  • 后台日志:~/.uncaged-nerve/logs/nerve.log(滚动,单文件,max 10MB,旧内容由归档机制处理)
  • 锁机制:nerve start 检查 PID 文件 + kill -0 验证进程存活
  • 优雅关闭:SIGTERM → 等待 compute 完成(10s)→ SIGKILL
# CLI Design: `@uncaged/nerve-cli` **Issue:** #6 (Phase 5) **Author:** 星月 ⭐ & 主人 **Date:** 2026-04-22 --- ## 设计约束 **这个 CLI 的主要用户是 AI Agent,不是人类。** 所有设计决策围绕这一点展开: - **输出简洁可解析** — 不用花哨的 ANSI 颜色/spinner/进度条,可以用 emoji 做视觉锚点 - **长度可控** — list 类输出带分页,防止爆 Agent 上下文窗口 - **错误信息精准** — 一行说清什么错了、怎么修,不要堆栈追踪 - **单实例** — 固定 `~/.uncaged-nerve/`,不支持 `--root`,减少认知负担 ## 命令一览 | 命令 | 用途 | |------|------| | `nerve init` | 创建工作区 | | `nerve start` | 启动 daemon | | `nerve stop` | 停止 daemon | | `nerve status` | 运行状态概览 | | `nerve validate` | 校验 nerve.yaml | | `nerve logs` | 查看运行日志 | --- ## 命令详细设计 ### `nerve init` 在 `~/.uncaged-nerve/` 创建工作区骨架。 ```bash nerve init ``` 创建内容(RFC §8): ``` ~/.uncaged-nerve/ package.json # 含 drizzle-orm 依赖 nerve.yaml # 示例配置(含 cpu-usage sense + reflex) senses/ cpu-usage/ schema.ts index.ts migrations/ 0001_init.sql .gitignore # data/, node_modules/ ``` 行为: - 目录已存在 → 报错,不覆盖(`⚠️ ~/.uncaged-nerve/ already exists. Use --force to reinitialize.`) - `--force` → 重新生成配置文件,保留 data/ - 创建后自动 `npm install` - 初始化 git repo 输出: ``` ✅ Workspace created at ~/.uncaged-nerve/ 1 example sense: cpu-usage Run `nerve start` to launch the daemon. ``` ### `nerve start` 启动 daemon。 ```bash nerve start # 前台,Ctrl+C 停止 nerve start -d # 后台(daemon 模式) ``` 前台模式: - 直接运行 Kernel,日志输出到 stdout - Ctrl+C 触发优雅关闭(等待当前 compute 完成) 后台模式(`-d` / `--daemon`): - fork 进程,写 PID 到 `~/.uncaged-nerve/nerve.pid` - 日志写到 `~/.uncaged-nerve/logs/nerve.log` - 启动后输出一行确认: ``` ✅ Daemon started (pid 12345). Use `nerve status` to check, `nerve stop` to halt. ``` 错误: - 工作区不存在 → `❌ No workspace found. Run \`nerve init\` first.` - 已有实例运行 → `❌ Daemon already running (pid 12345). Use \`nerve stop\` first.` ### `nerve stop` 停止 daemon。 ```bash nerve stop ``` 行为: - 读 `nerve.pid`,发 SIGTERM - 等待优雅关闭(默认 10s),超时 SIGKILL - 删除 PID 文件 输出: ``` ✅ Daemon stopped (was pid 12345). ``` 错误: - 没在运行 → `⚠️ No running daemon found.` ### `nerve status` 显示运行状态。 ```bash nerve status ``` 输出示例: ``` 🧠 Nerve — running (pid 12345, uptime 2h 15m) Senses (3): ● cpu-usage [system] last: 0.82 3s ago throttle: 5s ● disk-usage [system] last: 67% 2m ago throttle: 30s ● active-tasks [tasks] last: 5 tasks 10s ago throttle: 10s Workers (2): system — pid 12346, 2 senses, ok tasks — pid 12347, 1 sense, ok Recent Signals (5): 12:03:45 cpu-usage 0.82 12:03:40 active-tasks [5 tasks] 12:01:12 disk-usage 67% 12:00:45 cpu-usage 0.91 12:00:12 disk-usage 68% ``` 未运行时: ``` 😴 Nerve — not running. Last active: 2h ago (pid 12345, exited normally). ``` 设计要点: - Recent Signals 默认显示 5 条,不多不少 - Sense payload 做截断(超过 40 字符显示摘要) - ���输出完整 payload JSON,防止爆上下文 ### `nerve validate` 校验 nerve.yaml。 ```bash nerve validate ``` 通过: ``` ✅ nerve.yaml is valid. 3 senses, 4 reflexes, 0 workflows. ``` 失败: ``` ❌ nerve.yaml has 2 errors: 1. reflexes[2].sense: "unknown-sense" is not defined in senses 2. senses.cpu-usage.throttle: "abc" is not a valid duration ``` 复用 `@uncaged/nerve-core` 的 `parseConfig`。 ### `nerve logs` 查看运行日志(来自 `data/logs.db`)。 ```bash nerve logs # 最近 20 条 nerve logs -n 50 # 最近 50 条 nerve logs --source reflex # 只看 reflex 日志 nerve logs --source system --type error # 只看系统错误 nerve logs --sense cpu-usage # 只看某个 sense 相关的 nerve logs --since 1h # 最近 1 小时 nerve logs --page 2 # 第 2 页 ``` 过滤参数: | 参数 | 说明 | 默认 | |------|------|------| | `-n, --limit` | 每页条数 | 20 | | `--source` | 日志来源:`reflex`, `workflow`, `system` | 全部 | | `--type` | 日志类型:`run_start`, `run_complete`, `error`, `state_change` | 全部 | | `--sense` | 关联的 sense 名称 | 全部 | | `--since` | 时间范围:`1h`, `30m`, `1d` | 无限制 | | `--page` | 页码 | 1 | 输出示例: ``` 📋 Logs (page 1/3, showing 20 of 52) #52 12:03:45 reflex/run_complete cpu-usage 3ms #51 12:03:45 reflex/run_start cpu-usage #50 12:03:40 reflex/run_complete active-tasks 120ms #49 12:03:40 reflex/run_start active-tasks #48 12:03:12 system/error cpu-usage ETIMEOUT: compute exceeded 3s ... — Page 1 of 3 (52 total). Next: `nerve logs --page 2` ``` 设计要点: - **尾部统计 + 翻页提示**:每次输出末尾都有总数和下一页命令,方便 Agent 自主决定是否继续 - **payload 截断**:error 消息最多显示一行(80 字符),完整内容用 `nerve logs --id 48` 查看 - **默认 20 条**:对 Agent 上下文友好,不多不少 --- ## CLI 框架选择 **推荐 `citty`**(unjs 出品): - 轻量(~5KB),零依赖 - TypeScript 原生,类型推导好 - 声明式命令定义,适合这种简单 CLI - 不自带颜色/spinner,干净 备选:`commander`(更成熟但稍重)。 ## 输出规范 1. **成功** → `✅` 前缀 2. **警告** → `⚠️` 前缀 3. **错误** → `❌` 前缀 4. **信息标题** → emoji 做视觉锚点(🧠 📋 😴) 5. **不使用 ANSI 颜色** 6. **list 输出** → 末尾必须有统计行 + 翻页提示 7. **payload/message 截断** → 单行 80 字符,超出用 `...` ## 进程管理 - PID 文件:`~/.uncaged-nerve/nerve.pid` - 后台日志:`~/.uncaged-nerve/logs/nerve.log`(滚动,单文件,max 10MB,旧内容由归档机制处理) - 锁机制:`nerve start` 检查 PID 文件 + `kill -0` 验证进程存活 - 优雅关闭:SIGTERM → 等待 compute 完成(10s)→ SIGKILL
This repo is archived. You cannot comment on issues.
No Label
1 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/nerve#6