principles-disciple 1.5.4 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/commands/context.d.ts +5 -0
  2. package/dist/commands/context.js +312 -0
  3. package/dist/commands/evolution-status.d.ts +4 -0
  4. package/dist/commands/evolution-status.js +138 -0
  5. package/dist/commands/export.d.ts +2 -0
  6. package/dist/commands/export.js +45 -0
  7. package/dist/commands/focus.d.ts +14 -0
  8. package/dist/commands/focus.js +582 -0
  9. package/dist/commands/pain.js +143 -6
  10. package/dist/commands/principle-rollback.d.ts +4 -0
  11. package/dist/commands/principle-rollback.js +22 -0
  12. package/dist/commands/rollback.d.ts +19 -0
  13. package/dist/commands/rollback.js +119 -0
  14. package/dist/commands/samples.d.ts +2 -0
  15. package/dist/commands/samples.js +55 -0
  16. package/dist/core/config.d.ts +37 -0
  17. package/dist/core/config.js +47 -0
  18. package/dist/core/control-ui-db.d.ts +68 -0
  19. package/dist/core/control-ui-db.js +274 -0
  20. package/dist/core/detection-funnel.d.ts +1 -1
  21. package/dist/core/detection-funnel.js +4 -0
  22. package/dist/core/dictionary.d.ts +2 -0
  23. package/dist/core/dictionary.js +13 -0
  24. package/dist/core/event-log.d.ts +22 -1
  25. package/dist/core/event-log.js +319 -0
  26. package/dist/core/evolution-engine.d.ts +5 -5
  27. package/dist/core/evolution-engine.js +18 -18
  28. package/dist/core/evolution-migration.d.ts +5 -0
  29. package/dist/core/evolution-migration.js +65 -0
  30. package/dist/core/evolution-reducer.d.ts +69 -0
  31. package/dist/core/evolution-reducer.js +369 -0
  32. package/dist/core/evolution-types.d.ts +103 -0
  33. package/dist/core/focus-history.d.ts +65 -0
  34. package/dist/core/focus-history.js +266 -0
  35. package/dist/core/init.js +30 -7
  36. package/dist/core/migration.js +0 -2
  37. package/dist/core/path-resolver.d.ts +3 -0
  38. package/dist/core/path-resolver.js +90 -31
  39. package/dist/core/paths.d.ts +7 -8
  40. package/dist/core/paths.js +48 -40
  41. package/dist/core/profile.js +1 -1
  42. package/dist/core/session-tracker.d.ts +4 -0
  43. package/dist/core/session-tracker.js +15 -0
  44. package/dist/core/thinking-models.d.ts +38 -0
  45. package/dist/core/thinking-models.js +170 -0
  46. package/dist/core/trajectory.d.ts +184 -0
  47. package/dist/core/trajectory.js +817 -0
  48. package/dist/core/trust-engine.d.ts +2 -0
  49. package/dist/core/trust-engine.js +30 -4
  50. package/dist/core/workspace-context.d.ts +13 -0
  51. package/dist/core/workspace-context.js +50 -7
  52. package/dist/hooks/gate.js +301 -30
  53. package/dist/hooks/llm.d.ts +8 -0
  54. package/dist/hooks/llm.js +347 -69
  55. package/dist/hooks/message-sanitize.d.ts +3 -0
  56. package/dist/hooks/message-sanitize.js +37 -0
  57. package/dist/hooks/pain.js +105 -5
  58. package/dist/hooks/prompt.d.ts +20 -11
  59. package/dist/hooks/prompt.js +558 -158
  60. package/dist/hooks/subagent.d.ts +9 -2
  61. package/dist/hooks/subagent.js +40 -3
  62. package/dist/http/principles-console-route.d.ts +2 -0
  63. package/dist/http/principles-console-route.js +257 -0
  64. package/dist/i18n/commands.js +48 -20
  65. package/dist/index.js +264 -8
  66. package/dist/service/control-ui-query-service.d.ts +217 -0
  67. package/dist/service/control-ui-query-service.js +537 -0
  68. package/dist/service/empathy-observer-manager.d.ts +42 -0
  69. package/dist/service/empathy-observer-manager.js +147 -0
  70. package/dist/service/evolution-worker.d.ts +10 -0
  71. package/dist/service/evolution-worker.js +156 -24
  72. package/dist/service/trajectory-service.d.ts +2 -0
  73. package/dist/service/trajectory-service.js +15 -0
  74. package/dist/tools/agent-spawn.d.ts +27 -6
  75. package/dist/tools/agent-spawn.js +339 -87
  76. package/dist/tools/deep-reflect.d.ts +27 -7
  77. package/dist/tools/deep-reflect.js +282 -113
  78. package/dist/types/event-types.d.ts +84 -2
  79. package/dist/types/event-types.js +33 -0
  80. package/dist/types.d.ts +52 -0
  81. package/dist/types.js +24 -1
  82. package/openclaw.plugin.json +43 -11
  83. package/package.json +16 -6
  84. package/templates/langs/zh/core/HEARTBEAT.md +28 -4
  85. package/templates/langs/zh/skills/pd-daily/SKILL.md +97 -13
  86. package/templates/pain_settings.json +54 -2
  87. package/templates/workspace/.principles/PROFILE.json +2 -0
  88. package/templates/workspace/okr/CURRENT_FOCUS.md +57 -0
