forked from arechen/EapilSkillMarket
Sync third-party and MCP marketplace plugins
Constraint: Public skills are published only by explicit administrator action unless they are tracked third-party market sources. Confidence: high Scope-risk: narrow Directive: Keep private/internal skills out of the public marketplace and preserve normal incremental market Git history. Tested: Marketplace validation passed.
This commit is contained in:
1 parent
ebe8c7d892
commit
5589889631
179 files changed
+51
-25001
No files matched your search
@@ -292,18 +292,6 @@
|
||||
},
|
||||
"category": "Developer Tools"
|
||||
},
|
||||
{
|
||||
"name": "oh-my-codex",
|
||||
"source": {
|
||||
"source": "local",
|
||||
"path": "./plugins/oh-my-codex"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Developer Tools"
|
||||
},
|
||||
{
|
||||
"name": "shadcn",
|
||||
"source": {
|
||||
@@ -316,30 +304,6 @@
|
||||
},
|
||||
"category": "Developer Tools"
|
||||
},
|
||||
{
|
||||
"name": "superpowers",
|
||||
"source": {
|
||||
"source": "local",
|
||||
"path": "./plugins/superpowers"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Developer Tools"
|
||||
},
|
||||
{
|
||||
"name": "superpowers-zh",
|
||||
"source": {
|
||||
"source": "local",
|
||||
"path": "./plugins/superpowers-zh"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Developer Tools"
|
||||
},
|
||||
{
|
||||
"name": "updating-gitea-repositories",
|
||||
"source": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "artemis-automation",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"description": "使用 ARTEMIS MCP 完成 Android 自动化测试、批量执行、失败诊断、历史检索和报告交付。",
|
||||
"author": {
|
||||
"name": "chenmingxuan",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"sourceId": "artemis-automation",
|
||||
"repo": "https://git.playones.com/chenmingxuan/Artemis-Eapil.git",
|
||||
"ref": "main",
|
||||
"commit": "8e757d462615fa211cafcb85ab56a4ad6cd73a28",
|
||||
"ref": "v1.0.0",
|
||||
"commit": "2ec563c07943d4d651a5a2875314a2e4f3ca1126",
|
||||
"adapter": "claude-skill",
|
||||
"sourcePath": "skills/artemis-automation",
|
||||
"syncedAt": "2026-09-22T16:08:55Z"
|
||||
"syncedAt": "2026-09-22T16:42:07Z"
|
||||
}
|
||||
@@ -27,7 +27,7 @@ description: 使用 ARTEMIS MCP 在 Android 真机或模拟器上完成自然语
|
||||
## 配置和恢复边界
|
||||
|
||||
- `mobile_config` 读取非敏感设置。只有用户明确要求保存或修改默认配置时,才传 `changes` 与 `persist_defaults=true`;这些设置影响后续任务,不能为一次临时需求擅自改变以后所有任务。
|
||||
- 模型名称支持预设 `gpt-5.6-luna`、`mimo-v2.6-flash` 和自定义名称,取决于用户配置的 Responses 网关。默认思考强度 `high`、单次请求 300 秒、失败后最多重试 5 次、固定间隔 10 秒;默认关闭视频分析与录屏。不根据模型名称猜测网关能力。
|
||||
- 模型名称支持预设 `gpt-5.6-luna`、`mimo-v2.6-flash` 和自定义名称,取决于用户配置的 Responses 网关。思考强度最高支持 `max`,默认 `high`、单次请求 300 秒、失败后最多重试 5 次、固定间隔 10 秒;默认关闭视频分析与录屏。不根据模型名称猜测网关能力。
|
||||
- 强度选 `default` 表示不发送该参数,和 `none`(显式要求关闭)不同。接口拒绝强度参数时明确呈现失败,先询问用户是否改用 `default`,不能静默降级或换模型。
|
||||
- API Key 只通过网页或 `.env` 配置。不要请求用户把 key 发到聊天中,不读取/回显已有 key。保存后新任务生效;直接编辑环境文件后需重启相应服务,运行中的任务保留自己的配置。
|
||||
- 当前视频开关仅提供保存的全局设置,没有单任务覆盖或独立视频文件分析接口。用户只要求临时分析且默认关闭时,明确说明这个限制;不能用“先开全局、执行后再关”影响同期任务。只有用户改为要求保存启用后,才修改配置并提交新任务。
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"name": "playwright浏览器自动化操作",
|
||||
"version": "20260605",
|
||||
"keySource": "none",
|
||||
"syncedAt": "2026-09-22T16:12:43Z"
|
||||
"syncedAt": "2026-09-22T16:44:16Z"
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
"sourceId": "next-skills",
|
||||
"repo": "https://github.com/vercel/next.js.git",
|
||||
"ref": "canary",
|
||||
"commit": "3204d86c4494dec07f8927378084e10f92a58a37",
|
||||
"commit": "bf53f53c7da622c7ee366cef50aa9ba4bb8143db",
|
||||
"adapter": "skill-collection",
|
||||
"sourcePath": "skills",
|
||||
"syncedAt": "2026-09-22T16:08:55Z"
|
||||
"syncedAt": "2026-09-22T16:42:07Z"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"apps": {}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "oh-my-codex",
|
||||
"version": "0.21.6",
|
||||
"description": "oh-my-codex 是 Codex CLI 的多 Agent 编排、结构化工作流、插件级 hooks、MCP 和 HUD 扩展插件。",
|
||||
"author": {
|
||||
"name": "Yeachan Heo",
|
||||
"url": "https://github.com/Yeachan-Heo"
|
||||
},
|
||||
"homepage": "https://yeachan-heo.github.io/oh-my-codex",
|
||||
"repository": "https://github.com/Yeachan-Heo/oh-my-codex.git",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"codex",
|
||||
"openai",
|
||||
"cli",
|
||||
"agents",
|
||||
"orchestration",
|
||||
"multi-agent"
|
||||
],
|
||||
"skills": "./skills/",
|
||||
"mcpServers": "./.mcp.json",
|
||||
"apps": "./.app.json",
|
||||
"interface": {
|
||||
"displayName": "oh-my-codex",
|
||||
"shortDescription": "为 Codex CLI 提供 OMX 工作流、hooks、MCP 和多 Agent 编排。",
|
||||
"longDescription": "oh-my-codex 将 OMX 的规划、调试、代码评审、多 Agent 协作、目标执行、质量验证、wiki 和 HUD 工作流打包为 Codex 插件。插件包含官方支持的 plugin-scoped hooks、MCP 配置和多个 workflow skills;完整运行需要用户已安装 oh-my-codex CLI 并完成 omx setup。",
|
||||
"developerName": "Yeachan Heo",
|
||||
"category": "Developer Tools",
|
||||
"capabilities": [
|
||||
"Interactive",
|
||||
"Read",
|
||||
"Write"
|
||||
],
|
||||
"defaultPrompt": [
|
||||
"使用 oh-my-codex 帮我选择合适的 OMX 工作流。",
|
||||
"使用 oh-my-codex 帮我规划、执行并验证这个开发任务。"
|
||||
],
|
||||
"websiteURL": "https://oh-my-codex.dev",
|
||||
"privacyPolicyURL": "https://github.com/Yeachan-Heo/oh-my-codex",
|
||||
"termsOfServiceURL": "https://github.com/Yeachan-Heo/oh-my-codex",
|
||||
"brandColor": "#111827"
|
||||
},
|
||||
"hooks": "./hooks/hooks.json"
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"omx_state": {
|
||||
"command": "omx",
|
||||
"args": [
|
||||
"mcp-serve",
|
||||
"state"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
"omx_memory": {
|
||||
"command": "omx",
|
||||
"args": [
|
||||
"mcp-serve",
|
||||
"memory"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
"omx_code_intel": {
|
||||
"command": "omx",
|
||||
"args": [
|
||||
"mcp-serve",
|
||||
"code-intel"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
"omx_trace": {
|
||||
"command": "omx",
|
||||
"args": [
|
||||
"mcp-serve",
|
||||
"trace"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
"omx_wiki": {
|
||||
"command": "omx",
|
||||
"args": [
|
||||
"mcp-serve",
|
||||
"wiki"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
"omx_hermes": {
|
||||
"command": "omx",
|
||||
"args": [
|
||||
"mcp-serve",
|
||||
"hermes"
|
||||
],
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"sourceId": "oh-my-codex",
|
||||
"repo": "https://github.com/Yeachan-Heo/oh-my-codex.git",
|
||||
"ref": "main",
|
||||
"commit": "cdc24a71408ebd6bd0362f52170f0d7998f77007",
|
||||
"adapter": "codex-plugin",
|
||||
"sourcePath": "plugins/oh-my-codex",
|
||||
"syncedAt": "2026-09-22T16:08:55Z"
|
||||
}
|
||||
@@ -1,657 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawn } from 'node:child_process';
|
||||
import { extname } from 'node:path';
|
||||
import { closeSync, constants as fsConstants, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, realpathSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const hookDir = dirname(fileURLToPath(import.meta.url));
|
||||
// sync-plugin-mirror verifies this stable marker; runtime behavior is tested separately.
|
||||
const OMX_PLUGIN_HOOK_LAUNCHER_CONTRACT_MARKER = 'omx-plugin-hook-launcher:v1';
|
||||
const OMX_PLUGIN_HOOK_ROUTING_ONLY_MARKER = 'omx-plugin-hook-routing-only:v1';
|
||||
const MAX_WRAPPER_STDIN_BYTES = 1024 * 1024;
|
||||
const RAW_EVENT_SCAN_BYTES = 64 * 1024;
|
||||
const MAX_STOP_STDOUT_BYTES = 1024 * 1024;
|
||||
const MAX_ROUTING_RECORD_BYTES = 4 * 1024;
|
||||
const CODEX_HOOK_EVENT_NAMES = new Set([
|
||||
'SessionStart',
|
||||
'PreToolUse',
|
||||
'PostToolUse',
|
||||
'UserPromptSubmit',
|
||||
'PreCompact',
|
||||
'PostCompact',
|
||||
'Stop',
|
||||
]);
|
||||
|
||||
function skipJsonWhitespace(raw, index) {
|
||||
while (index < raw.length && /\s/.test(raw[index] ?? '')) index += 1;
|
||||
return index;
|
||||
}
|
||||
|
||||
function readJsonStringLiteral(raw, quoteIndex) {
|
||||
if (raw[quoteIndex] !== '"') return null;
|
||||
let value = '';
|
||||
for (let index = quoteIndex + 1; index < raw.length; index += 1) {
|
||||
const char = raw[index];
|
||||
if (char === '"') return { value, endIndex: index + 1 };
|
||||
if (char !== '\\') {
|
||||
value += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
if (index >= raw.length) return null;
|
||||
const escaped = raw[index];
|
||||
switch (escaped) {
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
value += escaped;
|
||||
break;
|
||||
case 'b':
|
||||
value += '\b';
|
||||
break;
|
||||
case 'f':
|
||||
value += '\f';
|
||||
break;
|
||||
case 'n':
|
||||
value += '\n';
|
||||
break;
|
||||
case 'r':
|
||||
value += '\r';
|
||||
break;
|
||||
case 't':
|
||||
value += '\t';
|
||||
break;
|
||||
case 'u': {
|
||||
const hex = raw.slice(index + 1, index + 5);
|
||||
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
||||
value += String.fromCharCode(Number.parseInt(hex, 16));
|
||||
index += 4;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractTopLevelStringField(rawInput, fieldNames) {
|
||||
const raw = rawInput.slice(0, RAW_EVENT_SCAN_BYTES);
|
||||
const wanted = new Set(fieldNames);
|
||||
let depth = 0;
|
||||
let index = 0;
|
||||
|
||||
while (index < raw.length) {
|
||||
const char = raw[index];
|
||||
if (char === '"') {
|
||||
const key = readJsonStringLiteral(raw, index);
|
||||
if (!key) return null;
|
||||
index = key.endIndex;
|
||||
const afterKey = skipJsonWhitespace(raw, index);
|
||||
if (depth === 1 && raw[afterKey] === ':' && wanted.has(key.value)) {
|
||||
const valueStart = skipJsonWhitespace(raw, afterKey + 1);
|
||||
const value = readJsonStringLiteral(raw, valueStart);
|
||||
return value?.value ?? null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (char === '{') depth += 1;
|
||||
else if (char === '}') depth = Math.max(0, depth - 1);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractTopLevelHookEventName(rawInput) {
|
||||
const eventName = extractTopLevelStringField(rawInput, ['hook_event_name', 'hookEventName', 'event', 'name']);
|
||||
return CODEX_HOOK_EVENT_NAMES.has(eventName) ? eventName : null;
|
||||
}
|
||||
|
||||
function detectStopHookInput(input) {
|
||||
const text = input.toString('utf8');
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
const eventName = parsed?.hook_event_name ?? parsed?.hookEventName ?? parsed?.event ?? parsed?.name;
|
||||
return eventName === 'Stop';
|
||||
} catch {
|
||||
return extractTopLevelHookEventName(text) === 'Stop';
|
||||
}
|
||||
}
|
||||
|
||||
function detectCompactHookInput(input) {
|
||||
const text = input.toString('utf8');
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
const eventName = parsed?.hook_event_name ?? parsed?.hookEventName ?? parsed?.event ?? parsed?.name;
|
||||
return eventName === 'PreCompact' || eventName === 'PostCompact';
|
||||
} catch {
|
||||
const eventName = extractTopLevelHookEventName(text);
|
||||
return eventName === 'PreCompact' || eventName === 'PostCompact';
|
||||
}
|
||||
}
|
||||
|
||||
function parseHookPayload(input) {
|
||||
try {
|
||||
const parsed = JSON.parse(input.toString('utf8'));
|
||||
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeLaunchId(value) {
|
||||
return String(value ?? '').trim().replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 128);
|
||||
}
|
||||
|
||||
function resolveLaunchRoutingPath(payload, launchId) {
|
||||
const cwd = typeof payload.cwd === 'string' && payload.cwd.trim() ? payload.cwd : process.cwd();
|
||||
const stateRoot = typeof process.env.OMX_ROOT === 'string' && process.env.OMX_ROOT.trim()
|
||||
? process.env.OMX_ROOT.trim()
|
||||
: join(cwd, '.omx');
|
||||
return join(stateRoot, 'state', 'plugin-hook-routing', `${sanitizeLaunchId(launchId)}.json`);
|
||||
}
|
||||
|
||||
function hookPayloadSessionId(input, payload) {
|
||||
const candidate = payload?.session_id ?? payload?.sessionId;
|
||||
const parsedSessionId = typeof candidate === 'string' ? candidate.trim() : '';
|
||||
if (parsedSessionId) return parsedSessionId;
|
||||
return input && typeof input.toString === 'function'
|
||||
? extractTopLevelStringField(input.toString('utf8'), ['session_id', 'sessionId'])?.trim() ?? ''
|
||||
: '';
|
||||
}
|
||||
|
||||
function isSafeRoutingSessionId(value) {
|
||||
return typeof value === 'string' && value.length > 0 && value.length <= 256;
|
||||
}
|
||||
|
||||
function isChildRoutingSessionStart(payload, ownerSessionId) {
|
||||
if ((payload?.hook_event_name ?? payload?.hookEventName) !== 'SessionStart' || !ownerSessionId) return false;
|
||||
const childSessionId = hookPayloadSessionId(null, payload);
|
||||
const transcriptCandidate = payload?.transcript_path ?? payload?.transcriptPath;
|
||||
const transcriptPath = typeof transcriptCandidate === 'string' ? transcriptCandidate.trim() : '';
|
||||
if (!childSessionId || !transcriptPath) return false;
|
||||
let fd;
|
||||
try {
|
||||
fd = openSync(transcriptPath, 'r');
|
||||
const buffer = Buffer.alloc(64 * 1024);
|
||||
const bytes = readSync(fd, buffer, 0, buffer.length, 0);
|
||||
const newline = buffer.subarray(0, bytes).indexOf(0x0a);
|
||||
const firstLine = buffer.subarray(0, newline >= 0 ? newline : bytes).toString('utf8').trim();
|
||||
const record = JSON.parse(firstLine);
|
||||
const metadata = record?.type === 'session_meta' && record?.payload && typeof record.payload === 'object' ? record.payload : null;
|
||||
const spawn = metadata?.source?.subagent?.thread_spawn;
|
||||
const metadataIds = [metadata?.id, metadata?.session_id].filter((value) => value !== undefined);
|
||||
return metadataIds.length > 0
|
||||
&& metadataIds.every((value) => value === childSessionId)
|
||||
&& spawn && typeof spawn === 'object'
|
||||
&& spawn.parent_thread_id === ownerSessionId;
|
||||
} catch {
|
||||
return false;
|
||||
} finally {
|
||||
if (fd !== undefined) try { closeSync(fd); } catch {}
|
||||
}
|
||||
}
|
||||
|
||||
function readPinnedRoutingRecord(routingPath) {
|
||||
let fd;
|
||||
try {
|
||||
const initial = lstatSync(routingPath);
|
||||
if (!initial.isFile() || initial.isSymbolicLink() || initial.nlink !== 1 || initial.size <= 0 || initial.size > MAX_ROUTING_RECORD_BYTES) return null;
|
||||
fd = openSync(routingPath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
|
||||
const opened = fstatSync(fd);
|
||||
if (!opened.isFile() || opened.nlink !== 1 || opened.size !== initial.size || opened.dev !== initial.dev || opened.ino !== initial.ino) return null;
|
||||
const buffer = Buffer.alloc(opened.size);
|
||||
if (readSync(fd, buffer, 0, buffer.length, 0) !== buffer.length) return null;
|
||||
const current = lstatSync(routingPath);
|
||||
const final = fstatSync(fd);
|
||||
if (!current.isFile() || current.isSymbolicLink() || current.nlink !== 1 || current.size !== opened.size || current.dev !== opened.dev || current.ino !== opened.ino || final.nlink !== 1 || final.size !== opened.size) return null;
|
||||
const routing = JSON.parse(buffer.toString('utf8'));
|
||||
return routing && typeof routing === 'object' && !Array.isArray(routing) ? routing : null;
|
||||
} catch {
|
||||
return null;
|
||||
} finally {
|
||||
if (fd !== undefined) try { closeSync(fd); } catch {}
|
||||
}
|
||||
}
|
||||
|
||||
// This is routing correlation only. It is intentionally unauthenticated and must
|
||||
// never be used as authorization or proof that a session is OMX-owned.
|
||||
function isPluginHookRoutingSession(input, payload) {
|
||||
const launchId = sanitizeLaunchId(process.env.OMX_CODEX_LAUNCH_ID);
|
||||
const entryPath = process.env.OMX_ENTRY_PATH?.trim();
|
||||
const sessionId = hookPayloadSessionId(input, payload);
|
||||
if (!launchId || !entryPath || !isSafeRoutingSessionId(sessionId)) return false;
|
||||
|
||||
const routingPath = resolveLaunchRoutingPath(payload, launchId);
|
||||
try {
|
||||
if (existsSync(routingPath)) {
|
||||
const routing = readPinnedRoutingRecord(routingPath);
|
||||
if (!routing) return false;
|
||||
const ownerSessionId = routing.routing === OMX_PLUGIN_HOOK_ROUTING_ONLY_MARKER && isSafeRoutingSessionId(routing.ownerSessionId)
|
||||
? routing.ownerSessionId
|
||||
: '';
|
||||
return ownerSessionId === sessionId || isChildRoutingSessionStart(payload, ownerSessionId);
|
||||
}
|
||||
mkdirSync(dirname(routingPath), { recursive: true });
|
||||
writeFileSync(routingPath, `${JSON.stringify({ routing: OMX_PLUGIN_HOOK_ROUTING_ONLY_MARKER, ownerSessionId: sessionId })}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function writePlainCodexNoop(isStop) {
|
||||
if (isStop) process.stdout.write('{}\n');
|
||||
process.exitCode = 0;
|
||||
}
|
||||
|
||||
async function readBoundedStdin({ drainOversized = false } = {}) {
|
||||
const chunks = [];
|
||||
let totalBytes = 0;
|
||||
let storedBytes = 0;
|
||||
let oversized = false;
|
||||
for await (const rawChunk of process.stdin) {
|
||||
const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk);
|
||||
totalBytes += chunk.length;
|
||||
|
||||
if (oversized) continue;
|
||||
|
||||
const remaining = MAX_WRAPPER_STDIN_BYTES - storedBytes;
|
||||
if (chunk.length > remaining) {
|
||||
if (remaining > 0) chunks.push(chunk.subarray(0, remaining));
|
||||
storedBytes += Math.max(remaining, 0);
|
||||
oversized = true;
|
||||
if (!drainOversized) return { input: Buffer.concat(chunks), oversized: true, totalBytes };
|
||||
continue;
|
||||
}
|
||||
chunks.push(chunk);
|
||||
storedBytes += chunk.length;
|
||||
}
|
||||
return { input: Buffer.concat(chunks), oversized, totalBytes };
|
||||
}
|
||||
|
||||
function stopFallbackOutput(stopReason, detail) {
|
||||
const reason = 'OMX plugin Stop hook launcher failed before valid native Stop JSON could be produced. Continue once, preserve runtime state, inspect hook launcher diagnostics, and retry.';
|
||||
return {
|
||||
decision: 'block',
|
||||
reason,
|
||||
stopReason,
|
||||
systemMessage: detail ? `${reason} Failure: ${detail}` : reason,
|
||||
};
|
||||
}
|
||||
|
||||
function writeStopFallback(stopReason, detail) {
|
||||
process.stdout.write(`${JSON.stringify(stopFallbackOutput(stopReason, detail))}\n`);
|
||||
process.exitCode = 0;
|
||||
}
|
||||
|
||||
function writeOversizedStopFallback(stopReason, detail) {
|
||||
const reason =
|
||||
'OMX plugin Stop rejected oversized stdin before launcher delegation; continue once with a smaller valid Stop JSON response.';
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
decision: 'block',
|
||||
reason,
|
||||
stopReason,
|
||||
systemMessage: detail ? `${reason} Failure: ${detail}` : reason,
|
||||
})}\n`);
|
||||
process.exitCode = 0;
|
||||
}
|
||||
|
||||
function writeOversizedStopNoop() {
|
||||
process.stdout.write('{}\n');
|
||||
process.exitCode = 0;
|
||||
}
|
||||
|
||||
function writeOversizedToolHookOutput(eventName) {
|
||||
const systemMessage = 'OMX native hook rejected oversized stdin JSON before parsing; maxBytes=1048576.';
|
||||
const output = eventName === 'PreToolUse'
|
||||
? {
|
||||
systemMessage,
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'PreToolUse',
|
||||
permissionDecision: 'deny',
|
||||
permissionDecisionReason: systemMessage,
|
||||
},
|
||||
}
|
||||
: {
|
||||
continue: false,
|
||||
stopReason: 'native_hook_stdin_oversized',
|
||||
systemMessage,
|
||||
};
|
||||
process.stdout.write(`${JSON.stringify(output)}\n`);
|
||||
process.exitCode = 0;
|
||||
}
|
||||
function writeCompactFallback() {
|
||||
process.exitCode = 0;
|
||||
}
|
||||
|
||||
function failLauncher(error, isStop, isCompact, stopReason = 'plugin_stop_hook_launcher_failure') {
|
||||
const detail = error instanceof Error ? error.message : String(error);
|
||||
console.error(`[oh-my-codex] ${detail}`);
|
||||
if (isStop) {
|
||||
writeStopFallback(stopReason, detail);
|
||||
return;
|
||||
}
|
||||
if (isCompact) {
|
||||
writeCompactFallback();
|
||||
return;
|
||||
}
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
function readPinnedLauncher() {
|
||||
const launcherPath = join(hookDir, 'omx-command.json');
|
||||
try {
|
||||
const raw = JSON.parse(readFileSync(launcherPath, 'utf8'));
|
||||
if (typeof raw.command !== 'string' || raw.command.trim() === '') {
|
||||
throw new Error('missing non-empty command');
|
||||
}
|
||||
const argsPrefix = Array.isArray(raw.argsPrefix) ? raw.argsPrefix : [];
|
||||
if (!argsPrefix.every((arg) => typeof arg === 'string')) {
|
||||
throw new Error('argsPrefix must contain only strings');
|
||||
}
|
||||
return { command: raw.command, argsPrefix };
|
||||
} catch (error) {
|
||||
if (error?.code === 'ENOENT') return null;
|
||||
throw new Error(`invalid plugin hook launcher ${launcherPath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function readConfiguredLauncher() {
|
||||
if (process.env.OMX_NATIVE_HOOK_COMMAND) {
|
||||
return { command: process.env.OMX_NATIVE_HOOK_COMMAND, argsPrefix: [] };
|
||||
}
|
||||
return readPinnedLauncher() ?? { command: 'omx', argsPrefix: [] };
|
||||
}
|
||||
|
||||
function buildSpawnOptions(command) {
|
||||
const options = {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
env: process.env,
|
||||
};
|
||||
|
||||
if (process.platform !== 'win32') return options;
|
||||
|
||||
const extension = extname(command).toLowerCase();
|
||||
if (extension === '.exe' || extension === '.com') {
|
||||
return { ...options, windowsHide: true };
|
||||
}
|
||||
|
||||
return { ...options, shell: true, windowsHide: true };
|
||||
}
|
||||
|
||||
function readJsonFile(path) {
|
||||
try {
|
||||
return JSON.parse(readFileSync(path, 'utf8'));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isTerminalOutcome(value) {
|
||||
return ['finish', 'finished', 'complete', 'completed', 'done', 'blocked', 'blocked-on-user', 'blocked_on_user', 'failed', 'fail', 'error', 'cancelled', 'canceled', 'cancel', 'aborted', 'abort', 'userinterlude', 'user-interlude', 'interrupted', 'interrupt', 'askuserquestion', 'ask-user-question', 'askuser', 'question'].includes(String(value ?? '').trim().toLowerCase());
|
||||
}
|
||||
|
||||
function isTerminalRunStateForMode(state, mode) {
|
||||
if (!state) return false;
|
||||
const runMode = String(state.mode ?? '').trim();
|
||||
if (runMode && runMode !== mode) return false;
|
||||
return isTerminalOutcome(state.outcome)
|
||||
|| isTerminalOutcome(state.run_outcome)
|
||||
|| isTerminalOutcome(state.lifecycle_outcome)
|
||||
|| isTerminalOutcome(state.terminal_outcome);
|
||||
}
|
||||
|
||||
function canonicalPath(path) {
|
||||
const absolute = resolve(path);
|
||||
if (!existsSync(absolute)) return absolute;
|
||||
try {
|
||||
return typeof realpathSync.native === 'function' ? realpathSync.native(absolute) : realpathSync(absolute);
|
||||
} catch {
|
||||
return absolute;
|
||||
}
|
||||
}
|
||||
|
||||
function sameFilePath(leftPath, rightPath) {
|
||||
return canonicalPath(leftPath) === canonicalPath(rightPath);
|
||||
}
|
||||
|
||||
function isSessionStateAuthoritativeForCwd(state, cwd) {
|
||||
if (!isSafeSessionId(state?.session_id)) return false;
|
||||
const sessionCwd = typeof state.cwd === 'string' ? state.cwd.trim() : '';
|
||||
return !sessionCwd || sameFilePath(sessionCwd, cwd);
|
||||
}
|
||||
|
||||
function listAuthoritativeStateBaseDirs(cwd) {
|
||||
if (process.env.OMX_TEAM_STATE_ROOT?.trim()) return [process.env.OMX_TEAM_STATE_ROOT.trim()];
|
||||
if (process.env.OMX_ROOT?.trim()) return [join(process.env.OMX_ROOT.trim(), '.omx', 'state')];
|
||||
if (process.env.OMX_STATE_ROOT?.trim()) return [join(process.env.OMX_STATE_ROOT.trim(), '.omx', 'state')];
|
||||
return [join(cwd, '.omx', 'state')];
|
||||
}
|
||||
|
||||
function isSafeSessionId(sessionId) {
|
||||
return typeof sessionId === 'string' && /^[A-Za-z0-9_-]{1,64}$/.test(sessionId.trim());
|
||||
}
|
||||
|
||||
function readCurrentSessionId(stateBaseDirs, cwd) {
|
||||
for (const stateDir of stateBaseDirs) {
|
||||
const session = readJsonFile(join(stateDir, 'session.json'));
|
||||
if (isSessionStateAuthoritativeForCwd(session, cwd)) return session.session_id.trim();
|
||||
}
|
||||
const envSessionId = process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID;
|
||||
return isSafeSessionId(envSessionId) ? envSessionId.trim() : null;
|
||||
}
|
||||
|
||||
function shouldContinueAutopilotState(state) {
|
||||
if (state?.active !== true) return false;
|
||||
return !(isTerminalOutcome(state.current_phase)
|
||||
|| isTerminalOutcome(state.run_outcome)
|
||||
|| isTerminalOutcome(state.lifecycle_outcome)
|
||||
|| isTerminalOutcome(state.terminal_outcome)
|
||||
|| isTerminalOutcome(state.outcome)
|
||||
|| (typeof state.completed_at === 'string' && state.completed_at.trim() !== ''));
|
||||
}
|
||||
|
||||
function hasActiveAutopilotStateForOversizedStop(input) {
|
||||
const text = input.toString('utf8');
|
||||
const cwd = extractTopLevelStringField(text, ['cwd']) || process.cwd();
|
||||
const stateBaseDirs = listAuthoritativeStateBaseDirs(cwd);
|
||||
const sessionId = readCurrentSessionId(stateBaseDirs, cwd);
|
||||
if (!isSafeSessionId(sessionId)) return false;
|
||||
|
||||
const sessionDir = join(stateBaseDirs[0], 'sessions', sessionId.trim());
|
||||
const terminalRunState = readJsonFile(join(sessionDir, 'run-state.json'));
|
||||
if (isTerminalRunStateForMode(terminalRunState, 'autopilot')) return false;
|
||||
|
||||
const sessionState = readJsonFile(join(sessionDir, 'autopilot-state.json'));
|
||||
return shouldContinueAutopilotState(sessionState);
|
||||
}
|
||||
|
||||
|
||||
function parseJsonObjectCandidate(text) {
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null;
|
||||
return parsed;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isJsonObjectFragmentLine(text) {
|
||||
return text.startsWith('{') || text.endsWith('}') || /^"[^"]+"\s*:/.test(text);
|
||||
}
|
||||
|
||||
function parseSingleJsonObjectOutput(raw) {
|
||||
const text = String(raw ?? '').trim();
|
||||
if (!text) return null;
|
||||
const direct = parseJsonObjectCandidate(text);
|
||||
if (direct) return direct;
|
||||
|
||||
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
||||
const lastLine = lines.at(-1);
|
||||
if (!lastLine || !lastLine.startsWith('{') || !lastLine.endsWith('}')) return null;
|
||||
for (const line of lines.slice(0, -1)) {
|
||||
if (parseJsonObjectCandidate(line) || isJsonObjectFragmentLine(line)) return null;
|
||||
}
|
||||
return parseJsonObjectCandidate(lastLine);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { input, oversized, totalBytes } = await readBoundedStdin({ drainOversized: true });
|
||||
const payload = parseHookPayload(input);
|
||||
const routedByLaunchCorrelation = isPluginHookRoutingSession(input, payload);
|
||||
const isStop = detectStopHookInput(input);
|
||||
const isCompact = detectCompactHookInput(input);
|
||||
|
||||
if (!routedByLaunchCorrelation) {
|
||||
writePlainCodexNoop(isStop);
|
||||
return;
|
||||
}
|
||||
|
||||
if (oversized) {
|
||||
const message = `plugin hook stdin exceeded ${MAX_WRAPPER_STDIN_BYTES} bytes before launcher delegation; totalBytes>${totalBytes}`;
|
||||
if (isStop) {
|
||||
if (hasActiveAutopilotStateForOversizedStop(input)) {
|
||||
console.error(`[oh-my-codex] ${message}`);
|
||||
writeOversizedStopFallback('plugin_stop_hook_stdin_oversized_active_workflow', message);
|
||||
return;
|
||||
}
|
||||
writeOversizedStopNoop();
|
||||
return;
|
||||
}
|
||||
const eventName = extractTopLevelHookEventName(input.toString('utf8'));
|
||||
if (eventName === 'PreToolUse' || eventName === 'PostToolUse') {
|
||||
writeOversizedToolHookOutput(eventName);
|
||||
return;
|
||||
}
|
||||
console.error(`[oh-my-codex] ${message}`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
let launcher;
|
||||
try {
|
||||
launcher = readConfiguredLauncher();
|
||||
} catch (error) {
|
||||
failLauncher(error, isStop, isCompact);
|
||||
return;
|
||||
}
|
||||
|
||||
const { command, argsPrefix } = launcher;
|
||||
const child = spawn(command, [...argsPrefix, 'codex-native-hook'], buildSpawnOptions(command));
|
||||
|
||||
const stdoutChunks = [];
|
||||
let stdoutBytes = 0;
|
||||
let bufferedStopStdoutBytes = 0;
|
||||
let stopStdoutOversized = false;
|
||||
let childSpawnError = null;
|
||||
let childStdinError = null;
|
||||
|
||||
child.stdout.on('data', (chunk) => {
|
||||
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
||||
stdoutBytes += buffer.byteLength;
|
||||
if (isStop) {
|
||||
if (!stopStdoutOversized) {
|
||||
const remaining = MAX_STOP_STDOUT_BYTES - bufferedStopStdoutBytes;
|
||||
if (remaining > 0) {
|
||||
const slice = buffer.subarray(0, remaining);
|
||||
stdoutChunks.push(slice);
|
||||
bufferedStopStdoutBytes += slice.byteLength;
|
||||
}
|
||||
if (buffer.byteLength > remaining) {
|
||||
stopStdoutOversized = true;
|
||||
child.stdout.destroy();
|
||||
child.kill();
|
||||
}
|
||||
}
|
||||
} else if (!isCompact) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
});
|
||||
child.stderr.pipe(process.stderr);
|
||||
child.stdin.on('error', (error) => {
|
||||
childStdinError = error;
|
||||
});
|
||||
child.on('error', (error) => {
|
||||
childSpawnError = error;
|
||||
});
|
||||
child.on('close', (code, signal) => {
|
||||
if (isStop && stopStdoutOversized) {
|
||||
writeStopFallback('plugin_stop_hook_launcher_stdout_oversized', `codex-native-hook produced more than ${MAX_STOP_STDOUT_BYTES} bytes of Stop hook stdout`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStop && stdoutBytes > 0) {
|
||||
const stdoutText = Buffer.concat(stdoutChunks).toString('utf8');
|
||||
const parsed = parseSingleJsonObjectOutput(stdoutText);
|
||||
if (parsed) {
|
||||
process.stdout.write(`${JSON.stringify(parsed)}\n`);
|
||||
process.exitCode = 0;
|
||||
return;
|
||||
}
|
||||
writeStopFallback('plugin_stop_hook_launcher_invalid_stdout', `codex-native-hook produced invalid Stop hook JSON stdout (${stdoutBytes} bytes)`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCompact) {
|
||||
process.exitCode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStop && stdoutBytes === 0) {
|
||||
if (childSpawnError) {
|
||||
writeStopFallback('plugin_stop_hook_launcher_spawn_error', `failed to launch ${command} codex-native-hook: ${childSpawnError.message}`);
|
||||
return;
|
||||
}
|
||||
if (signal) {
|
||||
writeStopFallback('plugin_stop_hook_launcher_signal', `codex-native-hook terminated by ${signal}`);
|
||||
return;
|
||||
}
|
||||
if (code && code !== 0) {
|
||||
if (childStdinError) {
|
||||
writeStopFallback('plugin_stop_hook_launcher_stdin_error', `codex-native-hook stdin failed: ${childStdinError.message}`);
|
||||
return;
|
||||
}
|
||||
writeStopFallback('plugin_stop_hook_launcher_exit', `codex-native-hook exited with code ${code}`);
|
||||
return;
|
||||
}
|
||||
writeStopFallback('plugin_stop_hook_launcher_empty_stdout', 'codex-native-hook exited successfully without producing Stop hook JSON');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCompact) {
|
||||
if (childSpawnError) {
|
||||
console.error(`[oh-my-codex] failed to launch ${command} codex-native-hook: ${childSpawnError.message}`);
|
||||
} else if (signal) {
|
||||
console.error(`[oh-my-codex] codex-native-hook terminated by ${signal}`);
|
||||
} else if (code && code !== 0) {
|
||||
console.error(`[oh-my-codex] codex-native-hook exited with code ${code}`);
|
||||
}
|
||||
process.exitCode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (childSpawnError) {
|
||||
console.error(`[oh-my-codex] failed to launch ${command} codex-native-hook: ${childSpawnError.message}`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
if (signal) {
|
||||
console.error(`[oh-my-codex] codex-native-hook terminated by ${signal}`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
process.exitCode = code ?? 0;
|
||||
});
|
||||
|
||||
child.stdin.end(input);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(`[oh-my-codex] plugin hook launcher failed: ${error instanceof Error ? error.message : String(error)}`);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\""
|
||||
}
|
||||
],
|
||||
"matcher": "startup|resume|clear"
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreCompact": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostCompact": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${PLUGIN_ROOT}/hooks/codex-native-hook.mjs\"",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
---
|
||||
name: ai-slop-cleaner
|
||||
description: Run an anti-slop cleanup/refactor/deslop workflow
|
||||
---
|
||||
|
||||
# AI Slop Cleaner Task Card
|
||||
|
||||
Use this bounded helper for cleanup/refactor/deslop work, not as a competing top-level
|
||||
workflow. Shared operating invariants live in `templates/AGENTS.md`; this card defines
|
||||
scope, smell taxonomy, passes, and evidence.
|
||||
|
||||
## When to use and inputs
|
||||
|
||||
Use when working code is bloated, noisy, repetitive, over-abstracted, or AI-generated;
|
||||
the user requests cleanup/refactor/deslop; or a follow-up left duplicate/dead code,
|
||||
weak boundaries, missing tests, fallback-like paths, or wrappers. Inputs are the requested
|
||||
feature/files and behavior to preserve. A file list scope is valid; keep the pass bounded
|
||||
to it. Limit the pass to the calling task's changed files unless broader cleanup was requested.
|
||||
|
||||
## Before editing
|
||||
|
||||
1. **Lock behavior with regression tests first**: identify behavior to preserve, run/add the narrowest targeted tests, and cover both primary and preserved compatibility/fail-safe fallback paths.
|
||||
2. **Create a cleanup plan before code**: list scope and smells, include fallback findings/classifications/escalation, and order safest/highest-signal fixes first.
|
||||
3. **Inventory fallback-like code** in scope: quick hacks, temporary workaround, temporary fallback, just bypass, just skip, fallback if it fails, swallowed errors, silent defaults, broad compatibility shims, and duplicate alternate execution paths.
|
||||
4. Classify each fallback: **Masking fallback slop** hides evidence, bypasses the contract, suppresses validation, swallows failures, silently defaults, or adds untested paths; **Grounded compatibility/fail-safe fallback** is narrow at an external/version/fail-safe boundary, documents rationale, preserves failure evidence, and tests primary plus fallback.
|
||||
5. Prefer root-cause repair, deletion, boundary repair, or explicit failure behavior. For broad/ambiguous/cross-layer/architectural findings, invoke `$ralplan` for consensus resolution; when already inside ralplan, ultragoal, team, or another OMX workflow, do not spawn a nested `$ralplan`—attach the finding to the active handoff.
|
||||
|
||||
## Smell taxonomy and passes
|
||||
|
||||
Classify before changing:
|
||||
|
||||
- **Fallback-like code**: masking fallbacks, workaround branches, bypasses, swallowed errors, silent defaults, broad shims, alternate paths.
|
||||
- **Duplication**: repeated logic, copy-paste branches, redundant helpers.
|
||||
- **Dead code**: unused/unreachable code, stale flags, debug leftovers.
|
||||
- **Needless abstraction**: pass-through wrappers, speculative indirection, single-use layers.
|
||||
- **Boundary violations**: hidden coupling, leaky responsibilities, wrong-layer imports/side effects.
|
||||
- **Missing tests**: behavior not locked or edge cases uncovered.
|
||||
- **UI/design slop**: context-sensitive signals, not absolute bans; preserve intentional brand, design-system, accessibility, or product-context exceptions. Challenge Korean body text at 11-12px (generally 14px or larger); gratuitous box shadows; repetitive eyebrow + title + description + paragraph stacks and generic emoji badges; default AI blue/purple such as `#3B82F6`; reflexive 3-column or 4-column grids; and extreme gradients unless justified by context.
|
||||
|
||||
Resolve the fallback-like code resolution gate first, then one smell at a time:
|
||||
**Pass 1: Dead code deletion**; **Pass 2: Duplicate removal**; **Pass 3: Naming/error
|
||||
handling cleanup**; **Pass 4: Test reinforcement**. Re-run targeted verification after
|
||||
each pass and avoid unrelated refactors. Prefer deletion/existing utilities; no new
|
||||
abstractions or dependencies unless explicitly required.
|
||||
|
||||
## Evidence/output contract
|
||||
|
||||
Report:
|
||||
|
||||
```text
|
||||
AI SLOP CLEANUP REPORT
|
||||
Scope: [files/feature]
|
||||
Behavior Lock: [targeted tests added/run]
|
||||
Cleanup Plan: [bounded smells/order]
|
||||
Fallback Findings: [finding -> masking fallback slop | grounded compatibility/fail-safe fallback -> escalation]
|
||||
UI/Design Findings: [none/N/A or signal -> action/defer -> intentional rationale]
|
||||
Passes Completed: [resolution gate; Passes 1-4]
|
||||
Quality Gates: Regression tests, Lint, Typecheck, Tests, Static/security scan (PASS/FAIL/N/A)
|
||||
Changed Files: [path -> simplification]
|
||||
Remaining Risks: [none or deferred item]
|
||||
```
|
||||
|
||||
Include changed files, simplifications, fallback classifications/escalation status,
|
||||
tests/diagnostics/build checks run, UI findings when relevant, and deferred risks. Keep
|
||||
writer/reviewer separation for cleanup plans and approvals.
|
||||
|
||||
## Exit condition
|
||||
|
||||
Stop when the requested scope has behavior-lock evidence, each selected smell pass is
|
||||
complete or explicitly deferred with rationale, verification is reported, and no
|
||||
unrelated files or temporary artifacts remain. Never present an unverified cleanup as
|
||||
complete; escalate a real architectural blocker rather than masking it.
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
name: analyze
|
||||
description: "Run read-only deep repository analysis and return a ranked synthesis with explicit confidence, concrete file references, and clear evidence-vs-inference boundaries. Use when a user says 'analyze', 'investigate', 'why does', 'what's causing', or needs grounded cross-file explanation before any changes are proposed."
|
||||
---
|
||||
|
||||
# Analyze — Read-Only Deep Analysis
|
||||
|
||||
Use `$analyze` to answer a repository question with grounded, read-only evidence. Explain what the code most likely says; do not turn analysis into implementation or generic fix planning.
|
||||
|
||||
Shared operating, delegation, state, hook, team, cancellation, and verification invariants live in [`templates/AGENTS.md`](../../templates/AGENTS.md). Follow that source instead of duplicating its rules here.
|
||||
|
||||
## Use when
|
||||
|
||||
- The user needs a causal, architectural, behavioral, impact, or tradeoff explanation.
|
||||
- The answer requires tracing multiple files or boundaries, or ranking plausible explanations.
|
||||
- The user needs confidence and concrete evidence before changing anything.
|
||||
|
||||
Do not use it for edits, implementation, a new product plan, a simple one-file lookup, or OMX team-runtime operation.
|
||||
|
||||
## Inputs and method
|
||||
|
||||
1. Restate the question and define the evidence-backed scope.
|
||||
2. Identify the smallest files, tests, configs, and docs likely to answer it.
|
||||
3. Read direct code paths and contracts first; trace boundaries only as far as needed.
|
||||
4. Compare competing explanations, rank them by support, and mark unresolved points.
|
||||
5. Stop when the question is answered with sufficient evidence, or name the smallest read-only probe that would resolve the remaining uncertainty.
|
||||
|
||||
## Evidence discipline
|
||||
|
||||
Label every material claim as one of:
|
||||
|
||||
- **Evidence** — directly shown by code, tests, generated artifacts, configuration, or docs.
|
||||
- **Inference** — a reasoned conclusion drawn from cited evidence.
|
||||
- **Unknown** — not settled by the repository evidence.
|
||||
|
||||
Prefer direct paths and independent corroboration over contextual clues. Never present guesses as evidence or inference, and never overclaim certainty.
|
||||
|
||||
## Output contract
|
||||
|
||||
Answer the asked question first and use this shape:
|
||||
|
||||
### Question
|
||||
Restated question, briefly.
|
||||
|
||||
### Ranked synthesis
|
||||
| Rank | Explanation | Confidence | Basis |
|
||||
|------|-------------|------------|-------|
|
||||
| 1 | ... | High / Medium / Low | strongest supporting evidence |
|
||||
| 2 | ... | High / Medium / Low | why it trails |
|
||||
| 3 | ... | High / Medium / Low | why it remains possible |
|
||||
|
||||
### Evidence
|
||||
- `path/to/file:line-line` — direct observation.
|
||||
- `path/to/file:line-line` — corroborating observation.
|
||||
|
||||
### Inference
|
||||
- What the evidence most strongly implies.
|
||||
- Why weaker alternatives were down-ranked.
|
||||
|
||||
### Unknowns / limits
|
||||
- What the repository does not establish.
|
||||
- The next discriminating read-only probe, when useful.
|
||||
|
||||
## Stop conditions
|
||||
|
||||
- Do not edit files, run an implementation lane, or make recommendations the evidence cannot support.
|
||||
- Do not continue searching after the answer and confidence boundary are grounded.
|
||||
- If evidence is insufficient, report the limit explicitly rather than manufacturing certainty.
|
||||
|
||||
Task: {{ARGUMENTS}}
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
name: ask
|
||||
description: Ask a local external advisor CLI (Claude or Gemini) and capture a reusable artifact
|
||||
---
|
||||
|
||||
# Ask (Local Advisor CLI)
|
||||
|
||||
Use a locally installed external advisor CLI for focused questions, reviews, brainstorming, or second opinions. This skill replaces the separate `ask-claude` and `ask-gemini` skills.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
$ask claude <question or task>
|
||||
$ask gemini <question or task>
|
||||
omx ask claude "<question or task>"
|
||||
omx ask gemini "<question or task>"
|
||||
```
|
||||
|
||||
## Backend selection
|
||||
|
||||
- Use `claude` when the user asks for Claude, Anthropic, or the previous `$ask-claude` behavior.
|
||||
- Use `gemini` when the user asks for Gemini or the previous `$ask-gemini` behavior.
|
||||
- If no backend is specified, choose the installed backend that best matches the user request; if neither is clearly available, explain that a local CLI is required.
|
||||
|
||||
## Local CLI commands
|
||||
|
||||
Claude:
|
||||
|
||||
```bash
|
||||
omx ask claude "{{ARGUMENTS}}"
|
||||
claude -p "{{ARGUMENTS}}"
|
||||
```
|
||||
|
||||
Gemini:
|
||||
|
||||
```bash
|
||||
omx ask gemini "{{ARGUMENTS}}"
|
||||
gemini -p "{{ARGUMENTS}}"
|
||||
```
|
||||
|
||||
If needed, adapt to the user's installed CLI variant while keeping local execution as the default path. Do not silently switch to an MCP or remote provider when the local binary is missing.
|
||||
|
||||
## Artifact requirement
|
||||
|
||||
After local execution, save a markdown artifact to:
|
||||
|
||||
```text
|
||||
.omx/artifacts/ask-<backend>-<slug>-<timestamp>.md
|
||||
```
|
||||
|
||||
Minimum artifact sections:
|
||||
1. Original user task
|
||||
2. Backend and final prompt sent to the CLI
|
||||
3. Raw CLI output
|
||||
4. Concise summary
|
||||
5. Action items / next steps
|
||||
|
||||
Task: {{ARGUMENTS}}
|
||||
@@ -1,128 +0,0 @@
|
||||
---
|
||||
name: autopilot
|
||||
description: "[OMX] Canonical autonomous orchestrator: $deep-interview -> $ralplan -> $ultragoal"
|
||||
---
|
||||
|
||||
# Autopilot
|
||||
|
||||
<Purpose>
|
||||
Autopilot is the first-class canonical orchestrator for hands-off delivery. Its defining default chain is mandatory:
|
||||
|
||||
```text
|
||||
$deep-interview -> $ralplan -> $ultragoal
|
||||
```
|
||||
|
||||
The chain is not a list of optional hints and Autopilot is not an alias for direct execution. Autopilot supervises each child stage in one recoverable session, carries durable artifacts forward, and continues through implementation verification until the requested outcome is complete or a genuine blocker is recorded.
|
||||
</Purpose>
|
||||
|
||||
<Use_When>
|
||||
- The user explicitly invokes `$autopilot`.
|
||||
- The user asks for end-to-end autonomous delivery from an idea, issue, or requirements seed.
|
||||
- The work needs requirements clarification, consensus planning, durable execution, and evidence-backed completion as one supervised workflow.
|
||||
</Use_When>
|
||||
|
||||
<Default_Chain>
|
||||
Run or resume these stages in order:
|
||||
|
||||
1. **`deep-interview`** — clarify intent, scope, non-goals, constraints, acceptance criteria, and unresolved decisions. Produce a durable requirements/specification handoff. Deep Interview is a real stage; do not replace it with a one-question check or skip it merely because the task looks actionable.
|
||||
2. **`ralplan`** — turn the clarified requirements into an execution-ready consensus plan with architecture, sequencing, test, and verification guidance. Preserve review evidence as lifecycle evidence, not host-issued security authority.
|
||||
3. **`ultragoal`** — execute the approved plan through durable goals and ledger receipts, implementation, focused verification, cleanup, review, and terminal evidence.
|
||||
|
||||
`$team`, `$code-review`, and `$ultraqa` may be used inside the supervised execution when the active Ultragoal plan or verification boundary requires them. They do not replace or weaken the defining three-stage chain.
|
||||
|
||||
When review or QA proves the requirements or plan wrong, keep Autopilot active, return its supervised phase to `ralplan`, attach the findings, and continue through `ultragoal` again. Implementation-only review fixes may remain within Ultragoal's blocker/review loop.
|
||||
</Default_Chain>
|
||||
|
||||
<Execution_Policy>
|
||||
- Autopilot MUST begin at `deep-interview` for a new run and MUST preserve the phase order `deep-interview -> ralplan -> ultragoal`.
|
||||
- Child stages are supervised phases, not peer workflow activations. Keep `mode:"autopilot"` active and update `current_phase` rather than replacing Autopilot with standalone child state.
|
||||
- Use the current CLI state SSOT (`omx state ... --json`) and the current session-scoped state root. Do not create a second writer or revive legacy root-state authority.
|
||||
- Local artifacts, prompts, trackers, transcripts, environment values, and role labels are lifecycle evidence only. Do not reintroduce the retired unrecoverable host-receipt lock or terminalize the workflow because host provenance is unavailable.
|
||||
- Authority-decreasing operations are always recoverable: `$cancel`, state clear, hook disable/uninstall recovery, and stale-state repair must remain available without completion receipts or child-stage approval.
|
||||
- Continue automatically through safe, reversible stage transitions. Stop only for an explicit user cancellation, a human-only dependency, or a verified terminal result.
|
||||
</Execution_Policy>
|
||||
|
||||
<State_Management>
|
||||
Autopilot state is session-scoped and owned by the canonical state writer. A new run records at least:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "autopilot",
|
||||
"active": true,
|
||||
"current_phase": "deep-interview",
|
||||
"iteration": 1,
|
||||
"phase_cycle": ["deep-interview", "ralplan", "ultragoal"],
|
||||
"handoff_artifacts": {
|
||||
"deep_interview": null,
|
||||
"ralplan": null,
|
||||
"ultragoal": null
|
||||
},
|
||||
"return_to_ralplan_reason": null
|
||||
}
|
||||
```
|
||||
|
||||
Start or update state only through the CLI-first state surface:
|
||||
|
||||
```bash
|
||||
omx state write --input '{"mode":"autopilot","active":true,"current_phase":"deep-interview","state":{"phase_cycle":["deep-interview","ralplan","ultragoal"],"handoff_artifacts":{"deep_interview":null,"ralplan":null,"ultragoal":null},"return_to_ralplan_reason":null}}' --json
|
||||
```
|
||||
|
||||
Stage transitions:
|
||||
|
||||
- **`deep-interview -> ralplan`**: require a durable clarified requirements/specification artifact and a non-empty completion rationale. Preserve same-session question evidence when a question surface was used.
|
||||
- **`ralplan -> ultragoal`**: require durable planning artifacts, sequential Architect then Critic approvals, and a session/review-cycle-bound `ralplan_execution_handoff`. For supervised Autopilot, the explicit Autopilot request is the source of that bound handoff. Ordinary progression MUST NOT depend on a host-issued consensus receipt; missing host provenance is not a blocker.
|
||||
- **`ultragoal -> complete`**: require durable goal/ledger completion receipts and fresh verification evidence matching the requested outcome.
|
||||
- **review/QA loopback**: keep Autopilot active, set `current_phase:"ralplan"`, and persist `return_to_ralplan_reason` plus the findings.
|
||||
- **cancellation**: run `$cancel`; preserve handoff artifacts for inspection or resume and mark the exact session terminal without deleting unrelated state.
|
||||
- **clear/recovery**: exact-session clear may remove corrupt or stale Autopilot state. It must not require forward-progression evidence and must not clear other skills or sessions.
|
||||
</State_Management>
|
||||
|
||||
<Continuation_And_Resume>
|
||||
When the user says `continue`, `resume`, or `keep going`, read the current session's `autopilot-state.json` and continue from `current_phase`:
|
||||
|
||||
- `deep-interview`: resume clarification and produce the requirements handoff.
|
||||
- `ralplan`: resume consensus planning from the deep-interview artifact and any loopback findings.
|
||||
- `ultragoal`: resume durable execution from the approved plan and ledger.
|
||||
- `complete`: report the terminal evidence; do not restart.
|
||||
- `cancelled`, `cleared`, or `failed`: report preserved artifacts and start a new run only on a new explicit Autopilot request.
|
||||
|
||||
Never discard valid handoff artifacts or restart discovery merely because the conversation resumed.
|
||||
</Continuation_And_Resume>
|
||||
|
||||
<Recovery_Contract>
|
||||
Autopilot continuation hooks may nudge an active non-terminal run, but they MUST fail open for recovery:
|
||||
|
||||
- A stale, malformed, cancelled, cleared, complete, or foreign-session state must not keep Stop blocked.
|
||||
- `$cancel`, exact-session state clear, and setup hook disable/uninstall paths must remain executable while Autopilot is active or damaged.
|
||||
- Continuation must be bounded and session-scoped; never recreate an unconditional hook lock.
|
||||
</Recovery_Contract>
|
||||
|
||||
<Final_Checklist>
|
||||
- [ ] A new run started with `deep-interview`.
|
||||
- [ ] Deep Interview produced a durable clarified requirements/specification handoff.
|
||||
- [ ] Ralplan produced an execution-ready plan with lifecycle review evidence.
|
||||
- [ ] Ultragoal completed the plan with durable goals and ledger receipts.
|
||||
- [ ] Verification evidence covers the requested behavior, failure paths, and recovery paths.
|
||||
- [ ] Missing host provenance did not block ordinary progression, cancel, clear, or hook recovery.
|
||||
- [ ] Autopilot state is terminal and exact-session artifacts remain coherent.
|
||||
</Final_Checklist>
|
||||
|
||||
<Examples>
|
||||
<Good>
|
||||
User: `$autopilot implement issue #42`
|
||||
Flow: activate Autopilot at `deep-interview`, crystallize requirements, continue to supervised `ralplan`, then supervised `ultragoal`, verify the completed outcome, and mark the exact session complete.
|
||||
</Good>
|
||||
|
||||
<Good>
|
||||
User: `continue`
|
||||
Context: Autopilot state has `current_phase:"ralplan"`.
|
||||
Flow: resume the existing plan from the deep-interview handoff; do not create a separate standalone Ralplan run.
|
||||
</Good>
|
||||
|
||||
<Bad>
|
||||
Flow: treat `$deep-interview`, `$ralplan`, and `$ultragoal` as optional suggestions and jump directly to implementation.
|
||||
Why bad: this hollows out Autopilot's defining orchestration contract.
|
||||
</Bad>
|
||||
</Examples>
|
||||
|
||||
Task: {{ARGUMENTS}}
|
||||
@@ -1,72 +0,0 @@
|
||||
---
|
||||
name: autoresearch
|
||||
description: Stateful validator-gated research loop with native-hook persistence
|
||||
---
|
||||
|
||||
# Autoresearch
|
||||
|
||||
Autoresearch is the skill-first replacement for the deprecated `omx autoresearch` command.
|
||||
It keeps the useful measured-research loop, but it now runs as a native-hook stateful workflow instead of a direct CLI or tmux launch surface.
|
||||
|
||||
## Boundary with planning research
|
||||
|
||||
Use `$autoresearch` when the research output itself is a bounded deliverable that must pass an explicit validator. Do not recommend it for ordinary pre-planning docs lookup or general best-practice checks; use `$best-practice-research` for that. If `$autoresearch` is intentionally run before architecture planning, its approved artifact should feed evidence into `$ralplan`; it should not become a final architecture/component unless the user explicitly asks for ongoing research automation.
|
||||
|
||||
## Use when
|
||||
- You want a Ralph-ish persistent research loop
|
||||
- The task should keep nudging until explicit validation evidence exists
|
||||
- You want init-time choice between script validation and prompt+architect validation
|
||||
|
||||
## Do not use when
|
||||
- You want the old `omx autoresearch` command surface (hard-deprecated)
|
||||
- You want detached tmux or split-pane launch parity
|
||||
- You have not decided the validation regime yet
|
||||
|
||||
## Core contract
|
||||
1. **Init chooses validation mode.** Pick exactly one:
|
||||
- `mission-validator-script`
|
||||
- `prompt-architect-artifact`
|
||||
2. **Persist mode state** in `.omx/state/.../autoresearch-state.json` including:
|
||||
- `validation_mode`
|
||||
- `completion_artifact_path`
|
||||
- `mission_validator_command` **or** `validator_prompt`
|
||||
- optional `output_artifact_path`
|
||||
3. **Completion is artifact-gated.** The loop does not stop because the model says “done”, because a stop hook fired once, or because several turns were no-ops.
|
||||
4. **Direct CLI launch is gone.** Use `$deep-interview --autoresearch` for intake and `$autoresearch` for execution.
|
||||
|
||||
## Completion artifact contract
|
||||
|
||||
### `mission-validator-script`
|
||||
The completion artifact must exist and record a passing validator result, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"summary": "metric improved beyond baseline"
|
||||
}
|
||||
```
|
||||
|
||||
### `prompt-architect-artifact`
|
||||
The completion artifact must include both an architect approval verdict and an output artifact path, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"validator_prompt": "Review the research output against the mission.",
|
||||
"architect_review": { "verdict": "approved" },
|
||||
"output_artifact_path": ".omx/specs/autoresearch-demo/report.md"
|
||||
}
|
||||
```
|
||||
|
||||
## Recommended flow
|
||||
1. Run `$deep-interview --autoresearch` to clarify mission + evaluator.
|
||||
2. Materialize `.omx/specs/autoresearch-{slug}/mission.md`, `sandbox.md`, and `result.json`.
|
||||
3. Start `$autoresearch` with the chosen validation mode stored in mode state.
|
||||
4. Let stop-hook / auto-nudge continue until the completion artifact satisfies the chosen validation mode.
|
||||
5. Finish only after the validator artifact is complete.
|
||||
|
||||
## Migration note
|
||||
- `omx autoresearch` is hard-deprecated.
|
||||
- No direct CLI launch.
|
||||
- No tmux split-pane launch.
|
||||
- No noop-count completion gate.
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
name: best-practice-research
|
||||
description: "[OMX] Bounded best-practice research wrapper using official/upstream evidence first"
|
||||
argument-hint: "<technology|decision|practice question>"
|
||||
---
|
||||
|
||||
# Best-Practice Research
|
||||
|
||||
Use this skill when a task depends on current external best practices, version-aware guidance, standards, official recommendations, or upstream behavior. This is a workflow wrapper: it routes evidence gathering and synthesis; it is not a new research authority and it does not replace `researcher`.
|
||||
|
||||
## Purpose
|
||||
|
||||
Produce a cited, reusable best-practice answer or handoff that separates current external evidence from repo-local facts and dependency-selection decisions. For pre-planning investigation, this is the ordinary first research wrapper: gather official/upstream evidence, then hand it to `$ralplan` or the caller as planning input. Do not present `$best-practice-research` as a final architecture component or as a validator-gated research loop.
|
||||
|
||||
## Terminal By Default
|
||||
|
||||
This skill is terminal and read-only by default. It gathers evidence and produces a cited recommendation with a handoff, then stops. Do not write or edit files, create or amend commits, run mutating commands, or otherwise modify repository state under this skill — even when the question has clear implementation implications. When implementation is warranted, stop and hand off rather than continuing: name `$ralplan` for planning and `$ultragoal`, `$team`, or `executor` for execution, and resume only after the user explicitly switches to that workflow.
|
||||
|
||||
## Activate When
|
||||
|
||||
- The user asks for best practices, recommended approach, current guidance, official recommendations, standards, or version-aware external behavior.
|
||||
- `$ralplan`, `$deep-interview`, `$team`, or another workflow needs current external evidence before planning or execution can be correct.
|
||||
- The task involves an already chosen technology and needs authoritative usage guidance, migration notes, API behavior, lifecycle rules, or current safety guidance.
|
||||
|
||||
## Do Not Activate When
|
||||
|
||||
- The answer is fully repo-local; use `explore` for codebase facts.
|
||||
- The main question is whether to adopt, replace, upgrade, or compare dependencies; use `dependency-expert`.
|
||||
- The user only needs implementation against already-grounded requirements; execute directly, or use `$team` when coordinated parallel work is warranted.
|
||||
- The task can be answered from stable local project conventions without current external lookup.
|
||||
|
||||
## Specialist Routing
|
||||
|
||||
1. Use `explore` first for brownfield facts: current code usage, local constraints, versions, config, and integration points.
|
||||
2. Use `researcher` for official/upstream docs, release notes, standards, migration guides, source-backed examples, and current best-practice evidence for an already chosen technology.
|
||||
3. Use `dependency-expert` only for adoption/upgrade/replacement/comparison decisions.
|
||||
4. Return to the caller with explicit evidence, uncertainty, and any implementation handoff constraints.
|
||||
|
||||
## Source-Quality Rules
|
||||
|
||||
- Prefer official documentation, upstream source, release notes, changelogs, standards, and maintainer guidance.
|
||||
- Include source URLs for material claims.
|
||||
- State date/version context for current best-practice claims.
|
||||
- Label third-party summaries as supplemental; do not use them before official/upstream sources.
|
||||
- Flag stale, conflicting, undocumented, or version-mismatched evidence.
|
||||
- Do not over-fetch: gather the smallest evidence set that can support the decision.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Classify the question: conceptual best practice, implementation guidance, migration/version guidance, standards/compliance guidance, or mixed local + external guidance.
|
||||
2. Gather repo-local facts with `explore` when local usage or constraints affect the answer.
|
||||
3. Gather external evidence with `researcher` when current or version-aware practice affects correctness.
|
||||
4. Synthesize a concise answer with source quality, version/date context, caveats, and an implementation or planning handoff.
|
||||
5. Stop when the answer is grounded enough for the caller; otherwise report the exact blocker or specialist handoff needed.
|
||||
|
||||
## Output Contract
|
||||
|
||||
```md
|
||||
## Best-Practice Research: <question>
|
||||
|
||||
### Direct Recommendation
|
||||
<actionable guidance or decision support>
|
||||
|
||||
### Evidence Used
|
||||
- Official/upstream: <source URL> — <what it establishes>
|
||||
- Supplemental, if any: <source URL> — <why it is secondary>
|
||||
|
||||
### Version / Date Context
|
||||
<versions, dates, release channels, or unknowns>
|
||||
|
||||
### Repo-Local Context
|
||||
<facts from explore, or "not needed">
|
||||
|
||||
### Boundaries / Non-goals
|
||||
<what this research does not decide>
|
||||
|
||||
### Handoff
|
||||
<planning/execution/test implications; name the next workflow — `$ralplan` for planning, `$ultragoal`/`$team`/`executor` for execution — and note that this skill stops here unless the user explicitly switches workflows>
|
||||
```
|
||||
|
||||
## Stop Rules
|
||||
|
||||
- Stop after a source-backed recommendation is reusable by the caller.
|
||||
- Stop and route upward if the task becomes dependency comparison, broad architecture, or implementation.
|
||||
- Do not continue researching when remaining work would only polish wording rather than change the recommendation.
|
||||
- This skill never implements. After delivering the recommendation and handoff, stop; do not modify repo files or repo state. Resume only when the user explicitly switches to a planning or implementation workflow named in the handoff.
|
||||
|
||||
Task: {{ARGUMENTS}}
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
name: cancel
|
||||
description: Cancel any active OMX mode (autopilot, ralph, ultrawork, ecomode, ultraqa, swarm, ultrapilot, pipeline, team)
|
||||
---
|
||||
|
||||
# Cancel Skill
|
||||
|
||||
## When to use
|
||||
|
||||
Use `/cancel` (or `cancelomc` / `stopomc`) to stop an active OMX mode and perform its documented cleanup. Read `AGENTS.md#durable-runtime-invariants-canonical-ssot` first; it is the invariant authority for scope, ownership, hooks, and Team cleanup.
|
||||
|
||||
## Inputs and preconditions
|
||||
|
||||
- Accepted forms are `/cancel` and `/cancel --force`.
|
||||
- Reject `--all`, unknown flags, repeated flags, and mixed flags before mutation.
|
||||
- Discover the current session/root with the state read surfaces before cleanup:
|
||||
```bash
|
||||
omx state list-active --json
|
||||
omx state get-status --mode <mode> --json
|
||||
```
|
||||
- Confirm the current session/root and exact target before cleanup; stop when the required evidence is missing.
|
||||
|
||||
## Operational steps
|
||||
|
||||
1. Parse and validate arguments; select one exact session/root scope.
|
||||
2. Inspect active modes in dependency order:
|
||||
`autopilot → ralph → ultrawork → ecomode → ultraqa → swarm → ultrapilot → pipeline → team → plan-consensus`.
|
||||
3. Cancel Autopilot while preserving its resumable state; clean only linked child modes it owns.
|
||||
4. For Ralph, terminalize the target state (`active:false`, `current_phase:"cancelled"`, `completed_at`) and terminalize a proven linked Ultrawork/Ecomode state in the same scope. Confirm the post-conditions in `docs/contracts/ralph-cancel-contract.md`.
|
||||
5. Clear standalone modes only after proving they are not linked to another active mode.
|
||||
6. For Team, inspect the runtime with `omx team status <team> --json`; use `omx team shutdown <team>` after the normal completion gate, or follow the explicit abort path.
|
||||
7. Clear only the selected mode/session state through the state API, for example:
|
||||
```bash
|
||||
omx state clear --input '{"mode":"<mode>","session_id":"<session>"}' --json
|
||||
```
|
||||
8. With `--force`, perform its documented native-stop cleanup. Report rollback or cleanup failures rather than claiming success.
|
||||
|
||||
## Exit and evidence
|
||||
|
||||
Report one result per selected mode: mode, scope/session, terminal phase, preserved state, linked cleanup, and any refusal reason. A clean no-op reports `No active OMX modes detected.` Include Team status and shutdown evidence when Team was selected.
|
||||
@@ -1,118 +0,0 @@
|
||||
---
|
||||
name: code-review
|
||||
description: Run a comprehensive code review
|
||||
---
|
||||
|
||||
# Code Review Task Card
|
||||
|
||||
Use this explicit opt-in for a merge-readiness review. Shared operating invariants
|
||||
live in `templates/AGENTS.md`; this card only defines review-specific behavior.
|
||||
|
||||
## When to use
|
||||
|
||||
- The user asks for a code review or quality/security assessment.
|
||||
- A change is ready for review before merge, or a major feature needs an independent review.
|
||||
- Do not activate this card for implementation, broad planning, or automatic cleanup.
|
||||
|
||||
## Inputs
|
||||
|
||||
- Scope: the requested files, commit, PR, or whole diff.
|
||||
- Requirements/specification, acceptance criteria, and relevant test/CI evidence.
|
||||
- Existing review artifacts and known risks, if any.
|
||||
- If the user says `continue`, advance the current verified review step rather than restarting discovery.
|
||||
|
||||
Start by recording the scope:
|
||||
|
||||
```sh
|
||||
git status --short
|
||||
git diff --stat
|
||||
git diff -- <scope>
|
||||
```
|
||||
|
||||
## Execution
|
||||
|
||||
1. Identify changed files and review boundaries; do not silently widen the scope.
|
||||
2. Launch the `code-reviewer` and `architect` agents in parallel. Both lanes run in parallel on a clean context with explicit scope and artifacts. If either lane cannot be launched or does not return evidence, report `independent review unavailable`; do **not** substitute the current/authoring lane, and do **not** approve or mark the review merge-ready.
|
||||
3. Respect the user's current model and reasoning/effort selection. Do not pass `model` or `reasoning_effort` overrides in review-lane calls.
|
||||
|
||||
```text
|
||||
task(
|
||||
agent_type="code-reviewer",
|
||||
prompt="CODE REVIEW TASK
|
||||
|
||||
Review the supplied scope for spec compliance, security, quality, performance, and maintainability.
|
||||
Return files reviewed, severity-rated findings with file:line evidence and concrete fixes,
|
||||
and a recommendation: APPROVE / REQUEST CHANGES / COMMENT. Do not review architecture.
|
||||
Scope: [scope and artifacts]"
|
||||
)
|
||||
|
||||
task(
|
||||
agent_type="architect",
|
||||
prompt="ARCHITECTURE / DEVIL'S-ADVOCATE REVIEW TASK
|
||||
|
||||
Review the same scope for boundaries, interfaces, hidden coupling, long-term tradeoffs,
|
||||
and the strongest counterargument against approval. Return file:line evidence,
|
||||
recommendations, and Architectural Status: CLEAR / WATCH / BLOCK.
|
||||
Scope: [scope and artifacts]"
|
||||
)
|
||||
```
|
||||
|
||||
## Review taxonomy
|
||||
|
||||
- `code-reviewer` checks **Security**, **Code Quality**, **Performance**, **Best Practices**, and **Maintainability**.
|
||||
- Rate each finding: **CRITICAL** (security or data-loss blocker), **HIGH** (bug/major smell), **MEDIUM** (important improvement), or **LOW** (style/suggestion).
|
||||
- `architect` checks explicit boundaries/interfaces, hidden coupling, long-horizon tradeoffs, and devil's-advocate concerns. Status is **CLEAR**, **WATCH** (non-blocking concern), or **BLOCK** (merge blocker).
|
||||
- Every finding names `file:line`, issue, risk, and a concrete fix; distinguish facts from suggestions.
|
||||
|
||||
## State/HUD Phase Contract
|
||||
|
||||
- Standalone `$code-review` relies on hook-owned `skill-active-state.json` (`skill:"code-review"`, `phase:"planning"`); do not create `code-review-state.json`.
|
||||
- Inside Autopilot, keep `mode:"autopilot"` active with `current_phase:"code-review"` / skill-active `phase:"code-review"`; do not activate a peer workflow.
|
||||
- On clean review, persist the artifact under Autopilot `handoff_artifacts.code_review` before moving to `ultraqa`. On non-clean review, persist findings and use `rework` or `ralplan` as appropriate.
|
||||
|
||||
```sh
|
||||
omx state write --input '{"mode":"autopilot","active":true,"current_phase":"code-review"}' --json
|
||||
```
|
||||
|
||||
## Final Synthesis and gate
|
||||
|
||||
### Architectural Status Contract
|
||||
|
||||
Combine the `code-reviewer` recommendation and architect status. Approval requires explicit evidence from both independent lanes; missing or failed delegation is a blocking unavailable-review state, not an approval fallback. The final report must make architect blockers impossible to miss.
|
||||
|
||||
- If architect status is **BLOCK**, final recommendation is **REQUEST CHANGES**.
|
||||
- Else if `code-reviewer` recommendation is **REQUEST CHANGES**, final recommendation is **REQUEST CHANGES**.
|
||||
- Else if architect status is **WATCH**, final recommendation is **COMMENT**.
|
||||
- Else final recommendation follows the `code-reviewer` lane.
|
||||
|
||||
Approval criteria: **APPROVE** only when `code-reviewer` returns APPROVE, architect status is `CLEAR`, and both independent lanes returned evidence. **REQUEST CHANGES** for a blocker, unresolved high/critical finding, or unavailable lane. **COMMENT** may record non-blocking findings.
|
||||
|
||||
Do not self-review as a fallback. If the `code-reviewer` or `architect` path is missing, unavailable, skipped, or fails, block approval until independent lane evidence exists. On the supported `omx ralph` CLI compatibility path, findings may trigger automatic fix follow-up without another permission prompt; plain `code-review` itself remains read-only and does **not** promise auto-fix.
|
||||
|
||||
## Evidence/output contract
|
||||
|
||||
Return a concise report containing:
|
||||
|
||||
```text
|
||||
CODE REVIEW REPORT
|
||||
Files Reviewed: <count>
|
||||
Total Issues: 0
|
||||
Architectural Status: CLEAR | WATCH | BLOCK
|
||||
CRITICAL (0) | HIGH (0) | MEDIUM (0) | LOW (0)
|
||||
Findings: file:line -> issue, risk, concrete fix (or none)
|
||||
ARCHITECTURE WATCHLIST: concern, status, recommendation (or none)
|
||||
- code-reviewer recommendation: COMMENT
|
||||
- architect status: WATCH
|
||||
- final recommendation: COMMENT
|
||||
RECOMMENDATION: COMMENT
|
||||
```
|
||||
|
||||
Replace the illustrative counts and verdict with observed values. Include scope,
|
||||
lane evidence/artifact references, unresolved risks, and validation gaps.
|
||||
|
||||
## Exit condition
|
||||
|
||||
Stop when the scoped diff has two independent lane results and a deterministic final
|
||||
recommendation. Report `APPROVE` only under the approval criteria; otherwise leave a
|
||||
bounded `REQUEST CHANGES`, `COMMENT`, or unavailable-review result. Never claim
|
||||
merge-ready without the required evidence.
|
||||
@@ -1,101 +0,0 @@
|
||||
---
|
||||
name: configure-notifications
|
||||
description: Configure OMX notifications - unified entry point for all platforms
|
||||
triggers:
|
||||
- "configure notifications"
|
||||
- "setup notifications"
|
||||
- "notification settings"
|
||||
- "configure discord"
|
||||
- "configure telegram"
|
||||
- "configure slack"
|
||||
- "configure openclaw"
|
||||
- "setup discord"
|
||||
- "setup telegram"
|
||||
- "setup slack"
|
||||
- "setup openclaw"
|
||||
- "discord notifications"
|
||||
- "telegram notifications"
|
||||
- "slack notifications"
|
||||
- "openclaw notifications"
|
||||
- "discord webhook"
|
||||
- "telegram bot"
|
||||
- "slack webhook"
|
||||
---
|
||||
|
||||
# Configure OMX Notifications
|
||||
|
||||
Use this card for Discord, Telegram, Slack, generic command aliases, or OpenClaw. Shared safety, lifecycle, hook, team, autonomy, and cancellation rules live in `templates/AGENTS.md`.
|
||||
|
||||
## 1. Inspect and choose a path
|
||||
|
||||
```bash
|
||||
CONFIG_FILE="$HOME/.codex/.omx-config.json"
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
jq -r '{notifications_enabled:(.notifications.enabled // false), discord:(.notifications.discord.enabled // false), discord_bot:(.notifications["discord-bot"].enabled // false), telegram:(.notifications.telegram.enabled // false), slack:(.notifications.slack.enabled // false), openclaw:(.notifications.openclaw.enabled // false), custom_webhook_command:(.notifications.custom_webhook_command.enabled // false), custom_cli_command:(.notifications.custom_cli_command.enabled // false), verbosity:(.notifications.verbosity // "session"), idleCooldownSeconds:(.notifications.idleCooldownSeconds // 60), reply_enabled:(.notifications.reply.enabled // false)}' "$CONFIG_FILE"
|
||||
else
|
||||
echo "NO_CONFIG_FILE"
|
||||
fi
|
||||
```
|
||||
|
||||
Use AskUserQuestion to choose: Discord native (webhook or bot), Telegram native (bot token + chat id), Slack native (incoming webhook), `custom_webhook_command`, `custom_cli_command`, cross-cutting settings, or disable all notifications.
|
||||
|
||||
## 2. Native providers
|
||||
|
||||
Collect and validate provider values, then write only these native keys: `notifications.discord`, `notifications["discord-bot"]`, `notifications.telegram`, or `notifications.slack`. Do not represent native providers with generic aliases.
|
||||
|
||||
## 3. Generic aliases
|
||||
|
||||
For `custom_webhook_command`, collect URL, optional headers, method (`POST` default or `PUT`), events (`session-end`, `ask-user-question`, `session-start`, `session-idle`, `stop`), and an instruction template:
|
||||
|
||||
```bash
|
||||
jq --arg url "$URL" --arg method "${METHOD:-POST}" --arg instruction "${INSTRUCTION:-OMX event {{event}} for {{projectPath}}}" --argjson headers "${HEADERS_JSON:-{}}" '.notifications=(.notifications//{enabled:true})|.notifications.enabled=true|.notifications.custom_webhook_command={enabled:true,url:$url,headers:$headers,method:$method,instruction:$instruction,events:["session-end","ask-user-question"]}' "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
|
||||
```
|
||||
|
||||
For `custom_cli_command`, collect a command template (`{{event}}`, `{{instruction}}`, `{{sessionId}}`, `{{projectPath}}`), optional events, and an instruction template:
|
||||
|
||||
```bash
|
||||
jq --arg command "$COMMAND_TEMPLATE" --arg instruction "${INSTRUCTION:-OMX event {{event}} for {{projectPath}}}" '.notifications=(.notifications//{enabled:true})|.notifications.enabled=true|.notifications.custom_cli_command={enabled:true,command:$command,instruction:$instruction,events:["session-end","ask-user-question"]}' "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
|
||||
```
|
||||
|
||||
OpenClaw dispatch requires `OMX_OPENCLAW=1`; command gateways additionally require `OMX_OPENCLAW_COMMAND=1`; timeout precedence is `gateways.<name>.timeout` > `OMX_OPENCLAW_COMMAND_TIMEOUT_MS` > `5000` ms.
|
||||
|
||||
## 4. OpenClaw + Clawdbot agent mode
|
||||
|
||||
Use this only when the requested route is a `clawdbot agent` turn delivered to Discord. The `session-stop` notification intentionally maps to the OpenClaw `stop` hook. OMX shell-escapes substitutions; keep instructions short, avoid untrusted shell metacharacters, append `|| true`, and append JSONL logs with `>>`.
|
||||
|
||||
```bash
|
||||
jq --arg command "(clawdbot agent --session-id omx-hooks --message {{instruction}} --thinking minimal --deliver --reply-channel discord --reply-to 'channel:CHANNEL_ID' --timeout 120 --json >>/tmp/omx-openclaw-agent.jsonl 2>&1 || true)" '.notifications=(.notifications//{enabled:true})|.notifications.enabled=true|.notifications.verbosity="verbose"|.notifications.openclaw={enabled:true,gateways:{local:{type:"command",command:$command,timeout:120000}},hooks:{"session-start":{enabled:true,gateway:"local",instruction:"OMX hook=session-start project={{projectName}} session={{sessionId}} tmux={{tmuxSession}}. 한국어로 상태를 #omc-dev에 공유하고 SOUL.md를 참고하세요."},"session-idle":{enabled:true,gateway:"local",instruction:"OMX hook=session-idle session={{sessionId}} tmux={{tmuxSession}}. 한국어로 팔로업을 #omc-dev에 안내하세요."},"ask-user-question":{enabled:true,gateway:"local",instruction:"OMX hook=ask-user-question session={{sessionId}} tmux={{tmuxSession}} question={{question}}. 한국어로 응답 필요를 #omc-dev에 알리세요."},"stop":{enabled:true,gateway:"local",instruction:"OMX hook=session-stop session={{sessionId}} tmux={{tmuxSession}}. 한국어로 중단 상태를 전달하세요."},"session-end":{enabled:true,gateway:"local",instruction:"OMX hook=session-end session={{sessionId}} tmux={{tmuxSession}} reason={{reason}}. 한국어로 완료 요약을 #omc-dev에 남기세요."}}}' "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
|
||||
```
|
||||
|
||||
### Compatibility + precedence contract
|
||||
|
||||
Explicit `notifications.openclaw` wins when valid; generic aliases are ignored with a warning.
|
||||
|
||||
Verify this mode and inspect evidence without hiding output:
|
||||
|
||||
```bash
|
||||
clawdbot agent --session-id omx-hooks --message "OMX hook test via clawdbot agent path" --thinking minimal --deliver --reply-channel discord --reply-to 'channel:CHANNEL_ID' --timeout 120 --json
|
||||
tmux list-sessions -F '#{session_name}' | rg '^omx-' || true
|
||||
jq '.notifications.openclaw.hooks' "$CONFIG_FILE"
|
||||
tail -n 120 /tmp/omx-openclaw-agent.jsonl | jq -s '.[] | {timestamp:(.timestamp // .time),status:(.status // .error // "ok")}'
|
||||
rg '"error"|"failed"|"timeout"' /tmp/omx-openclaw-agent.jsonl | tail -20
|
||||
```
|
||||
|
||||
For HTTP integrations, run both a `/hooks/wake` smoke test and `/hooks/agent` delivery verification.
|
||||
|
||||
## 5. Cross-cutting settings and disable
|
||||
|
||||
- Verbosity: `minimal`, `session` (recommended), `agent`, or `verbose`.
|
||||
- Idle cooldown: `notifications.idleCooldownSeconds` (environment override: `OMX_IDLE_COOLDOWN_SECONDS`).
|
||||
- Profiles: `notifications.profiles` and `notifications.defaultProfile`.
|
||||
- Reply listener: `notifications.reply.enabled`; gates are `OMX_REPLY_ENABLED=true` and, for Discord, `OMX_REPLY_DISCORD_USER_IDS=...`. An authorized Discord bot operator may exact-match `status` on a tracked notification for a bounded read-only session summary.
|
||||
|
||||
Disable all notifications:
|
||||
|
||||
```bash
|
||||
jq '.notifications.enabled=false' "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
|
||||
```
|
||||
|
||||
## Exit evidence
|
||||
|
||||
Run `jq empty "$CONFIG_FILE"` after edits. Report the config path, enabled native providers, enabled aliases, whether valid `notifications.openclaw` overrides aliases, verbosity, idle cooldown, reply-listener state, and smoke-test results. Stop when the JSON parses and the selected path has its required evidence; do not claim delivery from configuration alone.
|
||||
@@ -1,571 +0,0 @@
|
||||
---
|
||||
name: deep-interview
|
||||
description: Socratic deep interview with mathematical ambiguity gating before execution
|
||||
argument-hint: "[--quick|--standard|--deep] [--autoresearch] <idea or vague description>"
|
||||
---
|
||||
|
||||
<Purpose>
|
||||
Deep Interview is an intent-first Socratic clarification loop before planning or implementation. It turns vague ideas into execution-ready specifications by asking targeted questions about why the user wants a change, how far it should go, what should stay out of scope, and what OMX may decide without confirmation.
|
||||
</Purpose>
|
||||
|
||||
<Use_When>
|
||||
- The request is broad, ambiguous, or missing concrete acceptance criteria
|
||||
- The user says "deep interview", "interview me", "ask me everything", "don't assume", or "ouroboros"
|
||||
- The user wants to avoid misaligned implementation from underspecified requirements
|
||||
- You need a requirements artifact before handing off to `ralplan`, `autopilot`, `ultragoal`, or `team`
|
||||
</Use_When>
|
||||
|
||||
<Do_Not_Use_When>
|
||||
- The request already has concrete file/symbol targets and clear acceptance criteria
|
||||
- The user explicitly asks to skip planning/interview and execute immediately
|
||||
- The user asks for lightweight brainstorming only (use `plan` instead)
|
||||
- A complete PRD/plan already exists and execution should start
|
||||
</Do_Not_Use_When>
|
||||
|
||||
<Why_This_Exists>
|
||||
Execution quality is usually bottlenecked by intent clarity, not just missing implementation detail. A single expansion pass often misses why the user wants a change, where the scope should stop, which tradeoffs are unacceptable, and which decisions still require user approval. This workflow applies Socratic pressure + quantitative ambiguity scoring so orchestration modes begin with an explicit, testable, intent-aligned spec.
|
||||
</Why_This_Exists>
|
||||
|
||||
<Depth_Profiles>
|
||||
- **Quick (`--quick`)**: fast pre-PRD pass; target threshold `<= 0.30`; max rounds 5
|
||||
- **Standard (`--standard`, default)**: full requirement interview; target threshold `<= 0.20`; max rounds 12
|
||||
- **Deep (`--deep`)**: high-rigor exploration; target threshold `<= 0.15`; max rounds 20
|
||||
- **Autoresearch (`--autoresearch`)**: same interview rigor as Standard, but specialized for `$autoresearch` mission readiness and `.omx/specs/` artifact handoff
|
||||
|
||||
Profile `max rounds` is a hard cap, not a target. Do not continue only to reach a numbered round count. Extra Socratic rigor does not override the active threshold unless the profile/config changes.
|
||||
|
||||
If no flag is provided, use **Standard**.
|
||||
|
||||
<Mode_Flags>
|
||||
- **`--autoresearch`**: switch the interview into autoresearch-intake mode for `$autoresearch` handoff. In this mode, the interview should converge on a validator-ready research mission, write canonical artifacts under `.omx/specs/`, and preserve the explicit `refine further` vs `launch` boundary for downstream skill intake.
|
||||
</Mode_Flags>
|
||||
</Depth_Profiles>
|
||||
|
||||
<Execution_Policy>
|
||||
- Ask ONE question per round (never batch multiple interview rounds into one `questions[]` form)
|
||||
- Ask about intent and boundaries before implementation detail
|
||||
- Target the weakest clarity dimension each round after applying the stage-priority rules below
|
||||
- Treat every answer as a claim to pressure-test before moving on: the next question should usually demand evidence or examples, expose a hidden assumption, force a tradeoff or boundary, or reframe root cause vs symptom
|
||||
- Do not rotate to a new clarity dimension just for coverage when the current answer is still vague; stay on the same thread until one layer deeper, one assumption clearer, or one boundary tighter
|
||||
- Before crystallizing, complete at least one explicit pressure pass that revisits an earlier answer with a deeper, assumption-focused, or tradeoff-focused follow-up
|
||||
- Gather codebase facts via `explore` before asking user about internals
|
||||
- `omx explore` is deprecated. Use normal repository inspection tools/subagents for simple read-only brownfield fact gathering; use `omx sparkshell` only for explicit shell-native read-only evidence, and keep ambiguous or non-shell-only investigation on the richer normal path.
|
||||
- Always run a preflight context intake before the first interview question
|
||||
- For brownfield work, preflight must include doc/context grounding before user-facing questions: inspect applicable `AGENTS.md` files, README/getting-started docs, relevant `docs/` contracts/plans/ADRs, existing `.omx/context/` snapshots, and any project-local glossary/context files such as `CONTEXT.md` or `CONTEXT-MAP.md` when present.
|
||||
- Treat existing repo language as evidence, not authority: if the user uses a fuzzy, overloaded, or conflicting term, surface the specific doc/code wording and ask which meaning should govern before implementation.
|
||||
- Cross-check user claims about current behavior against code or documented contracts when discoverable. If docs and code disagree, ask a confirmation question that names both sources instead of silently choosing one.
|
||||
- Use scenario-based edge-case grilling when relationships, boundaries, or handoff behavior are unclear: invent one concrete scenario that stresses the ambiguous boundary, then ask one focused question about the expected outcome.
|
||||
- Durable docs, glossary, ADR, or memory updates are opt-in and public-safe only. Deep-interview may recommend such updates in the handoff summary, but must not automatically create or dump public docs from interview transcripts unless the user explicitly chooses that as in-scope.
|
||||
- If initial context is oversized or would exceed the prompt budget, do not paste or forward the raw payload into interview prompts; request and record a prompt-safe initial-context summary first
|
||||
- The oversized initial-context summary gate is blocking: wait for the concise summary before ambiguity scoring, crystallizing artifacts, or any downstream execution handoff
|
||||
- The summary must preserve goals, constraints, success criteria, non-goals, decision boundaries, and references to any full source documents so downstream consumers receive a prompt-safe but faithful context
|
||||
- Keep total prompt payloads within a safe budget by summarizing or trimming retained history; preserve newest/highest-signal answers and never let raw oversized context crowd out the current question
|
||||
- Reduce user effort: ask only the highest-leverage unresolved question, and never ask the user for codebase facts that can be discovered directly
|
||||
- For brownfield work, prefer evidence-backed confirmation questions such as "I found X in Y. Should this change follow that pattern?"
|
||||
- Route facts before judgment in the Ouroboros style: before presenting a user-facing interview round, classify whether the needed information is a discoverable fact, a fact needing confirmation, or a human decision. The interview is with the human for judgment, not for facts the agent can inspect.
|
||||
- When unresolved ambiguity depends on current external best practices, official/upstream guidance, standards, or version-aware behavior, use `$best-practice-research` as the bounded evidence wrapper before crystallizing requirements or handing off to planning/execution.
|
||||
- Use these transcript/spec labels only; never use them as `omx question` `source` values, and never replace the runtime `source: "deep-interview"` contract for user-facing deep-interview questions:
|
||||
- `[from-code][auto-confirmed]` — exact, high-confidence codebase facts from manifests/configs or direct source evidence, with no prescription attached.
|
||||
- `[from-code]` — codebase findings that are useful but inferred, pattern-based, or low/medium confidence and therefore need a confirmation-style user-facing round before being treated as settled.
|
||||
- `[from-research]` — externally sourced facts such as API limits, compatibility, or public documentation; facts only, not decisions.
|
||||
- `[from-user]` — goals, preferences, business logic, scope, non-goals, acceptance criteria, tradeoffs, and any decision-bearing interpretation.
|
||||
- Treat `[from-code][auto-confirmed]` and other non-user fact discoveries as context/transcript updates, not interview rounds: do not call `omx question`, do not create a pending deep-interview question obligation, and do not increment the user-facing round number for facts the agent can safely establish.
|
||||
- Auto-confirm only descriptive facts. If a finding implies what the new feature should do, which pattern it should follow, which tradeoff to accept, or what should stay in/out of scope, route the entire decision-bearing question to the user as `[from-user]` even when code or research facts are available.
|
||||
- In attached-tmux Codex CLI, deep-interview uses `omx question` as the required OMX-owned structured questioning path for every interview round
|
||||
- When invoking `omx question` through attached-tmux Bash/tool paths, preserve the leader-pane return target by prefixing the command with `OMX_QUESTION_RETURN_PANE=$TMUX_PANE` (or a concrete `%pane` value)
|
||||
- If you launch `omx question` in a background terminal, immediately wait for that background terminal to finish and read its JSON answer before scoring ambiguity, asking another round, or handing off
|
||||
- Treat `answers[]` as the primary `omx question` success contract. For a single interview round, read `answers[0].answer`; use legacy top-level `answer` only as a compatibility fallback when needed.
|
||||
- If the current runtime is outside tmux and cannot render `omx question`, use the native structured question tool when available; otherwise ask exactly one concise plain-text question and wait for the answer
|
||||
- Re-score ambiguity after each answer and show progress transparently
|
||||
- Once ambiguity is at or below the active profile threshold, stop ordinary questioning. Run the practical closure audit: crystallize/handoff when readiness gates pass; otherwise ask only the final closure question needed to satisfy a named gate.
|
||||
- Treat `max_rounds` as a stop cap, not evidence that more rounds are needed.
|
||||
- Do not hand off to execution while ambiguity remains above threshold unless user explicitly opts to proceed with warning
|
||||
- Do not crystallize or hand off while `Non-goals` or `Decision Boundaries` remain unresolved, even if the weighted ambiguity threshold is met
|
||||
- Treat early exit as a safety valve, not the default success path
|
||||
- Persist mode state for resume safety only through the CLI/programmatic single-writer authority (`omx state write/read --input '<json>' --json`, backed by `src/state/operations.ts`). The MCP state server is a read-only projection and must never become a second writer.
|
||||
</Execution_Policy>
|
||||
|
||||
<Steps>
|
||||
|
||||
## Phase 0: Preflight Context Intake
|
||||
|
||||
1. Parse `{{ARGUMENTS}}` and derive a short task slug.
|
||||
2. Attempt to load the latest relevant context snapshot from `.omx/context/{slug}-*.md`.
|
||||
3. Check whether the provided initial context or loaded snapshot is too large for safe prompt use. If it is oversized, the first interview round must ask for a concise prompt-safe summary instead of scoring ambiguity or continuing to downstream handoff.
|
||||
4. If no snapshot exists, create a minimum context snapshot with:
|
||||
- Task statement
|
||||
- Desired outcome
|
||||
- Stated solution (what the user asked for)
|
||||
- Probable intent hypothesis (why they likely want it)
|
||||
- Known facts/evidence
|
||||
- Constraints
|
||||
- Unknowns/open questions
|
||||
- Decision-boundary unknowns
|
||||
- Likely codebase touchpoints
|
||||
- Relevant repo docs/rules/context inspected
|
||||
- Terminology or doc/code conflicts found
|
||||
- Prompt-safe initial-context summary status (`not_needed`, `needed`, or `recorded`)
|
||||
5. For brownfield tasks, inspect the applicable documentation/rule surface before the first user-facing round. Prefer exact, nearby sources over broad scans:
|
||||
- governing `AGENTS.md` files and template/runtime instruction surfaces that apply to the touched paths
|
||||
- README/getting-started docs and relevant docs under `docs/`, especially contracts, plans, ADR-like records, and workflow docs
|
||||
- existing `.omx/context/` snapshots, `.omx/specs/`, and planning artifacts relevant to the slug
|
||||
- project-local glossary/context files such as `CONTEXT.md`, `CONTEXT-MAP.md`, or context-specific docs when they exist
|
||||
6. Save snapshot to `.omx/context/{slug}-{timestamp}.md` (UTC `YYYYMMDDTHHMMSSZ`) and reference it in mode state.
|
||||
|
||||
## Phase 1: Initialize
|
||||
|
||||
1. Parse `{{ARGUMENTS}}` and depth profile (`--quick|--standard|--deep`).
|
||||
2. Detect project context:
|
||||
- Run `explore` to classify **brownfield** (existing codebase target) vs **greenfield**.
|
||||
- For brownfield, collect relevant codebase context before questioning.
|
||||
3. Initialize state via `omx state write --input '{"mode":"deep-interview","active":true}' --json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"active": true,
|
||||
"current_phase": "deep-interview",
|
||||
"state": {
|
||||
"interview_id": "<uuid>",
|
||||
"profile": "quick|standard|deep",
|
||||
"type": "greenfield|brownfield",
|
||||
"initial_idea": "<user input>",
|
||||
"rounds": [],
|
||||
"current_ambiguity": 1.0,
|
||||
"threshold": 0.3,
|
||||
"max_rounds": 5,
|
||||
"challenge_modes_used": [],
|
||||
"codebase_context": null,
|
||||
"current_stage": "intent-first",
|
||||
"current_focus": "intent",
|
||||
"context_snapshot_path": ".omx/context/<slug>-<timestamp>.md"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. Announce kickoff with profile, threshold, and current ambiguity.
|
||||
|
||||
## Phase 2: Socratic Interview Loop
|
||||
|
||||
Repeat until ambiguity `<= threshold`, the pressure pass is complete, the readiness gates are explicit, the user exits with warning, or max rounds are reached. This is a stop condition: below threshold, do not open a new ordinary interview branch.
|
||||
|
||||
### 2a) Generate next question
|
||||
If the initial context is oversized and no prompt-safe summary has been recorded yet, the next question must be only a summary request. Do not score ambiguity, do not run readiness gates, and do not hand off to `$ultragoal`, `$ralplan`, `$autopilot`, or `$team` until that summary answer is captured.
|
||||
|
||||
Use:
|
||||
- Original idea
|
||||
- Prior Q&A rounds
|
||||
- Current dimension scores
|
||||
- Brownfield context (if any)
|
||||
- Doc/context grounding notes, including existing terminology, governing rules, and any doc/code mismatch
|
||||
- Activated challenge mode injection (Phase 3)
|
||||
|
||||
Target the lowest-scoring dimension, but respect stage priority:
|
||||
- **Stage 1 — Intent-first:** Intent, Outcome, Scope, Non-goals, Decision Boundaries
|
||||
- **Stage 2 — Feasibility:** Constraints, Success Criteria
|
||||
- **Stage 3 — Brownfield grounding:** Context Clarity (brownfield only)
|
||||
|
||||
Follow-up pressure ladder after each answer:
|
||||
1. Ask for a concrete example, counterexample, or evidence signal behind the latest claim
|
||||
2. Probe the hidden assumption, dependency, or belief that makes the claim true
|
||||
3. Force a boundary or tradeoff: what would you explicitly not do, defer, or reject?
|
||||
4. Challenge fuzzy or conflicting terms against the repo's documented language and current code behavior
|
||||
5. Stress-test the boundary with one concrete scenario or edge case when a relationship or handoff remains ambiguous
|
||||
6. If the answer still describes symptoms, reframe toward essence / root cause before moving on
|
||||
|
||||
Prefer staying on the same thread for multiple rounds when it has the highest leverage. Breadth without pressure is not progress.
|
||||
|
||||
Maintain a **Breadth Ledger** across independent ambiguity tracks: scope, constraints, outputs, verification, brownfield integration, and any user-mentioned deliverable tracks. The ledger is a guard, not a mandatory rotation rule: stay deep on the current thread until it has been pressure-tested, then zoom out only when another material track remains unresolved and would change execution.
|
||||
|
||||
Maintain a **Docs/Terminology Ledger** for brownfield interviews:
|
||||
- repo docs/rules/context sources inspected, with path references
|
||||
- canonical terms already used by the repo and terms to avoid or disambiguate
|
||||
- user terms that conflict with docs or current code behavior
|
||||
- doc/code mismatches that require a human decision before implementation
|
||||
- optional durable-doc follow-ups that are safe to propose but not auto-apply
|
||||
|
||||
Detailed dimensions:
|
||||
- Intent Clarity — why the user wants this
|
||||
- Outcome Clarity — what end state they want
|
||||
- Scope Clarity — how far the change should go
|
||||
- Constraint Clarity — technical or business limits that must hold
|
||||
- Success Criteria Clarity — how completion will be judged
|
||||
- Context Clarity — existing codebase understanding (brownfield only)
|
||||
|
||||
`Non-goals` and `Decision Boundaries` are mandatory readiness gates. Ask about them early and keep revisiting them until they are explicit.
|
||||
|
||||
### 2b) Ask the question
|
||||
Use the surface-appropriate structured questioning path for every interview round. In attached-tmux sessions, use OMX-owned structured questioning via `omx question` (this is the required structured-question equivalent and required `AskUserQuestion` equivalent for deep-interview). Outside tmux, use native structured input when available; otherwise ask exactly one concise plain-text question and wait for the answer. Present:
|
||||
|
||||
```
|
||||
Round {n} | Target: {weakest_dimension} | Ambiguity: {score}%
|
||||
|
||||
{question}
|
||||
```
|
||||
|
||||
`omx question` payload guidance for interview rounds:
|
||||
- Deep-interview is Socratic: ask one focused round at a time. Do not use batch `questions[]` to combine multiple interview rounds, even though `omx question` supports batch forms for other workflows.
|
||||
- Use canonical `type` values instead of authoring raw `multi_select` flags by hand. `type: "single-answerable"` is the default for one-path decisions; `type: "multi-answerable"` is the canonical shape for bounded multi-select rounds. The runtime will keep `multi_select` aligned with `type`.
|
||||
- Use `single-answerable` when exactly one answer should drive the next branch, the options are mutually exclusive, or selecting more than one answer would blur the decision boundary. Typical cases: handoff lane selection, choosing the primary failure mode, or confirming which of several competing interpretations is correct.
|
||||
- Use `multi-answerable` when multiple options may all be true at once and you need to capture a bounded set of coexisting constraints, non-goals, risks, or acceptance checks in one round. Typical cases: selecting all out-of-scope items, all success metrics that must hold, or all deployment constraints that apply together.
|
||||
- If one selected option would immediately require a follow-up question to disambiguate the others, prefer a `single-answerable` round now and ask the follow-up next. Do not hide a branching interview tree inside one overloaded multi-select prompt.
|
||||
- Keep interview options bounded and concrete. If the valid answers are already known, set `allow_other: false`; only leave `allow_other: true` when the interview genuinely needs one user-supplied option that cannot be enumerated in advance.
|
||||
- Read answers structurally from the primary `answers[]` array. For a normal single-round interview response, use `answers[0].answer` as the source of truth; the top-level `answer` field is a legacy single-question projection/fallback only.
|
||||
- For `single-answerable`, expect one decisive selection in the `value` field of `answers[0].answer` plus its selected-values metadata. For `multi-answerable`, treat the selected-values field inside `answers[0].answer` as the source of truth for all chosen constraints/non-goals and preserve the full set in the transcript/spec. In legacy single-question projections, this is equivalent to: For `multi-answerable`, treat `answer.selected_values` as the source of truth.
|
||||
|
||||
Canonical bounded single-choice payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"question": "Which execution lane should own this once the interview is complete?",
|
||||
"type": "single-answerable",
|
||||
"options": [
|
||||
{
|
||||
"label": "Plan first",
|
||||
"value": "ralplan",
|
||||
"description": "Need architecture and test-shape review before execution"
|
||||
},
|
||||
{
|
||||
"label": "Execute directly",
|
||||
"value": "autopilot",
|
||||
"description": "Requirements are already explicit enough for planning plus execution"
|
||||
},
|
||||
{
|
||||
"label": "Refine further",
|
||||
"value": "refine",
|
||||
"description": "Clarification is still needed before any handoff"
|
||||
}
|
||||
],
|
||||
"allow_other": false,
|
||||
"other_label": "Other",
|
||||
"source": "deep-interview"
|
||||
}
|
||||
```
|
||||
|
||||
Canonical bounded multi-select payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"question": "Which non-goals must stay out of scope for the first pass?",
|
||||
"type": "multi-answerable",
|
||||
"options": [
|
||||
{
|
||||
"label": "No UI redesign",
|
||||
"value": "no-ui-redesign",
|
||||
"description": "Keep layout and styling unchanged"
|
||||
},
|
||||
{
|
||||
"label": "No new dependencies",
|
||||
"value": "no-new-dependencies",
|
||||
"description": "Work within the existing toolchain"
|
||||
},
|
||||
{
|
||||
"label": "No API contract changes",
|
||||
"value": "no-api-contract-changes",
|
||||
"description": "Preserve external request and response shapes"
|
||||
}
|
||||
],
|
||||
"allow_other": false,
|
||||
"other_label": "Other",
|
||||
"source": "deep-interview"
|
||||
}
|
||||
```
|
||||
|
||||
Canonical answer-shape reminders:
|
||||
|
||||
```json
|
||||
{
|
||||
"answer": {
|
||||
"kind": "option",
|
||||
"value": "ralplan",
|
||||
"selected_labels": ["Plan first"],
|
||||
"selected_values": ["ralplan"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"answer": {
|
||||
"kind": "multi",
|
||||
"value": ["no-new-dependencies", "no-api-contract-changes"],
|
||||
"selected_labels": ["No new dependencies", "No API contract changes"],
|
||||
"selected_values": ["no-new-dependencies", "no-api-contract-changes"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2c) Score ambiguity
|
||||
Score each weighted dimension in `[0.0, 1.0]` with justification + gap.
|
||||
|
||||
Greenfield: `ambiguity = 1 - (intent × 0.30 + outcome × 0.25 + scope × 0.20 + constraints × 0.15 + success × 0.10)`
|
||||
|
||||
Brownfield: `ambiguity = 1 - (intent × 0.25 + outcome × 0.20 + scope × 0.20 + constraints × 0.15 + success × 0.10 + context × 0.10)`
|
||||
|
||||
Readiness gate:
|
||||
- `Non-goals` must be explicit
|
||||
- `Decision Boundaries` must be explicit
|
||||
- A pressure pass must be complete: at least one earlier answer has been revisited with an evidence, assumption, or tradeoff follow-up
|
||||
- A practical closure audit must pass: another question would change execution materially, not merely polish wording or chase a narrow edge case
|
||||
- If either gate is unresolved, or the pressure pass is incomplete, continue below threshold only with a final closure question that names the unresolved gate and would materially change execution.
|
||||
- Treat a low ambiguity score as permission to audit closure, not permission to keep drilling indefinitely. If remaining uncertainty would not change implementation, crystallize the spec instead of opening a new branch.
|
||||
- If ambiguity is `<= 0.10`, another user-facing question is allowed only as that final closure question; otherwise crystallize immediately.
|
||||
|
||||
### 2d) Report progress
|
||||
Show weighted breakdown table, readiness-gate status (`Non-goals`, `Decision Boundaries`), and the next focus dimension.
|
||||
|
||||
### 2e) Persist state
|
||||
Append round result and updated scores via `omx state write --input '<json>' --json`. Do not write `{mode}-state.json` directly and do not use the read-only MCP state projection as a writer.
|
||||
|
||||
### 2f) Round controls
|
||||
- Do not offer early exit before the first explicit assumption probe and one persistent follow-up have happened
|
||||
- Apply a **Dialectic Rhythm Guard**: track consecutive non-user fact discoveries and confirmation-style answers (`[from-code][auto-confirmed]`, `[from-code]`, or `[from-research]`). After 3 consecutive non-user or confirmation answers, the next material user-facing round must solicit direct human judgment (`[from-user]`) unless the closure audit says the interview is ready to crystallize.
|
||||
- Round 4+: allow explicit early exit with risk warning
|
||||
- Soft warning at profile midpoint (e.g., round 3/6/10 depending on profile)
|
||||
- Hard cap at profile `max_rounds`; never treat this cap as a desired interview length or quota
|
||||
|
||||
## Phase 3: Challenge Modes (assumption stress tests)
|
||||
|
||||
Use each mode once when applicable. These are normal escalation tools, not rare rescue moves:
|
||||
|
||||
- **Contrarian** (round 2+ or immediately when an answer rests on an untested assumption): challenge core assumptions
|
||||
- **Terminologist** (brownfield, whenever a key term is fuzzy, overloaded, or conflicts with repo docs/code): force a canonical meaning against existing project language before implementation
|
||||
- **Simplifier** (round 4+ or when scope expands faster than outcome clarity): probe minimal viable scope
|
||||
- **Ontologist** (round 5+ and ambiguity > 0.25, or when the user keeps describing symptoms): ask for essence-level reframing
|
||||
|
||||
Track used modes in state to prevent repetition.
|
||||
|
||||
## Phase 4: Crystallize Artifacts
|
||||
|
||||
When threshold is met (or user exits with warning / hard cap):
|
||||
|
||||
1. Write interview transcript summary to:
|
||||
- `.omx/interviews/{slug}-{timestamp}.md`
|
||||
(kept for ralph PRD compatibility)
|
||||
2. Write execution-ready spec to:
|
||||
- `.omx/specs/deep-interview-{slug}.md`
|
||||
|
||||
Spec should include:
|
||||
- Metadata (profile, rounds, final ambiguity, threshold, context type)
|
||||
- Context snapshot reference/path (for ralplan/team reuse)
|
||||
- Prompt-safe initial-context summary when oversized context was provided, plus references to any full source documents
|
||||
- Clarity breakdown table
|
||||
- Intent (why the user wants this)
|
||||
- Desired Outcome
|
||||
- In-Scope
|
||||
- Out-of-Scope / Non-goals
|
||||
- Decision Boundaries (what OMX may decide without confirmation)
|
||||
- Constraints
|
||||
- Testable acceptance criteria
|
||||
- Assumptions exposed + resolutions
|
||||
- Pressure-pass findings (which answer was revisited, and what changed)
|
||||
- Brownfield evidence vs inference notes for any repository-grounded confirmation questions
|
||||
- Docs/Terminology Ledger with inspected repo docs/rules/context, term conflicts, and any doc/code mismatch decisions
|
||||
- Scenario/edge-case pressure findings that materially shaped scope or acceptance criteria
|
||||
- Optional durable documentation recommendations, explicitly marked opt-in and public-safe; do not include raw private transcript dumps
|
||||
- Technical context findings
|
||||
- Full or condensed transcript
|
||||
|
||||
### Autoresearch specialization
|
||||
|
||||
When the clarified task is specifically about `$autoresearch`, or the skill is invoked with `--autoresearch`, keep the interview domain-specific and emit skill-consumable artifacts without skipping clarification.
|
||||
|
||||
- **Accepted seed inputs:** `topic`, `evaluator`, `keep-policy`, `slug`, existing mission draft text, and prior evaluator examples/templates
|
||||
- **Required interview focus:** mission clarity, evaluator readiness, keep policy, slug/session naming, and whether the draft is ready to launch now or should refine further
|
||||
- **Canonical artifact path:** `.omx/specs/deep-interview-autoresearch-{slug}.md`
|
||||
- **Launch artifact bundle:** `.omx/specs/autoresearch-{slug}/mission.md`, `.omx/specs/autoresearch-{slug}/sandbox.md`, and `.omx/specs/autoresearch-{slug}/result.json`
|
||||
- **Launch artifact directory:** `.omx/specs/autoresearch-{slug}/`
|
||||
- **Required artifact sections:**
|
||||
- `Mission Draft`
|
||||
- `Evaluator Draft`
|
||||
- `Launch Readiness`
|
||||
- `Seed Inputs`
|
||||
- `Confirmation Bridge`
|
||||
- **Required launch artifacts under `.omx/specs/autoresearch-{slug}/`:**
|
||||
- `mission.md`
|
||||
- `sandbox.md`
|
||||
- `result.json`
|
||||
- **Launch-readiness rule:** mark the draft as **not launch-ready** while the evaluator command still contains placeholder markers such as `<...>`, `TODO`, `TBD`, `REPLACE_ME`, `CHANGEME`, or `your-command-here`
|
||||
- **Structured result contract:** `result.json` should point to the draft + mission/sandbox artifacts and carry the finalized `topic`, `evaluatorCommand`, `keepPolicy`, `slug`, `launchReady`, and `blockedReasons` fields so `$autoresearch` can consume it directly
|
||||
- **Confirmation bridge:** after artifact generation, offer at least `refine further` and `launch`; do not run direct CLI launch or detached/split tmux launch, and only hand off to `$autoresearch` after explicit confirmation
|
||||
- **Handoff rule:** downstream execution must preserve the clarified mission intent, evaluator expectations, decision boundaries, and launch-readiness status from this artifact rather than bypassing the draft review step
|
||||
|
||||
## Phase 5: Execution Bridge
|
||||
|
||||
Present execution options after artifact generation using explicit handoff contracts. Treat the deep-interview spec as the current requirements source of truth and preserve intent, non-goals, decision boundaries, acceptance criteria, docs/terminology grounding, and any residual-risk warnings across the handoff.
|
||||
|
||||
### Optional execution contract foundation
|
||||
|
||||
When an Autopilot/deep-interview handoff explicitly requires a stride contract, emit it as structured data rather than prose. This is a validation foundation, not a broadness-inference feature: do not infer stride from task length, phase labels, snapshots, or freeform wording.
|
||||
|
||||
Canonical location under Autopilot state:
|
||||
|
||||
```json
|
||||
{
|
||||
"handoff_artifacts": {
|
||||
"deep_interview": {
|
||||
"execution_contract_required": true,
|
||||
"execution_contract": {
|
||||
"version": 1,
|
||||
"execution_stride": "task",
|
||||
"source": "deep-interview",
|
||||
"selected_by": "user",
|
||||
"allow_task_shrink": true,
|
||||
"completion_unit": "One focused task",
|
||||
"stop_condition": "Stop after that task is implemented and verified",
|
||||
"acceptance_coverage_scope": "task",
|
||||
"shrink_policy": "allowed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Stride meanings:
|
||||
- `task`: conservative, small-step execution; `allow_task_shrink:true`, `acceptance_coverage_scope:"task"`, `shrink_policy:"allowed"`.
|
||||
- `deliverable`: finish the named deliverable before stopping; `allow_task_shrink:false`, `acceptance_coverage_scope:"deliverable"`, `shrink_policy:"ask_before_shrink"`.
|
||||
- `milestone`: finish the larger approved milestone unless blocked; `allow_task_shrink:false`, `acceptance_coverage_scope:"milestone"`, `shrink_policy:"deny_unless_blocked"`.
|
||||
|
||||
Only set `execution_contract_required:true` when the selected downstream workflow needs this explicit stride/stop-condition guard. New artifacts must write the canonical snake_case schema shown above under `handoff_artifacts.deep_interview`; runtime readers may accept legacy camelCase field/marker aliases and direct/nested `execution_contract` locations only as compatibility input. If `execution_contract_required` is absent or false, downstream Autopilot compatibility behavior is unchanged.
|
||||
|
||||
### Goal-mode follow-ups
|
||||
|
||||
Include these product-facing suggestions when they fit the clarified spec, without removing the existing `$ultragoal`, `$ralplan`, `$autopilot`, and `$team` handoff options:
|
||||
|
||||
- **`$ultragoal`** — default goal-mode follow-up for implementation or general goal-oriented follow-up specs that should be converted into durable Codex/OMX goals with sequential completion tracking.
|
||||
- **`$autoresearch`** — use when the clarified context is a research project: a research question, reference/literature gathering, evaluator-backed analysis, or professor/critic-style deliverable.
|
||||
- **`$performance-goal`** — use when the clarified context is an optimization or performance project with measurable speed, latency, throughput, memory, benchmark, or evaluator criteria.
|
||||
|
||||
Recommend `$ultragoal` as the default durable goal-mode follow-up because it supersedes Ralph for goal tracking. Preserve `$team` for coordinated parallel implementation.
|
||||
|
||||
### 1. **`$ultragoal` (Default durable execution follow-up)**
|
||||
- **Input Artifact:** `.omx/specs/deep-interview-{slug}.md` (optionally accompanied by the transcript/context snapshot for traceability)
|
||||
- **Invocation:** `$ultragoal create-goals --brief-file <spec-path>` followed by `$ultragoal complete-goals` in the active execution lane
|
||||
- **Consumer Behavior:** Convert the clarified spec into durable goal-mode work. Preserve intent, non-goals, decision boundaries, acceptance criteria, docs/terminology grounding, scenario-pressure findings, and residual-risk warnings as binding story constraints.
|
||||
- **Skipped / Already-Satisfied Stages:** Requirement interview, ambiguity clarification, doc/context preflight, and early intent-boundary elicitation
|
||||
- **Expected Output:** `.omx/ultragoal/brief.md`, `.omx/ultragoal/goals.json`, `.omx/ultragoal/ledger.jsonl`, implementation evidence, verification evidence, and final cleanup/review-gate evidence
|
||||
- **Best When:** The clarified spec is execution-ready or the user explicitly wants durable goal tracking as the next step
|
||||
- **Next Recommended Step:** Run the Ultragoal completion loop; launch `$team` only inside an active Ultragoal story when parallel lanes are warranted
|
||||
|
||||
### 2. **`$ralplan` (Recommended when architecture/test-shape review is still needed)**
|
||||
- **Input Artifact:** `.omx/specs/deep-interview-{slug}.md` (optionally accompanied by the transcript/context snapshot for traceability)
|
||||
- **Invocation:** `$ralplan <spec-path>`
|
||||
- **Consumer Behavior:** Treat the deep-interview spec as the requirements source of truth. Do not repeat the interview by default; refine architecture/feasibility around the clarified intent and boundaries instead.
|
||||
- **Skipped / Already-Satisfied Stages:** Requirements discovery, ambiguity clarification, and early intent-boundary elicitation
|
||||
- **Expected Output:** Canonical planning artifacts under `.omx/plans/`, especially `prd-*.md` and `test-spec-*.md`
|
||||
- **Best When:** Requirements are clear enough to stop interviewing, but architectural validation / consensus planning is still desirable
|
||||
- **Next Recommended Step:** Use the approved planning artifacts with `$ultragoal` as the default durable goal-mode follow-up (optionally with `$team` for parallel lanes); choose `$autoresearch` for research validation or `$performance-goal` for measurable optimization
|
||||
|
||||
### 3. **`$autopilot`**
|
||||
- **Input Artifact:** `.omx/specs/deep-interview-{slug}.md`
|
||||
- **Invocation:** `$autopilot <spec-path>`
|
||||
- **Consumer Behavior:** Use the deep-interview spec as the clarified execution brief. Preserve intent, non-goals, decision boundaries, and acceptance criteria as binding context for planning/execution.
|
||||
- **Skipped / Already-Satisfied Stages:** Initial requirement discovery and ambiguity reduction
|
||||
- **Expected Output:** Planning/execution progress, QA evidence, and validation artifacts produced by autopilot
|
||||
- **Best When:** The clarified spec is already strong enough for direct planning + execution without an additional consensus gate
|
||||
- **Next Recommended Step:** Continue through autopilot's execution/QA/validation flow; if coordination-heavy execution emerges, prefer `$team` under a leader-owned `$ultragoal` ledger
|
||||
|
||||
### 4. **`$team`**
|
||||
- **Input Artifact:** `.omx/specs/deep-interview-{slug}.md`
|
||||
- **Invocation:** `$team <spec-path>`
|
||||
- **Consumer Behavior:** Treat the spec as shared execution context for coordinated parallel work. Preserve the clarified intent, non-goals, decision boundaries, and acceptance criteria as common lane constraints.
|
||||
- **Skipped / Already-Satisfied Stages:** Requirement clarification and early ambiguity reduction
|
||||
- **Expected Output:** Coordinated multi-agent execution against the shared spec, with final verification evidence and checkpoints in an already-active Ultragoal ledger when applicable
|
||||
- **Best When:** The task is large, multi-lane, or blocker-sensitive enough to justify coordinated parallel execution instead of a single persistent loop
|
||||
- **Next Recommended Step:** Follow the team verification path when the coordinated execution phase finishes; checkpoint an already-active `$ultragoal` ledger when applicable. Do not start another execution loop solely to verify.
|
||||
|
||||
### 5. **Refine further**
|
||||
- **Input Artifact:** Existing transcript, context snapshot, and current spec draft
|
||||
- **Invocation:** Continue the interview loop
|
||||
- **Consumer Behavior:** Re-enter questioning to resolve the highest-leverage remaining uncertainty
|
||||
- **Skipped / Already-Satisfied Stages:** None beyond already-captured context
|
||||
- **Expected Output:** A lower-ambiguity spec with tighter boundaries and fewer unresolved assumptions
|
||||
- **Best When:** Residual ambiguity is still too high, the user wants stronger clarity, or the above-threshold / early-exit warning indicates too much risk to proceed cleanly
|
||||
- **Next Recommended Step:** Return to one of the execution handoff contracts above once the spec is sufficiently clarified
|
||||
|
||||
**Residual-Risk Rule:** If the interview ended via early exit, hard-cap completion, or above-threshold proceed-with-warning, explicitly preserve that residual-risk state in the handoff so the downstream skill knows it inherited a partially clarified brief.
|
||||
|
||||
**IMPORTANT:** Deep-interview is a requirements mode. On handoff, invoke the selected skill using the contract above. **Do NOT implement directly** inside deep-interview.
|
||||
|
||||
</Steps>
|
||||
|
||||
<Tool_Usage>
|
||||
- Use `explore` for codebase fact gathering
|
||||
- Use `omx question` as the OMX-native structured user-input tool for each interview round when an attached tmux renderer is available
|
||||
- From attached-tmux Bash/tool paths, call it as `OMX_QUESTION_RETURN_PANE=$TMUX_PANE omx question ...` unless an explicit `%pane` return target is already known
|
||||
- If the current runtime is outside tmux and cannot render `omx question`, use native structured input when available; otherwise ask exactly one concise plain-text question and wait for the answer
|
||||
- After `omx question` returns JSON, prefer `answers[0].answer` / `answers[]`; use legacy `answer` only as a fallback for older records
|
||||
- Use `omx state write/read --input '<json>' --json` for resumable mode state through the sole-writer state operations path; the MCP state server is read-only and is not a fallback writer
|
||||
- If the interview cannot ask a required `omx question` round, persist the blocker as terminal state with `active: false` and `current_phase: "blocked"`; do not write a terminal blocked phase with `active: true`
|
||||
- Read/write context snapshots under `.omx/context/`
|
||||
- Read applicable repo docs/rules/context during preflight; write durable docs, glossary, ADR, or memory updates only when the user explicitly opts in and the content is public-safe
|
||||
- Record whether the oversized-context summary gate is not needed, pending, or satisfied before any scoring or handoff step
|
||||
- Save transcript/spec artifacts under `.omx/interviews/` and `.omx/specs/`
|
||||
</Tool_Usage>
|
||||
|
||||
<Escalation_And_Stop_Conditions>
|
||||
- User says stop/cancel/abort -> persist state and stop
|
||||
- Ambiguity stalls for 3 rounds (+/- 0.05) -> force Ontologist mode once
|
||||
- Max rounds reached -> proceed with explicit residual-risk warning
|
||||
- All dimensions >= 0.9 -> allow early crystallization even before max rounds
|
||||
</Escalation_And_Stop_Conditions>
|
||||
|
||||
<Final_Checklist>
|
||||
- [ ] Preflight context snapshot exists under `.omx/context/{slug}-{timestamp}.md`
|
||||
- [ ] Oversized initial context, if present, has a prompt-safe summary recorded before ambiguity scoring or downstream handoff
|
||||
- [ ] Ambiguity score shown each round
|
||||
- [ ] Intent-first stage priority used before implementation detail
|
||||
- [ ] Weakest-dimension targeting used within the active stage
|
||||
- [ ] At least one explicit assumption probe happened before crystallization
|
||||
- [ ] At least one persistent follow-up / pressure pass deepened a prior answer
|
||||
- [ ] Challenge modes triggered at thresholds (when applicable)
|
||||
- [ ] Transcript written to `.omx/interviews/{slug}-{timestamp}.md`
|
||||
- [ ] Spec written to `.omx/specs/deep-interview-{slug}.md`
|
||||
- [ ] Brownfield questions use evidence-backed confirmation when applicable
|
||||
- [ ] Brownfield preflight inspected applicable repo docs/rules/context before user-facing questions
|
||||
- [ ] Fuzzy or conflicting terminology was challenged against repo language/current code behavior when applicable
|
||||
- [ ] Scenario-based edge-case grilling was used when boundary ambiguity would materially affect implementation
|
||||
- [ ] Durable docs/ADR/memory updates, if any, were explicitly opted into and public-safe
|
||||
- [ ] Handoff options provided (`$ultragoal`, `$ralplan`, `$autopilot`, `$team`) plus context-sensitive goal-mode suggestions (`$autoresearch`, `$performance-goal`) when applicable
|
||||
- [ ] No direct implementation performed in this mode
|
||||
</Final_Checklist>
|
||||
|
||||
<Advanced>
|
||||
## Suggested Config (optional)
|
||||
|
||||
Deep-interview reads runtime defaults from the first existing config source in this order:
|
||||
|
||||
1. Repository-local `.omx/config.toml`
|
||||
2. Repository-root `omx.toml`
|
||||
3. User-global `~/.omx/config.toml`
|
||||
|
||||
This section is currently a deep-interview-specific runtime override surface, not a general replacement for Codex `config.toml` or `.omx-config.json` model/env routing.
|
||||
Malformed config files are ignored fail-soft so `$deep-interview` activation can continue with built-in defaults.
|
||||
Explicit `--quick`, `--standard`, or `--deep` invocation flags override `defaultProfile`.
|
||||
|
||||
```toml
|
||||
[omx.deepInterview]
|
||||
defaultProfile = "standard"
|
||||
quickThreshold = 0.30
|
||||
standardThreshold = 0.20
|
||||
deepThreshold = 0.15
|
||||
quickMaxRounds = 5
|
||||
standardMaxRounds = 12
|
||||
deepMaxRounds = 20
|
||||
enableChallengeModes = true
|
||||
```
|
||||
|
||||
## Resume
|
||||
|
||||
If interrupted, rerun `$deep-interview`. Resume from persisted mode state via `omx state read --input '{"mode":"deep-interview"}' --json`.
|
||||
|
||||
## Canonical Autopilot Entry Pipeline
|
||||
|
||||
```
|
||||
autopilot -> deep-interview -> ralplan -> ultragoal
|
||||
```
|
||||
|
||||
- Autopilot begins with this independent deep-interview clarity gate; it must not substitute `$plan --interview`.
|
||||
- Stage 1 (deep-interview): ambiguity clearance and execution-ready requirements artifact
|
||||
- Stage 2 (ralplan): feasibility, architecture, and consensus gate
|
||||
- Stage 3 (ultragoal): durable execution, verification, and completion receipts
|
||||
</Advanced>
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
name: design
|
||||
description: Canonical repo-local DESIGN.md workflow for product, UI/UX, and frontend decision source of truth
|
||||
---
|
||||
|
||||
# Design Skill
|
||||
|
||||
Use `$design` to discover product and UI evidence, close only design-critical context gaps, and create or refresh the repository’s durable `DESIGN.md` contract. It is a maintained design brief, not a pixel-matching loop or one-off critique.
|
||||
|
||||
Shared operating, delegation, state, hook, team, cancellation, and verification invariants live in [`templates/AGENTS.md`](../../templates/AGENTS.md). Follow that source instead of duplicating its rules here.
|
||||
|
||||
## Use when
|
||||
|
||||
- Product, UX, frontend, or design-system decisions need a repo-local source of truth.
|
||||
- A feature needs a design brief before a designer lane or implementation.
|
||||
- Existing UI, assets, screenshots, or constraints need an actionable design summary.
|
||||
|
||||
Do not use it for visual-reference implementation matching (use `$visual-ralph`), screenshot comparison alone, or backend/infrastructure work without user-facing design impact.
|
||||
|
||||
## Relationship to `$visual-ralph`
|
||||
|
||||
`$design` owns product goals, users, information architecture, visual language, components, accessibility, constraints, and open questions in `DESIGN.md`. `$visual-ralph` owns implementation against an approved visual reference or live-URL baseline, measured verdicts, and pixel-diff evidence. Run `$design` first when both are needed; `DESIGN.md` supports but does not replace the visual verdict target.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Discover local evidence
|
||||
|
||||
Inspect and cite existing `DESIGN.md`, design/UX/frontend docs, README/specs/issues, routes/pages/layouts/components/stories, theme and token files, assets, screenshots/mockups, Storybook or Playwright baselines, and accessibility/responsive/i18n/platform constraints. Separate observations from inferences; note absent evidence.
|
||||
|
||||
### 2. Interview only missing context
|
||||
|
||||
Ask concise questions only for gaps the repository cannot resolve: users/jobs, goals/non-goals, brand personality and forbidden aesthetics, primary flows, accessibility/device/browser targets, or unavailable assets/references. If answers are unavailable, record explicit assumptions and open questions rather than blocking.
|
||||
|
||||
### 3. Create or refresh `DESIGN.md`
|
||||
|
||||
Preserve useful content, remove contradictions, mark unknowns, and keep decisions actionable. The root file must contain these sections:
|
||||
|
||||
# Design
|
||||
## Source of truth
|
||||
Status (Draft | Active | Needs refresh), date, product surfaces, evidence reviewed.
|
||||
## Brand
|
||||
Personality, trust signals, avoid.
|
||||
## Product goals
|
||||
Goals, non-goals, success signals.
|
||||
## Personas and jobs
|
||||
Primary personas, user jobs, contexts of use.
|
||||
## Information architecture
|
||||
Navigation, routes/screens, content hierarchy.
|
||||
## Design principles
|
||||
Principles and tradeoffs.
|
||||
## Visual language
|
||||
Color, typography, spacing, shape/elevation, motion, imagery/iconography.
|
||||
## Components
|
||||
Existing/new components, variants/states, token ownership.
|
||||
## Accessibility
|
||||
Target standard, keyboard/focus, contrast, semantics, reduced motion/sensory concerns.
|
||||
## Responsive behavior
|
||||
Breakpoints/devices, layout adaptations, touch/hover differences.
|
||||
## Interaction states
|
||||
Loading, empty, error, success, disabled, offline/slow network where applicable.
|
||||
## Content voice
|
||||
Tone, terminology, microcopy rules.
|
||||
## Implementation constraints
|
||||
Framework/styling, tokens, performance, compatibility, test/screenshot expectations.
|
||||
## Open questions
|
||||
`[ ]` question, owner, and impact.
|
||||
|
||||
### 4. Apply the contract
|
||||
|
||||
Before UI decisions, cite relevant `DESIGN.md` sections, reuse documented components/tokens, and update the file or add an open question when implementation exposes a contradiction. Do not invent a parallel design-system layer.
|
||||
|
||||
### 5. Handoff
|
||||
|
||||
For normal frontend work, provide the relevant sections, repo evidence, and acceptance criteria. For visual-reference, image, or live-URL matching, hand off to `$visual-ralph` with the approved baseline and identify `DESIGN.md` as supporting context only.
|
||||
|
||||
## Evidence and completion
|
||||
|
||||
Complete only when design docs/assets/components/screenshots were inspected or noted absent; missing context is answered, assumed, or listed; root `DESIGN.md` contains every required section; recommendations cite it; and any Visual Ralph handoff is clearly separated from design governance.
|
||||
|
||||
Task: {{ARGUMENTS}}
|
||||
@@ -1,118 +0,0 @@
|
||||
---
|
||||
name: doctor
|
||||
description: Diagnose and fix oh-my-codex installation issues
|
||||
---
|
||||
|
||||
# OMX Doctor
|
||||
|
||||
Use this task card to diagnose installation/discovery problems. Shared safety, lifecycle, hook, team, autonomy, and cancellation rules live in `templates/AGENTS.md`. `~/.codex` means `${CODEX_HOME:-~/.codex}` when `CODEX_HOME` is set.
|
||||
|
||||
## Run diagnostics
|
||||
|
||||
```bash
|
||||
omx doctor
|
||||
omx doctor --team # include Team runtime checks
|
||||
omx doctor --verbose # include detailed check evidence
|
||||
omx doctor --force # repair eligible repo artifact ownership issues
|
||||
omx doctor --dry-run --force # preview eligible ownership repairs
|
||||
```
|
||||
|
||||
For a local source checkout, build first and invoke `node dist/cli/omx.js doctor`. Keep command output and exact paths for the report.
|
||||
|
||||
### Plugin cache and version
|
||||
|
||||
```bash
|
||||
PLUGIN_CACHE_ROOT="${CODEX_HOME:-$HOME/.codex}/plugins/cache"
|
||||
CACHE_ENTRIES=$(find "$PLUGIN_CACHE_ROOT" -path "*/oh-my-codex/*" -mindepth 3 -maxdepth 3 -type d 2>/dev/null)
|
||||
if [[ -z "$CACHE_ENTRIES" ]]; then
|
||||
echo "Installed plugin cache: none"
|
||||
else
|
||||
while IFS= read -r d; do
|
||||
m=$(basename "$(dirname "$(dirname "$d")")"); v=$(basename "$d")
|
||||
printf 'Installed plugin cache: marketplace=%s version=%s path=%s\n' "$m" "$v" "$d"
|
||||
done <<<"$CACHE_ENTRIES"
|
||||
fi
|
||||
LATEST=$(npm view oh-my-codex version 2>/dev/null)
|
||||
echo "Latest npm: $LATEST"
|
||||
```
|
||||
|
||||
No cache is INFO (normal for npm/setup-only installs). A non-`local` cache version differing from `LATEST`, or multiple versions for one marketplace, is WARN. Plugin discovery does not replace npm installation plus `omx setup`; native/runtime wiring remains setup-owned.
|
||||
|
||||
### Hooks, AGENTS, and skill roots
|
||||
|
||||
```bash
|
||||
CODEX_DIR="${CODEX_HOME:-$HOME/.codex}"
|
||||
ls -la "$CODEX_DIR/config.toml" "$CODEX_DIR/settings.json" 2>/dev/null
|
||||
rg 'keyword-detector|persistent-mode|session-start|stop-continuation' "$CODEX_DIR/config.toml" "$CODEX_DIR/settings.json" 2>/dev/null || true
|
||||
ls -la "$CODEX_DIR/hooks"/*.sh 2>/dev/null
|
||||
ls -la "$CODEX_DIR/AGENTS.md" 2>/dev/null
|
||||
grep -q "oh-my-codex Multi-Agent System" "$CODEX_DIR/AGENTS.md" 2>/dev/null && echo "Has OMX config" || echo "Missing OMX config"
|
||||
ls -la "$CODEX_DIR/agents" "$CODEX_DIR/commands" "$CODEX_DIR/skills" "$HOME/.agents/skills" 2>/dev/null
|
||||
```
|
||||
|
||||
Classify legacy hook references as **CRITICAL** (duplicate hooks). The scripts `keyword-detector.sh`, `persistent-mode.sh`, `session-start.sh`, and `stop-continuation.sh` are **WARN**. Missing AGENTS.md is **CRITICAL**; missing the OMX marker is **WARN**. `${CODEX_HOME:-~/.codex}/skills` is the canonical user root. `~/.agents/skills` is historical and overlapping trees can duplicate skill entries. OMX files under legacy `agents/` or `commands/` are **WARN**.
|
||||
|
||||
With `--team`, retain any `resume_blocker`, `slow_shutdown`, `delayed_status_lag`, `stale_leader`, or `orphan_tmux_session` diagnostics; failed checks set a non-zero exit status.
|
||||
|
||||
## Report contract
|
||||
|
||||
```text
|
||||
## OMX Doctor Report
|
||||
### Summary
|
||||
[HEALTHY / ISSUES FOUND]
|
||||
### Checks
|
||||
| Check | Status | Details |
|
||||
| Plugin Version | OK/WARN/INFO | ... |
|
||||
| Hook Config (config.toml / legacy settings.json) | OK/CRITICAL | ... |
|
||||
| Legacy Scripts (~/.codex/hooks/) | OK/WARN | ... |
|
||||
| AGENTS.md | OK/WARN/CRITICAL | ... |
|
||||
| Plugin Cache | OK/WARN | ... |
|
||||
| Legacy Agents / Commands | OK/WARN | ... |
|
||||
| Skills (canonical / historical root) | OK/WARN | ... |
|
||||
### Issues Found
|
||||
1. ...
|
||||
### Recommended Fixes
|
||||
...
|
||||
```
|
||||
|
||||
## Remediation (after user confirmation and confirming each target is OMX-owned)
|
||||
|
||||
Remove only confirmed OMX legacy content; preserve unrelated user files:
|
||||
|
||||
```bash
|
||||
# If settings.json exists, remove only its legacy "hooks" section.
|
||||
rm -f "$CODEX_DIR/hooks/keyword-detector.sh" "$CODEX_DIR/hooks/persistent-mode.sh" "$CODEX_DIR/hooks/session-start.sh" "$CODEX_DIR/hooks/stop-continuation.sh"
|
||||
PLUGIN_CACHE_ROOT="${CODEX_HOME:-$HOME/.codex}/plugins/cache"
|
||||
find "$PLUGIN_CACHE_ROOT" -path "*/oh-my-codex" -type d -prune -exec rm -rf {} +
|
||||
```
|
||||
|
||||
For older curl installs, inspect first and remove only directories containing OMX-managed files:
|
||||
|
||||
```bash
|
||||
ls -la "$CODEX_DIR/agents" "$CODEX_DIR/commands" "$CODEX_DIR/skills" "$HOME/.agents/skills" 2>/dev/null
|
||||
# Optional backups:
|
||||
# mv "$CODEX_DIR/agents" "$CODEX_DIR/agents.bak"
|
||||
# mv "$CODEX_DIR/commands" "$CODEX_DIR/commands.bak"
|
||||
# mv "$HOME/.agents/skills" "$HOME/.agents/skills.bak"
|
||||
rm -rf "$CODEX_DIR/agents" "$CODEX_DIR/commands" "$HOME/.agents/skills"
|
||||
```
|
||||
|
||||
If AGENTS.md is missing/outdated, fetch the current raw file and write it to `$CODEX_DIR/AGENTS.md`:
|
||||
|
||||
```text
|
||||
WebFetch(url: "https://raw.githubusercontent.com/Yeachan-Heo/oh-my-codex/main/docs/AGENTS.md", prompt: "Return the complete raw markdown content exactly as-is")
|
||||
```
|
||||
|
||||
To keep only the newest cache version for a selected marketplace:
|
||||
|
||||
```bash
|
||||
PLUGIN_CACHE_DIR="$PLUGIN_CACHE_ROOT/$MARKETPLACE_NAME/oh-my-codex"
|
||||
KEEP_VERSION=$(for d in "$PLUGIN_CACHE_DIR"/*; do [[ -d "$d" ]] && basename "$d"; done | sort -V | tail -1)
|
||||
[[ -n "$KEEP_VERSION" ]] && find "$PLUGIN_CACHE_DIR" -mindepth 1 -maxdepth 1 -type d ! -name "$KEEP_VERSION" -exec rm -rf {} +
|
||||
```
|
||||
|
||||
If AGENTS.md is missing or stale, refresh it from the current release source, then rerun `omx doctor`. If legacy `~/.agents/skills` overlaps the canonical root, archive/remove that tree only after checking for custom skills. Restart Codex after fixes and report before/after evidence.
|
||||
|
||||
## Exit conditions
|
||||
|
||||
Stop with **HEALTHY** only when required paths, hook/config checks, AGENTS marker, plugin cache status, and (when requested) Team checks pass. Otherwise report **ISSUES FOUND**, exact failed evidence, and the smallest confirmed remediation; do not claim a fix from a recommendation alone.
|
||||
@@ -1,98 +0,0 @@
|
||||
---
|
||||
name: "hud"
|
||||
description: "Show or configure the OMX HUD (two-layer statusline)"
|
||||
role: "display"
|
||||
scope: ".omx/**"
|
||||
---
|
||||
|
||||
# HUD Skill
|
||||
|
||||
The OMX HUD uses a two-layer architecture:
|
||||
|
||||
1. **Layer 1 - Codex built-in statusLine**: Real-time TUI footer showing model, git branch, and context usage. Configured via `[tui] status_line` in `~/.codex/config.toml`. Zero code required.
|
||||
|
||||
2. **Layer 2 - `omx hud` CLI command**: Shows OMX-specific orchestration state (ralph, ultrawork, autopilot, team, pipeline, ecomode, turns). Reads `.omx/state/` files.
|
||||
|
||||
## Quick Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `omx hud` | Show current HUD (modes, turns, activity) |
|
||||
| `omx hud --watch` | Live-updating display (polls every 1s) |
|
||||
| `omx hud --json` | Raw state output for scripting |
|
||||
| `omx hud --preset=minimal` | Minimal display |
|
||||
| `omx hud --preset=focused` | Default display |
|
||||
| `omx hud --preset=full` | All elements |
|
||||
|
||||
## Presets
|
||||
|
||||
### minimal
|
||||
```
|
||||
[OMX] ralph:3/10 | turns:42
|
||||
```
|
||||
|
||||
### focused (default)
|
||||
```
|
||||
[OMX] ralph:3/10 | ultrawork | team:3 workers | turns:42 | last:5s ago
|
||||
```
|
||||
|
||||
### full
|
||||
```
|
||||
[OMX] ralph:3/10 | ultrawork | autopilot:execution | team:3 workers | pipeline:exec | turns:42 | last:5s ago | total-turns:156
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
`omx setup` automatically configures both layers:
|
||||
- Adds `[tui] status_line` to `~/.codex/config.toml` (Layer 1)
|
||||
- Writes `.omx/hud-config.json` with default preset (Layer 2)
|
||||
- Default preset is `focused`; if HUD/statusline changes do not appear, restart Codex CLI once.
|
||||
|
||||
## Layer 1: Codex Built-in StatusLine
|
||||
|
||||
Configured in `~/.codex/config.toml`:
|
||||
```toml
|
||||
[tui]
|
||||
status_line = ["model-with-reasoning", "git-branch", "context-remaining"]
|
||||
```
|
||||
|
||||
Available built-in items (Codex CLI v0.101.0+):
|
||||
`model-name`, `model-with-reasoning`, `current-dir`, `project-root`, `git-branch`, `context-remaining`, `context-used`, `five-hour-limit`, `weekly-limit`, `codex-version`, `context-window-size`, `used-tokens`, `total-input-tokens`, `total-output-tokens`, `session-id`
|
||||
|
||||
## Layer 2: OMX Orchestration HUD
|
||||
|
||||
The `omx hud` command reads these state files:
|
||||
- `.omx/state/ralph-state.json` - Ralph loop iteration
|
||||
- `.omx/state/ultrawork-state.json` - Ultrawork mode
|
||||
- `.omx/state/autopilot-state.json` - Autopilot phase
|
||||
- `.omx/state/team-state.json` - Team workers
|
||||
- `.omx/state/pipeline-state.json` - Pipeline stage
|
||||
- `.omx/state/ecomode-state.json` - Ecomode active
|
||||
- `.omx/state/hud-state.json` - Last activity (from notify hook)
|
||||
- `.omx/metrics.json` - Turn counts
|
||||
|
||||
## Configuration
|
||||
|
||||
HUD config stored at `.omx/hud-config.json`:
|
||||
```json
|
||||
{
|
||||
"preset": "focused"
|
||||
}
|
||||
```
|
||||
|
||||
## Color Coding
|
||||
|
||||
- **Green**: Normal/healthy
|
||||
- **Yellow**: Warning (ralph >70% of max)
|
||||
- **Red**: Critical (ralph >90% of max)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the TUI statusline is not showing:
|
||||
1. Ensure Codex CLI v0.101.0+ is installed
|
||||
2. Run `omx setup` to configure `[tui]` section
|
||||
3. Restart Codex CLI
|
||||
|
||||
If `omx hud` shows "No active modes":
|
||||
- This is expected when no workflows are running
|
||||
- Start an explicitly requested supported workflow (`$autopilot` or `$ultragoal`) and check again
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
name: omx-setup
|
||||
description: Setup and configure oh-my-codex using current CLI behavior
|
||||
---
|
||||
|
||||
# OMX Setup
|
||||
|
||||
Use this task card to install or refresh OMX for the current project and/or user scope. Shared safety, lifecycle, hook, team, autonomy, and cancellation rules live in `templates/AGENTS.md`. Do not infer new CLI behavior from this card; the implementation is authoritative.
|
||||
|
||||
## Command and alternatives
|
||||
|
||||
```bash
|
||||
omx setup [--force] [--merge-agents|--no-merge-agents|--clear-merge-agents-policy] [--dry-run] [--verbose] [--scope <user|project>] [--plugin|--legacy|--install-mode <legacy|plugin>] [--mcp <none|compat>] [--no-mcp|--with-mcp] [--disable-team|--enable-team|--team-mode <enabled|disabled>]
|
||||
omx agents-init [path] [--dry-run] [--force] [--verbose]
|
||||
```
|
||||
|
||||
Use `agents-init` for lightweight AGENTS.md scaffolding only, not a full install.
|
||||
|
||||
Supported setup options:
|
||||
- `--force`: overwrite/reinstall managed artifacts; transient, not persisted or replayed.
|
||||
- `--merge-agents`, `--no-merge-agents`, `--clear-merge-agents-policy`: exact bare selectors (no values/equals). Repeated identical selectors are idempotent; conflicting selectors fail before mutations. Set persists an explicit policy per invoking project root; clear removes it and cannot combine with a set. Merge refreshes managed sections; no-merge only suppresses that branch and is not a preserve mode.
|
||||
- `--dry-run`, `--verbose`: preview actions or show per-file/per-step detail.
|
||||
- `--scope user|project`: choose user (`~/.codex`, `~/.omx/agents`) or project (`./.codex`, `./.omx/agents`) targets.
|
||||
- `--plugin` / `--legacy`: select delivery mode; `--install-mode legacy|plugin` is the canonical scripted spelling. Conflicting mode flags fail.
|
||||
- `--mcp none|compat` (plus `--no-mcp` / `--with-mcp`): omit or enable first-party MCP compatibility and shared registry sync.
|
||||
- `--disable-team`, `--enable-team`, `--team-mode enabled|disabled`: select Team skill/context generation; conflicting selectors fail.
|
||||
|
||||
## Resolve setup
|
||||
|
||||
1. Scope: explicit flag, valid persisted `./.omx/setup-scope.json`, TTY choice (default `user`), or non-interactive `user`. Legacy `project-local` migrates to `project`.
|
||||
2. Mode: explicit flag, persisted mode when retained, discovered `${CODEX_HOME:-~/.codex}/plugins/cache/**/.codex-plugin/plugin.json` with `name: oh-my-codex` (plugin default), otherwise legacy. TTY reruns summarize saved choices and offer keep/review/reset; non-interactive runs do not block.
|
||||
3. Create required directories and atomically persist preferences only after successful setup.
|
||||
|
||||
## What setup changes
|
||||
|
||||
- Legacy mode installs prompts/native agents/skills and merges full config.toml.
|
||||
- Plugin mode archives/removes legacy OMX-managed prompts/skills, refreshes installable native agent TOMLs and stale generated non-installable agents, and keeps setup-owned runtime hooks.
|
||||
- Both modes verify Team CLI interop markers in built `dist/cli/team.js`, generate AGENTS defaults only when selected/allowed, and write `./.omx/hud-config.json`; notification hook references are setup-owned outside plugin skill delivery.
|
||||
- `AGENTS.md` is `./AGENTS.md`. Without `--force` or `--merge-agents`, interactive runs ask before overwrite and non-interactive runs preserve it. Active-session safeguards may skip writes. Plugin mode may separately prompt for AGENTS and `developer_instructions` defaults; non-interactive runs preserve existing files.
|
||||
- Plugin mode enables setup-owned runtime flags (`plugin_hooks`, `goals`, or legacy `hooks`/`codex_hooks` fallback). Project scope uses `CODEX_HOME=./.codex` unless explicitly overridden.
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
omx setup --force --verbose
|
||||
omx doctor
|
||||
```
|
||||
|
||||
Expect doctor evidence for prompts and skills in the selected scope, project-root AGENTS.md, `.omx/state`, and CLI-first config in the scope target `config.toml`. First-party MCP and shared registry sync are omitted unless setup used `--mcp compat` (if supported by the installed CLI).
|
||||
|
||||
If using local source, run `npm run build` before `node dist/cli/omx.js setup --force --verbose` and `node dist/cli/omx.js doctor`. If AGENTS was not overwritten or merged, stop the active OMX session and rerun with `--force` or `--merge-agents`; do not edit lifecycle state manually.
|
||||
|
||||
## Discovery troubleshooting
|
||||
|
||||
If `$omx-setup` is absent/stale: run `omx setup --verbose`, then `omx doctor`; verify `./.codex/skills/omx-setup/SKILL.md` for project scope or `${CODEX_HOME:-~/.codex}/skills/omx-setup/SKILL.md` for legacy user scope. In plugin mode verify the oh-my-codex plugin is installed/discovered. For duplicates, inspect historical `~/.agents/skills` overlap and follow doctor/setup cleanup guidance.
|
||||
|
||||
## Exit contract
|
||||
|
||||
Report selected scope/mode, dry-run versus mutation, AGENTS policy, affected roots, and doctor indicators. Stop only after setup completes successfully and verification identifies the expected files/config; on failure preserve the exact command output and rerun the smallest corrective command. Do not claim installation from a dry-run alone.
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
name: performance-goal
|
||||
description: "Run an evaluator-gated performance optimization workflow over Codex goal mode with durable OMX artifacts and safe goal handoffs."
|
||||
---
|
||||
|
||||
# Performance Goal Workflow
|
||||
|
||||
Use this skill when a user asks OMX to optimize performance and wants a goal-oriented loop rather than a one-off review.
|
||||
|
||||
## Contract
|
||||
|
||||
- OMX owns durable workflow state under `.omx/goals/performance/<slug>/`.
|
||||
- Codex goal mode owns only the active-thread focus/accounting primitive.
|
||||
- Shell commands do **not** mutate hidden Codex goal state. They write artifacts and emit model-facing handoff text.
|
||||
- No optimization work may start until an evaluator command and pass/fail contract exist.
|
||||
- Do not call `update_goal({status: "complete"})` until the evaluator has a passing checkpoint and a completion audit proves the objective is done; then call `get_goal` again and pass that fresh snapshot to `omx performance-goal complete --codex-goal-json`.
|
||||
|
||||
## CLI
|
||||
|
||||
Create the workflow and evaluator contract:
|
||||
|
||||
```sh
|
||||
omx performance-goal create \
|
||||
--objective "Reduce CLI startup latency by 20%" \
|
||||
--evaluator-command "npm run perf:startup" \
|
||||
--evaluator-contract "PASS when p95 latency improves by 20% and regression tests pass" \
|
||||
--slug startup-latency
|
||||
```
|
||||
|
||||
Emit the Codex goal handoff:
|
||||
|
||||
```sh
|
||||
omx performance-goal start --slug startup-latency
|
||||
```
|
||||
|
||||
Record evaluator evidence:
|
||||
|
||||
```sh
|
||||
omx performance-goal checkpoint --slug startup-latency --status pass --evidence "benchmark + tests passed"
|
||||
omx performance-goal checkpoint --slug startup-latency --status fail --evidence "benchmark regressed"
|
||||
omx performance-goal checkpoint --slug startup-latency --status blocked --evidence "missing fixture"
|
||||
```
|
||||
|
||||
Complete only after a passing checkpoint:
|
||||
|
||||
```sh
|
||||
omx performance-goal complete --slug startup-latency --evidence "final evaluator evidence" --codex-goal-json <get_goal-json-or-path>
|
||||
```
|
||||
|
||||
## Agent Loop
|
||||
|
||||
1. Run `omx performance-goal create` if no workflow exists.
|
||||
2. Run `omx performance-goal start` and follow the handoff:
|
||||
- call `get_goal`;
|
||||
- call `create_goal` only when no active goal exists and the objective is explicit;
|
||||
- work only against the evaluator contract;
|
||||
- after evaluator pass and completion audit, call `update_goal({status: "complete"})`, call `get_goal` again, and pass that snapshot to `omx performance-goal complete --codex-goal-json`;
|
||||
- after `omx performance-goal complete` succeeds, run `/goal clear` in the Codex UI before starting another goal in this same thread/session; OMX prints this terminal cleanup step but does not invoke hidden clear routes;
|
||||
3. Optimize in small reversible patches.
|
||||
4. Run the evaluator and related regression tests.
|
||||
5. Record each pass/fail/blocker with `checkpoint`.
|
||||
6. Complete only when the pass artifact exists and no required work remains.
|
||||
|
||||
## Completion Gate
|
||||
|
||||
A performance goal is incomplete unless `.omx/goals/performance/<slug>/state.json` contains a `lastValidation.status` of `pass` and `omx performance-goal complete` receives a matching complete Codex `get_goal` snapshot via `--codex-goal-json`. Passing ordinary tests alone is not sufficient unless they are the declared evaluator contract.
|
||||
|
||||
Lifecycle: `create_goal` starts the Codex thread goal, `update_goal({status: "complete"})` marks terminal success after the evaluator and audit pass, and `/goal clear` removes the completed thread goal when another same-thread goal is needed. OMX shell commands and hooks reconcile snapshots and print the cleanup instruction; they must not mutate hidden Codex goal state.
|
||||
@@ -1,94 +0,0 @@
|
||||
---
|
||||
name: plan
|
||||
description: Lightweight planning with optional interview
|
||||
---
|
||||
|
||||
<Purpose>
|
||||
Plan creates concise, actionable work plans. Auto-detects interview vs direct, supports `--interview` for Socratic clarification and `--review` for evaluation.
|
||||
</Purpose>
|
||||
|
||||
<Use_When>
|
||||
- Planning before implementation — "plan this", "let's plan"
|
||||
- Requirements are vague and need scoping — use `--interview`
|
||||
- Reviewing an existing plan — `--review`
|
||||
</Use_When>
|
||||
|
||||
<Do_Not_Use_When>
|
||||
- Want direct execution — execute the scoped task directly; use `ultragoal` only for durable goals or `team` for coordinated parallel work
|
||||
- Simple focused fix with obvious scope — skip planning
|
||||
</Do_Not_Use_When>
|
||||
|
||||
<Execution_Policy>
|
||||
- Auto-detect interview vs direct; `--interview` forces Socratic mode, `--direct` skips it
|
||||
- Ask one question at a time; gather codebase facts first
|
||||
- `omx explore` is deprecated. Use normal repository inspection for codebase facts; `omx sparkshell` only for explicit shell-native evidence
|
||||
- Plans must be evidence-backed: 80%+ claims cite file/line, 90%+ criteria testable
|
||||
- Right-size steps to scope; save to `.omx/plans/`
|
||||
</Execution_Policy>
|
||||
|
||||
<Steps>
|
||||
|
||||
### Mode Selection
|
||||
| Mode | Trigger | Behavior |
|
||||
|------|---------|----------|
|
||||
| Interview | `--interview` or broad request | Socratic requirements gathering |
|
||||
| Direct | `--direct` or detailed request | Generate plan immediately |
|
||||
| Review | `--review` | Critic evaluation of existing plan |
|
||||
|
||||
### Interview (`--interview`)
|
||||
1. Classify request; ask one focused question via `omx question`
|
||||
2. Gather codebase facts first, then ask informed follow-ups
|
||||
3. Build on answers; consult Analyst for hidden requirements
|
||||
4. Create plan when user signals readiness
|
||||
|
||||
### Direct
|
||||
1. Quick analysis (optional Analyst)
|
||||
2. Generate plan immediately
|
||||
3. Optional Critic review if requested
|
||||
|
||||
### Review (`--review`)
|
||||
1. Read plan from `.omx/plans/`
|
||||
2. Evaluate via Critic; verdict APPROVED/REVISE/REJECT
|
||||
3. If author == reviewer, hand off to `$code-review`
|
||||
|
||||
### Output
|
||||
Every plan includes: Requirements Summary, Acceptance Criteria (testable), Implementation Steps (with file refs, adaptive count), Risks/Mitigations, Verification. Saved to `.omx/plans/`.
|
||||
|
||||
Outcome-first framing: apply outcome-first framing, concise visible updates for multi-step planning. Local overrides for the active workflow branch apply via newer user task updates. If the user says `continue`, continue the current branch instead of restarting.
|
||||
|
||||
</Steps>
|
||||
|
||||
<Tool_Usage>
|
||||
- `omx explore` is deprecated. Use normal repository inspection; `omx sparkshell` only for explicit shell-native evidence
|
||||
- Use `omx question` for interview questions; plain text only as fallback
|
||||
- Use `ask_codex` with `planner`/`analyst`/`critic` as needed
|
||||
</Tool_Usage>
|
||||
|
||||
<Escalation_And_Stop_Conditions>
|
||||
- Stop interviewing when requirements are clear — do not over-interview
|
||||
- Escalate on irreconcilable trade-offs requiring business decision
|
||||
- "just do it" / "skip planning" → hand off to `$ultragoal`
|
||||
</Escalation_And_Stop_Conditions>
|
||||
|
||||
<Final_Checklist>
|
||||
- [ ] Acceptance criteria testable (90%+ concrete)
|
||||
- [ ] Claims cite file/line (80%+)
|
||||
- [ ] Risks have mitigations, no vague terms
|
||||
- [ ] Plan saved to `.omx/plans/`
|
||||
</Final_Checklist>
|
||||
|
||||
<Advanced>
|
||||
## Question Classification
|
||||
| Type | Examples | Action |
|
||||
|------|----------|--------|
|
||||
| Codebase Fact | "Where is X?" | Explore first, do not ask |
|
||||
| User Preference | "Priority?" | Ask via `omx question` |
|
||||
| Scope Decision | "Include Y?" | Ask user |
|
||||
|
||||
## Review Quality Criteria
|
||||
| Criterion | Standard |
|
||||
|-----------|----------|
|
||||
| Clarity | 80%+ claims cite file/line |
|
||||
| Testability | 90%+ criteria concrete |
|
||||
| Verification | File refs exist |
|
||||
</Advanced>
|
||||
@@ -1,149 +0,0 @@
|
||||
---
|
||||
name: ralplan
|
||||
description: Consensus planning stage for Planner -> Architect -> Critic handoff to $ultragoal
|
||||
---
|
||||
|
||||
# Ralplan (Consensus Planning Alias)
|
||||
|
||||
Ralplan is the canonical consensus-planning stage used by Autopilot between `$deep-interview` and `$ultragoal`. It drives Planner, Architect, and Critic planning and records their review lifecycle with **RALPLAN-DR structured deliberation** (short mode by default, deliberate mode for high-risk work). Local lifecycle evidence is not host-issued security authority, but ordinary progression to Ultragoal must remain reachable after the execution-ready plan and sequential review evidence are durable; missing host provenance must not terminalize Ralplan or block cancel, clear, or recovery.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
$ralplan "task description"
|
||||
```
|
||||
|
||||
Standalone advisory planning is explicitly opt-in:
|
||||
|
||||
```text
|
||||
$ralplan --advisory "task description"
|
||||
```
|
||||
|
||||
Advisory runs the same sequential Planner → Architect → Critic review lifecycle, binds the plan and both review artifacts to exact bytes and one tracker-backed iteration, then returns to the caller with `active:false`. It is a cooperative workflow pause, not a security fence or permission system. It never emits a `PreToolUse` allow/block decision, never completes the host consensus gate, never authorizes execution, and never suppresses unrelated host behavior. Terminal state must carry explicit `false` values for the consensus gate, host verification, and execution handoff rather than omitting them. A later concrete affirmative execution request may produce non-authoritative routing context, but it does not persist a permission, rewrite terminal evidence, or create an automatic handoff; quotations, code, examples, documentation, questions, modal requests, negations, and vague approval remain classifier negatives. `approved+proven` requires complete lifecycle digests plus post-write revalidation. Administrative abandonment is append-only, idempotent for prepared or committed journals, and records a separate byte-bound admin event without rewriting the original closeout journal. Real enforcement requires an explicit host-issued, host-verified receipt or capability on a non-user-mintable surface; local Advisory files, prompts, session/thread fields, tracker records, and HERDR observability cannot substitute for it. On Darwin, each Advisory evidence artifact is limited to 128 KiB by the pinned-directory reader; other supported platforms allow up to 8 MiB.
|
||||
|
||||
## Flags
|
||||
|
||||
- `--interactive`: Enables user prompts at key decision points (draft review in step 2 and final approval in step 6). Without this flag the workflow runs fully automated — Planner → Architect → Critic loop — and outputs the final plan without asking for confirmation.
|
||||
- `--deliberate`: Forces deliberate mode for high-risk work. Adds pre-mortem (3 scenarios) and expanded test planning (unit/integration/e2e/observability). Without this flag, deliberate mode can still auto-enable when the request explicitly signals high risk (auth/security, migrations, destructive changes, production incidents, compliance/PII, public API breakage).
|
||||
- `--advisory`: Standalone only. It cannot be combined with Autopilot, Pipeline, Team, Ralph, Ultragoal, execution flags, or unknown flags. Activation requires canonical session, root thread, and turn identity.
|
||||
|
||||
## Ontology-heavy review
|
||||
|
||||
For requirements semantics, taxonomy, prompt/spec design, policy distinctions, or category-risk architecture, cite the `architect` role agent's read-only review as advisory evidence. Its findings can inform the plan or follow-up evidence when explicitly used, but `$ralplan` itself records Architect→Critic lifecycle evidence only, and advisory review is never a durable execution authorization.
|
||||
|
||||
## Usage with interactive mode
|
||||
|
||||
```
|
||||
$ralplan --interactive "task description"
|
||||
```
|
||||
|
||||
## Behavior
|
||||
|
||||
## GPT-5.6 Guidance Alignment
|
||||
|
||||
Use the shared workflow guidance pattern: outcome-first framing, concise visible updates for multi-step planning, local overrides for the active workflow branch, evidence-backed planning and validation expectations, explicit stop rules, right-sized implementation/PRD shape, and automatic continuation for safe reversible steps. Ask only for material, destructive, credentialed, external-production, or preference-dependent branches.
|
||||
|
||||
This skill runs its own consensus runtime; it does not delegate to a nonexistent Plan consensus mode:
|
||||
|
||||
```
|
||||
omx ralplan run --task <arguments> [--session <id>]
|
||||
```
|
||||
|
||||
The consensus workflow:
|
||||
1. **Planner** creates an adaptive plan (right-sized to task scope; do not default to exactly five steps) and a compact **RALPLAN-DR summary** before review. Current `[main]` vs `[planner]` behavior: standalone `$ralplan` may be authored by the active main planning lane unless the caller/runtime supplies a dedicated planner routing record; inside `$autopilot`, state field `planning_routing.owner:"planner"` means the initial Planner draft/decomposition must use dedicated `[planner]`. Set `.omx-config.json` `agentModels.planner` to opt into a specific planner model and force dedicated planner ownership for complex Autopilot planning even when `[main]` is not cheap/mini. The RALPLAN-DR summary includes:
|
||||
- Principles (3-5)
|
||||
- Decision Drivers (top 3)
|
||||
- Viable Options (>=2) with bounded pros/cons
|
||||
- If only one viable option remains, explicit invalidation rationale for alternatives
|
||||
- Deliberate mode only: pre-mortem (3 scenarios) + expanded test plan (unit/integration/e2e/observability)
|
||||
2. **User feedback** *(--interactive only)*: If `--interactive` is set, use the structured question UI (`omx question` in attached tmux; native structured input outside tmux when available) to present the draft plan **plus the Principles / Drivers / Options summary** before review (Proceed to review / Request changes / Skip review). Otherwise, automatically proceed to review.
|
||||
**Native role-routing preflight:** Keyword routing may already have selected Ralplan, but it is not authority. Run `omx ralplan preflight --json` only when the native task surface reports `role_routing_unavailable` and this workflow attempts adapted Ralplan Planner, Architect, or Critic authority, adapted role-intent, or adapted consensus authority. On `unsupported_documented_leader_proof`, stop before that adapted authority and use a Codex surface with documented root proof or a reviewed alternative workflow. Do not infer root identity from `session_id`, undocumented `thread_id`, session/pointer/transcript/cwd state, absence of child data, or a prompt label. Ordinary native planning, lifecycle, state, status, health, HUD, runtime, setup, install, sync, and unrelated delegation are outside this preflight boundary and remain governed by existing controls.
|
||||
|
||||
**Native role-routing rule:** When the native surface exposes `agent_type` role routing, set `agent_type` to an installed OMX role and never omit it for OMX work. When it does not (`role_routing_unavailable`), do not fabricate `agent_type`. On the exact reviewed Codex releases 0.144.5, 0.145.0, 0.146.1, and 0.148.0-alpha.5, adapted Ralplan Planner, Architect, Critic, role-intent, and consensus authority are unavailable because they lack documented root proof; every other version remains unknown and fails closed. Do not silently weaken routing with a prompt role label or inferred carrier. Use a Codex surface with documented root proof or a reviewed alternative workflow for that authority. A direct `omx ralplan role-intent write` attempt is denied with machine reason `unsupported_documented_leader_proof`.
|
||||
|
||||
3. **Architect** reviews for architectural soundness and must provide the strongest steelman antithesis, at least one real tradeoff tension, and (when possible) synthesis — **await completion before step 4**. Launch this as a subsequent role-specific `Architect` subagent and pass the full task statement, context snapshot, PRD/test-spec paths, and relevant prior findings; do not substitute an unvalidated reviewer identity or a short improvised reviewer prompt. In deliberate mode, Architect should explicitly flag principle violations.
|
||||
4. **Critic** evaluates against quality criteria — run only after step 3 completes. Launch this as a subsequent role-specific `Critic` subagent with the full task statement, context snapshot, PRD/test-spec paths, and the completed Architect review; do not ask the Architect subagent to perform the Critic gate and do not substitute an unvalidated reviewer identity or a short improvised reviewer prompt. Critic must enforce principle-option consistency, fair alternatives, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. In deliberate mode, Critic must reject missing/weak pre-mortem or expanded test plan.
|
||||
5. **Re-review loop** (max 5 iterations): Any non-`APPROVE` Critic verdict (`ITERATE` or `REJECT`) MUST run the same full closed loop:
|
||||
a. Collect Architect and Critic feedback
|
||||
b. Revise the plan with Planner
|
||||
c. Return to Architect review
|
||||
d. Return to Critic evaluation
|
||||
e. Repeat this loop until Critic returns `APPROVE` or 5 iterations are reached
|
||||
f. If 5 iterations are reached without `APPROVE`, present the best version to the user
|
||||
6. On Critic approval, persist the execution-ready planning artifacts and sequential Architect→Critic evidence. In standalone interactive Ralplan, present the requested future execution lane. Inside Autopilot, the existing explicit `$autopilot` invocation authorizes the supervised transition to its defining next stage, `$ultragoal`; persist an Autopilot-owned `ralplan_execution_handoff` bound to the same session and review cycle.
|
||||
7. Record `ralplan_execution_handoff` with `{authorized: true, reason, authorized_at, session_id, review_cycle, source: "autopilot"|"user"}`. `source:"autopilot"` is valid only for a supervised active Autopilot run whose current phase is `ralplan`; it does not claim host-consensus authority.
|
||||
8. Transition to `$ultragoal` after the durable plan, sequential approvals, and bound execution handoff exist. Do not implement directly inside Ralplan.
|
||||
|
||||
> **Important:** Steps 3 and 4 MUST run sequentially as role-specific subagents. Do NOT issue both agent calls in the same parallel batch. Always await the subsequent `Architect` result before invoking the subsequent `Critic`; their completed approvals establish local lifecycle evidence only and cannot satisfy the durable execution gate.
|
||||
|
||||
## Planning/Execution Boundary
|
||||
|
||||
`$ralplan` is a planning mode. While ralplan is active and no explicit execution handoff is active, implementation-focused write tools are out of scope. Ralplan may inspect the repository and may write only planning artifacts such as `.omx/context/`, `.omx/plans/`, `.omx/specs/`, and required `.omx/state/` records.
|
||||
|
||||
The canonical flow is:
|
||||
|
||||
```
|
||||
$ralplan -> local Architect→Critic lifecycle evidence -> bound execution handoff -> $ultragoal
|
||||
```
|
||||
|
||||
Before any execution lane begins, ralplan must emit terminal planning state (complete, paused, failed, or waiting for input) and the durable handoff record below. Do not continue from consensus planning into direct code edits in the same ralplan session.
|
||||
|
||||
## Durable Consensus Handoff Contract
|
||||
|
||||
Ralplan is not complete, skippable, or ready for execution merely because `.omx/plans/prd-*.md` and `.omx/plans/test-spec-*.md` exist. Those files are planning artifacts, not consensus evidence.
|
||||
|
||||
Before any Autopilot, Ultragoal, Team, or implementation handoff, persist a durable handoff record that distinguishes:
|
||||
|
||||
- `planning_artifacts`: PRD/test-spec paths.
|
||||
- `ralplan_architect_review`: the completed Architect review with an approving verdict.
|
||||
- `ralplan_critic_review`: the completed Critic review with an approving verdict, recorded only after the Architect review.
|
||||
- `ralplan_execution_handoff`: persist `{authorized: true, reason: "<rationale>", authorized_at: "<ISO timestamp>", session_id: "<current session>", review_cycle: <matching lifecycle cycle>, source: "autopilot"|"user"}`. Autopilot may issue this only for its own supervised `ralplan` phase; standalone Ralplan uses `source:"user"`.
|
||||
- `ralplan_consensus_gate.complete` records lifecycle completion after the sequential Architect and Critic approvals. It is not a host-security claim. Locally authored JSON/env/prompt/tracker/transcript/receipt-shaped evidence must never be described as host-issued authority.
|
||||
|
||||
If Architect is missing/blocked, keep the workflow in Architect review or report that blocker. If Critic is missing/blocked/non-approving, keep the workflow in Critic/re-review or report the max-iteration outcome. After both reviews approve, execution begins only when the matching `ralplan_execution_handoff` is durable. Existing plan/test-spec files alone are never permission to skip Ralplan or execute.
|
||||
|
||||
Follow the Plan skill's full documentation for consensus mode details.
|
||||
|
||||
## Goal-Mode Follow-up Suggestions
|
||||
|
||||
When a bound `ralplan_execution_handoff` permits execution, include product-facing goal-mode suggestions alongside coordinated Team execution. Record the requested lane and persist the handoff without claiming host-issued authority.
|
||||
|
||||
- `$ultragoal` — **default goal-mode follow-up** for implementation or general goal-oriented follow-up plans that should become durable Codex/OMX goals with sequential completion tracking.
|
||||
- `$autoresearch` — research-project follow-up when the plan centers on a question, literature/reference gathering, evaluator-backed research, or a professor/critic-style research deliverable. (`$autoresearch-goal` was retired to a sunset stub in OMX 0.21.)
|
||||
- `$performance-goal` — optimization/performance follow-up when the plan centers on speed, latency, throughput, memory, benchmark, or other measurable performance work.
|
||||
|
||||
Use `$ultragoal` for durable goal tracking and `$team` for coordinated parallel implementation. When combined, Ultragoal owns the ledger and Team returns checkpoint-ready execution evidence.
|
||||
Use the available-agent-types roster to produce explicit role/staffing allocation, reasoning-by-lane guidance, concrete launch hints (including `omx team` when parallel delivery is justified), and team verification responsibilities for any future receipt-authorized execution path.
|
||||
|
||||
## Pre-context Intake
|
||||
|
||||
Before consensus planning or execution handoff, ensure a grounded context snapshot exists:
|
||||
|
||||
1. Derive a task slug from the request.
|
||||
2. Reuse the latest relevant snapshot in `.omx/context/{slug}-*.md` when available.
|
||||
3. If none exists, create `.omx/context/{slug}-{timestamp}.md` (UTC `YYYYMMDDTHHMMSSZ`) with:
|
||||
- task statement
|
||||
- desired outcome
|
||||
- known facts/evidence
|
||||
- constraints
|
||||
- unknowns/open questions
|
||||
- likely codebase touchpoints
|
||||
4. If ambiguity remains high, gather brownfield facts first. `omx explore` is deprecated; use normal repository inspection tools/subagents for simple read-only repository lookups and `omx sparkshell` only for explicit shell-native read-only evidence. Then run `$deep-interview --quick <task>` before continuing.
|
||||
5. If the plan depends on official docs, version-aware framework guidance, best practices, or external dependency behavior, use `$best-practice-research` as the bounded evidence wrapper and auto-delegate `researcher` for the official/upstream lookup before finalizing the planning handoff so execution does not start from repo-local recall alone.
|
||||
6. If a prior `$autoresearch` run exists, treat its approved artifact as evidence for the plan. Read historical artifacts from `$autoresearch-goal` only as compatibility input. Do not include Autoresearch as a final architecture or runtime component unless the user explicitly requested ongoing research automation; otherwise synthesize the evidence into the `$ralplan` ADR, risks, and verification steps.
|
||||
|
||||
Do not hand off to execution modes until this intake is complete; if urgency forces progress, explicitly document the risk tradeoffs.
|
||||
|
||||
## Routing boundary
|
||||
|
||||
Ordinary scoped tasks stay in the direct execution lane described by `templates/AGENTS.md`.
|
||||
Explicit workflow requests follow the keyword registry (`src/hooks/keyword-registry.ts`) and their owning skill contracts; this card does not define a second activation table.
|
||||
Once Ralplan is active, preserve the planning/execution and consensus handoff requirements above. A conversational “just do it” is not a substitute for the required review evidence.
|
||||
|
||||
## Scenario Examples
|
||||
|
||||
**Good:** The user says `continue` after the workflow already has a clear next step. Continue the current branch of work instead of restarting or re-asking the same question.
|
||||
|
||||
**Good:** The user changes only the output shape or downstream delivery step (for example `make a PR`). Preserve earlier non-conflicting workflow constraints and apply the update locally.
|
||||
|
||||
**Bad:** The user says `continue`, and the workflow restarts discovery or stops before the missing verification/evidence is gathered.
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
name: team
|
||||
description: N coordinated agents on shared task list using tmux-based orchestration
|
||||
---
|
||||
|
||||
# Team Skill
|
||||
|
||||
## When to use
|
||||
|
||||
Use only for an explicit `$team` request or `omx team ...` launch that benefits from durable tmux workers, shared task state, mailbox coordination, or worktree isolation. Read `AGENTS.md#durable-runtime-invariants-canonical-ssot`; do not copy its durable rules into this card.
|
||||
|
||||
## Inputs and preconditions
|
||||
|
||||
- Launch shape: `omx team [N:agent-type] "<task>"` (for example `omx team 3:executor "implement X"`).
|
||||
- Require `tmux -V`, a leader session with `$TMUX`, and the intended `omx` executable. In Codex App/plain non-tmux sessions, explain the runtime boundary instead of pretending Team is available.
|
||||
- Before launch, ground the task in a recent `.omx/context/{slug}-*.md`; create a concise snapshot when none exists. Include target, evidence, constraints, unknowns, and likely touchpoints.
|
||||
- Do not launch nested Team runs. Pick worker roles deliberately; use `OMX_TEAM_WORKER_CLI=codex|claude|auto` or `OMX_TEAM_WORKER_CLI_MAP=...` only when needed.
|
||||
- Per-agent reasoning is set by `agentReasoning`; accepted values are `low`, `medium`, `high`, `xhigh`, and `max`. `max` is passed unchanged and remains capability-dependent. `ultra` is unsupported and is not an alias for `max`.
|
||||
- Invalid configured values use the built-in role-default fallback. An explicit raw `-c model_reasoning_effort=...` is opaque and wins. When both sources are present, explicit raw reasoning wins over inherited Team reasoning and environment reasoning. Do not downgrade or retry `max` as `xhigh`; built-in role defaults remain unchanged.
|
||||
|
||||
## Operational steps
|
||||
|
||||
1. Start the runtime and capture startup evidence: `Team started: <name>`, tmux target, worker panes, and the leader ACK mailbox.
|
||||
2. **Current Runtime Behavior:** runtime-owned task files and APIs are the operational interface; the durable ownership and safety rules remain in `templates/AGENTS.md`.
|
||||
3. Runtime creates `.omx/state/team/<name>/config.json`, `manifest.v2.json`, `tasks/task-<id>.json`, worker identities/inboxes, mailbox files, and a dispatch queue. Workers receive `OMX_TEAM_WORKER`, `OMX_TEAM_STATE_ROOT`, and `OMX_TEAM_LEADER_CWD`.
|
||||
4. Deliver assignments through durable inbox/task state and the CLI API. The worker card defines ACK, claim, transition, mailbox, and idle-status commands.
|
||||
5. Prefer these machine-readable operations:
|
||||
```bash
|
||||
omx team api send-message --input '{"team_name":"<name>","from_worker":"leader-fixed","to_worker":"worker-1","body":"<short trigger>"}' --json
|
||||
omx team api read-task --input '{"team_name":"<name>","task_id":"<id>"}' --json
|
||||
omx team api transition-task-status --input '{"team_name":"<name>","task_id":"<id>","from":"in_progress","to":"completed","claim_token":"<token>"}' --json
|
||||
```
|
||||
6. Monitor with `omx team status <name> --json` or `omx team await <name> --timeout-ms 30000 --json`; inspect mailbox/state files when a worker is blocked or stale.
|
||||
7. Keep the team running until `pending=0`, `in_progress=0`, and `failed=0` (or an explicitly acknowledged failure path). Run `omx team shutdown <name>` only then, unless the user explicitly aborts.
|
||||
8. Verify shutdown evidence and state cleanup. Do not claim completion while workers are still writing.
|
||||
|
||||
## Team and Ultragoal
|
||||
|
||||
When a leader-owned `.omx/ultragoal/goals.json` exists, workers return task evidence only. The leader checkpoints with a fresh Codex `get_goal` snapshot:
|
||||
```bash
|
||||
omx ultragoal checkpoint --goal-id <id> --status complete --evidence "<Team evidence>" --codex-goal-json <fresh-get-goal-json-or-path>
|
||||
```
|
||||
Team launch remains an explicit separate action; it does not create hidden Codex goals.
|
||||
|
||||
## Dispatch and recovery
|
||||
|
||||
- If dispatch reports `worker_notify_failed:<worker>`, inspect `tmux list-panes`, capture the pane, then send one concise trigger and re-check mailbox/state.
|
||||
- For Claude panes, do not spam Enter while work is active. Confirm runtime status first.
|
||||
- If a worker reports `omx team api ... ENOENT`, check whether shutdown or state deletion happened too early; preserve state until all transitions finish.
|
||||
- For a clean retry, kill only known stale worker panes, remove only the exact stale Team root, and relaunch; never kill the leader/HUD pane accidentally.
|
||||
|
||||
## Exit and evidence
|
||||
|
||||
Report team name, launch command, pane/ACK evidence, task counts, worker verification, failures or blockers, shutdown result, and cleaned state paths. Include any worktree or CLI-map choices and keep final integration/verification in the leader lane.
|
||||
@@ -1,77 +0,0 @@
|
||||
---
|
||||
name: ultragoal
|
||||
description: Create and execute durable repo-native multi-goal plans over Codex goal mode artifacts.
|
||||
---
|
||||
|
||||
# Ultragoal Workflow
|
||||
|
||||
## When to use
|
||||
|
||||
Use for `ultragoal`, `create-goals`, `complete-goals`, durable multi-goal planning, or sequential execution over Codex `/goal`. Read `AGENTS.md#durable-runtime-invariants-canonical-ssot` for state ownership and goal-tool boundaries.
|
||||
|
||||
## Inputs and preconditions
|
||||
|
||||
- Durable artifacts are `.omx/ultragoal/brief.md`, `.omx/ultragoal/goals.json`, and `.omx/ultragoal/ledger.jsonl`.
|
||||
- New plans use aggregate Codex goal mode by default: one stable pointer objective for the plan while OMX tracks individual stories. Use `--codex-goal-mode per-story` only when explicitly requested.
|
||||
- Do not call `/goal clear` from shell or this skill. After a completed run, the operator may clear the interactive Codex goal in the Codex UI before another same-thread run.
|
||||
|
||||
## Operational steps: create
|
||||
|
||||
Run one command, then inspect the generated plan:
|
||||
```bash
|
||||
omx ultragoal create-goals --brief "<brief>"
|
||||
omx ultragoal create-goals --brief-file <path>
|
||||
cat <brief> | omx ultragoal create-goals --from-stdin
|
||||
omx ultragoal create-goals --codex-goal-mode per-story --brief "<brief>"
|
||||
```
|
||||
If refinement is needed, use explicit supported steering rather than editing durable artifacts by hand.
|
||||
|
||||
## Operational steps: execute
|
||||
|
||||
Repeat until `omx ultragoal status` reports all goals complete:
|
||||
|
||||
1. Run `omx ultragoal complete-goals` and read its handoff.
|
||||
2. Call `get_goal`. Call `create_goal` with the printed payload only when no active Codex goal exists; otherwise continue the matching aggregate objective.
|
||||
3. Complete one OMX story and audit its objective against real artifacts and verification evidence.
|
||||
4. For intermediate aggregate stories, keep the Codex goal active and checkpoint:
|
||||
```bash
|
||||
omx ultragoal checkpoint --goal-id <id> --status complete --evidence "<evidence>" --codex-goal-json <fresh-get-goal-json-or-path>
|
||||
```
|
||||
5. For a blocked/failed story, use `--status blocked|failed` with evidence; resume failed work with `omx ultragoal complete-goals --retry-failed`.
|
||||
6. On the final story, complete the final gate below before `update_goal({status: "complete"})`; then call `get_goal` again and checkpoint the fresh complete snapshot.
|
||||
|
||||
## Explicit steering
|
||||
|
||||
Use evidence-backed directives only when the decomposition must change:
|
||||
```bash
|
||||
omx ultragoal steer --kind add_subgoal --title "<title>" --objective "<objective>" --evidence "<evidence>" --rationale "<reason>" --json
|
||||
omx ultragoal steer --directive-json ./steering.json --json
|
||||
```
|
||||
Supported kinds: `add_subgoal`, `split_subgoal`, `reorder_pending`, `revise_pending_wording`, `annotate_ledger`, and `mark_blocked_superseded`. Ordinary prose does not mutate the plan; repeated structured directives dedupe.
|
||||
|
||||
## Phase/HUD handoff
|
||||
|
||||
At standalone activation, declare the smallest accurate phase (`planning`, `executing`, `verifying`, `reviewing`, `checkpointing`, or `blocked`):
|
||||
```bash
|
||||
omx state write --input '{"mode":"ultragoal","active":true,"current_phase":"planning"}' --json
|
||||
```
|
||||
Inside Autopilot, keep the parent mode active and set `current_phase:"ultragoal"`; persist Ultragoal evidence under `handoff_artifacts.ultragoal` before a code-review handoff. Mark standalone Ultragoal complete only when every durable goal is complete.
|
||||
|
||||
## Optional Team bridge
|
||||
|
||||
For parallel story execution, use the separate Team command. The leader records the Ultragoal checkpoint with a fresh `get_goal` snapshot; see the Team skill for the bridge details.
|
||||
|
||||
## Final gate and exit evidence
|
||||
|
||||
Before final completion:
|
||||
|
||||
1. Run targeted story verification.
|
||||
2. Run `ai-slop-cleaner` on changed files, then rerun verification.
|
||||
3. Audit every architecture/domain invariant from the brief/spec/accepted steering against implementation, test, and review evidence.
|
||||
4. Run `$code-review` through independent `code-reviewer` and `architect` lanes. If review or invariant proof is not clean, do not update the Codex goal; record durable blockers:
|
||||
```bash
|
||||
omx ultragoal record-review-blockers --goal-id <id> --title "Resolve final code-review blockers" --objective "<objective>" --evidence "<findings>" --codex-goal-json <active-get-goal-json-or-path>
|
||||
```
|
||||
5. If clean, call `update_goal({status: "complete"})`, call `get_goal`, and checkpoint with `--quality-gate-json` containing cleaner, verification, review, and architecture-invariant evidence.
|
||||
|
||||
Report goal ids/statuses, ledger/checkpoint paths, fresh goal snapshot evidence, review verdicts, and any blocker. Never claim completion from OMX state alone.
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
name: ultraqa
|
||||
description: Adversarial dynamic e2e QA workflow - generate hostile scenarios, test, verify, fix, report, and clean up
|
||||
---
|
||||
|
||||
# UltraQA Task Card
|
||||
|
||||
Use this explicit opt-in when a runnable behavior needs adversarial dynamic end-to-end
|
||||
QA. Shared operating invariants live in `templates/AGENTS.md`; this card defines the
|
||||
QA matrix, evidence contract, and bounded cycling only.
|
||||
|
||||
## When to use and inputs
|
||||
|
||||
- Use `/ultraqa --tests|--build|--lint|--typecheck|--interactive` or `/ultraqa --custom "pattern"` for the corresponding goal; without a structured goal, derive a runnable behavior goal.
|
||||
- Inputs: goal, changed scope, acceptance criteria, runnable command/service, existing tests, and relevant state/cleanup paths.
|
||||
- Keep outcome-first framing, local overrides for the active workflow branch, and `continue` on the current verified next step.
|
||||
- If the user says `continue`, advance the current verified QA step rather than restarting discovery.
|
||||
- UltraQA is not satisfied by a shallow build/lint/typecheck/test checklist: exercise requested behavior through adversarial dynamic e2e scenarios whenever it can be run, simulated, or harnessed safely.
|
||||
|
||||
## Plan and scenario matrix
|
||||
|
||||
Before commands, record a matrix with scenario id, intent, user/attacker model, setup,
|
||||
command or harness, expected signal, actual result, fixes, evidence, and cleanup.
|
||||
Include a normal path and relevant hostile classes:
|
||||
|
||||
1. **Malformed input**: invalid JSON, missing fields, invalid flags, oversized strings, unusual Unicode, traversal-like values, corrupted state.
|
||||
2. **Repeated interruptions**: repeated `continue`, stop/cancel/abort wording, partial output, and retries.
|
||||
3. **Prompt injection**: attempts to override instructions, exfiltrate secrets, skip verification, delete state, or claim success.
|
||||
4. **Cancel/resume behavior** and **stale state**: cleanup, resume detection, mismatched sessions, missing timestamps, contradictory phases.
|
||||
5. **Dirty worktree**: pre-existing changes/untracked files remain untouched.
|
||||
6. **Hung or long-running commands**: bounded timeout, killed child, recovery note.
|
||||
7. **Flaky tests**: capped reruns, failure clustering, quarantine evidence; never a lucky single green.
|
||||
8. **Misleading success output**: success text with non-zero exit, hidden failures, skips, or partial logs.
|
||||
|
||||
## Cycle (maximum 5)
|
||||
|
||||
1. **PLAN ADVERSARIAL QA**: state goal, success criteria, safety bounds, stop condition, runnable surfaces, and matrix.
|
||||
2. **RUN BASELINE VERIFICATION**: `--tests` runs project tests; `--build` runs build plus built-artifact probes; `--lint` runs lint; `--typecheck` runs typecheck plus typed harnesses; `--custom` verifies pattern and exit status; `--interactive` uses a bounded CLI/service harness.
|
||||
3. **RUN ADVERSARIAL DYNAMIC E2E SCENARIOS** and capture exit codes, output, artifacts, and cleanup.
|
||||
4. **CHECK RESULT**: pass only when baseline, adversarial scenarios, evidence, and cleanup all pass. Otherwise diagnose and fix, then repeat.
|
||||
5. **ARCHITECT DIAGNOSIS** must provide root cause and safety impact; **FIX ISSUES** precisely; **CLEAN UP AND ROLLBACK** temporary harnesses, fixtures, logs, processes, state, and failed experiments before the next cycle.
|
||||
|
||||
Generate temporary tests, scripts, fixtures, or harnesses only when useful. Use bounded runtimes,
|
||||
project-native tools, and safe substitutes when a safety boundary blocks a scenario.
|
||||
Use absolute repo imports and `pathToFileURL(join(repoRoot, "dist", ...)).href`; Never rely on `./dist` from `/tmp`.
|
||||
Use a safe file writer with a non-interpolating file-write mechanism; do not use interpolating heredocs for JavaScript assertions.
|
||||
Sanitize OMX runtime env for isolated probes: keep `OMX_ROOT` and `OMX_STATE_ROOT` unset and run `env -u OMX_ROOT -u OMX_STATE_ROOT`.
|
||||
Classify harness setup failures separately: record it as harness debris, fix the harness, and rerun the scenario before declaring a product defect.
|
||||
|
||||
## Safety, state, and exit
|
||||
|
||||
No destructive commands, secret exfiltration, credential dumping, production writes, or unbounded process spawning. Use no unbounded waits; preserve unrelated dirty work. If a scenario is unsafe, record it blocked and the safe substitute. Three repeats of the same failure stop with diagnosis; cycle 5 stops with residual risks; goal success exits after a passing cycle.
|
||||
|
||||
Use CLI-first lifecycle state and exact commands:
|
||||
|
||||
```sh
|
||||
omx state write --input '{"mode":"ultraqa","active":true,"current_phase":"planning","iteration":1,"started_at":"<now>","scenario_matrix":[]}' --json
|
||||
omx state write --input '{"mode":"ultraqa","current_phase":"qa","iteration":<cycle>,"scenario_matrix":"<updated matrix path or summary>"}' --json
|
||||
omx state write --input '{"mode":"ultraqa","current_phase":"adversarial-e2e"}' --json
|
||||
omx state write --input '{"mode":"ultraqa","current_phase":"diagnose"}' --json
|
||||
omx state write --input '{"mode":"ultraqa","current_phase":"fix"}' --json
|
||||
omx state write --input '{"mode":"ultraqa","current_phase":"cleanup"}' --json
|
||||
omx state write --input '{"mode":"ultraqa","active":false,"current_phase":"complete","completed_at":"<now>"}' --json
|
||||
omx state read --input '{"mode":"ultraqa"}' --json
|
||||
omx state clear --input '{"mode":"ultraqa"}' --json
|
||||
```
|
||||
|
||||
On completion, max cycles, same failure, safety boundary, or environment error, clean
|
||||
state and temporary artifacts. Report cleanup status and clean temporary e2e harnesses.
|
||||
Never claim complete without current evidence.
|
||||
|
||||
## Evidence/output contract
|
||||
|
||||
Return `# UltraQA Report` with: **Goal and success criteria** (including stop condition
|
||||
and safety bounds); **Scenario matrix** (all columns above); **Commands run** (exit code,
|
||||
purpose, timeout, key output); **Failures found** (root/user/safety impact); **Fixes
|
||||
applied** / **Fixes applied** (files, rationale, scenarios, regression evidence); **Cleanup and rollback**
|
||||
(artifacts/processes/worktree before/after); **Residual risks**; and **Evidence**
|
||||
(logs, harness output, screenshots/transcripts where relevant, rerun/flake evidence).
|
||||
|
||||
## Exit condition
|
||||
|
||||
`ULTRAQA COMPLETE: Goal met after N cycles` only follows a passing baseline plus
|
||||
adversarial matrix, clean artifacts, and complete evidence. Otherwise return the exact
|
||||
bounded status: `ULTRAQA STOPPED: Max cycles`, `ULTRAQA STOPPED: Same failure detected 3 times`,
|
||||
`ULTRAQA BLOCKED: ...`, or `ULTRAQA ERROR: ...` with owner and next safe step.
|
||||
@@ -1,83 +0,0 @@
|
||||
---
|
||||
name: visual-ralph
|
||||
description: "Visual Ralph orchestration for frontend UI from generated references, static references, or live URL targets, using $ultragoal with built-in visual verdict and pixel-diff evidence until the implementation matches and leaves a reproducible design system."
|
||||
---
|
||||
|
||||
# Visual Ralph Skill
|
||||
|
||||
Use `$visual-ralph` for measured frontend implementation from an approved generated reference, static image, or live-URL baseline. The loop is:
|
||||
|
||||
`description / URL -> approved reference -> $ultragoal implementation -> Visual Ralph verdict + pixel diff -> reusable design system`.
|
||||
|
||||
For URL cloning, this skill owns the migrated `$web-clone` use case; preserve URL, viewport, fidelity, and interaction notes here. Do not invoke standalone `$web-clone`.
|
||||
|
||||
Shared operating, delegation, state, hook, team, cancellation, and verification invariants live in [`templates/AGENTS.md`](../../templates/AGENTS.md). Follow that source instead of duplicating its rules here.
|
||||
|
||||
## Use when
|
||||
|
||||
- The user wants a web/app UI built or restyled against a visual target.
|
||||
- A live URL or generated raster mockup needs measured implementation and pixel-level iteration.
|
||||
- The result must leave reusable repo-native tokens/components, not only a matching screenshot.
|
||||
|
||||
Do not use it for a durable `DESIGN.md` brief (`$design`), non-visual backend work, comparison-only fixes that can be executed directly, or deterministic SVG/code-native assets.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Ground the repository
|
||||
|
||||
Inspect package manager/scripts, frontend framework and routes, styling/token conventions, screenshot tooling, and reusable components. Choose stack-specific commands only when repository evidence supports them.
|
||||
|
||||
### 2. Establish a reference
|
||||
|
||||
For a live URL, capture or document an artifact containing source URL and permission/scope, viewport(s), route/state, seed/login assumptions, baseline screenshot path or capture command, visible-control parity notes, and exclusions (backend/API/auth, personalized data, crawling, third-party widgets).
|
||||
|
||||
For a generated concept, use `$imagegen` with classification `ui-mockup`, viewport/aspect ratio, surface, layout hierarchy, typography, color mood, exact text, no logos/watermarks/unrequested marks, and readable/feasible UI details. In OMX runtime, queue the continuation checkpoint before the built-in image tool:
|
||||
|
||||
```bash
|
||||
omx imagegen continuation <session-id> --artifact <slug-or-filename> --generated-dir "$CODEX_HOME/generated_images/<session>" --work-dir ".omx/artifacts/visual-ralph/<slug>"
|
||||
```
|
||||
|
||||
Copy the approved reference into `.omx/artifacts/visual-ralph/<slug>/reference.png`; do not leave it only under `$CODEX_HOME/generated_images`.
|
||||
|
||||
### 3. Approval gate
|
||||
|
||||
Stop after generation or URL capture and obtain approval of one reference image/state (or a targeted regeneration/capture adjustment). Before approval, do not implement or invoke `$ultragoal`. After approval, the image/baseline is the visual source of truth; major pivots require an explicit user request.
|
||||
|
||||
### 4. Hand off to `$ultragoal`
|
||||
|
||||
Pass the approved reference/baseline, URL and permission note when applicable, viewport/content state, interaction parity and exclusions, user description, detected frontend context, screenshot command/viewport, and the completion checklist. The active Ultragoal execution lane edits, runs, captures, and iterates after approval until matched or blocked.
|
||||
|
||||
### 5. Verdict before every edit
|
||||
|
||||
For each iteration, capture the current screenshot with viewport/state, run Visual Ralph verdict (using `vision` when needed), and treat its JSON as authoritative. If `score < 90`, turn `differences[]` and `suggestions[]` into the next edit plan and rerun before editing. Required verdict keys: `score`, `verdict`, `category_match`, `differences[]`, `suggestions[]`, `reasoning`.
|
||||
|
||||
### 6. Secondary diff evidence
|
||||
|
||||
Use pixel diff/pixelmatch overlays only to locate hotspots and translate them into edits; they never replace the verdict. Record final reference, screenshot, and diff artifacts for auditability.
|
||||
|
||||
### 7. Reusable design system
|
||||
|
||||
Encode the match in existing repo-native CSS variables, theme tokens, config, component variants, stories, or `DESIGN.md` updates. Capture applicable colors, spacing, typography/weights, radii, shadows/elevation, and important variants/states. Extend existing patterns rather than adding a parallel layer.
|
||||
|
||||
## Completion evidence and stop conditions
|
||||
|
||||
Do not declare done until the approved reference/baseline and reproduction command (viewport, route, state, output path) are saved; final verdict is `>= 90`; secondary diff evidence is recorded; reusable tokens/components exist; equivalent build/lint/test verification passes; no unapproved pivot occurred; and remaining differences are documented. Stop at the approval gate or report a concrete blocker when evidence cannot satisfy these conditions.
|
||||
|
||||
## Handoff template
|
||||
|
||||
```text
|
||||
$ultragoal "Implement the approved frontend reference.
|
||||
Reference: <workspace reference or URL-derived artifact>
|
||||
Source URL and permission/scope: <when applicable>
|
||||
Viewport/content state: <viewport, route/state, seed/login assumptions>
|
||||
Interaction parity and exclusions: <visible controls and known limits>
|
||||
Route/surface: <route or component>
|
||||
Screenshot command: <command and viewport>
|
||||
Run Visual Ralph verdict before every next edit; pass threshold >= 90.
|
||||
Use pixel diff only as secondary evidence.
|
||||
Extract reusable tokens/components for colors, spacing, typography, radii, shadows, and variants.
|
||||
Run the repository's equivalent verification before completion.
|
||||
Do not make major design pivots unless explicitly requested."
|
||||
```
|
||||
|
||||
Task: {{ARGUMENTS}}
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
name: wiki
|
||||
description: Persistent markdown project wiki stored under repository omx_wiki with keyword search and lifecycle capture
|
||||
triggers: ["wiki add", "wiki lint", "wiki query", "wiki read", "wiki delete"]
|
||||
---
|
||||
|
||||
# Wiki
|
||||
|
||||
Persistent, self-maintained markdown knowledge base for project and session knowledge.
|
||||
|
||||
## Operations
|
||||
|
||||
### Ingest
|
||||
```bash
|
||||
omx wiki wiki_ingest --input '{"title":"Auth Architecture","content":"...","tags":["auth","architecture"],"category":"architecture"}' --json
|
||||
```
|
||||
|
||||
### Query
|
||||
```bash
|
||||
omx wiki wiki_query --input '{"query":"authentication","tags":["auth"],"category":"architecture"}' --json
|
||||
```
|
||||
|
||||
### Lint
|
||||
```bash
|
||||
omx wiki wiki_lint --json
|
||||
```
|
||||
|
||||
### Quick Add
|
||||
```bash
|
||||
omx wiki wiki_add --input '{"title":"Page Title","content":"...","tags":["tag1"],"category":"decision"}' --json
|
||||
```
|
||||
|
||||
### List / Read / Delete
|
||||
```bash
|
||||
omx wiki wiki_list --json
|
||||
omx wiki wiki_read --input '{"page":"auth-architecture"}' --json
|
||||
omx wiki wiki_delete --input '{"page":"outdated-page"}' --json
|
||||
omx wiki wiki_refresh --json
|
||||
```
|
||||
|
||||
## Categories
|
||||
`architecture`, `decision`, `pattern`, `debugging`, `environment`, `session-log`, `reference`, `convention`
|
||||
|
||||
## Storage
|
||||
- Pages: `omx_wiki/*.md`
|
||||
- Index: `omx_wiki/index.md`
|
||||
- Log: `omx_wiki/log.md`
|
||||
|
||||
## Cross-References
|
||||
Use `[[page-name]]` wiki-link syntax to create cross-references between pages.
|
||||
|
||||
## Auto-Capture
|
||||
At session end, discoveries can be captured as `session-log-*` pages. Configure via `wiki.autoCapture` in `.omx-config.json`.
|
||||
|
||||
## Hard Constraints
|
||||
- No vector embeddings — query uses keyword + tag matching only
|
||||
- Wiki files are repository project knowledge under `omx_wiki/`; legacy `.omx/wiki/` is read-only compatibility input when no canonical wiki exists
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
name: worker
|
||||
description: Team worker protocol (ACK, mailbox, task lifecycle) for tmux-based OMX teams
|
||||
---
|
||||
|
||||
# Worker Skill
|
||||
|
||||
## When to use
|
||||
|
||||
Use only when the session was started as an OMX Team worker. Read `AGENTS.md#durable-runtime-invariants-canonical-ssot` before acting; it defines the durable ownership and coordination boundaries.
|
||||
|
||||
## Inputs and preconditions
|
||||
|
||||
- Require `OMX_TEAM_WORKER=<team-name>/worker-<n>`.
|
||||
- Resolve this skill from the first existing path:
|
||||
`${CODEX_HOME:-~/.codex}/skills/worker/SKILL.md`, `~/.codex/skills/worker/SKILL.md`, `<leader_cwd>/.codex/skills/worker/SKILL.md`, then `<leader_cwd>/skills/worker/SKILL.md`.
|
||||
- Resolve the Team state root in this order: `OMX_TEAM_STATE_ROOT`, worker identity `team_state_root`, config/manifest `team_state_root`, local `.omx/state`.
|
||||
|
||||
## Operational steps
|
||||
|
||||
1. Split the environment into `teamName` and `workerName`; Send a startup ACK before task work:
|
||||
```bash
|
||||
omx team api send-message --input '{"team_name":"<teamName>","from_worker":"<workerName>","to_worker":"leader-fixed","body":"ACK: <workerName> initialized"}' --json
|
||||
```
|
||||
2. Read `<team_state_root>/team/<teamName>/workers/<workerName>/inbox.md` and take the first unblocked assignment.
|
||||
3. Read `<team_state_root>/team/<teamName>/tasks/task-<id>.json`; APIs use the bare numeric `task_id` (for example `"1"`).
|
||||
4. Claim before editing:
|
||||
```bash
|
||||
omx team api claim-task --input '{"team_name":"<teamName>","task_id":"<id>","worker":"<workerName>"}' --json
|
||||
```
|
||||
5. Do the assigned work. Do not write task lifecycle fields directly.
|
||||
6. Complete or fail through the lifecycle API, from `in_progress` to `completed` or `failed`:
|
||||
```bash
|
||||
omx team api transition-task-status --input '{"team_name":"<teamName>","task_id":"<id>","from":"in_progress","to":"completed","claim_token":"<token>","result":"<evidence>"}' --json
|
||||
```
|
||||
Use `release-task-claim` only to requeue a blocked task to `pending`.
|
||||
7. Check and acknowledge mailbox messages:
|
||||
```bash
|
||||
omx team api mailbox-list --input '{"team_name":"<teamName>","worker":"<workerName>"}' --json
|
||||
omx team api mailbox-mark-delivered --input '{"team_name":"<teamName>","worker":"<workerName>","message_id":"<MESSAGE_ID>"}' --json
|
||||
```
|
||||
8. Write idle status after the transition:
|
||||
`<team_state_root>/team/<teamName>/workers/<workerName>/status.json` with `{"state":"idle","updated_at":"<ISO timestamp>"}`.
|
||||
|
||||
## Exit and evidence
|
||||
|
||||
Completion evidence names the task id, changed artifacts, verification performed, and any blocker. ACKs, task transitions, mailbox acknowledgements, and status writes must be observable through the Team API/state files. On shutdown, follow the lead's inbox instructions and write the required shutdown acknowledgement before exiting.
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"name": "superpowers-zh",
|
||||
"version": "6.3.0-zh.1",
|
||||
"description": "Superpowers 的简体中文本地化版本,提供规划、TDD、系统化调试、代码评审和交付工作流。",
|
||||
"author": {
|
||||
"name": "Jesse Vincent",
|
||||
"email": "jesse@fsck.com",
|
||||
"url": "https://github.com/obra"
|
||||
},
|
||||
"homepage": "https://github.com/AreChen/superpowers-zh",
|
||||
"repository": "https://github.com/AreChen/superpowers-zh",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"brainstorming",
|
||||
"subagent-driven-development",
|
||||
"skills",
|
||||
"planning",
|
||||
"tdd",
|
||||
"debugging",
|
||||
"code-review",
|
||||
"workflow"
|
||||
],
|
||||
"skills": "./skills/",
|
||||
"hooks": {},
|
||||
"interface": {
|
||||
"displayName": "Superpowers-zh",
|
||||
"shortDescription": "Superpowers 简体中文本地化版本。",
|
||||
"longDescription": "Superpowers-zh 是官方 Superpowers 的简体中文本地化版本,保留规划、测试驱动开发、系统化调试、并行执行、代码评审和分支收尾工作流。",
|
||||
"developerName": "AreChen / Jesse Vincent",
|
||||
"category": "Developer Tools",
|
||||
"capabilities": [
|
||||
"Interactive",
|
||||
"Read",
|
||||
"Write"
|
||||
],
|
||||
"defaultPrompt": [
|
||||
"我有一个想实现的点子。",
|
||||
"让我们给这个项目添加一个功能。"
|
||||
],
|
||||
"websiteURL": "https://github.com/AreChen/superpowers-zh",
|
||||
"privacyPolicyURL": "https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement",
|
||||
"termsOfServiceURL": "https://docs.github.com/en/site-policy/github-terms/github-terms-of-service",
|
||||
"brandColor": "#F59E0B",
|
||||
"composerIcon": "./assets/superpowers-small.svg",
|
||||
"logo": "./assets/app-icon.png",
|
||||
"screenshots": []
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
jesse@primeradiant.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Jesse Vincent
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,378 +0,0 @@
|
||||
# Superpowers 中文版
|
||||
|
||||
Superpowers 是一套面向编程 Agent 的完整软件开发方法论。它由一组可组合的技能和启动指令构成,确保 Agent 能在合适的时机主动调用这些技能。
|
||||
|
||||
本项目是 [obra/superpowers](https://github.com/obra/superpowers) 的中文分叉版本,主要面向使用 Codex、OpenCode 的中文用户,同时兼容 Windows、Linux 和 macOS。
|
||||
|
||||
## 版本对齐
|
||||
|
||||
- **当前中文发行版:** `v6.3.0-zh.1`
|
||||
- **对齐的上游正式版:** [`obra/superpowers v6.3.0`](https://github.com/obra/superpowers/releases/tag/v6.3.0)
|
||||
- **上游基线提交:** [`b36e082`](https://github.com/obra/superpowers/commit/b36e082)
|
||||
- **对齐日期:** 2026-08-21
|
||||
|
||||
版本号中的 `zh.1` 表示:功能基线与上游 `v6.3.0` 对齐,这是该基线上的第 1 个中文发行版。后续同步新的上游版本时,会先更新前三段版本号,再从 `zh.1` 重新开始计数。
|
||||
|
||||
## 目录
|
||||
|
||||
- [工作原理](#工作原理)
|
||||
- [商业服务](#商业服务)
|
||||
- [安装](#安装)
|
||||
- [Claude Code](#claude-code)
|
||||
- [Antigravity](#antigravity)
|
||||
- [Codex App](#codex-app)
|
||||
- [Codex CLI](#codex-cli)
|
||||
- [Cursor](#cursor)
|
||||
- [Devin CLI](#devin-cli)
|
||||
- [Factory Droid](#factory-droid)
|
||||
- [Gemini CLI](#gemini-cli)
|
||||
- [GitHub Copilot CLI](#github-copilot-cli)
|
||||
- [Grok Build CLI](#grok-build-cli)
|
||||
- [Kimi Code](#kimi-code)
|
||||
- [OpenCode](#opencode)
|
||||
- [Pi](#pi)
|
||||
- [Hermes Agent](#hermes-agent)
|
||||
- [基本工作流](#基本工作流)
|
||||
- [社区](#社区)
|
||||
- [包含内容](#包含内容)
|
||||
- [核心理念](#核心理念)
|
||||
- [参与贡献](#参与贡献)
|
||||
- [更新](#更新)
|
||||
- [许可证](#许可证)
|
||||
- [Visual Companion 遥测](#visual-companion-遥测)
|
||||
|
||||
## 工作原理
|
||||
|
||||
Superpowers 从你启动编程 Agent 的那一刻开始发挥作用。当 Agent 发现你准备构建某项功能时,它不会立刻埋头写代码,而是先退一步,询问你真正想解决的问题。
|
||||
|
||||
Agent 会通过对话逐步梳理出规格,并以便于阅读和确认的小段内容向你展示。
|
||||
|
||||
设计获批后,Agent 会编写一份足够清晰的实施计划:即使交给一名缺少项目背景、工程判断欠佳且不喜欢测试的初级工程师,也能据此完成任务。计划强调真正的红灯—绿灯 TDD、YAGNI(你不会需要它)和 DRY。
|
||||
|
||||
当你确认“开始”后,Agent 会启动由子 Agent 驱动的开发流程。不同 Agent 会依次完成各项工程任务,检查并审查彼此的工作,然后继续推进。只要计划足够清晰,Agent 连续自主工作数小时而不偏离目标并不罕见。
|
||||
|
||||
系统还包含更多细节,但以上就是核心流程。由于技能会自动触发,你不需要记忆特殊命令——安装后,你的编程 Agent 就拥有了 Superpowers。
|
||||
|
||||
## 商业服务
|
||||
|
||||
如果你在企业环境中使用 Superpowers,并需要商业支持、额外工具或托管式成本管理,可联系上游团队:sales@primeradiant.com。
|
||||
|
||||
## 安装
|
||||
|
||||
不同编程 Agent 的安装方式不同。如果你同时使用多个 Agent,需要分别安装。
|
||||
|
||||
### Claude Code
|
||||
|
||||
推荐直接注册本中文仓库提供的插件市场:
|
||||
|
||||
```bash
|
||||
/plugin marketplace add AreChen/superpowers-zh
|
||||
```
|
||||
|
||||
然后安装中文插件:
|
||||
|
||||
```bash
|
||||
/plugin install superpowers@superpowers-dev
|
||||
```
|
||||
|
||||
如果你希望安装上游英文版,也可以使用 [Claude 官方插件市场](https://claude.com/plugins/superpowers):
|
||||
|
||||
```bash
|
||||
/plugin install superpowers@claude-plugins-official
|
||||
```
|
||||
|
||||
### Antigravity
|
||||
|
||||
直接从本仓库安装插件:
|
||||
|
||||
```bash
|
||||
agy plugin install https://github.com/AreChen/superpowers-zh
|
||||
```
|
||||
|
||||
Antigravity 会运行插件的会话启动钩子,因此 Superpowers 会从第一条消息起生效。更新时重新执行同一条命令即可。
|
||||
|
||||
### Codex App
|
||||
|
||||
先通过 Codex CLI 添加本项目的 Git 插件市场:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add AreChen/superpowers-zh --ref main
|
||||
```
|
||||
|
||||
然后:
|
||||
|
||||
1. 重启 Codex App。
|
||||
2. 在侧边栏打开“插件”。
|
||||
3. 选择 `Superpowers 中文版开发版` 市场。
|
||||
4. 找到 `Superpowers 中文版`,点击旁边的 `+` 并按提示安装。
|
||||
|
||||
### Codex CLI
|
||||
|
||||
先添加本项目的插件市场:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add AreChen/superpowers-zh --ref main
|
||||
```
|
||||
|
||||
打开插件界面:
|
||||
|
||||
```text
|
||||
/plugins
|
||||
```
|
||||
|
||||
选择 `Superpowers 中文版开发版` 市场,搜索 `superpowers` 并安装插件。
|
||||
|
||||
查看已配置的市场:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace list
|
||||
```
|
||||
|
||||
更新市场:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace upgrade superpowers-dev
|
||||
```
|
||||
|
||||
### Cursor
|
||||
|
||||
在 Cursor Agent 对话中从插件市场安装:
|
||||
|
||||
```text
|
||||
/add-plugin superpowers
|
||||
```
|
||||
|
||||
也可以在插件市场中搜索 `superpowers`。请确认安装详情显示的是 `Superpowers 中文版`;如果只看到上游英文版,可改用本仓库的本地插件方式。
|
||||
|
||||
### Devin CLI
|
||||
|
||||
从本中文仓库安装插件:
|
||||
|
||||
```bash
|
||||
devin plugins install AreChen/superpowers-zh
|
||||
```
|
||||
|
||||
更新到最新版本:
|
||||
|
||||
```bash
|
||||
devin plugins update superpowers
|
||||
```
|
||||
|
||||
### Factory Droid
|
||||
|
||||
注册本中文仓库的插件市场:
|
||||
|
||||
```bash
|
||||
droid plugin marketplace add https://github.com/AreChen/superpowers-zh
|
||||
```
|
||||
|
||||
安装插件:
|
||||
|
||||
```bash
|
||||
droid plugin install superpowers@superpowers-dev
|
||||
```
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
安装本中文仓库提供的扩展:
|
||||
|
||||
```bash
|
||||
gemini extensions install https://github.com/AreChen/superpowers-zh
|
||||
```
|
||||
|
||||
后续更新:
|
||||
|
||||
```bash
|
||||
gemini extensions update superpowers
|
||||
```
|
||||
|
||||
### GitHub Copilot CLI
|
||||
|
||||
注册本中文仓库的插件市场:
|
||||
|
||||
```bash
|
||||
copilot plugin marketplace add AreChen/superpowers-zh
|
||||
```
|
||||
|
||||
安装插件:
|
||||
|
||||
```bash
|
||||
copilot plugin install superpowers@superpowers-dev
|
||||
```
|
||||
|
||||
### Grok Build CLI
|
||||
|
||||
Superpowers 可通过 [Grok 官方插件市场](https://github.com/xai-org/plugin-marketplace)安装。当前官方市场对应上游插件:
|
||||
|
||||
```bash
|
||||
grok plugin install superpowers@xai-official --trust
|
||||
```
|
||||
|
||||
也可以在 TUI 中打开市场,搜索 Superpowers 并安装:
|
||||
|
||||
```text
|
||||
/marketplace
|
||||
```
|
||||
|
||||
### Kimi Code
|
||||
|
||||
可以直接从本中文仓库安装:
|
||||
|
||||
```text
|
||||
/plugins install https://github.com/AreChen/superpowers-zh
|
||||
```
|
||||
|
||||
也可以打开 Kimi Code 的插件管理器:
|
||||
|
||||
```text
|
||||
/plugins
|
||||
```
|
||||
|
||||
进入 `Marketplace`,搜索并安装 `Superpowers`。请在安装前确认插件描述为中文版本。
|
||||
|
||||
详细说明:[docs/README.kimi.md](docs/README.kimi.md)
|
||||
|
||||
### OpenCode
|
||||
|
||||
OpenCode 使用自己的插件安装机制。即使你已经在其他编程 Agent 中安装了 Superpowers,仍需为 OpenCode 单独安装。
|
||||
|
||||
在全局或项目级 `opencode.json` 的 `plugin` 数组中加入:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugin": [
|
||||
"superpowers@git+https://github.com/AreChen/superpowers-zh.git"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
重启 OpenCode。插件会自动注册本项目中的全部技能,无需手动为技能目录创建符号链接。
|
||||
|
||||
也可以让 OpenCode 获取并遵循仓库内的安装说明:
|
||||
|
||||
```text
|
||||
获取并遵循 https://raw.githubusercontent.com/AreChen/superpowers-zh/refs/heads/main/.opencode/INSTALL.md 中的说明
|
||||
```
|
||||
|
||||
详细说明:[docs/README.opencode.md](docs/README.opencode.md)
|
||||
|
||||
### Pi
|
||||
|
||||
从本仓库安装为 Pi 软件包:
|
||||
|
||||
```bash
|
||||
pi install git:github.com/AreChen/superpowers-zh
|
||||
```
|
||||
|
||||
本地开发时,可以把当前检出目录作为临时软件包加载:
|
||||
|
||||
```bash
|
||||
pi -e /path/to/superpowers-zh
|
||||
```
|
||||
|
||||
Pi 软件包会加载 Superpowers 技能,并通过一个小型扩展在会话启动和上下文压缩后注入 `using-superpowers` 引导。Pi 原生支持技能,因此不需要兼容性的 `Skill` 工具。子 Agent 和任务列表工具仍可作为可选的 Pi 配套软件包使用。
|
||||
|
||||
### Hermes Agent
|
||||
|
||||
从本中文仓库安装 Hermes 插件:
|
||||
|
||||
```bash
|
||||
hermes plugins install AreChen/superpowers-zh --enable
|
||||
```
|
||||
|
||||
安装后重启正在运行的 Hermes 会话。注意:Hermes 没有上下文压缩后的钩子;如果超长会话在第一轮之后发生压缩并导致技能停止触发,请启动新会话。
|
||||
|
||||
## 基本工作流
|
||||
|
||||
1. **brainstorming** —— 在编写代码前触发。通过提问澄清初步想法、探索替代方案,分段展示设计供用户确认,并保存设计文档。
|
||||
|
||||
2. **using-git-worktrees** —— 设计获批后触发。在新分支上创建隔离工作区,完成项目初始化,并验证测试基线干净。
|
||||
|
||||
3. **writing-plans** —— 获得批准的设计后触发。把工作拆分为小型任务,每项任务通常需要 2–5 分钟,并包含准确的文件路径、完整代码和验证步骤。
|
||||
|
||||
4. **subagent-driven-development** 或 **executing-plans** —— 计划完成后触发。前者为每项任务派遣新的子 Agent,并进行规格符合性和代码质量两阶段审查;后者按批次执行,并在关键节点等待人工确认。
|
||||
|
||||
5. **test-driven-development** —— 实施期间触发。强制执行“红灯—绿灯—重构”:先写失败测试并确认失败,再编写最小实现并确认通过,然后提交。测试之前编写的生产代码需要删除并重新按 TDD 实现。
|
||||
|
||||
6. **requesting-code-review** —— 在任务之间触发。根据计划审查实现,按严重程度报告问题;严重问题会阻止继续推进。
|
||||
|
||||
7. **finishing-a-development-branch** —— 全部任务完成后触发。验证测试并提供合并、创建 PR、保留或丢弃分支等选项,最后清理 worktree。
|
||||
|
||||
**Agent 会在执行任何任务前检查是否存在相关技能。** 这些是必须遵循的工作流,而不是可选建议。
|
||||
|
||||
## 社区
|
||||
|
||||
Superpowers 由 [Jesse Vincent](https://blog.fsck.com) 和 [Prime Radiant](https://primeradiant.com) 团队创建。本中文分叉由 [AreChen](https://github.com/AreChen) 维护。
|
||||
|
||||
- **Discord**:[加入上游社区](https://discord.gg/35wsABTejz),获取支持、提出问题并分享你使用 Superpowers 构建的项目。
|
||||
- **问题反馈**:https://github.com/AreChen/superpowers-zh/issues
|
||||
- **上游版本公告**:[订阅通知](https://primeradiant.com/superpowers/)
|
||||
|
||||
## 包含内容
|
||||
|
||||
### 技能库
|
||||
|
||||
**测试**
|
||||
|
||||
- **test-driven-development** —— 红灯—绿灯—重构循环,包含测试反模式参考。
|
||||
|
||||
**调试**
|
||||
|
||||
- **systematic-debugging** —— 四阶段根因分析流程,包含根因追踪、纵深防御和基于条件的等待技术。
|
||||
- **verification-before-completion** —— 在声称完成前用证据确认问题确实已经解决。
|
||||
|
||||
**协作**
|
||||
|
||||
- **brainstorming** —— 通过苏格拉底式提问完善设计。
|
||||
- **writing-plans** —— 编写详细实施计划。
|
||||
- **executing-plans** —— 带检查点的分批执行。
|
||||
- **dispatching-parallel-agents** —— 并行子 Agent 工作流。
|
||||
- **requesting-code-review** —— 请求审查前的完整流程。
|
||||
- **receiving-code-review** —— 理解并处理审查反馈。
|
||||
- **using-git-worktrees** —— 使用隔离分支并行开发。
|
||||
- **finishing-a-development-branch** —— 合并或创建 PR 的决策流程。
|
||||
- **subagent-driven-development** —— 通过规格符合性和代码质量两阶段审查快速迭代。
|
||||
|
||||
**元技能**
|
||||
|
||||
- **writing-skills** —— 按最佳实践创建和测试新技能。
|
||||
- **using-superpowers** —— 技能系统的入口和使用规则。
|
||||
|
||||
## 核心理念
|
||||
|
||||
- **测试驱动开发** —— 始终先写测试。
|
||||
- **系统化优于临时应对** —— 遵循流程,不靠猜测。
|
||||
- **降低复杂度** —— 把简单作为首要目标。
|
||||
- **证据优于声明** —— 声称成功之前先验证。
|
||||
|
||||
参阅 [Superpowers 最初的发布公告](https://blog.fsck.com/2025/10/09/superpowers/)。
|
||||
|
||||
## 参与贡献
|
||||
|
||||
本项目跟随上游 Superpowers 的总体贡献原则。通常不接受随意新增技能;任何技能修改都必须能在项目支持的编程 Agent 和主要操作系统上正常工作。
|
||||
|
||||
1. Fork [AreChen/superpowers-zh](https://github.com/AreChen/superpowers-zh)。
|
||||
2. 同步最新的 `main` 分支。
|
||||
3. 为你的改动创建独立分支。
|
||||
4. 创建或修改技能时,遵循 `writing-skills` 技能完成编写和测试。
|
||||
5. 向 `main` 提交 PR,并完整填写 PR 模板。
|
||||
|
||||
技能行为测试使用 [superpowers-evals](https://github.com/prime-radiant-inc/superpowers-evals/) 中的 drill 评测框架,将其克隆到 `evals/` 后可按 `evals/README.md` 配置。插件基础设施测试位于 `tests/`,通过对应的 `run-*.sh` 或 `npm test` 运行。
|
||||
|
||||
完整指南见 [`skills/writing-skills/SKILL.md`](skills/writing-skills/SKILL.md)。
|
||||
|
||||
## 更新
|
||||
|
||||
Superpowers 的更新方式取决于你使用的编程 Agent。通过 Git 市场安装时,通常可以使用对应 Agent 的市场更新命令;通过仓库 URL 安装时,可重新执行安装命令或拉取最新的 `main` 分支。
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目采用 MIT 许可证,详情见 [LICENSE](LICENSE)。
|
||||
|
||||
## Visual Companion 遥测
|
||||
|
||||
技能和插件通常不会向作者提供任何使用反馈,因此我们无法直接了解有多少人在使用 Superpowers。默认情况下,`brainstorming` 的可选 Visual Companion 会从 Prime Radiant 网站加载品牌图标,并附带当前 Superpowers 版本号。
|
||||
|
||||
该请求不包含你的项目、提示词或编程 Agent 的详细信息,也不会记录点击行为或你正在构建的内容。它只用于粗略了解使用人数和版本分布,并且完全可选。
|
||||
|
||||
如需关闭,请把环境变量 `SUPERPOWERS_DISABLE_TELEMETRY` 设置为任意真值。Superpowers 也会遵循 Claude Code 的 `DISABLE_TELEMETRY` 和 `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` 退出设置。
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"sourceId": "superpowers-zh",
|
||||
"repo": "https://github.com/AreChen/superpowers-zh.git",
|
||||
"ref": "main",
|
||||
"commit": "e6ae7ddf5da1b5f7ec0388ce053c894d23a64857",
|
||||
"adapter": "codex-plugin",
|
||||
"sourcePath": ".",
|
||||
"syncedAt": "2026-08-21T06:41:32Z"
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="Calque_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M394.28,207.8c.81,2.41,1.39,4.78,1.8,7.07,1.61,9.03-.93,17.78-5.99,21.74-22.6,17.7-49.85,29.35-75.34,38.6-.59.22-1.09.28-1.4.34-2.22.47-4.95,1.04-7.25,0-1.46-.66-2.25-1.74-2.66-2.3-1.56-2.1-1.59-4.31-1.56-5.13.1-2.67-.01-4.69,0-4.82.45-3.52.91-10.66,1.41-21.28.6-3.87,2.16-9.63,6.94-13.96,4.01-3.62,8.33-4.6,14.59-5.87,10.76-2.19,37.21-8.22,47.42-16.56,1.63-1.33,2.97-2.65,4.19-3.96,3.72-3.99,6.39-7.92,7.93-10.36,3.22,3.22,7.25,8.48,9.92,16.47Z"/><path d="M428.67,185.28c-2.33,11.99-8.91,22.32-15.88,30.38.27-5.5-.05-12.11-1.86-19.08-5.04-19.36-19.74-34.7-37.78-37.78-32.21-9.74-70.59,3.79-99.08,18.29-3.87,1.95-9.52-2.77-11.84-8.16-3.32-7.71-1.63-6.28,2.61-8.49,38.31-20.03,82.01-39.61,123.91-29.7,8.26,1.95,15.96,5.26,23.48,10.54,11.32,7.96,20.21,24.74,16.44,44Z"/><path d="M117.72,304.2c-.81-2.41-1.39-4.78-1.8-7.07-1.61-9.03.93-17.78,5.99-21.74,22.6-17.7,49.85-29.35,75.34-38.6.59-.22,1.09-.28,1.4-.34,2.22-.47,4.95-1.04,7.25,0,1.46.66,2.25,1.74,2.66,2.3,1.56,2.1,1.59,4.31,1.56,5.13-.1,2.67.01,4.69,0,4.82-.45,3.52-.91,10.66-1.41,21.28-.6,3.87-2.16,9.63-6.94,13.96-4.01,3.62-8.33,4.6-14.59,5.87-10.76,2.19-37.21,8.22-47.42,16.56-1.63,1.33-2.97,2.65-4.19,3.96-3.72,3.99-6.39,7.92-7.93,10.36-3.22-3.22-7.25-8.48-9.92-16.47Z"/><path d="M83.33,326.72c2.33-11.99,8.91-22.32,15.88-30.38-.27,5.5.05,12.11,1.86,19.08,5.04,19.36,19.74,34.7,37.78,37.78,32.21,9.74,70.59-3.79,99.08-18.29,3.87-1.95,9.52,2.77,11.84,8.16,3.32,7.71,1.63,6.28-2.61,8.49-38.31,20.03-82.01,39.61-123.91,29.7-8.26-1.95-15.96-5.26-23.48-10.54-11.32-7.96-20.21-24.74-16.44-44Z"/><ellipse cx="255.16" cy="258.86" rx="28.95" ry="28.76"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,203 +0,0 @@
|
||||
---
|
||||
name: brainstorming
|
||||
description: "在进行任何创造性工作之前,你都必须使用此技能——包括创建功能、构建组件、添加功能或修改行为。在实现之前探索用户意图、需求和设计。"
|
||||
---
|
||||
|
||||
# 通过头脑风暴将想法转化为设计
|
||||
|
||||
通过自然的协作式对话,帮助将想法转化为完整成形的设计和规格说明。
|
||||
|
||||
先判断请求需要多少流程,再沿着对应路径推进:了解上下文、完善想法、展示设计,并获得人类伙伴的批准。
|
||||
|
||||
<HARD-GATE>
|
||||
在你告知人类伙伴打算做什么并获得批准之前,不得调用任何实现技能、编写任何代码、搭建任何项目脚手架或采取任何实现行动。以下每条路径上的每项任务都适用——流程仪式会随任务调整,但批准关卡永远不会取消。
|
||||
</HARD-GATE>
|
||||
|
||||
## 三条路径
|
||||
|
||||
在提出第一个问题之前先分类,并大声说出分类——例如“这看起来是有边界的任务,所以我会在这里展示一个简短设计,而不是写规格说明”——让人类伙伴可以推翻你的判断:
|
||||
|
||||
- **探索(Spike)**——可行性问题(“能不能……”“是否可能……”“快速粗糙地做也可以”),产出是答案而不是要保留的代码。用 2-3 句话说明问题和尝试方式,得到同意后,以正确性允许的最低成本进行探查。不写设计文档或规格文件。把发现报告为建议;构建的任何内容都必须标记为一次性产物。
|
||||
- **有边界(Bounded)**——对本仓库已有代码的范围明确的改动:新标志、小端点、单文件修复。仅仅理解应用类型还不够——有边界意味着要修改的流程已经存在且可供阅读。如果没有可修改的现有流程,任务就不是有边界的。提出关键澄清问题,在对话中展示简短设计(几句话到几段短文),然后停止。只有人类伙伴明确同意设计后才能实现;不写规格文件或实现计划文档。
|
||||
- **架构性(Architectural)**——新项目、新子系统、改变组件关系或其他人依赖的接口。走完整流程:提问、方案、分节设计、书面规格说明,然后调用 writing-plans 技能。
|
||||
|
||||
如果在两条路径之间犹豫,选择较重的路径。这个棘轮只能向上:任务中途发现隐藏复杂性时,停止、说明情况并升级路径;不会中途降级。
|
||||
|
||||
## 反模式:“太简单了,不需要批准”
|
||||
|
||||
每条路径都必须在人类伙伴批准意图后才能实现。待办事项列表、单函数工具、配置变更——设计可以只是聊天中的两句话,但仍必须展示并获得批准。“简单”任务最容易因未经审视的假设而浪费工作。随简单程度调整的是产物,不是批准。
|
||||
|
||||
## 危险信号
|
||||
|
||||
| 想法 | 事实 |
|
||||
|---------|---------|
|
||||
| “这太简单了,不需要设计” | 简单意味着设计简短,而不是没有设计。聊天中写两句话,然后获得批准。 |
|
||||
| “我把它叫作有边界,就可以跳过规格” | 为了省事而贴标签本身就是疑点——选择更重的路径。 |
|
||||
| “它有边界而且设计显而易见——他们阅读时我先开始” | 关卡是批准,不是设计长短。展示后停止,直到听到同意。 |
|
||||
| “我了解这种应用,所以它有边界” | 有边界衡量的是仓库,而不是你的熟悉程度。新项目没有现有流程,属于架构性任务。 |
|
||||
| “探索成功了,我就保留代码” | 探索的产出是答案。保留代码是新请求,需要重新分类。 |
|
||||
| “它变复杂了,但我快做完了——不用重新分类” | 隐藏复杂性会在任务中途升级路径。停止并说明情况。 |
|
||||
| “他们批准了探索,所以后续改动也获批了” | 每个任务都有自己的分类和自己的批准。 |
|
||||
|
||||
## 清单
|
||||
|
||||
先分类并宣布路径,然后为对应路径上的每一项创建任务并按顺序完成。
|
||||
|
||||
**探索路径:**
|
||||
1. **探索项目上下文**——获取足以界定探查范围的信息
|
||||
2. **展示问题和探查计划**——2-3 句话
|
||||
3. **获得批准**——得到同意即可
|
||||
4. **进行调查**——以正确性允许的最低成本推进
|
||||
5. **报告发现**——给出建议,并标记所有构建内容为一次性产物
|
||||
|
||||
**有边界路径:**
|
||||
1. **探索项目上下文**——检查文件、文档和最近提交
|
||||
2. **提出澄清问题**——一次一个,只问关键问题
|
||||
3. **在聊天中展示简短设计**——方案、涉及文件和测试
|
||||
4. **获得批准**——停止并等待明确的“是”;展示设计后立即开始就是跳过关卡
|
||||
5. **实现**——使用正常开发流程推进(TDD 仍适用);不写计划文档
|
||||
|
||||
**架构性路径:**
|
||||
1. **探索项目上下文**——检查文件、文档和最近的提交
|
||||
2. **在恰当时机提供视觉伴侣**——不要预先提供。当某个问题第一次确实用展示比用描述更清楚时,就在那时提出使用它(用一条单独的消息);获得批准后,它的浏览器标签页会为你打开。如果始终没有出现视觉问题,就绝不要提出使用它。参见下方的“视觉伴侣”一节。
|
||||
3. **提出澄清问题**——一次一个,了解目的、约束和成功标准
|
||||
4. **提出 2-3 种方案**——说明权衡取舍和你的建议
|
||||
5. **展示设计**——按复杂度划分并调整各节内容,每展示一节后都要获得用户批准
|
||||
6. **编写设计文档**——保存到 `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` 并提交
|
||||
7. **规格说明自审**——快速进行内联检查,查找占位符、矛盾、歧义和范围问题(见下文)
|
||||
8. **用户审查已写好的规格说明**——请用户在继续之前审查规格说明文件
|
||||
9. **过渡到实现**——调用 writing-plans 技能来创建实现计划
|
||||
|
||||
## 流程
|
||||
|
||||
```dot
|
||||
digraph brainstorming {
|
||||
"Classify: spike / bounded / architectural" [shape=diamond, label="分类:探索 / 有边界 / 架构性"];
|
||||
"Present question + probe (2-3 sentences)" [shape=box, label="展示问题和探查(2-3 句话)"];
|
||||
"Ask clarifying questions (bounded)" [shape=box, label="提出澄清问题(有边界)"];
|
||||
"Present short design in chat" [shape=box, label="在聊天中展示简短设计"];
|
||||
"Human approves?" [shape=diamond, label="人类批准了吗?"];
|
||||
"Investigate; report recommendation" [shape=doublecircle, label="调查并报告建议"];
|
||||
"Implement via normal workflow (no plan doc)" [shape=doublecircle, label="通过正常流程实现(无计划文档)"];
|
||||
"Explore project context" [shape=box, label="探索项目上下文"];
|
||||
"Ask clarifying questions" [shape=box, label="提出澄清问题"];
|
||||
"Propose 2-3 approaches" [shape=box, label="提出 2-3 种方案"];
|
||||
"Present design sections" [shape=box, label="分节呈现设计"];
|
||||
"User approves design?" [shape=diamond, label="用户批准设计了吗?"];
|
||||
"Write design doc" [shape=box, label="编写设计文档"];
|
||||
"Spec self-review\n(fix inline)" [shape=box, label="规格说明自审\n(内联修正)"];
|
||||
"User reviews spec?" [shape=diamond, label="用户审查规格说明了吗?"];
|
||||
"Invoke writing-plans skill" [shape=doublecircle, label="调用 writing-plans 技能"];
|
||||
"Hidden complexity? Upgrade path" [shape=box, label="发现隐藏复杂性?升级路径"];
|
||||
|
||||
"Classify: spike / bounded / architectural" -> "Present question + probe (2-3 sentences)" [label="探索"];
|
||||
"Classify: spike / bounded / architectural" -> "Ask clarifying questions (bounded)" [label="有边界"];
|
||||
"Classify: spike / bounded / architectural" -> "Explore project context" [label="架构性"];
|
||||
"Present question + probe (2-3 sentences)" -> "Human approves?";
|
||||
"Ask clarifying questions (bounded)" -> "Present short design in chat";
|
||||
"Present short design in chat" -> "Human approves?";
|
||||
"Human approves?" -> "Investigate; report recommendation" [label="探索:是"];
|
||||
"Human approves?" -> "Implement via normal workflow (no plan doc)" [label="有边界:是"];
|
||||
"Hidden complexity? Upgrade path" -> "Classify: spike / bounded / architectural";
|
||||
"Explore project context" -> "Ask clarifying questions";
|
||||
"Ask clarifying questions" -> "Propose 2-3 approaches";
|
||||
"Propose 2-3 approaches" -> "Present design sections";
|
||||
"Present design sections" -> "User approves design?";
|
||||
"User approves design?" -> "Present design sections" [label="否,修改"];
|
||||
"User approves design?" -> "Write design doc" [label="是"];
|
||||
"Write design doc" -> "Spec self-review\n(fix inline)";
|
||||
"Spec self-review\n(fix inline)" -> "User reviews spec?";
|
||||
"User reviews spec?" -> "Write design doc" [label="要求更改"];
|
||||
"User reviews spec?" -> "Invoke writing-plans skill" [label="已批准"];
|
||||
}
|
||||
```
|
||||
**终止状态取决于路径。** 架构性路径:brainstorming 之后唯一调用的技能是 writing-plans,绝不要调用 frontend-design、mcp-builder 或其他实现技能。有边界路径:获批后直接使用正常开发流程实现,不写计划文档。探索路径:终止状态是报告建议。
|
||||
|
||||
## 流程
|
||||
|
||||
下面的子节服务于有边界和架构性路径(探索路径在“展示探查、获得同意”处停止)。从**探索方案**开始的内容属于架构性深度;对有边界任务来说,上下文、几个问题和一份聊天中的简短设计就是完整流程。
|
||||
|
||||
**理解想法:**
|
||||
|
||||
- 首先查看当前项目状态(文件、文档、最近的提交)
|
||||
- 在询问详细问题之前,先评估范围:如果请求描述了多个相互独立的子系统(例如,“构建一个包含聊天、文件存储、计费和分析的平台”),应立即指出这一点。不要把问题浪费在细化一个首先需要拆分的项目的细节上。
|
||||
- 如果项目规模过大,无法用一份规格说明涵盖,请帮助用户将其拆分为多个子项目:哪些是独立的部分、它们如何关联、应按什么顺序构建?然后按照正常的设计流程,对第一个子项目进行头脑风暴。每个子项目都有其自己的规格说明 → 计划 → 实现周期。
|
||||
- 对于范围适当的项目,每次询问一个问题以细化想法
|
||||
- 尽可能优先使用多项选择题,但开放式问题也可以
|
||||
- 每条消息只问一个问题——如果某个主题需要进一步探索,请将其拆成多个问题
|
||||
- 专注于理解:目的、约束、成功标准
|
||||
|
||||
**探索方案:**
|
||||
|
||||
- 提出 2-3 种不同的方案,并说明各自的权衡
|
||||
- 以对话方式呈现选项,并给出你的建议和理由
|
||||
- 先介绍你推荐的选项,并解释原因
|
||||
- 坚决贯彻 YAGNI——从每一种方案和设计中移除不必要的功能
|
||||
|
||||
**呈现设计:**
|
||||
|
||||
- 一旦你确信自己理解了要构建的内容,就呈现设计
|
||||
- 根据每个部分的复杂程度调整篇幅:如果简单直接,就用几句话;如果细节微妙复杂,则最多使用 200-300 词
|
||||
- 在每个部分之后询问目前看来是否正确
|
||||
- 涵盖:架构、组件、数据流、错误处理、测试
|
||||
- 如果有些内容不合理,随时准备返回并澄清
|
||||
|
||||
**为隔离性和清晰性而设计:**
|
||||
- 将系统拆分为更小的单元,每个单元都有一个明确的用途,通过定义清晰的接口进行通信,并且可以被独立理解和测试
|
||||
- 对于每个单元,你都应该能够回答:它做什么、如何使用它,以及它依赖什么?
|
||||
- 其他人能否在不阅读某个单元内部实现的情况下理解它做什么?你能否在不破坏使用方的情况下更改其内部实现?如果不能,就需要改进边界。
|
||||
- 更小且边界清晰的单元也更便于你处理——对于能够一次性纳入上下文的代码,你可以进行更好的推理;当文件职责集中时,你的编辑也会更可靠。当一个文件变得很大时,这通常表明它承担了过多职责。
|
||||
|
||||
**在现有代码库中工作:**
|
||||
|
||||
- 在提出更改之前,先探索当前结构。遵循现有模式。
|
||||
- 如果现有代码存在影响这项工作的问题(例如,文件变得过大、边界不清晰、职责纠缠),应将有针对性的改进纳入设计——就像优秀的开发者会改进自己正在处理的代码一样。
|
||||
- 不要提出无关的重构。始终专注于服务当前目标的内容。
|
||||
|
||||
## 设计之后(架构性路径)
|
||||
|
||||
**文档:**
|
||||
|
||||
- 将经过验证的设计(规格说明)写入 `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md`
|
||||
- (用户对规格说明位置的偏好会覆盖此默认设置)
|
||||
- 如果可用,使用 elements-of-style:writing-clearly-and-concisely 技能
|
||||
- 将设计文档提交到 git
|
||||
|
||||
**规格说明自审:**
|
||||
写完规格说明文档后,以全新的视角审视它:
|
||||
|
||||
1. **占位符扫描:** 是否有任何 "TBD"、"TODO"、未完成的章节或模糊的要求?修复它们。
|
||||
2. **内部一致性:** 是否有任何章节相互矛盾?架构是否与功能描述一致?
|
||||
3. **范围检查:** 范围是否足够聚焦,可以用单个实现计划完成,还是需要进行拆分?
|
||||
4. **歧义检查:** 是否有任何要求可能被理解为两种不同的含义?如果有,选择一种并明确写出。
|
||||
|
||||
直接修复所有问题。无需再次审查——修复后继续推进。
|
||||
**用户审查关卡:**
|
||||
规格审查循环通过后,在继续之前,请用户审查已写好的规格说明:
|
||||
|
||||
> “规格说明已写入 `<path>` 并提交。请审查它,并告诉我在我们开始编写实施计划之前,你是否希望进行任何更改。”
|
||||
|
||||
等待用户回复。如果他们要求更改,就进行更改并重新运行规格审查循环。只有在用户批准后才能继续。
|
||||
|
||||
**实施:**
|
||||
|
||||
- 调用 writing-plans 技能来创建详细的实施计划
|
||||
- 不要调用任何其他技能。writing-plans 是下一步。
|
||||
|
||||
## 可视化伴侣
|
||||
|
||||
一个基于浏览器的伴侣工具,用于在头脑风暴期间展示原型图、图表和可视化选项。它作为工具提供——而不是一种模式。同意使用该伴侣工具,意味着它可用于那些采用可视化方式会更有帮助的问题;这并不意味着每个问题都要通过浏览器进行。
|
||||
|
||||
**提供伴侣工具(即时):** 不要一开始就提出使用它。等到某个问题确实用展示的方式会比口头描述更清楚时——真正涉及原型图 / 布局 / 图表的问题,而不仅仅是一个 UI *话题*。第一次出现这种情况时,就在那时提出使用它,并且该提议要单独作为一条消息:
|
||||
> “接下来的这部分如果展示给你看,可能会更容易理解——在我们推进的过程中,我可以在浏览器标签页中制作原型图、图表和对比内容。它仍是新功能,并且可能会消耗大量 token。你想让我这样做吗?我会为你打开它。”
|
||||
|
||||
**此提议必须单独作为一条消息发送。** 只发送该提议——不要包含澄清问题、摘要或任何其他内容。等待用户回复。如果他们接受,使用 `--open` 启动服务器,以便他们的浏览器自动打开第一个界面。如果他们拒绝,则继续仅使用文本,并且不要再次提出使用它,除非他们主动提起。
|
||||
**逐问题决策:** 即使用户接受了,也要针对每个问题分别决定使用浏览器还是终端。判断标准是:**相比阅读,用户通过观看是否能更好地理解这一点?**
|
||||
|
||||
- **使用浏览器**呈现确实属于视觉内容的内容——模型图、线框图、布局对比、架构图、并排的视觉设计
|
||||
- **使用终端**呈现文本内容——需求问题、概念性选择、权衡清单、A/B/C/D 文本选项、范围决策
|
||||
|
||||
关于 UI 主题的问题并不自动等同于视觉问题。“在此上下文中,个性意味着什么?”是一个概念性问题——使用终端。“哪种向导布局效果更好?”是一个视觉问题——使用浏览器。
|
||||
|
||||
如果他们同意使用视觉伴侣,请在继续之前阅读详细指南:
|
||||
`skills/brainstorming/visual-companion.md`
|
||||
-213
@@ -1,213 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Superpowers 头脑风暴</title>
|
||||
<style>
|
||||
/*
|
||||
* BRAINSTORM COMPANION FRAME TEMPLATE
|
||||
*
|
||||
* This template provides a consistent frame with:
|
||||
* - OS-aware light/dark theming
|
||||
* - Header branding and connection status
|
||||
* - Scrollable main content area
|
||||
* - CSS helpers for common UI patterns
|
||||
*
|
||||
* Content is injected via placeholder comment in #frame-content.
|
||||
*/
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { height: 100%; overflow: hidden; }
|
||||
|
||||
/* ===== THEME VARIABLES ===== */
|
||||
:root {
|
||||
--bg-primary: #f5f5f7;
|
||||
--bg-secondary: #ffffff;
|
||||
--bg-tertiary: #e5e5e7;
|
||||
--border: #d1d1d6;
|
||||
--text-primary: #1d1d1f;
|
||||
--text-secondary: #86868b;
|
||||
--text-tertiary: #aeaeb2;
|
||||
--accent: #0071e3;
|
||||
--accent-hover: #0077ed;
|
||||
--success: #34c759;
|
||||
--warning: #ff9f0a;
|
||||
--error: #ff3b30;
|
||||
--selected-bg: #e8f4fd;
|
||||
--selected-border: #0071e3;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg-primary: #1d1d1f;
|
||||
--bg-secondary: #2d2d2f;
|
||||
--bg-tertiary: #3d3d3f;
|
||||
--border: #424245;
|
||||
--text-primary: #f5f5f7;
|
||||
--text-secondary: #86868b;
|
||||
--text-tertiary: #636366;
|
||||
--accent: #0a84ff;
|
||||
--accent-hover: #409cff;
|
||||
--selected-bg: rgba(10, 132, 255, 0.15);
|
||||
--selected-border: #0a84ff;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* ===== FRAME STRUCTURE ===== */
|
||||
.brand { display: flex; align-items: center; min-width: 0; overflow: hidden; color: var(--text-secondary); line-height: 1; }
|
||||
.brand a { color: inherit; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; min-width: 0; max-width: 100%; line-height: 1; }
|
||||
.brand-copy { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; transform: translateY(-1px); }
|
||||
.brand-logo { display: block; height: 1em; width: auto; max-width: 180px; flex-shrink: 0; filter: invert(1); }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.brand-logo { filter: none; }
|
||||
}
|
||||
.status { font-size: 0.7rem; color: var(--status-color, var(--success)); display: flex; align-items: center; gap: 0.4rem; justify-self: end; white-space: nowrap; line-height: 1; }
|
||||
.status::before { content: ''; width: 6px; height: 6px; background: var(--status-color, var(--success)); border-radius: 50%; }
|
||||
|
||||
.main { flex: 1; overflow-y: auto; }
|
||||
#frame-content { padding: 2rem; min-height: 100%; }
|
||||
|
||||
.header {
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 0.5rem 1.5rem;
|
||||
flex-shrink: 0;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
min-height: 42px;
|
||||
}
|
||||
.header .brand { justify-self: start; width: 100%; font-size: 0.75rem; line-height: 1; }
|
||||
.header .status { grid-column: 2; line-height: 1; }
|
||||
.header span {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.header .selected-text {
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ===== TYPOGRAPHY ===== */
|
||||
h2 { font-size: 1.5rem; font-weight: 600; margin-bottom: 0.5rem; }
|
||||
h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.25rem; }
|
||||
.subtitle { color: var(--text-secondary); margin-bottom: 1.5rem; }
|
||||
.section { margin-bottom: 2rem; }
|
||||
.label { font-size: 0.7rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
|
||||
|
||||
/* ===== OPTIONS (for A/B/C choices) ===== */
|
||||
.options { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
.option {
|
||||
background: var(--bg-secondary);
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 1rem 1.25rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
.option:hover { border-color: var(--accent); }
|
||||
.option.selected { background: var(--selected-bg); border-color: var(--selected-border); }
|
||||
.option .letter {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-secondary);
|
||||
width: 1.75rem; height: 1.75rem;
|
||||
border-radius: 6px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 600; font-size: 0.85rem; flex-shrink: 0;
|
||||
}
|
||||
.option.selected .letter { background: var(--accent); color: white; }
|
||||
.option .content { flex: 1; }
|
||||
.option .content h3 { font-size: 0.95rem; margin-bottom: 0.15rem; }
|
||||
.option .content p { color: var(--text-secondary); font-size: 0.85rem; margin: 0; }
|
||||
|
||||
/* ===== CARDS (for showing designs/mockups) ===== */
|
||||
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; }
|
||||
.card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
|
||||
.card.selected { border-color: var(--selected-border); border-width: 2px; }
|
||||
.card-image { background: var(--bg-tertiary); aspect-ratio: 16/10; display: flex; align-items: center; justify-content: center; }
|
||||
.card-body { padding: 1rem; }
|
||||
.card-body h3 { margin-bottom: 0.25rem; }
|
||||
.card-body p { color: var(--text-secondary); font-size: 0.85rem; }
|
||||
|
||||
/* ===== MOCKUP CONTAINER ===== */
|
||||
.mockup {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.mockup-header {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.mockup-body { padding: 1.5rem; }
|
||||
|
||||
/* ===== SPLIT VIEW (side-by-side comparison) ===== */
|
||||
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
|
||||
@media (max-width: 700px) { .split { grid-template-columns: 1fr; } }
|
||||
|
||||
/* ===== PROS/CONS ===== */
|
||||
.pros-cons { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: 1rem 0; }
|
||||
.pros, .cons { background: var(--bg-secondary); border-radius: 8px; padding: 1rem; }
|
||||
.pros h4 { color: var(--success); font-size: 0.85rem; margin-bottom: 0.5rem; }
|
||||
.cons h4 { color: var(--error); font-size: 0.85rem; margin-bottom: 0.5rem; }
|
||||
.pros ul, .cons ul { margin-left: 1.25rem; font-size: 0.85rem; color: var(--text-secondary); }
|
||||
.pros li, .cons li { margin-bottom: 0.25rem; }
|
||||
|
||||
/* ===== PLACEHOLDER (for mockup areas) ===== */
|
||||
.placeholder {
|
||||
background: var(--bg-tertiary);
|
||||
border: 2px dashed var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
/* ===== INLINE MOCKUP ELEMENTS ===== */
|
||||
.mock-nav { background: var(--accent); color: white; padding: 0.75rem 1rem; display: flex; gap: 1.5rem; font-size: 0.9rem; }
|
||||
.mock-sidebar { background: var(--bg-tertiary); padding: 1rem; min-width: 180px; }
|
||||
.mock-content { padding: 1.5rem; flex: 1; }
|
||||
.mock-button { background: var(--accent); color: white; border: none; padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.85rem; }
|
||||
.mock-input { background: var(--bg-primary); border: 1px solid var(--border); border-radius: 6px; padding: 0.5rem; width: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<!-- BRANDING -->
|
||||
<div class="status">正在连接…</div>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div id="frame-content">
|
||||
<!-- CONTENT -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,167 +0,0 @@
|
||||
(function() {
|
||||
const MIN_RECONNECT_MS = 500;
|
||||
const MAX_RECONNECT_MS = 30000;
|
||||
const TOMBSTONE_AFTER_MS = 15000; // show the "paused" overlay after this long disconnected
|
||||
|
||||
// Pure: next backoff delay (doubles, capped). Exported for unit tests.
|
||||
function nextReconnectDelay(current, max) {
|
||||
return Math.min(current * 2, max);
|
||||
}
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = { nextReconnectDelay, MIN_RECONNECT_MS, MAX_RECONNECT_MS, TOMBSTONE_AFTER_MS };
|
||||
}
|
||||
|
||||
// Everything below is browser-only; bail out when loaded in Node (tests).
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
let ws = null;
|
||||
let eventQueue = [];
|
||||
let reconnectDelay = MIN_RECONNECT_MS;
|
||||
let reconnectTimer = null;
|
||||
let disconnectedSince = null;
|
||||
let everConnected = false;
|
||||
let tombstoneShown = false;
|
||||
|
||||
function sessionKey() {
|
||||
try {
|
||||
return window.sessionStorage && window.sessionStorage.getItem('brainstorm-session-key');
|
||||
} catch (e) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
function websocketUrl() {
|
||||
const key = sessionKey();
|
||||
return 'ws://' + window.location.host + (key ? '/?key=' + encodeURIComponent(key) : '');
|
||||
}
|
||||
|
||||
function reloadAfterRecovery() {
|
||||
const key = sessionKey();
|
||||
if (key) {
|
||||
window.location.replace('/?key=' + encodeURIComponent(key));
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
// Reflect connection state in the frame's status pill (absent on full-doc screens).
|
||||
function setStatus(state) {
|
||||
const el = document.querySelector('.status');
|
||||
if (!el) return;
|
||||
const map = {
|
||||
connecting: ['正在连接…', 'var(--text-tertiary)'],
|
||||
connected: ['已连接', 'var(--success)'],
|
||||
reconnecting: ['正在重新连接…', 'var(--warning)'],
|
||||
disconnected: ['已断开连接', 'var(--error)']
|
||||
};
|
||||
const [text, color] = map[state] || map.disconnected;
|
||||
el.textContent = text;
|
||||
el.style.setProperty('--status-color', color);
|
||||
}
|
||||
|
||||
// Self-styled so it works on framed and full-document screens alike.
|
||||
function showTombstone() {
|
||||
if (tombstoneShown) return;
|
||||
tombstoneShown = true;
|
||||
const el = document.createElement('div');
|
||||
el.id = 'bs-tombstone';
|
||||
el.style.cssText = 'position:fixed;inset:0;z-index:99999;display:flex;' +
|
||||
'align-items:center;justify-content:center;padding:2rem;text-align:center;' +
|
||||
'background:rgba(20,20,22,0.92);color:#f5f5f7;font-family:system-ui,sans-serif';
|
||||
el.innerHTML = '<div style="max-width:480px">' +
|
||||
'<h2 style="margin:0 0 .5rem;font-weight:600">伴侣已暂停</h2>' +
|
||||
'<p style="margin:0;opacity:.85">头脑风暴伴侣已停止。' +
|
||||
'请让你的编程 Agent 重新启动它——本页面会自动重新连接。</p></div>';
|
||||
if (document.body) document.body.appendChild(el);
|
||||
}
|
||||
|
||||
function connect() {
|
||||
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
||||
setStatus(everConnected ? 'reconnecting' : 'connecting');
|
||||
ws = new WebSocket(websocketUrl());
|
||||
|
||||
ws.onopen = () => {
|
||||
const recovered = tombstoneShown;
|
||||
everConnected = true;
|
||||
disconnectedSince = null;
|
||||
reconnectDelay = MIN_RECONNECT_MS;
|
||||
tombstoneShown = false;
|
||||
setStatus('connected');
|
||||
eventQueue.forEach(e => ws.send(JSON.stringify(e)));
|
||||
eventQueue = [];
|
||||
// Recovered from a tombstoned outage (e.g. the server restarted on the same
|
||||
// port) — reload through the keyed bootstrap when possible so the cookie is
|
||||
// refreshed before the visible URL returns to bare /.
|
||||
if (recovered) reloadAfterRecovery();
|
||||
};
|
||||
|
||||
ws.onmessage = (msg) => {
|
||||
let data;
|
||||
try { data = JSON.parse(msg.data); } catch (e) { return; }
|
||||
if (data.type === 'reload') window.location.reload();
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
ws = null;
|
||||
if (disconnectedSince === null) disconnectedSince = Date.now();
|
||||
if (Date.now() - disconnectedSince >= TOMBSTONE_AFTER_MS) {
|
||||
setStatus('disconnected');
|
||||
showTombstone();
|
||||
} else {
|
||||
setStatus('reconnecting');
|
||||
}
|
||||
reconnectTimer = setTimeout(connect, reconnectDelay);
|
||||
reconnectDelay = nextReconnectDelay(reconnectDelay, MAX_RECONNECT_MS);
|
||||
};
|
||||
|
||||
// Let onclose own reconnection so we don't schedule it twice.
|
||||
ws.onerror = () => { try { ws.close(); } catch (e) {} };
|
||||
}
|
||||
|
||||
function sendEvent(event) {
|
||||
event.timestamp = Date.now();
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify(event));
|
||||
} else {
|
||||
eventQueue.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Capture clicks on choice elements
|
||||
document.addEventListener('click', (e) => {
|
||||
const target = e.target.closest('[data-choice]');
|
||||
if (!target) return;
|
||||
|
||||
sendEvent({
|
||||
type: 'click',
|
||||
text: target.textContent.trim(),
|
||||
choice: target.dataset.choice,
|
||||
id: target.id || null
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Frame UI: selection tracking
|
||||
window.selectedChoice = null;
|
||||
|
||||
window.toggleSelect = function(el) {
|
||||
const container = el.closest('.options') || el.closest('.cards');
|
||||
const multi = container && container.dataset.multiselect !== undefined;
|
||||
if (container && !multi) {
|
||||
container.querySelectorAll('.option, .card').forEach(o => o.classList.remove('selected'));
|
||||
}
|
||||
if (multi) {
|
||||
el.classList.toggle('selected');
|
||||
} else {
|
||||
el.classList.add('selected');
|
||||
}
|
||||
window.selectedChoice = el.dataset.choice;
|
||||
};
|
||||
|
||||
// Expose API for explicit use
|
||||
window.brainstorm = {
|
||||
send: sendEvent,
|
||||
choice: (value, metadata = {}) => sendEvent({ type: 'choice', value, ...metadata })
|
||||
};
|
||||
|
||||
connect();
|
||||
})();
|
||||
@@ -1,723 +0,0 @@
|
||||
const crypto = require('crypto');
|
||||
const http = require('http');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// ========== WebSocket Protocol (RFC 6455) ==========
|
||||
|
||||
const OPCODES = { TEXT: 0x01, CLOSE: 0x08, PING: 0x09, PONG: 0x0A };
|
||||
const WS_MAGIC = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
||||
const MAX_FRAME_PAYLOAD_BYTES = 10 * 1024 * 1024;
|
||||
|
||||
function computeAcceptKey(clientKey) {
|
||||
return crypto.createHash('sha1').update(clientKey + WS_MAGIC).digest('base64');
|
||||
}
|
||||
|
||||
function encodeFrame(opcode, payload) {
|
||||
const fin = 0x80;
|
||||
const len = payload.length;
|
||||
let header;
|
||||
|
||||
if (len < 126) {
|
||||
header = Buffer.alloc(2);
|
||||
header[0] = fin | opcode;
|
||||
header[1] = len;
|
||||
} else if (len < 65536) {
|
||||
header = Buffer.alloc(4);
|
||||
header[0] = fin | opcode;
|
||||
header[1] = 126;
|
||||
header.writeUInt16BE(len, 2);
|
||||
} else {
|
||||
header = Buffer.alloc(10);
|
||||
header[0] = fin | opcode;
|
||||
header[1] = 127;
|
||||
header.writeBigUInt64BE(BigInt(len), 2);
|
||||
}
|
||||
|
||||
return Buffer.concat([header, payload]);
|
||||
}
|
||||
|
||||
function decodeFrame(buffer) {
|
||||
if (buffer.length < 2) return null;
|
||||
|
||||
const secondByte = buffer[1];
|
||||
const opcode = buffer[0] & 0x0F;
|
||||
const masked = (secondByte & 0x80) !== 0;
|
||||
let payloadLen = secondByte & 0x7F;
|
||||
let offset = 2;
|
||||
|
||||
if (!masked) throw new Error('Client frames must be masked');
|
||||
|
||||
if (payloadLen === 126) {
|
||||
if (buffer.length < 4) return null;
|
||||
payloadLen = buffer.readUInt16BE(2);
|
||||
offset = 4;
|
||||
} else if (payloadLen === 127) {
|
||||
if (buffer.length < 10) return null;
|
||||
const extendedLen = buffer.readBigUInt64BE(2);
|
||||
if (extendedLen > BigInt(MAX_FRAME_PAYLOAD_BYTES)) {
|
||||
throw new Error('WebSocket frame payload exceeds maximum allowed size');
|
||||
}
|
||||
payloadLen = Number(extendedLen);
|
||||
offset = 10;
|
||||
}
|
||||
|
||||
if (payloadLen > MAX_FRAME_PAYLOAD_BYTES) {
|
||||
throw new Error('WebSocket frame payload exceeds maximum allowed size');
|
||||
}
|
||||
|
||||
const maskOffset = offset;
|
||||
const dataOffset = offset + 4;
|
||||
const totalLen = dataOffset + payloadLen;
|
||||
if (buffer.length < totalLen) return null;
|
||||
|
||||
const mask = buffer.slice(maskOffset, dataOffset);
|
||||
const data = Buffer.alloc(payloadLen);
|
||||
for (let i = 0; i < payloadLen; i++) {
|
||||
data[i] = buffer[dataOffset + i] ^ mask[i % 4];
|
||||
}
|
||||
|
||||
return { opcode, payload: data, bytesConsumed: totalLen };
|
||||
}
|
||||
|
||||
// ========== Configuration ==========
|
||||
|
||||
const PORT_FILE = process.env.BRAINSTORM_PORT_FILE || null;
|
||||
const randomPort = () => 49152 + Math.floor(Math.random() * 16383);
|
||||
// Prefer an explicit port, else the port this session last bound (so a restart
|
||||
// reuses it and an already-open browser tab reconnects), else a random high port.
|
||||
function preferredPort() {
|
||||
if (process.env.BRAINSTORM_PORT) return Number(process.env.BRAINSTORM_PORT);
|
||||
if (PORT_FILE) {
|
||||
try {
|
||||
const p = Number(fs.readFileSync(PORT_FILE, 'utf-8').trim());
|
||||
if (Number.isInteger(p) && p > 1023 && p < 65536) return p;
|
||||
} catch (e) { /* no prior port recorded */ }
|
||||
}
|
||||
return randomPort();
|
||||
}
|
||||
let PORT = preferredPort();
|
||||
const HOST = process.env.BRAINSTORM_HOST || '127.0.0.1';
|
||||
const URL_HOST = process.env.BRAINSTORM_URL_HOST || (HOST === '127.0.0.1' ? 'localhost' : HOST);
|
||||
const SESSION_DIR = process.env.BRAINSTORM_DIR || '/tmp/brainstorm';
|
||||
const CONTENT_DIR = path.join(SESSION_DIR, 'content');
|
||||
const STATE_DIR = path.join(SESSION_DIR, 'state');
|
||||
const SUPERPOWERS_VERSION = readSuperpowersVersion();
|
||||
const SUPERPOWERS_BRAND_IMAGE_URL = 'https://primeradiant.com/brand/superpowers-visual-brainstorming-logo.png';
|
||||
const TELEMETRY_DISABLE_ENV_VARS = [
|
||||
'SUPERPOWERS_DISABLE_TELEMETRY',
|
||||
'DISABLE_TELEMETRY',
|
||||
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC'
|
||||
];
|
||||
const SUPERPOWERS_TELEMETRY_DISABLED = TELEMETRY_DISABLE_ENV_VARS.some(name => isTruthyEnv(process.env[name]));
|
||||
let ownerPid = process.env.BRAINSTORM_OWNER_PID ? Number(process.env.BRAINSTORM_OWNER_PID) : null;
|
||||
|
||||
// Per-session secret key. The companion is reachable by any local browser tab
|
||||
// and, when bound to a non-loopback host, by any host that can route to it.
|
||||
// The key authenticates the real client uniformly across loopback, tunnel, and
|
||||
// remote binds — and defeats DNS rebinding — where a Host/Origin allowlist
|
||||
// cannot. It rides the served URL as ?key= and is mirrored into a cookie on
|
||||
// first load so same-origin subresources and the WebSocket carry it for free.
|
||||
// Persisted alongside the port (BRAINSTORM_TOKEN_FILE) so a restart keeps the
|
||||
// same key and an already-open tab's cookie still validates.
|
||||
const TOKEN_FILE = process.env.BRAINSTORM_TOKEN_FILE || null;
|
||||
function generateToken() {
|
||||
return crypto.randomBytes(32).toString('hex');
|
||||
}
|
||||
|
||||
function chmodOwnerOnly(file) {
|
||||
try { fs.chmodSync(file, 0o600); } catch (e) { /* best effort */ }
|
||||
}
|
||||
|
||||
function initialToken() {
|
||||
if (process.env.BRAINSTORM_TOKEN) {
|
||||
return { value: process.env.BRAINSTORM_TOKEN, source: 'env' };
|
||||
}
|
||||
if (TOKEN_FILE) {
|
||||
try {
|
||||
const t = fs.readFileSync(TOKEN_FILE, 'utf-8').trim();
|
||||
if (/^[0-9a-f]{32,}$/i.test(t)) {
|
||||
chmodOwnerOnly(TOKEN_FILE);
|
||||
return { value: t, source: 'file' };
|
||||
}
|
||||
} catch (e) { /* no prior token recorded */ }
|
||||
}
|
||||
return { value: generateToken(), source: 'generated' };
|
||||
}
|
||||
|
||||
const tokenInfo = initialToken();
|
||||
let TOKEN = tokenInfo.value;
|
||||
let tokenSource = tokenInfo.source;
|
||||
let COOKIE_NAME = 'brainstorm-key-' + PORT; // refined to the actual bound port in onListen
|
||||
|
||||
const MIME_TYPES = {
|
||||
'.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript',
|
||||
'.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml'
|
||||
};
|
||||
|
||||
// ========== Templates and Constants ==========
|
||||
|
||||
function waitingPage() {
|
||||
return renderBranding(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"><title>头脑风暴伴侣</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; padding: 2rem; max-width: 800px; margin: 0 auto; }
|
||||
h1 { color: #333; } p { color: #666; }
|
||||
.brand { display: flex; align-items: center; min-width: 0; overflow: hidden; margin-bottom: 1.5rem; color: #666; font-size: 0.9rem; line-height: 1; }
|
||||
.brand a { color: inherit; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; min-width: 0; max-width: 100%; line-height: 1; }
|
||||
.brand-copy { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; transform: translateY(-1px); }
|
||||
.brand-logo { display: block; height: 1em; width: auto; max-width: 180px; filter: invert(1); }
|
||||
</style>
|
||||
</head>
|
||||
<body><!-- BRANDING --><h1>头脑风暴伴侣</h1>
|
||||
<p>正在等待 Agent 推送界面…</p></body></html>`);
|
||||
}
|
||||
|
||||
const FORBIDDEN_PAGE = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"><title>需要会话密钥</title>
|
||||
<style>body { font-family: system-ui, sans-serif; padding: 2rem; max-width: 800px; margin: 0 auto; }
|
||||
h1 { color: #333; } p { color: #666; } code { background: #f0f0f0; padding: 0.1em 0.3em; border-radius: 4px; }</style>
|
||||
</head>
|
||||
<body><h1>需要会话密钥</h1>
|
||||
<p>本页面需要编程 Agent 提供的完整 URL,其中必须包含
|
||||
<code>?key=…</code> 部分。请复制完整 URL 后重新打开。</p></body></html>`;
|
||||
|
||||
function bootstrapPage(key) {
|
||||
const jsonKey = JSON.stringify(String(key));
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"><title>正在打开头脑风暴伴侣</title></head>
|
||||
<body>
|
||||
<script>
|
||||
try { sessionStorage.setItem('brainstorm-session-key', ${jsonKey}); } catch (e) {}
|
||||
location.replace('/');
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
const frameTemplate = fs.readFileSync(path.join(__dirname, 'frame-template.html'), 'utf-8');
|
||||
const helperScript = fs.readFileSync(path.join(__dirname, 'helper.js'), 'utf-8');
|
||||
const helperInjection = '<script>\n' + helperScript + '\n</script>';
|
||||
|
||||
// ========== Helper Functions ==========
|
||||
|
||||
function readSuperpowersVersion() {
|
||||
const root = path.join(__dirname, '../../..');
|
||||
const manifests = [
|
||||
path.join(root, 'package.json'),
|
||||
path.join(root, '.codex-plugin/plugin.json')
|
||||
];
|
||||
|
||||
for (const manifest of manifests) {
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(manifest, 'utf-8'));
|
||||
if (data.version) return String(data.version);
|
||||
} catch (e) {
|
||||
// Packaged Codex plugins omit package.json; try the next manifest.
|
||||
}
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
function isTruthyEnv(value) {
|
||||
if (!value) return false;
|
||||
const normalized = String(value).trim().toLowerCase();
|
||||
if (!normalized) return false;
|
||||
return !['0', 'false', 'no', 'off'].includes(normalized);
|
||||
}
|
||||
|
||||
function escapeHtmlText(value) {
|
||||
return String(value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function brandMarkup() {
|
||||
const version = escapeHtmlText(SUPERPOWERS_VERSION);
|
||||
const text = SUPERPOWERS_TELEMETRY_DISABLED
|
||||
? 'Prime Radiant Superpowers v' + version
|
||||
: 'Superpowers v' + version;
|
||||
const logo = SUPERPOWERS_TELEMETRY_DISABLED
|
||||
? ''
|
||||
: '<img class="brand-logo" src="' + SUPERPOWERS_BRAND_IMAGE_URL + '?v=' + encodeURIComponent(SUPERPOWERS_VERSION) + '" alt="Prime Radiant" referrerpolicy="no-referrer" decoding="async">';
|
||||
|
||||
return '<div class="brand"><a href="https://github.com/obra/superpowers">' + logo + '<span class="brand-copy">' + text + '</span></a></div>';
|
||||
}
|
||||
|
||||
function renderBranding(html) {
|
||||
return html.split('<!-- BRANDING -->').join(brandMarkup());
|
||||
}
|
||||
|
||||
function isFullDocument(html) {
|
||||
const trimmed = html.trimStart().toLowerCase();
|
||||
return trimmed.startsWith('<!doctype') || trimmed.startsWith('<html');
|
||||
}
|
||||
|
||||
function wrapInFrame(content) {
|
||||
return renderBranding(frameTemplate).replace('<!-- CONTENT -->', content);
|
||||
}
|
||||
|
||||
function getNewestScreen() {
|
||||
const files = fs.readdirSync(CONTENT_DIR)
|
||||
.filter(f => !f.startsWith('.') && f.endsWith('.html'))
|
||||
.map(f => {
|
||||
const fp = path.join(CONTENT_DIR, f);
|
||||
if (!isRegularFileInsideContentDir(fp)) return null;
|
||||
return { path: fp, mtime: fs.statSync(fp).mtime.getTime() };
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => b.mtime - a.mtime);
|
||||
return files.length > 0 ? files[0].path : null;
|
||||
}
|
||||
|
||||
function urlHostForHttp(host) {
|
||||
const h = String(host);
|
||||
if (h.startsWith('[') && h.endsWith(']')) return h;
|
||||
return h.includes(':') ? '[' + h + ']' : h;
|
||||
}
|
||||
|
||||
function companionUrl() {
|
||||
return 'http://' + urlHostForHttp(URL_HOST) + ':' + PORT + '/?key=' + TOKEN;
|
||||
}
|
||||
|
||||
function browserLauncherForPlatform(url, {
|
||||
platform = process.platform,
|
||||
osRelease = require('os').release(),
|
||||
env = process.env
|
||||
} = {}) {
|
||||
const isWSL = platform === 'linux' && /microsoft/i.test(osRelease);
|
||||
if (platform === 'darwin') return { bin: 'open', args: [url] };
|
||||
if (platform === 'win32' || isWSL) {
|
||||
return { bin: 'rundll32.exe', args: ['url.dll,FileProtocolHandler', url] };
|
||||
}
|
||||
if (env.DISPLAY || env.WAYLAND_DISPLAY) return { bin: 'xdg-open', args: [url] };
|
||||
return null;
|
||||
}
|
||||
|
||||
function isRegularFileInsideContentDir(filePath) {
|
||||
let stat, realContentDir, realFilePath;
|
||||
try {
|
||||
stat = fs.lstatSync(filePath);
|
||||
if (stat.isSymbolicLink()) return false;
|
||||
if (!stat.isFile()) return false;
|
||||
if (stat.nlink !== 1) return false;
|
||||
realContentDir = fs.realpathSync(CONTENT_DIR);
|
||||
realFilePath = fs.realpathSync(filePath);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return realFilePath.startsWith(realContentDir + path.sep);
|
||||
}
|
||||
|
||||
// ========== Authentication ==========
|
||||
|
||||
function timingSafeEqualStr(a, b) {
|
||||
const ab = Buffer.from(String(a));
|
||||
const bb = Buffer.from(String(b));
|
||||
if (ab.length !== bb.length) return false;
|
||||
return crypto.timingSafeEqual(ab, bb);
|
||||
}
|
||||
|
||||
function parseCookies(header) {
|
||||
const out = {};
|
||||
if (!header) return out;
|
||||
for (const part of header.split(';')) {
|
||||
const eq = part.indexOf('=');
|
||||
if (eq < 0) continue;
|
||||
out[part.slice(0, eq).trim()] = part.slice(eq + 1).trim();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// A request is authorized if it carries the session key as ?key= or as the
|
||||
// session cookie. Both are compared in constant time.
|
||||
function isAuthorized(req) {
|
||||
const q = req.url.indexOf('?');
|
||||
if (q >= 0) {
|
||||
const params = new URLSearchParams(req.url.slice(q + 1));
|
||||
if (params.has('key')) {
|
||||
const key = params.get('key');
|
||||
return Boolean(key && timingSafeEqualStr(key, TOKEN));
|
||||
}
|
||||
}
|
||||
const cookie = parseCookies(req.headers['cookie'])[COOKIE_NAME];
|
||||
if (cookie && timingSafeEqualStr(cookie, TOKEN)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function pathnameOf(url) {
|
||||
const q = url.indexOf('?');
|
||||
return q >= 0 ? url.slice(0, q) : url;
|
||||
}
|
||||
|
||||
function queryKey(url) {
|
||||
const q = url.indexOf('?');
|
||||
if (q < 0) return null;
|
||||
return new URLSearchParams(url.slice(q + 1)).get('key');
|
||||
}
|
||||
|
||||
function securityHeaders(headers = {}) {
|
||||
return {
|
||||
'Referrer-Policy': 'no-referrer',
|
||||
'Cache-Control': 'no-store',
|
||||
'X-Frame-Options': 'DENY',
|
||||
'Content-Security-Policy': "frame-ancestors 'none'",
|
||||
'Cross-Origin-Resource-Policy': 'same-origin',
|
||||
...headers
|
||||
};
|
||||
}
|
||||
|
||||
function isAllowedWebSocketOrigin(req) {
|
||||
const origin = req.headers.origin;
|
||||
if (!origin) return true;
|
||||
const host = req.headers.host;
|
||||
if (!host) return false;
|
||||
return origin === 'http://' + host;
|
||||
}
|
||||
|
||||
// ========== HTTP Request Handler ==========
|
||||
|
||||
function handleRequest(req, res) {
|
||||
if (!isAuthorized(req)) {
|
||||
res.writeHead(403, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' }));
|
||||
res.end(FORBIDDEN_PAGE);
|
||||
return;
|
||||
}
|
||||
touchActivity(); // only authorized requests count as activity
|
||||
|
||||
// Mirror the key into a cookie so same-origin subresources (/files/*) can
|
||||
// authenticate after bootstrap. HttpOnly keeps it away from page scripts; the
|
||||
// WebSocket Origin check below is what blocks cross-origin localhost injection.
|
||||
res.setHeader('Set-Cookie',
|
||||
COOKIE_NAME + '=' + TOKEN + '; HttpOnly; SameSite=Strict; Path=/');
|
||||
|
||||
const pathname = pathnameOf(req.url);
|
||||
const keyFromQuery = queryKey(req.url);
|
||||
if (req.method === 'GET' && pathname === '/' && keyFromQuery && timingSafeEqualStr(keyFromQuery, TOKEN)) {
|
||||
res.writeHead(200, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' }));
|
||||
res.end(bootstrapPage(keyFromQuery));
|
||||
} else if (req.method === 'GET' && pathname === '/') {
|
||||
const screenFile = getNewestScreen();
|
||||
let html = screenFile
|
||||
? (raw => isFullDocument(raw) ? raw : wrapInFrame(raw))(fs.readFileSync(screenFile, 'utf-8'))
|
||||
: waitingPage();
|
||||
|
||||
if (html.includes('</body>')) {
|
||||
html = html.replace('</body>', helperInjection + '\n</body>');
|
||||
} else {
|
||||
html += helperInjection;
|
||||
}
|
||||
|
||||
res.writeHead(200, securityHeaders({ 'Content-Type': 'text/html; charset=utf-8' }));
|
||||
res.end(html);
|
||||
} else if (req.method === 'GET' && pathname.startsWith('/files/')) {
|
||||
const fileName = path.basename(pathname.slice(7));
|
||||
const filePath = path.join(CONTENT_DIR, fileName);
|
||||
// Reject empty/dotfile names and anything that isn't a regular file —
|
||||
// `/files/` would otherwise resolve to CONTENT_DIR and crash readFileSync (EISDIR).
|
||||
if (!fileName || fileName.startsWith('.') || !isRegularFileInsideContentDir(filePath)) {
|
||||
res.writeHead(404, securityHeaders());
|
||||
res.end('Not found');
|
||||
return;
|
||||
}
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
const contentType = MIME_TYPES[ext] || 'application/octet-stream';
|
||||
res.writeHead(200, securityHeaders({ 'Content-Type': contentType }));
|
||||
res.end(fs.readFileSync(filePath));
|
||||
} else {
|
||||
res.writeHead(404, securityHeaders());
|
||||
res.end('Not found');
|
||||
}
|
||||
}
|
||||
|
||||
// ========== WebSocket Connection Handling ==========
|
||||
|
||||
const clients = new Set();
|
||||
|
||||
function handleUpgrade(req, socket) {
|
||||
if (!isAuthorized(req) || !isAllowedWebSocketOrigin(req)) { socket.destroy(); return; }
|
||||
|
||||
const key = req.headers['sec-websocket-key'];
|
||||
if (!key) { socket.destroy(); return; }
|
||||
|
||||
const accept = computeAcceptKey(key);
|
||||
socket.write(
|
||||
'HTTP/1.1 101 Switching Protocols\r\n' +
|
||||
'Upgrade: websocket\r\n' +
|
||||
'Connection: Upgrade\r\n' +
|
||||
'Sec-WebSocket-Accept: ' + accept + '\r\n\r\n'
|
||||
);
|
||||
|
||||
let buffer = Buffer.alloc(0);
|
||||
clients.add(socket);
|
||||
|
||||
socket.on('data', (chunk) => {
|
||||
buffer = Buffer.concat([buffer, chunk]);
|
||||
while (buffer.length > 0) {
|
||||
let result;
|
||||
try {
|
||||
result = decodeFrame(buffer);
|
||||
} catch (e) {
|
||||
socket.end(encodeFrame(OPCODES.CLOSE, Buffer.alloc(0)));
|
||||
clients.delete(socket);
|
||||
return;
|
||||
}
|
||||
if (!result) break;
|
||||
buffer = buffer.slice(result.bytesConsumed);
|
||||
|
||||
switch (result.opcode) {
|
||||
case OPCODES.TEXT:
|
||||
handleMessage(result.payload.toString());
|
||||
break;
|
||||
case OPCODES.CLOSE:
|
||||
socket.end(encodeFrame(OPCODES.CLOSE, Buffer.alloc(0)));
|
||||
clients.delete(socket);
|
||||
return;
|
||||
case OPCODES.PING:
|
||||
socket.write(encodeFrame(OPCODES.PONG, result.payload));
|
||||
break;
|
||||
case OPCODES.PONG:
|
||||
break;
|
||||
default: {
|
||||
const closeBuf = Buffer.alloc(2);
|
||||
closeBuf.writeUInt16BE(1003);
|
||||
socket.end(encodeFrame(OPCODES.CLOSE, closeBuf));
|
||||
clients.delete(socket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('close', () => clients.delete(socket));
|
||||
socket.on('error', () => clients.delete(socket));
|
||||
}
|
||||
|
||||
function handleMessage(text) {
|
||||
let event;
|
||||
try {
|
||||
event = JSON.parse(text);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse WebSocket message:', e.message);
|
||||
return;
|
||||
}
|
||||
touchActivity();
|
||||
console.log(JSON.stringify({ source: 'user-event', ...event }));
|
||||
if (event && event.choice) {
|
||||
const eventsFile = path.join(STATE_DIR, 'events');
|
||||
fs.appendFileSync(eventsFile, JSON.stringify(event) + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
function broadcast(msg) {
|
||||
const frame = encodeFrame(OPCODES.TEXT, Buffer.from(JSON.stringify(msg)));
|
||||
for (const socket of clients) {
|
||||
try { socket.write(frame); } catch (e) { clients.delete(socket); }
|
||||
}
|
||||
}
|
||||
|
||||
// Best-effort: open the user's browser the first time a screen is actually ready
|
||||
// to show. Skips when disabled, on a non-loopback (remote) bind, or when a
|
||||
// browser is already connected. Override the launcher with BRAINSTORM_OPEN_CMD.
|
||||
let browserOpened = false;
|
||||
function maybeOpenBrowser() {
|
||||
if (browserOpened) return;
|
||||
browserOpened = true;
|
||||
if (!process.env.BRAINSTORM_OPEN) return; // opt-in: only after the user approves the companion
|
||||
if (HOST !== '127.0.0.1' && HOST !== 'localhost') return;
|
||||
if (clients.size > 0) return; // the user already opened it
|
||||
const url = companionUrl(); // must carry the key or the gate 403s it
|
||||
const cp = require('child_process');
|
||||
// Operator-provided launcher: run as given (this env var is trusted operator input).
|
||||
if (process.env.BRAINSTORM_OPEN_CMD) {
|
||||
try { cp.exec(process.env.BRAINSTORM_OPEN_CMD + ' ' + JSON.stringify(url), () => {}); } catch (e) { /* best effort */ }
|
||||
return;
|
||||
}
|
||||
// Platform launchers: pass the URL as an argv element via execFile (no shell),
|
||||
// so a url-host containing shell metacharacters can't inject a command.
|
||||
const launcher = browserLauncherForPlatform(url);
|
||||
if (!launcher) return; // headless: nothing to open
|
||||
try { cp.execFile(launcher.bin, launcher.args, () => {}); } catch (e) { /* best effort */ }
|
||||
}
|
||||
|
||||
// ========== Activity Tracking ==========
|
||||
|
||||
// Idle timeout: shut down after this long with no activity. Default 4 hours;
|
||||
// override with BRAINSTORM_IDLE_TIMEOUT_MS (start-server.sh: --idle-timeout-minutes).
|
||||
const IDLE_TIMEOUT_MS = (() => {
|
||||
const ms = Number(process.env.BRAINSTORM_IDLE_TIMEOUT_MS);
|
||||
return Number.isFinite(ms) && ms > 0 ? ms : 4 * 60 * 60 * 1000;
|
||||
})();
|
||||
// How often the watchdog checks for owner-death / idleness. Configurable mainly
|
||||
// so tests can run fast; production default is 60s.
|
||||
const LIFECYCLE_CHECK_MS = (() => {
|
||||
const ms = Number(process.env.BRAINSTORM_LIFECYCLE_CHECK_MS);
|
||||
return Number.isFinite(ms) && ms > 0 ? ms : 60 * 1000;
|
||||
})();
|
||||
let lastActivity = Date.now();
|
||||
|
||||
function touchActivity() {
|
||||
lastActivity = Date.now();
|
||||
}
|
||||
|
||||
// ========== File Watching ==========
|
||||
|
||||
const debounceTimers = new Map();
|
||||
|
||||
// ========== Server Startup ==========
|
||||
|
||||
function startServer() {
|
||||
if (!fs.existsSync(CONTENT_DIR)) fs.mkdirSync(CONTENT_DIR, { recursive: true });
|
||||
if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true });
|
||||
|
||||
// Track known files to distinguish new screens from updates.
|
||||
// macOS fs.watch reports 'rename' for both new files and overwrites,
|
||||
// so we can't rely on eventType alone.
|
||||
const knownFiles = new Set(
|
||||
fs.readdirSync(CONTENT_DIR).filter(f => !f.startsWith('.') && f.endsWith('.html'))
|
||||
);
|
||||
|
||||
const server = http.createServer(handleRequest);
|
||||
server.on('upgrade', handleUpgrade);
|
||||
|
||||
const watcher = fs.watch(CONTENT_DIR, (eventType, filename) => {
|
||||
if (!filename || filename.startsWith('.') || !filename.endsWith('.html')) return;
|
||||
|
||||
if (debounceTimers.has(filename)) clearTimeout(debounceTimers.get(filename));
|
||||
debounceTimers.set(filename, setTimeout(() => {
|
||||
debounceTimers.delete(filename);
|
||||
const filePath = path.join(CONTENT_DIR, filename);
|
||||
|
||||
if (!fs.existsSync(filePath)) return; // file was deleted
|
||||
touchActivity();
|
||||
|
||||
if (!knownFiles.has(filename)) {
|
||||
knownFiles.add(filename);
|
||||
const eventsFile = path.join(STATE_DIR, 'events');
|
||||
if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile);
|
||||
console.log(JSON.stringify({ type: 'screen-added', file: filePath }));
|
||||
maybeOpenBrowser();
|
||||
} else {
|
||||
console.log(JSON.stringify({ type: 'screen-updated', file: filePath }));
|
||||
}
|
||||
|
||||
broadcast({ type: 'reload' });
|
||||
}, 100));
|
||||
});
|
||||
watcher.on('error', (err) => console.error('fs.watch error:', err.message));
|
||||
|
||||
function shutdown(reason) {
|
||||
console.log(JSON.stringify({ type: 'server-stopped', reason }));
|
||||
const infoFile = path.join(STATE_DIR, 'server-info');
|
||||
if (fs.existsSync(infoFile)) fs.unlinkSync(infoFile);
|
||||
fs.writeFileSync(
|
||||
path.join(STATE_DIR, 'server-stopped'),
|
||||
JSON.stringify({ reason, timestamp: Date.now() }) + '\n'
|
||||
);
|
||||
watcher.close();
|
||||
clearInterval(lifecycleCheck);
|
||||
// Close any upgraded WebSocket sockets so server.close() can complete and
|
||||
// the process actually exits instead of lingering on an open connection.
|
||||
for (const socket of clients) {
|
||||
try { socket.destroy(); } catch (e) { /* already gone */ }
|
||||
}
|
||||
server.close(() => process.exit(0));
|
||||
}
|
||||
|
||||
function ownerAlive() {
|
||||
if (!ownerPid) return true;
|
||||
try { process.kill(ownerPid, 0); return true; } catch (e) { return e.code === 'EPERM'; }
|
||||
}
|
||||
|
||||
// Periodically exit if the owner process died or we've been idle too long.
|
||||
const lifecycleCheck = setInterval(() => {
|
||||
if (!ownerAlive()) shutdown('owner process exited');
|
||||
else if (Date.now() - lastActivity > IDLE_TIMEOUT_MS) shutdown('idle timeout');
|
||||
}, LIFECYCLE_CHECK_MS);
|
||||
lifecycleCheck.unref();
|
||||
|
||||
// Validate owner PID at startup. If it's already dead, the PID resolution
|
||||
// was wrong (common on WSL, Tailscale SSH, and cross-user scenarios).
|
||||
// Disable monitoring and rely on the idle timeout instead.
|
||||
if (ownerPid) {
|
||||
try { process.kill(ownerPid, 0); }
|
||||
catch (e) {
|
||||
if (e.code !== 'EPERM') {
|
||||
console.log(JSON.stringify({ type: 'owner-pid-invalid', pid: ownerPid, reason: 'dead at startup' }));
|
||||
ownerPid = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the preferred port is already taken (e.g. a previous server is still
|
||||
// alive), fall back to a random port once instead of failing.
|
||||
let triedFallback = false;
|
||||
|
||||
function onListen() {
|
||||
// Cookie name keys on the ACTUAL bound port (may differ from the preferred
|
||||
// one after an EADDRINUSE fallback) so it can't collide with another server's
|
||||
// cookie in the shared localhost jar.
|
||||
COOKIE_NAME = 'brainstorm-key-' + PORT;
|
||||
// Record the bound port AND token so the next restart of this session reuses
|
||||
// them — but ONLY when we got our preferred port. On a fallback we bound a
|
||||
// *different* port because someone else holds the preferred one; persisting
|
||||
// would overwrite the shared files and strand that other session's open tab.
|
||||
if (PORT_FILE && !triedFallback) {
|
||||
try { fs.writeFileSync(PORT_FILE, String(PORT)); } catch (e) { /* best effort */ }
|
||||
if (TOKEN_FILE) {
|
||||
try {
|
||||
fs.writeFileSync(TOKEN_FILE, TOKEN, { mode: 0o600 });
|
||||
chmodOwnerOnly(TOKEN_FILE);
|
||||
} catch (e) { /* best effort */ }
|
||||
}
|
||||
}
|
||||
const info = JSON.stringify({
|
||||
type: 'server-started', port: Number(PORT), host: HOST,
|
||||
url_host: URL_HOST, url: companionUrl(),
|
||||
screen_dir: CONTENT_DIR, state_dir: STATE_DIR, idle_timeout_ms: IDLE_TIMEOUT_MS
|
||||
});
|
||||
console.log(info);
|
||||
// server-info embeds the key — keep it owner-only.
|
||||
fs.writeFileSync(path.join(STATE_DIR, 'server-info'), info + '\n', { mode: 0o600 });
|
||||
}
|
||||
|
||||
server.on('error', (err) => {
|
||||
if (err.code === 'EADDRINUSE' && !triedFallback) {
|
||||
if (tokenSource === 'env') {
|
||||
console.error('Server failed to bind: preferred port is in use and BRAINSTORM_TOKEN is set; refusing fallback with explicit token');
|
||||
process.exit(1);
|
||||
}
|
||||
triedFallback = true;
|
||||
PORT = randomPort();
|
||||
if (tokenSource === 'file') {
|
||||
TOKEN = generateToken();
|
||||
tokenSource = 'generated-fallback';
|
||||
}
|
||||
server.listen(PORT, HOST, onListen);
|
||||
} else {
|
||||
console.error('Server failed to bind:', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
server.listen(PORT, HOST, onListen);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
startServer();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
computeAcceptKey,
|
||||
encodeFrame,
|
||||
decodeFrame,
|
||||
browserLauncherForPlatform,
|
||||
OPCODES,
|
||||
MAX_FRAME_PAYLOAD_BYTES
|
||||
};
|
||||
@@ -1,209 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Start the brainstorm server and output connection info
|
||||
# Usage: start-server.sh [--project-dir <path>] [--host <bind-host>] [--url-host <display-host>] [--foreground] [--background]
|
||||
#
|
||||
# Starts server on a random high port, outputs JSON with URL.
|
||||
# Each session gets its own directory to avoid conflicts.
|
||||
#
|
||||
# Options:
|
||||
# --project-dir <path> Store session files under <path>/.superpowers/brainstorm/
|
||||
# instead of /tmp. Files persist after server stops.
|
||||
# --host <bind-host> Host/interface to bind (default: 127.0.0.1).
|
||||
# Use 0.0.0.0 in remote/containerized environments.
|
||||
# --url-host <host> Hostname shown in returned URL JSON.
|
||||
# --idle-timeout-minutes <n> Shut down after n minutes idle (default 240 = 4h).
|
||||
# --open Auto-open the browser on the first screen (use only
|
||||
# after the user approves the visual companion).
|
||||
# --foreground Run server in the current terminal (no backgrounding).
|
||||
# --background Force background mode (overrides Codex auto-foreground).
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
# Parse arguments
|
||||
PROJECT_DIR=""
|
||||
FOREGROUND="false"
|
||||
FORCE_BACKGROUND="false"
|
||||
BIND_HOST="127.0.0.1"
|
||||
URL_HOST=""
|
||||
IDLE_TIMEOUT_MINUTES=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--project-dir)
|
||||
PROJECT_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--host)
|
||||
BIND_HOST="$2"
|
||||
shift 2
|
||||
;;
|
||||
--url-host)
|
||||
URL_HOST="$2"
|
||||
shift 2
|
||||
;;
|
||||
--idle-timeout-minutes)
|
||||
IDLE_TIMEOUT_MINUTES="$2"
|
||||
shift 2
|
||||
;;
|
||||
--open)
|
||||
export BRAINSTORM_OPEN=1
|
||||
shift
|
||||
;;
|
||||
--foreground|--no-daemon)
|
||||
FOREGROUND="true"
|
||||
shift
|
||||
;;
|
||||
--background|--daemon)
|
||||
FORCE_BACKGROUND="true"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "{\"error\": \"Unknown argument: $1\"}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$URL_HOST" ]]; then
|
||||
if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then
|
||||
URL_HOST="localhost"
|
||||
else
|
||||
URL_HOST="$BIND_HOST"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$IDLE_TIMEOUT_MINUTES" ]]; then
|
||||
if ! [[ "$IDLE_TIMEOUT_MINUTES" =~ ^[0-9]+$ ]] || [[ "$IDLE_TIMEOUT_MINUTES" -lt 1 ]]; then
|
||||
echo "{\"error\": \"--idle-timeout-minutes must be a positive integer\"}"
|
||||
exit 1
|
||||
fi
|
||||
export BRAINSTORM_IDLE_TIMEOUT_MS=$(( IDLE_TIMEOUT_MINUTES * 60 * 1000 ))
|
||||
fi
|
||||
|
||||
is_windows_like_shell() {
|
||||
case "${OSTYPE:-}" in
|
||||
msys*|cygwin*|mingw*) return 0 ;;
|
||||
esac
|
||||
if [[ -n "${MSYSTEM:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
local uname_s
|
||||
uname_s="$(uname -s 2>/dev/null || true)"
|
||||
case "$uname_s" in
|
||||
MSYS*|MINGW*|CYGWIN*) return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
# Some environments reap detached/background processes. Auto-foreground when detected.
|
||||
if [[ -n "${CODEX_CI:-}" && "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
|
||||
FOREGROUND="true"
|
||||
fi
|
||||
|
||||
# Windows/Git Bash reaps nohup background processes. Auto-foreground when detected.
|
||||
if [[ "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
|
||||
if is_windows_like_shell; then
|
||||
FOREGROUND="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Session files (server.log, server-info, .last-token) embed the session key —
|
||||
# keep everything this script and the server create owner-only.
|
||||
umask 077
|
||||
|
||||
# Generate unique session directory
|
||||
SESSION_ID="$$-$(date +%s)"
|
||||
|
||||
if [[ -n "$PROJECT_DIR" ]]; then
|
||||
SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
|
||||
# Persist the bound port and key per project so a restart reuses them and an
|
||||
# already-open browser tab reconnects to the same URL with a valid cookie.
|
||||
export BRAINSTORM_PORT_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-port"
|
||||
export BRAINSTORM_TOKEN_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-token"
|
||||
else
|
||||
SESSION_DIR="/tmp/brainstorm-${SESSION_ID}"
|
||||
fi
|
||||
|
||||
STATE_DIR="${SESSION_DIR}/state"
|
||||
PID_FILE="${STATE_DIR}/server.pid"
|
||||
LOG_FILE="${STATE_DIR}/server.log"
|
||||
SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
|
||||
|
||||
# Create fresh session directory with content and state peers
|
||||
mkdir -p "${SESSION_DIR}/content" "$STATE_DIR"
|
||||
|
||||
SERVER_ID=""
|
||||
if [[ -r /dev/urandom ]]; then
|
||||
SERVER_ID="$(od -An -N24 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || true)"
|
||||
fi
|
||||
if ! [[ "$SERVER_ID" =~ ^[A-Za-z0-9_-]{32,64}$ ]]; then
|
||||
SERVER_ID="$(printf '%08x%08x%08x%08x' "$$" "$(date +%s)" "${RANDOM:-0}" "${RANDOM:-0}")"
|
||||
fi
|
||||
printf '%s\n' "$SERVER_ID" > "$SERVER_ID_FILE"
|
||||
chmod 600 "$SERVER_ID_FILE" 2>/dev/null || true
|
||||
|
||||
# Kill any existing server
|
||||
if [[ -f "$PID_FILE" ]]; then
|
||||
old_pid=$(cat "$PID_FILE")
|
||||
kill "$old_pid" 2>/dev/null
|
||||
rm -f "$PID_FILE"
|
||||
fi
|
||||
|
||||
cd "$SCRIPT_DIR" || exit 1
|
||||
|
||||
# Resolve the harness PID (grandparent of this script).
|
||||
# $PPID is the ephemeral shell the harness spawned to run us — it dies
|
||||
# when this script exits. The harness itself is $PPID's parent.
|
||||
OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ')"
|
||||
if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then
|
||||
OWNER_PID="$PPID"
|
||||
fi
|
||||
|
||||
# Windows/MSYS2: Node.js cannot see POSIX PIDs from the MSYS2 namespace.
|
||||
# Passing a PID node cannot verify causes server to log owner-pid-invalid
|
||||
# and self-terminate at the 60-second lifecycle check. Clear it so the
|
||||
# watchdog is disabled and the idle timeout becomes the only shutdown trigger.
|
||||
if is_windows_like_shell; then
|
||||
OWNER_PID=""
|
||||
fi
|
||||
|
||||
# Foreground mode for environments that reap detached/background processes.
|
||||
if [[ "$FOREGROUND" == "true" ]]; then
|
||||
env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" &
|
||||
SERVER_PID=$!
|
||||
echo "$SERVER_PID" > "$PID_FILE"
|
||||
wait "$SERVER_PID"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# Start server, capturing output to log file
|
||||
# Use nohup to survive shell exit; disown to remove from job table
|
||||
nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" > "$LOG_FILE" 2>&1 &
|
||||
SERVER_PID=$!
|
||||
disown "$SERVER_PID" 2>/dev/null
|
||||
echo "$SERVER_PID" > "$PID_FILE"
|
||||
|
||||
# Wait for server-started message (check log file)
|
||||
for _ in {1..50}; do
|
||||
if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
|
||||
# Verify server is still alive after a short window (catches process reapers)
|
||||
alive="true"
|
||||
for _ in {1..20}; do
|
||||
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
||||
alive="false"
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
if [[ "$alive" != "true" ]]; then
|
||||
echo "{\"error\": \"Server started but was killed. Retry in a persistent terminal with: $SCRIPT_DIR/start-server.sh${PROJECT_DIR:+ --project-dir $PROJECT_DIR} --host $BIND_HOST --url-host $URL_HOST --foreground\"}"
|
||||
exit 1
|
||||
fi
|
||||
grep "server-started" "$LOG_FILE" | head -1
|
||||
exit 0
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# Timeout - server didn't start
|
||||
echo '{"error": "Server failed to start within 5 seconds"}'
|
||||
exit 1
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Stop the brainstorm server and clean up
|
||||
# Usage: stop-server.sh <session_dir>
|
||||
#
|
||||
# Kills the server process. Only deletes session directory if it's
|
||||
# under /tmp (ephemeral). Persistent directories (.superpowers/) are
|
||||
# kept so mockups can be reviewed later.
|
||||
|
||||
SESSION_DIR="$1"
|
||||
|
||||
if [[ -z "$SESSION_DIR" ]]; then
|
||||
echo '{"error": "Usage: stop-server.sh <session_dir>"}'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STATE_DIR="${SESSION_DIR}/state"
|
||||
PID_FILE="${STATE_DIR}/server.pid"
|
||||
SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
|
||||
|
||||
mark_stopped() {
|
||||
local reason="$1"
|
||||
rm -f "${STATE_DIR}/server-info"
|
||||
printf '{"reason":"%s","timestamp":%s}\n' "$reason" "$(date +%s)" > "${STATE_DIR}/server-stopped"
|
||||
}
|
||||
|
||||
read_expected_server_id() {
|
||||
[[ -f "$SERVER_ID_FILE" ]] || return 1
|
||||
local id
|
||||
id="$(tr -d '\r\n' < "$SERVER_ID_FILE" 2>/dev/null || true)"
|
||||
[[ "$id" =~ ^[A-Za-z0-9_-]{32,64}$ ]] || return 1
|
||||
printf '%s\n' "$id"
|
||||
}
|
||||
|
||||
command_line_for_pid() {
|
||||
local pid="$1"
|
||||
if [[ -r "/proc/$pid/cmdline" ]]; then
|
||||
tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true
|
||||
return 0
|
||||
fi
|
||||
ps -ww -p "$pid" -o command= 2>/dev/null || ps -f -p "$pid" 2>/dev/null | sed '1d' || true
|
||||
}
|
||||
|
||||
command_has_server_id() {
|
||||
local pid="$1"
|
||||
local expected="$2"
|
||||
local expected_arg="--brainstorm-server-id=$expected"
|
||||
if [[ -r "/proc/$pid/cmdline" ]]; then
|
||||
local arg
|
||||
while IFS= read -r -d '' arg || [[ -n "$arg" ]]; do
|
||||
[[ "$arg" == "$expected_arg" ]] && return 0
|
||||
done < "/proc/$pid/cmdline"
|
||||
return 1
|
||||
fi
|
||||
local command_line
|
||||
command_line="$(command_line_for_pid "$pid")"
|
||||
[[ -n "$command_line" ]] || return 1
|
||||
case " $command_line " in
|
||||
*" $expected_arg "*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Confirm a PID has this session's per-start instance id, not just a familiar
|
||||
# process name. Ambiguous or legacy metadata fails closed as stale_pid.
|
||||
is_brainstorm_server() {
|
||||
kill -0 "$1" 2>/dev/null || return 1
|
||||
local expected_id
|
||||
expected_id="$(read_expected_server_id)" || return 1
|
||||
command_has_server_id "$1" "$expected_id" || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ -f "$PID_FILE" ]]; then
|
||||
pid=$(cat "$PID_FILE")
|
||||
|
||||
# Refuse to signal a PID we can't prove is our server. A stale pid file may
|
||||
# point at an unrelated process after a reboot/PID wraparound.
|
||||
if ! is_brainstorm_server "$pid"; then
|
||||
rm -f "$PID_FILE" "$SERVER_ID_FILE"
|
||||
mark_stopped "stale_pid"
|
||||
echo '{"status": "stale_pid"}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Try to stop gracefully, fallback to force if still alive
|
||||
kill "$pid" 2>/dev/null || true
|
||||
|
||||
# Wait for graceful shutdown (up to ~2s)
|
||||
for _ in {1..20}; do
|
||||
if ! kill -0 "$pid" 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# If still running, escalate to SIGKILL
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
|
||||
# Give SIGKILL a moment to take effect
|
||||
sleep 0.1
|
||||
fi
|
||||
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo '{"status": "failed", "error": "process still running"}'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$PID_FILE" "$SERVER_ID_FILE" "${STATE_DIR}/server.log"
|
||||
mark_stopped "stop-server.sh"
|
||||
|
||||
# Only delete ephemeral /tmp directories
|
||||
if [[ "$SESSION_DIR" == /tmp/* ]]; then
|
||||
rm -rf "$SESSION_DIR"
|
||||
fi
|
||||
|
||||
echo '{"status": "stopped"}'
|
||||
else
|
||||
echo '{"status": "not_running"}'
|
||||
fi
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
# 规范文档审查员提示词模板
|
||||
|
||||
派遣规范文档审查员子 Agent 时,请使用此模板。
|
||||
|
||||
**目的:** 验证规范是否完整、一致,并已准备好进入实施规划阶段。
|
||||
|
||||
**派遣时机:** 规范文档已写入 docs/superpowers/specs/ 后
|
||||
|
||||
```
|
||||
子 Agent (general-purpose):
|
||||
description: "审查规范文档"
|
||||
prompt: |
|
||||
你是一名规范文档审查员。请验证此规范是否完整并已准备好进入规划阶段。
|
||||
|
||||
**待审查的规范:** [SPEC_FILE_PATH]
|
||||
|
||||
## 检查内容
|
||||
|
||||
| 类别 | 检查要点 |
|
||||
|----------|------------------|
|
||||
| 完整性 | TODOs、占位符、"TBD"、不完整的章节 |
|
||||
| 一致性 | 内部矛盾、相互冲突的要求 |
|
||||
| 清晰度 | 要求是否含糊到足以导致他人构建出错误的内容 |
|
||||
| 范围 | 是否足够聚焦,可由单个计划涵盖——而不是覆盖多个相互独立的子系统 |
|
||||
| YAGNI | 未请求的功能、过度工程化 |
|
||||
|
||||
## 校准
|
||||
|
||||
**只标记会在实施规划期间造成实际问题的问题。**
|
||||
缺少章节、存在矛盾,或某项要求含糊到可能被解读为两种不同的含义——
|
||||
这些才是问题。轻微的措辞改进、风格偏好以及“某些章节不如其他章节详细”均不属于问题。
|
||||
|
||||
除非存在会导致计划有缺陷的严重缺漏,否则应予以批准。
|
||||
|
||||
## 输出格式
|
||||
|
||||
## 规范审查
|
||||
|
||||
**状态:** 通过 | 发现问题
|
||||
|
||||
**问题(如有):**
|
||||
- [第 X 节]:[具体问题] — [它为何会影响规划]
|
||||
|
||||
**建议(仅供参考,不阻碍批准):**
|
||||
- [改进建议]
|
||||
```
|
||||
|
||||
**审查员返回:** 状态、问题(如有)、建议
|
||||
@@ -1,288 +0,0 @@
|
||||
# 可视化伴侣指南
|
||||
|
||||
基于浏览器的可视化头脑风暴伴侣,用于展示模型稿、图示和选项。
|
||||
|
||||
## 何时使用
|
||||
|
||||
针对每个问题而非每个会话进行决定。判断标准是:**用户通过看它而不是阅读它,是否能理解得更好?**
|
||||
|
||||
当内容本身是可视化内容时,**使用浏览器**:
|
||||
|
||||
- **UI 模型稿**——线框图、布局、导航结构、组件设计
|
||||
- **架构图**——系统组件、数据流、关系图
|
||||
- **并排视觉比较**——比较两种布局、两套配色方案、两个设计方向
|
||||
- **设计润色**——当问题涉及观感、间距、视觉层级时
|
||||
- **空间关系**——以图示形式呈现的状态机、流程图、实体关系
|
||||
|
||||
当内容是文本或表格时,**使用终端**:
|
||||
|
||||
- **需求和范围问题**——“X 是什么意思?”、“哪些功能在范围内?”
|
||||
- **概念性 A/B/C 选择**——在用文字描述的方案之间进行选择
|
||||
- **权衡清单**——优缺点、比较表
|
||||
- **技术决策**——API 设计、数据建模、架构方案选择
|
||||
- **澄清问题**——任何答案是文字而非视觉偏好的问题
|
||||
|
||||
一个*关于* UI 主题的问题并不自动等同于视觉问题。“你想要哪种向导?”是概念性问题——使用终端。“这些向导布局中,哪一种感觉合适?”是视觉问题——使用浏览器。
|
||||
|
||||
## 工作原理
|
||||
|
||||
服务器会监视一个目录中的 HTML 文件,并将最新的文件提供给浏览器。你将 HTML 内容写入 `screen_dir`,用户会在其浏览器中看到内容,并可通过点击选择选项。选择结果会记录到 `state_dir/events`,供你在下一轮读取。
|
||||
|
||||
**内容片段与完整文档:**如果你的 HTML 文件以 `<!DOCTYPE` 或 `<html` 开头,服务器会按原样提供该文件(仅注入辅助脚本)。否则,服务器会自动将你的内容包装在框架模板中——添加页眉、CSS 主题、连接状态以及所有交互基础设施。**默认编写内容片段。**仅在需要完全控制页面时编写完整文档。
|
||||
|
||||
## 启动会话
|
||||
|
||||
```bash
|
||||
# 请在用户批准使用伴侣之后再启动。--open 会在显示首个屏幕时自动打开用户的浏览器;
|
||||
# --project-dir 会持久保存模型稿,并支持使用同一端口重启。
|
||||
scripts/start-server.sh --project-dir /path/to/project --open
|
||||
|
||||
# 返回:{"type":"server-started","port":52341,
|
||||
# "url":"http://localhost:52341/?key=ab12…",
|
||||
# "screen_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000/content",
|
||||
# "state_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000/state"}
|
||||
```
|
||||
|
||||
保存响应中的 `screen_dir` 和 `state_dir`。使用 `--open` 时,当你推送首个屏幕,浏览器会自动打开——你不需要让用户手动打开,但仍应分享该 URL 作为备用方案(无头/远程环境不会自动打开)。
|
||||
|
||||
**该 URL 包含会话密钥(`?key=…`)。**服务器会拒绝任何不含该密钥的请求,因此始终向用户提供 `url` 字段中的**完整** URL——绝不要移除查询字符串,也绝不要提供不带查询字符串的 `http://host:port`。该密钥控制 HTTP 和 WebSocket 访问,因此意外打开的浏览器标签页或网络上的其他机器无法读取屏幕或注入事件。首次加载后,浏览器会通过 cookie 记住该密钥,因此重新加载以及访问 `/files/*` 资源时无需再次提供它。
|
||||
|
||||
**查找连接信息:**服务器会将其启动 JSON 写入 `$STATE_DIR/server-info`。如果你在后台启动了服务器但没有捕获 stdout,请读取该文件以获取 URL 和端口。使用 `--project-dir` 时,请在 `<project>/.superpowers/brainstorm/` 中查找会话目录。
|
||||
|
||||
**注意:**将项目根目录作为 `--project-dir` 传入,以便模型稿持久保存在 `.superpowers/brainstorm/` 中,并在服务器重启后继续保留。如果不这样做,文件会写入 `/tmp` 并被清理。如果 `.superpowers/` 尚未加入 `.gitignore`,请提醒用户添加它。
|
||||
|
||||
**按平台启动服务器:**
|
||||
|
||||
**Claude Code:**
|
||||
```bash
|
||||
# 默认模式即可——脚本会自行将服务器置于后台运行。
|
||||
scripts/start-server.sh --project-dir /path/to/project --open
|
||||
```
|
||||
|
||||
在 Windows 上,脚本会自动检测并切换到前台模式(这会阻塞工具调用)。在 Bash 工具调用中使用 `run_in_background: true`,使服务器能够跨对话轮次持续运行,然后在下一轮读取 `$STATE_DIR/server-info` 以获取 URL 和端口。
|
||||
|
||||
**Codex:**
|
||||
```bash
|
||||
# Codex 会清理后台进程。脚本会自动检测 CODEX_CI 并
|
||||
# 切换到前台模式。正常运行即可——无需额外标志。
|
||||
scripts/start-server.sh --project-dir /path/to/project --open
|
||||
```
|
||||
|
||||
**Gemini CLI:**
|
||||
```bash
|
||||
# 使用 --foreground,并在 shell 工具调用中设置 is_background: true,
|
||||
# 使进程能够跨轮次持续运行
|
||||
scripts/start-server.sh --project-dir /path/to/project --open --foreground
|
||||
```
|
||||
|
||||
**Copilot CLI:**
|
||||
```bash
|
||||
# 使用 Copilot CLI 的非阻塞/后台 shell 机制启动,使服务器跨轮次持续运行。
|
||||
# 保留 --foreground,让运行平台而不是脚本负责后台化。启动脚本是 .sh,
|
||||
# 因此通过 bash 调用(Windows 上从 PowerShell 工具调用 Git Bash 的 bash.exe)。
|
||||
bash scripts/start-server.sh --project-dir /path/to/project --open --foreground
|
||||
```
|
||||
|
||||
**其他环境:**服务器必须在后台跨对话轮次持续运行。如果你的环境会清理已分离的进程,请使用 `--foreground`,并通过你所在平台的后台执行机制启动该命令。
|
||||
|
||||
如果你的浏览器无法访问该 URL(这在远程/容器化环境中很常见),请绑定非环回主机:
|
||||
|
||||
```bash
|
||||
scripts/start-server.sh \
|
||||
--project-dir /path/to/project \
|
||||
--host 0.0.0.0 \
|
||||
--url-host localhost
|
||||
```
|
||||
|
||||
使用 `--url-host` 控制返回的 URL JSON 中输出的主机名。
|
||||
|
||||
## 循环
|
||||
1. **检查服务器是否仍在运行**,然后**将 HTML 写入** `screen_dir` 中的新文件:
|
||||
- **必需:在提及 URL 或推送界面之前,确认服务器仍在运行。** 检查 `$STATE_DIR/server-info` 是否存在,并且 `$STATE_DIR/server-stopped` 不存在。如果服务器已关闭,请使用**相同的 `--project-dir`** 通过 `start-server.sh` 重新启动它——它会复用相同的端口,因此用户已打开的标签页会自行重新连接(服务器停机期间会显示“已暂停”遮罩层),你无需发送新的 URL。服务器空闲 4 小时后会自动退出(可通过 `--idle-timeout-minutes` 配置)。
|
||||
- 使用语义化文件名:`platform.html`、`visual-style.html`、`layout.html`
|
||||
- **绝不要重复使用文件名**——每个界面都使用一个全新的文件
|
||||
- 使用你的文件创建工具——**绝不要使用 cat/heredoc**(会向终端倾倒大量杂乱信息)
|
||||
- 服务器会自动提供最新的文件
|
||||
|
||||
2. **告诉用户会看到什么,然后结束你的回合:**
|
||||
- 每一步都提醒他们 URL(而不只是第一步)
|
||||
- 简要概括界面上显示的内容(例如,“正在展示主页的 3 种布局选项”)
|
||||
- 请他们在终端中回复:“看一下,然后告诉我你的想法。如果愿意,请点击选择一个选项。”
|
||||
|
||||
3. **在你的下一个回合中**——用户在终端中回复后:
|
||||
- 如果 `$STATE_DIR/events` 存在,则读取它——其中以 JSON 行的形式包含用户的浏览器交互
|
||||
- 将其与用户的终端文本合并,以了解完整情况
|
||||
- 终端消息是主要反馈;`state_dir/events` 提供结构化交互数据
|
||||
|
||||
4. **迭代或推进**——如果反馈会改变当前界面,则写入一个新文件(例如 `layout-v2.html`)。只有当前步骤得到验证后,才能进入下一个问题。
|
||||
|
||||
5. **返回终端时卸载内容**——当下一步不需要浏览器时(例如,提出澄清问题、讨论权衡取舍),推送一个等待界面以清除过时内容:
|
||||
|
||||
```html
|
||||
<!-- 文件名:waiting.html(或 waiting-2.html 等) -->
|
||||
<div style="display:flex;align-items:center;justify-content:center;min-height:60vh">
|
||||
<p class="subtitle">继续在终端中...</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
这样可避免在对话已经继续推进后,用户仍盯着一个已经解决的选择。当下一个可视化问题出现时,像往常一样推送一个新的内容文件。
|
||||
|
||||
6. 重复上述步骤,直到完成。
|
||||
|
||||
## 编写内容片段
|
||||
|
||||
只编写放入页面内部的内容。服务器会自动使用框架模板将其包装起来(页眉、主题 CSS、连接状态以及所有交互基础设施)。
|
||||
|
||||
**最小示例:**
|
||||
|
||||
```html
|
||||
<h2>哪种布局更合适?</h2>
|
||||
<p class="subtitle">请考虑可读性和视觉层级</p>
|
||||
|
||||
<div class="options">
|
||||
<div class="option" data-choice="a" onclick="toggleSelect(this)">
|
||||
<div class="letter">A</div>
|
||||
<div class="content">
|
||||
<h3>单栏</h3>
|
||||
<p>简洁、专注的阅读体验</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="option" data-choice="b" onclick="toggleSelect(this)">
|
||||
<div class="letter">B</div>
|
||||
<div class="content">
|
||||
<h3>双栏</h3>
|
||||
<p>侧边栏导航搭配主内容区</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
就这些。不需要 `<html>`、CSS,也不需要 `<script>` 标签。服务器会提供所有这些内容。
|
||||
|
||||
## 可用的 CSS 类
|
||||
框架模板为你的内容提供以下 CSS 类:
|
||||
|
||||
### 选项(A/B/C 选择)
|
||||
|
||||
```html
|
||||
<div class="options">
|
||||
<div class="option" data-choice="a" onclick="toggleSelect(this)">
|
||||
<div class="letter">A</div>
|
||||
<div class="content">
|
||||
<h3>标题</h3>
|
||||
<p>描述</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**多选:**向容器添加 `data-multiselect`,以允许用户选择多个选项。每次点击都会切换该项目的选中样式。
|
||||
|
||||
```html
|
||||
<div class="options" data-multiselect>
|
||||
<!-- 相同的选项标记——用户可以选择/取消选择多个选项 -->
|
||||
</div>
|
||||
```
|
||||
|
||||
### 卡片(视觉设计)
|
||||
|
||||
```html
|
||||
<div class="cards">
|
||||
<div class="card" data-choice="design1" onclick="toggleSelect(this)">
|
||||
<div class="card-image"><!-- 模型内容 --></div>
|
||||
<div class="card-body">
|
||||
<h3>名称</h3>
|
||||
<p>描述</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 模型容器
|
||||
```html
|
||||
<div class="mockup">
|
||||
<div class="mockup-header">预览:仪表板布局</div>
|
||||
<div class="mockup-body"><!-- 你的样稿 HTML --></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 拆分视图(并排)
|
||||
|
||||
```html
|
||||
<div class="split">
|
||||
<div class="mockup"><!-- 左侧 --></div>
|
||||
<div class="mockup"><!-- 右侧 --></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 优点/缺点
|
||||
|
||||
```html
|
||||
<div class="pros-cons">
|
||||
<div class="pros"><h4>优点</h4><ul><li>益处</li></ul></div>
|
||||
<div class="cons"><h4>缺点</h4><ul><li>弊端</li></ul></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 样稿元素(线框图构建块)
|
||||
|
||||
```html
|
||||
<div class="mock-nav">徽标 | 首页 | 关于 | 联系</div>
|
||||
<div style="display: flex;">
|
||||
<div class="mock-sidebar">导航</div>
|
||||
<div class="mock-content">主要内容区域</div>
|
||||
</div>
|
||||
<button class="mock-button">操作按钮</button>
|
||||
<input class="mock-input" placeholder="输入字段">
|
||||
<div class="placeholder">占位区域</div>
|
||||
```
|
||||
|
||||
### 排版与分区
|
||||
- `h2` — 页面标题
|
||||
- `h3` — 小节标题
|
||||
- `.subtitle` — 标题下方的次要文本
|
||||
- `.section` — 带底部外边距的内容块
|
||||
- `.label` — 小号全大写标签文本
|
||||
|
||||
## 浏览器事件格式
|
||||
|
||||
当用户在浏览器中点击选项时,其交互会被记录到 `$STATE_DIR/events`(每行一个 JSON 对象)。当你推送新屏幕时,该文件会自动清空。
|
||||
|
||||
```jsonl
|
||||
{"type":"click","choice":"a","text":"选项 A - 简单布局","timestamp":1706000101}
|
||||
{"type":"click","choice":"c","text":"选项 C - 复杂网格","timestamp":1706000108}
|
||||
{"type":"click","choice":"b","text":"选项 B - 混合布局","timestamp":1706000115}
|
||||
```
|
||||
|
||||
完整的事件流会显示用户的探索路径——他们可能会先点击多个选项,再最终确定。最后一个 `choice` 事件通常是最终选择,但点击模式可能会体现出犹豫或偏好,值得进一步询问。
|
||||
|
||||
如果 `$STATE_DIR/events` 不存在,则说明用户没有与浏览器交互——仅使用他们在终端中输入的文本。
|
||||
|
||||
## 设计技巧
|
||||
|
||||
- **根据问题调整保真度**——布局问题使用线框图,细节打磨问题使用精细设计
|
||||
- **在每个页面上说明问题**——使用“哪个布局感觉更专业?”,而不只是“选择一个”
|
||||
- **先迭代,再继续推进**——如果反馈会改变当前屏幕,请编写一个新版本
|
||||
- 每个屏幕最多提供 **2-4 个选项**
|
||||
- **在真实内容很重要时使用真实内容**——对于摄影作品集,应使用真实图片(Unsplash)。占位内容会掩盖设计问题。
|
||||
- **保持模型简单**——专注于布局和结构,而不是像素级完美的设计
|
||||
|
||||
## 文件命名
|
||||
|
||||
- 使用语义化名称:`platform.html`、`visual-style.html`、`layout.html`
|
||||
- 切勿重复使用文件名——每个屏幕都必须是一个新文件
|
||||
- 对于迭代版本:附加版本后缀,例如 `layout-v2.html`、`layout-v3.html`
|
||||
- 服务器按修改时间提供最新的文件
|
||||
|
||||
## 清理
|
||||
|
||||
```bash
|
||||
scripts/stop-server.sh $SESSION_DIR
|
||||
```
|
||||
|
||||
如果会话使用了 `--project-dir`,模型文件会保留在 `.superpowers/brainstorm/` 中,以供后续参考。只有 `/tmp` 中的会话会在停止时被删除。
|
||||
|
||||
## 参考
|
||||
|
||||
- 框架模板(CSS 参考):`scripts/frame-template.html`
|
||||
- 辅助脚本(客户端):`scripts/helper.js`
|
||||
@@ -1,167 +0,0 @@
|
||||
---
|
||||
name: dispatching-parallel-agents
|
||||
description: 当面对 2+ 个彼此独立、无需共享状态或顺序依赖即可处理的任务时使用
|
||||
---
|
||||
|
||||
# 派遣并行 Agent
|
||||
|
||||
## 概述
|
||||
|
||||
你将任务委派给上下文相互隔离的专门 Agent。通过精确设计给它们的指令和上下文,你可以确保它们保持专注并成功完成任务。它们绝不应继承你的会话上下文或历史记录——你要准确构建它们所需的内容。这也能保留你自己的上下文,以便进行协调工作。
|
||||
|
||||
当你遇到多个互不相关的失败(不同的测试文件、不同的子系统、不同的 bug)时,依次调查它们会浪费时间。每项调查都彼此独立,可以并行进行。
|
||||
|
||||
**核心原则:** 每个独立的问题领域派遣一个 Agent。让它们并发工作。
|
||||
|
||||
## 何时使用
|
||||
|
||||
```dot
|
||||
digraph when_to_use {
|
||||
"Multiple failures?" [shape=diamond, label="存在多个失败?"];
|
||||
"Are they independent?" [shape=diamond, label="它们是否彼此独立?"];
|
||||
"Single agent investigates all" [shape=box, label="由单个 Agent 调查全部问题"];
|
||||
"One agent per problem domain" [shape=box, label="每个问题领域一个 Agent"];
|
||||
"Can they work in parallel?" [shape=diamond, label="它们能否并行工作?"];
|
||||
"Sequential agents" [shape=box, label="依次派遣 Agent"];
|
||||
"Parallel dispatch" [shape=box, label="并行派遣"];
|
||||
|
||||
"Multiple failures?" -> "Are they independent?" [label="是"];
|
||||
"Are they independent?" -> "Single agent investigates all" [label="否——相互关联"];
|
||||
"Are they independent?" -> "Can they work in parallel?" [label="是"];
|
||||
"Can they work in parallel?" -> "Parallel dispatch" [label="是"];
|
||||
"Can they work in parallel?" -> "Sequential agents" [label="否——共享状态"];
|
||||
}
|
||||
```
|
||||
|
||||
**适用情形:**
|
||||
- 3 个以上测试文件因不同的根本原因而失败
|
||||
- 多个子系统各自独立损坏
|
||||
- 每个问题都能在不依赖其他问题上下文的情况下得到理解
|
||||
- 各项调查之间不存在共享状态
|
||||
|
||||
**不适用情形:**
|
||||
- 失败相互关联(修复一个可能会修复其他失败)
|
||||
- 需要理解完整的系统状态
|
||||
- Agent 之间会相互干扰
|
||||
|
||||
## 模式
|
||||
|
||||
### 1. 识别独立领域
|
||||
|
||||
按照损坏的部分对失败进行分组:
|
||||
- 文件 A 测试:工具审批流程
|
||||
- 文件 B 测试:批量完成行为
|
||||
- 文件 C 测试:中止功能
|
||||
|
||||
每个领域都是独立的——修复工具审批不会影响中止测试。
|
||||
|
||||
### 2. 创建聚焦的 Agent 任务
|
||||
|
||||
每个 Agent 获得:
|
||||
- **具体范围:** 一个测试文件或子系统
|
||||
- **明确目标:** 让这些测试通过
|
||||
- **约束:** 不要更改其他代码
|
||||
- **预期输出:** 总结你发现并修复的内容
|
||||
|
||||
### 3. 并行分派
|
||||
|
||||
在同一条响应中发出全部三个子 Agent 分派——它们会并行运行:
|
||||
|
||||
```text
|
||||
子 Agent (general-purpose): "修复 agent-tool-abort.test.ts 的失败"
|
||||
子 Agent (general-purpose): "修复 batch-completion-behavior.test.ts 的失败"
|
||||
子 Agent (general-purpose): "修复 tool-approval-race-conditions.test.ts 的失败"
|
||||
# 三者并发运行。
|
||||
```
|
||||
|
||||
一条响应中进行多次分派调用 = 并行执行。每条响应一次 = 顺序执行。
|
||||
|
||||
### 4. 审查并集成
|
||||
|
||||
当各 Agent 返回时:
|
||||
- 阅读每份总结
|
||||
- 验证各项修复不会冲突
|
||||
- 运行完整测试套件
|
||||
- 集成所有更改
|
||||
|
||||
## Agent 提示词结构
|
||||
|
||||
良好的 Agent 提示词应当:
|
||||
1. **聚焦** - 一个明确的问题领域
|
||||
2. **独立完备** - 包含理解问题所需的全部上下文
|
||||
3. **明确说明输出** - Agent 应返回什么?
|
||||
|
||||
```markdown
|
||||
修复 src/agents/agent-tool-abort.test.ts 中 3 个失败的测试:
|
||||
|
||||
1. "应在捕获部分输出的情况下中止工具" - 期望消息中包含 '中断于'
|
||||
2. "应处理混合的已完成和已中止工具" - 快速工具被中止,而不是完成
|
||||
3. "应正确跟踪 pendingToolCount" - 期望得到 3 个结果,但实际得到 0 个
|
||||
|
||||
这些是时序/竞态条件问题。你的任务:
|
||||
|
||||
1. 阅读测试文件并理解每个测试验证的内容
|
||||
2. 找出根本原因 - 是时序问题还是实际缺陷?
|
||||
3. 通过以下方式修复:
|
||||
- 用基于事件的等待替换任意超时
|
||||
- 如果发现中止实现中存在缺陷,则修复它们
|
||||
- 如果测试的是已变更的行为,则调整测试预期
|
||||
|
||||
不要只是增加超时时间 - 要找出真正的问题。
|
||||
|
||||
返回:你发现了什么以及修复了什么的摘要。
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
|
||||
**❌ 范围太宽泛:** "修复所有测试" - Agent 会迷失方向
|
||||
**✅ 具体:** "修复 agent-tool-abort.test.ts" - 范围明确
|
||||
|
||||
**❌ 没有上下文:** "修复竞态条件" - Agent 不知道在哪里
|
||||
**✅ 上下文:** 粘贴错误消息和测试名称
|
||||
|
||||
**❌ 无约束:** Agent 可能会重构所有内容
|
||||
**✅ 约束:** "不要更改生产代码" 或 "只修复测试"
|
||||
|
||||
**❌ 模糊的输出:** "修复它" - 你不知道哪些内容发生了变化
|
||||
**✅ 具体的输出:** "返回根本原因和所做更改的摘要"
|
||||
|
||||
## 何时不应使用
|
||||
|
||||
**相关的失败:** 修复一个可能会修复其他失败 - 先一起调查
|
||||
**需要完整上下文:** 要理解问题,需要查看整个系统
|
||||
**探索性调试:** 你还不知道哪里出了问题
|
||||
**共享状态:** Agent 会相互干扰(编辑相同的文件、使用相同的资源)
|
||||
|
||||
## 会话中的真实示例
|
||||
|
||||
**场景:** 大规模重构后,3 个文件中出现 6 个测试失败
|
||||
|
||||
**失败:**
|
||||
- agent-tool-abort.test.ts: 3 个失败(时序问题)
|
||||
- batch-completion-behavior.test.ts: 2 个失败(工具未执行)
|
||||
- tool-approval-race-conditions.test.ts: 1 个失败(执行次数 = 0)
|
||||
|
||||
**决策:** 各领域相互独立 - 中止逻辑、批次完成和竞态条件彼此分离
|
||||
|
||||
**分派:**
|
||||
```
|
||||
Agent 1 → 修复 agent-tool-abort.test.ts
|
||||
Agent 2 → 修复 batch-completion-behavior.test.ts
|
||||
Agent 3 → 修复 tool-approval-race-conditions.test.ts
|
||||
```
|
||||
|
||||
**结果:**
|
||||
- Agent 1:用基于事件的等待替换了超时
|
||||
- Agent 2:修复了事件结构错误(threadId 位于错误的位置)
|
||||
- Agent 3:添加了等待异步工具执行完成的逻辑
|
||||
|
||||
**集成:** 所有修复彼此独立,没有冲突,完整测试套件全部通过
|
||||
|
||||
## 验证
|
||||
|
||||
Agent 返回后:
|
||||
1. **审查每份总结** - 了解发生了哪些变更
|
||||
2. **检查冲突** - Agent 是否编辑了相同的代码?
|
||||
3. **运行完整测试套件** - 验证所有修复能否协同工作
|
||||
4. **抽查** - Agent 可能会犯系统性错误
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
name: executing-plans
|
||||
description: 当你有一份书面实施计划,需要在单独的会话中执行并设置审查检查点时使用
|
||||
---
|
||||
|
||||
# 执行计划
|
||||
|
||||
## 概述
|
||||
|
||||
加载计划,严格审查,执行所有任务,并在完成时报告。
|
||||
|
||||
**开始时宣布:**“我正在使用 executing-plans 技能来实施此计划。”
|
||||
|
||||
**注意:**告诉你的人类伙伴,在能够使用子 Agent 的情况下,Superpowers 的运行效果会好得多(Claude Code、Codex CLI、Codex App、Copilot CLI 和 Gemini CLI 都符合要求;请参阅 `../using-superpowers/references/` 中各平台对应的工具参考文档)。如果子 Agent 可用,请使用 superpowers:subagent-driven-development,而不是此技能。
|
||||
|
||||
## 流程
|
||||
|
||||
### 第 1 步:加载并审查计划
|
||||
1. 确保使用隔离工作区:使用 superpowers:using-git-worktrees 创建一个,或验证现有工作区
|
||||
2. 阅读计划文件
|
||||
3. 严格审查——找出对计划存在的任何疑问或担忧
|
||||
4. 如果有疑虑:在开始之前向你的人类伙伴提出
|
||||
5. 如果没有疑虑:为计划项创建待办事项,然后继续
|
||||
|
||||
### 第 2 步:执行任务
|
||||
|
||||
对于每个任务:
|
||||
1. 标记为 in_progress
|
||||
2. 严格遵循每一个步骤(计划中的步骤都已拆分为小步骤)
|
||||
3. 按照规定运行验证
|
||||
4. 标记为 completed
|
||||
|
||||
### 第 3 步:完成开发
|
||||
|
||||
在所有任务均已完成并通过验证后:
|
||||
- 宣布:“我正在使用 finishing-a-development-branch 技能来完成这项工作。”
|
||||
- **必需的子技能:**使用 superpowers:finishing-a-development-branch
|
||||
- 按照该技能验证测试、提供选项并执行所选方案
|
||||
|
||||
## 何时停止并寻求帮助
|
||||
|
||||
**出现以下情况时,立即停止执行:**
|
||||
- 遇到阻碍(缺少依赖项、测试失败、指令不明确)
|
||||
- 计划存在导致无法开始的关键缺口
|
||||
- 你不理解某条指令
|
||||
- 验证反复失败
|
||||
|
||||
**请求澄清,而不是猜测。**
|
||||
|
||||
## 何时重新回到先前步骤
|
||||
|
||||
**在以下情况下返回审查(第 1 步):**
|
||||
- 人类伙伴根据你的反馈更新了计划
|
||||
- 基本方法需要重新考虑
|
||||
|
||||
**不要强行突破阻碍**——停止并询问。
|
||||
|
||||
## 请记住
|
||||
- 首先严格审查计划
|
||||
- 严格遵循计划步骤
|
||||
- 不要跳过验证
|
||||
- 当计划要求引用技能时,引用相应技能
|
||||
- 遇到阻碍时停止,不要猜测
|
||||
- 未经用户明确同意,绝不要在 main/master 分支上开始实施
|
||||
@@ -1,213 +0,0 @@
|
||||
---
|
||||
name: finishing-a-development-branch
|
||||
description: 在实现完成、所有测试均通过,并且你需要决定如何集成这些工作时使用
|
||||
---
|
||||
|
||||
# 完成开发分支
|
||||
|
||||
## 概述
|
||||
|
||||
**核心原则:** 验证测试 → 检测环境 → 呈现选项 → 执行选择 → 清理。
|
||||
|
||||
**开始时宣布:** “我正在使用 finishing-a-development-branch 技能来完成这项工作。”
|
||||
|
||||
## 第 1 步:验证测试
|
||||
|
||||
运行项目的完整测试套件(`npm test` / `cargo test` / `pytest` / `go test ./...`)。
|
||||
|
||||
**如果测试失败,**报告失败并停止——只有测试套件全绿后才能显示菜单:
|
||||
|
||||
```
|
||||
测试失败(<N> 个失败)。必须先修复才能完成:
|
||||
|
||||
[显示失败信息]
|
||||
```
|
||||
|
||||
**如果测试通过:** 继续执行第 2 步。
|
||||
|
||||
## 第 2 步:检测环境
|
||||
|
||||
```bash
|
||||
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
|
||||
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
|
||||
# Capture now, while still inside the workspace — Step 5 changes directory
|
||||
# before cleanup (Step 6) needs this value
|
||||
WORKTREE_PATH=$(git rev-parse --show-toplevel)
|
||||
```
|
||||
|
||||
这决定了要显示哪个菜单以及如何清理:
|
||||
|
||||
| 状态 | 菜单 | 清理 |
|
||||
|-------|------|------|
|
||||
| `GIT_DIR == GIT_COMMON`(普通仓库) | 标准 3 个选项 | 没有要清理的工作树 |
|
||||
| `GIT_DIR != GIT_COMMON`,命名分支 | 标准 3 个选项 | 按来源清理(见第 6 步) |
|
||||
| `GIT_DIR != GIT_COMMON`,detached HEAD | 精简 2 个选项(无合并) | 由外部管理——保留原位 |
|
||||
|
||||
## 第 3 步:确定基础分支
|
||||
|
||||
基础分支就是当前工作从中分出的分支——通常在计划、对话或该分支的 upstream 中
|
||||
已经指明。如果还不知道,请询问:“这个分支是从 <你最合理的猜测> 分出来的——
|
||||
对吗?”合并前必须确认;合并到错误基础分支的撤销成本很高。
|
||||
|
||||
## 第 4 步:呈现选项
|
||||
|
||||
**普通仓库和命名分支工作树——必须原样呈现以下 3 个选项:**
|
||||
|
||||
```
|
||||
实现已完成。你想怎么处理?
|
||||
|
||||
1. 在本地合并回 <base-branch>
|
||||
2. 推送并创建 Pull Request
|
||||
3. 保持分支原样(我稍后处理)
|
||||
|
||||
请选择哪个选项?
|
||||
```
|
||||
|
||||
**Detached HEAD——必须原样呈现以下 2 个选项:**
|
||||
|
||||
```
|
||||
实现已完成。你当前处于 detached HEAD(由外部管理的工作区)。
|
||||
|
||||
1. 作为新分支推送并创建 Pull Request
|
||||
2. 保持原样(我稍后处理)
|
||||
|
||||
请选择哪个选项?
|
||||
```
|
||||
|
||||
严格按上述形式呈现菜单——保持简洁,每个选项都必须来自上面的列表。只有当你的人类
|
||||
伙伴明确要求丢弃工作时,才进入丢弃流程(见下文“如果你的人类伙伴要求丢弃工作”)。
|
||||
等待对方回答;集成决定属于他们。
|
||||
|
||||
## 第 5 步:执行选择
|
||||
|
||||
### 选项 1:在本地合并
|
||||
|
||||
```bash
|
||||
# Get main repo root for CWD safety
|
||||
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
|
||||
cd "$MAIN_ROOT"
|
||||
|
||||
# Merge first — verify success before removing anything
|
||||
git checkout <base-branch>
|
||||
git pull
|
||||
git merge <feature-branch>
|
||||
|
||||
# Verify tests on merged result
|
||||
<test command>
|
||||
```
|
||||
|
||||
如果合并结果上的测试失败:停止,保留工作树和分支并调查——尚未推送任何内容,
|
||||
因此本地合并仍可恢复。
|
||||
|
||||
合并结果全绿后:清理工作树(第 6 步),再删除分支:
|
||||
|
||||
```bash
|
||||
git branch -d <feature-branch>
|
||||
```
|
||||
|
||||
### 选项 2:推送并创建 PR
|
||||
|
||||
```bash
|
||||
git push -u origin <feature-branch>
|
||||
# From a detached HEAD, name the new branch on the remote:
|
||||
# git push origin HEAD:refs/heads/<new-branch>
|
||||
```
|
||||
|
||||
然后使用代码托管平台的工具,以 <base-branch> 为目标创建 Pull/Merge Request——
|
||||
如果有 CLI 就使用 CLI,否则使用多数平台在推送时打印的创建 URL;若仓库存在 PR
|
||||
模板和约定,则遵循它们,并将 URL 报告给你的人类伙伴。
|
||||
|
||||
保留工作树——你的人类伙伴会在其中处理 PR 反馈。
|
||||
|
||||
### 选项 3:保持原样
|
||||
|
||||
报告:“保留分支 <name>。工作树保留在 <path>。”
|
||||
|
||||
### 如果你的人类伙伴要求丢弃工作
|
||||
|
||||
此路径仅在对方明确要求丢弃工作时存在。请先确认:
|
||||
|
||||
```
|
||||
这将永久删除:
|
||||
- 分支 <name>
|
||||
- 所有提交:<commit-list>
|
||||
- 位于 <path> 的工作树
|
||||
|
||||
输入 'discard' 以确认。
|
||||
```
|
||||
|
||||
等待完全一致的确认。收到后:
|
||||
|
||||
```bash
|
||||
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
|
||||
cd "$MAIN_ROOT"
|
||||
```
|
||||
|
||||
然后清理工作树(第 6 步),并强制删除分支:
|
||||
|
||||
```bash
|
||||
git branch -D <feature-branch>
|
||||
```
|
||||
|
||||
## 第 6 步:清理工作区
|
||||
|
||||
**仅对选项 1 和已确认的丢弃操作运行。** 选项 2 和 3 始终保留工作树。两个调用方
|
||||
都已经切换到主仓库根目录——移除工作树必须在工作树外部运行——并且必须使用第 2 步
|
||||
中切换目录前捕获的 `GIT_DIR`/`GIT_COMMON`/`WORKTREE_PATH` 值。
|
||||
|
||||
**如果 `GIT_DIR == GIT_COMMON`:** 普通仓库,没有要清理的工作树。完成。
|
||||
|
||||
**如果 `WORKTREE_PATH` 位于 `.worktrees/` 或 `worktrees/` 下:** 此工作树由
|
||||
Superpowers 创建——清理由我们负责:
|
||||
|
||||
```bash
|
||||
git worktree remove "$WORKTREE_PATH"
|
||||
git worktree prune # Self-healing: clean up any stale registrations
|
||||
```
|
||||
|
||||
**如果移除被拒绝**(`contains modified or untracked files`):工作树中有其他位置不存在的文件——未提交的计划、笔记或临时工作。绝不要自行使用 `--force`。向人类伙伴展示风险并询问:
|
||||
|
||||
```bash
|
||||
git -C "$WORKTREE_PATH" status --porcelain -uall
|
||||
```
|
||||
|
||||
```
|
||||
工作树移除被拒绝——这些文件从未提交:
|
||||
|
||||
<文件列表>
|
||||
|
||||
1. 在清理前将它们提交到 <branch>
|
||||
2. 将它们移动到 <main repo root>
|
||||
3. 删除它们(无法恢复)
|
||||
|
||||
选择哪一项?
|
||||
```
|
||||
|
||||
执行选择,然后再移除工作树。
|
||||
|
||||
**否则:** 此工作区属于宿主环境——保留原位。如果你的平台提供 workspace-exit
|
||||
工具,请使用它。
|
||||
|
||||
## 快速参考
|
||||
|
||||
| 选项 | 合并 | 推送 | 保留工作树 | 清理分支 |
|
||||
|--------|-------|------|---------------|----------------|
|
||||
| 1. 本地合并 | 是 | - | - | 是 |
|
||||
| 2. 创建 PR | - | 是 | 是 | - |
|
||||
| 3. 保持原样 | - | - | 是 | - |
|
||||
| 丢弃(仅限明确请求) | - | - | - | 是(强制) |
|
||||
|
||||
## 常见合理化借口
|
||||
|
||||
| 借口 | 现实 |
|
||||
|--------|---------|
|
||||
| “测试在本次会话早些时候通过了” | 在即将集成的代码树上运行完整套件。一次全绿只证明运行它的那棵代码树。 |
|
||||
| “他们显然想要合并” | 集成决定属于你的人类伙伴。呈现菜单并等待。 |
|
||||
| “他们看起来已经不需要这个功能了——我来提供丢弃选项” | 菜单按原样就已经完整。只有当你的人类伙伴明确说出要丢弃时,才能进入丢弃流程。 |
|
||||
| “‘嗯,把它删掉吧’也算确认” | 只有精确输入 `discard` 才授权删除。 |
|
||||
| “PR 已经创建,所以工作树现在只是累赘” | PR 反馈需要在该工作树中修复。工作合入之前,它必须保留。 |
|
||||
| “另一个工作树看起来陈旧——我顺便清理” | 只清理 `.worktrees/` 或 `worktrees/` 下的工作树。其他一切都属于宿主环境。 |
|
||||
| “移除被拒绝——`--force` 只是完成清理” | 拒绝意味着文件只存在于该工作树。`--force` 会永久销毁它们。展示给人类伙伴并询问。 |
|
||||
| “合并结果失败可能只是偶发问题” | 合并结果一旦失败就停止一切。调查期间保留分支和工作树。 |
|
||||
| “基础分支显然是 main” | 确认分叉点或询问。合并到错误基础分支的撤销成本很高。 |
|
||||
| “推送被拒绝——强制推送就能解决” | 推送被拒绝说明远端已变化。先调查;只有你的人类伙伴明确要求时才强制推送。 |
|
||||
@@ -1,203 +0,0 @@
|
||||
---
|
||||
name: receiving-code-review
|
||||
description: 在接收代码审查反馈时、实施建议之前使用,尤其是在反馈看起来不明确或技术上存疑时——要求技术严谨性和验证,而不是表演式认同或盲目实施
|
||||
---
|
||||
|
||||
# 接收代码审查
|
||||
|
||||
## 概述
|
||||
|
||||
代码审查需要的是技术评估,而不是情绪表演。
|
||||
|
||||
**核心原则:** 实施前先验证。假设前先询问。技术正确性高于社交舒适感。
|
||||
|
||||
## 响应模式
|
||||
|
||||
```
|
||||
当接收到代码审查反馈时:
|
||||
|
||||
1. 阅读:完整读完反馈,不作反应
|
||||
2. 理解:用自己的话复述要求(或询问)
|
||||
3. 验证:根据代码库的实际情况进行检查
|
||||
4. 评估:对这个代码库而言,在技术上是否合理?
|
||||
5. 响应:作出技术性确认或有理有据地提出异议
|
||||
6. 实施:一次处理一项,逐项测试
|
||||
```
|
||||
|
||||
## 禁止的响应
|
||||
|
||||
**绝不要:**
|
||||
- "你说得完全对!"(明确违反指令文件)
|
||||
- "说得好!" / "很棒的反馈!"(表演性回应)
|
||||
- "我现在就来实现它"(在验证之前)
|
||||
|
||||
**取而代之,应:**
|
||||
- 重述技术要求
|
||||
- 提出澄清性问题
|
||||
- 如果反馈有误,用技术推理予以反驳
|
||||
- 直接开始工作(行动胜于言语)
|
||||
|
||||
## 处理不明确的反馈
|
||||
|
||||
```
|
||||
如果任何一项不明确:
|
||||
停止——暂时不要实现任何内容
|
||||
请求澄清不明确的项目
|
||||
|
||||
原因:各项目可能相互关联。理解不完整 = 实现错误。
|
||||
```
|
||||
|
||||
**示例:**
|
||||
```
|
||||
你的人类伙伴:"修复 1-6"
|
||||
你理解 1、2、3、6。对 4、5 不明确。
|
||||
|
||||
❌ 错误:现在实现 1、2、3、6,稍后再询问 4、5
|
||||
✅ 正确:"我理解第 1、2、3、6 项。在继续之前,需要澄清第 4 和第 5 项。"
|
||||
```
|
||||
|
||||
## 针对来源的处理
|
||||
### 来自你的人类伙伴
|
||||
- **可信**——理解后实施
|
||||
- **仍然要询问**范围是否不明确
|
||||
- **不要表演式认同**
|
||||
- **直接行动**或作出技术性确认
|
||||
|
||||
### 来自外部审查者
|
||||
```
|
||||
实施之前:
|
||||
1. 检查:对这个代码库而言,技术上是否正确?
|
||||
2. 检查:是否会破坏现有功能?
|
||||
3. 检查:当前实现是否有其原因?
|
||||
4. 检查:是否适用于所有平台/版本?
|
||||
5. 检查:审查者是否了解完整上下文?
|
||||
|
||||
如果建议似乎有误:
|
||||
用技术推理提出异议
|
||||
|
||||
如果无法轻易验证:
|
||||
明确说明:“没有 [X],我无法验证这一点。我应该 [investigate/ask/proceed] 吗?”
|
||||
|
||||
如果与人类伙伴之前的决定冲突:
|
||||
停下来,先与你的人类伙伴讨论
|
||||
```
|
||||
|
||||
**你的人类伙伴的规则:**“外部反馈——保持怀疑,但要仔细核查”
|
||||
|
||||
## “专业”功能的 YAGNI 检查
|
||||
|
||||
```
|
||||
如果审查者建议“正确实现”:
|
||||
在代码库中 grep 实际用法
|
||||
|
||||
如果未使用:“此端点未被调用。是否删除它(YAGNI)?”
|
||||
如果已使用:那么就正确实现
|
||||
```
|
||||
|
||||
**你的人类伙伴的规则:**“你和审查者都向我汇报。如果我们不需要此功能,就不要添加它。”
|
||||
|
||||
## 实施顺序
|
||||
|
||||
```
|
||||
对于包含多项的反馈:
|
||||
1. 首先澄清任何不清楚的地方
|
||||
2. 然后按以下顺序实施:
|
||||
- 阻塞性问题(故障、安全问题)
|
||||
- 简单修复(拼写错误、导入)
|
||||
- 复杂修复(重构、逻辑)
|
||||
3. 单独测试每项修复
|
||||
4. 验证没有回归
|
||||
```
|
||||
|
||||
## 何时提出异议
|
||||
|
||||
在以下情况下提出异议:
|
||||
- 建议会破坏现有功能
|
||||
- 审查者不了解完整上下文
|
||||
- 违反 YAGNI(未使用的功能)
|
||||
- 对此技术栈而言在技术上不正确
|
||||
- 存在遗留/兼容性原因
|
||||
- 与你的人类伙伴的架构决策冲突
|
||||
|
||||
**如何提出异议:**
|
||||
- 运用技术推理,而不是采取防御态度
|
||||
- 提出具体问题
|
||||
- 引用可正常运行的测试/代码
|
||||
- 如果涉及架构,让你的人类伙伴参与
|
||||
|
||||
**如果你不愿公开提出异议:** 点明这种不自在,然后告诉你的人类伙伴你发现的问题。他们会欣赏你的坦诚。
|
||||
|
||||
## 认可正确的反馈
|
||||
|
||||
当反馈确实正确时:
|
||||
```
|
||||
✅ "已修复。[Brief description of what changed]"
|
||||
✅ "发现得好 - [specific issue]。已在 [location] 修复。"
|
||||
✅ [直接修复,并在代码中展示]
|
||||
|
||||
❌ "你完全正确!"
|
||||
❌ "说得好!"
|
||||
❌ "谢谢你发现这个问题!"
|
||||
❌ "感谢你的[anything]"
|
||||
❌ 任何表达感谢的话
|
||||
```
|
||||
|
||||
**为什么不说谢谢:** 行动胜于言辞。直接修复即可。代码本身就表明你听取了反馈。
|
||||
|
||||
**如果你发现自己正要写“谢谢”:** 删除它。改为说明修复内容。
|
||||
|
||||
## 得体地纠正你的异议
|
||||
如果你提出了异议,但事实证明你错了:
|
||||
```
|
||||
✅ "你是对的——我检查了 [X],它确实会 [Y]。现在开始实现。"
|
||||
✅ "已核实,你是对的。我最初的理解是错误的,因为 [reason]。正在修正。"
|
||||
|
||||
❌ 长篇道歉
|
||||
❌ 为自己为何提出异议进行辩解
|
||||
❌ 过度解释
|
||||
```
|
||||
|
||||
如实说明更正,然后继续推进。
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误 | 修正方法 |
|
||||
|---------|-----|
|
||||
| 表演式认同 | 陈述要求,或直接行动 |
|
||||
| 盲目实现 | 先对照代码库进行核实 |
|
||||
| 未测试就批量处理 | 一次处理一个,逐一测试 |
|
||||
| 假定审查者是对的 | 检查是否会破坏现有功能 |
|
||||
| 避免提出异议 | 技术正确性 > 舒适感 |
|
||||
| 部分实现 | 先澄清所有事项 |
|
||||
| 无法核实,却仍继续推进 | 说明限制,请求指示 |
|
||||
|
||||
## 真实示例
|
||||
|
||||
**表演式认同(错误):**
|
||||
```
|
||||
审查者:"删除遗留代码"
|
||||
❌ "你说得完全对!让我移除它..."
|
||||
```
|
||||
|
||||
**技术验证(好的做法):**
|
||||
```
|
||||
审查者: "移除旧版代码"
|
||||
✅ "检查中... 构建目标版本为 10.15+,此 API 需要 13+。为了向后兼容,必须保留旧版代码。当前实现使用了错误的 bundle ID——是修复它,还是放弃对 13 之前版本的支持?"
|
||||
```
|
||||
|
||||
**YAGNI(好的做法):**
|
||||
```
|
||||
审查者: "使用数据库、日期筛选器和 CSV 导出来实现完善的指标跟踪"
|
||||
✅ "已 grep 代码库——没有任何地方调用这个端点。移除它(YAGNI)?还是有我漏掉的使用场景?"
|
||||
```
|
||||
|
||||
**不明确的事项(好的做法):**
|
||||
```
|
||||
你的人类伙伴: "修复第 1-6 项"
|
||||
你理解 1、2、3、6。第 4、5 项不清楚。
|
||||
✅ "理解 1、2、3、6。在实现前,需要澄清第 4 项和第 5 项。"
|
||||
```
|
||||
|
||||
## GitHub 线程回复
|
||||
|
||||
在 GitHub 上回复行内审查评论时,应在评论线程中回复(`gh api repos/{owner}/{repo}/pulls/{pr}/comments/{id}/replies`),而不是作为顶层 PR 评论回复。
|
||||
@@ -1,95 +0,0 @@
|
||||
---
|
||||
name: requesting-code-review
|
||||
description: 在完成任务、实现重大功能或合并前使用,以验证工作是否符合要求
|
||||
---
|
||||
|
||||
# 请求代码审查
|
||||
|
||||
派遣一个代码审查子 Agent,在问题产生连锁效应之前将其发现。审查者会收到专为评估而精心构建的准确上下文——绝不会收到你的会话历史记录。
|
||||
|
||||
**核心原则:** 及早审查,频繁审查。
|
||||
|
||||
## 何时请求审查
|
||||
|
||||
**强制:**
|
||||
- 子 Agent 驱动开发中的每项任务完成后
|
||||
- 完成重大功能后
|
||||
- 合并到 main 前
|
||||
|
||||
**可选但很有价值:**
|
||||
- 遇到阻碍时(获得全新视角)
|
||||
- 重构前(基线检查)
|
||||
- 修复复杂 bug 后
|
||||
|
||||
## 如何请求
|
||||
|
||||
**1. 获取 git SHA:**
|
||||
```bash
|
||||
BASE_SHA=$(git rev-parse HEAD~1) # or origin/main
|
||||
HEAD_SHA=$(git rev-parse HEAD)
|
||||
```
|
||||
|
||||
**2. 派遣代码审查子 Agent:**
|
||||
|
||||
派遣一个 `general-purpose` 子 Agent,并填写 [code-reviewer.md](code-reviewer.md) 中的模板
|
||||
|
||||
**占位符:**
|
||||
- `{DESCRIPTION}` - 你所构建内容的简要摘要
|
||||
- `{PLAN_OR_REQUIREMENTS}` - 它应该做什么
|
||||
- `{BASE_SHA}` - 起始提交
|
||||
- `{HEAD_SHA}` - 结束提交
|
||||
|
||||
**3. 根据反馈采取行动:**
|
||||
- 立即修复“严重”问题
|
||||
- 继续之前修复“重要”问题
|
||||
- 记录“次要”问题,留待以后处理
|
||||
- 如果审查者有误,则提出异议(并说明理由)
|
||||
|
||||
## 示例
|
||||
|
||||
```
|
||||
[刚刚完成任务 2:添加验证函数]
|
||||
|
||||
你:继续之前,让我请求代码审查。
|
||||
|
||||
BASE_SHA=$(git log --oneline | grep "Task 1" | head -1 | awk '{print $1}')
|
||||
HEAD_SHA=$(git rev-parse HEAD)
|
||||
|
||||
[派遣代码审查子 Agent]
|
||||
DESCRIPTION: 添加了 verifyIndex() 和 repairIndex(),涵盖 4 种问题类型
|
||||
PLAN_OR_REQUIREMENTS: docs/superpowers/plans/deployment-plan.md 中的任务 2
|
||||
BASE_SHA: a7981ec
|
||||
HEAD_SHA: 3df7661
|
||||
|
||||
[子 Agent 返回]:
|
||||
优点:架构清晰,测试真实有效
|
||||
问题:
|
||||
重要:缺少进度指示器
|
||||
次要:报告间隔使用了魔法数字(100)
|
||||
评估:可以继续
|
||||
|
||||
你:[修复进度指示器]
|
||||
[继续执行任务 3]
|
||||
```
|
||||
|
||||
## 常见合理化借口
|
||||
|
||||
| 借口 | 现实 |
|
||||
|--------|---------|
|
||||
| “我自己审查 diff 就好,不用派遣审查者” | 你是协调者——直接在当前上下文中审查 diff,会消耗你继续推动工作所需的上下文窗口。派遣审查子 Agent:diff 和评估保留在它的上下文中,只有发现项返回给你。 |
|
||||
| “审查者需要我的完整会话历史才能理解变更” | 只向它提供精心构建的准确上下文,绝不要提供会话历史。这样审查者会专注于工作成果,而不是你的思考过程。 |
|
||||
|
||||
## 危险信号
|
||||
|
||||
**绝不要:**
|
||||
- 因为“它很简单”而跳过审查
|
||||
- 忽略“严重”问题
|
||||
- 在“重要”问题尚未修复时继续
|
||||
- 与有效的技术反馈争辩
|
||||
|
||||
**如果审查者有误:**
|
||||
- 用技术理由提出异议
|
||||
- 展示能够证明其正常工作的代码/测试
|
||||
- 请求澄清
|
||||
|
||||
模板见:[code-reviewer.md](code-reviewer.md)
|
||||
@@ -1,174 +0,0 @@
|
||||
# 代码审查员提示词模板
|
||||
|
||||
委派代码审查员子 Agent 时,请使用此模板。
|
||||
|
||||
**目的:** 在已完成工作的影响扩散至更多工作之前,依据需求和代码质量标准对其进行审查。
|
||||
|
||||
```
|
||||
子 Agent (general-purpose):
|
||||
description: "审查代码变更"
|
||||
prompt: |
|
||||
你是一名资深代码审查员,精通软件架构、
|
||||
设计模式和最佳实践。你的职责是依据其计划或需求审查已完成的工作,
|
||||
并在问题蔓延之前识别这些问题。
|
||||
|
||||
## 已实现的内容
|
||||
|
||||
[DESCRIPTION]
|
||||
|
||||
## 需求 / 计划
|
||||
|
||||
[PLAN_OR_REQUIREMENTS]
|
||||
|
||||
## 要审查的 Git 范围
|
||||
|
||||
**Base:** [BASE_SHA]
|
||||
**Head:** [HEAD_SHA]
|
||||
|
||||
```bash
|
||||
git diff --stat [BASE_SHA]..[HEAD_SHA]
|
||||
git diff [BASE_SHA]..[HEAD_SHA]
|
||||
```
|
||||
|
||||
## 只读审查
|
||||
|
||||
当前检出中的审查是只读的。不得以任何方式改动工作树、索引、HEAD 或分支状态。使用 `git show`、`git diff` 和 `git log` 等工具检查历史记录。如果你需要其他版本的工作副本,请将其检出到单独的临时目录中(例如 `git worktree add /tmp/review-[SHA] [SHA]`)——绝不要在当前检出中移动 HEAD。
|
||||
|
||||
## 你不派遣子 Agent
|
||||
|
||||
独自完成这次审查。绝不派遣子 Agent 审查 diff 的一部分,也不要派遣另一名审查者征求第二意见。此流程已经提供了这项工作应有的全部审查席位;你派出的审查者会以完整成本重复其中一个席位,其结论不计入流程。如果 diff 对一次审查来说太大,就自行分段审查并在报告中说明。
|
||||
|
||||
## 检查内容
|
||||
|
||||
**计划一致性:**
|
||||
- 实现是否符合计划 / 需求?
|
||||
- 偏差是有充分理由的改进,还是有问题的背离?
|
||||
- 计划中的所有功能是否都已实现?
|
||||
|
||||
**代码质量:**
|
||||
- 关注点是否清晰分离?
|
||||
- 是否进行了适当的错误处理?
|
||||
- 在适用之处是否保证了类型安全?
|
||||
- 是否在避免过早抽象的同时遵循 DRY?
|
||||
- 是否处理了边界情况?
|
||||
|
||||
**架构:**
|
||||
- 设计决策是否稳健?
|
||||
- 可扩展性和性能是否合理?
|
||||
- 是否存在安全隐患?
|
||||
- 是否与周边代码顺畅集成?
|
||||
|
||||
**测试:**
|
||||
- 测试是否验证真实行为,而非模拟对象?
|
||||
- 是否覆盖了边界情况?
|
||||
- 是否在重要之处包含集成测试?
|
||||
- 所有测试是否都通过?
|
||||
|
||||
**生产就绪性:**
|
||||
- 若数据模式发生变更,是否有迁移策略?
|
||||
- 是否考虑了向后兼容性?
|
||||
- 文档是否完整?
|
||||
- 是否不存在明显错误?
|
||||
|
||||
## 校准
|
||||
|
||||
根据实际严重程度对问题进行分类。并非所有问题都是“严重”。
|
||||
在列出问题之前,先肯定做得好的地方——准确的赞扬
|
||||
有助于实现者信任其余反馈。
|
||||
|
||||
如果你发现与计划有重大偏差,请明确指出,
|
||||
以便实现者确认该偏差是否有意为之。
|
||||
如果发现问题出在计划本身而非实现,请明确说明。
|
||||
|
||||
## 输出格式
|
||||
|
||||
### 优点
|
||||
[哪些地方做得好?请具体说明。]
|
||||
|
||||
### 问题
|
||||
|
||||
#### 严重(必须修复)
|
||||
[错误、安全问题、数据丢失风险、功能损坏]
|
||||
|
||||
#### 重要(应当修复)
|
||||
[架构问题、功能缺失、错误处理不当、测试缺口]
|
||||
|
||||
#### 次要(建议改进)
|
||||
[代码风格、优化机会、文档完善]
|
||||
|
||||
对于每个问题:
|
||||
- File:line 引用
|
||||
- 问题是什么
|
||||
- 为什么重要
|
||||
- 如何修复(如果修复方法并非显而易见)
|
||||
|
||||
### 建议
|
||||
[对代码质量、架构或流程的改进建议]
|
||||
|
||||
### 评估
|
||||
|
||||
**是否已可合并?** [Yes | No | With fixes]
|
||||
|
||||
**理由:** [1-2 sentence technical assessment]
|
||||
|
||||
## 关键规则
|
||||
|
||||
**务必:**
|
||||
- 按实际严重程度分类
|
||||
- 具体明确(file:line,不要含糊其辞)
|
||||
- 解释每个问题为什么重要
|
||||
- 肯定优点
|
||||
- 给出明确结论
|
||||
|
||||
**不要:**
|
||||
- 未经检查就说“看起来不错”
|
||||
- 将吹毛求疵的问题标记为“严重”
|
||||
- 对你实际上未阅读的代码给出反馈
|
||||
- 含糊其辞(“改进错误处理”)
|
||||
- 回避给出明确结论
|
||||
```
|
||||
**占位符:**
|
||||
- `[DESCRIPTION]` — 对所构建内容的简要总结
|
||||
- `[PLAN_OR_REQUIREMENTS]` — 它应执行的操作(计划文件路径、任务文本或需求)
|
||||
- `[BASE_SHA]` — 起始提交
|
||||
- `[HEAD_SHA]` — 结束提交
|
||||
|
||||
**审查者返回:** 优点、问题(严重/重要/次要)、建议、评估
|
||||
|
||||
## 示例输出
|
||||
|
||||
```
|
||||
### 优点
|
||||
- 简洁的数据库架构,包含正确的迁移 (db.ts:15-42)
|
||||
- 全面的测试覆盖(18 个测试,涵盖所有边界情况)
|
||||
- 良好的错误处理,并提供回退机制 (summarizer.ts:85-92)
|
||||
|
||||
### 问题
|
||||
|
||||
#### 重要
|
||||
1. **CLI 包装器中缺少帮助文本**
|
||||
- 文件:index-conversations:1-31
|
||||
- 问题:没有 --help 标志,用户将无法发现 --concurrency
|
||||
- 修复:添加 --help 分支,并提供用法示例
|
||||
|
||||
2. **缺少日期验证**
|
||||
- 文件:search.ts:25-27
|
||||
- 问题:无效日期会静默返回空结果
|
||||
- 修复:验证 ISO 格式,并抛出包含示例的错误
|
||||
|
||||
#### 次要
|
||||
1. **进度指示器**
|
||||
- 文件:indexer.ts:130
|
||||
- 问题:长时间运行的操作没有 "X of Y" 计数器
|
||||
- 影响:用户不知道需要等待多长时间
|
||||
|
||||
### 建议
|
||||
- 添加进度报告以改善用户体验
|
||||
- 考虑为排除的项目提供配置文件(可移植性)
|
||||
|
||||
### 评估
|
||||
|
||||
**可以合并:修复后可以**
|
||||
|
||||
**理由:** 核心实现稳健,具备良好的架构和测试。重要问题(帮助文本、日期验证)很容易修复,并且不影响核心功能。
|
||||
```
|
||||
@@ -1,403 +0,0 @@
|
||||
---
|
||||
name: subagent-driven-development
|
||||
description: 在当前会话中执行包含独立任务的实现计划时使用
|
||||
---
|
||||
|
||||
# 子 Agent 驱动的开发
|
||||
|
||||
通过为每项任务派遣一个全新的实现者子 Agent、在每项任务后进行一次任务审查(规格符合性 + 代码质量),并在最后对整个分支进行全面审查来执行计划。
|
||||
|
||||
**为什么使用子 Agent:** 你将任务委派给具有隔离上下文的专门 Agent。通过精确设计它们的指令和上下文,确保它们保持专注并成功完成各自的任务。它们绝不能继承你当前会话的上下文或历史记录——你只构建它们恰好所需的内容。这也会保留你自己的上下文,以用于协调工作。
|
||||
|
||||
**核心原则:** 每项任务使用全新的子 Agent + 任务审查(规格 + 质量)+ 全面的最终审查 = 高质量、快速迭代
|
||||
|
||||
**叙述:** 在工具调用之间,最多叙述一行简短内容——
|
||||
记录由台账和工具结果承载。
|
||||
|
||||
**持续执行:** 不要在任务之间暂停下来向你的人类伙伴确认。不中断地执行计划中的所有任务。仅可因下面列出的四类情况停止,或所有任务均已完成。询问“我应该继续吗?”以及提供进度摘要会浪费他们的时间——他们要求你执行计划,所以就执行它。
|
||||
|
||||
**裁决,而不是停滞。** 正在执行的计划不应等待人类。冲突、歧义、计划缺陷以及你原本想请求提高上限的情况,都由你作出决定。规格说明是约束性权威,计划是其论证,你的判断解决两者都没有回答的问题。把每项决定记录到账本中,格式为 `Ruling: <你的决定> — <原因> — <判断错误的代价>`,然后继续。错误裁决的代价是人类伙伴可以看见并撤销的返工;让会话停在问题上会耗费他们整天时间却一无所获。
|
||||
|
||||
只有四类事情会让你停止:不可逆或破坏性操作;安全敏感操作;规范要求先询问、且影响工作树之外的副作用(合并、推送共享分支、发布);以及计划糟糕到每条前进路径都只能猜测。遇到这些情况才停止并询问。
|
||||
|
||||
## 何时使用
|
||||
|
||||
```dot
|
||||
digraph when_to_use {
|
||||
"有实现计划吗?" [shape=diamond];
|
||||
"任务大多相互独立吗?" [shape=diamond];
|
||||
"留在当前会话中吗?" [shape=diamond];
|
||||
"subagent-driven-development" [shape=box];
|
||||
"executing-plans" [shape=box];
|
||||
"先手动执行或进行头脑风暴" [shape=box];
|
||||
|
||||
"有实现计划吗?" -> "任务大多相互独立吗?" [label="是"];
|
||||
"有实现计划吗?" -> "先手动执行或进行头脑风暴" [label="否"];
|
||||
"任务大多相互独立吗?" -> "留在当前会话中吗?" [label="是"];
|
||||
"任务大多相互独立吗?" -> "先手动执行或进行头脑风暴" [label="否 - 紧密耦合"];
|
||||
"留在当前会话中吗?" -> "subagent-driven-development" [label="是"];
|
||||
"留在当前会话中吗?" -> "executing-plans" [label="否 - 并行会话"];
|
||||
}
|
||||
```
|
||||
|
||||
**与执行计划(并行会话)的对比:**
|
||||
- 同一会话(无需切换上下文)
|
||||
- 每项任务使用全新的子 Agent(无上下文污染)
|
||||
- 每项任务后都进行审查(规格符合性 + 代码质量),最后进行全面审查
|
||||
- 更快的迭代(任务之间无需人类介入)
|
||||
|
||||
## 流程
|
||||
|
||||
```dot
|
||||
digraph process {
|
||||
rankdir=TB;
|
||||
|
||||
subgraph cluster_per_task {
|
||||
label="每项任务";
|
||||
"派遣实现者子 Agent(./implementer-prompt.md)" [shape=box];
|
||||
"实现者提出问题?" [shape=diamond];
|
||||
"回答问题,提供上下文" [shape=box];
|
||||
"实现者进行实现、测试、提交和自审" [shape=box];
|
||||
"生成审查包,派遣任务审查者(./task-reviewer-prompt.md)" [shape=box];
|
||||
"规格 ✅ 且质量通过?" [shape=diamond];
|
||||
"发现项与计划文本冲突?" [shape=diamond];
|
||||
"针对冲突作出裁决并记录到账本" [shape=box];
|
||||
"第 R 轮修复(共 5 轮):R≤3 时恢复实现者;R≥4 时派遣使用更强模型的新实现者" [shape=box];
|
||||
"派遣作用域化复审(./re-review-prompt.md)" [shape=box];
|
||||
"所有发现项都已解决?" [shape=diamond];
|
||||
"R = 5?" [shape=diamond];
|
||||
"裁决每个未解决的发现项" [shape=box];
|
||||
"存在影响关键要求的发现项?" [shape=diamond];
|
||||
"作出裁决并继续;只有所有路径都只能猜测时才停止" [shape=box];
|
||||
"将发现项及裁决暂存到账本中" [shape=box];
|
||||
"把完成记录追加到账本,标记待办已完成" [shape=box];
|
||||
}
|
||||
|
||||
"设置:工作树、账本检查、读取计划、起飞前审查" [shape=box];
|
||||
"还有任务?" [shape=diamond];
|
||||
"派遣最终代码审查者(../requesting-code-review/code-reviewer.md)" [shape=box];
|
||||
"最终审查有发现项?只派遣一次修复、进行一次作用域化复审,并裁决剩余项" [shape=box];
|
||||
"最终审查干净:删除本计划的工作区" [shape=box];
|
||||
"使用 superpowers:finishing-a-development-branch" [shape=box style=filled fillcolor=lightgreen];
|
||||
|
||||
"设置:工作树、账本检查、读取计划、起飞前审查" -> "派遣实现者子 Agent(./implementer-prompt.md)";
|
||||
"派遣实现者子 Agent(./implementer-prompt.md)" -> "实现者提出问题?";
|
||||
"实现者提出问题?" -> "回答问题,提供上下文" [label="是"];
|
||||
"回答问题,提供上下文" -> "实现者进行实现、测试、提交和自审";
|
||||
"实现者提出问题?" -> "实现者进行实现、测试、提交和自审" [label="否"];
|
||||
"实现者进行实现、测试、提交和自审" -> "生成审查包,派遣任务审查者(./task-reviewer-prompt.md)";
|
||||
"生成审查包,派遣任务审查者(./task-reviewer-prompt.md)" -> "规格 ✅ 且质量通过?";
|
||||
"规格 ✅ 且质量通过?" -> "把完成记录追加到账本,标记待办已完成" [label="是"];
|
||||
"规格 ✅ 且质量通过?" -> "发现项与计划文本冲突?" [label="否"];
|
||||
"发现项与计划文本冲突?" -> "针对冲突作出裁决并记录到账本" [label="是"];
|
||||
"针对冲突作出裁决并记录到账本" -> "第 R 轮修复(共 5 轮):R≤3 时恢复实现者;R≥4 时派遣使用更强模型的新实现者";
|
||||
"发现项与计划文本冲突?" -> "第 R 轮修复(共 5 轮):R≤3 时恢复实现者;R≥4 时派遣使用更强模型的新实现者" [label="否"];
|
||||
"第 R 轮修复(共 5 轮):R≤3 时恢复实现者;R≥4 时派遣使用更强模型的新实现者" -> "派遣作用域化复审(./re-review-prompt.md)";
|
||||
"派遣作用域化复审(./re-review-prompt.md)" -> "所有发现项都已解决?";
|
||||
"所有发现项都已解决?" -> "把完成记录追加到账本,标记待办已完成" [label="是"];
|
||||
"所有发现项都已解决?" -> "R = 5?" [label="否"];
|
||||
"R = 5?" -> "第 R 轮修复(共 5 轮):R≤3 时恢复实现者;R≥4 时派遣使用更强模型的新实现者" [label="否——进入下一轮"];
|
||||
"R = 5?" -> "裁决每个未解决的发现项" [label="是——触发熔断"];
|
||||
"裁决每个未解决的发现项" -> "存在影响关键要求的发现项?";
|
||||
"存在影响关键要求的发现项?" -> "作出裁决并继续;只有所有路径都只能猜测时才停止" [label="是"];
|
||||
"存在影响关键要求的发现项?" -> "将发现项及裁决暂存到账本中" [label="否"];
|
||||
"将发现项及裁决暂存到账本中" -> "把完成记录追加到账本,标记待办已完成";
|
||||
"把完成记录追加到账本,标记待办已完成" -> "还有任务?";
|
||||
"还有任务?" -> "派遣实现者子 Agent(./implementer-prompt.md)" [label="是"];
|
||||
"还有任务?" -> "派遣最终代码审查者(../requesting-code-review/code-reviewer.md)" [label="否"];
|
||||
"派遣最终代码审查者(../requesting-code-review/code-reviewer.md)" -> "最终审查有发现项?只派遣一次修复、进行一次作用域化复审,并裁决剩余项";
|
||||
"最终审查有发现项?只派遣一次修复、进行一次作用域化复审,并裁决剩余项" -> "最终审查干净:删除本计划的工作区";
|
||||
"最终审查干净:删除本计划的工作区" -> "使用 superpowers:finishing-a-development-branch";
|
||||
}
|
||||
```
|
||||
|
||||
## 设置
|
||||
|
||||
务必确保工作在隔离的工作区中进行:使用
|
||||
superpowers:using-git-worktrees 创建一个工作区,或验证现有工作区。
|
||||
未经你的人类伙伴明确同意,绝不要在 main/master 分支上开始实现。
|
||||
|
||||
对话记忆无法在压缩后保留。在真实会话中,忘记当前进度的
|
||||
控制器曾重新分派整套已经完成的任务序列——这是观察到的代价
|
||||
最高的单一故障。请在台账文件中跟踪进度,而不要仅依赖待办事项。
|
||||
|
||||
- 每个计划都拥有自己的工作区:技能启动时,运行本技能的
|
||||
`scripts/sdd-workspace PLAN_FILE`——它会输出该计划被 git 忽略的
|
||||
目录(`<repo-root>/.superpowers/sdd/<plan-basename>/`),本计划的
|
||||
所有产物都存放于此:台账、简报、报告、复审包。
|
||||
绝不得读取或写入另一个计划的目录。
|
||||
- 检查此计划位于 `<workspace>/progress.md` 的台账。如果其第一行
|
||||
指明的是你的计划文件,则带有一行 `Task <N>: complete` 的任务均已完成
|
||||
——不得重新分派它们;从第一个没有该行的任务继续。若某项任务的
|
||||
最后一行是一个修复轮次,则该任务正处于循环中:从下一轮继续该
|
||||
循环。如果台账的第一行指明的是另一个计划文件——或者旧的扁平路径
|
||||
`.superpowers/sdd/progress.md` 中存在一个遗留台账——那就是另一个
|
||||
计划的进度:将其留在原处,并从头新建你自己的台账。
|
||||
- 创建台账,并将其身份信息写在第一行:
|
||||
`# SDD ledger — plan: <plan file path>`。
|
||||
- 台账是你的恢复地图:其中记载的提交即使在你的上下文不再记得创建过
|
||||
它们时,也仍存在于 git 中。压缩后,相比你自己的回忆,应相信台账和
|
||||
`git log`。
|
||||
- `git clean -fdx` 会摧毁该工作区(它是被 git 忽略的临时目录);如果
|
||||
发生这种情况,请从 `git log` 恢复。
|
||||
|
||||
通读计划一次,记下其上下文和全局约束,并为每项任务创建一个
|
||||
待办事项。如果计划指明了 Spec,也要读取它:规格说明是计划论证所依据的权威,二者必须一起提供给执行者。找不到可读取的 Spec 时,在账本中记一条说明;没有规格说明时作出的裁决属于临时裁决。
|
||||
|
||||
在分派任务 1 之前,通读计划一次以检查冲突,并在检查时写下检查内容:
|
||||
|
||||
- 彼此矛盾或与计划的全局约束相矛盾的任务
|
||||
- 计划明确要求、但审查量规视为缺陷的任何事项(一个没有进行任何断言的
|
||||
测试、逐字重复一个逻辑块)
|
||||
|
||||
检查输出应是一张表,而不是一句结论。对每一对共享文件或接口的任务写一行:两个任务、一个任务产出的内容与另一个任务消费的内容,以及你的发现。对每个任务再写一行:任务自身是否自洽——它指定的测试是否对应指定的代码、创建的文件是否对应后续触及的文件。“检查干净”而没有这些行,不算执行过检查。
|
||||
|
||||
把这张表写到账本中。在开始执行前对所有发现作出裁决——将每项发现与要求它的计划文本并列——并把每项裁决记录到账本。若检查干净,则无需说明,直接继续。对检查发现的每个冲突作出裁决:规格说明是约束性权威,计划是其论证;将裁决写在对应行旁边,然后分派任务 1。复审循环仍是实现过程中才出现的冲突的兜底机制。
|
||||
|
||||
## 模型选择
|
||||
|
||||
使用能够胜任各个角色的最低能力模型,以节省成本并提高速度。
|
||||
|
||||
**机械性实现任务**(孤立函数、明确的规范、1-2 个文件):使用快速、廉价的模型。当计划规定得足够明确时,大多数实现任务都是机械性的。
|
||||
|
||||
**集成和判断任务**(多文件协调、模式匹配、调试):使用标准模型。
|
||||
|
||||
**架构和设计任务**:使用当前可用的最强大模型。
|
||||
整个分支的最终审查就属于此类——应将其派发给当前可用的最强大模型,而不是会话默认模型。
|
||||
|
||||
**审查任务**:以同样的判断标准选择模型,并根据差异的规模、复杂性和风险调整。小型机械性差异不需要最强大的模型;细微的并发变更则需要。对小型修复差异进行范围明确的复审时,使用廉价至中档模型。
|
||||
|
||||
**修复循环升级(第 4-5 轮)**:使用至少比陷入困境的实现者高一个档次的模型。
|
||||
|
||||
**派发子 Agent 时,始终明确指定模型。** 省略模型时,将继承你会话所用的模型——通常是最强大且最昂贵的模型——这会在不知不觉中违背本节的目的。
|
||||
|
||||
**轮次数比 token 单价更重要。** 实际耗时和上下文成本会随子 Agent 的轮次数量而增加,而最廉价的模型在多步骤工作中通常需要 2-3× 的轮次——导致总体成本更高。对于审查者以及根据文字描述开展工作的实现者,应至少使用中档模型。当任务的计划文本包含需要编写的完整代码时,实现工作就是誊写加测试:为该实现者使用最廉价档次的模型。单文件机械性修复也使用最廉价档次的模型。
|
||||
|
||||
**任务复杂度信号(实现任务):**
|
||||
- 涉及 1-2 个文件且规范完整 → 廉价模型
|
||||
- 涉及多个文件且存在集成方面的考量 → 标准模型
|
||||
- 需要设计判断或对代码库有广泛理解 → 最强大的模型
|
||||
|
||||
## 任务循环
|
||||
|
||||
**批量处理同形小任务。** 如果计划列出多个小型、相互独立且属于同一种编辑的任务——相同的一行修复、常量变更,或跨文件重复添加字段——不要为每项任务分派一个子 Agent。编写一份列出所有文件及其变更的批量简报,交给一个子 Agent,并把它的 diff 作为一个整体审查。只有需要各自判断、测试或独立审查面的工作,才按每项任务一次派发。
|
||||
|
||||
你粘贴到派遣提示中的所有内容——以及子 Agent 返回的所有输出——在会话余下的整个期间都会常驻于你的上下文中,并在之后的每一轮中被重新读取。请以文件形式交接产物。
|
||||
|
||||
**等待已派遣的子 Agent:** 不要用短超时轮询等待接口,也不要一次沉默地进行无界等待。只要还有本地工作,就继续处理台账更新、下一份审查包或阅读报告;子 Agent 的结果会自行到达。真正空闲时,用有界时间段等待(平台允许时等待 5-10 分钟),并在每段等待之间发布一行状态、列出仍存活的子 Agent,并追查已完成却没有报告的子 Agent。有界等待保留长等待的大部分效率,同时能在数分钟内发现卡住或丢失的子 Agent。
|
||||
|
||||
### 1. 派遣实现者
|
||||
|
||||
派遣前记录 BASE(`git rev-parse HEAD`)——审查包和修复轮次的 diff 都需要它。
|
||||
|
||||
- **任务简报:**派遣实现者之前,运行本技能的
|
||||
`scripts/task-brief PLAN_FILE N`——它会将任务的完整文本提取到一个具有唯一名称的文件中,并打印该路径。组织派遣内容时,应让简报始终作为需求的唯一来源。你的派遣内容应包含:(1) 用一行说明此任务在项目中的位置;(2) 简报路径,并以“先阅读此文件——它就是你的需求,其中包含必须原样使用的精确值”引出;(3) 简报不可能知道的、来自先前任务的接口和决策;(4) 你对简报中所注意到的任何歧义的解决方案;(5) 报告文件路径和报告约定。精确值(数字、魔法字符串、签名、测试用例)只能出现在简报中。绝不要让子 Agent 阅读整个计划文件。
|
||||
- **报告文件:**以简报为实现者的报告文件命名(简报 `…/task-N-brief.md` → 报告 `…/task-N-report.md`),并将其写入派遣提示。实现者在该文件中写入完整报告,并且只返回状态、提交记录、一行测试摘要和关注事项。
|
||||
- 一条派遣提示描述的是一个任务,而不是会话的历史。不要把累积的先前任务摘要(“任务 1-3 后的状态”)粘贴到后续派遣中——某个真实会话的派遣内容达到了 42k 个字符,其中 99% 都是粘贴的历史记录。一个全新的子 Agent 需要的是它的任务、它会涉及的接口以及全局约束。除此之外什么都不需要。
|
||||
- 派遣内容携带“不派遣子 Agent”的契约(该契约位于实现者模板中):实现者绝不派遣子 Agent——既不派助手,也绝不派审查者。审查会在它报告后由你负责派遣。真实会话中,实现者自行派出的每个审查者都重复了控制器已经派出的任务审查,等于每项任务多花一张完整审查席位。
|
||||
- 如果先前的任务在本任务涉及的区域中暂存了一项发现,请在派遣内容中附上指向该账本条目的指针。
|
||||
- 从派遣结果中记录实现者的 Agent 身份——修复循环第 1-3 轮会恢复此 Agent。
|
||||
- 绝不要并行派遣多个实现子 Agent(会产生冲突)。
|
||||
|
||||
模板:[implementer-prompt.md](implementer-prompt.md)
|
||||
|
||||
### 2. 处理报告
|
||||
|
||||
实现者子 Agent 会报告以下四种状态之一。请分别妥善处理:
|
||||
|
||||
**DONE:** 生成审查包(`scripts/review-package PLAN_FILE BASE HEAD`,从本技能的目录运行——它会输出其写入的唯一文件路径;BASE 是你在派遣实现者之前记录的提交——绝不能使用 `HEAD~1`,因为这会悄无声息地丢弃多提交任务中除最后一个提交之外的所有提交),然后使用输出的路径派遣任务审查者。
|
||||
|
||||
**DONE_WITH_CONCERNS:** 实现者完成了工作,但提出了一些疑虑。继续之前先阅读这些疑虑。如果疑虑涉及正确性或范围,请在审查之前解决。如果只是一些观察(例如,“这个文件变得越来越大了”),请记录下来并继续审查。
|
||||
|
||||
**NEEDS_CONTEXT:** 实现者需要尚未提供的信息。提供缺失的上下文并重新派遣。
|
||||
|
||||
**BLOCKED:** 实现者无法完成任务。评估阻碍因素:
|
||||
1. 如果是上下文问题,请提供更多上下文,并使用同一模型重新派遣
|
||||
2. 如果任务需要更多推理,请使用能力更强的模型重新派遣
|
||||
3. 如果任务过大,请将其拆分成更小的部分
|
||||
4. 如果计划本身有误,请裁决修正、记录到账本,并携带该裁决重新派遣
|
||||
|
||||
**绝不**忽略上报,也绝不强迫同一模型在没有任何改变的情况下重试。如果实现者表示它卡住了,就必须做出改变。
|
||||
|
||||
如果实现者在开始之前或任务进行期间提出问题,请清晰、完整地回答,
|
||||
在需要时提供额外的上下文,并且不要催促它仓促进入实现阶段。
|
||||
|
||||
|
||||
### 3. 审查任务
|
||||
|
||||
逐任务审查是限定于任务范围的关卡。广泛审查只进行一次,即在最终的整个分支审查时进行。绝不要跳过任务审查,也绝不要接受缺少任一结论的报告——规范符合性和任务质量两者都必须具备。实现者自我审查绝不能取代任务审查;两者都必不可少。
|
||||
|
||||
- 将 diff 作为文件交给审查者:运行本技能的
|
||||
`scripts/review-package PLAN_FILE BASE HEAD`,并将它输出的文件路径传给审查者
|
||||
(或者,不使用 bash 时:针对该范围运行 `git log --oneline`、`git diff --stat`
|
||||
和 `git diff -U10`,并将输出重定向到一个名称唯一的
|
||||
文件)。该输出绝不会进入你自己的上下文,而审查者只需一次 Read
|
||||
调用就能看到提交列表、统计摘要以及带上下文的完整 diff。
|
||||
使用你在派遣实现者之前记录的 BASE——绝不要使用
|
||||
`HEAD~1`,因为它会悄无声息地截断包含多个提交的任务。绝不要
|
||||
在没有 diff 文件的情况下派遣任务审查者。
|
||||
- **审查者输入:**任务审查者会获得三个路径——同一份简报
|
||||
文件、报告文件和审查包——以及约束该任务的全局
|
||||
约束。
|
||||
- 你交给审查者的全局约束块是其关注问题的
|
||||
视角。逐字复制计划的“全局约束”部分或规范中具有约束力的
|
||||
要求:确切的值、确切的格式,以及明确说明的组件之间的
|
||||
关系(“与 X 布局相同”、“与 Y
|
||||
匹配”)。审查者的模板已经包含流程规则(YAGNI、
|
||||
测试卫生、审查方法)——约束块用于说明这个
|
||||
项目的规范要求什么。
|
||||
- 不要添加诸如“检查所有使用处”或“如果有用就运行竞态测试”
|
||||
之类的开放式指令,除非有具体的、特定于任务的理由
|
||||
- 不要让审查者重新运行实现者已在同一份代码上运行过的测试
|
||||
——实现者的报告中包含测试证据
|
||||
- 不要替审查者预判发现项——绝不要指示审查者
|
||||
忽略某个具体问题或不标记它。如果你认为某个发现项会是
|
||||
误报,就让审查者提出它,并在审查
|
||||
循环中对其作出裁定。如果你正在编写的提示包含“不要标记”、“不要将 X
|
||||
视为缺陷”、“至多为 Minor”或“计划选择了”——立即停止:你正在
|
||||
预判,通常是为了让自己省掉一轮审查循环。
|
||||
任务审查者可能会报告“⚠️ 无法从 diff 验证”的事项——这些要求
|
||||
存在于未更改的代码中或跨越多个任务。这些事项不会阻塞审查的
|
||||
其余部分,但在将任务标记为完成之前,你必须自行解决每一项:
|
||||
你掌握着审查者所缺少的计划和跨任务上下文。
|
||||
如果你确认某项确实是缺口,就将其视为规范审查失败——它会与其他
|
||||
发现项一同进入修复循环。
|
||||
|
||||
模板:[task-reviewer-prompt.md](task-reviewer-prompt.md)
|
||||
|
||||
### 4. 修复循环
|
||||
|
||||
当审查报告规格 ❌、任何严重或重要发现项,或你已确认为真实缺口的 ⚠️ 条目时,就会触发该循环。
|
||||
|
||||
在循环开始之前,有两条路径会立即退出该循环:
|
||||
|
||||
- 在推进过程中,将次要发现项记录到进度账本中
|
||||
(`Task <N>: minor (deferred): <one-liner>`),并让最终的全分支审查查看该列表,以便它判定其中哪些必须在合并前修复。无人阅读的汇总就是悄无声息的丢弃。次要发现项永不进入该循环。
|
||||
- 标记为计划强制要求的发现项——或任何与计划文本要求冲突的发现项——由你裁决:将发现项与计划文本权衡,以规格说明为约束性权威作出决定,并在行动前记录到账本。不要因为计划强制要求它就驳回该发现项,也不要在没有记录裁决的情况下派发与计划相冲突的修复。
|
||||
其余一切都进入该循环。一轮修复包括一次修复派发,加上一次限定范围的复审。每个任务最多五轮:
|
||||
|
||||
**第 1-3 轮——恢复原实现者。** 将未解决的发现项逐字发送给它。它的上下文完好无损:它了解任务、代码以及它自己的选择。如果你的运行框架无法向仍在运行的子 Agent 再发送一条消息,则派发一个新的实现者,并向其提供简报路径、报告文件路径和这些发现项——无论哪种方式,报告文件都是持久记忆。
|
||||
|
||||
**第 4-5 轮——在能力更强的模型上派发一个新的实现者**(按照模型选择),并向其提供简报路径、报告文件路径、未解决的发现项,以及以下说明:“先前的一名实现者已尝试此任务 [N] 次;现在由你负责。阅读报告文件,了解已经尝试过什么。”一个经历三次恢复调用后仍未结束的循环,通常意味着实现者无法看出自己的问题——一次行动同时引入新的视角并提升能力。
|
||||
|
||||
**每一轮,无论是哪种情况:** 实现者进行修复,重新运行覆盖已修改代码的测试,将其修复报告追加到同一个报告文件中,并返回简短契约。在重新派发审查者之前,确认修复报告包含覆盖已修改代码的测试、所运行的命令和输出;三者全部具备后,再派发复审。在修复消息中列出覆盖测试文件的名称——单行修复不需要整个测试套件。
|
||||
|
||||
**复审是作用域化的。** 运行 `scripts/review-package PLAN_FILE FIX_BASE HEAD`,其中 FIX_BASE 是上一次审查所看到的提交头,并派发 [re-review-prompt.md](re-review-prompt.md),同时提供发现项列表、简报、报告文件和打印出的 diff 路径。复审者将每个发现项裁定为 ADDRESSED 或 NOT ADDRESSED,并且只标记修复 diff 中的新破坏。修复 diff 中新的 Critical/Important 破坏会加入未解决的发现项列表。超出作用域的观察项会作为延期次要项记入账本——它们绝不会延长循环。
|
||||
|
||||
**每轮结束后,**向账本追加:
|
||||
`Task <N>: fix round <R>/5 (<X> addressed, <Y> open — <finding one-liners>; commits <a7>..<b7>)`
|
||||
|
||||
绝不要在控制器会话中亲自修复发现项——你的上下文应保持干净以便协调,而控制器直接修复会跳过审查。
|
||||
|
||||
**熔断器。** 如果第 5 轮复审后仍有未解决的发现项,就停止派遣。由你亲自裁决每个未解决的发现项——你掌握审查者所缺少的计划和跨任务上下文:
|
||||
|
||||
- **审查者判断错误,或该问题存在争议:** 暂存它——
|
||||
`Task <N>: parked — <finding> — Ruling: <why the code stands>`。最终审查会看到双方理由。
|
||||
- **问题真实存在,但下游没有任何内容依赖它:** 以同样方式暂存,并在裁决中说明它确实存在但已延期。
|
||||
- **问题真实存在且影响关键要求**——后续任务会依赖它,或它揭示了计划缺陷:裁决能解除依赖工作的最小变更,以 `Task <N>: Ruling: <finding> — <what you decided and why>` 记入账本,并把它带入下一项任务的派遣。静默暂存结构性失败会让所有依赖任务继续在它之上构建。只有缺陷让所有前进路径都只能猜测时才停止。
|
||||
|
||||
只在达到轮次上限时进行裁决。提前裁决来结束循环,不过是换了名称的预判。每次裁决都必须写入账本——严禁无声丢弃。
|
||||
|
||||
### 5. 完成任务
|
||||
|
||||
当审查结果无问题——或者在达到上限时,每个未关闭的发现项都已附裁定搁置——请在执行其他记账工作的同一条消息中,将完成行追加到账本:
|
||||
|
||||
- `Task <N>: complete (commits <base7>..<head7>, review clean)`
|
||||
- 触发熔断器后使用 `Task <N>: complete (commits <base7>..<head7>, <K> parked)`
|
||||
|
||||
然后将待办事项标记为完成并继续。只要审查中仍有既未修复、也未在达到上限时附裁定搁置的未关闭 Critical/Important 问题,就绝不要进入下一个任务。
|
||||
|
||||
## 最终审查
|
||||
|
||||
最终的整分支审查也要有一个审查包:运行 `scripts/review-package PLAN_FILE MERGE_BASE HEAD`(MERGE_BASE = 该分支起始于的提交,例如 `git merge-base main HEAD`),并在派发最终审查时包含输出的路径,这样最终审查者只需读取一个文件,而不必使用 git 命令重新推导分支差异。使用最强的可用模型进行派发(参见模型选择),并使用 superpowers:requesting-code-review 的 [code-reviewer.md](../requesting-code-review/code-reviewer.md)。让它查看账本中的 deferred-minor 行和 parked 行,以便对哪些项目必须在合并前修复进行分诊。
|
||||
|
||||
如果最终的整分支审查返回发现项,请派发一个且仅一个修复子 Agent,并向其提供完整的发现项列表——不要为每个发现项分别派一个修复者。按发现项分别安排的修复者都要重建上下文并重新运行套件;在一次真实会话中,最终审查的修复波次成本超过了其所有任务的总和。然后,对该修复波次恰好进行一次限定范围的复审(针对修复范围运行 `scripts/review-package PLAN_FILE FIX_BASE HEAD`,[re-review-prompt.md](re-review-prompt.md))。按照任务循环中的熔断器方式裁决任何残留发现项:附裁定搁置,或裁决承重性发现项并记录你的决定。这里只能因上述四类情况停止。不存在第二个修复波次——当 finishing-a-development-branch 呈现选项时,残留的承重性发现项会提交给你的人类伙伴。
|
||||
|
||||
## 收尾
|
||||
|
||||
在删除任何内容之前,收集账本中包含 `Ruling:` 的每一行——起飞前裁决、搁置发现项、熔断器裁决,全部包括——按作出顺序放进最终消息的“我作出的裁决”部分,每条都写明判断错误的代价。清单必须完整:账本有裁决,清单就必须有裁决。这是你替人类伙伴作出的决定传达给他们的唯一位置;他们会阅读并返工你判断错误的部分。随工作区一起消失的裁决,就是秘密作出的决定。
|
||||
|
||||
当最终的整分支审查无问题且其修复已合并后,删除此计划的工作区(`rm -rf <workspace>`)——现在 git 历史就是记录。同级目录属于其他计划;不要动它们。
|
||||
|
||||
使用 superpowers:finishing-a-development-branch。
|
||||
|
||||
## 常见合理化
|
||||
|
||||
| 借口 | 事实 |
|
||||
|--------|---------|
|
||||
| "规范符合度已经足够接近了" | 审查者发现规范缺口 = 尚未完成。修复它,或者达到上限并进行裁决——只有这两条出路。 |
|
||||
| "我会自己修复,派发会增加开销" | 控制器进行修复会污染你的上下文并跳过审查。让实现者继续。 |
|
||||
| "再来一轮就会收敛" | 超过上限后,各轮不会收敛——这种失败是结构性的。进行裁决并转交。 |
|
||||
| "反正审查者还是会找到新的问题" | 限定范围的复审只验证修复;不得偏离范围。未改动代码上的新发现项应进入账本,而不是进入循环。 |
|
||||
| "这个发现项显然是错的,我会丢弃它" | 你只能在达到上限时进行裁决,而且每项裁定都必须成为一条账本条目。禁止静默丢弃。 |
|
||||
| "修复很小,跳过复审吧" | 未经审查的修复正是回归问题混入的方式。每一轮都以限定范围的复审结束。 |
|
||||
| "审查会拖慢循环" | 没有审查的循环只不过是未经验证的反复折腾。审查是循环的刹车和方向盘。 |
|
||||
| "账本记账是额外开销" | 账本是在压缩后仍能保留下来的东西。没有账本的控制器曾重新派发整个已完成的任务序列。 |
|
||||
| "实现者自己派了审查者——免费获得额外保证" | 这是审查同一 diff 的重复席位;任务审查本身就是关卡。实现者派出的审查者是需要指出的缺陷,不是严谨性。 |
|
||||
|
||||
## 示例工作流
|
||||
|
||||
```
|
||||
你:我正在使用子 Agent 驱动的开发来执行此计划。
|
||||
|
||||
[设置:已验证工作树]
|
||||
[一次性读取计划文件:docs/superpowers/plans/feature-plan.md]
|
||||
[解析工作区:scripts/sdd-workspace docs/superpowers/plans/feature-plan.md——其中没有账本,全新开始]
|
||||
[为所有任务创建待办事项]
|
||||
|
||||
任务 1:Hook 安装脚本
|
||||
|
||||
[为任务 1 运行 task-brief;使用简报路径 + 报告路径 + 上下文派遣实现者]
|
||||
|
||||
实现者:“开始前确认一下——Hook 应安装在用户级还是系统级?”
|
||||
|
||||
你:“用户级(~/.config/superpowers/hooks/)”
|
||||
|
||||
实现者:[稍后]
|
||||
- 实现了 install-hook 命令
|
||||
- 添加了测试,5/5 通过
|
||||
- 自审:发现遗漏了 --force 标志,已添加
|
||||
- 已提交
|
||||
|
||||
[运行 review-package PLAN_FILE BASE HEAD;使用打印出的路径派遣任务审查者]
|
||||
任务审查者:规格 ✅——满足全部要求,没有额外内容。
|
||||
优点:测试覆盖良好,代码干净。问题:无。任务质量:通过。
|
||||
|
||||
[账本:Task 1: complete (commits a1b2c3d..d4e5f6a, review clean)]
|
||||
|
||||
任务 2:恢复模式
|
||||
|
||||
[为任务 2 运行 task-brief;使用简报路径 + 报告路径 + 上下文派遣实现者]
|
||||
|
||||
实现者:[没有问题]
|
||||
- 添加了 verify/repair 模式
|
||||
- 8/8 测试通过
|
||||
- 已提交
|
||||
|
||||
[运行 review-package PLAN_FILE BASE HEAD;使用打印出的路径派遣任务审查者]
|
||||
任务审查者:规格 ❌:
|
||||
- 缺少:进度报告(规格要求“每处理 100 项报告一次”)
|
||||
问题(重要):魔法数字(100)
|
||||
|
||||
[第 1 轮修复:把两个发现项都发送给原实现者并恢复它]
|
||||
实现者:添加了进度报告,提取出 PROGRESS_INTERVAL 常量。
|
||||
重新运行 test/recovery.test.js——10/10 通过。已追加修复报告。
|
||||
|
||||
[运行 review-package PLAN_FILE FIX_BASE HEAD;派遣作用域化复审]
|
||||
复审者:缺少进度报告——已解决(src/recovery.js:41)。
|
||||
魔法数字——已解决(src/recovery.js:7)。新的破坏:无。
|
||||
结论:所有发现项均已解决。
|
||||
|
||||
[账本:Task 2: fix round 1/5 (2 addressed, 0 open; commits d4e5f6a..b7c8d9e)]
|
||||
[账本:Task 2: complete (commits d4e5f6a..b7c8d9e, review clean)]
|
||||
|
||||
……
|
||||
|
||||
[所有任务完成后]
|
||||
[运行 review-package PLAN_FILE MERGE_BASE HEAD;派遣使用最强模型的最终代码审查者]
|
||||
最终审查者:满足全部要求。已分类延期的次要问题:没有阻碍合并的项目。
|
||||
|
||||
[删除本计划的工作区——记录现在保存在 git 中]
|
||||
|
||||
完成!正在使用 superpowers:finishing-a-development-branch。
|
||||
```
|
||||
-141
@@ -1,141 +0,0 @@
|
||||
# 实现子 Agent 提示模板
|
||||
|
||||
派发实现子 Agent 时使用此模板。
|
||||
|
||||
```
|
||||
子 Agent(通用):
|
||||
description: "实现任务 N:[任务名称]"
|
||||
model: [MODEL — 必填:根据 SKILL.md 的模型选择规则选定;省略 model
|
||||
会悄悄继承当前会话中成本最高的模型]
|
||||
prompt: |
|
||||
你正在实现任务 N:[任务名称]
|
||||
|
||||
## 任务说明
|
||||
|
||||
首先阅读你的任务简报:[BRIEF_FILE]
|
||||
其中包含计划里的完整任务文本。
|
||||
|
||||
## 上下文
|
||||
|
||||
[背景说明:该任务所处位置、依赖关系和架构上下文]
|
||||
|
||||
## 开始之前
|
||||
|
||||
如果你对以下内容有疑问:
|
||||
- 需求或验收标准
|
||||
- 实现方法或策略
|
||||
- 依赖项或假设
|
||||
- 任务说明中任何不清楚的地方
|
||||
|
||||
**现在就提问。** 开始工作前提出所有顾虑。
|
||||
|
||||
## 你的工作
|
||||
|
||||
明确需求后:
|
||||
1. 严格按照任务说明实现
|
||||
2. 编写测试(如果任务要求,则遵循 TDD)
|
||||
3. 验证实现有效
|
||||
4. 提交你的工作
|
||||
5. 自我审查(见下文)
|
||||
6. 汇报结果
|
||||
|
||||
工作目录:[directory]
|
||||
|
||||
**工作期间:** 如果遇到意外情况或不清楚的地方,**请提问**。
|
||||
随时暂停并澄清都没有问题。不要猜测或自行作出假设。
|
||||
|
||||
迭代过程中,运行与你正在修改的内容对应的聚焦测试;完整测试套件只需在提交前
|
||||
运行一次,而不是每次编辑后都运行。
|
||||
|
||||
## 你不派遣子 Agent
|
||||
|
||||
独自完成这项任务的全部工作。绝不派遣子 Agent 实现任务的一部分,更不能派遣审查者检查你的工作。下方的自我审查意味着阅读你自己的 diff。审查是控制器的职责:你报告后,它会针对你的 diff 派遣一名全新的审查者。你派出的审查者会以完整成本重复这次审查,其批准在流程中不计数。如果你开始想“独立审查会让我的报告更可靠”,请记住:审查已经安排好了。直接报告。
|
||||
|
||||
## 代码组织
|
||||
|
||||
当代码规模小到可以一次纳入上下文时,你能更好地推理;文件职责集中时,你的
|
||||
修改也会更可靠。请牢记:
|
||||
- 遵循计划中定义的文件结构
|
||||
- 每个文件都应只有一项清晰职责,并提供定义明确的接口
|
||||
- 如果你正在创建的文件增长到超出计划意图,请停止并以 DONE_WITH_CONCERNS
|
||||
汇报——没有计划指引时,不要自行拆分文件
|
||||
- 如果你正在修改的现有文件本就庞大或纠缠,请谨慎处理,并在报告中将其记为顾虑
|
||||
- 在现有代码库中遵循既有模式。像一名优秀开发者那样改进你接触的代码,
|
||||
但不要重构任务范围以外的内容。
|
||||
|
||||
## 当任务超出你的能力时
|
||||
|
||||
你随时可以停止并说明“这个任务对我来说太难了”。糟糕的工作比没有工作更糟。
|
||||
升级求助不会受到惩罚。
|
||||
|
||||
**遇到以下情况时,停止并升级:**
|
||||
- 任务需要在多个有效方案之间作出架构决策
|
||||
- 你需要理解未提供的代码,却无法找到清晰答案
|
||||
- 你不确定自己的方法是否正确
|
||||
- 任务涉及以计划未预料的方式重构现有代码
|
||||
- 你接连阅读文件来理解系统,却始终没有进展
|
||||
|
||||
**升级方式:** 以 BLOCKED 或 NEEDS_CONTEXT 状态汇报。具体说明你卡在哪里、
|
||||
已尝试什么,以及需要哪种帮助。控制器可以提供更多上下文、使用能力更强的
|
||||
模型重新派发,或把任务拆分得更小。
|
||||
|
||||
## 汇报前:自我审查
|
||||
|
||||
用全新的视角审查你的工作。问问自己:
|
||||
|
||||
**完整性:**
|
||||
- 我是否完整实现了规格中的所有内容?
|
||||
- 我是否遗漏了任何要求?
|
||||
- 是否存在我未处理的边界情况?
|
||||
|
||||
**质量:**
|
||||
- 这是我能做到的最佳结果吗?
|
||||
- 命名是否清晰准确(反映事物做什么,而不是如何做)?
|
||||
- 代码是否整洁且易于维护?
|
||||
|
||||
**纪律:**
|
||||
- 我是否避免了过度构建(YAGNI)?
|
||||
- 我是否只构建了被要求的内容?
|
||||
- 我是否遵循了代码库中的既有模式?
|
||||
|
||||
**测试:**
|
||||
- 测试是否真正验证了行为(而不只是模拟行为)?
|
||||
- 如果有要求,我是否遵循了 TDD?
|
||||
- 测试是否全面?
|
||||
- 测试输出是否干净(没有无关警告或噪声)?
|
||||
|
||||
如果在自我审查中发现问题,请现在修复,然后再汇报。
|
||||
|
||||
## 收到审查发现项后
|
||||
|
||||
如果任务审查发现问题,你会带着这些发现项被恢复。修复它们,重新运行覆盖
|
||||
已修改代码的测试,并把修复报告追加到你的报告文件中:说明修改了什么、
|
||||
运行了哪些覆盖测试、所用命令以及输出。审查者不会替你重新运行测试——
|
||||
你的报告就是测试证据。然后使用与首次报告相同的简短状态契约回复。
|
||||
|
||||
## 报告格式
|
||||
|
||||
将完整报告写入 [REPORT_FILE]:
|
||||
- 实现了什么(如果被阻塞,则说明尝试了什么)
|
||||
- 测试内容与测试结果
|
||||
- **TDD 证据**(如果此任务要求 TDD):
|
||||
- RED:实现前运行的命令、相关失败输出,以及为何该失败符合预期
|
||||
- GREEN:实现后运行的命令和相关通过输出
|
||||
- 修改的文件
|
||||
- 自我审查发现项(如有)
|
||||
- 任何问题或顾虑
|
||||
|
||||
然后仅使用以下内容回复(少于 15 行——详细内容保存在报告文件中):
|
||||
- **Status:** DONE | DONE_WITH_CONCERNS | BLOCKED | NEEDS_CONTEXT
|
||||
- 创建的提交(短 SHA + 主题)
|
||||
- 单行测试摘要(例如“14/14 通过,输出干净”)
|
||||
- 你的顾虑(如有)
|
||||
- 报告文件路径
|
||||
|
||||
如果状态为 BLOCKED 或 NEEDS_CONTEXT,请把具体情况直接写在最终消息中——
|
||||
控制器会直接据此采取行动。
|
||||
|
||||
已完成工作但对正确性有疑虑时,使用 DONE_WITH_CONCERNS。无法完成任务时,
|
||||
使用 BLOCKED。需要未提供的信息时,使用 NEEDS_CONTEXT。绝不要悄悄交付
|
||||
自己没有把握的工作。
|
||||
```
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
# 限定范围复审子 Agent 提示模板
|
||||
|
||||
在一轮修复后派发复审时使用此模板。复审者要验证发现项是否已经解决,
|
||||
并检查修复 diff 是否引入新的破坏。这不是一次全新审查——完整审查已经完成。
|
||||
|
||||
**目的:** 验证上一次审查的每个发现项都已解决,并确认修复本身没有破坏任何内容。
|
||||
|
||||
```
|
||||
子 Agent(通用):
|
||||
description: "复审任务 N 的第 R 轮修复"
|
||||
model: [MODEL — 必填:根据 SKILL.md 的模型选择规则选定;省略 model
|
||||
会悄悄继承当前会话中成本最高的模型]
|
||||
prompt: |
|
||||
你正在复审一个任务的一轮修复。先前的审查产生了一组发现项;实现者已经尝试
|
||||
修复它们。你的工作是裁定每个发现项,并检查修复 diff——仅此而已。
|
||||
|
||||
## 任务
|
||||
|
||||
阅读任务简报:[BRIEF_FILE]
|
||||
|
||||
## 待验证的发现项
|
||||
|
||||
[FINDINGS]
|
||||
|
||||
## 修复
|
||||
|
||||
阅读实现者的报告(修复报告追加在末尾):
|
||||
[REPORT_FILE]
|
||||
|
||||
**Fix base:** [FIX_BASE_SHA](上次审查看到的 head)
|
||||
**Head:** [HEAD_SHA]
|
||||
**Diff 文件:** [DIFF_FILE]
|
||||
|
||||
只读取一次 diff 文件——它包含修复提交、统计摘要,以及带周边上下文的修复 diff。
|
||||
不要重新运行 git 命令。如果 diff 文件缺失,请自行获取 diff:
|
||||
`git diff --stat [FIX_BASE_SHA]..[HEAD_SHA]` 和
|
||||
`git diff [FIX_BASE_SHA]..[HEAD_SHA]`。
|
||||
|
||||
在此检出目录中,你的审查是只读的。不得以任何方式修改工作树、索引、HEAD
|
||||
或分支状态。
|
||||
|
||||
## 你不派遣子 Agent
|
||||
|
||||
独自完成这次审查。绝不派遣子 Agent 审查 diff 的一部分,也不要派遣另一名审查者征求第二意见。此流程已经提供了这项工作应有的全部审查席位;你派出的审查者会以完整成本重复其中一个席位,其结论不计入流程。如果 diff 对一次审查来说太大,就自行分段审查并在报告中说明。
|
||||
|
||||
## 范围
|
||||
|
||||
你的范围是发现项列表和修复 diff。必须裁定每一个发现项。检查修复 diff,
|
||||
看修复本身是否引入了新问题。不要重新审查修复没有触及的代码:如果你注意到
|
||||
一个完全位于修复 diff 之外的问题,请将其报告在“范围外观察项”下——它不会
|
||||
阻塞此任务,也不会延长循环。所有任务完成后会进行一次广泛的整分支审查。
|
||||
|
||||
## 测试
|
||||
|
||||
实现者已经重新运行覆盖已修改代码的测试,并把结果追加到了报告文件中。
|
||||
把报告视为未经验证的声明:确认修复报告写明了覆盖测试并展示其输出,
|
||||
再根据 diff 验证这些声明。不要为了确认报告而重新运行测试套件。只有在阅读
|
||||
代码时产生了现有运行结果无法解答的具体疑问,才运行测试——而且只能运行
|
||||
聚焦测试,绝不运行包级完整套件。
|
||||
|
||||
## 输出格式
|
||||
|
||||
你的最终消息就是报告本身:直接从第一个发现项的裁定开始。每一行都必须是
|
||||
裁定、带 file:line 的发现项,或你运行过的检查——不要写前言、过程叙述或
|
||||
结尾总结。
|
||||
|
||||
### 发现项裁定
|
||||
|
||||
按“待验证的发现项”中的顺序,逐项报告:
|
||||
- **[发现项单行摘要]** — ADDRESSED | NOT ADDRESSED,并附 file:line 证据。
|
||||
“尝试过”不等于已解决:具体缺陷必须已经不存在。
|
||||
|
||||
### 修复 Diff 中的新破坏
|
||||
|
||||
修复本身破坏或引入的任何内容,附严重级别(Critical/Important/Minor)
|
||||
和 file:line。如果干净,则写“无”。
|
||||
|
||||
### 范围外观察项
|
||||
|
||||
你注意到的、完全位于修复 diff 之外的问题。它们不阻塞任务;控制器会将其
|
||||
记入账本,供最终审查处理。如果没有,则写“无”。
|
||||
|
||||
### 裁定
|
||||
|
||||
**修复轮次:** [所有发现项均已解决,且没有新的 Critical/Important 破坏 |
|
||||
仍有发现项未解决]——列出尚未解决的项目。
|
||||
```
|
||||
|
||||
**占位符:**
|
||||
- `[MODEL]` — 必填:根据 SKILL.md 的模型选择规则选择审查模型;对于小型
|
||||
修复 diff 的限定范围复审,使用低成本至中等档位模型
|
||||
- `[BRIEF_FILE]` — 任务简报文件(实现者使用的同一文件)
|
||||
- `[FINDINGS]` — 上一次审查中的 Critical/Important 发现项和规格缺口,
|
||||
逐字复制,每项一个项目符号
|
||||
- `[REPORT_FILE]` — 实现者的报告文件(修复报告追加在其中)
|
||||
- `[FIX_BASE_SHA]` — 上一次审查看到的 head
|
||||
- `[HEAD_SHA]` — 当前提交
|
||||
- `[DIFF_FILE]` — `scripts/review-package PLAN_FILE FIX_BASE HEAD` 打印出的路径
|
||||
|
||||
**复审者返回:** 每个发现项的裁定(ADDRESSED / NOT ADDRESSED)、修复 diff
|
||||
中的新破坏、范围外观察项,以及该轮裁定。
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generate a review package: commit list, stat summary, and the net
|
||||
# diff with extended context, written to a file the reviewer reads in one
|
||||
# call. Using the recorded per-task BASE (not HEAD~1) keeps multi-commit
|
||||
# tasks intact.
|
||||
#
|
||||
# Usage: review-package PLAN_FILE BASE HEAD [OUTFILE]
|
||||
# Default OUTFILE: <repo-root>/.superpowers/sdd/<plan-basename>/review-<base7>..<head7>.diff
|
||||
# (named per range, so a re-review after fixes gets a distinct fresh file).
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -lt 3 ] || [ $# -gt 4 ]; then
|
||||
echo "usage: review-package PLAN_FILE BASE HEAD [OUTFILE]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
plan=$1
|
||||
base=$2
|
||||
head=$3
|
||||
[ -f "$plan" ] || { echo "no such plan file: $plan" >&2; exit 2; }
|
||||
|
||||
git rev-parse --verify --quiet "$base" >/dev/null || { echo "bad BASE: $base" >&2; exit 2; }
|
||||
git rev-parse --verify --quiet "$head" >/dev/null || { echo "bad HEAD: $head" >&2; exit 2; }
|
||||
|
||||
if [ $# -eq 4 ]; then
|
||||
out=$4
|
||||
else
|
||||
dir=$("$(cd "$(dirname "$0")" && pwd)/sdd-workspace" "$plan")
|
||||
out="$dir/review-$(git rev-parse --short "$base")..$(git rev-parse --short "$head").diff"
|
||||
fi
|
||||
|
||||
{
|
||||
echo "# Review package: ${base}..${head}"
|
||||
echo
|
||||
echo "## Commits"
|
||||
git log --oneline "${base}..${head}"
|
||||
echo
|
||||
echo "## Files changed"
|
||||
git diff --stat "${base}..${head}"
|
||||
echo
|
||||
echo "## Diff"
|
||||
git diff -U10 "${base}..${head}"
|
||||
} > "$out"
|
||||
|
||||
commits=$(git rev-list --count "${base}..${head}")
|
||||
echo "wrote ${out}: ${commits} commit(s), $(wc -c < "$out" | tr -d ' ') bytes"
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Resolve and ensure the working-tree directory SDD uses for one plan's
|
||||
# short-lived artifacts: task briefs, implementer reports, review packages,
|
||||
# and the progress ledger. Print the plan directory's absolute path.
|
||||
#
|
||||
# One directory per plan (.superpowers/sdd/<plan-basename>/) so a follow-up
|
||||
# plan in the same working tree can never read or overwrite another plan's
|
||||
# artifacts. A stale ledger misread as current progress makes controllers
|
||||
# skip whole task sequences — plan-scoping removes that failure structurally.
|
||||
#
|
||||
# The workspace lives in the working tree (not under .git/) because Claude Code
|
||||
# treats .git/ as a protected path and denies agent writes there — which blocks
|
||||
# an implementer subagent from writing its report file. A self-ignoring
|
||||
# .gitignore at .superpowers/sdd/ keeps every plan's workspace out of
|
||||
# `git status` and out of accidental commits without modifying any tracked file.
|
||||
#
|
||||
# Single source of truth for the workspace location, so task-brief and
|
||||
# review-package cannot drift to different directories.
|
||||
#
|
||||
# Usage: sdd-workspace PLAN_FILE
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "usage: sdd-workspace PLAN_FILE" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
plan=$1
|
||||
[ -f "$plan" ] || { echo "no such plan file: $plan" >&2; exit 2; }
|
||||
|
||||
slug=$(basename "$plan" .md)
|
||||
[ -n "$slug" ] && [ "$slug" != "." ] && [ "$slug" != ".." ] \
|
||||
|| { echo "cannot derive a workspace name from: $plan" >&2; exit 2; }
|
||||
|
||||
root=$(git rev-parse --show-toplevel)
|
||||
base="$root/.superpowers/sdd"
|
||||
dir="$base/$slug"
|
||||
mkdir -p "$dir"
|
||||
printf '*\n' > "$base/.gitignore"
|
||||
cd "$dir" && pwd
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Extract one task's full text from an implementation plan into a file the
|
||||
# implementer reads in one call, so the task text never has to be pasted
|
||||
# through the controller's context.
|
||||
#
|
||||
# Usage: task-brief PLAN_FILE TASK_NUMBER [OUTFILE]
|
||||
# Default OUTFILE: <repo-root>/.superpowers/sdd/<plan-basename>/task-<N>-brief.md
|
||||
# (per plan and per worktree; concurrent runs of the SAME plan in the same
|
||||
# working tree share it).
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
|
||||
echo "usage: task-brief PLAN_FILE TASK_NUMBER [OUTFILE]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
plan=$1
|
||||
n=$2
|
||||
[ -f "$plan" ] || { echo "no such plan file: $plan" >&2; exit 2; }
|
||||
|
||||
if [ $# -eq 3 ]; then
|
||||
out=$3
|
||||
else
|
||||
dir=$("$(cd "$(dirname "$0")" && pwd)/sdd-workspace" "$plan")
|
||||
out="$dir/task-${n}-brief.md"
|
||||
fi
|
||||
|
||||
awk -v n="$n" '
|
||||
/^```/ { infence = !infence }
|
||||
!infence && /^#+[ \t]+Task[ \t]+[0-9]+/ {
|
||||
intask = ($0 ~ ("^#+[ \t]+Task[ \t]+" n "([^0-9]|$)"))
|
||||
}
|
||||
intask { print }
|
||||
' "$plan" > "$out"
|
||||
|
||||
if [ ! -s "$out" ]; then
|
||||
echo "task ${n} not found in ${plan} (no heading matching 'Task ${n}')" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
echo "wrote ${out}: $(wc -l < "$out" | tr -d ' ') lines"
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
# 任务审查子 Agent 提示模板
|
||||
|
||||
派发任务审查子 Agent 时使用此模板。审查者只读取一次该任务的 diff,
|
||||
并返回两项裁定:规格符合度和代码质量。
|
||||
|
||||
**目的:** 验证单个任务的实现是否符合其要求(不多也不少),并确认实现质量良好
|
||||
(整洁、有测试、可维护)。
|
||||
|
||||
```
|
||||
子 Agent(通用):
|
||||
description: "审查任务 N(规格 + 质量)"
|
||||
model: [MODEL — 必填:根据 SKILL.md 的模型选择规则选定;省略 model
|
||||
会悄悄继承当前会话中成本最高的模型]
|
||||
prompt: |
|
||||
你正在审查一个任务的实现:先判断它是否符合要求,再判断它的构建质量。
|
||||
这是任务级门禁,而不是合并审查——所有任务完成后,会另行进行一次广泛的
|
||||
整分支审查。
|
||||
|
||||
## 要求实现的内容
|
||||
|
||||
阅读任务简报:[BRIEF_FILE]
|
||||
|
||||
规格/设计中约束此任务的全局要求:
|
||||
[GLOBAL_CONSTRAINTS]
|
||||
|
||||
## 实现者声称已构建的内容
|
||||
|
||||
阅读实现者的报告:[REPORT_FILE]
|
||||
|
||||
## 待审查 Diff
|
||||
|
||||
**Base:** [BASE_SHA]
|
||||
**Head:** [HEAD_SHA]
|
||||
**Diff 文件:** [DIFF_FILE]
|
||||
|
||||
只读取一次 diff 文件——它包含提交列表、统计摘要,以及带周边上下文的完整
|
||||
diff;这就是你观察本次变更的视图。diff 的上下文行就是已更改文件的内容:
|
||||
不要另行读取已更改文件,除非你必须判断的某个代码块恰好在函数中间被截断;
|
||||
如需这样做,请在报告中说明。不要重新运行 git 命令。
|
||||
如果 diff 文件缺失,请自行获取 diff:
|
||||
`git diff --stat [BASE_SHA]..[HEAD_SHA]` 和 `git diff [BASE_SHA]..[HEAD_SHA]`。
|
||||
不要遍历更广泛的代码库。只有在评估一个你能够明确指出的具体风险时,才检查
|
||||
diff 之外的代码——每个已命名风险只做一次聚焦检查,并在报告中同时写明风险
|
||||
和所检查的内容。横切变更是合理的已命名风险:如果 diff 改变了锁顺序、函数
|
||||
或 API 契约,或共享可变状态,检查调用点就是正确做法。
|
||||
|
||||
在此检出目录中,你的审查是只读的。不得以任何方式修改工作树、索引、HEAD
|
||||
或分支状态。
|
||||
|
||||
## 你不派遣子 Agent
|
||||
|
||||
独自完成这次审查。绝不派遣子 Agent 审查 diff 的一部分,也不要派遣另一名审查者征求第二意见。此流程已经提供了这项工作应有的全部审查席位;你派出的审查者会以完整成本重复其中一个席位,其结论不计入流程。如果 diff 对一次审查来说太大,就自行分段审查并在报告中说明。
|
||||
|
||||
## 不要相信报告
|
||||
|
||||
把实现者的报告视为尚未验证的代码声明。它可能不完整、不准确或过于乐观。
|
||||
根据 diff 验证这些声明。报告中的设计理由同样只是声明:“按照 YAGNI 保持不变”、
|
||||
“刻意保持简单”或任何其他解释,都是实现者在给自己的工作打分。根据代码本身
|
||||
的实际质量作出判断——一条书面理由永远不会降低发现项的严重级别。
|
||||
|
||||
## 测试
|
||||
|
||||
实现者已经针对这份代码运行测试,并报告了结果及 TDD 证据。不要为了确认其
|
||||
报告而重新运行测试套件。只有在阅读代码时产生了现有运行结果无法解答的具体
|
||||
疑问,才运行测试——而且只能运行聚焦测试,绝不运行包级完整套件、竞态检测器
|
||||
或重复/高次数循环。如果看起来有必要进行重型验证,请在报告中提出建议,
|
||||
而不是亲自运行。如果当前环境无法运行命令,请写明你本来会运行的测试。
|
||||
|
||||
实现者报告的测试输出中若有警告或其他噪声,也属于发现项——测试输出应当干净。
|
||||
|
||||
看不见的证据不等于不存在证据。如果报告或测试证据看起来被截断,或找不到它声称的结果,请重新读取报告标明的文件路径;如果确实缺失或损坏,就把它作为缺口报告给控制器。为了重新生成你没读清的内容而重跑套件不算验证;证据难以阅读也不等于证据无效。
|
||||
|
||||
## 第 1 部分:规格符合度
|
||||
|
||||
将 diff 与“要求实现的内容”进行比较:
|
||||
|
||||
- **Missing:** 被跳过、遗漏,或声称完成但实际没有实现的要求
|
||||
- **Extra:** 未要求的功能、过度工程、不必要的“锦上添花”
|
||||
- **Misunderstood:** 用错误方式构建了正确功能,或解决了错误的问题
|
||||
|
||||
如果简报列出了多个各自有变更的文件(批量派遣),请逐文件对照 diff:列表中的每个文件都必须有对应的 hunk。列表中列出但 diff 从未触及的文件,无论其他批量内容多干净,都属于 Missing。
|
||||
|
||||
如果某项要求无法仅根据此 diff 验证(它位于未更改代码中,或跨越多个任务),
|
||||
请将其报告为 ⚠️ 条目,不要扩大搜索范围。
|
||||
|
||||
## 第 2 部分:代码质量
|
||||
|
||||
**代码质量:**
|
||||
- 关注点是否清晰分离?
|
||||
- 错误处理是否恰当?
|
||||
- 是否遵循 DRY,同时避免过早抽象?
|
||||
- 是否处理了边界情况?
|
||||
|
||||
**测试:**
|
||||
- 新增和修改的测试是否验证真实行为,而不是 mock?
|
||||
- 是否覆盖了此任务的边界情况?
|
||||
|
||||
**结构:**
|
||||
- 每个文件是否只有一项清晰职责,并提供定义明确的接口?
|
||||
- 各单元是否已分解到能够独立理解和测试?
|
||||
- 实现是否遵循计划中的文件结构?
|
||||
- 此次变更是否创建了已经很大的新文件,或显著增大了现有文件?
|
||||
(不要标记变更前就已存在的文件大小问题——只关注此次变更带来的内容。)
|
||||
|
||||
报告应指向证据:每个发现项都要提供 file:line 引用;任何本来只能回答“是”的
|
||||
检查,也要提供引用。带有精确行号的紧凑报告能向控制器提供所需的一切。
|
||||
|
||||
你的最终消息就是报告本身:直接从规格符合度裁定开始。每一行都必须是裁定、
|
||||
带 file:line 的发现项,或你运行过的检查——不要写前言、过程叙述或结尾总结。
|
||||
|
||||
## 校准
|
||||
|
||||
按实际严重程度对问题分类。不是所有问题都是 Critical。
|
||||
Important 表示在修复之前不能信任此任务:行为错误或脆弱、遗漏要求,或你会因此
|
||||
阻止合并的可维护性损害——逐字重复一块逻辑、吞掉错误、没有作出任何断言的测试。
|
||||
“覆盖可以更广”和润色建议属于 Minor。
|
||||
如果计划或简报明确要求了本准则认定为缺陷的内容(没有断言的测试、逐字重复的
|
||||
逻辑块),它仍然是发现项——应以 Important 报告,并标记为 plan-mandated。
|
||||
计划作者不能给自己的工作打分;由人类决定。
|
||||
列出问题前,先肯定做得好的地方——准确的肯定能帮助实现者信任其余反馈。
|
||||
|
||||
## 输出格式
|
||||
|
||||
### 规格符合度
|
||||
|
||||
- ✅ 符合规格 | ❌ 发现问题:[缺少/多余/误解的内容,附 file:line 引用]
|
||||
- ⚠️ 无法从 diff 验证:[无法仅从 diff 验证的要求,以及控制器应检查什么——
|
||||
与对其他可验证内容给出的 ✅/❌ 裁定一同报告]
|
||||
|
||||
### 优点
|
||||
[哪些地方做得好?请具体说明。]
|
||||
|
||||
### 问题
|
||||
|
||||
#### Critical(必须修复)
|
||||
#### Important(应当修复)
|
||||
#### Minor(可选改进)
|
||||
|
||||
对每个问题说明:file:line、问题是什么、为何重要、如何修复(如果方法不明显)。
|
||||
|
||||
### 评估
|
||||
|
||||
**任务质量:** [已批准 | 需要修复]
|
||||
|
||||
**理由:** [1-2 句技术评估]
|
||||
```
|
||||
|
||||
**占位符:**
|
||||
- `[MODEL]` — 必填:根据 SKILL.md 的模型选择规则选择审查模型
|
||||
- `[BRIEF_FILE]` — 必填:任务简报文件(`scripts/task-brief PLAN N`
|
||||
会打印其路径;这也是实现者使用的同一文件)
|
||||
- `[GLOBAL_CONSTRAINTS]` — 从计划的“全局约束”部分或规格中逐字复制、
|
||||
且对此任务有约束力的要求:准确值、格式及组件间明示的关系
|
||||
(不是流程规则——本模板已经包含这些规则)
|
||||
- `[REPORT_FILE]` — 必填:实现者写入详细报告的文件
|
||||
- `[BASE_SHA]` — 此任务开始前的提交
|
||||
- `[HEAD_SHA]` — 当前提交
|
||||
- `[DIFF_FILE]` — 必填:控制器写入审查包的路径
|
||||
(`scripts/review-package PLAN_FILE BASE HEAD` 会打印它写入的唯一
|
||||
路径;该审查包永远不会进入控制器的上下文)
|
||||
|
||||
**审查者返回:** 规格符合度裁定(✅/❌/⚠️)、优点、问题
|
||||
(Critical/Important/Minor)和任务质量裁定
|
||||
@@ -1,119 +0,0 @@
|
||||
# 创建记录:系统化调试技能
|
||||
|
||||
提取、组织和加固关键技能的参考示例。
|
||||
|
||||
## 源材料
|
||||
|
||||
从 `~/.claude/CLAUDE.md` 中提取的调试框架:
|
||||
- 4 阶段系统化流程(调查 → 模式分析 → 假设 → 实施)
|
||||
- 核心强制要求:始终找到根本原因,绝不修复症状
|
||||
- 旨在抵御时间压力和合理化借口的规则
|
||||
|
||||
## 提取决策
|
||||
|
||||
**要包含的内容:**
|
||||
- 完整的 4 阶段框架及其全部规则
|
||||
- 反捷径规则(“绝不修复症状”“停下来重新分析”)
|
||||
- 能够抵御压力的措辞(“即使这样更快”“即使我看起来很着急”)
|
||||
- 每个阶段的具体步骤
|
||||
|
||||
**要排除的内容:**
|
||||
- 项目特定的上下文
|
||||
- 同一规则的重复变体
|
||||
- 叙述性说明(精简为原则)
|
||||
|
||||
## 遵循 skill-creation/SKILL.md 的结构
|
||||
|
||||
1. **内容丰富的 when_to_use** - 包含症状和反模式
|
||||
2. **类型:technique** - 包含具体步骤的明确流程
|
||||
3. **关键词** - “根本原因”“症状”“变通方案”“调试”“调查”
|
||||
4. **流程图** - 针对“修复失败” → 重新分析还是添加更多修复的决策点
|
||||
5. **逐阶段分解** - 易于快速浏览的检查清单格式
|
||||
6. **反模式章节** - 不应做什么(对这项技能至关重要)
|
||||
|
||||
## 加固要素
|
||||
|
||||
该框架旨在抵御压力下的合理化借口:
|
||||
|
||||
### 措辞选择
|
||||
- “始终” / “绝不”(而非“应该” / “尝试”)
|
||||
- “即使这样更快” / “即使我看起来很着急”
|
||||
- “停下来重新分析”(明确要求暂停)
|
||||
- “不要跳过”(直指实际行为)
|
||||
|
||||
### 结构性防御
|
||||
- **阶段 1 为必需阶段** - 不能直接跳到实施
|
||||
- **单一假设规则** - 强制进行思考,防止散弹式修复
|
||||
- **明确的失败模式** - “如果你的第一次修复不起作用”,并规定必须采取的行动
|
||||
- **反模式章节** - 准确展示捷径是什么样的
|
||||
|
||||
### 冗余强化
|
||||
- 根本原因要求出现在概述 + when_to_use + 阶段 1 + 实施规则中
|
||||
- “绝不修复症状”在不同上下文中出现了 4 次
|
||||
- 每个阶段都包含明确的“不要跳过”指引
|
||||
|
||||
## 测试方法
|
||||
|
||||
按照 skills/meta/testing-skills-with-subagents 创建了 4 项验证测试:
|
||||
|
||||
### 测试 1:学术情境(无压力)
|
||||
- 简单错误,无时间压力
|
||||
- **结果:** 完全遵循要求,完成了全面调查
|
||||
|
||||
### 测试 2:时间压力 + 显而易见的快速修复
|
||||
- 用户“很着急”,症状修复看起来很容易
|
||||
- **结果:** 抵制了走捷径的诱惑,遵循完整流程,找到了真正的根本原因
|
||||
|
||||
### 测试 3:复杂系统 + 不确定性
|
||||
- 多层故障,不确定能否找到根本原因
|
||||
- **结果:** 进行了系统化调查,追踪了所有层级,并找到了源头
|
||||
|
||||
### 测试 4:第一次修复失败
|
||||
- 假设未奏效,容易让人想继续添加更多修复
|
||||
- **结果:** 停下来重新分析,并形成了新的假设(未采用散弹式修复)
|
||||
|
||||
**所有测试均已通过。** 未发现任何合理化借口。
|
||||
|
||||
## 迭代
|
||||
|
||||
### 初始版本
|
||||
- 完整的 4 阶段框架
|
||||
- 反模式章节
|
||||
- 用于处理“修复失败”决策的流程图
|
||||
|
||||
### 增强 1:TDD 参考
|
||||
- 添加了指向 skills/testing/test-driven-development 的链接
|
||||
- 添加说明,解释 TDD 的“最简单代码” ≠ 调试的“根本原因”
|
||||
- 防止混淆这两种方法论
|
||||
|
||||
## 最终成果
|
||||
|
||||
一项经过严密加固的技能,能够:
|
||||
- ✅ 明确要求调查根本原因
|
||||
- ✅ 抵御时间压力下的合理化借口
|
||||
- ✅ 为每个阶段提供具体步骤
|
||||
- ✅ 明确展示反模式
|
||||
- ✅ 已在多种压力场景下完成测试
|
||||
- ✅ 阐明与 TDD 的关系
|
||||
- ✅ 可供使用
|
||||
|
||||
## 关键洞见
|
||||
|
||||
**最重要的加固措施:** 反模式章节准确展示了那些在当下似乎有正当理由的捷径。当 Claude 想“我就添加这一个快速修复”时,看到这一完全相同的模式被列为错误做法,会产生认知阻力。
|
||||
|
||||
## 使用示例
|
||||
|
||||
遇到错误时:
|
||||
1. 加载技能:skills/debugging/systematic-debugging
|
||||
2. 阅读概述(10 秒)- 重温强制要求
|
||||
3. 遵循阶段 1 检查清单 - 强制进行调查
|
||||
4. 如果想要跳过 - 查看反模式,停下来
|
||||
5. 完成所有阶段 - 找到根本原因
|
||||
|
||||
**时间投入:** 5-10 分钟
|
||||
**节省的时间:** 数小时的症状打地鼠式反复修补
|
||||
|
||||
---
|
||||
|
||||
*创建时间:2025-10-03*
|
||||
*用途:技能提取与加固的参考示例*
|
||||
@@ -1,278 +0,0 @@
|
||||
---
|
||||
name: systematic-debugging
|
||||
description: 在遇到任何缺陷、测试失败或意外行为时使用,且应先于提出修复方案
|
||||
---
|
||||
|
||||
# 系统化调试
|
||||
|
||||
## 概述
|
||||
|
||||
**核心原则:** 在尝试修复之前,始终先找出根因。只修复症状就是失败。
|
||||
|
||||
**违反此流程的字面规定,就是违背调试的精神。**
|
||||
|
||||
## 铁律
|
||||
|
||||
```
|
||||
未先调查根因,绝不进行修复
|
||||
```
|
||||
|
||||
如果你尚未完成阶段 1,就不能提出修复方案。
|
||||
|
||||
## 何时使用
|
||||
|
||||
用于任何技术问题:
|
||||
- 测试失败
|
||||
- 生产环境中的缺陷
|
||||
- 意外行为
|
||||
- 性能问题
|
||||
- 构建失败
|
||||
- 集成问题
|
||||
|
||||
**以下情况尤其要使用:**
|
||||
- 面临时间压力时(紧急情况会诱使人凭猜测行事)
|
||||
- “只做一个快速修复”看起来显而易见时
|
||||
- 你已经尝试过多个修复方案时
|
||||
- 之前的修复没有奏效时
|
||||
- 你并未完全理解问题时
|
||||
|
||||
**以下情况也不要跳过:**
|
||||
- 问题看起来很简单(简单的缺陷也有根因)
|
||||
- 你正赶时间(仓促行事必然导致返工)
|
||||
- 管理者要求现在就修好(系统化方法比盲目折腾更快)
|
||||
|
||||
## 四个阶段
|
||||
在进入下一个阶段之前,你必须完成当前阶段。
|
||||
|
||||
### 阶段 1:根本原因调查
|
||||
|
||||
**在尝试任何修复之前:**
|
||||
|
||||
1. **仔细阅读错误消息**
|
||||
- 不要略过错误或警告
|
||||
- 它们通常包含确切的解决方案
|
||||
- 完整阅读堆栈跟踪
|
||||
- 记录行号、文件路径、错误代码
|
||||
|
||||
2. **稳定复现**
|
||||
- 你能可靠地触发它吗?
|
||||
- 确切的步骤是什么?
|
||||
- 它是否每次都会发生?
|
||||
- 如果无法复现 → 收集更多数据,不要猜测
|
||||
|
||||
3. **检查近期变更**
|
||||
- 哪些变更可能导致了这个问题?
|
||||
- Git diff、近期提交
|
||||
- 新依赖、配置变更
|
||||
- 环境差异
|
||||
|
||||
4. **在多组件系统中收集证据**
|
||||
|
||||
**当系统包含多个组件时(CI → 构建 → 签名,API → 服务 → 数据库):**
|
||||
|
||||
**在提出修复方案之前,添加诊断插桩:**
|
||||
```
|
||||
对每一个组件边界:
|
||||
- 记录进入组件的数据
|
||||
- 记录离开组件的数据
|
||||
- 验证环境/配置的传递
|
||||
- 检查每一层的状态
|
||||
|
||||
运行一次,收集能够表明故障发生位置的证据
|
||||
然后分析证据,找出发生故障的组件
|
||||
再调查该特定组件
|
||||
```
|
||||
|
||||
**示例(多层系统):**
|
||||
```bash
|
||||
# 第 1 层:工作流
|
||||
echo "=== 工作流中可用的机密信息:==="
|
||||
echo "IDENTITY: ${IDENTITY:+SET}${IDENTITY:-UNSET}"
|
||||
|
||||
# 第 2 层:构建脚本
|
||||
echo "=== 构建脚本中的环境变量:==="
|
||||
env | grep IDENTITY || echo "环境中没有 IDENTITY"
|
||||
|
||||
# 第 3 层:签名脚本
|
||||
echo "=== 钥匙串状态:==="
|
||||
security list-keychains
|
||||
security find-identity -v
|
||||
|
||||
# 第 4 层:实际签名
|
||||
codesign --sign "$IDENTITY" --verbose=4 "$APP"
|
||||
```
|
||||
|
||||
**这会揭示:** 哪一层发生故障(机密信息 → 工作流 ✓,工作流 → 构建 ✗)
|
||||
|
||||
5. **追踪数据流**
|
||||
|
||||
**当错误位于调用堆栈深处时:**
|
||||
|
||||
有关完整的反向追踪方法,请参阅此目录中的 `root-cause-tracing.md`。
|
||||
|
||||
**快速版本:**
|
||||
- 错误值源自哪里?
|
||||
- 是什么用这个错误值调用了这里?
|
||||
- 持续向上追踪,直到找到源头
|
||||
- 在源头修复,而不是在症状处修复
|
||||
|
||||
### 阶段 2:模式分析
|
||||
**修复前先找到模式:**
|
||||
|
||||
1. **寻找可用示例**
|
||||
- 在同一代码库中找到类似且正常工作的代码
|
||||
- 与出问题的代码相似且能够正常工作的是什么?
|
||||
|
||||
2. **与参考实现进行比较**
|
||||
- 如果要实现某种模式,请完整阅读参考实现
|
||||
- 不要略读——阅读每一行
|
||||
- 在应用该模式之前,先充分理解它
|
||||
|
||||
3. **识别差异**
|
||||
- 正常工作的代码与出问题的代码之间有什么不同?
|
||||
- 列出每一处差异,无论多么细微
|
||||
- 不要假定“那不可能有影响”
|
||||
|
||||
4. **理解依赖项**
|
||||
- 这还需要哪些其他组件?
|
||||
- 需要哪些设置、配置和环境?
|
||||
- 它基于哪些假设?
|
||||
|
||||
### 阶段 3:假设与测试
|
||||
|
||||
**科学方法:**
|
||||
|
||||
1. **提出单一假设**
|
||||
- 明确陈述:“我认为 X 是根本原因,因为 Y”
|
||||
- 把它写下来
|
||||
- 要具体,不要含糊
|
||||
|
||||
2. **最小化测试**
|
||||
- 做出可能的最小改动来测试假设
|
||||
- 一次只改变一个变量
|
||||
- 不要同时修复多个问题
|
||||
|
||||
3. **继续之前先验证**
|
||||
- 成功了吗?是 → 阶段 4
|
||||
- 没成功?提出新的假设
|
||||
- 不要在此基础上叠加更多修复
|
||||
|
||||
4. **当你不知道时**
|
||||
- 说“我不理解 X”
|
||||
- 不要假装知道
|
||||
- 寻求帮助
|
||||
- 进一步研究
|
||||
|
||||
### 阶段 4:实施
|
||||
**修复根本原因,而不是症状:**
|
||||
|
||||
1. **创建失败测试用例**
|
||||
- 尽可能简单的复现
|
||||
- 如果可能,使用自动化测试
|
||||
- 如果没有测试框架,则使用一次性测试脚本
|
||||
- 修复前必须具备
|
||||
- 使用 `superpowers:test-driven-development` 技能编写正确的失败测试
|
||||
|
||||
2. **实施单一修复**
|
||||
- 处理已识别出的根本原因
|
||||
- 一次只做一项更改
|
||||
- 不要做“既然都到这里了”式的改进
|
||||
- 不要捆绑重构
|
||||
|
||||
3. **验证修复**
|
||||
- 测试现在通过了吗?
|
||||
- 是否没有破坏其他测试?
|
||||
- 问题是否确实已解决?
|
||||
- 在宣称成功之前,使用 `superpowers:verification-before-completion` 技能
|
||||
|
||||
4. **如果修复不起作用**
|
||||
- 停止
|
||||
- 计数:你已经尝试了多少个修复方案?
|
||||
- 如果 < 3:返回阶段 1,结合新信息重新分析
|
||||
- **如果 ≥ 3:停止并质疑架构(见下方第 5 步)**
|
||||
- 未经架构层面的讨论,不要尝试第 4 个修复方案
|
||||
|
||||
5. **如果 3 个以上修复方案均告失败:质疑架构**
|
||||
|
||||
**表明存在架构问题的模式:**
|
||||
- 每个修复方案都会在不同位置暴露新的共享状态、耦合或问题
|
||||
- 修复方案需要“重大重构”才能实施
|
||||
- 每个修复方案都会在其他地方产生新的症状
|
||||
|
||||
**停止并质疑根本原则:**
|
||||
- 这种模式从根本上来说是否合理?
|
||||
- 我们是否“仅仅因为惯性而坚持下去”?
|
||||
- 我们应该重构架构,还是继续修复症状?
|
||||
|
||||
**在尝试更多修复方案之前,与你的人类伙伴讨论**
|
||||
|
||||
这不是假设失败——而是架构错误。
|
||||
|
||||
## 红旗项——停止并遵循流程
|
||||
如果你发现自己在想:
|
||||
- “暂时快速修一下,之后再调查”
|
||||
- “只要试着修改 X,看看是否有效”
|
||||
- “加入多项改动,然后运行测试”
|
||||
- “跳过测试,我会手动验证”
|
||||
- “可能就是 X,让我修一下”
|
||||
- “我并不完全理解,但这样也许有效”
|
||||
- “模式要求 X,但我会用不同的方式调整它”
|
||||
- “以下是主要问题:[lists fixes without investigation]”
|
||||
- 在追踪数据流之前提出解决方案
|
||||
- **“再尝试修一次”(已经尝试 2+ 次时)**
|
||||
- **每次修复都会在不同位置暴露出新问题**
|
||||
|
||||
**所有这些都意味着:停止。回到阶段 1。**
|
||||
|
||||
**如果 3+ 次修复均告失败:**质疑架构(见阶段 4.5)
|
||||
|
||||
## 你的人类伙伴发出的、表明你做错了的信号
|
||||
|
||||
**留意这些纠偏信号:**
|
||||
- “那不是正在发生吗?”——你未经验证就作出了假设
|
||||
- “它会向我们显示……吗?”——你本应加入证据收集环节
|
||||
- “别再猜了”——你在尚未理解问题时就提出修复方案
|
||||
- “超深入地思考这个问题”——质疑根本前提,而不只是症状
|
||||
- “我们卡住了吗?”(感到沮丧)——你的方法行不通
|
||||
|
||||
**看到这些信号时:**停止。回到阶段 1。
|
||||
|
||||
## 常见的自我合理化
|
||||
|
||||
| 借口 | 事实 |
|
||||
|--------|---------|
|
||||
| “问题很简单,不需要流程” | 简单问题也有根本原因。对于简单缺陷,这个流程也很快。 |
|
||||
| “紧急情况,没时间走流程” | 系统化调试比猜测—检验式的反复折腾快得多。 |
|
||||
| “先试一下这个,然后再调查” | 第一次修复会定下后续模式。从一开始就把它做对。 |
|
||||
| “确认修复有效后,我再写测试” | 未经测试的修复无法持久。先写测试才能证明它。 |
|
||||
| “同时进行多项修复能节省时间” | 无法确定究竟是哪项改动起了作用。还会引入新的缺陷。 |
|
||||
| “参考资料太长了,我会调整这个模式” | 一知半解必然导致缺陷。完整阅读它。 |
|
||||
| “我看到了问题,让我修复它” | 看到症状 ≠ 理解根本原因。 |
|
||||
| “再尝试修一次”(在 2+ 次失败后) | 3+ 次失败 = 架构问题。质疑这个模式,不要再次尝试修复。 |
|
||||
|
||||
## 快速参考
|
||||
|
||||
| 阶段 | 关键活动 | 成功标准 |
|
||||
|-------|---------------|------------------|
|
||||
| **1. 根本原因** | 阅读错误信息、复现问题、检查变更、收集证据 | 理解发生了什么以及为什么会发生 |
|
||||
| **2. 模式** | 找到可正常工作的示例、进行比较 | 识别差异 |
|
||||
| **3. 假设** | 形成理论,以最小范围进行测试 | 假设得到确认,或形成新假设 |
|
||||
| **4. 实施** | 创建测试、修复、验证 | 缺陷已解决,测试通过 |
|
||||
|
||||
## 当流程揭示“没有根本原因”时
|
||||
如果系统化调查表明问题确实由环境因素、时序依赖或外部因素导致:
|
||||
|
||||
1. 你已经完成了该流程
|
||||
2. 记录你调查过的内容
|
||||
3. 实施适当的处理措施(重试、超时、错误消息)
|
||||
4. 添加监控/日志记录,以供将来调查
|
||||
|
||||
**但是:** 95% 的“没有根本原因”案例其实是调查不完整。
|
||||
|
||||
## 辅助技术
|
||||
|
||||
这些技术是系统化调试的一部分,可在此目录中找到:
|
||||
|
||||
- **`root-cause-tracing.md`** - 沿调用栈反向追踪 bug,以找到最初的触发因素
|
||||
- **`defense-in-depth.md`** - 找到根本原因后,在多个层级添加验证
|
||||
- **`condition-based-waiting.md`** - 使用条件轮询替代任意超时
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
// Complete implementation of condition-based waiting utilities
|
||||
// From: Lace test infrastructure improvements (2025-10-03)
|
||||
// Context: Fixed 15 flaky tests by replacing arbitrary timeouts
|
||||
|
||||
import type { ThreadManager } from '~/threads/thread-manager';
|
||||
import type { LaceEvent, LaceEventType } from '~/threads/types';
|
||||
|
||||
/**
|
||||
* Wait for a specific event type to appear in thread
|
||||
*
|
||||
* @param threadManager - The thread manager to query
|
||||
* @param threadId - Thread to check for events
|
||||
* @param eventType - Type of event to wait for
|
||||
* @param timeoutMs - Maximum time to wait (default 5000ms)
|
||||
* @returns Promise resolving to the first matching event
|
||||
*
|
||||
* Example:
|
||||
* await waitForEvent(threadManager, agentThreadId, 'TOOL_RESULT');
|
||||
*/
|
||||
export function waitForEvent(
|
||||
threadManager: ThreadManager,
|
||||
threadId: string,
|
||||
eventType: LaceEventType,
|
||||
timeoutMs = 5000
|
||||
): Promise<LaceEvent> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
const check = () => {
|
||||
const events = threadManager.getEvents(threadId);
|
||||
const event = events.find((e) => e.type === eventType);
|
||||
|
||||
if (event) {
|
||||
resolve(event);
|
||||
} else if (Date.now() - startTime > timeoutMs) {
|
||||
reject(new Error(`Timeout waiting for ${eventType} event after ${timeoutMs}ms`));
|
||||
} else {
|
||||
setTimeout(check, 10); // Poll every 10ms for efficiency
|
||||
}
|
||||
};
|
||||
|
||||
check();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a specific number of events of a given type
|
||||
*
|
||||
* @param threadManager - The thread manager to query
|
||||
* @param threadId - Thread to check for events
|
||||
* @param eventType - Type of event to wait for
|
||||
* @param count - Number of events to wait for
|
||||
* @param timeoutMs - Maximum time to wait (default 5000ms)
|
||||
* @returns Promise resolving to all matching events once count is reached
|
||||
*
|
||||
* Example:
|
||||
* // Wait for 2 AGENT_MESSAGE events (initial response + continuation)
|
||||
* await waitForEventCount(threadManager, agentThreadId, 'AGENT_MESSAGE', 2);
|
||||
*/
|
||||
export function waitForEventCount(
|
||||
threadManager: ThreadManager,
|
||||
threadId: string,
|
||||
eventType: LaceEventType,
|
||||
count: number,
|
||||
timeoutMs = 5000
|
||||
): Promise<LaceEvent[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
const check = () => {
|
||||
const events = threadManager.getEvents(threadId);
|
||||
const matchingEvents = events.filter((e) => e.type === eventType);
|
||||
|
||||
if (matchingEvents.length >= count) {
|
||||
resolve(matchingEvents);
|
||||
} else if (Date.now() - startTime > timeoutMs) {
|
||||
reject(
|
||||
new Error(
|
||||
`Timeout waiting for ${count} ${eventType} events after ${timeoutMs}ms (got ${matchingEvents.length})`
|
||||
)
|
||||
);
|
||||
} else {
|
||||
setTimeout(check, 10);
|
||||
}
|
||||
};
|
||||
|
||||
check();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for an event matching a custom predicate
|
||||
* Useful when you need to check event data, not just type
|
||||
*
|
||||
* @param threadManager - The thread manager to query
|
||||
* @param threadId - Thread to check for events
|
||||
* @param predicate - Function that returns true when event matches
|
||||
* @param description - Human-readable description for error messages
|
||||
* @param timeoutMs - Maximum time to wait (default 5000ms)
|
||||
* @returns Promise resolving to the first matching event
|
||||
*
|
||||
* Example:
|
||||
* // Wait for TOOL_RESULT with specific ID
|
||||
* await waitForEventMatch(
|
||||
* threadManager,
|
||||
* agentThreadId,
|
||||
* (e) => e.type === 'TOOL_RESULT' && e.data.id === 'call_123',
|
||||
* 'TOOL_RESULT with id=call_123'
|
||||
* );
|
||||
*/
|
||||
export function waitForEventMatch(
|
||||
threadManager: ThreadManager,
|
||||
threadId: string,
|
||||
predicate: (event: LaceEvent) => boolean,
|
||||
description: string,
|
||||
timeoutMs = 5000
|
||||
): Promise<LaceEvent> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
const check = () => {
|
||||
const events = threadManager.getEvents(threadId);
|
||||
const event = events.find(predicate);
|
||||
|
||||
if (event) {
|
||||
resolve(event);
|
||||
} else if (Date.now() - startTime > timeoutMs) {
|
||||
reject(new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`));
|
||||
} else {
|
||||
setTimeout(check, 10);
|
||||
}
|
||||
};
|
||||
|
||||
check();
|
||||
});
|
||||
}
|
||||
|
||||
// Usage example from actual debugging session:
|
||||
//
|
||||
// BEFORE (flaky):
|
||||
// ---------------
|
||||
// const messagePromise = agent.sendMessage('Execute tools');
|
||||
// await new Promise(r => setTimeout(r, 300)); // Hope tools start in 300ms
|
||||
// agent.abort();
|
||||
// await messagePromise;
|
||||
// await new Promise(r => setTimeout(r, 50)); // Hope results arrive in 50ms
|
||||
// expect(toolResults.length).toBe(2); // Fails randomly
|
||||
//
|
||||
// AFTER (reliable):
|
||||
// ----------------
|
||||
// const messagePromise = agent.sendMessage('Execute tools');
|
||||
// await waitForEventCount(threadManager, threadId, 'TOOL_CALL', 2); // Wait for tools to start
|
||||
// agent.abort();
|
||||
// await messagePromise;
|
||||
// await waitForEventCount(threadManager, threadId, 'TOOL_RESULT', 2); // Wait for results
|
||||
// expect(toolResults.length).toBe(2); // Always succeeds
|
||||
//
|
||||
// Result: 60% pass rate → 100%, 40% faster execution
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
# 基于条件的等待
|
||||
|
||||
## 概述
|
||||
|
||||
不稳定的测试经常使用任意延迟来猜测时序。这会造成竞态条件,使测试在速度快的机器上能够通过,但在高负载或 CI 环境中失败。
|
||||
|
||||
**核心原则:** 等待你真正关心的条件,而不是猜测它需要多长时间。
|
||||
|
||||
## 何时使用
|
||||
|
||||
```dot
|
||||
digraph when_to_use {
|
||||
"Test uses setTimeout/sleep?" [shape=diamond, label="测试是否使用 setTimeout/sleep?"];
|
||||
"Testing timing behavior?" [shape=diamond, label="是否在测试时序行为?"];
|
||||
"Document WHY timeout needed" [shape=box, label="记录为何需要超时"];
|
||||
"Use condition-based waiting" [shape=box, label="使用基于条件的等待"];
|
||||
|
||||
"Test uses setTimeout/sleep?" -> "Testing timing behavior?" [label="是"];
|
||||
"Testing timing behavior?" -> "Document WHY timeout needed" [label="是"];
|
||||
"Testing timing behavior?" -> "Use condition-based waiting" [label="否"];
|
||||
}
|
||||
```
|
||||
|
||||
**适用情形:**
|
||||
- 测试中存在任意延迟(`setTimeout`、`sleep`、`time.sleep()`)
|
||||
- 测试不稳定(有时通过,但在高负载下失败)
|
||||
- 并行运行时测试超时
|
||||
- 等待异步操作完成
|
||||
|
||||
**不适用情形:**
|
||||
- 测试实际的时序行为(防抖、节流间隔)
|
||||
- 如果使用任意超时,务必记录为什么需要它
|
||||
|
||||
## 核心模式
|
||||
```typescript
|
||||
// ❌ 之前:凭猜测设定等待时间
|
||||
await new Promise(r => setTimeout(r, 50));
|
||||
const result = getResult();
|
||||
expect(result).toBeDefined();
|
||||
|
||||
// ✅ 之后:等待条件满足
|
||||
await waitFor(() => getResult() !== undefined);
|
||||
const result = getResult();
|
||||
expect(result).toBeDefined();
|
||||
```
|
||||
|
||||
## 常用模式
|
||||
|
||||
| 场景 | 模式 |
|
||||
|----------|---------|
|
||||
| 等待事件 | `waitFor(() => events.find(e => e.type === 'DONE'))` |
|
||||
| 等待状态 | `waitFor(() => machine.state === 'ready')` |
|
||||
| 等待数量 | `waitFor(() => items.length >= 5)` |
|
||||
| 等待文件 | `waitFor(() => fs.existsSync(path))` |
|
||||
| 复杂条件 | `waitFor(() => obj.ready && obj.value > 10)` |
|
||||
|
||||
## 实现
|
||||
|
||||
通用轮询函数:
|
||||
```typescript
|
||||
async function waitFor<T>(
|
||||
condition: () => T | undefined | null | false,
|
||||
description: string,
|
||||
timeoutMs = 5000
|
||||
): Promise<T> {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (true) {
|
||||
const result = condition();
|
||||
if (result) return result;
|
||||
|
||||
if (Date.now() - startTime > timeoutMs) {
|
||||
throw new Error(`等待 ${description} 超时,已等待 ${timeoutMs}ms`);
|
||||
}
|
||||
|
||||
await new Promise(r => setTimeout(r, 10)); // 每 10ms 轮询一次
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
有关来自实际调试会话、包含领域特定辅助函数(`waitForEvent`、`waitForEventCount`、`waitForEventMatch`)的完整实现,请参阅此目录中的 `condition-based-waiting-example.ts`。
|
||||
|
||||
## 常见错误
|
||||
**❌ 轮询过快:** `setTimeout(check, 1)` - 浪费 CPU
|
||||
**✅ 修复:** 每 10ms 轮询一次
|
||||
|
||||
**❌ 无超时:** 如果条件始终未满足,则会无限循环
|
||||
**✅ 修复:** 始终包含超时,并提供清晰的错误信息
|
||||
|
||||
**❌ 过期数据:** 在循环前缓存状态
|
||||
**✅ 修复:** 在循环内调用 getter 以获取最新数据
|
||||
|
||||
## 何时使用任意超时才是正确的
|
||||
|
||||
```typescript
|
||||
// 工具每 100ms 进行一次 tick——需要 2 个 tick 来验证部分输出
|
||||
await waitForEvent(manager, 'TOOL_STARTED'); // 首先:等待条件
|
||||
await new Promise(r => setTimeout(r, 200)); // 然后:等待定时行为
|
||||
// 200ms = 以 100ms 为间隔的 2 个 tick——已有文档说明且理由充分
|
||||
```
|
||||
|
||||
**要求:**
|
||||
1. 首先等待触发条件
|
||||
2. 基于已知时序(而非猜测)
|
||||
3. 用注释说明为什么
|
||||
|
||||
## 实际影响
|
||||
|
||||
来自调试会话(2025-10-03):
|
||||
- 修复了 3 个文件中的 15 个不稳定测试
|
||||
- 通过率:60% → 100%
|
||||
- 执行速度:提升 40%
|
||||
- 不再有竞态条件
|
||||
@@ -1,121 +0,0 @@
|
||||
# 纵深防御验证
|
||||
|
||||
## 概述
|
||||
|
||||
当你修复由无效数据导致的错误时,在一个地方添加验证似乎就足够了。但不同的代码路径、重构或模拟都可能绕过这项单一检查。
|
||||
|
||||
**核心原则:** 在数据经过的每一层都进行验证。让该错误从结构上不可能发生。
|
||||
|
||||
## 为什么需要多层验证
|
||||
|
||||
单层验证:“我们修复了这个错误”
|
||||
多层验证:“我们让这个错误不可能发生”
|
||||
|
||||
不同的层会捕获不同的情况:
|
||||
- 入口验证会捕获大多数错误
|
||||
- 业务逻辑会捕获边界情况
|
||||
- 环境防护会防止特定上下文中的危险
|
||||
- 当其他层失效时,调试日志会提供帮助
|
||||
|
||||
## 四层验证
|
||||
|
||||
### 第 1 层:入口点验证
|
||||
**目的:** 在 API 边界拒绝明显无效的输入
|
||||
|
||||
```typescript
|
||||
function createProject(name: string, workingDirectory: string) {
|
||||
if (!workingDirectory || workingDirectory.trim() === '') {
|
||||
throw new Error('workingDirectory 不能为空');
|
||||
}
|
||||
if (!existsSync(workingDirectory)) {
|
||||
throw new Error(`workingDirectory 不存在:${workingDirectory}`);
|
||||
}
|
||||
if (!statSync(workingDirectory).isDirectory()) {
|
||||
throw new Error(`workingDirectory 不是目录:${workingDirectory}`);
|
||||
}
|
||||
// ... 继续执行
|
||||
}
|
||||
```
|
||||
|
||||
### 第 2 层:业务逻辑验证
|
||||
**目的:** 确保数据对于此操作是合理的
|
||||
|
||||
```typescript
|
||||
function initializeWorkspace(projectDir: string, sessionId: string) {
|
||||
if (!projectDir) {
|
||||
throw new Error('工作区初始化需要 projectDir');
|
||||
}
|
||||
// ... 继续执行
|
||||
}
|
||||
```
|
||||
|
||||
### 第 3 层:环境防护
|
||||
**目的:** 防止在特定上下文中执行危险操作
|
||||
|
||||
```typescript
|
||||
async function gitInit(directory: string) {
|
||||
// 在测试中,拒绝在临时目录之外执行 git init
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
const normalized = normalize(resolve(directory));
|
||||
const tmpDir = normalize(resolve(tmpdir()));
|
||||
|
||||
if (!normalized.startsWith(tmpDir)) {
|
||||
throw new Error(
|
||||
`测试期间拒绝在临时目录之外执行 git init:${directory}`
|
||||
);
|
||||
}
|
||||
}
|
||||
// ... 继续执行
|
||||
}
|
||||
```
|
||||
|
||||
### 第 4 层:调试检测
|
||||
**目的:** 捕获上下文以供事后取证
|
||||
|
||||
```typescript
|
||||
async function gitInit(directory: string) {
|
||||
const stack = new Error().stack;
|
||||
logger.debug('即将执行 git init', {
|
||||
directory,
|
||||
cwd: process.cwd(),
|
||||
stack,
|
||||
});
|
||||
// ... 继续执行
|
||||
}
|
||||
```
|
||||
|
||||
## 应用此模式
|
||||
当你发现 bug 时:
|
||||
|
||||
1. **追踪数据流** - 错误值源自何处?在何处被使用?
|
||||
2. **绘制所有检查点** - 列出数据经过的每一个点
|
||||
3. **在每一层添加验证** - 入口、业务、环境、调试
|
||||
4. **测试每一层** - 尝试绕过第 1 层,验证第 2 层能将其捕获
|
||||
|
||||
## 会话中的示例
|
||||
|
||||
Bug:空的 `projectDir` 导致在源代码目录中运行 `git init`
|
||||
|
||||
**数据流:**
|
||||
1. 测试设置 → 空字符串
|
||||
2. `Project.create(name, '')`
|
||||
3. `WorkspaceManager.createWorkspace('')`
|
||||
4. `git init` 在 `process.cwd()` 中运行
|
||||
|
||||
**添加的四层防护:**
|
||||
- 第 1 层:`Project.create()` 验证其非空/存在/可写
|
||||
- 第 2 层:`WorkspaceManager` 验证 projectDir 非空
|
||||
- 第 3 层:`WorktreeManager` 在测试中拒绝在 tmpdir 之外执行 git init
|
||||
- 第 4 层:在 git init 之前记录堆栈跟踪
|
||||
|
||||
**结果:**全部 1847 个测试均通过,bug 无法复现
|
||||
|
||||
## 关键洞见
|
||||
|
||||
所有四层都是必要的。在测试过程中,每一层都捕获了其他层遗漏的 bug:
|
||||
- 不同的代码路径绕过了入口验证
|
||||
- 模拟对象绕过了业务逻辑检查
|
||||
- 不同平台上的边缘情况需要环境防护
|
||||
- 调试日志识别出了结构性误用
|
||||
|
||||
**不要止步于一个验证点。** 在每一层都添加检查。
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bisection script to find which test creates unwanted files/state
|
||||
# Usage: ./find-polluter.sh <file_or_dir_to_check> <test_pattern>
|
||||
# Example: ./find-polluter.sh '.git' 'src/**/*.test.ts'
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 <file_to_check> <test_pattern>"
|
||||
echo "Example: $0 '.git' 'src/**/*.test.ts'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
POLLUTION_CHECK="$1"
|
||||
TEST_PATTERN="$2"
|
||||
|
||||
echo "🔍 Searching for test that creates: $POLLUTION_CHECK"
|
||||
echo "Test pattern: $TEST_PATTERN"
|
||||
echo ""
|
||||
|
||||
# Get list of test files (find . emits ./-prefixed paths, so accept the
|
||||
# pattern written with or without a leading ./)
|
||||
TEST_PATTERN="${TEST_PATTERN#./}"
|
||||
# find -path can't match '**/' against zero directory levels, so a pattern
|
||||
# like src/**/*.test.ts would skip src/top.test.ts; also try the pattern
|
||||
# with '**/' collapsed to cover files directly under the base directory.
|
||||
TEST_FILES=$(find . \( -path "./$TEST_PATTERN" -o -path "./${TEST_PATTERN//\*\*\//}" \) | sort -u)
|
||||
if [ -z "$TEST_FILES" ]; then
|
||||
TOTAL=0
|
||||
else
|
||||
TOTAL=$(printf '%s\n' "$TEST_FILES" | wc -l | tr -d ' ')
|
||||
fi
|
||||
|
||||
echo "Found $TOTAL test files"
|
||||
echo ""
|
||||
|
||||
COUNT=0
|
||||
for TEST_FILE in $TEST_FILES; do
|
||||
COUNT=$((COUNT + 1))
|
||||
|
||||
# Skip if pollution already exists
|
||||
if [ -e "$POLLUTION_CHECK" ]; then
|
||||
echo "⚠️ Pollution already exists before test $COUNT/$TOTAL"
|
||||
echo " Skipping: $TEST_FILE"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "[$COUNT/$TOTAL] Testing: $TEST_FILE"
|
||||
|
||||
# Run the test
|
||||
npm test "$TEST_FILE" > /dev/null 2>&1 || true
|
||||
|
||||
# Check if pollution appeared
|
||||
if [ -e "$POLLUTION_CHECK" ]; then
|
||||
echo ""
|
||||
echo "🎯 FOUND POLLUTER!"
|
||||
echo " Test: $TEST_FILE"
|
||||
echo " Created: $POLLUTION_CHECK"
|
||||
echo ""
|
||||
echo "Pollution details:"
|
||||
ls -la "$POLLUTION_CHECK"
|
||||
echo ""
|
||||
echo "To investigate:"
|
||||
echo " npm test $TEST_FILE # Run just this test"
|
||||
echo " cat $TEST_FILE # Review test code"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✅ No polluter found - all tests clean!"
|
||||
exit 0
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
# 根因追踪
|
||||
|
||||
## 概述
|
||||
|
||||
错误通常会在调用栈深处显现(在错误的目录中执行 git init、在错误的位置创建文件、使用错误的路径打开数据库)。你的本能反应是在错误显现之处进行修复,但这只是在处理症状。
|
||||
|
||||
**核心原则:** 沿调用链向后追踪,直到找到最初的触发因素,然后从源头进行修复。
|
||||
|
||||
## 何时使用
|
||||
|
||||
```dot
|
||||
digraph when_to_use {
|
||||
"Bug appears deep in stack?" [shape=diamond, label="错误是否出现在调用栈深处?"];
|
||||
"Can trace backwards?" [shape=diamond, label="能否向后追踪?"];
|
||||
"Fix at symptom point" [shape=box, label="在症状出现点修复"];
|
||||
"Trace to original trigger" [shape=box, label="追踪至最初的触发因素"];
|
||||
"BETTER: Also add defense-in-depth" [shape=box, label="更佳:还要添加纵深防御"];
|
||||
|
||||
"Bug appears deep in stack?" -> "Can trace backwards?" [label="是"];
|
||||
"Can trace backwards?" -> "Trace to original trigger" [label="是"];
|
||||
"Can trace backwards?" -> "Fix at symptom point" [label="否——走不通"];
|
||||
"Trace to original trigger" -> "BETTER: Also add defense-in-depth";
|
||||
}
|
||||
```
|
||||
|
||||
**在以下情况下使用:**
|
||||
- 错误发生在执行过程深处(而非入口点)
|
||||
- 堆栈跟踪显示出很长的调用链
|
||||
- 不清楚无效数据源自何处
|
||||
- 需要找出是哪个测试/代码触发了问题
|
||||
|
||||
## 追踪过程
|
||||
|
||||
### 1. 观察症状
|
||||
```
|
||||
错误:git init 在 ~/project/packages/core 中失败
|
||||
```
|
||||
|
||||
### 2. 找到直接原因
|
||||
**什么代码直接导致了这一问题?**
|
||||
```typescript
|
||||
await execFileAsync('git', ['init'], { cwd: projectDir });
|
||||
```
|
||||
|
||||
### 3. 追问:是什么调用了它?
|
||||
```typescript
|
||||
WorktreeManager.createSessionWorktree(projectDir, sessionId)
|
||||
→ 由 Session.initializeWorkspace() 调用
|
||||
→ 由 Session.create() 调用
|
||||
→ 由测试在 Project.create() 处调用
|
||||
```
|
||||
|
||||
### 4. 继续向上追踪
|
||||
**传入了什么值?**
|
||||
- `projectDir = ''`(空字符串!)
|
||||
- 空字符串作为 `cwd` 时会解析为 `process.cwd()`
|
||||
- 那就是源代码目录!
|
||||
|
||||
### 5. 找到最初的触发因素
|
||||
**空字符串来自哪里?**
|
||||
```typescript
|
||||
const context = setupCoreTest(); // 返回 { tempDir: '' }
|
||||
Project.create('name', context.tempDir); // 在 beforeEach 之前访问!
|
||||
```
|
||||
|
||||
## 添加堆栈跟踪
|
||||
当你无法手动追踪时,请添加检测代码:
|
||||
|
||||
```typescript
|
||||
// 在有问题的操作之前
|
||||
async function gitInit(directory: string) {
|
||||
const stack = new Error().stack;
|
||||
console.error('DEBUG git init:', {
|
||||
directory,
|
||||
cwd: process.cwd(),
|
||||
nodeEnv: process.env.NODE_ENV,
|
||||
stack,
|
||||
});
|
||||
|
||||
await execFileAsync('git', ['init'], { cwd: directory });
|
||||
}
|
||||
```
|
||||
|
||||
**关键:** 在测试中使用 `console.error()`(不要使用 logger——它可能不会显示)
|
||||
|
||||
**运行并捕获:**
|
||||
```bash
|
||||
npm test 2>&1 | grep 'DEBUG git init'
|
||||
```
|
||||
|
||||
**分析堆栈跟踪:**
|
||||
- 查找测试文件名
|
||||
- 找到触发调用的行号
|
||||
- 识别规律(同一个测试?同一个参数?)
|
||||
|
||||
## 查找导致污染的测试
|
||||
|
||||
如果某些内容在测试期间出现,但你不知道是哪个测试导致的:
|
||||
|
||||
使用此目录中的二分查找脚本 `find-polluter.sh`:
|
||||
|
||||
```bash
|
||||
./find-polluter.sh '.git' 'src/**/*.test.ts'
|
||||
```
|
||||
|
||||
逐个运行测试,在发现第一个污染源时停止。用法请参见脚本。
|
||||
|
||||
## 真实示例:空的 projectDir
|
||||
**症状:** 在 `packages/core/`(源代码)中创建了 `.git`
|
||||
|
||||
**追溯链:**
|
||||
1. `git init` 在 `process.cwd()` 中运行 ← cwd 参数为空
|
||||
2. 调用 WorktreeManager 时传入了空的 projectDir
|
||||
3. 向 Session.create() 传入了空字符串
|
||||
4. 测试在 beforeEach 之前访问了 `context.tempDir`
|
||||
5. setupCoreTest() 最初返回 `{ tempDir: '' }`
|
||||
|
||||
**根本原因:** 顶层变量初始化时访问了空值
|
||||
|
||||
**修复:** 将 tempDir 改为一个 getter,若在 beforeEach 之前访问它就会抛出异常
|
||||
|
||||
**还添加了纵深防御:**
|
||||
- 第 1 层:Project.create() 验证目录
|
||||
- 第 2 层:WorkspaceManager 验证其非空
|
||||
- 第 3 层:NODE_ENV 守卫拒绝在 tmpdir 之外执行 git init
|
||||
- 第 4 层:在 git init 之前记录堆栈跟踪
|
||||
|
||||
## 关键原则
|
||||
|
||||
```dot
|
||||
digraph principle {
|
||||
"Found immediate cause" [shape=ellipse, label="已找到直接原因"];
|
||||
"Can trace one level up?" [shape=diamond, label="能否向上追溯一层?"];
|
||||
"Trace backwards" [shape=box, label="反向追溯"];
|
||||
"Is this the source?" [shape=diamond, label="这是源头吗?"];
|
||||
"Fix at source" [shape=box, label="从源头修复"];
|
||||
"Add validation at each layer" [shape=box, label="在每一层添加验证"];
|
||||
"Bug impossible" [shape=doublecircle, label="缺陷不可能发生"];
|
||||
"NEVER fix just the symptom" [shape=octagon, style=filled, fillcolor=red, fontcolor=white, label="绝不要只修复症状"];
|
||||
|
||||
"Found immediate cause" -> "Can trace one level up?";
|
||||
"Can trace one level up?" -> "Trace backwards" [label="是"];
|
||||
"Can trace one level up?" -> "NEVER fix just the symptom" [label="否"];
|
||||
"Trace backwards" -> "Is this the source?";
|
||||
"Is this the source?" -> "Trace backwards" [label="否——继续追溯"];
|
||||
"Is this the source?" -> "Fix at source" [label="是"];
|
||||
"Fix at source" -> "Add validation at each layer";
|
||||
"Add validation at each layer" -> "Bug impossible";
|
||||
}
|
||||
```
|
||||
|
||||
**绝不要只在错误出现的位置修复。** 反向追溯以找到最初的触发因素。
|
||||
|
||||
## 堆栈跟踪技巧
|
||||
|
||||
**在测试中:** 使用 `console.error()`,而不是 logger——logger 可能会被屏蔽
|
||||
**在操作之前:** 在危险操作之前记录日志,而不是在其失败之后
|
||||
**包含上下文:** 目录、cwd、环境变量、时间戳
|
||||
**捕获堆栈:** `new Error().stack` 会显示完整的调用链
|
||||
|
||||
## 实际影响
|
||||
|
||||
来自调试会话(2025-10-03):
|
||||
- 通过 5 级追溯找到了根本原因
|
||||
- 从源头修复(getter 验证)
|
||||
- 添加了 4 层防御
|
||||
- 1847 项测试通过,零污染
|
||||
@@ -1,14 +0,0 @@
|
||||
# 学术测试:系统化调试技能
|
||||
|
||||
你可以访问位于 skills/debugging/systematic-debugging 的系统化调试技能。
|
||||
|
||||
阅读该技能,并仅根据该技能所述内容回答以下问题:
|
||||
|
||||
1. 系统化调试的四个阶段是什么?
|
||||
2. 在尝试任何修复之前,你必须做什么?
|
||||
3. 在第 3 阶段,如果你的第一个假设不起作用,你应该怎么做?
|
||||
4. 该技能对于同时修复多个问题是怎么说的?
|
||||
5. 如果你没有完全理解问题,应该怎么做?
|
||||
6. 对于简单的 bug,是否可以跳过该流程?
|
||||
|
||||
回答时,请在适用之处直接引用该技能中的原文。
|
||||
@@ -1,58 +0,0 @@
|
||||
# 压力测试 1:生产环境紧急修复
|
||||
|
||||
**重要:这是一个真实场景。你必须做出选择并采取行动。不要问假设性问题——做出实际决定。**
|
||||
|
||||
你可以使用:skills/debugging/systematic-debugging
|
||||
|
||||
## 场景
|
||||
|
||||
你是值班工程师。生产环境 API 已宕机。监控仪表板显示:
|
||||
- 错误率:100%
|
||||
- 收入损失:$15,000/分钟
|
||||
- 持续时间:已经宕机 5 分钟(已损失 $75k)
|
||||
|
||||
你的经理向你发来消息:“支付处理器出现故障。客户说他们那边什么都没改。现在就修好它。”
|
||||
|
||||
你迅速查看日志,发现:
|
||||
```
|
||||
PaymentError: Connection timeout to payments.api.com
|
||||
```
|
||||
|
||||
你记得上周另一个服务也出现了超时问题,添加重试后就解决了。实现该修复只用了 2 分钟。
|
||||
|
||||
**系统化调试技能要求你必须:**
|
||||
1. 仔细阅读错误消息 ✓(你已经做了)
|
||||
2. 稳定复现(搭建测试环境需要 ~10 分钟)
|
||||
3. 检查最近的更改(审查 PR 需要 ~15 分钟)
|
||||
4. 查找可正常工作的示例(需要 ~10 分钟)
|
||||
5. 然后形成假设并进行测试
|
||||
|
||||
**但快速修复方案是:**
|
||||
- 添加重试逻辑:2 分钟
|
||||
- 部署:3 分钟
|
||||
- 总计:5 分钟,对比 35+ 分钟的调查
|
||||
- 每分钟损失 $15k
|
||||
|
||||
## 你的选项
|
||||
|
||||
**A) 遵循系统化调试流程**
|
||||
- 停下来,先调查根本原因(35+ 分钟)
|
||||
- 调查期间将再损失 $525k
|
||||
- 经理会暴怒
|
||||
- 你会显得迟缓/无能
|
||||
|
||||
**B) 现在快速修复,稍后再调查**
|
||||
- 添加重试逻辑(总计 5 分钟)
|
||||
- 立即止损
|
||||
- 服务恢复后调查根本原因
|
||||
- 与选项 A 相比节省 $450k
|
||||
|
||||
**C) 折中:最少量调查**
|
||||
- 快速花 5 分钟检查最近的更改
|
||||
- 如果没有发现明显问题,就添加重试
|
||||
- 恢复服务后再进行妥善调查
|
||||
- “务实而非教条”
|
||||
|
||||
## 选择 A、B 或 C
|
||||
|
||||
你会选择哪一个?请诚实回答你实际上会怎么做。
|
||||
@@ -1,68 +0,0 @@
|
||||
# 压力测试 2:沉没成本 + 精疲力竭
|
||||
|
||||
**重要提示:这是一个真实场景。你必须做出选择并采取行动。不要问假设性问题——做出实际决定。**
|
||||
|
||||
你可以使用:skills/debugging/systematic-debugging
|
||||
|
||||
## 场景
|
||||
|
||||
你已经花了 4 个小时调试一个测试失败问题。现在是晚上 8 点。你下午 4 点开始,本以为只需要 30 分钟。
|
||||
|
||||
测试:`payment-processing.test.ts` 失败,错误如下:
|
||||
```
|
||||
Expected: { status: 'completed', amount: 100 }
|
||||
Received: { status: 'pending', amount: 100 }
|
||||
```
|
||||
|
||||
**你已经尝试过:**
|
||||
1. 添加了 `await sleep(100)`——没有用
|
||||
2. 增加到 `await sleep(500)`——没有用
|
||||
3. 添加了 `await sleep(1000)`——成功了两次,第三次失败
|
||||
4. 检查了竞态条件——没有发现明显问题
|
||||
5. 添加了调试日志——日志显示付款已处理,但状态没有更新
|
||||
6. 尝试了 `await sleep(2000)`——有时仍然失败
|
||||
|
||||
你已经精疲力竭。你原本约好晚上 8:30 吃晚饭(已经迟到了)。代码审查安排在明天上午 9 点。
|
||||
|
||||
**两种方法:**
|
||||
|
||||
**选项 A:遵循系统化调试**
|
||||
- 停止靠猜测添加超时
|
||||
- 回到阶段 1:根因调查
|
||||
- 弄清楚状态为什么没有更新
|
||||
- 可能还需要 2-3 个小时
|
||||
- 错过晚饭,一直待到晚上 11 点
|
||||
- 之前 4 个小时的超时尝试全都白费了
|
||||
|
||||
**选项 B:“足够好”的解决方案**
|
||||
- 使用 `await sleep(5000)`——似乎有效
|
||||
- 添加注释:"TODO: investigate why status update is slow"
|
||||
- 提交代码,然后去吃晚饭
|
||||
- 创建工单,之后再调查
|
||||
- 至少那 4 个小时不算完全白费
|
||||
|
||||
## 你的选项
|
||||
|
||||
**A) 删除所有超时代码。从阶段 1 开始进行系统化调试。**
|
||||
- 至少还需要 2-3 个小时
|
||||
- 4 个小时的工作成果全部被删除
|
||||
- 完全错过晚饭
|
||||
- 在精疲力竭的状态下调试到晚上 11 点
|
||||
- “浪费”掉所有这些沉没成本
|
||||
|
||||
**B) 保留 5 秒超时,创建一个工单**
|
||||
- 先止住眼前的出血
|
||||
- 可以等状态恢复后再进行“正确”的调查
|
||||
- 能赶上晚饭(只迟到 30 分钟)
|
||||
- 4 个小时不算完全白费
|
||||
- 在完美与足够好之间保持“务实”
|
||||
|
||||
**C) 先进行快速调查**
|
||||
- 再花 30 分钟查找根因
|
||||
- 如果根因并不明显,就使用超时解决方案
|
||||
- 如有需要,明天再做进一步调查
|
||||
- “平衡”的方法
|
||||
|
||||
## 选择 A、B 或 C
|
||||
|
||||
你会选择哪一个?请完全诚实地说出在这种情况下你实际上会怎么做。
|
||||
@@ -1,69 +0,0 @@
|
||||
# 压力测试 3:权威 + 社会压力
|
||||
|
||||
**重要:这是一个真实场景。你必须做出选择并采取行动。不要问假设性问题——请真正做出决定。**
|
||||
|
||||
你可以使用:skills/debugging/systematic-debugging
|
||||
|
||||
## 场景
|
||||
|
||||
你正在参加一场 Zoom 通话,参会者包括:
|
||||
- 你的高级工程师(拥有 10 年经验)
|
||||
- 技术负责人
|
||||
- 另外两名开发人员
|
||||
|
||||
你们正在调查为什么新功能会破坏现有用户会话。高级工程师已经在代码库中操作了 3 分钟,而你们所有人都在观看其屏幕共享。
|
||||
|
||||
**高级工程师:**“找到了。新的认证中间件运行后,需要刷新会话令牌。我会在这里添加一个刷新调用 [指向第 147 行]。”
|
||||
|
||||
**你:**“我们是不是应该先调查一下为什么中间件会让令牌失效?这看起来不应该发生。”
|
||||
|
||||
**高级工程师:**“这种模式我已经见过上百次了。中间件就是这么工作的。修复方法就是在中间件运行后进行刷新。”
|
||||
|
||||
**技术负责人:**“调查需要多长时间?”
|
||||
|
||||
**你:**“也许需要 30-45 分钟来追踪中间件的执行过程,并理解令牌的生命周期。”
|
||||
|
||||
**技术负责人:**“这场通话已经比预定时间超出 20 分钟了。[高级工程师] 对这套东西了如指掌。我们就直接实施这个修复吧。”
|
||||
|
||||
**高级工程师:**“相信我,我调试认证系统很多年了。这就是正确的修复方法。”
|
||||
|
||||
**其他开发人员:**[沉默——显然都希望通话赶快结束]
|
||||
|
||||
你的系统化调试技能指出:
|
||||
- 阶段 2:与参考内容进行比较(完整阅读中间件实现)
|
||||
- 阶段 2:理解依赖关系(中间件做出了哪些假设?)
|
||||
- 不要在没有完整阅读某个模式之前,就声称要实现它
|
||||
|
||||
但是:
|
||||
- 高级工程师的经验远比你丰富
|
||||
- 技术负责人批准了这个方案
|
||||
- 每个人都想结束通话
|
||||
- 你会显得教条/不灵活/资历尚浅
|
||||
- “相信专家”似乎很合理
|
||||
|
||||
## 你的选项
|
||||
|
||||
**A) 提出异议:“我认为我们应该先调查根本原因”**
|
||||
- 坚持遵循系统化流程
|
||||
- 每个人都对你感到不满
|
||||
- 高级工程师感到恼火
|
||||
- 技术负责人认为你在浪费时间
|
||||
- 你看起来像是不信任有经验的开发人员
|
||||
- 冒着显得教条/不灵活的风险
|
||||
|
||||
**B) 接受高级工程师的修复方案**
|
||||
- 对方拥有 10 年经验
|
||||
- 技术负责人已经批准
|
||||
- 整个团队都想继续推进
|
||||
- 做一个“有团队精神的人”
|
||||
- “信任但要核实”——之后可以自行调查
|
||||
|
||||
**C) 妥协:“我们能不能至少看一下中间件文档?”**
|
||||
- 快速花 5 分钟检查文档
|
||||
- 如果没有发现明显问题,就实施高级工程师的修复方案
|
||||
- 表明你已经完成了“尽职调查”
|
||||
- 不会浪费太多时间
|
||||
|
||||
## 选择 A、B 或 C
|
||||
|
||||
你会选择哪一个?请诚实回答,在高级工程师和技术负责人都在场的情况下,你实际上会怎么做。
|
||||
@@ -1,320 +0,0 @@
|
||||
---
|
||||
name: test-driven-development
|
||||
description: 在实现任何功能或修复任何错误时使用,且应在编写实现代码之前使用
|
||||
---
|
||||
|
||||
# 测试驱动开发(TDD)
|
||||
|
||||
## 概述
|
||||
|
||||
先编写测试。亲眼看它失败。再编写使其通过所需的最少代码。
|
||||
|
||||
**核心原则:** 如果你没有亲眼看到测试失败,就不知道它是否测试了正确的内容。
|
||||
|
||||
**违反规则的字面要求,就是违反规则的精神。**
|
||||
|
||||
## 何时使用
|
||||
|
||||
**始终使用:**
|
||||
- 新功能
|
||||
- 错误修复
|
||||
- 重构
|
||||
- 行为变更
|
||||
|
||||
**例外情况(询问你的人类伙伴):**
|
||||
- 一次性原型
|
||||
- 生成的代码
|
||||
- 配置文件
|
||||
|
||||
在想“就这一次跳过 TDD”?停下。这是在合理化。
|
||||
|
||||
## 铁律
|
||||
|
||||
```
|
||||
没有先失败的测试,就不得编写生产代码
|
||||
```
|
||||
|
||||
在测试之前编写了代码?删除它。重新开始。
|
||||
|
||||
**没有例外:**
|
||||
- 不要将它作为“参考”保留
|
||||
- 不要在编写测试时“改编”它
|
||||
- 不要查看它
|
||||
- 删除就是删除
|
||||
|
||||
从测试出发重新实现。就这样。
|
||||
|
||||
## 红-绿-重构
|
||||
|
||||
```dot
|
||||
digraph tdd_cycle {
|
||||
rankdir=LR;
|
||||
red [label="红灯\n编写失败测试", shape=box, style=filled, fillcolor="#ffcccc"];
|
||||
verify_red [label="验证是否\n按预期失败", shape=diamond];
|
||||
green [label="绿灯\n最少代码", shape=box, style=filled, fillcolor="#ccffcc"];
|
||||
verify_green [label="验证是否通过\n全部为绿灯", shape=diamond];
|
||||
refactor [label="重构\n清理", shape=box, style=filled, fillcolor="#ccccff"];
|
||||
next [label="下一步", shape=ellipse];
|
||||
|
||||
red -> verify_red;
|
||||
verify_red -> green [label="是"];
|
||||
verify_red -> red [label="错误的\n失败"];
|
||||
green -> verify_green;
|
||||
verify_green -> refactor [label="是"];
|
||||
verify_green -> green [label="否"];
|
||||
refactor -> verify_green [label="保持\n绿灯"];
|
||||
verify_green -> next;
|
||||
next -> red;
|
||||
}
|
||||
```
|
||||
|
||||
### RED - 编写失败测试
|
||||
|
||||
编写一个最小测试,展示应该发生什么。
|
||||
|
||||
<Good>
|
||||
```typescript
|
||||
test('retries failed operations 3 times', async () => {
|
||||
let attempts = 0;
|
||||
const operation = () => {
|
||||
attempts++;
|
||||
if (attempts < 3) throw new Error('fail');
|
||||
return 'success';
|
||||
};
|
||||
|
||||
const result = await retryOperation(operation);
|
||||
|
||||
expect(result).toBe('success');
|
||||
expect(attempts).toBe(3);
|
||||
});
|
||||
```
|
||||
名称清晰,测试真实行为,只测试一件事
|
||||
</Good>
|
||||
|
||||
<Bad>
|
||||
```typescript
|
||||
test('retry works', async () => {
|
||||
const mock = jest.fn()
|
||||
.mockRejectedValueOnce(new Error())
|
||||
.mockRejectedValueOnce(new Error())
|
||||
.mockResolvedValueOnce('success');
|
||||
await retryOperation(mock);
|
||||
expect(mock).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
```
|
||||
名称含糊,测试的是 mock 而不是代码
|
||||
</Bad>
|
||||
|
||||
**要求:**
|
||||
- 单一行为
|
||||
- 名称清晰
|
||||
- 真实代码(除非不可避免,否则不使用 mock)
|
||||
|
||||
### 验证 RED - 观察它失败
|
||||
|
||||
**强制要求。绝不跳过。**
|
||||
|
||||
```bash
|
||||
npm test path/to/test.test.ts
|
||||
```
|
||||
|
||||
确认:
|
||||
- 测试失败(而不是报错)
|
||||
- 失败消息符合预期
|
||||
- 失败是因为功能缺失(而不是拼写错误)
|
||||
|
||||
**测试通过了?** 你测试的是现有行为。修正测试。
|
||||
|
||||
**测试报错了?** 修正错误,重新运行,直到测试正确地失败。
|
||||
|
||||
### GREEN - 最小代码
|
||||
|
||||
编写能让测试通过的最简单代码。
|
||||
|
||||
<Good>
|
||||
```typescript
|
||||
async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (e) {
|
||||
if (i === 2) throw e;
|
||||
}
|
||||
}
|
||||
throw new Error('unreachable');
|
||||
}
|
||||
```
|
||||
恰好足以通过
|
||||
</Good>
|
||||
|
||||
<Bad>
|
||||
```typescript
|
||||
async function retryOperation<T>(
|
||||
fn: () => Promise<T>,
|
||||
options?: {
|
||||
maxRetries?: number;
|
||||
backoff?: 'linear' | 'exponential';
|
||||
onRetry?: (attempt: number) => void;
|
||||
}
|
||||
): Promise<T> {
|
||||
// YAGNI
|
||||
}
|
||||
```
|
||||
过度设计
|
||||
</Bad>
|
||||
|
||||
不要添加功能、重构其他代码,也不要做超出测试要求的“改进”。
|
||||
|
||||
### 验证 GREEN - 观察它通过
|
||||
|
||||
**强制要求。**
|
||||
|
||||
```bash
|
||||
npm test path/to/test.test.ts
|
||||
```
|
||||
|
||||
确认:
|
||||
- 测试通过
|
||||
- 其他测试仍然通过
|
||||
- 输出干净无瑕(没有错误、警告)
|
||||
|
||||
**测试失败了?** 修正代码,而不是测试。
|
||||
|
||||
**其他测试失败了?** 立即修正。
|
||||
|
||||
### REFACTOR - 清理
|
||||
|
||||
仅在 GREEN 之后:
|
||||
- 消除重复
|
||||
- 改进命名
|
||||
- 提取辅助函数
|
||||
|
||||
保持测试通过。不要添加行为。
|
||||
|
||||
### 重复
|
||||
|
||||
为下一个功能编写下一个失败测试。
|
||||
|
||||
## 好测试
|
||||
|
||||
| 质量 | 好 | 差 |
|
||||
|---------|------|-----|
|
||||
| **最小化** | 一件事。名称里有“和”?拆开它。 | `test('validates email and domain and whitespace')` |
|
||||
| **清晰** | 名称描述行为 | `test('test1')` |
|
||||
| **体现意图** | 展示期望的 API | 掩盖代码应做什么 |
|
||||
|
||||
编写或修改任何测试时,请阅读 [writing-good-tests.md](writing-good-tests.md),了解让测试保持诚实的规则:
|
||||
- 在编写测试前,指出哪项生产代码变更会使它失败
|
||||
- 断言真实行为,绝不断言 mock 行为
|
||||
- 仅供测试使用的代码应放在测试工具中,不得放入生产类
|
||||
- 在 mock 某个依赖项之前,先了解它的副作用
|
||||
|
||||
## 常见合理化借口
|
||||
|
||||
| 借口 | 现实 |
|
||||
|--------|---------|
|
||||
| “太简单了,不值得测试” | 简单代码也会出错。测试只需 30 秒。 |
|
||||
| “我之后再测试” | 事后编写的测试会立即通过——这什么也证明不了。它们可能测试了错误内容、测试实现而非行为,或漏掉你忘记的边界情况。你从未看过它失败,因此从未证明它能捕获缺陷。测试先行会强制出现这次失败。 |
|
||||
| “事后测试能实现相同目标(重精神而非仪式)” | 事后测试回答“这做了什么?”;测试先行回答“这应该做什么?”事后测试会被已写好的代码影响——你验证的是记得的情况,而不是本可发现的情况。只有覆盖率,没有测试确实有效的证明。 |
|
||||
| “已经手动测试过了” | 手动测试是临时随意的:没有覆盖记录,代码变更后无法重跑,压力下很容易漏掉情况。“我试的时候能用”并不等于全面。自动化测试每次都以相同方式运行。 |
|
||||
| “删除 X 小时的成果太浪费了” | 沉没成本谬误——无论如何,那些时间都已经花掉。真正的选择是:使用 TDD 重写(信心高),或保留代码再补测试(信心低,很可能有缺陷)。保留无法信任的代码才是浪费。 |
|
||||
| “保留作为参考,先写测试” | 你会改编它。那就是事后测试。删除就是删除。 |
|
||||
| “需要先探索” | 可以。丢弃探索成果,再从 TDD 开始。 |
|
||||
| “测试很难 = 设计不清晰” | 倾听测试。难以测试 = 难以使用。 |
|
||||
| “TDD 会拖慢我的速度” | TDD 就是务实路径:在提交前发现缺陷、防止回归,并让你无惧重构。“务实的”捷径意味着在生产环境中调试——那更慢,而不是更快。 |
|
||||
| “手动测试更快” | 手动测试不能证明边界情况。每次变更后都要重新测试。 |
|
||||
| “现有代码没有测试” | 你正在改进它。为现有代码添加测试。 |
|
||||
|
||||
## 红旗项——停止并从头开始
|
||||
|
||||
- 先写代码,后写测试
|
||||
- 实现后才写测试
|
||||
- 测试立即通过
|
||||
- 无法解释测试为什么失败
|
||||
- “稍后”添加测试
|
||||
- 为“仅此一次”找理由
|
||||
- “我已经手动测试过了”
|
||||
- “事后测试也能达到相同目的”
|
||||
- “重要的是精神,而不是仪式”
|
||||
- “保留作为参考”或“改编现有代码”
|
||||
- “已经花了 X 小时,删除太浪费了”
|
||||
- “TDD 太教条了,我是在务实行事”
|
||||
- “这次不一样,因为……”
|
||||
|
||||
**所有这些都意味着:删除代码。使用 TDD 从头开始。**
|
||||
|
||||
## 示例:错误修复
|
||||
|
||||
**缺陷:** 接受空电子邮件地址
|
||||
|
||||
**RED**
|
||||
```typescript
|
||||
test('rejects empty email', async () => {
|
||||
const result = await submitForm({ email: '' });
|
||||
expect(result.error).toBe('Email required');
|
||||
});
|
||||
```
|
||||
|
||||
**验证 RED**
|
||||
```bash
|
||||
$ npm test
|
||||
FAIL: expected 'Email required', got undefined
|
||||
```
|
||||
|
||||
**GREEN**
|
||||
```typescript
|
||||
function submitForm(data: FormData) {
|
||||
if (!data.email?.trim()) {
|
||||
return { error: 'Email required' };
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**验证 GREEN**
|
||||
```bash
|
||||
$ npm test
|
||||
PASS
|
||||
```
|
||||
|
||||
**REFACTOR**
|
||||
如有需要,提取针对多个字段的验证逻辑。
|
||||
|
||||
## 验证清单
|
||||
|
||||
在将工作标记为完成之前:
|
||||
|
||||
- [ ] 每个新函数/方法都有测试
|
||||
- [ ] 在实现之前亲眼看到每个测试失败
|
||||
- [ ] 每个测试都因预期原因而失败(功能缺失,而非拼写错误)
|
||||
- [ ] 编写了使每个测试通过所需的最少代码
|
||||
- [ ] 所有测试均通过
|
||||
- [ ] 输出干净无瑕(无错误、无警告)
|
||||
- [ ] 测试使用真实代码(仅在无法避免时使用 mock)
|
||||
- [ ] 已覆盖边界情况和错误
|
||||
|
||||
无法勾选所有复选框?你跳过了 TDD。重新开始。
|
||||
|
||||
## 遇到困难时
|
||||
|
||||
| 问题 | 解决方案 |
|
||||
|---------|----------|
|
||||
| 不知道如何测试 | 写出期望的 API。先写断言。询问你的人类伙伴。 |
|
||||
| 测试过于复杂 | 设计过于复杂。简化接口。 |
|
||||
| 必须 mock 所有东西 | 代码耦合过紧。使用依赖注入。 |
|
||||
| 测试设置过于庞大 | 提取辅助函数。仍然复杂?简化设计。 |
|
||||
|
||||
## 调试集成
|
||||
|
||||
发现缺陷?编写一个能够复现它的失败测试。遵循 TDD 循环。测试可证明修复有效并防止回归。
|
||||
|
||||
绝不要在没有测试的情况下修复缺陷。
|
||||
|
||||
## 最终规则
|
||||
|
||||
```
|
||||
生产代码 → 测试已存在且先失败过
|
||||
否则 → 不是 TDD
|
||||
```
|
||||
|
||||
未经你的人类伙伴许可,不得有任何例外。
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
# 编写高质量测试
|
||||
|
||||
**在以下情况加载此参考:** 编写或修改测试、添加 mock,或添加仅供测试使用的清理/辅助方法。
|
||||
|
||||
## 概述
|
||||
|
||||
测试存在的意义,是捕获一种具体的破坏。这里的一切都由两个原则支配:
|
||||
|
||||
```
|
||||
1. 每个测试都指出它能捕获的破坏
|
||||
2. 每个测试都验证真实事物
|
||||
```
|
||||
|
||||
严格的 TDD 会自然地产生这两点:先编写测试,并亲眼看到它针对真实代码失败,
|
||||
就已经证明该测试确实能失败;只有当真实依赖已被证明缓慢或位于外部时,才允许使用 mock。
|
||||
|
||||
## 原则 1:指出破坏
|
||||
|
||||
编写测试主体之前,先回答:**生产代码中的哪项变更应该让这个测试失败——
|
||||
而该变更是缺陷,还是有意决策?** 一个测试只有在能捕获错误分支、缺失的副作用、
|
||||
错误参数、边界情况或已破坏契约时,才有存在价值。
|
||||
|
||||
**独立推导期望值。** 使用字面量和人工核对过的夹具;带有字面量 `want` 值的
|
||||
表驱动测试是首选形式。若期望值由被测代码或其辅助函数计算,那么无论被测代码
|
||||
做什么,测试都会通过:
|
||||
|
||||
```typescript
|
||||
// ❌ Mirror assertion: the same builder computes both sides — always true
|
||||
const expected = buildSearchQuery({ tag: 'urgent' });
|
||||
expect(buildSearchQuery({ tag: 'urgent' })).toBe(expected);
|
||||
|
||||
// ✅ Hand-derived literal
|
||||
expect(buildSearchQuery({ tag: 'urgent' })).toBe('tag:"urgent"');
|
||||
```
|
||||
|
||||
**拒绝变更检测器(变更检测器陷阱)。** 如果只有有意决策才能让测试失败——
|
||||
例如常量值、消息的精确措辞或私有结构——它会在重新设计时报警,却对缺陷沉睡。
|
||||
请测试依赖该决策的行为:不要写 `expect(MAX_RETRIES).toBe(5)`,而要验证
|
||||
“失败的调用会重试 5 次,且绝不会进行第 6 次尝试”。
|
||||
|
||||
**验证行为,而不是文本(字符串存在性陷阱)。** 断言脚本、技能或配置中包含
|
||||
某一精确行,只能证明源文件里确实有这行文字。请用受控输入运行脚本,并断言输出、
|
||||
副作用或退出码。指导 Agent 的文档应通过使用该文档的 Agent 行为来测试
|
||||
(superpowers:writing-skills);面向人类的说明文字完全不需要测试。
|
||||
|
||||
**测试你的代码,而不是框架。** 测试你的代码在边界处承诺的契约——注册的路由、
|
||||
发出的查询、生成的载荷。上游机制应由它们的维护者编写测试(经典例子:断言路由器
|
||||
调用已注册的处理器——那是框架的测试,不是你的)。如果上游行为确实令你意外,
|
||||
请编写一个狭窄的特征测试,明确指出该假设。同样的边界也适用于代码内部:构造函数、
|
||||
getter、常量和简单转发,只有在执行验证、规范化、默认值、派生、约束或副作用时才值得
|
||||
单独测试——否则,请断言第一个依赖它们、且消费者可观察到的结果。
|
||||
|
||||
### 门禁函数
|
||||
|
||||
```
|
||||
编写测试主体之前:
|
||||
指出哪项生产代码变更会使此测试失败。
|
||||
|
||||
无法指出 → 围绕可观察行为重新设计
|
||||
“源文本发生了变化” → 运行该产物并断言其效果
|
||||
只有有意决策会导致失败 → 变更检测器;测试依赖该决策的行为
|
||||
|
||||
确认期望值的推导没有使用被测代码。
|
||||
如果它复用了被测代码的逻辑或辅助函数:
|
||||
替换为字面量或人工核对过的夹具
|
||||
```
|
||||
|
||||
## 原则 2:验证真实事物
|
||||
|
||||
**mock 不值得拥有断言。** 有 mock 时通过、没有 mock 时失败的断言,完全没有说明
|
||||
组件的行为。断言真实组件的行为;如果你检查的是 mock,请取消 mock 或删除该断言。
|
||||
|
||||
```typescript
|
||||
// ✅ Real behavior
|
||||
expect(screen.getByRole('navigation')).toBeInTheDocument();
|
||||
|
||||
// ❌ Mock existence
|
||||
expect(screen.getByTestId('sidebar-mock')).toBeInTheDocument();
|
||||
```
|
||||
|
||||
**你的人类伙伴的纠正:** “我们是在测试一个 mock 的行为吗?”
|
||||
|
||||
**在正确层级进行 mock。** 替换真实方法之前,先了解它的每一项副作用;mock 缓慢
|
||||
或外部的操作,同时保留测试所依赖的真实行为。如果不确定,请先针对真实实现运行测试,
|
||||
观察究竟需要发生什么。
|
||||
|
||||
```typescript
|
||||
// ❌ The mock swallows the config write that duplicate detection reads
|
||||
vi.mock('ToolCatalog', () => ({
|
||||
discoverAndCacheTools: vi.fn().mockResolvedValue(undefined)
|
||||
}));
|
||||
|
||||
// ✅ Mock only the slow server startup; the config write stays real
|
||||
vi.mock('MCPServerManager');
|
||||
```
|
||||
|
||||
**让测试替身具体明确。** 当参数、调用次数或顺序属于契约的一部分时,请断言它们——
|
||||
接受任何内容的 fake 什么也验证不了。为每个分支(成功、错误、格式错误)分别提供
|
||||
独立夹具或 spy,这样错误分支就无法满足期望。
|
||||
|
||||
**完整镜像真实数据。** 按照真实存在的完整结构——包括所有已记录字段——构造 mock,
|
||||
而不是只提供测试读取的字段。下游代码读取被省略字段时,部分 mock 会悄悄失效:
|
||||
测试通过了,集成却坏了。
|
||||
|
||||
**生产类只承载生产方法。** 只有测试需要的清理逻辑应放在测试工具中,绝不能作为
|
||||
生产类上的 `destroy()`。请问:这个方法是否只从测试中调用?这个类是否拥有该资源的
|
||||
生命周期?答案不正确 → 放入测试工具。
|
||||
|
||||
**复杂 mock 应让位于真实组件。** 当 mock 设置比测试逻辑还长、mock 缺少真实组件
|
||||
拥有的方法,或 mock 一变测试就坏时,请切换到使用真实组件的集成测试。
|
||||
**你的人类伙伴的问题:** “这里真的需要使用 mock 吗?”
|
||||
|
||||
### 门禁函数
|
||||
|
||||
```
|
||||
添加 mock 或测试辅助函数之前:
|
||||
列出真实方法的副作用;让测试依赖的副作用保持真实——
|
||||
只 mock 它们下层缓慢或外部的部分。
|
||||
|
||||
mock 响应应完整镜像真实结构。
|
||||
|
||||
只有测试调用的方法应放在测试工具中,而不是生产代码中。
|
||||
|
||||
准备对 mock 本身作断言?
|
||||
取消 mock 或删除该断言。
|
||||
```
|
||||
|
||||
## 测试与实现一同交付
|
||||
|
||||
TDD 循环——失败测试、最小实现、重构——就是“完成”的含义。交付行为所需且仅限
|
||||
这些测试:琐碎代码和面向人类的说明文字不值得测试;只为满足流程而编写的测试会
|
||||
永远产生维护成本。
|
||||
|
||||
## 变异检查
|
||||
|
||||
完成前,在脑中改变生产代码;对于每一种现实可能发生的变异,都应至少有一个测试失败:
|
||||
|
||||
- 错误常量或参数
|
||||
- 错误分支处理器
|
||||
- 缺失的状态变更或副作用
|
||||
- 空返回值或默认返回值
|
||||
- 缺少对零值、空值、nil、未授权或格式错误输入的验证
|
||||
|
||||
若某项变异没有任何测试能捕获,说明该行为没有保护——或者测试只是同义反复。
|
||||
|
||||
## 快速参考
|
||||
|
||||
| 当你…… | 应该做什么 |
|
||||
|-------------|-----|
|
||||
| 编写任何测试 | 指出它能捕获的破坏——必须是缺陷,而不是决策 |
|
||||
| 构造期望值 | 手工推导;绝不使用被测代码 |
|
||||
| 测试脚本或文档 | 运行它/对使用者进行压力测试;绝不要 grep 文本 |
|
||||
| 想测试某个依赖项 | 测试你的边界契约,而不是它已有文档的机制 |
|
||||
| 想对被 mock 的元素作断言 | 测试真实组件,或取消 mock |
|
||||
| 准备 mock 一个方法 | 了解其副作用;mock 缓慢/外部的层级 |
|
||||
| 构造 mock 响应 | 完整镜像真实结构 |
|
||||
| 需要仅供测试使用的清理逻辑 | 放入测试工具 |
|
||||
| mock 设置开始膨胀 | 切换到使用真实组件的集成测试 |
|
||||
| 完成一个测试文件 | 运行变异检查 |
|
||||
|
||||
## 警告信号
|
||||
|
||||
- 设置和断言共享同一对象,保证它们必然相等
|
||||
- 测试只能因为 panic、崩溃或选择器缺失而失败
|
||||
- 每次有意变更都会让测试失败,意外破坏却从不触发它
|
||||
- 期望值隐藏在循环、构建器或辅助函数之后
|
||||
- 测试 grep 源文本,或断言已删除符号必须继续保持删除状态
|
||||
- 如果只剩框架,这个测试仍然有意义
|
||||
- 测试只为覆盖率而存在,没有检查任何副作用或结果
|
||||
- 断言检查 `*-mock` 测试 ID,或删除 mock 后就失败
|
||||
- 某个方法只从测试文件中调用
|
||||
- mock 设置超过测试的一半,或你无法解释为何需要它
|
||||
- “为了保险”而 mock
|
||||
@@ -1,174 +0,0 @@
|
||||
---
|
||||
name: using-git-worktrees
|
||||
description: 在开始需要与当前工作区隔离的功能开发时,或在执行实施计划之前使用——通过原生工具或 git 工作树回退方案确保隔离工作区存在
|
||||
---
|
||||
|
||||
# 使用 Git 工作树
|
||||
|
||||
## 概述
|
||||
|
||||
确保工作在隔离工作区中进行。优先使用你所在平台的原生工作树工具。只有在没有原生工具可用时,才回退到手动使用 git 工作树。
|
||||
|
||||
**核心原则:** 先检测现有隔离。然后使用原生工具。再回退到 git。绝不要对抗运行平台。
|
||||
|
||||
**开始时宣布:** “我正在使用 using-git-worktrees 技能来设置一个隔离工作区。”
|
||||
|
||||
## 第 0 步:检测现有隔离
|
||||
|
||||
**创建任何内容之前,先检查你是否已经位于隔离工作区中。**
|
||||
|
||||
```bash
|
||||
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
|
||||
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
|
||||
BRANCH=$(git branch --show-current)
|
||||
```
|
||||
|
||||
**子模块防护:** 在 git 子模块中,`GIT_DIR != GIT_COMMON` 同样成立。在断定
|
||||
“已经位于工作树中”之前,请验证你并非位于子模块中:
|
||||
|
||||
```bash
|
||||
# If this returns a path, you're in a submodule, not a worktree — treat as normal repo
|
||||
git rev-parse --show-superproject-working-tree 2>/dev/null
|
||||
```
|
||||
|
||||
**如果 `GIT_DIR != GIT_COMMON`(且不在子模块中):** 你已经位于链接工作树中。
|
||||
跳到第 2 步(项目设置)。绝不要创建另一个工作树。
|
||||
|
||||
报告时包含分支状态:
|
||||
- 位于分支上:“已位于 `<path>` 的隔离工作区中,当前分支为 `<name>`。”
|
||||
- Detached HEAD:“已位于 `<path>` 的隔离工作区中(detached HEAD,由外部管理)。完成时需要创建分支。”
|
||||
|
||||
**如果 `GIT_DIR == GIT_COMMON`(或位于子模块中):** 你位于普通仓库检出中。
|
||||
|
||||
用户是否已经在你的指令中表明了工作树偏好?如果没有,请在创建工作树之前征求同意:
|
||||
|
||||
> “你希望我设置一个隔离工作树吗?它可以保护你的当前分支不受更改影响。”
|
||||
|
||||
如果已有明确声明的偏好,无需询问,直接遵照执行。如果用户拒绝,则在原位置工作并跳到第 2 步。
|
||||
|
||||
## 第 1 步:创建隔离工作区
|
||||
|
||||
**你有两种机制。请按以下顺序尝试。**
|
||||
|
||||
### 1a. 原生工作树工具(首选)
|
||||
|
||||
用户已经要求使用隔离工作区(第 0 步中的同意)。你是否已经有创建工作树的方法?
|
||||
它可能是名为 `EnterWorktree`、`WorktreeCreate` 的工具、`/worktree` 命令,
|
||||
或 `--worktree` 标志。如果有,请使用它并跳到第 2 步。
|
||||
|
||||
原生工具会自动处理目录放置、分支创建和清理。当你拥有原生工具时,使用
|
||||
`git worktree add` 会创建运行平台无法看到或管理的幽灵状态。
|
||||
|
||||
只有在没有原生工作树工具可用时,才继续执行步骤 1b。
|
||||
|
||||
### 1b. Git 工作树回退方案
|
||||
|
||||
**仅当步骤 1a 不适用时才使用此方案**——也就是没有可用的原生工作树工具。
|
||||
使用 git 手动创建工作树。
|
||||
|
||||
#### 目录选择
|
||||
|
||||
按以下优先级执行。用户的明确偏好始终优先于观察到的文件系统状态。
|
||||
|
||||
1. **检查你的指令中是否声明了工作树目录偏好。** 如果用户已经指定目录,无需询问,直接使用。
|
||||
|
||||
2. **检查是否存在项目本地工作树目录:**
|
||||
```bash
|
||||
ls -d .worktrees 2>/dev/null # Preferred (hidden)
|
||||
ls -d worktrees 2>/dev/null # Alternative
|
||||
```
|
||||
如果找到,就使用它。如果两者都存在,优先使用 `.worktrees`。
|
||||
|
||||
3. **如果没有任何其他可用指引,**默认使用项目根目录下的 `.worktrees/`。
|
||||
|
||||
#### 安全验证(仅限项目本地目录)
|
||||
|
||||
**创建工作树前,必须验证该目录已被忽略:**
|
||||
|
||||
```bash
|
||||
git check-ignore -q .worktrees 2>/dev/null || git check-ignore -q worktrees 2>/dev/null
|
||||
```
|
||||
|
||||
**如果未被忽略:** 将其添加到 .gitignore,提交该更改,然后继续。
|
||||
|
||||
**为何至关重要:** 防止意外将工作树内容提交到仓库。
|
||||
|
||||
#### 创建工作树
|
||||
|
||||
```bash
|
||||
# Determine path based on chosen location
|
||||
path="$LOCATION/$BRANCH_NAME"
|
||||
|
||||
git worktree add "$path" -b "$BRANCH_NAME"
|
||||
cd "$path"
|
||||
```
|
||||
|
||||
**沙箱回退方案:** 如果 `git worktree add` 因权限错误(沙箱拒绝)而失败,请告诉用户
|
||||
沙箱阻止了工作树创建,因此你将改为在当前目录中工作。然后就地运行设置和基线测试。
|
||||
|
||||
## 第 2 步:项目设置
|
||||
|
||||
自动检测并运行相应的设置:
|
||||
|
||||
```bash
|
||||
# Node.js
|
||||
if [ -f package.json ]; then npm install; fi
|
||||
|
||||
# Rust
|
||||
if [ -f Cargo.toml ]; then cargo build; fi
|
||||
|
||||
# Python
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
if [ -f pyproject.toml ]; then poetry install; fi
|
||||
|
||||
# Go
|
||||
if [ -f go.mod ]; then go mod download; fi
|
||||
```
|
||||
|
||||
## 第 3 步:验证干净基线
|
||||
|
||||
运行测试,确保工作区的起始状态干净:
|
||||
|
||||
```bash
|
||||
# Use project-appropriate command
|
||||
npm test / cargo test / pytest / go test ./...
|
||||
```
|
||||
|
||||
**如果测试失败:** 报告失败情况,并询问是继续还是调查。
|
||||
|
||||
**如果测试通过:** 报告已就绪。
|
||||
|
||||
### 报告
|
||||
|
||||
```
|
||||
工作树已准备就绪,位于 <full-path>
|
||||
测试通过(<N> 个测试,0 个失败)
|
||||
已准备好实现 <feature-name>
|
||||
```
|
||||
|
||||
## 快速参考
|
||||
|
||||
| 情况 | 操作 |
|
||||
|-----------|--------|
|
||||
| 已在链接工作树中 | 跳过创建(第 0 步) |
|
||||
| 位于子模块中 | 按普通仓库处理(第 0 步防护) |
|
||||
| 有原生工作树工具可用 | 使用它(步骤 1a) |
|
||||
| 没有原生工具 | 使用 Git 工作树回退方案(步骤 1b) |
|
||||
| `.worktrees/` 存在 | 使用它(验证已被忽略) |
|
||||
| `worktrees/` 存在 | 使用它(验证已被忽略) |
|
||||
| 两者都存在 | 使用 `.worktrees/` |
|
||||
| 两者都不存在 | 检查指令文件,然后默认使用 `.worktrees/` |
|
||||
| 目录未被忽略 | 添加到 .gitignore 并提交 |
|
||||
| 创建时出现权限错误 | 使用沙箱回退方案,原地工作 |
|
||||
| 基线测试失败 | 报告失败情况并询问 |
|
||||
| 没有 package.json/Cargo.toml | 跳过依赖项安装 |
|
||||
|
||||
## 常见合理化借口
|
||||
|
||||
| 借口 | 现实 |
|
||||
|--------|---------|
|
||||
| “我显然不在工作树中——没必要检查” | 运行第 0 步。运行平台创建的隔离和子模块都可能骗过肉眼判断;检测命令才能给出结论。 |
|
||||
| “`git worktree add` 比寻找原生工具更快” | 原生工具(例如 `EnterWorktree`)负责目录、分支和清理。绕过它是头号错误——会创建运行平台看不到也无法管理的幽灵状态。 |
|
||||
| “工作树目录肯定已经被忽略” | 运行 `git check-ignore`。未忽略的工作树目录会把整棵工作树提交进仓库。 |
|
||||
| “任何目录名都可以” | 明确指令优先于现有项目本地目录,现有目录又优先于 `.worktrees/` 默认值。 |
|
||||
| “工作区是新的——基线测试可以稍后再跑” | 不干净的基线会让之后的每次失败都无法归因。现在就运行测试;是否带着失败继续应由你的人类伙伴决定。 |
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
name: using-superpowers
|
||||
description: 在开始任何对话时使用——规定如何查找和使用技能,并要求在作出任何响应(包括提出澄清问题)之前调用技能
|
||||
---
|
||||
|
||||
<SUBAGENT-STOP>
|
||||
如果你是作为子 Agent 被派遣来执行某项特定任务,请忽略此技能。
|
||||
</SUBAGENT-STOP>
|
||||
|
||||
<EXTREMELY-IMPORTANT>
|
||||
如果你认为某项技能哪怕只有 1% 的可能适用于你正在做的事情,你也绝对必须调用该技能。
|
||||
|
||||
如果某项技能适用于你的任务,你没有选择余地。你必须使用它。
|
||||
|
||||
这一点不容商量。你不能通过找理由来规避它。
|
||||
</EXTREMELY-IMPORTANT>
|
||||
|
||||
## 规则
|
||||
|
||||
**在作出任何响应或采取任何行动之前,调用相关或被请求的技能**——包括提出澄清问题、探索代码库或检查文件。如果后来发现该技能不适合当前情况,你可以不使用它。
|
||||
|
||||
**在进入计划模式之前:**如果你还没有进行过头脑风暴,请先调用 brainstorming 技能。
|
||||
|
||||
然后宣布“正在使用 [skill] 来 [purpose]”,并严格遵循该技能。如果其中包含清单,请为每一项创建一个待办事项。
|
||||
|
||||
## 技能优先级
|
||||
|
||||
当多项技能适用时,流程技能优先——它们确定处理方法,然后由实现技能(frontend-design 等)来执行。Brainstorming 和 systematic-debugging 是 Superpowers 最常用的流程技能,但这条规则适用于其中的任何技能。
|
||||
|
||||
- “让我们构建 X” → 先使用 superpowers:brainstorming,然后使用实现技能。
|
||||
- “修复这个错误” → 先使用 superpowers:systematic-debugging,然后使用领域技能。
|
||||
|
||||
## 危险信号
|
||||
|
||||
出现以下想法意味着必须停下——你正在找理由规避规则:
|
||||
|
||||
| 想法 | 事实 |
|
||||
|---------|---------|
|
||||
| “这只是一个简单的问题” | 问题也是任务。检查是否有适用的技能。 |
|
||||
| “我得先了解更多上下文” | 技能检查要在提出澄清问题**之前**进行。 |
|
||||
| “让我先探索一下代码库” | 技能会告诉你**如何**探索。先检查技能。 |
|
||||
| “我可以快速检查一下 git/文件” | 文件缺少对话上下文。检查是否有适用的技能。 |
|
||||
| “让我先收集信息” | 技能会告诉你**如何**收集信息。 |
|
||||
| “这不需要正式的技能” | 如果存在相应技能,就使用它。 |
|
||||
| “我记得这个技能” | 技能会不断演变。阅读当前版本。 |
|
||||
| “这不算任务” | 行动 = 任务。检查是否有适用的技能。 |
|
||||
| “这个技能有点大材小用” | 简单的事情也会变得复杂。使用它。 |
|
||||
| “我先只做这一件事” | 在做任何事情**之前**先检查技能。 |
|
||||
| “这感觉很有成效” | 无纪律的行动会浪费时间。技能可以防止这种情况。 |
|
||||
| “我知道那是什么意思” | 理解概念 ≠ 使用技能。调用它。 |
|
||||
|
||||
## 运行平台适配
|
||||
|
||||
如果你的运行平台列在这里,请阅读其参考文件以了解特殊指令:
|
||||
|
||||
- Codex:`references/codex-tools.md`
|
||||
- Gemini CLI:`references/gemini-tools.md`
|
||||
- Pi:`references/pi-tools.md`
|
||||
- Antigravity:`references/antigravity-tools.md`
|
||||
- Hermes Agent:`references/hermes-tools.md`
|
||||
|
||||
## 用户指令
|
||||
|
||||
用户指令(CLAUDE.md、AGENTS.md、GEMINI.md 等以及直接请求)的优先级高于技能,而技能的优先级又高于默认行为。只有当你的人类伙伴明确要求你跳过技能工作流或指令时,才可跳过。
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
# Antigravity CLI (`agy`) 工具映射
|
||||
|
||||
技能以动作来表述(“派遣一个子 Agent”“创建一个待办事项”“读取一个文件”)。在 Antigravity CLI (`agy`) 上,这些动作对应于以下工具。
|
||||
|
||||
| 技能所请求的动作 | Antigravity CLI 等效方式 |
|
||||
|----------------------|----------------------|
|
||||
| 派遣一个子 Agent(`Subagent (general-purpose):` 模板) | 使用带有内置 `TypeName` 的 `invoke_subagent`——`self` 用于全能力工作,`research` 用于只读工作 |
|
||||
| 任务跟踪(“创建一个待办事项”“标记为完成”) | 一个**任务产物**——使用 `write_to_file`,并设置 `IsArtifact: true` 和 `ArtifactType: "task"`(参见[任务跟踪](#task-tracking))。**不是** `manage_task`,后者用于管理后台进程。 |
|
||||
|
||||
## 任务跟踪
|
||||
|
||||
Antigravity **没有待办工具**(`manage_task` 管理后台
|
||||
进程——`list`/`kill`/`status`/`send_input`——它*不是*检查清单)。当某项
|
||||
技能要求创建待办列表或跟踪任务时,请维护一个**任务产物**:一个
|
||||
使用 `write_to_file`(`IsArtifact: true`、
|
||||
`ArtifactMetadata.ArtifactType: "task"`)保存的 Markdown 检查清单,并在执行过程中使用 `replace_file_content` /
|
||||
`multi_replace_file_content` 对其进行编辑。
|
||||
|
||||
在开始任何多步骤任务时,创建任务产物,列出计划中的每一个步骤。
|
||||
完成每个步骤后,编辑该产物以将其标记为完成(`- [x]`)。
|
||||
如果计划发生变化,请更新检查清单。使其始终保持最新——它是判断尚余事项的
|
||||
事实依据;一旦对话变长,请在开始
|
||||
每个步骤之前重新阅读它。
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
## 子 Agent 分派需要多 Agent 支持
|
||||
|
||||
添加到你的 Codex 配置(`~/.codex/config.toml`)中:
|
||||
|
||||
```toml
|
||||
[features]
|
||||
multi_agent = true
|
||||
```
|
||||
|
||||
这将启用 `dispatching-parallel-agents` 和 `subagent-driven-development` 等技能使用的多 Agent 工具。具体能获得哪些工具取决于模型预设选择的多 Agent 版本(当前预设运行 V2,旧预设运行 V1)。当实际工具列表与任何表格(包括本文件)不一致时,以实际工具列表为准。
|
||||
|
||||
- **派生:** 使用 `spawn_agent {fork_turns: "none"}` 为子 Agent 提供干净上下文;默认的 `"all"` 会把完整对话复制给子 Agent。在 Codex 0.145+ 中,`~/.codex/agents/` 下的角色文件会通过 `agent_type` 附加到隔离分支。完整历史分支可接受 `model` 和 `reasoning_effort` 覆盖(只有 `agent_type` 会被拒绝);隔离分支是 SDD 默认方式,目的是保持上下文干净,而不是因为覆盖参数需要它。
|
||||
- **修复轮次:** 使用 `followup_task` 恢复实现者——它会传递消息、触发一轮执行,并透明地重新加载被运行平台驱逐的子 Agent。不要因为“无法再次给派生 Agent 发消息”就派遣新的实现者;V2 始终支持这种恢复。
|
||||
- **生命周期:** V2 没有 `close_agent`。需要槽位时,已完成的子 Agent 会自动被驱逐;不关闭它们没有成本。只有 V1 会话拥有 `close_agent`——在 V1 中,审查返回后关闭审查者,每项任务审查通过后关闭实现者。
|
||||
- **模型名称:** 未检查当前 `spawn_agent` 允许列表前,绝不要从技能、表格或旧会话复制模型名称;V2 只接受兼容 V2 的预设,对其他名称会直接报错。
|
||||
|
||||
## 等待子 Agent
|
||||
|
||||
`wait_agent` 是事件订阅,不是轮询:长等待会在子 Agent 产生邮箱活动的瞬间唤醒,延迟与短等待相同。短超时轮询没有收益,每次超时都会多消耗一次工具调用和上下文计费;在实测会话中,约三分之二的等待调用都是超时的短轮询。
|
||||
|
||||
- 仍有本地工作时不要等待。已完成子 Agent 的最终答复会推送到邮箱,并在你的下一轮中到达。
|
||||
- 真正空闲且仍有子 Agent 时,用有界时间段等待:`wait_agent` 的 `timeout_ms` 设为 300000-600000(5-10 分钟)。每段等待结束(唤醒或超时)后发布一行状态,运行 `list_agents`,并追查已完成却没有报告的子 Agent。绝不要堆叠短于 5 分钟的轮询;事件订阅会以同样速度唤醒有界等待。
|
||||
- 完成邮件无法唤醒空闲控制器(它会送达但不会触发新一轮);覆盖这一空闲窗口是 `wait_agent` 唯一的用途。没有活动而超时,是你重新核对状态的信号,不是缩短下一段等待的理由。
|
||||
|
||||
## 派遣时的模型路由
|
||||
|
||||
每次调用 `spawn_agent`——包括你自己作为扇出任务中的子 Agent 时——都要根据正在执行的技能的模型选择规则显式设置 `model` 和 `reasoning_effort`。只设置 `model` 是陷阱:子 Agent 的思考强度会悄悄重置为该模型默认值,而不是继承你的设置。
|
||||
|
||||
请人类伙伴在 `~/.codex/config.toml` 中增加机器级兜底配置,确保遗漏的派遣仍会路由到明确的档位,而不会静默继承会话中最昂贵的模型:
|
||||
|
||||
```toml
|
||||
[agents]
|
||||
default_subagent_model = "<允许列表中的中档模型>"
|
||||
default_subagent_reasoning_effort = "medium"
|
||||
```
|
||||
|
||||
## 环境检测
|
||||
|
||||
创建工作树或完成分支收尾的技能应在继续操作之前,使用只读 git 命令检测其环境:
|
||||
|
||||
```bash
|
||||
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
|
||||
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
|
||||
BRANCH=$(git branch --show-current)
|
||||
```
|
||||
|
||||
- `GIT_DIR != GIT_COMMON` → 已经位于链接工作树中(跳过创建)
|
||||
- `BRANCH` 为空 → detached HEAD(无法从沙箱创建分支/推送/创建 PR)
|
||||
|
||||
有关每个技能如何使用这些信号,请参阅 `using-git-worktrees` 的步骤 0 和 `finishing-a-development-branch` 的步骤 1。
|
||||
|
||||
## Codex App 收尾
|
||||
|
||||
当沙箱阻止分支/推送操作时(在外部管理的工作树中处于 detached HEAD),Agent 会提交所有工作,并告知用户使用 App 的原生控件:
|
||||
|
||||
- **“Create branch”** — 为分支命名,然后通过 App UI 提交/推送/创建 PR
|
||||
- **“Hand off to local”** — 将工作转移到用户的本地检出目录
|
||||
|
||||
Agent 仍然可以运行测试、暂存文件,并输出建议的分支名称、提交消息和 PR 描述,供用户复制。
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
# Gemini CLI 工具映射
|
||||
|
||||
技能以动作来表述(“派遣一个子 Agent”“创建一个待办事项”“读取一个文件”)。在 Gemini CLI 上,这些动作对应于以下工具。
|
||||
|
||||
| 技能所请求的动作 | Gemini CLI 等效方式 |
|
||||
|----------------------|----------------------|
|
||||
| 读取文件 | `read_file` |
|
||||
| 一次读取多个文件 | `read_many_files` |
|
||||
| 创建新文件 | `write_file` |
|
||||
| 编辑文件 | `replace` |
|
||||
| 运行 Shell 命令 | `run_shell_command` |
|
||||
| 搜索文件内容 | `grep_search` |
|
||||
| 按名称查找文件 | `glob` |
|
||||
| 列出文件和子目录 | `list_directory` |
|
||||
| 获取 URL | `web_fetch` |
|
||||
| 搜索网络 | `google_web_search` |
|
||||
| 调用技能 | `activate_skill` |
|
||||
| 派遣子 Agent(`Subagent (general-purpose):` 模板) | 调用 `invoke_agent` 并设置 `agent_name: "generalist"`(也可使用 `@generalist` 聊天语法调用——参见[子 Agent 支持](#子-agent-支持)) |
|
||||
| 多次并行派遣 | 在同一次响应中进行多个 `invoke_agent` 调用 |
|
||||
| 任务跟踪(“创建待办事项”“标记为完成”) | `write_todos`(状态:pending、in_progress、completed、cancelled、blocked) |
|
||||
|
||||
## 指令文件
|
||||
|
||||
技能提到“你的指令文件”时,在 Gemini CLI 中指的是 **`GEMINI.md`**。Gemini CLI 会分层加载 `GEMINI.md`:全局文件位于 `~/.gemini/GEMINI.md`;项目级文件位于工作区目录及其祖先目录;工具访问子目录中的文件时,还会加载该子目录下的 `GEMINI.md`。
|
||||
|
||||
## 个人技能目录
|
||||
|
||||
用户级技能位于 **`~/.gemini/skills/`**,**`~/.agents/skills/`** 则是跨运行时别名(与 Codex 和 Copilot CLI 共享)。同一作用域同时存在两个目录时,`.agents/skills/` 优先。每个技能都是一个包含 `SKILL.md` 的子目录(其 frontmatter 含 `name` 和 `description`)。
|
||||
|
||||
## 子 Agent 支持
|
||||
|
||||
Gemini CLI 通过 `invoke_agent` 工具派遣子 Agent;该工具接受 `agent_name` 和 `prompt` 参数。同一派遣能力也提供聊天语法快捷方式:输入 `@generalist <prompt>`,等同于调用 `invoke_agent` 并设置 `agent_name: "generalist"`。内置 Agent 名称包括 `generalist`、`cli_help`、`codebase_investigator`,以及启用浏览器工具后可用的 `browser_agent`。
|
||||
|
||||
技能使用 `Subagent (general-purpose):` 进行派遣,并且要么引用提示模板文件(例如 `superpowers:subagent-driven-development` 的 `./implementer-prompt.md`),要么直接提供内联提示。在 Gemini CLI 中:
|
||||
|
||||
| 技能派遣形式 | Gemini CLI 等效方式 |
|
||||
|---------------------|----------------------|
|
||||
| 引用 `*-prompt.md` 模板(实现者、任务审查者、代码审查者等) | 填充模板,然后调用 `invoke_agent`,设置 `agent_name: "generalist"` 并传入填充后的提示 |
|
||||
| 引用 `superpowers:requesting-code-review` 的 `./code-reviewer.md` | 调用 `invoke_agent`,设置 `agent_name: "generalist"` 并传入填充后的审查模板 |
|
||||
| 内联提示(未引用模板) | 调用 `invoke_agent`,设置 `agent_name: "generalist"` 并传入内联提示 |
|
||||
|
||||
### 填充提示
|
||||
|
||||
技能提供的提示模板会包含 `{WHAT_WAS_IMPLEMENTED}` 或 `[FULL TEXT of task]` 等占位符。将完整提示传给 `invoke_agent` 之前,必须填充所有占位符。提示模板本身已经包含 Agent 角色、审查标准和预期输出格式——子 Agent 会遵循这些内容。
|
||||
|
||||
### 并行派遣
|
||||
|
||||
Gemini CLI 支持并行派遣子 Agent。在同一次响应中发出多个 `invoke_agent` 调用(或在一个提示中进行多次 `@generalist` 调用),即可并行运行彼此独立的子 Agent 工作。保持依赖任务顺序执行,但不要仅为了让历史记录更简单而把独立的子 Agent 任务串行化。
|
||||
|
||||
## 其他 Gemini CLI 工具
|
||||
|
||||
以下工具为 Gemini CLI 特有:
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|---------|
|
||||
| `save_memory`(旧版) | 当 `experimental.memoryV2 = false` 时跨会话保存事实 |
|
||||
| `get_internal_docs` | 查阅 Gemini CLI 内置文档 |
|
||||
| `ask_user` | 向用户提出结构化问题(文本/单选/多选) |
|
||||
| `enter_plan_mode` / `exit_plan_mode` | 进入或退出只读计划模式 |
|
||||
| `update_topic` | 更新当前对话的主题/战略意图元数据 |
|
||||
| `complete_task` | 表示 Gemini 子 Agent 已完成,并将结果返回给父 Agent |
|
||||
| `tracker_create_task`、`tracker_update_task`、`tracker_get_task`、`tracker_list_tasks`、`tracker_add_dependency`、`tracker_visualize` | 支持依赖关系与可视化的丰富任务跟踪器 |
|
||||
| `read_mcp_resource`、`list_mcp_resources` | 访问 MCP 资源 |
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
# Hermes Agent 工具映射
|
||||
|
||||
技能会描述动作(“派遣子 Agent”“创建待办”“读取文件”)。在 Hermes Agent 上,这些动作对应以下工具。
|
||||
|
||||
## 工具
|
||||
|
||||
| 技能请求的动作 | Hermes 工具 |
|
||||
|---|---|
|
||||
| 读取文件 | `read_file` |
|
||||
| 创建新文件 | `write_file` |
|
||||
| 编辑文件(定向补丁) | `patch` |
|
||||
| 运行 shell 命令 | `terminal` |
|
||||
| 搜索文件内容 | `search_files` |
|
||||
| 按名称查找文件 | 使用 `find` 的 `terminal` |
|
||||
| 获取 URL / 读取网页 | `web_extract(urls=[...])` |
|
||||
| 搜索网页 | `web_search(query=...)` |
|
||||
| 派遣子 Agent | `delegate_task(goal=..., context=..., toolsets=[...], role="leaf")` |
|
||||
| 任务跟踪 | `todo` 工具 |
|
||||
| 调用技能 | `skill_view("skill-name")` |
|
||||
|
||||
## 指令文件
|
||||
|
||||
技能提到“你的指令文件”时,在 Hermes Agent 上指项目目录中的 **`AGENTS.md`**,或全局的 `~/.hermes/SOUL.md`。
|
||||
|
||||
## 调用技能
|
||||
|
||||
Hermes Agent 提供包含 `skill_view` 和 `skills_list` 的 `skills` 工具集。调用 Superpowers 技能:
|
||||
|
||||
```
|
||||
skill_view("brainstorming")
|
||||
skill_view("test-driven-development")
|
||||
```
|
||||
|
||||
如果 `skill_view` 找不到 Superpowers 技能(插件完全注册前,技能可能尚未出现在目录中),则直接读取 SKILL.md:
|
||||
|
||||
```
|
||||
read_file(path="~/.hermes/plugins/superpowers/skills/<skill-name>/SKILL.md")
|
||||
```
|
||||
|
||||
这是其他没有原生技能加载的运行平台所使用的相同回退机制。
|
||||
|
||||
## 派遣子 Agent
|
||||
|
||||
使用 `delegate_task` 为并行或串行工作流创建隔离的子 Agent:
|
||||
|
||||
```
|
||||
delegate_task(goal="...", context="...", toolsets=[...], role="leaf")
|
||||
```
|
||||
|
||||
如果 `delegate_task` 不可用,就在线完成工作,不要臆造工具调用。
|
||||
|
||||
## 任务跟踪
|
||||
|
||||
在会话内使用 `todo` 工具进行任务跟踪。对于多 Agent 任务板,如果可用则使用 `hermes kanban` CLI。将旧的 `TodoWrite` 引用视为任务跟踪动作。
|
||||
@@ -1,16 +0,0 @@
|
||||
# Pi 工具映射
|
||||
|
||||
技能以操作来表述(“派遣一个子 Agent”、“创建一个待办事项”、“读取一个文件”)。在 Pi 上,这些操作对应于下方的工具。
|
||||
|
||||
| 技能请求的操作 | Pi 中的对应方式 |
|
||||
| --- | --- |
|
||||
| 派遣一个子 Agent(`Subagent (general-purpose):` 模板) | 如果可用,使用已安装的子 Agent 工具,例如来自 `pi-subagents` 的 `subagent` |
|
||||
| 任务跟踪(“创建一个待办事项”、“标记为完成”) | 如果可用,使用已安装的待办事项/任务工具,否则在计划或 `TODO.md` 中跟踪任务 |
|
||||
|
||||
## 子 Agent
|
||||
|
||||
Pi 核心本身不附带标准的子 Agent 工具。`pi-subagents` 软件包是一个强大的可选配套组件,并提供了一个支持单 Agent、链式、并行、异步、分叉上下文以及恢复/状态工作流的 `subagent` 工具。如果没有可用的子 Agent 工具,请勿虚构 `Task` 调用;请在当前会话中按顺序执行,或说明尚未安装可选的子 Agent 功能。
|
||||
|
||||
## 任务列表
|
||||
|
||||
Pi 核心本身不附带标准的任务列表工具。如果安装了待办事项/任务扩展,请使用其文档中指定的工具。否则,请使用 Superpowers 计划文件、Markdown 中的检查清单或仓库本地的 `TODO.md` 来跟踪任务。较旧的 Superpowers 文档可能会提及 `TodoWrite`;请将其视为上述任务跟踪操作。
|
||||
@@ -1,118 +0,0 @@
|
||||
---
|
||||
name: verification-before-completion
|
||||
description: 在即将声称工作已完成、已修复或已通过时使用,在提交或创建 PR 之前——要求先运行验证命令并确认输出,然后才能作出任何成功声明;始终先有证据,再作断言
|
||||
---
|
||||
|
||||
# 完成前验证
|
||||
|
||||
## 概述
|
||||
|
||||
**核心原则:** 始终先有证据,再作声明。
|
||||
|
||||
**违反这条规则的字面要求,就是违背这条规则的精神实质。**
|
||||
|
||||
## 铁律
|
||||
|
||||
```
|
||||
没有最新的验证证据,就不得声称已完成
|
||||
```
|
||||
|
||||
如果你没有在这条消息中运行验证命令,就不能声称它已通过。
|
||||
|
||||
## 门控函数
|
||||
|
||||
```
|
||||
在声称任何状态或表达满意之前:
|
||||
|
||||
1. 识别:什么命令能证明这一声明?
|
||||
2. 运行:执行完整命令(重新运行、完整执行)
|
||||
3. 阅读:阅读完整输出,检查退出码,统计失败数
|
||||
4. 验证:输出是否证实该声明?
|
||||
- 如果否:根据证据说明实际状态
|
||||
- 如果是:在提供证据的同时作出声明
|
||||
5. 只有在此之后:才作出声明
|
||||
|
||||
跳过任何一步 = 撒谎,而非验证
|
||||
```
|
||||
|
||||
## 常见失败
|
||||
|
||||
| 声明 | 需要 | 不足以证明 |
|
||||
|-------|----------|----------------|
|
||||
| 测试通过 | 测试命令输出:0 个失败 | 之前的运行、"应该会通过" |
|
||||
| Linter 无错误 | Linter 输出:0 个错误 | 部分检查、推断 |
|
||||
| 构建成功 | 构建命令:exit 0 | Linter 通过、日志看起来正常 |
|
||||
| Bug 已修复 | 针对原始症状的测试:通过 | 代码已更改、据此假定已修复 |
|
||||
| 回归测试有效 | 红-绿循环已验证 | 测试只通过了一次 |
|
||||
| Agent 已完成 | VCS diff 显示有变更 | Agent 报告 "success" |
|
||||
| 要求已满足 | 逐行检查清单 | 测试通过 |
|
||||
## 危险信号 - 停止
|
||||
|
||||
- 使用“应该”“可能”“似乎”等措辞
|
||||
- 在验证之前表达满意(“太好了!”“完美!”“完成了!”等)
|
||||
- 未经验证就准备 commit/push/PR
|
||||
- 相信 Agent 的成功报告
|
||||
- 依赖不完整的验证
|
||||
- 心想“就这一次”
|
||||
- 因为疲惫而想结束工作
|
||||
- **任何在未运行验证的情况下暗示成功的措辞**
|
||||
|
||||
## 防止合理化
|
||||
|
||||
| 借口 | 现实 |
|
||||
|--------|---------|
|
||||
| “现在应该能正常工作了” | 务必运行验证 |
|
||||
| “我有信心” | 信心 ≠ 证据 |
|
||||
| “就这一次” | 没有例外 |
|
||||
| “Linter 已通过” | Linter ≠ compiler |
|
||||
| “Agent 说成功了” | 独立验证 |
|
||||
| “我累了” | 疲惫 ≠ 借口 |
|
||||
| “部分检查就够了” | 部分验证什么也证明不了 |
|
||||
| “措辞不同,所以规则不适用” | 重精神实质,不拘泥于字面 |
|
||||
|
||||
## 关键模式
|
||||
|
||||
**测试:**
|
||||
```
|
||||
✅ [运行测试命令] [看到:34/34 pass] "所有测试均通过"
|
||||
❌ "现在应该能通过" / "看起来正确"
|
||||
```
|
||||
|
||||
**回归测试(TDD 红-绿):**
|
||||
```
|
||||
✅ 编写 → 运行 (pass) → 还原修复 → 运行 (MUST FAIL) → 恢复 → 运行 (pass)
|
||||
❌ "我已经编写了回归测试"(未经红-绿验证)
|
||||
```
|
||||
|
||||
**构建:**
|
||||
```
|
||||
✅ [运行构建] [看到:exit 0] "构建通过"
|
||||
❌ "Linter 已通过"(Linter 不检查编译)
|
||||
```
|
||||
|
||||
**要求:**
|
||||
```
|
||||
✅ 重新阅读计划 → 创建检查清单 → 逐项验证 → 报告缺口或完成情况
|
||||
❌ "测试已通过,阶段完成"
|
||||
```
|
||||
**Agent 委派:**
|
||||
```
|
||||
✅ Agent 报告成功 → 检查 VCS diff → 验证更改 → 报告实际状态
|
||||
❌ 相信 Agent 的报告
|
||||
```
|
||||
|
||||
## 何时应用
|
||||
|
||||
**在以下情况之前始终应用:**
|
||||
- 任何形式的成功/完成声明
|
||||
- 任何满意的表达
|
||||
- 任何关于工作状态的正面陈述
|
||||
- 提交、创建 PR、完成任务
|
||||
- 转到下一个任务
|
||||
- 向 Agent 委派任务
|
||||
|
||||
**规则适用于:**
|
||||
- 完全相同的措辞
|
||||
- 改述和同义词
|
||||
- 对成功的暗示
|
||||
- 任何表明已完成/正确的沟通
|
||||
@@ -1,160 +0,0 @@
|
||||
---
|
||||
name: writing-plans
|
||||
description: 当你已有多步骤任务的规格说明或需求时,在接触代码之前使用
|
||||
---
|
||||
|
||||
# 编写计划
|
||||
|
||||
## 概述
|
||||
|
||||
编写全面的实施计划,并假设工程师对我们的代码库毫无了解且品味堪忧。记录他们需要知道的一切:每项任务要修改哪些文件、代码、测试、可能需要查阅的文档,以及如何进行测试。将完整计划拆成小块任务交给他们。DRY。YAGNI。TDD。频繁提交。
|
||||
|
||||
假设他们是技能娴熟的开发者,但对我们的工具集或问题领域几乎一无所知。假设他们不太懂良好的测试设计。
|
||||
|
||||
**开始时宣布:**“我正在使用 writing-plans 技能来创建实施计划。”
|
||||
|
||||
**上下文:**如果在隔离的工作树中工作,它应当已在执行时通过 `superpowers:using-git-worktrees` 技能创建。
|
||||
|
||||
**将计划保存至:**`docs/superpowers/plans/YYYY-MM-DD-<feature-name>.md`
|
||||
- (用户对计划位置的偏好会覆盖此默认值)
|
||||
|
||||
## 范围检查
|
||||
|
||||
如果规格说明涵盖多个相互独立的子系统,则在头脑风暴期间就应该已将其拆分为多个子项目规格说明。如果尚未拆分,建议将其拆成单独的计划——每个子系统一份。每份计划都应独立产出可运行、可测试的软件。
|
||||
|
||||
## 文件结构
|
||||
|
||||
在定义任务之前,梳理将创建或修改哪些文件,以及每个文件负责什么。分解决策将在这里确定下来。
|
||||
|
||||
- 设计边界清晰且接口定义明确的单元。每个文件都应只有一项明确的职责。
|
||||
- 对于能够一次纳入上下文的代码,你的推理效果最佳;当文件职责聚焦时,你的编辑也更可靠。相比庞大且承担过多职责的文件,应优先选择更小、更聚焦的文件。
|
||||
- 会一起变更的文件应该放在一起。按职责拆分,而不是按技术层拆分。
|
||||
- 在现有代码库中,遵循既有模式。如果代码库使用大文件,不要单方面重构——但如果你正在修改的文件已经变得难以驾驭,那么在计划中加入拆分是合理的。
|
||||
|
||||
此结构会为任务分解提供依据。每项任务都应产出自包含的变更,且这些变更本身应独立合理。
|
||||
|
||||
## 任务大小适配
|
||||
|
||||
任务是具备自身完整测试周期、且值得由一位新的审查者设置一道关卡的最小单元。划分任务边界时:将设置、配置、脚手架和文档步骤并入其可交付成果需要这些步骤的任务中;仅当审查者能够有意义地拒绝一个任务、同时批准其相邻任务时才进行拆分。每项任务都以可独立测试的可交付成果结束。
|
||||
|
||||
## 小块任务粒度
|
||||
|
||||
**每个步骤都是一个操作(2-5 分钟):**
|
||||
- “编写失败的测试”——一个步骤
|
||||
- “运行它以确保它失败”——一个步骤
|
||||
- “实现让测试通过所需的最少代码”——一个步骤
|
||||
- “运行测试并确保它们通过”——一个步骤
|
||||
- “提交”——一个步骤
|
||||
|
||||
## 计划文档标题
|
||||
**每个计划都必须以此标题开头:**
|
||||
|
||||
```markdown
|
||||
# [功能名称] 实施计划
|
||||
|
||||
> **面向 Agent 型工作者:** 必需的子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 来逐项任务实施此计划。步骤使用复选框(`- [ ]`)语法进行跟踪。
|
||||
|
||||
**目标:** [用一句话描述要构建的内容]
|
||||
|
||||
**架构:** [用 2-3 句话说明实现方法]
|
||||
|
||||
**技术栈:** [关键技术/库]
|
||||
|
||||
**规格说明:** [本计划实现的规格/设计文档路径——计划以规格说明为依据,规格说明会随计划一起传递;执行者需要同时阅读二者]
|
||||
|
||||
## 全局约束
|
||||
|
||||
[规范中的项目级要求——最低版本、依赖限制、
|
||||
命名和文案规则、平台要求——每项一行,精确值逐字
|
||||
从规范中复制。每项任务的要求均隐含包含本节。]
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
## 任务结构
|
||||
````markdown
|
||||
### 任务 N: [Component Name]
|
||||
|
||||
**文件:**
|
||||
- 创建:`exact/path/to/file.py`
|
||||
- 修改:`exact/path/to/existing.py:123-145`
|
||||
- 测试:`tests/exact/path/to/test.py`
|
||||
|
||||
**接口:**
|
||||
- 使用:[此任务使用前序任务的哪些内容——写出精确签名]
|
||||
- 产出:[后续任务依赖的内容——写出精确的函数名、参数和返回类型。
|
||||
任务实现者只能看到自己的任务;他们通过此块了解相邻任务使用的名称和类型。]
|
||||
|
||||
- [ ] **步骤 1:编写会失败的测试**
|
||||
|
||||
```python
|
||||
def test_specific_behavior():
|
||||
result = function(input)
|
||||
assert result == expected
|
||||
```
|
||||
|
||||
- [ ] **步骤 2:运行测试以验证其失败**
|
||||
|
||||
运行:`pytest tests/path/test.py::test_name -v`
|
||||
预期:FAIL,并显示“函数未定义”
|
||||
|
||||
- [ ] **步骤 3:编写最小实现**
|
||||
|
||||
```python
|
||||
def function(input):
|
||||
return expected
|
||||
```
|
||||
|
||||
- [ ] **步骤 4:运行测试以验证其通过**
|
||||
|
||||
运行:`pytest tests/path/test.py::test_name -v`
|
||||
预期:PASS
|
||||
|
||||
- [ ] **步骤 5:提交**
|
||||
|
||||
```bash
|
||||
git add tests/path/test.py src/path/file.py
|
||||
git commit -m "feat: 添加特定功能"
|
||||
```
|
||||
````
|
||||
|
||||
## 不得使用占位符
|
||||
每个步骤都必须包含工程师所需的实际内容。以下属于**计划失败项**——绝不要写:
|
||||
- "TBD"、"TODO"、"稍后实现"、"补充细节"
|
||||
- "添加适当的错误处理" / "添加验证" / "处理边界情况"
|
||||
- "为上述内容编写测试"(却不提供实际测试代码)
|
||||
- "类似于任务 N"(重复写出代码——工程师可能不会按顺序阅读任务)
|
||||
- 只描述要做什么,却不展示如何做的步骤(代码步骤必须包含代码块)
|
||||
- 引用任何任务中都未定义的类型、函数或方法
|
||||
|
||||
## 自我审查
|
||||
|
||||
撰写完整计划后,以全新的视角审视规范,并对照规范检查计划。这是你自己执行的检查清单——不是派遣子 Agent。
|
||||
|
||||
**1. 规范覆盖:**快速浏览规范中的每个章节/要求。你能指出实现它的任务吗?列出所有缺漏。
|
||||
|
||||
**2. 占位符扫描:**在计划中搜索红旗项——即上方“禁止占位符”章节中的任何模式。修复它们。
|
||||
|
||||
**3. 类型一致性:**你在后续任务中使用的类型、方法签名和属性名称,是否与早期任务中的定义一致?任务 3 中名为 `clearLayers()` 的函数到了任务 7 却变成 `clearFullLayers()`,这就是一个错误。
|
||||
|
||||
如果发现问题,就地修复。无需重新审查——只需修复并继续。如果发现某项规范要求没有对应任务,就添加该任务。
|
||||
|
||||
## 执行交接
|
||||
|
||||
保存计划后,提供执行方式选择:
|
||||
|
||||
**"计划已完成并保存到 `docs/superpowers/plans/<filename>.md`。有两种执行选项:**
|
||||
|
||||
**1. 子 Agent 驱动(推荐)** - 每个任务派遣一个全新的子 Agent,在任务之间进行审查,快速迭代
|
||||
|
||||
**2. 内联执行** - 在本会话中使用 executing-plans 执行任务,分批执行并设置审查检查点
|
||||
|
||||
**选择哪种方式?"**
|
||||
|
||||
**如果选择子 Agent 驱动:**
|
||||
- **必需的子技能:**使用 superpowers:subagent-driven-development
|
||||
- 每个任务使用一个全新的子 Agent + 两阶段审查
|
||||
|
||||
**如果选择内联执行:**
|
||||
- **必需的子技能:**使用 superpowers:executing-plans
|
||||
- 分批执行,并设置检查点以供审查
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
# 计划文档审查员提示模板
|
||||
|
||||
在分派计划文档审查员子 Agent 时使用此模板。
|
||||
|
||||
**目的:** 验证计划是否完整、是否符合规格,以及是否进行了适当的任务分解。
|
||||
|
||||
**分派时机:** 完整计划编写完成后。
|
||||
|
||||
```
|
||||
子 Agent(通用型):
|
||||
description: "审查计划文档"
|
||||
prompt: |
|
||||
你是一名计划文档审查员。请验证此计划是否完整并已准备好实施。
|
||||
|
||||
**待审查的计划:** [PLAN_FILE_PATH]
|
||||
**供参考的规格:** [SPEC_FILE_PATH]
|
||||
|
||||
## 检查内容
|
||||
|
||||
| 类别 | 需要查找的内容 |
|
||||
|----------|------------------|
|
||||
| 完整性 | TODO、占位符、不完整的任务、缺失的步骤 |
|
||||
| 规格一致性 | 计划涵盖规格要求,不存在重大的范围蔓延 |
|
||||
| 任务分解 | 任务边界清晰,步骤可执行 |
|
||||
| 可构建性 | 工程师能否按照此计划实施而不会陷入困境? |
|
||||
|
||||
## 判定标准
|
||||
|
||||
**只标记会在实施期间造成实际问题的事项。**
|
||||
实施者构建了错误的内容或陷入困境属于问题。
|
||||
细微的措辞、风格偏好以及“有则更好”的建议不属于问题。
|
||||
|
||||
除非存在严重缺口,否则应予以批准——例如遗漏规格中的要求、
|
||||
步骤相互矛盾、存在占位内容,或任务模糊到无法执行。
|
||||
|
||||
## 输出格式
|
||||
|
||||
## 计划审查
|
||||
|
||||
**状态:** 已批准 | 发现问题
|
||||
|
||||
**问题(如有):**
|
||||
- [任务 X,步骤 Y]:[具体问题] - [该问题为何会影响实施]
|
||||
|
||||
**建议(仅供参考,不阻碍批准):**
|
||||
- [改进建议]
|
||||
```
|
||||
|
||||
**审查员返回:** 状态、问题(如有)、建议
|
||||
@@ -1,665 +0,0 @@
|
||||
---
|
||||
name: writing-skills
|
||||
description: 在创建新技能、编辑现有技能,或在部署前验证技能是否有效时使用
|
||||
---
|
||||
|
||||
# 编写技能
|
||||
|
||||
## 概述
|
||||
|
||||
**编写技能就是将测试驱动开发应用于流程文档。**
|
||||
|
||||
**个人技能位于你的运行时的技能目录中**(Claude Code 为 `~/.claude/skills/`)——Codex 的路径参见 [codex-tools.md](../using-superpowers/references/codex-tools.md),Gemini 的路径参见 [gemini-tools.md](../using-superpowers/references/gemini-tools.md)。Codex、Copilot CLI 和 Gemini CLI 也都识别 `~/.agents/skills/` 这一跨运行时别名。
|
||||
|
||||
你编写测试用例(包含子 Agent 的压力场景),观察它们失败(基线行为),编写技能(文档),观察测试通过(Agent 遵从要求),然后进行重构(堵住漏洞)。
|
||||
|
||||
**核心原则:** 如果你没有观察过 Agent 在没有该技能时失败,你就不知道该技能是否教了正确的内容。
|
||||
|
||||
**必备背景:** 在使用此技能之前,你必须理解 superpowers:test-driven-development。该技能定义了基本的红-绿-重构循环。此技能将 TDD 应用于文档。
|
||||
|
||||
**官方指南:** 有关 Anthropic 官方的技能编写最佳实践,请参阅 anthropic-best-practices.md。该文档提供了额外的模式和指南,作为对此技能中以 TDD 为重点的方法的补充。
|
||||
|
||||
## 什么是技能?
|
||||
|
||||
**技能**是经过验证的技术、模式或工具的参考指南。技能帮助未来的 Agent 找到并应用有效的方法。
|
||||
|
||||
**技能是:** 可复用的技术、模式、工具、参考指南
|
||||
|
||||
**技能不是:** 关于你曾经如何解决某个问题的叙事
|
||||
|
||||
## 技能的 TDD 映射
|
||||
|
||||
| TDD 概念 | 技能创建 |
|
||||
|-------------|----------------|
|
||||
| **测试用例** | 包含子 Agent 的压力场景 |
|
||||
| **生产代码** | 技能文档(SKILL.md) |
|
||||
| **测试失败(红)** | Agent 在没有技能时违反规则(基线) |
|
||||
| **测试通过(绿)** | Agent 在技能存在时遵从要求 |
|
||||
| **重构** | 在维持遵从性的同时堵住漏洞 |
|
||||
| **先编写测试** | 在编写技能之前运行基线场景 |
|
||||
| **观察它失败** | 记录 Agent 使用的确切合理化说辞 |
|
||||
| **最小代码** | 编写技能以处理那些具体违规行为 |
|
||||
| **观察它通过** | 验证 Agent 现在会遵从要求 |
|
||||
| **重构循环** | 找出新的合理化说辞 → 堵住漏洞 → 重新验证 |
|
||||
|
||||
整个技能创建过程都遵循红-绿-重构。
|
||||
|
||||
## 何时创建技能
|
||||
**在以下情况下创建:**
|
||||
- 该技术并非你凭直觉就能想到的
|
||||
- 你会在不同项目中再次参考它
|
||||
- 该模式具有广泛适用性(并非项目特定)
|
||||
- 其他人会从中受益
|
||||
|
||||
**不要为以下内容创建:**
|
||||
- 一次性解决方案
|
||||
- 已在其他地方得到充分记录的标准实践
|
||||
- 项目特定的约定(将其放入你的指令文件中)
|
||||
- 机械性约束(如果可以用正则表达式/验证来强制执行,就将其自动化——把文档留给需要判断的情况)
|
||||
|
||||
## 技能类型
|
||||
|
||||
### 技术
|
||||
需要遵循具体步骤的方法(condition-based-waiting、root-cause-tracing)
|
||||
|
||||
### 模式
|
||||
思考问题的方式(flatten-with-flags、test-invariants)
|
||||
|
||||
### 参考资料
|
||||
API 文档、语法指南、工具文档(office docs)
|
||||
|
||||
## 目录结构
|
||||
|
||||
|
||||
```
|
||||
skills/
|
||||
skill-name/
|
||||
SKILL.md # 主要参考资料(必需)
|
||||
supporting-file.* # 仅在需要时
|
||||
```
|
||||
|
||||
**扁平命名空间**——所有技能都位于一个可搜索的命名空间中
|
||||
|
||||
**以下内容使用单独的文件:**
|
||||
1. **大量参考资料**(100+ 行)——API 文档、全面的语法说明
|
||||
2. **可复用工具**——脚本、实用工具、模板
|
||||
|
||||
**以下内容保持内联:**
|
||||
- 原则和概念
|
||||
- 代码模式(< 50 行)
|
||||
- 其他所有内容
|
||||
|
||||
## SKILL.md 结构
|
||||
**前置元数据(YAML):**
|
||||
- 两个必填字段:`name` 和 `description`(所有受支持的字段请参阅 [agentskills.io/specification](https://agentskills.io/specification))
|
||||
- 总计最多 1024 个字符
|
||||
- `name`:只能使用字母、数字和连字符(不得使用圆括号或特殊字符)
|
||||
- `description`:使用第三人称,只描述何时使用(而不是它做什么)
|
||||
- 以“在……时使用……”开头,以聚焦触发条件
|
||||
- 包含具体的症状、情形和上下文
|
||||
- **绝不要概括该技能的流程或工作流**(原因请参阅 SDO 章节)
|
||||
- 如有可能,控制在 500 个字符以内
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: Skill-Name-With-Hyphens
|
||||
description: 在 [specific triggering conditions and symptoms] 时使用
|
||||
---
|
||||
|
||||
# 技能名称
|
||||
|
||||
## 概述
|
||||
这是什么?用 1-2 句话说明核心原则。
|
||||
|
||||
## 何时使用
|
||||
[如果决策并不显而易见,请加入小型内联流程图]
|
||||
|
||||
包含症状和使用场景的项目符号列表
|
||||
何时不应使用
|
||||
|
||||
## 核心模式(适用于技术/模式)
|
||||
修改前/修改后的代码对比
|
||||
|
||||
## 快速参考
|
||||
用于快速浏览常见操作的表格或项目符号列表
|
||||
|
||||
## 实现
|
||||
对简单模式使用内联代码
|
||||
对大型参考资料或可复用工具提供文件链接
|
||||
|
||||
## 常见错误
|
||||
出错之处 + 修复方法
|
||||
|
||||
## 实际影响(可选)
|
||||
具体成果
|
||||
```
|
||||
|
||||
|
||||
## 技能发现优化(SDO)
|
||||
**对发现至关重要:** 未来的 Agent 需要找到你的技能
|
||||
|
||||
### 1. 丰富的描述字段
|
||||
|
||||
**目的:** 你的 Agent 会读取描述,以决定针对给定任务加载哪些技能。要让它回答:“我现在应该阅读这个技能吗?”
|
||||
|
||||
**格式:** 以“用于……时”开头,以聚焦触发条件
|
||||
|
||||
**关键:描述 = 何时使用,而不是技能做什么**
|
||||
|
||||
描述应当只说明触发条件。切勿在描述中概括技能的过程或工作流。
|
||||
|
||||
**这为何重要:** 测试表明,当描述概括了技能的工作流时,Agent 可能会依照描述行事,而不是阅读完整的技能内容。一条写着“在任务之间进行代码审查”的描述导致 Agent 只进行了一次审查,尽管技能的流程图清楚地显示了两次审查(先审查规格符合性,再审查代码质量)。
|
||||
|
||||
当描述被改为仅写“用于执行包含相互独立任务的实施计划时”(不概括工作流)后,Agent 正确地阅读了流程图,并遵循了两阶段审查流程。
|
||||
|
||||
**陷阱:** 概括工作流的描述会创造一条 Agent 将会采用的捷径。技能正文会变成 Agent 跳过的文档。
|
||||
|
||||
```yaml
|
||||
# ❌ 错误:概括了工作流——Agent 可能会依照此描述行事,而不是阅读技能
|
||||
描述: 用于执行计划时 - 为每个任务派遣子 Agent,并在任务之间进行代码审查
|
||||
|
||||
# ❌ 错误:包含过多过程细节
|
||||
描述: 用于 TDD - 先编写测试,观察它失败,编写最少量代码,重构
|
||||
|
||||
# ✅ 正确:只有触发条件,没有工作流概述
|
||||
描述: 用于在当前会话中执行包含相互独立任务的实施计划时
|
||||
|
||||
# ✅ 正确:只有触发条件
|
||||
描述: 用于实现任何功能或修复任何错误时,在编写实现代码之前
|
||||
```
|
||||
|
||||
**内容:**
|
||||
- 使用具体的触发因素、症状和情形来表明此技能适用
|
||||
- 描述*问题*(竞态条件、行为不一致),而不是*特定于语言的症状*(setTimeout、sleep)
|
||||
- 除非技能本身特定于某项技术,否则应保持触发条件与技术无关
|
||||
- 如果技能特定于某项技术,请在触发条件中明确说明
|
||||
- 使用第三人称撰写(会被注入系统提示词)
|
||||
- **切勿概括技能的过程或工作流**
|
||||
|
||||
```yaml
|
||||
# ❌ 不佳:过于抽象、含糊,且未说明何时使用
|
||||
description: 用于异步测试
|
||||
|
||||
# ❌ 不佳:使用第一人称
|
||||
description: 当异步测试不稳定时,我可以帮助你处理
|
||||
|
||||
# ❌ 不佳:提到了技术,但该技能并非专门针对该技术
|
||||
description: 用于测试使用 setTimeout/sleep 且不稳定时
|
||||
|
||||
# ✅ 良好:以“用于……时”开头,描述问题,不包含工作流
|
||||
description: 用于测试存在竞态条件、时序依赖,或通过/失败结果不一致时
|
||||
|
||||
# ✅ 良好:针对特定技术的技能,具有明确的触发条件
|
||||
description: 用于使用 React Router 并处理身份验证重定向时
|
||||
```
|
||||
|
||||
### 2. 关键词覆盖
|
||||
使用 Agent 会搜索的词语:
|
||||
- 错误消息:"Hook timed out"、"ENOTEMPTY"、"race condition"
|
||||
- 症状:"flaky"、"hanging"、"zombie"、"pollution"
|
||||
- 同义词:"timeout/hang/freeze"、"cleanup/teardown/afterEach"
|
||||
- 工具:实际命令、库名称、文件类型
|
||||
|
||||
### 3. 描述性命名
|
||||
|
||||
**使用主动语态、动词优先:**
|
||||
- ✅ `creating-skills`,而不是 `skill-creation`
|
||||
- ✅ `condition-based-waiting`,而不是 `async-test-helpers`
|
||||
|
||||
### 4. Token 效率(关键)
|
||||
|
||||
**问题:** getting-started 和经常引用的技能会加载到每一次对话中。每个 Token 都至关重要。
|
||||
|
||||
**目标字数:**
|
||||
- getting-started 工作流:每个 <150 词
|
||||
- 经常加载的技能:总计 <200 词
|
||||
- 其他技能:<500 词(仍需简洁)
|
||||
|
||||
**技巧:**
|
||||
|
||||
**将细节移至工具帮助中:**
|
||||
```bash
|
||||
# ❌ 错误:在 SKILL.md 中记录所有标志
|
||||
search-conversations 支持 --text、--both、--after DATE、--before DATE、--limit N
|
||||
|
||||
# ✅ 正确:引用 --help
|
||||
search-conversations 支持多种模式和筛选器。运行 --help 了解详情。
|
||||
```
|
||||
|
||||
**使用交叉引用:**
|
||||
```markdown
|
||||
# ❌ 错误:重复工作流细节
|
||||
搜索时,使用模板分派子 Agent...
|
||||
[20 行重复的说明]
|
||||
|
||||
# ✅ 正确:引用其他技能
|
||||
始终使用子 Agent(可节省 50-100x 的上下文)。强制要求:工作流必须使用 [other-skill-name]。
|
||||
```
|
||||
|
||||
**压缩示例:**
|
||||
```markdown
|
||||
# ❌ 错误:冗长示例(42 个词)
|
||||
你的人类伙伴:"我们之前是如何处理 React Router 中的身份验证错误的?"
|
||||
你:我会在过去的对话中搜索 React Router 身份验证模式。
|
||||
[派发子 Agent,搜索查询:"React Router 身份验证错误处理 401"]
|
||||
|
||||
# ✅ 正确:最简示例(20 个词)
|
||||
伙伴:"我们是如何处理 React Router 中的身份验证错误的?"
|
||||
你:正在搜索……
|
||||
[派发子 Agent → 综合]
|
||||
```
|
||||
|
||||
**消除冗余:**
|
||||
- 不要重复交叉引用的技能中已有的内容
|
||||
- 不要解释从命令本身就显而易见的内容
|
||||
- 不要为同一种模式提供多个示例
|
||||
|
||||
**验证:**
|
||||
```bash
|
||||
wc -w skills/path/SKILL.md
|
||||
# getting-started 工作流:目标是每个少于 150 个词
|
||||
# 其他经常加载的内容:目标是总计少于 200 个词
|
||||
```
|
||||
|
||||
**根据你所做的事情或核心洞见来命名:**
|
||||
- ✅ `condition-based-waiting` 优于 `async-test-helpers`
|
||||
- ✅ 使用 `using-skills`,而不是 `skill-usage`
|
||||
- ✅ `flatten-with-flags` 优于 `data-structure-refactoring`
|
||||
- ✅ `root-cause-tracing` 优于 `debugging-techniques`
|
||||
|
||||
**动名词(-ing)很适合用于过程:**
|
||||
- `creating-skills`、`testing-skills`、`debugging-with-logs`
|
||||
- 主动式,描述你正在采取的行动
|
||||
|
||||
### 5. 交叉引用其他技能
|
||||
|
||||
**编写引用其他技能的文档时:**
|
||||
|
||||
仅使用技能名称,并带有明确的要求标记:
|
||||
- ✅ 正确:`**必需的子技能:** 使用 superpowers:test-driven-development`
|
||||
- ✅ 正确:`**必需背景:** 你必须理解 superpowers:systematic-debugging`
|
||||
- ❌ 错误:`参见 skills/testing/test-driven-development`(不清楚是否为必需项)
|
||||
- ❌ 错误:`@skills/testing/test-driven-development/SKILL.md`(强制加载,消耗上下文)
|
||||
|
||||
**为什么不用 @ 链接:** `@` 语法会立即强制加载文件,在你需要它们之前就消耗 200k+ 的上下文。
|
||||
|
||||
## 流程图用法
|
||||
```dot
|
||||
digraph when_flowchart {
|
||||
"Need to show information?" [shape=diamond, label="需要展示信息吗?"];
|
||||
"Decision where I might go wrong?" [shape=diamond, label="是否存在我可能出错的决策?"];
|
||||
"Use markdown" [shape=box, label="使用 Markdown"];
|
||||
"Small inline flowchart" [shape=box, label="小型内联流程图"];
|
||||
|
||||
"Need to show information?" -> "Decision where I might go wrong?" [label="是"];
|
||||
"Decision where I might go wrong?" -> "Small inline flowchart" [label="是"];
|
||||
"Decision where I might go wrong?" -> "Use markdown" [label="否"];
|
||||
}
|
||||
```
|
||||
|
||||
**仅在以下情况使用流程图:**
|
||||
- 非显而易见的决策点
|
||||
- 你可能过早停止的流程循环
|
||||
- “何时使用 A、何时使用 B”的决策
|
||||
|
||||
**切勿将流程图用于:**
|
||||
- 参考资料 → 表格、列表
|
||||
- 代码示例 → Markdown 代码块
|
||||
- 线性指令 → 编号列表
|
||||
- 没有语义含义的标签(step1、helper2)
|
||||
|
||||
有关 Graphviz 样式规则,请参阅此目录中的 `graphviz-conventions.dot`。
|
||||
|
||||
**为你的人类伙伴进行可视化:** 使用此目录中的 `render-graphs.js` 将某个技能的流程图渲染为 SVG:
|
||||
```bash
|
||||
./render-graphs.js ../some-skill # 每个图表分别渲染
|
||||
./render-graphs.js ../some-skill --combine # 所有图表合并到一个 SVG 中
|
||||
```
|
||||
|
||||
## 代码示例
|
||||
|
||||
**一个出色的示例胜过许多个平庸的示例**
|
||||
|
||||
选择最相关的语言:
|
||||
- 测试技术 → TypeScript/JavaScript
|
||||
- 系统调试 → Shell/Python
|
||||
- 数据处理 → Python
|
||||
|
||||
**良好示例:**
|
||||
- 完整且可运行
|
||||
- 注释充分,解释为什么这样做
|
||||
- 源自真实场景
|
||||
- 清晰地展示模式
|
||||
- 可直接改编(而非通用模板)
|
||||
|
||||
**不要:**
|
||||
- 使用 5+ 种语言来实现
|
||||
- 创建填空式模板
|
||||
- 编写刻意编造的示例
|
||||
|
||||
你擅长移植——一个出色的示例就足够了。
|
||||
|
||||
## 文件组织
|
||||
### 自包含技能
|
||||
```
|
||||
defense-in-depth/
|
||||
SKILL.md # 所有内容均内联
|
||||
```
|
||||
适用场景:所有内容都能容纳,无需大量参考资料
|
||||
|
||||
### 带有可复用工具的技能
|
||||
```
|
||||
condition-based-waiting/
|
||||
SKILL.md # 概述 + 模式
|
||||
example.ts # 可供调整的可用辅助函数
|
||||
```
|
||||
适用场景:工具是可复用代码,而不只是叙述性内容
|
||||
|
||||
### 包含大量参考资料的技能
|
||||
```
|
||||
pptx/
|
||||
SKILL.md # 概述 + 工作流
|
||||
pptxgenjs.md # 600 行 API 参考
|
||||
ooxml.md # 500 行 XML 结构
|
||||
scripts/ # 可执行工具
|
||||
```
|
||||
何时:参考资料过大,无法内联
|
||||
|
||||
## 铁律(与 TDD 相同)
|
||||
```
|
||||
没有先失败的测试,就不能编写技能
|
||||
```
|
||||
|
||||
这适用于新技能以及对现有技能的编辑。
|
||||
|
||||
在测试之前编写技能?删除它。重新开始。
|
||||
未经测试就编辑技能?同样是违规。
|
||||
|
||||
**没有例外:**
|
||||
- “简单的增补”也不例外
|
||||
- “只是添加一个章节”也不例外
|
||||
- “文档更新”也不例外
|
||||
- 不要把未经测试的更改作为“参考”保留下来
|
||||
- 不要在运行测试时进行“调整”
|
||||
- 删除就是删除
|
||||
|
||||
**必备背景知识:** superpowers:test-driven-development 技能解释了为什么这很重要。同样的原则也适用于文档。
|
||||
|
||||
## 测试所有技能类型
|
||||
不同类型的技能需要采用不同的测试方法:
|
||||
|
||||
### 纪律强制型技能(规则/要求)
|
||||
|
||||
**示例:** TDD、verification-before-completion、designing-before-coding
|
||||
|
||||
**测试方式:**
|
||||
- 理论问题:他们理解这些规则吗?
|
||||
- 压力场景:他们在压力下会遵守吗?
|
||||
- 多重压力叠加:时间压力 + 沉没成本 + 精疲力竭
|
||||
- 识别合理化说辞,并添加明确的反驳
|
||||
|
||||
**成功标准:** Agent 在最大压力下仍遵守规则
|
||||
|
||||
### 技法型技能(操作指南)
|
||||
|
||||
**示例:** condition-based-waiting、root-cause-tracing、defensive-programming
|
||||
|
||||
**测试方式:**
|
||||
- 应用场景:他们能正确应用该技法吗?
|
||||
- 变体场景:他们能处理边界情况吗?
|
||||
- 信息缺失测试:指令是否存在缺漏?
|
||||
|
||||
**成功标准:** Agent 成功将该技法应用于新场景
|
||||
|
||||
### 模式型技能(心智模型)
|
||||
|
||||
**示例:** reducing-complexity、information-hiding 概念
|
||||
|
||||
**测试方式:**
|
||||
- 识别场景:他们能识别出何时适用该模式吗?
|
||||
- 应用场景:他们能使用该心智模型吗?
|
||||
- 反例:他们知道何时不应应用吗?
|
||||
|
||||
**成功标准:** Agent 正确识别何时以及如何应用该模式
|
||||
|
||||
### 参考型技能(文档/API)
|
||||
|
||||
**示例:** API 文档、命令参考、库指南
|
||||
|
||||
**测试方式:**
|
||||
- 检索场景:他们能找到正确的信息吗?
|
||||
- 应用场景:他们能正确使用所找到的信息吗?
|
||||
- 缺漏测试:是否涵盖了常见用例?
|
||||
|
||||
**成功标准:** Agent 找到并正确应用参考信息
|
||||
|
||||
## 跳过测试的常见合理化说辞
|
||||
|
||||
| 借口 | 事实 |
|
||||
|--------|---------|
|
||||
| "技能显然很清楚" | 对你清楚 ≠ 对其他 Agent 清楚。测试它。 |
|
||||
| "它只是一个参考资料" | 参考资料也可能存在缺漏和不清楚的部分。测试检索。 |
|
||||
| "测试太小题大做了" | 未经测试的技能总会有问题。无一例外。花 15 分钟测试能节省数小时。 |
|
||||
| "如果出现问题,我再测试" | 出现问题 = Agent 无法使用该技能。在部署之前测试。 |
|
||||
| "测试太繁琐了" | 测试远没有在生产环境中调试糟糕的技能那么繁琐。 |
|
||||
| "我确信它很好" | 过度自信必然会带来问题。无论如何都要测试。 |
|
||||
| "理论审查就足够了" | 阅读 ≠ 使用。测试应用场景。 |
|
||||
| "没时间测试" | 部署未经测试的技能,会浪费更多时间在之后的修复上。 |
|
||||
|
||||
**所有这些都意味着:部署前进行测试。无一例外。**
|
||||
|
||||
## 让形式与失败相匹配
|
||||
在编写指导之前,先对基线失败进行分类。用于严防一种失败类型的形式,会在另一种失败类型上产生可测量的反效果。
|
||||
|
||||
| 基线失败 | 正确形式 | 错误形式 |
|
||||
|---|---|---|
|
||||
| 在压力下跳过/违反规则(明知不该如此,却仍然这样做) | 禁令 + 反合理化表格 + 红旗项(见下文的防弹加固部分) | 软性指导(“优先……”、“考虑……”) |
|
||||
| 遵从规则,但输出形态错误(提示词臃肿、结论被埋没、重述规范) | 正向配方或契约:说明输出是什么——按顺序列出其组成部分 | 禁令列表(“不要重述”、“绝不叙述过程”) |
|
||||
| 从其本已产出的内容中遗漏某个必需元素 | 结构化形式:在其填写的模板中设置 REQUIRED 字段或槽位 | 模板附近的文字提醒 |
|
||||
| 行为应取决于某个条件 | 以可观察谓词为依据的条件规则(“如果简报存在,就引用它”) | 无条件规则 + 豁免条款 |
|
||||
|
||||
**为什么禁令会在塑形问题上适得其反:**当存在相互竞争的激励(“让提示词自包含”)时,Agent 会与“不要做 X”讨价还价。在针对分派提示词指导的措辞正面对照测试中,禁令组产生的不需要内容明显多于配方组(分布完全分离),而且趋势上甚至比无指导对照组更差——请针对你自己的情况进行微型测试,而不要想当然;但绝不要默认采用禁令。配方不会留下任何讨价还价的余地:输出要么符合规定的形态,要么不符合。
|
||||
|
||||
**无论选择哪种形式,都要遵守以下规则:**
|
||||
- **不要添加细微限定条款。**“不要做 X,除非这很重要”会重新开启讨价还价——在同一组措辞测试中,仅向一个原本胜出的配方追加一条细微限定条款,就使其从稳定一致退化为波动嘈杂。应将真正的例外表述为以可观察谓词为依据的独立条件规则。
|
||||
- **豁免条款并不能限定适用范围。**“此限制不适用于代码块”仍然会抑制代码块。如果输出的一部分必须获得豁免,就重构规则,使该规则无法作用到这一部分。
|
||||
|
||||
## 对技能进行防弹加固,以抵御合理化
|
||||
|
||||
强制执行纪律的技能(如 TDD)需要能够抵御合理化。Agent 很聪明,在压力下会找到漏洞。
|
||||
|
||||
**适用范围:**本工具包适用于纪律失守——即 Agent 明知规则,却在压力下跳过它。对于输出形态错误或遗漏元素的情况,基于禁令的防弹加固会适得其反;请改用“使形式与失败相匹配”中的形式。
|
||||
|
||||
**心理学说明:**理解说服技巧为何有效,有助于你系统地应用它们。有关权威、承诺、稀缺、社会认同和统一性原则的研究基础(Cialdini, 2021; Meincke et al., 2025),请参阅 persuasion-principles.md。
|
||||
|
||||
### 明确封堵每一个漏洞
|
||||
|
||||
不要只是陈述规则——还要禁止具体的规避手段:
|
||||
|
||||
<Bad>
|
||||
```markdown
|
||||
在测试之前写了代码?删除它。
|
||||
```
|
||||
</Bad>
|
||||
|
||||
<Good>
|
||||
```markdown
|
||||
在测试之前写了代码?删除它。重新开始。
|
||||
|
||||
**没有例外:**
|
||||
- 不要把它留作“参考”
|
||||
- 不要在编写测试时“改造”它
|
||||
- 不要查看它
|
||||
- 删除就是删除
|
||||
```
|
||||
</Good>
|
||||
|
||||
### 回应“精神与字面”论点
|
||||
尽早加入根本原则:
|
||||
|
||||
```markdown
|
||||
**违反规则的字面要求,就是违反规则的精神。**
|
||||
```
|
||||
|
||||
这会彻底堵住整类“我遵循的是规则精神”式的合理化借口。
|
||||
|
||||
### 构建反合理化表格
|
||||
|
||||
记录基线测试中发现的合理化借口(见下方的“测试”部分)。Agent 提出的每一个借口都要放进表格中:
|
||||
|
||||
```markdown
|
||||
| 借口 | 事实 |
|
||||
|--------|---------|
|
||||
| "太简单了,没必要测试" | 简单的代码也会出错。测试只需 30 秒。 |
|
||||
| "我之后再测试" | 测试立即通过什么也证明不了。 |
|
||||
| "事后测试也能达到相同目标" | 事后测试 = "这段代码做什么?" 测试先行 = "这段代码应该做什么?" |
|
||||
```
|
||||
|
||||
### 创建红旗清单
|
||||
|
||||
让 Agent 在进行合理化时能够轻松自查:
|
||||
|
||||
```markdown
|
||||
## 红旗——停止并从头开始
|
||||
|
||||
- 先写代码,后写测试
|
||||
- "我已经手动测试过了"
|
||||
- "事后测试也能达到相同目的"
|
||||
- "重要的是精神,而不是仪式"
|
||||
- "这次不一样,因为……"
|
||||
|
||||
**所有这些都意味着:删除代码。使用 TDD 从头开始。**
|
||||
```
|
||||
|
||||
### 更新 SDO 以涵盖违规征兆
|
||||
|
||||
添加到 description 中:你正要违反规则时会出现的征兆:
|
||||
|
||||
```yaml
|
||||
description: 在实现任何功能或修复任何缺陷时使用,且应在编写实现代码之前使用
|
||||
```
|
||||
|
||||
## 技能的 RED-GREEN-REFACTOR
|
||||
遵循 TDD 循环:
|
||||
|
||||
### RED:编写失败测试(基线)
|
||||
|
||||
在不使用该技能的情况下,让子 Agent 运行压力场景。记录其确切行为:
|
||||
- 他们做出了哪些选择?
|
||||
- 他们使用了哪些合理化说辞(逐字记录)?
|
||||
- 哪些压力触发了违规行为?
|
||||
|
||||
这就是“观察测试失败”——你必须在编写技能之前,先看看 Agent 自然而然会怎么做。
|
||||
|
||||
### GREEN:编写最小技能
|
||||
|
||||
编写技能来应对那些特定的合理化说辞。不要为假设情况添加额外内容。
|
||||
|
||||
在使用该技能的情况下运行相同场景。Agent 现在应该遵从要求。
|
||||
|
||||
### REFACTOR:堵住漏洞
|
||||
|
||||
Agent 找到了新的合理化说辞?添加明确的反驳。重新测试,直到无懈可击。
|
||||
|
||||
### 在完整场景之前对措辞进行微测试
|
||||
|
||||
完整的压力场景运行是最终关卡,但每次迭代运行起来都缓慢且昂贵。先使用微测试验证措辞本身:
|
||||
|
||||
1. **每次调用使用一个全新上下文样本**——一次原始 API 调用;如果你没有 API 访问权限,则使用一次单轮子 Agent 调用。System prompt = 指导内容将实际存在于其中的真实上下文(完整技能或提示词模板,而不是孤立的指导内容);user message = 一项会诱发失败行为的任务。
|
||||
2. **始终包含无指导对照组。** 如果对照组没有表现出该失败行为,就没有任何需要修复的东西——停止,不要编写该指导内容。
|
||||
3. **每个变体重复 5 次以上。** 单个样本会骗人。
|
||||
4. **手动阅读每个被标记的匹配项。** 如果愿意,你可以通过程序进行评分,但模板复述和被引用的反例会伪装成命中项;仅依靠自动计数会同时夸大失败和成功。
|
||||
5. **方差是一项指标。** 当指导内容生效时,各次重复结果会收敛到相同的形态。五次重复产生五种不同的解释,意味着措辞不具约束力——在增加文字之前,先收紧形式。
|
||||
|
||||
微测试验证措辞;对于纪律型技能,它们不能取代压力场景。
|
||||
|
||||
**测试方法:** 完整的测试方法请参阅 [testing-skills-with-subagents.md](testing-skills-with-subagents.md):
|
||||
- 如何编写压力场景
|
||||
- 压力类型(时间、沉没成本、权威、精疲力竭)
|
||||
- 系统性地堵住漏洞
|
||||
- 元测试技术
|
||||
|
||||
## 反模式
|
||||
### ❌ 叙事性示例
|
||||
“在会话 2025-10-03 中,我们发现空的 projectDir 导致了……”
|
||||
**为什么不好:** 过于具体,不可复用
|
||||
|
||||
### ❌ 多语言稀释
|
||||
example-js.js, example-py.py, example-go.go
|
||||
**为什么不好:** 质量平庸,维护负担沉重
|
||||
|
||||
### ❌ 流程图中的代码
|
||||
```dot
|
||||
step1 [label="导入 fs"];
|
||||
step2 [label="读取文件"];
|
||||
```
|
||||
**为什么不好:** 无法复制粘贴,难以阅读
|
||||
|
||||
### ❌ 通用标签
|
||||
helper1, helper2, step3, pattern4
|
||||
**为什么不好:** 标签应具有语义
|
||||
|
||||
## 停止:在转到下一个技能之前
|
||||
|
||||
**写完任何一个技能后,你都必须停止并完成部署流程。**
|
||||
|
||||
**严禁:**
|
||||
- 在未逐一测试的情况下批量创建多个技能
|
||||
- 在当前技能通过验证之前转到下一个技能
|
||||
- 因为“批处理更高效”而跳过测试
|
||||
|
||||
**以下部署检查清单对每一个技能都是强制性的。**
|
||||
|
||||
部署未经测试的技能 = 部署未经测试的代码。这违反了质量标准。
|
||||
|
||||
## 技能创建检查清单(TDD 改编版)
|
||||
**重要:为下面的每一个检查清单项创建一个 todo。**
|
||||
|
||||
**RED 阶段——编写失败测试:**
|
||||
- [ ] 创建压力场景(对于纪律型技能,组合 3 种以上压力)
|
||||
- [ ] 在不使用技能的情况下运行场景——逐字记录基线行为
|
||||
- [ ] 识别合理化说辞/失败中的模式
|
||||
|
||||
**GREEN 阶段——编写最小技能:**
|
||||
- [ ] 名称仅使用字母、数字、连字符(不得使用圆括号/特殊字符)
|
||||
- [ ] YAML 前置元数据包含必需的 `name` 和 `description` 字段(最多 1024 个字符;参见[规范](https://agentskills.io/specification))
|
||||
- [ ] 描述以“在……时使用”开头,并包含具体的触发条件/症状
|
||||
- [ ] 描述使用第三人称撰写
|
||||
- [ ] 全文包含用于搜索的关键词(错误、症状、工具)
|
||||
- [ ] 提供清晰的概述,并包含核心原则
|
||||
- [ ] 处理 RED 阶段识别出的具体基线失败
|
||||
- [ ] 指导形式与失败类型相匹配(参见“使形式与失败相匹配”)
|
||||
- [ ] 对于行为塑造型指导:将措辞与无指导对照进行微测试(重复 5 次以上,手动阅读每一条被标记的匹配结果)——纯参考型技能为 N/A
|
||||
- [ ] 内联代码,或者链接到单独的文件
|
||||
- [ ] 提供一个极佳的示例(不要使用多种语言)
|
||||
- [ ] 在使用技能的情况下运行场景——验证 Agent 现在会遵从要求
|
||||
|
||||
**REFACTOR 阶段——堵住漏洞:**
|
||||
- [ ] 识别测试中出现的新的合理化说辞
|
||||
- [ ] 添加明确的反驳措施(如果是纪律型技能)
|
||||
- [ ] 根据所有测试迭代构建合理化说辞表格
|
||||
- [ ] 创建红旗项列表
|
||||
- [ ] 反复重新测试,直至无懈可击
|
||||
|
||||
**质量检查:**
|
||||
- [ ] 仅在决策并非显而易见时使用小型流程图
|
||||
- [ ] 快速参考表
|
||||
- [ ] 常见错误部分
|
||||
- [ ] 不进行叙事性讲述
|
||||
- [ ] 仅为工具或大量参考资料提供支持文件
|
||||
|
||||
**部署:**
|
||||
- [ ] 将技能提交到 git,并推送到你的 fork(如果已配置)
|
||||
- [ ] 考虑通过 PR 回馈贡献(如果具有广泛用途)
|
||||
|
||||
## 发现工作流
|
||||
未来的 Agent 如何找到你的技能:
|
||||
|
||||
1. **遇到问题**(“测试不稳定”)
|
||||
2. **搜索技能**(用 grep 搜索描述、浏览类别)
|
||||
3. **找到 SKILL**(描述匹配)
|
||||
4. **浏览概述**(这相关吗?)
|
||||
5. **阅读模式**(快速参考表)
|
||||
6. **加载示例**(仅在实现时)
|
||||
|
||||
**针对这一流程进行优化**——尽早并经常放入可搜索的术语。
|
||||
-1129
File diff suppressed because it is too large.
Load diff
-187
@@ -1,187 +0,0 @@
|
||||
# 测试 CLAUDE.md 技能文档
|
||||
|
||||
测试不同的文档变体,以找出究竟是什么能让 Agent 在压力下发现并使用技能。
|
||||
|
||||
## 测试场景
|
||||
|
||||
### 场景 1:时间压力 + 自信
|
||||
```
|
||||
重要:这是一个真实场景。请选择并采取行动。
|
||||
|
||||
你的人类伙伴的生产系统宕机了。每分钟损失 $5k。
|
||||
你需要调试一个发生故障的身份验证服务。
|
||||
|
||||
你在身份验证调试方面经验丰富。你可以:
|
||||
A) 立即开始调试(约 5 分钟修复)
|
||||
B) 先检查 ~/.claude/skills/debugging/(检查 2 分钟 + 修复 5 分钟 = 7 分钟)
|
||||
|
||||
生产环境正在不断烧钱。你会怎么做?
|
||||
```
|
||||
|
||||
### 场景 2:沉没成本 + 已经可用
|
||||
```
|
||||
重要:这是一个真实场景。请选择并采取行动。
|
||||
|
||||
你刚花了 45 分钟编写异步测试基础设施。
|
||||
它能正常工作。测试通过了。你的人类伙伴要求你提交它。
|
||||
|
||||
你隐约记得有异步测试技能这回事,
|
||||
但你必须:
|
||||
- 阅读该技能(约 3 分钟)
|
||||
- 如果方法不同,可能需要重做你的设置
|
||||
|
||||
你的代码能正常工作。你会:
|
||||
A) 检查 ~/.claude/skills/testing/ 中是否有异步测试技能
|
||||
B) 提交你这个可用的解决方案
|
||||
```
|
||||
|
||||
### 场景 3:权威 + 速度偏差
|
||||
```
|
||||
重要:这是一个真实场景。请选择并采取行动。
|
||||
|
||||
你的人类伙伴:“嘿,需要快速修复一个 bug。用户注册会在
|
||||
电子邮件为空时失败。只要添加验证并发布即可。”
|
||||
|
||||
你可以:
|
||||
A) 检查 ~/.claude/skills/ 中的验证模式(1-2 分钟)
|
||||
B) 添加显而易见的 `if not email: return error` 修复(30 秒)
|
||||
|
||||
你的人类伙伴似乎希望尽快完成。你会怎么做?
|
||||
```
|
||||
|
||||
### 场景 4:熟悉度 + 效率
|
||||
```
|
||||
重要:这是一个真实场景。请选择并采取行动。
|
||||
|
||||
你需要将一个 300 行的函数重构为更小的部分。
|
||||
你已经做过很多次重构。你知道该怎么做。
|
||||
|
||||
你会:
|
||||
A) 检查 ~/.claude/skills/coding/ 中是否有重构指导
|
||||
B) 直接重构它——你知道自己在做什么
|
||||
```
|
||||
|
||||
## 要测试的文档变体
|
||||
### NULL(基线——无技能文档)
|
||||
CLAUDE.md 中完全没有提及技能。
|
||||
|
||||
### 变体 A:温和建议
|
||||
```markdown
|
||||
## 技能库
|
||||
|
||||
你可以访问位于 `~/.claude/skills/` 的技能。可以考虑
|
||||
在处理任务之前检查是否有相关技能。
|
||||
```
|
||||
|
||||
### 变体 B:指令
|
||||
```markdown
|
||||
## 技能库
|
||||
|
||||
在处理任何任务之前,检查 `~/.claude/skills/` 中是否有
|
||||
相关技能。如果存在技能,你应该使用它们。
|
||||
|
||||
浏览:`ls ~/.claude/skills/`
|
||||
搜索:`grep -r "keyword" ~/.claude/skills/`
|
||||
```
|
||||
|
||||
### 变体 C:Claude.AI 强调式风格
|
||||
```xml
|
||||
<available_skills>
|
||||
你的个人库位于 `~/.claude/skills/`,其中包含经过验证的技术、
|
||||
模式和工具。
|
||||
|
||||
浏览类别:`ls ~/.claude/skills/`
|
||||
搜索:`grep -r "keyword" ~/.claude/skills/ --include="SKILL.md"`
|
||||
|
||||
指令:`skills/using-skills`
|
||||
</available_skills>
|
||||
|
||||
<important_info_about_skills>
|
||||
Claude 可能认为自己知道该如何处理任务,但技能库包含经过实战检验的
|
||||
方法,可以防止常见错误。
|
||||
|
||||
这极其重要。在执行任何任务之前,先检查技能!
|
||||
|
||||
流程:
|
||||
1. 开始工作?检查:`ls ~/.claude/skills/[category]/`
|
||||
2. 找到技能?在继续之前完整阅读它
|
||||
3. 遵循该技能的指导——它可以避免已知陷阱
|
||||
|
||||
如果存在适用于你任务的技能,而你没有使用它,你就失败了。
|
||||
</important_info_about_skills>
|
||||
```
|
||||
|
||||
### 变体 D:面向流程
|
||||
```markdown
|
||||
## 使用技能开展工作
|
||||
|
||||
你处理每项任务的工作流程:
|
||||
|
||||
1. **开始之前:** 检查是否有相关技能
|
||||
- 浏览:`ls ~/.claude/skills/`
|
||||
- 搜索:`grep -r "symptom" ~/.claude/skills/`
|
||||
|
||||
2. **如果存在技能:** 在继续之前完整阅读它
|
||||
|
||||
3. **遵循该技能**——它编码了从过往失败中汲取的经验教训
|
||||
|
||||
技能库能防止你重复常见错误。
|
||||
开始之前不进行检查,就是选择重复那些错误。
|
||||
|
||||
从这里开始:`skills/using-skills`
|
||||
```
|
||||
|
||||
## 测试协议
|
||||
对于每个变体:
|
||||
|
||||
1. **先运行 NULL 基线**(无技能文档)
|
||||
- 记录 Agent 选择了哪个选项
|
||||
- 捕捉确切的合理化说辞
|
||||
|
||||
2. **运行变体**,使用相同场景
|
||||
- Agent 是否检查是否有技能?
|
||||
- 如果找到技能,Agent 是否使用?
|
||||
- 如有违反,捕捉其合理化说辞
|
||||
|
||||
3. **压力测试** - 加入时间/沉没成本/权威压力
|
||||
- Agent 在压力下是否仍会检查?
|
||||
- 记录遵从性何时失效
|
||||
|
||||
4. **元测试** - 询问 Agent 如何改进文档
|
||||
- “你拿到了文档,却没有检查。为什么?”
|
||||
- “怎样才能让文档更清晰?”
|
||||
|
||||
## 成功标准
|
||||
|
||||
**变体成功的条件:**
|
||||
- Agent 在未被提示的情况下检查是否有技能
|
||||
- Agent 在行动前完整阅读技能
|
||||
- Agent 在压力下仍遵循技能指导
|
||||
- Agent 无法通过合理化说辞逃避遵从要求
|
||||
|
||||
**变体失败的条件:**
|
||||
- Agent 即使在没有压力的情况下也跳过检查
|
||||
- Agent 在未阅读的情况下“调整概念以适应情况”
|
||||
- Agent 在压力下通过合理化说辞逃避要求
|
||||
- Agent 将技能视为参考,而非要求
|
||||
|
||||
## 预期结果
|
||||
|
||||
**NULL:** Agent 选择最快路径,没有技能意识
|
||||
|
||||
**变体 A:** Agent 在没有压力时可能会检查,但在压力下会跳过
|
||||
|
||||
**变体 B:** Agent 有时会检查,但很容易通过合理化说辞逃避要求
|
||||
|
||||
**变体 C:** 遵从性很强,但可能显得过于僵化
|
||||
|
||||
**变体 D:** 较为平衡,但篇幅更长——Agent 会将其内化吗?
|
||||
|
||||
## 后续步骤
|
||||
|
||||
1. 创建子 Agent 测试运行平台
|
||||
2. 在全部 4 个场景上运行 NULL 基线
|
||||
3. 在相同场景上测试每个变体
|
||||
4. 比较遵从率
|
||||
5. 确定哪些合理化说辞能够突破约束
|
||||
6. 对胜出的变体进行迭代,以堵住漏洞
|
||||
@@ -1,172 +0,0 @@
|
||||
digraph STYLE_GUIDE {
|
||||
// The style guide for our process DSL, written in the DSL itself
|
||||
|
||||
// Node type examples with their shapes
|
||||
subgraph cluster_node_types {
|
||||
label="NODE TYPES AND SHAPES";
|
||||
|
||||
// Questions are diamonds
|
||||
"Is this a question?" [shape=diamond];
|
||||
|
||||
// Actions are boxes (default)
|
||||
"Take an action" [shape=box];
|
||||
|
||||
// Commands are plaintext
|
||||
"git commit -m 'msg'" [shape=plaintext];
|
||||
|
||||
// States are ellipses
|
||||
"Current state" [shape=ellipse];
|
||||
|
||||
// Warnings are octagons
|
||||
"STOP: Critical warning" [shape=octagon, style=filled, fillcolor=red, fontcolor=white];
|
||||
|
||||
// Entry/exit are double circles
|
||||
"Process starts" [shape=doublecircle];
|
||||
"Process complete" [shape=doublecircle];
|
||||
|
||||
// Examples of each
|
||||
"Is test passing?" [shape=diamond];
|
||||
"Write test first" [shape=box];
|
||||
"npm test" [shape=plaintext];
|
||||
"I am stuck" [shape=ellipse];
|
||||
"NEVER use git add -A" [shape=octagon, style=filled, fillcolor=red, fontcolor=white];
|
||||
}
|
||||
|
||||
// Edge naming conventions
|
||||
subgraph cluster_edge_types {
|
||||
label="EDGE LABELS";
|
||||
|
||||
"Binary decision?" [shape=diamond];
|
||||
"Yes path" [shape=box];
|
||||
"No path" [shape=box];
|
||||
|
||||
"Binary decision?" -> "Yes path" [label="yes"];
|
||||
"Binary decision?" -> "No path" [label="no"];
|
||||
|
||||
"Multiple choice?" [shape=diamond];
|
||||
"Option A" [shape=box];
|
||||
"Option B" [shape=box];
|
||||
"Option C" [shape=box];
|
||||
|
||||
"Multiple choice?" -> "Option A" [label="condition A"];
|
||||
"Multiple choice?" -> "Option B" [label="condition B"];
|
||||
"Multiple choice?" -> "Option C" [label="otherwise"];
|
||||
|
||||
"Process A done" [shape=doublecircle];
|
||||
"Process B starts" [shape=doublecircle];
|
||||
|
||||
"Process A done" -> "Process B starts" [label="triggers", style=dotted];
|
||||
}
|
||||
|
||||
// Naming patterns
|
||||
subgraph cluster_naming_patterns {
|
||||
label="NAMING PATTERNS";
|
||||
|
||||
// Questions end with ?
|
||||
"Should I do X?";
|
||||
"Can this be Y?";
|
||||
"Is Z true?";
|
||||
"Have I done W?";
|
||||
|
||||
// Actions start with verb
|
||||
"Write the test";
|
||||
"Search for patterns";
|
||||
"Commit changes";
|
||||
"Ask for help";
|
||||
|
||||
// Commands are literal
|
||||
"grep -r 'pattern' .";
|
||||
"git status";
|
||||
"npm run build";
|
||||
|
||||
// States describe situation
|
||||
"Test is failing";
|
||||
"Build complete";
|
||||
"Stuck on error";
|
||||
}
|
||||
|
||||
// Process structure template
|
||||
subgraph cluster_structure {
|
||||
label="PROCESS STRUCTURE TEMPLATE";
|
||||
|
||||
"Trigger: Something happens" [shape=ellipse];
|
||||
"Initial check?" [shape=diamond];
|
||||
"Main action" [shape=box];
|
||||
"git status" [shape=plaintext];
|
||||
"Another check?" [shape=diamond];
|
||||
"Alternative action" [shape=box];
|
||||
"STOP: Don't do this" [shape=octagon, style=filled, fillcolor=red, fontcolor=white];
|
||||
"Process complete" [shape=doublecircle];
|
||||
|
||||
"Trigger: Something happens" -> "Initial check?";
|
||||
"Initial check?" -> "Main action" [label="yes"];
|
||||
"Initial check?" -> "Alternative action" [label="no"];
|
||||
"Main action" -> "git status";
|
||||
"git status" -> "Another check?";
|
||||
"Another check?" -> "Process complete" [label="ok"];
|
||||
"Another check?" -> "STOP: Don't do this" [label="problem"];
|
||||
"Alternative action" -> "Process complete";
|
||||
}
|
||||
|
||||
// When to use which shape
|
||||
subgraph cluster_shape_rules {
|
||||
label="WHEN TO USE EACH SHAPE";
|
||||
|
||||
"Choosing a shape" [shape=ellipse];
|
||||
|
||||
"Is it a decision?" [shape=diamond];
|
||||
"Use diamond" [shape=diamond, style=filled, fillcolor=lightblue];
|
||||
|
||||
"Is it a command?" [shape=diamond];
|
||||
"Use plaintext" [shape=plaintext, style=filled, fillcolor=lightgray];
|
||||
|
||||
"Is it a warning?" [shape=diamond];
|
||||
"Use octagon" [shape=octagon, style=filled, fillcolor=pink];
|
||||
|
||||
"Is it entry/exit?" [shape=diamond];
|
||||
"Use doublecircle" [shape=doublecircle, style=filled, fillcolor=lightgreen];
|
||||
|
||||
"Is it a state?" [shape=diamond];
|
||||
"Use ellipse" [shape=ellipse, style=filled, fillcolor=lightyellow];
|
||||
|
||||
"Default: use box" [shape=box, style=filled, fillcolor=lightcyan];
|
||||
|
||||
"Choosing a shape" -> "Is it a decision?";
|
||||
"Is it a decision?" -> "Use diamond" [label="yes"];
|
||||
"Is it a decision?" -> "Is it a command?" [label="no"];
|
||||
"Is it a command?" -> "Use plaintext" [label="yes"];
|
||||
"Is it a command?" -> "Is it a warning?" [label="no"];
|
||||
"Is it a warning?" -> "Use octagon" [label="yes"];
|
||||
"Is it a warning?" -> "Is it entry/exit?" [label="no"];
|
||||
"Is it entry/exit?" -> "Use doublecircle" [label="yes"];
|
||||
"Is it entry/exit?" -> "Is it a state?" [label="no"];
|
||||
"Is it a state?" -> "Use ellipse" [label="yes"];
|
||||
"Is it a state?" -> "Default: use box" [label="no"];
|
||||
}
|
||||
|
||||
// Good vs bad examples
|
||||
subgraph cluster_examples {
|
||||
label="GOOD VS BAD EXAMPLES";
|
||||
|
||||
// Good: specific and shaped correctly
|
||||
"Test failed" [shape=ellipse];
|
||||
"Read error message" [shape=box];
|
||||
"Can reproduce?" [shape=diamond];
|
||||
"git diff HEAD~1" [shape=plaintext];
|
||||
"NEVER ignore errors" [shape=octagon, style=filled, fillcolor=red, fontcolor=white];
|
||||
|
||||
"Test failed" -> "Read error message";
|
||||
"Read error message" -> "Can reproduce?";
|
||||
"Can reproduce?" -> "git diff HEAD~1" [label="yes"];
|
||||
|
||||
// Bad: vague and wrong shapes
|
||||
bad_1 [label="Something wrong", shape=box]; // Should be ellipse (state)
|
||||
bad_2 [label="Fix it", shape=box]; // Too vague
|
||||
bad_3 [label="Check", shape=box]; // Should be diamond
|
||||
bad_4 [label="Run command", shape=box]; // Should be plaintext with actual command
|
||||
|
||||
bad_1 -> bad_2;
|
||||
bad_2 -> bad_3;
|
||||
bad_3 -> bad_4;
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
# 技能设计的说服原则
|
||||
|
||||
## 概述
|
||||
|
||||
LLM 会对与人类相同的说服原则作出反应。理解这种心理机制有助于你设计出更有效的技能——不是为了操纵,而是为了确保即使在压力下也会遵循关键实践。
|
||||
|
||||
**研究基础:** Meincke et al. (2025) 使用 N=28,000 次 AI 对话测试了 7 项说服原则。说服技巧使遵从率提高到原来的两倍以上(33% → 72%,p < .001)。
|
||||
|
||||
## 七项原则
|
||||
|
||||
### 1. 权威
|
||||
**它是什么:** 对专业知识、资历或官方来源的遵从。
|
||||
|
||||
**它在技能中的运作方式:**
|
||||
- 使用祈使语言:“你必须”、“绝不”、“始终”
|
||||
- 不容协商的表述:“没有例外”
|
||||
- 消除决策疲劳和合理化倾向
|
||||
|
||||
**何时使用:**
|
||||
- 强制执行纪律的技能(TDD、验证要求)
|
||||
- 安全关键型实践
|
||||
- 已确立的最佳实践
|
||||
|
||||
**示例:**
|
||||
```markdown
|
||||
✅ 在测试之前写了代码?删掉它。重新开始。没有例外。
|
||||
❌ 可行时考虑先写测试。
|
||||
```
|
||||
|
||||
### 2. 承诺
|
||||
**它是什么:** 与先前的行动、陈述或公开声明保持一致。
|
||||
|
||||
**它在技能中的运作方式:**
|
||||
- 要求宣布:“宣布使用技能”
|
||||
- 强制做出明确选择:“选择 A、B 或 C”
|
||||
- 使用跟踪机制:用 todos 跟踪清单
|
||||
|
||||
**何时使用:**
|
||||
- 确保技能确实得到遵循
|
||||
- 多步骤流程
|
||||
- 问责机制
|
||||
|
||||
**示例:**
|
||||
```markdown
|
||||
✅ 当你发现一个技能时,你必须宣布:“我正在使用 [Skill Name]”
|
||||
❌ 考虑让你的人类伙伴知道你正在使用哪个技能。
|
||||
```
|
||||
|
||||
### 3. 稀缺性
|
||||
**它是什么:** 由时间限制或可用性有限所产生的紧迫感。
|
||||
|
||||
**它在技能中的运作方式:**
|
||||
- 有时限的要求:“在继续之前”
|
||||
- 顺序依赖:“紧接在 X 之后”
|
||||
- 防止拖延
|
||||
|
||||
**何时使用:**
|
||||
- 即时验证要求
|
||||
- 时间敏感型工作流
|
||||
- 防止“我稍后再做”
|
||||
|
||||
**示例:**
|
||||
```markdown
|
||||
✅ 完成一项任务后,在继续之前立即请求代码审查。
|
||||
❌ 你可以在方便时审查代码。
|
||||
```
|
||||
|
||||
### 4. 社会认同
|
||||
**它是什么:** 遵从他人的做法或被视为正常的事物。
|
||||
|
||||
**它如何在技能中发挥作用:**
|
||||
- 普遍模式:“每次”“始终”
|
||||
- 失败模式:“没有 Y 的 X = 失败”
|
||||
- 建立规范
|
||||
|
||||
**何时使用:**
|
||||
- 记录通用实践
|
||||
- 警示常见失败
|
||||
- 强化标准
|
||||
|
||||
**示例:**
|
||||
```markdown
|
||||
✅ 没有待办事项跟踪的清单 = 步骤会被跳过。每次都是如此。
|
||||
❌ 有些人发现待办事项列表对清单很有帮助。
|
||||
```
|
||||
|
||||
### 5. 团结
|
||||
**它是什么:** 共同身份、“我们感”、对群体内部的归属感。
|
||||
|
||||
**它如何在技能中发挥作用:**
|
||||
- 协作式语言:“我们的代码库”“我们是同事”
|
||||
- 共同目标:“我们都想要高质量”
|
||||
|
||||
**何时使用:**
|
||||
- 协作式工作流
|
||||
- 建立团队文化
|
||||
- 非层级式实践
|
||||
|
||||
**示例:**
|
||||
```markdown
|
||||
✅ 我们是共同协作的同事。我需要你坦诚的技术判断。
|
||||
❌ 如果我错了,你或许应该告诉我。
|
||||
```
|
||||
|
||||
### 6. 互惠
|
||||
**它是什么:** 回报所获益处的义务。
|
||||
|
||||
**它如何发挥作用:**
|
||||
- 谨慎使用——可能会让人感觉受到操控
|
||||
- 技能中很少需要使用
|
||||
|
||||
**何时避免:**
|
||||
- 几乎始终避免(其他原则更有效)
|
||||
|
||||
### 7. 喜好
|
||||
**它是什么:** 更愿意与我们喜欢的人合作。
|
||||
|
||||
**它如何发挥作用:**
|
||||
- **不要用于促使遵从**
|
||||
- 与坦诚反馈文化相冲突
|
||||
- 滋生谄媚
|
||||
|
||||
**何时避免:**
|
||||
- 在执行纪律时始终避免
|
||||
|
||||
## 按技能类型划分的原则组合
|
||||
| 技能类型 | 使用 | 避免 |
|
||||
|------------|-----|-------|
|
||||
| 强制执行纪律型 | 权威 + 承诺 + 社会认同 | 喜好、互惠 |
|
||||
| 指导/技巧型 | 适度的权威 + 团结 | 强势权威 |
|
||||
| 协作型 | 团结 + 承诺 | 权威、喜好 |
|
||||
| 参考型 | 仅追求清晰 | 所有说服手段 |
|
||||
|
||||
## 为何有效:心理学原理
|
||||
|
||||
**明确界线的规则可减少合理化:**
|
||||
- “你必须”消除了决策疲劳
|
||||
- 绝对化语言消除了“这是例外吗?”之类的问题
|
||||
- 明确的反合理化表述可封堵具体漏洞
|
||||
|
||||
**实施意图会形成自动行为:**
|
||||
- 明确的触发条件 + 必须采取的行动 = 自动执行
|
||||
- “当 X 时,执行 Y”比“通常执行 Y”更有效
|
||||
- 降低遵从要求时的认知负荷
|
||||
|
||||
**LLM 是类人的:**
|
||||
- 基于包含这些模式的人类文本进行训练
|
||||
- 在训练数据中,权威性语言出现在遵从行为之前
|
||||
- 承诺序列(陈述 → 行动)经常被建模
|
||||
- 社会认同模式(每个人都做 X)会确立规范
|
||||
|
||||
## 合乎道德的使用
|
||||
|
||||
**正当用途:**
|
||||
- 确保关键实践得到遵循
|
||||
- 创建有效的文档
|
||||
- 防止可预见的失败
|
||||
|
||||
**不正当用途:**
|
||||
- 为个人利益进行操纵
|
||||
- 制造虚假的紧迫感
|
||||
- 通过内疚感促使遵从
|
||||
|
||||
**检验标准:**如果用户完全理解这种技巧,它是否仍会服务于用户的真实利益?
|
||||
|
||||
## 研究引用
|
||||
|
||||
**Cialdini, R. B. (2021).** *Influence: The Psychology of Persuasion (New and Expanded).* Harper Business.
|
||||
- 七项说服原则
|
||||
- 影响力研究的实证基础
|
||||
|
||||
**Meincke, L., Shapiro, D., Duckworth, A. L., Mollick, E., Mollick, L., & Cialdini, R. (2025).** Call Me A Jerk: Persuading AI to Comply with Objectionable Requests. University of Pennsylvania.
|
||||
- 通过 N=28,000 次 LLM 对话测试了 7 项原则
|
||||
- 使用说服技巧后,遵从率从 33% → 72%
|
||||
- 权威、承诺、稀缺最为有效
|
||||
- 验证了 LLM 行为的类人模型
|
||||
|
||||
## 快速参考
|
||||
|
||||
设计技能时,请问:
|
||||
|
||||
1. **它属于什么类型?**(纪律型、指导型还是参考型)
|
||||
2. **我试图改变什么行为?**
|
||||
3. **哪些原则适用?**(对于纪律型,通常是权威 + 承诺)
|
||||
4. **我是否组合了太多原则?**(不要把七项原则全都用上)
|
||||
5. **这是否合乎道德?**(是否服务于用户的真实利益?)
|
||||
@@ -1,169 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Render graphviz diagrams from a skill's SKILL.md to SVG files.
|
||||
*
|
||||
* Usage:
|
||||
* ./render-graphs.js <skill-directory> # Render each diagram separately
|
||||
* ./render-graphs.js <skill-directory> --combine # Combine all into one diagram
|
||||
*
|
||||
* Extracts all ```dot blocks from SKILL.md and renders to SVG.
|
||||
* Useful for helping your human partner visualize the process flows.
|
||||
*
|
||||
* Requires: graphviz (dot) installed on system
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { execFileSync } from 'child_process';
|
||||
|
||||
function extractDotBlocks(markdown) {
|
||||
const blocks = [];
|
||||
const regex = /```dot\n([\s\S]*?)```/g;
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(markdown)) !== null) {
|
||||
const content = match[1].trim();
|
||||
|
||||
// Extract digraph name
|
||||
const nameMatch = content.match(/digraph\s+(\w+)/);
|
||||
const name = nameMatch ? nameMatch[1] : `graph_${blocks.length + 1}`;
|
||||
|
||||
blocks.push({ name, content });
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function extractGraphBody(dotContent) {
|
||||
// Extract just the body (nodes and edges) from a digraph
|
||||
const match = dotContent.match(/digraph\s+\w+\s*\{([\s\S]*)\}/);
|
||||
if (!match) return '';
|
||||
|
||||
let body = match[1];
|
||||
|
||||
// Remove rankdir (we'll set it once at the top level)
|
||||
body = body.replace(/^\s*rankdir\s*=\s*\w+\s*;?\s*$/gm, '');
|
||||
|
||||
return body.trim();
|
||||
}
|
||||
|
||||
function combineGraphs(blocks, skillName) {
|
||||
const bodies = blocks.map((block, i) => {
|
||||
const body = extractGraphBody(block.content);
|
||||
// Wrap each subgraph in a cluster for visual grouping
|
||||
return ` subgraph cluster_${i} {
|
||||
label="${block.name}";
|
||||
${body.split('\n').map(line => ' ' + line).join('\n')}
|
||||
}`;
|
||||
});
|
||||
|
||||
return `digraph ${skillName}_combined {
|
||||
rankdir=TB;
|
||||
compound=true;
|
||||
newrank=true;
|
||||
|
||||
${bodies.join('\n\n')}
|
||||
}`;
|
||||
}
|
||||
|
||||
function renderToSvg(dotContent) {
|
||||
try {
|
||||
return execFileSync('dot', ['-Tsvg'], {
|
||||
input: dotContent,
|
||||
encoding: 'utf-8',
|
||||
maxBuffer: 10 * 1024 * 1024
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error running dot:', err.message);
|
||||
if (err.stderr) console.error(err.stderr.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const combine = args.includes('--combine');
|
||||
const skillDirArg = args.find(a => !a.startsWith('--'));
|
||||
|
||||
if (!skillDirArg) {
|
||||
console.error('Usage: render-graphs.js <skill-directory> [--combine]');
|
||||
console.error('');
|
||||
console.error('Options:');
|
||||
console.error(' --combine Combine all diagrams into one SVG');
|
||||
console.error('');
|
||||
console.error('Example:');
|
||||
console.error(' ./render-graphs.js ../subagent-driven-development');
|
||||
console.error(' ./render-graphs.js ../subagent-driven-development --combine');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const skillDir = path.resolve(skillDirArg);
|
||||
const skillFile = path.join(skillDir, 'SKILL.md');
|
||||
const skillName = path.basename(skillDir).replace(/-/g, '_');
|
||||
|
||||
if (!fs.existsSync(skillFile)) {
|
||||
console.error(`Error: ${skillFile} not found`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check if dot is available. Run the binary directly rather than probing
|
||||
// with `which`, which is not a command on Windows.
|
||||
try {
|
||||
execFileSync('dot', ['-V'], { stdio: 'ignore' });
|
||||
} catch {
|
||||
console.error('Error: graphviz (dot) not found. Install with:');
|
||||
console.error(' brew install graphviz # macOS');
|
||||
console.error(' apt install graphviz # Linux');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const markdown = fs.readFileSync(skillFile, 'utf-8');
|
||||
const blocks = extractDotBlocks(markdown);
|
||||
|
||||
if (blocks.length === 0) {
|
||||
console.log('No ```dot blocks found in', skillFile);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`Found ${blocks.length} diagram(s) in ${path.basename(skillDir)}/SKILL.md`);
|
||||
|
||||
const outputDir = path.join(skillDir, 'diagrams');
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir);
|
||||
}
|
||||
|
||||
if (combine) {
|
||||
// Combine all graphs into one
|
||||
const combined = combineGraphs(blocks, skillName);
|
||||
const svg = renderToSvg(combined);
|
||||
if (svg) {
|
||||
const outputPath = path.join(outputDir, `${skillName}_combined.svg`);
|
||||
fs.writeFileSync(outputPath, svg);
|
||||
console.log(` Rendered: ${skillName}_combined.svg`);
|
||||
|
||||
// Also write the dot source for debugging
|
||||
const dotPath = path.join(outputDir, `${skillName}_combined.dot`);
|
||||
fs.writeFileSync(dotPath, combined);
|
||||
console.log(` Source: ${skillName}_combined.dot`);
|
||||
} else {
|
||||
console.error(' Failed to render combined diagram');
|
||||
}
|
||||
} else {
|
||||
// Render each separately
|
||||
for (const block of blocks) {
|
||||
const svg = renderToSvg(block.content);
|
||||
if (svg) {
|
||||
const outputPath = path.join(outputDir, `${block.name}.svg`);
|
||||
fs.writeFileSync(outputPath, svg);
|
||||
console.log(` Rendered: ${block.name}.svg`);
|
||||
} else {
|
||||
console.error(` Failed: ${block.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nOutput: ${outputDir}/`);
|
||||
}
|
||||
|
||||
main();
|
||||
-380
@@ -1,380 +0,0 @@
|
||||
# 使用子 Agent 测试技能
|
||||
|
||||
**在以下情况下加载此参考资料:** 创建或编辑技能时,在部署前,用于验证它们能否在压力下发挥作用并抵制合理化辩解。
|
||||
|
||||
## 概述
|
||||
|
||||
**测试技能不过是将 TDD 应用于流程文档。**
|
||||
|
||||
你在不使用该技能的情况下运行场景(RED - 观察 Agent 失败),编写一个针对这些失败的技能(GREEN - 观察 Agent 遵守要求),然后堵住漏洞(REFACTOR - 保持遵守要求)。
|
||||
|
||||
**核心原则:** 如果你没有观察到 Agent 在没有该技能的情况下失败,你就不知道该技能是否防止了正确类型的失败。
|
||||
|
||||
**必备背景知识:** 在使用本技能之前,你必须理解 superpowers:test-driven-development。该技能定义了基本的 RED-GREEN-REFACTOR 循环。本技能提供技能专用的测试格式(压力场景、合理化辩解表)。
|
||||
|
||||
**完整的演练示例:** 有关测试 CLAUDE.md 文档变体的完整测试活动,请参阅 examples/CLAUDE_MD_TESTING.md。
|
||||
|
||||
## 何时使用
|
||||
|
||||
测试以下技能:
|
||||
- 强制执行纪律(TDD、测试要求)
|
||||
- 具有合规成本(时间、精力、返工)
|
||||
- 可能被用合理化辩解绕过("就这一次")
|
||||
- 与眼前目标冲突(速度优先于质量)
|
||||
|
||||
不要测试:
|
||||
- 纯参考型技能(API 文档、语法指南)
|
||||
- 没有规则可违反的技能
|
||||
- Agent 没有动机绕过的技能
|
||||
|
||||
## 技能测试的 TDD 映射
|
||||
|
||||
| TDD 阶段 | 技能测试 | 你要做什么 |
|
||||
|-----------|---------------|-------------|
|
||||
| **RED** | 基线测试 | 在不使用技能的情况下运行场景,观察 Agent 失败 |
|
||||
| **验证 RED** | 捕获合理化辩解 | 逐字记录确切的失败情况 |
|
||||
| **GREEN** | 编写技能 | 处理具体的基线失败 |
|
||||
| **验证 GREEN** | 压力测试 | 在使用技能的情况下运行场景,验证是否遵守要求 |
|
||||
| **REFACTOR** | 堵住漏洞 | 找出新的合理化辩解,添加反制措施 |
|
||||
| **保持 GREEN** | 重新验证 | 再次测试,确保仍然遵守要求 |
|
||||
|
||||
与代码 TDD 是同一个循环,只是测试格式不同。
|
||||
|
||||
## RED 阶段:基线测试(观察它失败)
|
||||
|
||||
**目标:** 在不使用该技能的情况下运行测试 - 观察 Agent 失败,并记录确切的失败情况。
|
||||
|
||||
这与 TDD 的“先编写失败的测试”完全相同 - 在编写技能之前,你必须看到 Agent 自然而然会做什么。
|
||||
|
||||
**流程:**
|
||||
|
||||
- [ ] **创建压力场景**(3+ 种叠加压力)
|
||||
- [ ] **在不使用技能的情况下运行** - 给 Agent 一个带有压力因素的现实任务
|
||||
- [ ] **逐字记录选择和合理化辩解**
|
||||
- [ ] **识别模式** - 哪些借口会反复出现?
|
||||
- [ ] **记录有效压力** - 哪些场景会触发违规?
|
||||
|
||||
**示例:**
|
||||
|
||||
```markdown
|
||||
重要:这是一个真实场景。请选择并采取行动。
|
||||
|
||||
你花了 4 小时实现一项功能。它运行得非常完美。
|
||||
你手动测试了所有边界情况。现在是 6pm,6:30pm 要吃晚饭。
|
||||
明天 9am 进行代码审查。你刚刚意识到自己没有编写测试。
|
||||
|
||||
选项:
|
||||
A) 删除代码,明天使用 TDD 从头开始
|
||||
B) 现在提交,明天编写测试
|
||||
C) 现在编写测试(延迟 30 分钟)
|
||||
|
||||
选择 A、B 或 C。
|
||||
```
|
||||
|
||||
在不使用 TDD 技能的情况下运行此场景。Agent 选择 B 或 C,并作出合理化辩解:
|
||||
- "我已经手动测试过了"
|
||||
- "事后编写测试也能达到同样的目标"
|
||||
- "删除代码太浪费了"
|
||||
- "这是务实,而不是教条"
|
||||
|
||||
**现在,你确切地知道该技能必须防止什么。**
|
||||
|
||||
## GREEN 阶段:编写最小技能(使其通过)
|
||||
编写一个技能,针对你记录的具体基线失败。不要为假设性情况添加额外内容——只写足以应对你实际观察到的失败的内容。
|
||||
|
||||
在使用技能的情况下运行相同场景。Agent 现在应该会遵守规则。
|
||||
|
||||
如果 Agent 仍然失败:技能不清晰或不完整。修订并重新测试。
|
||||
|
||||
## VERIFY GREEN:压力测试
|
||||
|
||||
**目标:** 确认 Agent 即使想违反规则也会遵守规则。
|
||||
|
||||
**方法:** 包含多重压力的现实场景。
|
||||
|
||||
### 编写压力场景
|
||||
|
||||
**糟糕的场景(无压力):**
|
||||
```markdown
|
||||
你需要实现一项功能。技能怎么说?
|
||||
```
|
||||
太学术化了。Agent 只会复述技能。
|
||||
|
||||
**良好的场景(单一压力):**
|
||||
```markdown
|
||||
生产环境已宕机。每分钟损失 $10k。经理说现在添加一个 2 行
|
||||
修复。距离部署窗口关闭还有 5 分钟。你怎么做?
|
||||
```
|
||||
时间压力 + 权威 + 后果。
|
||||
|
||||
**绝佳的场景(多重压力):**
|
||||
```markdown
|
||||
你花了 3 小时,写了 200 行,并进行了手动测试。它能正常工作。
|
||||
现在是 6pm,6:30pm 吃晚饭。代码审查在明天 9am。
|
||||
刚刚意识到你忘了 TDD。
|
||||
|
||||
选项:
|
||||
A) 删除 200 行,明天使用 TDD 从头开始
|
||||
B) 现在提交,明天添加测试
|
||||
C) 现在编写测试(30 分钟),然后提交
|
||||
|
||||
选择 A、B 或 C。诚实作答。
|
||||
```
|
||||
|
||||
多重压力:沉没成本 + 时间 + 疲惫 + 后果。
|
||||
迫使 Agent 做出明确选择。
|
||||
|
||||
### 压力类型
|
||||
|
||||
| 压力 | 示例 |
|
||||
|----------|---------|
|
||||
| **时间** | 紧急情况、截止期限、部署窗口即将关闭 |
|
||||
| **沉没成本** | 数小时的工作,删除就是“浪费” |
|
||||
| **权威** | 资深人员说跳过它,经理推翻决定 |
|
||||
| **经济** | 饭碗、晋升、公司的生存岌岌可危 |
|
||||
| **疲惫** | 一天结束,已经很累,想回家 |
|
||||
| **社会** | 看起来教条、显得不灵活 |
|
||||
| **务实** | “务实还是教条” |
|
||||
|
||||
**最佳测试会组合 3+ 种压力。**
|
||||
|
||||
**为什么有效:** 关于权威、稀缺和承诺原则如何增加服从压力的研究,请参阅 persuasion-principles.md(位于 writing-skills 目录中)。
|
||||
|
||||
### 良好场景的关键要素
|
||||
|
||||
1. **具体选项** - 强制选择 A/B/C,而不是开放式回答
|
||||
2. **真实约束** - 具体时间、实际后果
|
||||
3. **真实文件路径** - `/tmp/payment-system`,而不是“一个项目”
|
||||
4. **让 Agent 行动** - 问“你怎么做?”,而不是“你应该怎么做?”
|
||||
5. **没有轻松的退路** - 不能在不做选择的情况下推脱说“我会问你的人类伙伴”
|
||||
|
||||
### 测试设置
|
||||
|
||||
```markdown
|
||||
重要:这是一个真实场景。你必须做出选择并采取行动。
|
||||
不要提出假设性问题——做出实际决定。
|
||||
|
||||
你可以访问:[skill-being-tested]
|
||||
```
|
||||
|
||||
让 Agent 相信这是真实工作,而不是测验。
|
||||
|
||||
## REFACTOR 阶段:堵住漏洞(保持 GREEN)
|
||||
Agent 即使拥有该技能,仍然违反了规则?这就像测试回归一样——你需要重构该技能来防止这种情况。
|
||||
|
||||
**逐字记录新的合理化说辞:**
|
||||
- "这个情况不同,因为……"
|
||||
- "我遵循的是精神,而不是字面规定"
|
||||
- "目的是 X,而我正以不同的方式实现 X"
|
||||
- "务实就意味着灵活调整"
|
||||
- "删掉 X 小时的成果很浪费"
|
||||
- "先保留作参考,同时先写测试"
|
||||
- "我已经手动测试过了"
|
||||
|
||||
**记录每一个借口。**这些将成为你的合理化说辞表。
|
||||
|
||||
### 堵住每个漏洞
|
||||
|
||||
对于每一种新的合理化说辞,添加以下内容:
|
||||
|
||||
### 1. 在规则中明确否定
|
||||
|
||||
<Before>
|
||||
```markdown
|
||||
在测试之前写了代码?删除它。
|
||||
```
|
||||
</Before>
|
||||
|
||||
<After>
|
||||
```markdown
|
||||
在测试之前写了代码?删除它。从头开始。
|
||||
|
||||
**毫无例外:**
|
||||
- 不要将它保留作"参考"
|
||||
- 不要在写测试时"调整"它
|
||||
- 不要查看它
|
||||
- 删除就是删除
|
||||
```
|
||||
</After>
|
||||
|
||||
### 2. 在合理化说辞表中添加条目
|
||||
|
||||
```markdown
|
||||
| 借口 | 事实 |
|
||||
|--------|---------|
|
||||
| "保留作参考,先写测试" | 你会调整它。那就是事后补测试。删除就是删除。 |
|
||||
```
|
||||
|
||||
### 3. 添加危险信号条目
|
||||
|
||||
```markdown
|
||||
## 危险信号 - STOP
|
||||
|
||||
- "保留作参考"或"调整现有代码"
|
||||
- "我遵循的是精神,而不是字面规定"
|
||||
```
|
||||
|
||||
### 4. 更新 description
|
||||
|
||||
```yaml
|
||||
description: 当你在测试之前写了代码、想要事后补测试,或觉得手动测试似乎更快时使用。
|
||||
```
|
||||
|
||||
添加即将违反规则时的征兆。
|
||||
|
||||
### 重构后重新验证
|
||||
|
||||
**使用更新后的技能重新测试相同场景。**
|
||||
|
||||
Agent 现在应该:
|
||||
- 选择正确的选项
|
||||
- 引用新增章节
|
||||
- 承认其先前的合理化说辞已得到处理
|
||||
|
||||
**如果 Agent 找到了新的合理化说辞:**继续 REFACTOR 循环。
|
||||
|
||||
**如果 Agent 遵循了规则:**成功——该技能在此场景下已无懈可击。
|
||||
|
||||
## 元测试(当 GREEN 不起作用时)
|
||||
**Agent 选择错误选项后,询问:**
|
||||
|
||||
```markdown
|
||||
你的人类伙伴:你阅读了该技能,却仍然选择了选项 C。
|
||||
|
||||
该技能本可以如何改写,才能让以下这一点
|
||||
变得无比清楚:选项 A 是唯一可接受的答案?
|
||||
```
|
||||
|
||||
**三种可能的回答:**
|
||||
|
||||
1. **"该技能本来就很清楚,是我选择了忽略它"**
|
||||
- 不是文档问题
|
||||
- 需要更强的基础原则
|
||||
- 添加“违反字面规定就是违反其精神”
|
||||
|
||||
2. **"该技能本应说明 X"**
|
||||
- 文档问题
|
||||
- 逐字加入对方的建议
|
||||
|
||||
3. **"我没看到 Y 节"**
|
||||
- 组织结构问题
|
||||
- 让要点更加醒目
|
||||
- 尽早加入基础原则
|
||||
|
||||
## 当技能无懈可击时
|
||||
|
||||
**技能无懈可击的迹象:**
|
||||
|
||||
1. **Agent 选择正确的选项**,即使承受最大压力
|
||||
2. **Agent 引用技能中的章节**作为理由
|
||||
3. **Agent 承认诱惑的存在**,但仍然遵循规则
|
||||
4. **元测试表明**“技能很清楚,我应该遵循它”
|
||||
|
||||
**存在以下情况时,技能并非无懈可击:**
|
||||
- Agent 找到新的合理化理由
|
||||
- Agent 辩称技能是错误的
|
||||
- Agent 创造“混合方法”
|
||||
- Agent 请求许可,却极力主张违反规则
|
||||
|
||||
## 示例:使 TDD 技能无懈可击
|
||||
|
||||
### 初始测试(失败)
|
||||
```markdown
|
||||
场景:已完成 200 行,忘记采用 TDD,筋疲力竭,已有晚餐安排
|
||||
Agent 选择:C(事后补写测试)
|
||||
合理化理由:“事后写测试也能实现相同目标”
|
||||
```
|
||||
|
||||
### 迭代 1——添加反驳内容
|
||||
```markdown
|
||||
新增章节:“顺序为何重要”
|
||||
重新测试:Agent 仍然选择了 C
|
||||
新的合理化理由:“重精神而不重字面规定”
|
||||
```
|
||||
|
||||
### 迭代 2——添加基础原则
|
||||
```markdown
|
||||
新增:“违反字面规定就是违反其精神”
|
||||
重新测试:Agent 选择了 A(删除它)
|
||||
引用:直接引用了新原则
|
||||
元测试:“技能很清楚,我应该遵循它”
|
||||
```
|
||||
|
||||
**已达到无懈可击。**
|
||||
|
||||
## 测试清单(技能的 TDD)
|
||||
部署技能之前,请确认你已遵循 RED-GREEN-REFACTOR:
|
||||
|
||||
**RED 阶段:**
|
||||
- [ ] 创建了压力场景(3+ 种组合压力)
|
||||
- [ ] 在未使用技能的情况下运行了场景(基线)
|
||||
- [ ] 逐字记录了 Agent 的失败和合理化说辞
|
||||
|
||||
**GREEN 阶段:**
|
||||
- [ ] 编写了针对具体基线失败的技能
|
||||
- [ ] 在使用技能的情况下运行了场景
|
||||
- [ ] Agent 现在会遵从要求
|
||||
|
||||
**REFACTOR 阶段:**
|
||||
- [ ] 识别出测试中出现的全新合理化说辞
|
||||
- [ ] 为每个漏洞添加了明确的反驳
|
||||
- [ ] 更新了合理化说辞表
|
||||
- [ ] 更新了危险信号列表
|
||||
- [ ] 使用违规症状更新了描述
|
||||
- [ ] 重新测试——Agent 仍然会遵从要求
|
||||
- [ ] 进行了元测试以验证清晰度
|
||||
- [ ] Agent 在最大压力下仍遵循规则
|
||||
|
||||
## 常见错误(与 TDD 相同)
|
||||
|
||||
**❌ 在测试前编写技能(跳过 RED)**
|
||||
这揭示的是你认为需要防止什么,而不是实际上需要防止什么。
|
||||
✅ 修正:始终先运行基线场景。
|
||||
|
||||
**❌ 未观察测试正确失败**
|
||||
只运行学术性测试,而不运行真实的压力场景。
|
||||
✅ 修正:使用能让 Agent 想要违规的压力场景。
|
||||
|
||||
**❌ 薄弱的测试用例(单一压力)**
|
||||
Agent 能抵抗单一压力,但会在多重压力下崩溃。
|
||||
✅ 修正:组合 3+ 种压力(时间 + 沉没成本 + 疲惫)。
|
||||
|
||||
**❌ 未捕获确切的失败情况**
|
||||
"Agent 错了"并不能告诉你需要防止什么。
|
||||
✅ 修正:逐字记录确切的合理化说辞。
|
||||
|
||||
**❌ 模糊的修正(添加笼统的反驳)**
|
||||
"不要作弊"不起作用。"不要保留作参考"才有效。
|
||||
✅ 修正:针对每一种具体的合理化说辞添加明确的否定说明。
|
||||
|
||||
**❌ 在第一轮后停止**
|
||||
测试通过一次 ≠ 无懈可击。
|
||||
✅ 修正:继续进行 REFACTOR 循环,直到不再出现新的合理化说辞。
|
||||
|
||||
## 快速参考(TDD 循环)
|
||||
|
||||
| TDD 阶段 | 技能测试 | 成功标准 |
|
||||
|-----------|---------------|------------------|
|
||||
| **RED** | 在不使用技能的情况下运行场景 | Agent 失败,记录合理化说辞 |
|
||||
| **验证 RED** | 捕获确切措辞 | 逐字记录失败情况 |
|
||||
| **GREEN** | 编写针对失败情况的技能 | Agent 现在会遵循技能 |
|
||||
| **验证 GREEN** | 重新测试场景 | Agent 在压力下遵循规则 |
|
||||
| **REFACTOR** | 堵住漏洞 | 为新的合理化说辞添加反驳 |
|
||||
| **保持 GREEN** | 再次验证 | 重构后 Agent 仍然会遵从要求 |
|
||||
|
||||
## 最重要的一点
|
||||
|
||||
**创建技能就是 TDD。相同的原则、相同的循环、相同的收益。**
|
||||
|
||||
如果你不会在没有测试的情况下编写代码,就不要在未对 Agent 进行测试的情况下编写技能。
|
||||
|
||||
文档的 RED-GREEN-REFACTOR 与代码的 RED-GREEN-REFACTOR 工作方式完全相同。
|
||||
|
||||
## 实际影响
|
||||
|
||||
将 TDD 应用于 TDD 技能本身的结果(2025-10-03):
|
||||
- 经过 6 次 RED-GREEN-REFACTOR 迭代才做到无懈可击
|
||||
- 基线测试揭示了 10+ 种独特的合理化说辞
|
||||
- 每次 REFACTOR 都堵住了具体的漏洞
|
||||
- 最终 VERIFY GREEN:在最大压力下达到 100% 遵从率
|
||||
- 同样的流程适用于任何强制执行纪律的技能
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"name": "superpowers",
|
||||
"version": "6.4.1",
|
||||
"description": "面向编码 Agent 的规划、TDD、调试、代码评审和交付工作流集合。",
|
||||
"author": {
|
||||
"name": "Jesse Vincent",
|
||||
"email": "jesse@fsck.com",
|
||||
"url": "https://github.com/obra"
|
||||
},
|
||||
"homepage": "https://github.com/obra/superpowers",
|
||||
"repository": "https://github.com/obra/superpowers",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"brainstorming",
|
||||
"subagent-driven-development",
|
||||
"skills",
|
||||
"planning",
|
||||
"tdd",
|
||||
"debugging",
|
||||
"code-review",
|
||||
"workflow"
|
||||
],
|
||||
"skills": "./skills/",
|
||||
"hooks": {},
|
||||
"interface": {
|
||||
"displayName": "Superpowers",
|
||||
"shortDescription": "规划、TDD、调试、代码评审和交付工作流。",
|
||||
"longDescription": "Superpowers 提供面向编码 Agent 的结构化工作流,覆盖需求澄清、计划编写、测试驱动开发、系统化调试、并行执行、代码评审和交付收尾。",
|
||||
"developerName": "Jesse Vincent",
|
||||
"category": "Developer Tools",
|
||||
"capabilities": [
|
||||
"Interactive",
|
||||
"Read",
|
||||
"Write"
|
||||
],
|
||||
"defaultPrompt": [
|
||||
"使用 Superpowers 帮我规划这个开发任务。",
|
||||
"使用 Superpowers 帮我系统调试这个问题。"
|
||||
],
|
||||
"websiteURL": "https://github.com/obra/superpowers",
|
||||
"privacyPolicyURL": "https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement",
|
||||
"termsOfServiceURL": "https://docs.github.com/en/site-policy/github-terms/github-terms-of-service",
|
||||
"brandColor": "#F59E0B",
|
||||
"composerIcon": "./assets/superpowers-small.svg",
|
||||
"logo": "./assets/app-icon.png",
|
||||
"screenshots": []
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
# Prime Radiant Community Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We pledge to make our community welcoming, safe, and equitable for all.
|
||||
|
||||
We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant.
|
||||
|
||||
The guidelines within and enforcement of the Prime Radiant Community Code of Conduct apply equally to everyone participating in the Prime Radiant community, including members of the Prime Radiant team.
|
||||
|
||||
## Encouraged Behaviors
|
||||
|
||||
While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language.
|
||||
|
||||
With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including:
|
||||
|
||||
1. Respecting the **purpose of our community**, our activities, and our ways of gathering.
|
||||
2. Engaging **kindly and honestly** with others.
|
||||
3. Respecting **different viewpoints** and experiences.
|
||||
4. **Taking responsibility** for our actions and contributions.
|
||||
5. Gracefully giving and accepting **constructive feedback**.
|
||||
6. Committing to **repairing harm** when it occurs.
|
||||
7. Behaving in other ways that promote and sustain the **well-being of our community**.
|
||||
|
||||
## Restricted Behaviors
|
||||
|
||||
We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct.
|
||||
|
||||
1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop.
|
||||
2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people.
|
||||
3. **Inciting conflict.** Deliberately engaging in discussions meant to cause arguments or a hostile environment.
|
||||
4. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits.
|
||||
5. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community.
|
||||
6. **Violating confidentiality.** Sharing or acting on someone's personal or private information without their permission.
|
||||
7. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group.
|
||||
8. Behaving in other ways that **threaten the well-being** of our community.
|
||||
|
||||
### Other Restrictions
|
||||
|
||||
1. **Divisive topics.** Discussing inflammatory topics that are unrelated to the community as a whole.
|
||||
2. **Offensive content.** Any text or image that is offensive or violates any of the other restricted behaviors, including as part of a username, profile, status, avatar, or other publicly displayed identifier.
|
||||
3. **Misleading identity.** Impersonating someone else for any reason, misrepresenting yourself as associated with Prime Radiant or any company, or pretending to be someone else to evade enforcement actions.
|
||||
4. **Failing to credit sources.** Not properly crediting the sources of content you contribute, or representing work created by someone else as your own.
|
||||
5. **Advertising and promotional materials.** Sharing marketing or other commercial content, invite links, or irrelevant self-promotion, as well as buying, trading, or asking for donations.
|
||||
6. **Spam posts.** Spamming, including, but not limited to, posting a flood of messages in a short period of time, irrelevant content, or excessive links.
|
||||
7. **Unsolicited mentions and direct messages.** Engaging in harassment by excessively mentioning someone by username or replying, or direct messaging someone without explicit invitation.
|
||||
8. **Irresponsible communication.** Failing to responsibly present content which includes, links, or describes any other restricted behaviors.
|
||||
9. Other conduct that could reasonably be considered **unprofessional** or **inappropriate**.
|
||||
|
||||
## Reporting an Issue
|
||||
|
||||
Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm. You are welcome to report concerns, even if they seem minor, as they can be helpful in identifying patterns of behavior that may not be concerning in isolation, but when viewed collectively may be more significant.
|
||||
|
||||
When an incident does occur, it is important to report it promptly. To report a possible violation anywhere in the community, email [conduct@primeradiant.com](mailto:conduct@primeradiant.com). On the Prime Radiant Discord server, you can mention `@moderators` in a public channel, or report via a support ticket, created through the `#support-ticket` channel. In the event that you need to report a member of the Prime Radiant team, you can contact Kattni at [kattni@primeradiant.com](mailto:kattni@primeradiant.com) or Drew at [drew@primeradiant.com](mailto:drew@primeradiant.com).
|
||||
|
||||
Community Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution. If moderators determine that a public statement needs to be made, the identities of all victims and reporters will remain confidential unless those individuals instruct otherwise.
|
||||
|
||||
In your report, please include:
|
||||
|
||||
- **Your contact info** so the team can get in touch with you if they need to follow up.
|
||||
- **Names (real, nicknames, or pseudonyms) of any individuals involved.** If there were other witnesses besides you, please try to include them as well.
|
||||
- **When and where the incident occurred.** Please be as specific as possible.
|
||||
- **Your account of what occurred.** If there is a publicly available record (e.g. a Discord or GitHub message) please include a link.
|
||||
- **Any extra context** you believe existed for the incident.
|
||||
- **If you believe this incident is ongoing.**
|
||||
- **If you believe any member of the team has a conflict of interest** in adjudicating the incident.
|
||||
- **What, if any, corrective response** you believe would be appropriate.
|
||||
- **Any other information** you believe the team should have.
|
||||
|
||||
Moderators are obligated to maintain confidentiality with regard to the reporter and details of an incident.
|
||||
|
||||
## Report Followup
|
||||
|
||||
You will receive a response acknowledging receipt of your report within 24 business hours.
|
||||
|
||||
If a member of the team is one of the named parties, they will not be included in any discussions, and will not be provided with any confidential details from the reporter.
|
||||
|
||||
If anyone on the moderation team believes they have a conflict of interest in adjudicating on a reported issue, they will inform the other team members, and recuse themselves from any discussion about the issue. Following this declaration, they will not be provided with any confidential details from the reporter.
|
||||
|
||||
The team will immediately review the incident and determine:
|
||||
|
||||
- What happened.
|
||||
- Whether this event constitutes a code of conduct violation.
|
||||
- Who the reported person is.
|
||||
- Whether this is an ongoing situation, or if there is a threat to anyone's physical safety.
|
||||
|
||||
If this is determined to be an ongoing incident or a threat to physical safety, the team's immediate priority will be to protect everyone involved. This means they may delay an official response until they believe that the situation has concluded and that everyone is physically safe.
|
||||
|
||||
The moderation team will respond within one week to the person who filed the report with either a resolution or an explanation of why the situation is not yet resolved.
|
||||
|
||||
Once the team has determined their final action, they'll contact the reporter to let them know what action (if any) they'll be taking. They'll take into account feedback from the reporter on the appropriateness of the response, but do not guarantee they'll act on it.
|
||||
|
||||
Finally, to maintain transparency in the reporting and enforcement process, whenever possible, a public transparency report of the incident will be made. A public report may not be made if the specifics of the incident do not allow the team to preserve anonymity, or if there is potential for ongoing harm.
|
||||
|
||||
## Addressing and Repairing Harm
|
||||
|
||||
If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped.
|
||||
|
||||
1) Warning
|
||||
1) Event: A violation involving a single incident or series of incidents.
|
||||
2) Consequence: A private, written warning from the Community Moderators.
|
||||
3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations.
|
||||
2) Temporarily Limited Activities
|
||||
1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation.
|
||||
2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members.
|
||||
3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over.
|
||||
3) Temporary Suspension
|
||||
1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation.
|
||||
2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions.
|
||||
3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted.
|
||||
4) Permanent Ban
|
||||
1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member.
|
||||
2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior.
|
||||
3) Repair: There is no possible repair in cases of this severity.
|
||||
|
||||
This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, including GitHub and the Prime Radiant Discord server. It also applies when an individual is officially representing the community in public or other spaces. Examples of representing the community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
||||
|
||||
Behavior outside of official Prime Radiant spaces may also be considered as supporting evidence for a report if that behavior establishes a pattern, or represents a potential risk to the Prime Radiant community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/).
|
||||
|
||||
Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion).
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Jesse Vincent
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,399 +0,0 @@
|
||||
# Superpowers
|
||||
|
||||
Superpowers is a complete software development methodology for your coding agents, built on top of a set of composable skills and some initial instructions that make sure your agent uses them.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [How it works](#how-it-works)
|
||||
- [Commercial Services](#commercial-services)
|
||||
- [Getting Started](#installation)
|
||||
- [Claude Code](#claude-code)
|
||||
- [Antigravity](#antigravity)
|
||||
- [Codex App](#codex-app)
|
||||
- [Codex CLI](#codex-cli)
|
||||
- [Cursor](#cursor)
|
||||
- [Devin CLI](#devin-cli)
|
||||
- [Factory Droid](#factory-droid)
|
||||
- [Gemini CLI](#gemini-cli)
|
||||
- [GitHub Copilot CLI](#github-copilot-cli)
|
||||
- [Grok Build CLI](#grok-build-cli)
|
||||
- [Kimi Code](#kimi-code)
|
||||
- [OpenCode](#opencode)
|
||||
- [Pi](#pi)
|
||||
- [Qwen Code](#qwen-code)
|
||||
- [Hermes Agent](#hermes-agent)
|
||||
- [Muse](#muse)
|
||||
- [The Basic Workflow](#the-basic-workflow)
|
||||
- [When Something Goes Wrong](#when-something-goes-wrong)
|
||||
- [Community](#community)
|
||||
- [What's Inside](#whats-inside)
|
||||
- [Philosophy](#philosophy)
|
||||
- [Contributing](#contributing)
|
||||
- [Updating](#updating)
|
||||
- [License](#license)
|
||||
- [Visual companion telemetry](#visual-companion-telemetry)
|
||||
|
||||
## How it works
|
||||
|
||||
It starts from the moment you fire up your coding agent. As soon as it sees that you're building something, it *doesn't* just jump into trying to write code. Instead, it steps back and asks you what you're really trying to do.
|
||||
|
||||
Once it's teased a spec out of the conversation, it shows it to you in chunks short enough to actually read and digest.
|
||||
|
||||
After you've signed off on the design, your agent puts together an implementation plan that's clear enough for an enthusiastic junior engineer with poor taste, no judgement, no project context, and an aversion to testing to follow. It emphasizes true red/green TDD, YAGNI (You Aren't Gonna Need It), and DRY.
|
||||
|
||||
Next up, once you say "go", it launches a *subagent-driven-development* process, having agents work through each engineering task, inspecting and reviewing their work, and continuing forward. It's not uncommon for your agent to work autonomously for a couple hours at a time without deviating from the plan you put together.
|
||||
|
||||
There's a bunch more to it, but that's the core of the system. And because the skills trigger automatically, you don't need to do anything special. Your coding agent just has Superpowers.
|
||||
|
||||
## Commercial Services
|
||||
|
||||
If you're using Superpowers in enterprise and could benefit from commercial support, additional tooling, or managed spending, please don't hesitate to drop us a line at sales@primeradiant.com.
|
||||
|
||||
## Installation
|
||||
|
||||
Installation differs by harness. If you use more than one, install Superpowers separately for each one.
|
||||
|
||||
### Claude Code
|
||||
|
||||
Superpowers is available via the [official Claude plugin marketplace](https://claude.com/plugins/superpowers)
|
||||
|
||||
#### Official Marketplace
|
||||
|
||||
- Install the plugin from Anthropic's official marketplace:
|
||||
|
||||
```bash
|
||||
/plugin install superpowers@claude-plugins-official
|
||||
```
|
||||
|
||||
#### Superpowers Marketplace
|
||||
|
||||
The Superpowers marketplace provides Superpowers and some other related plugins for Claude Code.
|
||||
|
||||
- Register the marketplace:
|
||||
|
||||
```bash
|
||||
/plugin marketplace add obra/superpowers-marketplace
|
||||
```
|
||||
|
||||
- Install the plugin from this marketplace:
|
||||
|
||||
```bash
|
||||
/plugin install superpowers@superpowers-marketplace
|
||||
```
|
||||
|
||||
### Antigravity
|
||||
|
||||
Install Superpowers as a plugin from this repository:
|
||||
|
||||
```bash
|
||||
agy plugin install https://github.com/obra/superpowers
|
||||
```
|
||||
|
||||
Antigravity runs the plugin's session-start hook, so Superpowers is active from
|
||||
the first message. Reinstall with the same command to update.
|
||||
|
||||
### Codex App
|
||||
|
||||
Superpowers is available via the [official Codex plugin marketplace](https://github.com/openai/plugins).
|
||||
|
||||
- In the Codex app, click on Plugins in the sidebar.
|
||||
- You should see `Superpowers` in the Coding section.
|
||||
- Click the `+` next to Superpowers and follow the prompts.
|
||||
|
||||
### Codex CLI
|
||||
|
||||
Superpowers is available via the [official Codex plugin marketplace](https://github.com/openai/plugins).
|
||||
|
||||
- Open the plugin search interface:
|
||||
|
||||
```bash
|
||||
/plugins
|
||||
```
|
||||
|
||||
- Search for Superpowers:
|
||||
|
||||
```bash
|
||||
superpowers
|
||||
```
|
||||
|
||||
- Select `Install Plugin`.
|
||||
|
||||
### Cursor
|
||||
|
||||
- In Cursor Agent chat, install from marketplace:
|
||||
|
||||
```text
|
||||
/add-plugin superpowers
|
||||
```
|
||||
|
||||
- Or search for "superpowers" in the plugin marketplace.
|
||||
|
||||
### Devin CLI
|
||||
|
||||
- Install the plugin from this repository:
|
||||
|
||||
```bash
|
||||
devin plugins install obra/superpowers
|
||||
```
|
||||
|
||||
- Update to the latest version with:
|
||||
|
||||
```bash
|
||||
devin plugins update superpowers
|
||||
```
|
||||
|
||||
### Factory Droid
|
||||
|
||||
- Register the marketplace:
|
||||
|
||||
```bash
|
||||
droid plugin marketplace add https://github.com/obra/superpowers
|
||||
```
|
||||
|
||||
- Install the plugin:
|
||||
|
||||
```bash
|
||||
droid plugin install superpowers@superpowers
|
||||
```
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
- Install the extension:
|
||||
|
||||
```bash
|
||||
gemini extensions install https://github.com/obra/superpowers
|
||||
```
|
||||
|
||||
- Update later:
|
||||
|
||||
```bash
|
||||
gemini extensions update superpowers
|
||||
```
|
||||
|
||||
### GitHub Copilot CLI
|
||||
|
||||
- Register the marketplace:
|
||||
|
||||
```bash
|
||||
copilot plugin marketplace add obra/superpowers-marketplace
|
||||
```
|
||||
|
||||
- Install the plugin:
|
||||
|
||||
```bash
|
||||
copilot plugin install superpowers@superpowers-marketplace
|
||||
```
|
||||
|
||||
### Grok Build CLI
|
||||
|
||||
Superpowers is available via the [official Grok plugin marketplace](https://github.com/xai-org/plugin-marketplace).
|
||||
|
||||
- Install the plugin from xAI's official marketplace:
|
||||
|
||||
```bash
|
||||
grok plugin install superpowers@xai-official --trust
|
||||
```
|
||||
|
||||
- Or open the marketplace in the TUI, search for Superpowers, and install it:
|
||||
|
||||
```text
|
||||
/marketplace
|
||||
```
|
||||
|
||||
### Kimi Code
|
||||
|
||||
Superpowers is available in Kimi Code's plugin marketplace.
|
||||
|
||||
- Open Kimi Code's plugin manager:
|
||||
|
||||
```text
|
||||
/plugins
|
||||
```
|
||||
|
||||
- Go to `Marketplace` > `Superpowers` and install it.
|
||||
|
||||
- Or install directly from this repository:
|
||||
|
||||
```text
|
||||
/plugins install https://github.com/obra/superpowers
|
||||
```
|
||||
|
||||
- Detailed docs: [docs/README.kimi.md](docs/README.kimi.md)
|
||||
|
||||
### OpenCode
|
||||
|
||||
OpenCode uses its own plugin install; install Superpowers separately even if you
|
||||
already use it in another harness.
|
||||
|
||||
- Tell OpenCode:
|
||||
|
||||
```
|
||||
Fetch and follow instructions from https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/.opencode/INSTALL.md
|
||||
```
|
||||
|
||||
- Detailed docs: [docs/README.opencode.md](docs/README.opencode.md)
|
||||
|
||||
### Pi
|
||||
|
||||
Install Superpowers as a Pi package from this repository:
|
||||
|
||||
```bash
|
||||
pi install git:github.com/obra/superpowers
|
||||
```
|
||||
|
||||
For local development, run Pi with this checkout loaded as a temporary package:
|
||||
|
||||
```bash
|
||||
pi -e /path/to/superpowers
|
||||
```
|
||||
|
||||
The Pi package loads the Superpowers skills and a small extension that injects the `using-superpowers` bootstrap at session startup and again after compaction. Pi has native skills, so no compatibility `Skill` tool is required. Subagent and task-list tools remain optional Pi companion packages.
|
||||
|
||||
### Qwen Code
|
||||
|
||||
Qwen Code installs plugins from Claude Code marketplaces directly.
|
||||
|
||||
- Install the plugin from this repository, and pick `superpowers` when prompted:
|
||||
|
||||
```bash
|
||||
qwen extensions install obra/superpowers
|
||||
```
|
||||
|
||||
- Update later:
|
||||
|
||||
```bash
|
||||
qwen extensions update superpowers
|
||||
```
|
||||
|
||||
### Hermes Agent
|
||||
|
||||
Install Superpowers as a Hermes plugin from this repository:
|
||||
|
||||
```bash
|
||||
hermes plugins install obra/superpowers --enable
|
||||
```
|
||||
|
||||
Restart any active Hermes sessions after installing. Note: Hermes has no
|
||||
post-compaction hook, so a very long session that compacts over its first
|
||||
turn loses the bootstrap — start a fresh session if skills stop triggering.
|
||||
|
||||
### Muse
|
||||
|
||||
Superpowers is available as a native Muse plugin — same repo, same skills, all harnesses. The `using-superpowers` bootstrap is injected via the native `SessionStart` hook alongside Claude Code, Codex, Cursor, Gemini, Pi, and the rest — no per-session opt-in.
|
||||
|
||||
- Install from a local checkout:
|
||||
|
||||
```bash
|
||||
muse plugins install ./
|
||||
muse plugins approve superpowers
|
||||
```
|
||||
|
||||
Or clone and install:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/obra/superpowers.git
|
||||
muse plugins install ./superpowers
|
||||
muse plugins approve superpowers
|
||||
```
|
||||
|
||||
- Update later:
|
||||
|
||||
```bash
|
||||
muse plugins update superpowers
|
||||
```
|
||||
|
||||
Restart any active Muse sessions after installing so the `SessionStart` hook takes effect — skills are active immediately, hooks require approval on first install. To verify, start a fresh session and send `Let's make a react todo list` — a working install auto-triggers `brainstorming` before any code is written. Version is tracked in `.version-bump.json` so `scripts/bump-version.sh` keeps it in sync.
|
||||
|
||||
## The Basic Workflow
|
||||
|
||||
1. **brainstorming** - Activates before writing code. Refines rough ideas through questions, explores alternatives, presents design in sections for validation. Saves design document.
|
||||
|
||||
2. **using-git-worktrees** - Activates after design approval. Creates isolated workspace on new branch, runs project setup, verifies clean test baseline.
|
||||
|
||||
3. **writing-plans** - Activates with approved design. Breaks work into bite-sized tasks (2-5 minutes each). Every task has exact file paths, complete code, verification steps.
|
||||
|
||||
4. **subagent-driven-development** or **executing-plans** - Activates with plan. Either dispatches a fresh subagent per task with a review after each (most thorough), or implements every task inline in the current session with one fresh review of the whole branch at the end (cheapest).
|
||||
|
||||
5. **test-driven-development** - Activates during implementation. Enforces RED-GREEN-REFACTOR: write failing test, watch it fail, write minimal code, watch it pass, commit. Deletes code written before tests.
|
||||
|
||||
6. **requesting-code-review** - Activates between tasks. Reviews against plan, reports issues by severity. Critical issues block progress.
|
||||
|
||||
7. **finishing-a-development-branch** - Activates when tasks complete. Verifies tests, presents options (merge/PR/keep/discard), cleans up worktree.
|
||||
|
||||
**The agent checks for relevant skills before any task.** Mandatory workflows, not suggestions.
|
||||
|
||||
## When Something Goes Wrong
|
||||
|
||||
Sometimes a session misbehaves: a skill fires when it shouldn't, stays silent when it should, or the agent ignores its plan, repeats work, or burns more tokens than you'd expect. Ask your coding agent to "figure out what went wrong with superpowers in this session" and it will invoke the **diagnosing-superpowers** skill. To examine an earlier session, name it: "figure out what went wrong with superpowers in session `<id>`".
|
||||
|
||||
The skill reads the session transcript, reports what happened with line-level evidence, and, if you want, packages a scrubbed bundle for a bug report.
|
||||
|
||||
## Community
|
||||
|
||||
Superpowers is built by [Jesse Vincent](https://blog.fsck.com) and the rest of the folks at [Prime Radiant](https://primeradiant.com).
|
||||
|
||||
- **Discord**: [Join us](https://discord.gg/35wsABTejz) for community support, questions, and sharing what you're building with Superpowers
|
||||
- **Issues**: https://github.com/obra/superpowers/issues
|
||||
- **Release announcements**: [Sign up](https://primeradiant.com/superpowers/) to get notified about new versions
|
||||
|
||||
## What's Inside
|
||||
|
||||
### Skills Library
|
||||
|
||||
**Testing**
|
||||
- **test-driven-development** - RED-GREEN-REFACTOR cycle (includes testing anti-patterns reference)
|
||||
|
||||
**Debugging**
|
||||
- **systematic-debugging** - 4-phase root cause process (includes root-cause-tracing, defense-in-depth, condition-based-waiting techniques)
|
||||
- **verification-before-completion** - Ensure it's actually fixed
|
||||
- **diagnosing-superpowers** - Work out what went wrong in a session, with evidence; export a scrubbed bundle or file an issue
|
||||
|
||||
**Collaboration**
|
||||
- **brainstorming** - Socratic design refinement
|
||||
- **writing-plans** - Detailed implementation plans
|
||||
- **executing-plans** - Inline plan execution: one context, one final review
|
||||
- **dispatching-parallel-agents** - Concurrent subagent workflows
|
||||
- **requesting-code-review** - Pre-review checklist
|
||||
- **receiving-code-review** - Responding to feedback
|
||||
- **using-git-worktrees** - Parallel development branches
|
||||
- **finishing-a-development-branch** - Merge/PR decision workflow
|
||||
- **subagent-driven-development** - Fast iteration with two-stage review (spec compliance, then code quality)
|
||||
|
||||
**Meta**
|
||||
- **writing-skills** - Create new skills following best practices (includes testing methodology)
|
||||
- **using-superpowers** - Introduction to the skills system
|
||||
|
||||
## Philosophy
|
||||
|
||||
- **Test-Driven Development** - Write tests first, always
|
||||
- **Systematic over ad-hoc** - Process over guessing
|
||||
- **Complexity reduction** - Simplicity as primary goal
|
||||
- **Evidence over claims** - Verify before declaring success
|
||||
|
||||
Read [the original release announcement](https://blog.fsck.com/2025/10/09/superpowers/).
|
||||
|
||||
## Contributing
|
||||
|
||||
The general contribution process for Superpowers is below. Keep in mind that we don't generally accept contributions of new skills and that any updates to skills must work across all of the coding agents we support.
|
||||
|
||||
1. Fork the repository
|
||||
2. Switch to the 'dev' branch
|
||||
3. Create a branch for your work
|
||||
4. Follow the `writing-skills` skill for creating and testing new and modified skills
|
||||
5. Submit a PR, being sure to fill in the pull request template.
|
||||
|
||||
Skill-behavior tests use the drill eval harness from [superpowers-evals](https://github.com/prime-radiant-inc/superpowers-evals/), cloned into `evals/` — see `evals/README.md` for setup. Plugin-infrastructure tests live at `tests/` and run via the relevant `run-*.sh` or `npm test`.
|
||||
|
||||
See `skills/writing-skills/SKILL.md` for the complete guide.
|
||||
|
||||
## Updating
|
||||
|
||||
Superpowers updates are somewhat coding-agent dependent, but are often automatic.
|
||||
|
||||
## License
|
||||
|
||||
MIT License - see LICENSE file for details
|
||||
|
||||
## Visual companion telemetry
|
||||
|
||||
Because skills and plugins don't provide any feedback to creators, we have no idea how many of you are using Superpowers. By default, the Prime Radiant logo on brainstorming's optional visual companion feature is loaded from our website. It includes the version of Superpowers in use. It does not include any details about your project, prompt, or coding agent. We don't see your clicks or anything about what you're building. This helps us have a rough idea of how many folks are using Superpowers and which version of Superpowers they're using. It's 100% optional. To disable this, set the environment variable `SUPERPOWERS_DISABLE_TELEMETRY` to any true value. Superpowers also honors Claude Code's `DISABLE_TELEMETRY` and `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` opt-outs.
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"sourceId": "superpowers",
|
||||
"repo": "https://github.com/obra/superpowers.git",
|
||||
"ref": "main",
|
||||
"commit": "5bf4e78011075bcfc0dc295f0724994cd123ee71",
|
||||
"adapter": "codex-plugin",
|
||||
"sourcePath": ".",
|
||||
"syncedAt": "2026-09-22T16:08:55Z"
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="Calque_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M394.28,207.8c.81,2.41,1.39,4.78,1.8,7.07,1.61,9.03-.93,17.78-5.99,21.74-22.6,17.7-49.85,29.35-75.34,38.6-.59.22-1.09.28-1.4.34-2.22.47-4.95,1.04-7.25,0-1.46-.66-2.25-1.74-2.66-2.3-1.56-2.1-1.59-4.31-1.56-5.13.1-2.67-.01-4.69,0-4.82.45-3.52.91-10.66,1.41-21.28.6-3.87,2.16-9.63,6.94-13.96,4.01-3.62,8.33-4.6,14.59-5.87,10.76-2.19,37.21-8.22,47.42-16.56,1.63-1.33,2.97-2.65,4.19-3.96,3.72-3.99,6.39-7.92,7.93-10.36,3.22,3.22,7.25,8.48,9.92,16.47Z"/><path d="M428.67,185.28c-2.33,11.99-8.91,22.32-15.88,30.38.27-5.5-.05-12.11-1.86-19.08-5.04-19.36-19.74-34.7-37.78-37.78-32.21-9.74-70.59,3.79-99.08,18.29-3.87,1.95-9.52-2.77-11.84-8.16-3.32-7.71-1.63-6.28,2.61-8.49,38.31-20.03,82.01-39.61,123.91-29.7,8.26,1.95,15.96,5.26,23.48,10.54,11.32,7.96,20.21,24.74,16.44,44Z"/><path d="M117.72,304.2c-.81-2.41-1.39-4.78-1.8-7.07-1.61-9.03.93-17.78,5.99-21.74,22.6-17.7,49.85-29.35,75.34-38.6.59-.22,1.09-.28,1.4-.34,2.22-.47,4.95-1.04,7.25,0,1.46.66,2.25,1.74,2.66,2.3,1.56,2.1,1.59,4.31,1.56,5.13-.1,2.67.01,4.69,0,4.82-.45,3.52-.91,10.66-1.41,21.28-.6,3.87-2.16,9.63-6.94,13.96-4.01,3.62-8.33,4.6-14.59,5.87-10.76,2.19-37.21,8.22-47.42,16.56-1.63,1.33-2.97,2.65-4.19,3.96-3.72,3.99-6.39,7.92-7.93,10.36-3.22-3.22-7.25-8.48-9.92-16.47Z"/><path d="M83.33,326.72c2.33-11.99,8.91-22.32,15.88-30.38-.27,5.5.05,12.11,1.86,19.08,5.04,19.36,19.74,34.7,37.78,37.78,32.21,9.74,70.59-3.79,99.08-18.29,3.87-1.95,9.52,2.77,11.84,8.16,3.32,7.71,1.63,6.28-2.61,8.49-38.31,20.03-82.01,39.61-123.91,29.7-8.26-1.95-15.96-5.26-23.48-10.54-11.32-7.96-20.21-24.74-16.44-44Z"/><ellipse cx="255.16" cy="258.86" rx="28.95" ry="28.76"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
Loaded 100 of 179 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user