npx skills add https://github.com/joelhooks/joelclaw --skill cli-design本系统中的 CLI 采用 以智能体为先,以人类为次要考量 的设计理念。每个命令都返回结构化的 JSON,以便智能体解析、执行和跟进。人类用户也可以通过 jq 进行管道处理。
每个命令都返回 JSON。没有纯文本。没有表格。没有颜色代码。智能体解析 JSON;它们不解析散文。
# 这是唯一的输出格式
joelclaw status
# → { "ok": true, "command": "joelclaw status", "result": {...}, "next_actions": [...] }
没有 --json 标志。没有 --human 标志。JSON 是默认且唯一的格式。
每个响应都包含 next_actions —— 一个智能体接下来可以运行的命令模板数组。模板使用标准的 POSIX/docopt 占位符语法:
<placeholder> —— 必需参数
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
[--flag <value>] —— 带值的可选标志
[--flag] —— 可选布尔标志
没有 params 字段 —— 字面命令(按原样运行)
存在 params —— 模板(智能体填充占位符)
params.*.value —— 从上下文预填充(智能体可以覆盖)
params.*.default —— 省略时的默认值
params.*.enum —— 有效选项
{ "ok": true, "command": "joelclaw send pipeline/video.download", "result": { "event_id": "01KHF98SKZ7RE6HC2BH8PW2HB2", "status": "accepted" }, "next_actions": [ { "command": "joelclaw run <run-id>", "description": "检查此事件的运行状态", "params": { "run-id": { "value": "01KHF98SKZ7RE6HC2BH8PW2HB2", "description": "运行 ID (ULID)" } } }, { "command": "joelclaw logs <source> [--lines <lines>] [--grep <text>] [--follow]", "description": "查看工作进程日志", "params": { "source": { "enum": ["worker", "errors", "server"], "default": "worker" } } }, { "command": "joelclaw status", "description": "检查系统健康状况" } ] }
next_actions 是上下文相关的 —— 它们根据刚刚发生的情况而变化。失败的命令建议的下一步操作与成功的命令不同。模板是智能体的可供性 —— 它们展示了哪些是可参数化的,哪些值是有效的,以及当前上下文预填充了什么。
智能体通过两种路径发现命令:根命令(JSON 树)和 --help(由 Effect CLI 自动生成)。两者都必须有用。
根命令(无参数) 以 JSON 形式返回完整的命令树:
{
"ok": true,
"command": "joelclaw",
"result": {
"description": "JoelClaw —— 个人 AI 系统 CLI",
"health": { "server": {...}, "worker": {...} },
"commands": [
{ "name": "send", "description": "发送事件到 Inngest", "usage": "joelclaw send <event> -d '<json>'" },
{ "name": "status", "description": "系统状态", "usage": "joelclaw status" },
{ "name": "gateway", "description": "网关操作", "usage": "joelclaw gateway status" }
]
},
"next_actions": [...]
}
--help 输出 由 Effect CLI 根据 Command.withDescription() 自动生成。每个子命令必须有描述 —— 智能体总是调用 --help,没有描述的裸命令列表是无用的。
// ❌ 智能体看到一个空白的命令列表
const status = Command.make("status", {}, () => ...)
// ✅ 智能体看到每个命令的作用
const status = Command.make("status", {}, () => ...).pipe(
Command.withDescription("活动会话、队列深度、Redis 健康状况")
)
COMMANDS
- status 活动会话、队列深度、Redis 健康状况
- diagnose [--hours integer] 分层健康检查
- review [--hours integer] 最近的会话上下文
智能体的上下文窗口是有限的。CLI 输出绝不能使其爆炸。
规则:
默认简洁 —— 最小可行输出
在合理限制下自动截断大型输出(日志、列表)
截断时,包含完整输出的文件路径
绝不转储原始日志、完整记录或无界列表
{ "ok": true, "command": "joelclaw logs", "result": { "lines": 20, "total": 4582, "truncated": true, "full_output": "/var/folders/.../joelclaw-logs-abc123.log", "entries": ["...最后 20 行..."] }, "next_actions": [ { "command": "joelclaw logs <source> [--lines <lines>]", "description": "显示更多日志行", "params": { "source": { "enum": ["worker", "errors", "server"], "default": "worker" }, "lines": { "default": 20, "description": "行数" } } } ] }
当某些操作失败时,响应包含一个 fix 字段 —— 用通俗语言告诉智能体如何处理。
{
"ok": false,
"command": "joelclaw send pipeline/video.download",
"error": {
"message": "Inngest 服务器无响应",
"code": "SERVER_UNREACHABLE"
},
"fix": "启动 Inngest 服务器 Pod:kubectl rollout restart statefulset/inngest -n joelclaw",
"next_actions": [
{ "command": "joelclaw status", "description": "修复后重新检查系统健康状况" },
{
"command": "kubectl get pods [--namespace <ns>]",
"description": "检查 Pod 状态",
"params": { "ns": { "default": "joelclaw" } }
}
]
}
每个命令都使用这个确切的形状:
{
ok: true,
command: string, // 运行的命令
result: object, // 命令特定的负载
next_actions: Array<{
command: string, // 命令模板(POSIX 语法)或字面量
description: string, // 它的作用
params?: Record<string, { // 存在 = 命令是模板
description?: string, // 此参数的含义
value?: string | number, // 从当前上下文预填充
default?: string | number,// 省略时的值
enum?: string[], // 有效选项
required?: boolean // 对于 <positional> 参数为 true
}>
}>
}
{
ok: false,
command: string,
error: {
message: string, // 出了什么问题
code: string // 机器可读的错误代码
},
fix: string, // 通俗语言建议的修复方案
next_actions: Array<{
command: string, // 命令模板或字面量
description: string,
params?: Record<string, { ... }> // 与成功相同的模式
}>
}
joelclaw —— ~/Code/joelhooks/joelclaw/packages/cli/(Effect CLI,操作层面)slog —— 系统日志 CLI(相同的信封模式)将这些作为当前信封的单一事实来源。
所有 CLI 都使用 @effect/cli 和 Bun。这是不可协商的 —— 系统内的一致性比框架偏好更重要。
import { Command, Options } from "@effect/cli"
import { NodeContext, NodeRuntime } from "@effect/platform-node"
const send = Command.make("send", {
event: Options.text("event"),
data: Options.optional(Options.text("data").pipe(Options.withAlias("d"))),
}, ({ event, data }) => {
// ... 执行,返回 JSON 信封
})
const root = Command.make("joelclaw", {}, () => {
// 根命令:返回健康状况 + 命令树
}).pipe(Command.withSubcommands([send, status, logs]))
使用 Bun 构建,安装到 ~/.bun/bin/:
bun build src/cli.ts --compile --outfile joelclaw
cp joelclaw ~/.bun/bin/
Command.make 定义命令next_actions —— 在此特定命令之后什么操作是合理的commands 数组请求-响应覆盖了空间维度(当前状态是什么?)。流式 NDJSON 覆盖了时间维度(随着时间的推移发生了什么?)。两者结合通过一个协议使整个系统可观测。
当命令涉及时间性操作时使用流式传输 —— 监视、跟随、追踪。并非每个命令都需要流式传输。时间点查询(status, functions, runs)保持为单个信封。
流式传输由命令语义激活(--follow, watch, gateway stream),而不是通过全局的 --stream 标志。
每一行都是一个自包含的 JSON 对象,带有 type 鉴别器。最后一行始终是标准的 HATEOAS 信封(result 或 error)。不理解流式传输的工具读取最后一行,并得到它们期望的内容。
{"type":"start","command":"joelclaw send video/download --follow","ts":"2026-02-19T08:25:00Z"}
{"type":"step","name":"download","status":"started","ts":"..."}
{"type":"progress","name":"download","percent":45,"ts":"..."}
{"type":"step","name":"download","status":"completed","duration_ms":3200,"ts":"..."}
{"type":"step","name":"transcribe","status":"started","ts":"..."}
{"type":"log","level":"warn","message":"文件过大,分块转录","ts":"..."}
{"type":"step","name":"transcribe","status":"completed","duration_ms":45000,"ts":"..."}
{"type":"result","ok":true,"command":"...","result":{...},"next_actions":[...]}
| 类型 | 含义 | 是否为终端? |
|---|---|---|
start | 流开始,回显命令 | 否 |
step | Inngest 步骤生命周期(已开始/已完成/已失败) | 否 |
progress | 进度更新(百分比、字节数、消息) | 否 |
log | 诊断消息(信息/警告/错误级别) | 否 |
event | 发出 Inngest 事件(扇出可见性) | 否 |
result | HATEOAS 成功信封 —— 始终是最后一行 | 是 |
error | HATEOAS 错误信封 —— 始终是最后一行 | 是 |
import type { NextAction } from "./response"
type StreamEvent =
| { type: "start"; command: string; ts: string }
| { type: "step"; name: string; status: "started" | "completed" | "failed"; duration_ms?: number; error?: string; ts: string }
| { type: "progress"; name: string; percent?: number; message?: string; ts: string }
| { type: "log"; level: "info" | "warn" | "error"; message: string; ts: string }
| { type: "event"; name: string; data: unknown; ts: string }
| { type: "result"; ok: true; command: string; result: unknown; next_actions: NextAction[] }
| { type: "error"; ok: false; command: string; error: { message: string; code: string }; fix: string; next_actions: NextAction[] }
使用 emit() 辅助函数 —— 每次调用输出一行 JSON,立即刷新:
import { emit, emitResult, emitError } from "../stream"
// 进度事件
emit({ type: "start", command: "joelclaw send video/download --follow", ts: new Date().toISOString() })
emit({ type: "step", name: "download", status: "started", ts: new Date().toISOString() })
emit({ type: "step", name: "download", status: "completed", duration_ms: 3200, ts: new Date().toISOString() })
// 终端事件 —— 始终是最后一行
emitResult("send --follow", { videoId: "abc123" }, [
{ command: "joelclaw run abc123", description: "检查已完成的运行" },
])
流式命令订阅与网关扩展使用的相同 Redis 发布/订阅通道。pushGatewayEvent() 中间件是发出点 —— CLI 只是另一个订阅者。
import { streamFromRedis } from "../stream"
// 订阅通道,转换事件,发出 NDJSON
await streamFromRedis({
channel: `joelclaw:notify:gateway`,
command: "joelclaw gateway stream",
transform: (event) => ({
type: "event" as const,
name: event.type,
data: event.data,
ts: new Date().toISOString(),
}),
// 可选:结束条件
until: (event) => event.type === "loop.complete",
})
NDJSON 是管道原生的。智能体和人类可以过滤流:
# 仅步骤事件
joelclaw watch | jq --unbuffered 'select(.type == "step")'
# 仅失败事件
joelclaw send video/download --follow | jq --unbuffered 'select(.type == "error" or (.type == "step" and .status == "failed"))'
# 统计步骤数
joelclaw send pipeline/run --follow | jq --unbuffered 'select(.type == "step" and .status == "completed")' | wc -l
消费流的智能体在数据到达时逐行读取,并可以在执行过程中做出决策:
joelclaw send video/download --followresult/error 行包含 next_actions,指示之后该做什么这消除了轮询开销 —— 无需浪费工具调用来检查“完成了吗?”
流式命令持有 Redis 连接。它们必须:
connectTimeout 和 commandTimeout 防止挂起| 不要 | 要 |
|---|---|
| 纯文本输出 | JSON 信封 |
| 带有 ANSI 颜色的表格 | JSON 数组 |
--json 标志来选择 JSON | JSON 是唯一格式 |
| 转储 10,000 行 | 截断 + 文件指针 |
Error: something went wrong | { ok: false, error: {...}, fix: "..." } |
| 不可发现的命令 | 根命令返回完整的命令树 |
| 静态帮助文本 | HATEOAS next_actions |
console.log("Success!") | { ok: true, result: {...} } |
| 仅使用退出代码作为错误信号 | JSON 中的错误 + 退出代码 |
| 要求智能体阅读 --help | 根命令自描述 |
子命令没有 withDescription | 每个命令都有描述以供 --help 使用 |
| 循环轮询时间性数据 | 通过 Redis 订阅流式传输 NDJSON(ADR-0058) |
| 流式命令中的纯文本 | 每一行都是类型化的 JSON 对象 |
| 持有 Redis 连接而不清理 | SIGINT 处理程序 + 连接超时 |
send, status, logs, gatewayjoelclaw search "query", joelclaw loop start--kebab-case:--max-quality, --follow-d 对应 --data,-f 对应 --followdomain/action:pipeline/video.download, content/summarizeCommand.withDescription()(在 --help 中显示)<required>, [--flag <value>])+ paramsparams.*.value 预填充skillrecordings/support 仓库中验证过的 GitHub 设备流 → 代理 → 会话令牌 → 环境变量具体化模式。涵盖:设备代码轮询循环、组织/团队成员资格门控、短期 AES-GCM 会话令牌、使用 age 加密的秘密传递到 CLI 临时密钥对、仅内存的环境注入。参考实现:skillrecordings/support 中的 apps/front/lib/broker/ + apps/front/app/api/auth/device/。这消除了 1Password CLI 作为开发者依赖,同时保持服务器端秘密管理不变。每周安装量
103
仓库
GitHub 星标数
49
首次出现
2026 年 2 月 15 日
安全审计
安装于
codex100
opencode100
gemini-cli99
github-copilot99
cursor98
kimi-cli97
CLIs in this system are agent-first, human-distant-second. Every command returns structured JSON that an agent can parse, act on, and follow. Humans are welcome to pipe through jq.
Every command returns JSON. No plain text. No tables. No color codes. Agents parse JSON; they don't parse prose.
# This is the ONLY output format
joelclaw status
# → { "ok": true, "command": "joelclaw status", "result": {...}, "next_actions": [...] }
No --json flag. No --human flag. JSON is the default and only format.
Every response includes next_actions — an array of command templates the agent can run next. Templates use standard POSIX/docopt placeholder syntax:
<placeholder> — required argument
[--flag <value>] — optional flag with value
[--flag] — optional boolean flag
No params field — literal command (run as-is)
params present — template (agent fills placeholders)
params.*.value — pre-filled from context (agent can override)
params.*.default — value if omitted
params.*.enum — valid choices
{ "ok": true, "command": "joelclaw send pipeline/video.download", "result": { "event_id": "01KHF98SKZ7RE6HC2BH8PW2HB2", "status": "accepted" }, "next_actions": [ { "command": "joelclaw run <run-id>", "description": "Check run status for this event", "params": { "run-id": { "value": "01KHF98SKZ7RE6HC2BH8PW2HB2", "description": "Run ID (ULID)" } } }, { "command": "joelclaw logs <source> [--lines <lines>] [--grep <text>] [--follow]", "description": "View worker logs", "params": { "source": { "enum": ["worker", "errors", "server"], "default": "worker" } } }, { "command": "joelclaw status", "description": "Check system health" } ] }
next_actions are contextual — they change based on what just happened. A failed command suggests different next steps than a successful one. Templates are the agent's affordances — they show what's parameterizable, what values are valid, and what the current context pre-fills.
Agents discover commands via two paths : the root command (JSON tree) and --help (Effect CLI auto-generated). Both must be useful.
Root command (no args) returns the full command tree as JSON:
{
"ok": true,
"command": "joelclaw",
"result": {
"description": "JoelClaw — personal AI system CLI",
"health": { "server": {...}, "worker": {...} },
"commands": [
{ "name": "send", "description": "Send event to Inngest", "usage": "joelclaw send <event> -d '<json>'" },
{ "name": "status", "description": "System status", "usage": "joelclaw status" },
{ "name": "gateway", "description": "Gateway operations", "usage": "joelclaw gateway status" }
]
},
"next_actions": [...]
}
--help output is auto-generated by Effect CLI from Command.withDescription(). Every subcommand must have a description — agents always call --help and a bare command list with no descriptions is useless.
// ❌ Agents see a blank command list
const status = Command.make("status", {}, () => ...)
// ✅ Agents see what each command does
const status = Command.make("status", {}, () => ...).pipe(
Command.withDescription("Active sessions, queue depths, Redis health")
)
COMMANDS
- status Active sessions, queue depths, Redis health
- diagnose [--hours integer] Layer-by-layer health check
- review [--hours integer] Recent session context
Agents have finite context windows. CLI output must not blow them up.
Rules:
Terse by default — minimum viable output
Auto-truncate large outputs (logs, lists) at a reasonable limit
When truncated, include a file path to the full output
Never dump raw logs, full transcripts, or unbounded lists
{ "ok": true, "command": "joelclaw logs", "result": { "lines": 20, "total": 4582, "truncated": true, "full_output": "/var/folders/.../joelclaw-logs-abc123.log", "entries": ["...last 20 lines..."] }, "next_actions": [ { "command": "joelclaw logs <source> [--lines <lines>]", "description": "Show more log lines", "params": { "source": { "enum": ["worker", "errors", "server"], "default": "worker" }, "lines": { "default": 20, "description": "Number of lines" } } } ] }
When something fails, the response includes a fix field — plain language telling the agent what to do about it.
{
"ok": false,
"command": "joelclaw send pipeline/video.download",
"error": {
"message": "Inngest server not responding",
"code": "SERVER_UNREACHABLE"
},
"fix": "Start the Inngest server pod: kubectl rollout restart statefulset/inngest -n joelclaw",
"next_actions": [
{ "command": "joelclaw status", "description": "Re-check system health after fix" },
{
"command": "kubectl get pods [--namespace <ns>]",
"description": "Check pod status",
"params": { "ns": { "default": "joelclaw" } }
}
]
}
Every command uses this exact shape:
{
ok: true,
command: string, // the command that was run
result: object, // command-specific payload
next_actions: Array<{
command: string, // command template (POSIX syntax) or literal
description: string, // what it does
params?: Record<string, { // presence = command is a template
description?: string, // what this param means
value?: string | number, // pre-filled from current context
default?: string | number,// value if omitted
enum?: string[], // valid choices
required?: boolean // true for <positional> args
}>
}>
}
{
ok: false,
command: string,
error: {
message: string, // what went wrong
code: string // machine-readable error code
},
fix: string, // plain-language suggested fix
next_actions: Array<{
command: string, // command template or literal
description: string,
params?: Record<string, { ... }> // same schema as success
}>
}
joelclaw — ~/Code/joelhooks/joelclaw/packages/cli/ (Effect CLI, operational surface)slog — system log CLI (same envelope patterns)Use these as the current envelope source-of-truth.
All CLIs use @effect/cli with Bun. This is non-negotiable — consistency across the system matters more than framework preference.
import { Command, Options } from "@effect/cli"
import { NodeContext, NodeRuntime } from "@effect/platform-node"
const send = Command.make("send", {
event: Options.text("event"),
data: Options.optional(Options.text("data").pipe(Options.withAlias("d"))),
}, ({ event, data }) => {
// ... execute, return JSON envelope
})
const root = Command.make("joelclaw", {}, () => {
// Root: return health + command tree
}).pipe(Command.withSubcommands([send, status, logs]))
Build with Bun, install to ~/.bun/bin/:
bun build src/cli.ts --compile --outfile joelclaw
cp joelclaw ~/.bun/bin/
Command.makenext_actions — what makes sense AFTER this specific commandcommands array in the self-documenting outputRequest-response covers the spatial dimension (what's the state now?). Streamed NDJSON covers the temporal dimension (what's happening over time?). Together they make the full system observable through one protocol.
Stream when the command involves temporal operations — watching, following, tailing. Not every command needs streaming. Point-in-time queries (status, functions, runs) stay as single envelopes.
Streaming is activated by command semantics (--follow, watch, gateway stream), never by a global --stream flag.
Each line is a self-contained JSON object with a type discriminator. The last line is always the standard HATEOAS envelope (result or error). Tools that don't understand streaming read the last line and get exactly what they expect.
{"type":"start","command":"joelclaw send video/download --follow","ts":"2026-02-19T08:25:00Z"}
{"type":"step","name":"download","status":"started","ts":"..."}
{"type":"progress","name":"download","percent":45,"ts":"..."}
{"type":"step","name":"download","status":"completed","duration_ms":3200,"ts":"..."}
{"type":"step","name":"transcribe","status":"started","ts":"..."}
{"type":"log","level":"warn","message":"Large file, chunked transcription","ts":"..."}
{"type":"step","name":"transcribe","status":"completed","duration_ms":45000,"ts":"..."}
{"type":"result","ok":true,"command":"...","result":{...},"next_actions":[...]}
| Type | Meaning | Terminal? |
|---|---|---|
start | Stream begun, echoes command | No |
step | Inngest step lifecycle (started/completed/failed) | No |
progress | Progress update (percent, bytes, message) | No |
log | Diagnostic message (info/warn/error level) | No |
event | An Inngest event was emitted (fan-out visibility) |
import type { NextAction } from "./response"
type StreamEvent =
| { type: "start"; command: string; ts: string }
| { type: "step"; name: string; status: "started" | "completed" | "failed"; duration_ms?: number; error?: string; ts: string }
| { type: "progress"; name: string; percent?: number; message?: string; ts: string }
| { type: "log"; level: "info" | "warn" | "error"; message: string; ts: string }
| { type: "event"; name: string; data: unknown; ts: string }
| { type: "result"; ok: true; command: string; result: unknown; next_actions: NextAction[] }
| { type: "error"; ok: false; command: string; error: { message: string; code: string }; fix: string; next_actions: NextAction[] }
Use the emit() helper — one JSON line per call, flushed immediately:
import { emit, emitResult, emitError } from "../stream"
// Progress events
emit({ type: "start", command: "joelclaw send video/download --follow", ts: new Date().toISOString() })
emit({ type: "step", name: "download", status: "started", ts: new Date().toISOString() })
emit({ type: "step", name: "download", status: "completed", duration_ms: 3200, ts: new Date().toISOString() })
// Terminal — always last
emitResult("send --follow", { videoId: "abc123" }, [
{ command: "joelclaw run abc123", description: "Inspect the completed run" },
])
Streaming commands subscribe to the same Redis pub/sub channels the gateway extension uses. pushGatewayEvent() middleware is the emission point — the CLI is just another subscriber.
import { streamFromRedis } from "../stream"
// Subscribe to a channel, transform events, emit NDJSON
await streamFromRedis({
channel: `joelclaw:notify:gateway`,
command: "joelclaw gateway stream",
transform: (event) => ({
type: "event" as const,
name: event.type,
data: event.data,
ts: new Date().toISOString(),
}),
// Optional: end condition
until: (event) => event.type === "loop.complete",
})
NDJSON is pipe-native. Agents and humans can filter streams:
# Only step events
joelclaw watch | jq --unbuffered 'select(.type == "step")'
# Only failures
joelclaw send video/download --follow | jq --unbuffered 'select(.type == "error" or (.type == "step" and .status == "failed"))'
# Count steps
joelclaw send pipeline/run --follow | jq --unbuffered 'select(.type == "step" and .status == "completed")' | wc -l
Agents consuming streams read lines as they arrive and can make decisions mid-execution:
joelclaw send video/download --followresult/error line contains next_actions for what to do afterThis eliminates the polling tax — no wasted tool calls checking "is it done yet?"
Streaming commands hold a Redis connection. They must :
connectTimeout and commandTimeout to prevent hangs| Don't | Do |
|---|---|
| Plain text output | JSON envelope |
| Tables with ANSI colors | JSON arrays |
--json flag to opt into JSON | JSON is the only format |
| Dump 10,000 lines | Truncate + file pointer |
Error: something went wrong | { ok: false, error: {...}, fix: "..." } |
| Undiscoverable commands | Root returns full command tree |
| Static help text | HATEOAS next_actions |
console.log("Success!") |
send, status, logs, gatewayjoelclaw search "query", joelclaw loop start--kebab-case: --max-quality, --follow-d for --data, for Command.withDescription() set (shows in --help)<required>, [--flag <value>]) + paramsparams.*.valueskillrecordings/support repo. Covers: device code polling loop, org/team membership gating, short-lived AES-GCM session tokens, age-encrypted secret delivery to CLI ephemeral keypairs, in-memory-only env injection. Reference implementation: apps/front/lib/broker/ + apps/front/app/api/auth/device/ in skillrecordings/support. This eliminates 1Password CLI as a developer dependency while keeping server-side secret management intact.Weekly Installs
103
Repository
GitHub Stars
49
First Seen
Feb 15, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex100
opencode100
gemini-cli99
github-copilot99
cursor98
kimi-cli97
AI Elements:基于shadcn/ui的AI原生应用组件库,快速构建对话界面
66,200 周安装
Web开发全栈指南:Bootstrap、Django、HTMX框架实战与最佳实践
102 周安装
FDA咨询专家服务:医疗器械法规、质量体系、HIPAA合规与网络安全咨询
198 周安装
AI上下文管理器技能 - 动态上下文工程、向量数据库与多智能体工作流编排专家指南
201 周安装
项目安全设置协调器 - 自动化安全扫描、配置与风险评估工具
203 周安装
TypeScript/Python/.NET项目代码检查工具自动配置 - ESLint, Ruff, Roslyn Analyzers
199 周安装
Eve 计划实施技能:将计划文档转化为并行任务,实现高效项目编排与执行
201 周安装
| No |
result | HATEOAS success envelope — always last | Yes |
error | HATEOAS error envelope — always last | Yes |
{ ok: true, result: {...} }| Exit code as the only error signal | Error in JSON + exit code |
| Require the agent to read --help | Root command self-documents |
Subcommand with no withDescription | Every command gets a description for --help |
| Poll in a loop for temporal data | Stream NDJSON via Redis sub (ADR-0058) |
| Plain text in streaming commands | Every line is a typed JSON object |
| Hold Redis connections without cleanup | SIGINT handler + connection timeout |
-f--followdomain/action: pipeline/video.download, content/summarize