@@ -1,2 +1,9 @@
1
- import { PluginHookSubagentEndedEvent, PluginHookAgentContext } from '../openclaw-sdk.js';
2
- export declare function handleSubagentEnded(event: PluginHookSubagentEndedEvent, ctx: PluginHookAgentContext): Promise<void>;
1
+ import type { PluginHookSubagentEndedEvent, PluginHookSubagentContext } from '../openclaw-sdk.js';
2
+ import { type EmpathyObserverApi } from '../service/empathy-observer-manager.js';
3
+ type SubagentEndedHookContext = PluginHookSubagentContext & {
4
+ api?: EmpathyObserverApi;
5
+ workspaceDir?: string;
6
+ sessionId?: string;
7
+ };
8
+ export declare function handleSubagentEnded(event: PluginHookSubagentEndedEvent, ctx: SubagentEndedHookContext): Promise<void>;
9
+ export {};
@@ -1,24 +1,56 @@
1
1
  import { writePainFlag } from '../core/pain.js';
2
2
  import { WorkspaceContext } from '../core/workspace-context.js';
3
+ import { empathyObserverManager } from '../service/empathy-observer-manager.js';
3
4
  import * as fs from 'fs';
5
+ function emitSubagentPainEvent(wctx, payload) {
6
+ try {
7
+ wctx.evolutionReducer.emitSync({
8
+ ts: new Date().toISOString(),
9
+ type: 'pain_detected',
10
+ data: {
11
+ painId: `pain_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
12
+ painType: 'subagent_error',
13
+ source: payload.source,
14
+ reason: payload.reason,
15
+ score: payload.score,
16
+ sessionId: payload.sessionId,
17
+ },
18
+ });
19
+ }
20
+ catch (e) {
21
+ console.warn(`[PD:Subagent] failed to emit evolution event: ${String(e)}`);
22
+ }
23
+ }
4
24
  export async function handleSubagentEnded(event, ctx) {
5
25
  const { outcome, targetSessionKey } = event;
6
26
  const workspaceDir = ctx.workspaceDir;
7
27
  if (!workspaceDir)
8
28
  return;
9
29
  const wctx = WorkspaceContext.fromHookContext(ctx);
30
+ // Empathy observer subagent session is handled by sidecar manager
31
+ if (targetSessionKey?.startsWith('empathy_obs:')) {
32
+ await empathyObserverManager.reap(ctx.api, targetSessionKey, workspaceDir);
33
+ return;
34
+ }
10
35
  const config = wctx.config;
11
36
  // 1. Autonomous Pain Capture: If subagent failed, record pain
12
37
  if (outcome === 'error' || outcome === 'timeout') {
13
38
  const scoreSettings = config.get('scores');
14
39
  const score = outcome === 'error' ? scoreSettings.subagent_error_penalty : scoreSettings.subagent_timeout_penalty;
40
+ const reason = `Subagent session ${targetSessionKey} ended with outcome: ${outcome}`;
15
41
  writePainFlag(workspaceDir, {
16
42
  source: `subagent_${outcome}`,
17
43
  score: String(score),
18
44
  time: new Date().toISOString(),
19
- reason: `Subagent session ${targetSessionKey} ended with outcome: ${outcome}`,
45
+ reason,
20
46
  is_risky: 'true'
21
47
  });
48
+ emitSubagentPainEvent(wctx, {
49
+ source: `subagent_${outcome}`,
50
+ reason,
51
+ score,
52
+ sessionId: ctx.sessionId,
53
+ });
22
54
  }
23
55
  // 2. Loop Closure: Clean up evolution queue if any subagent finished successfully
24
56
  if (outcome === 'ok' || outcome === 'deleted') {
@@ -35,8 +67,13 @@ export async function handleSubagentEnded(event, ctx) {
35
67
  // Find in_progress tasks
36
68
  const inProgressTasks = queue.filter((t) => t.status === 'in_progress');
37
69
  if (inProgressTasks.length > 0) {
38
- // Sort by timestamp to find the oldest one
39
- const oldestTask = inProgressTasks.sort((a, b) => new Date(a.enqueued_at).getTime() - new Date(b.enqueued_at).getTime())[0];
70
+ const resolveTaskTime = (task) => {
71
+ const raw = task?.enqueued_at || task?.timestamp;
72
+ const ts = new Date(raw).getTime();
73
+ return Number.isFinite(ts) ? ts : Number.MAX_SAFE_INTEGER;
74
+ };
75
+ // Sort by enqueue timestamp (fallback to legacy timestamp) to find the oldest task
76
+ const oldestTask = inProgressTasks.sort((a, b) => resolveTaskTime(a) - resolveTaskTime(b))[0];
40
77
  // Mark as completed
41
78
  const taskIndex = queue.findIndex((t) => t.id === oldestTask.id);
42
79
  if (taskIndex !== -1) {
@@ -0,0 +1,2 @@
1
+ import type { OpenClawPluginApi, OpenClawPluginHttpRouteParams } from '../openclaw-sdk.js';
2
+ export declare function createPrinciplesConsoleRoute(api: OpenClawPluginApi): OpenClawPluginHttpRouteParams;
@@ -0,0 +1,257 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { ControlUiQueryService } from '../service/control-ui-query-service.js';
4
+ const ROUTE_PREFIX = '/plugins/principles';
5
+ const API_PREFIX = `${ROUTE_PREFIX}/api`;
6
+ const ASSETS_PREFIX = `${ROUTE_PREFIX}/assets`;
7
+ function json(res, statusCode, payload) {
8
+ const body = JSON.stringify(payload, null, 2);
9
+ res.statusCode = statusCode;
10
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
11
+ res.end(body);
12
+ }
13
+ function text(res, statusCode, body) {
14
+ res.statusCode = statusCode;
15
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
16
+ res.end(body);
17
+ }
18
+ function contentTypeFor(filePath) {
19
+ const ext = path.extname(filePath).toLowerCase();
20
+ switch (ext) {
21
+ case '.html':
22
+ return 'text/html; charset=utf-8';
23
+ case '.css':
24
+ return 'text/css; charset=utf-8';
25
+ case '.js':
26
+ return 'application/javascript; charset=utf-8';
27
+ case '.json':
28
+ return 'application/json; charset=utf-8';
29
+ case '.svg':
30
+ return 'image/svg+xml';
31
+ default:
32
+ return 'application/octet-stream';
33
+ }
34
+ }
35
+ async function readJsonBody(req) {
36
+ const chunks = [];
37
+ for await (const chunk of req) {
38
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
39
+ }
40
+ if (chunks.length === 0)
41
+ return {};
42
+ const body = Buffer.concat(chunks).toString('utf8');
43
+ try {
44
+ return JSON.parse(body);
45
+ }
46
+ catch {
47
+ throw new Error('invalid_json');
48
+ }
49
+ }
50
+ function safeStaticPath(rootDir, requestPath) {
51
+ const relative = requestPath.startsWith(ASSETS_PREFIX)
52
+ ? requestPath.slice(ASSETS_PREFIX.length).replace(/^\/+/, '')
53
+ : '';
54
+ const normalized = path.normalize(relative);
55
+ const webRoot = path.join(rootDir, 'dist', 'web');
56
+ const assetsRoot = path.join(webRoot, 'assets');
57
+ const target = path.join(assetsRoot, normalized);
58
+ const relativeTarget = path.relative(assetsRoot, target);
59
+ if (relativeTarget.startsWith('..') || path.isAbsolute(relativeTarget)) {
60
+ return null;
61
+ }
62
+ return target;
63
+ }
64
+ function serveFile(res, filePath) {
65
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
66
+ return false;
67
+ }
68
+ res.statusCode = 200;
69
+ res.setHeader('Content-Type', contentTypeFor(filePath));
70
+ const stream = fs.createReadStream(filePath);
71
+ stream.on('error', () => {
72
+ res.statusCode = 500;
73
+ res.end('Internal Server Error');
74
+ });
75
+ stream.pipe(res);
76
+ return true;
77
+ }
78
+ function createService(api) {
79
+ const workspaceDir = api.resolvePath('.');
80
+ return new ControlUiQueryService(workspaceDir);
81
+ }
82
+ function handleApiRoute(api, pathname, req, res) {
83
+ const service = createService(api);
84
+ const url = new URL(req.url || pathname, 'http://127.0.0.1');
85
+ const method = (req.method || 'GET').toUpperCase();
86
+ const done = (fn) => {
87
+ try {
88
+ const payload = fn();
89
+ json(res, 200, payload);
90
+ return true;
91
+ }
92
+ catch (error) {
93
+ api.logger.warn(`[PD:ControlUI] API request failed for ${pathname}: ${String(error)}`);
94
+ json(res, 500, { error: 'internal_error', message: String(error) });
95
+ return true;
96
+ }
97
+ finally {
98
+ service.dispose();
99
+ }
100
+ };
101
+ if (pathname === `${API_PREFIX}/overview` && method === 'GET') {
102
+ return done(() => service.getOverview());
103
+ }
104
+ if (pathname === `${API_PREFIX}/samples` && method === 'GET') {
105
+ return done(() => service.listSamples({
106
+ status: url.searchParams.get('status') ?? undefined,
107
+ qualityMin: url.searchParams.has('qualityMin') ? Number(url.searchParams.get('qualityMin')) : undefined,
108
+ dateFrom: url.searchParams.get('dateFrom') ?? undefined,
109
+ dateTo: url.searchParams.get('dateTo') ?? undefined,
110
+ failureMode: url.searchParams.get('failureMode') ?? undefined,
111
+ page: url.searchParams.has('page') ? Number(url.searchParams.get('page')) : undefined,
112
+ pageSize: url.searchParams.has('pageSize') ? Number(url.searchParams.get('pageSize')) : undefined,
113
+ }));
114
+ }
115
+ const sampleDetailMatch = pathname.match(/^\/plugins\/principles\/api\/samples\/([^/]+)$/);
116
+ if (sampleDetailMatch && method === 'GET') {
117
+ try {
118
+ const detail = service.getSampleDetail(decodeURIComponent(sampleDetailMatch[1]));
119
+ if (!detail) {
120
+ json(res, 404, { error: 'not_found', message: 'Sample not found.' });
121
+ return true;
122
+ }
123
+ json(res, 200, detail);
124
+ return true;
125
+ }
126
+ catch (error) {
127
+ api.logger.warn(`[PD:ControlUI] API request failed for ${pathname}: ${String(error)}`);
128
+ json(res, 500, { error: 'internal_error', message: String(error) });
129
+ return true;
130
+ }
131
+ finally {
132
+ service.dispose();
133
+ }
134
+ }
135
+ const sampleReviewMatch = pathname.match(/^\/plugins\/principles\/api\/samples\/([^/]+)\/review$/);
136
+ if (sampleReviewMatch && method === 'POST') {
137
+ return (async () => {
138
+ try {
139
+ const body = await readJsonBody(req);
140
+ const decision = body.decision === 'approved' || body.decision === 'rejected'
141
+ ? body.decision
142
+ : null;
143
+ if (!decision) {
144
+ json(res, 400, { error: 'bad_request', message: 'decision must be approved or rejected' });
145
+ return true;
146
+ }
147
+ const record = service.reviewSample(decodeURIComponent(sampleReviewMatch[1]), decision, typeof body.note === 'string' ? body.note : undefined);
148
+ json(res, 200, record);
149
+ return true;
150
+ }
151
+ catch (error) {
152
+ if (error instanceof Error && error.message === 'invalid_json') {
153
+ json(res, 400, { error: 'bad_request', message: 'Request body must be valid JSON.' });
154
+ return true;
155
+ }
156
+ api.logger.warn(`[PD:ControlUI] Review request failed for ${pathname}: ${String(error)}`);
157
+ json(res, 500, { error: 'internal_error', message: String(error) });
158
+ return true;
159
+ }
160
+ finally {
161
+ service.dispose();
162
+ }
163
+ })();
164
+ }
165
+ if (pathname === `${API_PREFIX}/thinking` && method === 'GET') {
166
+ return done(() => service.getThinkingOverview());
167
+ }
168
+ const thinkingDetailMatch = pathname.match(/^\/plugins\/principles\/api\/thinking\/models\/([^/]+)$/);
169
+ if (thinkingDetailMatch && method === 'GET') {
170
+ try {
171
+ const detail = service.getThinkingModelDetail(decodeURIComponent(thinkingDetailMatch[1]));
172
+ if (!detail) {
173
+ json(res, 404, { error: 'not_found', message: 'Thinking model not found.' });
174
+ return true;
175
+ }
176
+ json(res, 200, detail);
177
+ return true;
178
+ }
179
+ catch (error) {
180
+ api.logger.warn(`[PD:ControlUI] API request failed for ${pathname}: ${String(error)}`);
181
+ json(res, 500, { error: 'internal_error', message: String(error) });
182
+ return true;
183
+ }
184
+ finally {
185
+ service.dispose();
186
+ }
187
+ }
188
+ if (pathname === `${API_PREFIX}/export/corrections` && method === 'GET') {
189
+ try {
190
+ const mode = url.searchParams.get('mode') === 'redacted' ? 'redacted' : 'raw';
191
+ const result = service.exportCorrections(mode);
192
+ if (!fs.existsSync(result.filePath)) {
193
+ json(res, 404, { error: 'not_found', message: 'Export file not found.' });
194
+ return true;
195
+ }
196
+ res.statusCode = 200;
197
+ res.setHeader('Content-Type', 'application/x-ndjson; charset=utf-8');
198
+ res.setHeader('Content-Disposition', `attachment; filename="${path.basename(result.filePath)}"`);
199
+ const stream = fs.createReadStream(result.filePath);
200
+ stream.on('error', () => {
201
+ res.statusCode = 500;
202
+ res.end('Internal Server Error');
203
+ });
204
+ stream.pipe(res);
205
+ return true;
206
+ }
207
+ catch (error) {
208
+ api.logger.warn(`[PD:ControlUI] Export request failed for ${pathname}: ${String(error)}`);
209
+ json(res, 500, { error: 'internal_error', message: String(error) });
210
+ return true;
211
+ }
212
+ finally {
213
+ service.dispose();
214
+ }
215
+ }
216
+ service.dispose();
217
+ json(res, 404, { error: 'not_found', message: 'Unknown Principles Console API route.' });
218
+ return true;
219
+ }
220
+ export function createPrinciplesConsoleRoute(api) {
221
+ return {
222
+ path: ROUTE_PREFIX,
223
+ auth: 'gateway',
224
+ match: 'prefix',
225
+ async handler(req, res) {
226
+ const url = new URL(req.url || ROUTE_PREFIX, 'http://127.0.0.1');
227
+ const pathname = url.pathname;
228
+ const method = (req.method || 'GET').toUpperCase();
229
+ if (!pathname.startsWith(ROUTE_PREFIX)) {
230
+ return false;
231
+ }
232
+ if (pathname.startsWith(API_PREFIX)) {
233
+ return handleApiRoute(api, pathname, req, res);
234
+ }
235
+ if (pathname.startsWith(ASSETS_PREFIX)) {
236
+ if (method !== 'GET' && method !== 'HEAD') {
237
+ text(res, 405, 'Method Not Allowed');
238
+ return true;
239
+ }
240
+ const assetPath = safeStaticPath(api.rootDir, pathname);
241
+ if (!assetPath || !serveFile(res, assetPath)) {
242
+ text(res, 404, 'Asset Not Found');
243
+ }
244
+ return true;
245
+ }
246
+ if (method !== 'GET' && method !== 'HEAD') {
247
+ text(res, 405, 'Method Not Allowed');
248
+ return true;
249
+ }
250
+ const indexPath = path.join(api.rootDir, 'dist', 'web', 'index.html');
251
+ if (!serveFile(res, indexPath)) {
252
+ text(res, 503, 'Principles Console UI is not built yet.');
253
+ }
254
+ return true;
255
+ },
256
+ };
257
+ }
@@ -15,48 +15,76 @@ export function normalizeLanguage(lang) {
15
15
  }
16
16
  export const commandDescriptions = {
17
17
  'pd-init': {
18
- zh: '初始化战略访谈和OKR',
19
- en: 'Initialize strategy interview and OKRs'
18
+ zh: '初始化工作区(生成 PRINCIPLES.md、THINKING_OS.md 等)',
19
+ en: 'Initialize workspace (generate PRINCIPLES.md, THINKING_OS.md, etc.)'
20
20
  },
21
21
  'pd-okr': {
22
- zh: '目标与关键结果管理',
23
- en: 'Manage OKRs and align goals'
22
+ zh: '管理 OKR 目标与关键结果',
23
+ en: 'Manage OKR goals and key results'
24
24
  },
25
25
  'pd-bootstrap': {
26
- zh: '环境工具扫描与升级',
27
- en: 'Scan and upgrade environment tools'
26
+ zh: '扫描环境工具并建议升级',
27
+ en: 'Scan environment tools and suggest upgrades'
28
28
  },
29
29
  'pd-research': {
30
- zh: '发起工具升级研究',
31
- en: 'Research tool upgrades'
30
+ zh: '研究工具升级方案',
31
+ en: 'Research tool upgrade solutions'
32
32
  },
33
33
  'pd-thinking': {
34
- zh: '管理思维模型与候选方案',
35
- en: 'Manage Thinking OS mental models'
34
+ zh: '管理思维模型 [status|propose|audit]',
35
+ en: 'Manage Thinking OS [status|propose|audit]'
36
36
  },
37
37
  'pd-evolve': {
38
- zh: '执行完整进化循环',
39
- en: 'Run full evolution loop'
38
+ zh: '执行进化循环处理 Pain 信号',
39
+ en: 'Run evolution loop to process Pain signals'
40
40
  },
41
41
  'pd-daily': {
42
42
  zh: '配置并发送进化日报',
43
- en: 'Configure and send daily report'
43
+ en: 'Configure and send daily evolution report'
44
44
  },
45
45
  'pd-grooming': {
46
- zh: '工作区数字大扫除',
46
+ zh: '工作区清理与大扫除',
47
47
  en: 'Workspace cleanup and grooming'
48
48
  },
49
49
  'pd-trust': {
50
- zh: '查看信任积分与安全等级',
51
- en: 'View trust score and security stage'
50
+ zh: '查看信任分数和权限等级 (1-4)',
51
+ en: 'View trust score and permission stage (1-4)'
52
52
  },
53
53
  'pd-help': {
54
- zh: '获取交互式命令引导',
55
- en: 'Get interactive command guidance'
54
+ zh: '显示所有命令和使用指南',
55
+ en: 'Show all commands and usage guide'
56
56
  },
57
57
  'pd-status': {
58
- zh: '查看数字神经系统状态(GFI和痛苦词典)',
59
- en: 'View Digital Nerve System status (GFI and Pain Dictionary)'
58
+ zh: '查看系统状态(GFI、Pain 词典)',
59
+ en: 'View system status (GFI, Pain dictionary)'
60
+ },
61
+ 'pd-context': {
62
+ zh: '控制上下文注入 [status|thinking|trust|reflection|focus|preset] - 输入 /pd-context help 查看详情',
63
+ en: 'Control context injection [status|thinking|trust|reflection|focus|preset] - Type /pd-context help for details'
64
+ },
65
+ 'pd-focus': {
66
+ zh: '管理 CURRENT_FOCUS.md [status|history|compress|rollback] - 查看/压缩/回滚焦点文件',
67
+ en: 'Manage CURRENT_FOCUS.md [status|history|compress|rollback] - View/compress/rollback focus file'
68
+ },
69
+ 'pd-evolution-status': {
70
+ zh: '查看 evolution 闭环状态(candidate/probation/active)',
71
+ en: 'Show evolution loop status (candidate/probation/active)'
72
+ },
73
+ 'pd-principle-rollback': {
74
+ zh: '回滚原则并加入黑名单 <principle-id> [reason]',
75
+ en: 'Rollback principle and blacklist pattern <principle-id> [reason]'
76
+ },
77
+ 'pd-rollback': {
78
+ zh: '回滚情绪事件惩罚 <event-id>|last',
79
+ en: 'Rollback empathy event penalty <event-id>|last'
80
+ },
81
+ 'pd-export': {
82
+ zh: '导出 analytics 或纠错样本 [analytics|corrections --redacted]',
83
+ en: 'Export analytics or correction samples [analytics|corrections --redacted]'
84
+ },
85
+ 'pd-samples': {
86
+ zh: '查看或审核纠错样本 [review approve|reject <sample-id> [note]]',
87
+ en: 'List or review correction samples [review approve|reject <sample-id> [note]]'
60
88
  }
61
89
  };
62
90
  /**