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
@@ -0,0 +1,369 @@
1
+ import * as crypto from 'crypto';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { withLock } from '../utils/file-lock.js';
5
+ import { PathResolver } from './path-resolver.js';
6
+ import { SystemLogger } from './system-logger.js';
7
+ import { shouldIgnorePainProtocolText } from './dictionary.js';
8
+ import { TrajectoryRegistry } from './trajectory.js';
9
+ const PROBATION_SUCCESS_THRESHOLD = 3;
10
+ const CIRCUIT_BREAKER_THRESHOLD = 3;
11
+ const PROBATION_MAX_AGE_DAYS = 30;
12
+ export class EvolutionReducerImpl {
13
+ streamPath;
14
+ lockTargetPath;
15
+ blacklistPath;
16
+ workspaceDir;
17
+ memoryEvents = [];
18
+ principles = new Map();
19
+ failureStreak = new Map();
20
+ lastPromotedAt = null;
21
+ isReplaying = false;
22
+ constructor(opts) {
23
+ this.workspaceDir = opts.workspaceDir;
24
+ const resolver = new PathResolver({ workspaceDir: opts.workspaceDir });
25
+ this.streamPath = resolver.resolve('EVOLUTION_STREAM');
26
+ this.lockTargetPath = resolver.resolve('EVOLUTION_LOCK');
27
+ this.blacklistPath = resolver.resolve('PRINCIPLE_BLACKLIST');
28
+ this.ensureDirs();
29
+ this.loadFromStream();
30
+ this.sweepExpiredProbation();
31
+ }
32
+ emit(event) {
33
+ this.emitSync(event);
34
+ }
35
+ emitSync(event) {
36
+ withLock(this.lockTargetPath, () => {
37
+ fs.appendFileSync(this.streamPath, `${JSON.stringify(event)}\n`, 'utf8');
38
+ }, { lockStaleMs: 15000 });
39
+ this.applyEvent(event);
40
+ if (event.type !== 'pain_detected') {
41
+ try {
42
+ TrajectoryRegistry.use(this.workspaceDir, (trajectory) => {
43
+ trajectory.recordPrincipleEvent({
44
+ principleId: 'principleId' in event.data && typeof event.data.principleId === 'string' ? event.data.principleId : null,
45
+ eventType: event.type,
46
+ payload: event.data,
47
+ createdAt: event.ts,
48
+ });
49
+ });
50
+ }
51
+ catch {
52
+ // Keep evolution loop resilient if trajectory storage is unavailable.
53
+ }
54
+ }
55
+ // Performance: sweepExpiredProbation() moved to getProbationPrinciples() for lazy cleanup
56
+ }
57
+ getEventLog() {
58
+ return [...this.memoryEvents];
59
+ }
60
+ getCandidatePrinciples() {
61
+ return this.getByStatus('candidate');
62
+ }
63
+ getProbationPrinciples() {
64
+ // Lazy cleanup: sweep expired probation principles on access
65
+ this.sweepExpiredProbation();
66
+ return this.getByStatus('probation');
67
+ }
68
+ getActivePrinciples() {
69
+ return this.getByStatus('active');
70
+ }
71
+ getPrincipleById(id) {
72
+ return this.principles.get(id) ?? null;
73
+ }
74
+ promote(principleId, reason = 'manual') {
75
+ const p = this.principles.get(principleId);
76
+ if (!p || p.status === 'active' || p.status === 'deprecated')
77
+ return;
78
+ const nextStatus = p.status === 'candidate' ? 'probation' : 'active';
79
+ const event = {
80
+ ts: new Date().toISOString(),
81
+ type: 'principle_promoted',
82
+ data: {
83
+ principleId,
84
+ from: p.status,
85
+ to: nextStatus,
86
+ reason,
87
+ successCount: p.validation.successCount,
88
+ },
89
+ };
90
+ this.emitSync(event);
91
+ }
92
+ deprecate(principleId, reason) {
93
+ const p = this.principles.get(principleId);
94
+ if (!p || p.status === 'deprecated')
95
+ return;
96
+ this.emitSync({
97
+ ts: new Date().toISOString(),
98
+ type: 'principle_deprecated',
99
+ data: {
100
+ principleId,
101
+ reason,
102
+ triggeredBy: 'manual',
103
+ },
104
+ });
105
+ }
106
+ rollbackPrinciple(principleId, reason) {
107
+ const p = this.principles.get(principleId);
108
+ if (!p)
109
+ return;
110
+ this.emitSync({
111
+ ts: new Date().toISOString(),
112
+ type: 'principle_rolled_back',
113
+ data: {
114
+ principleId,
115
+ reason,
116
+ triggeredBy: 'user_command',
117
+ blacklistPattern: p.trigger,
118
+ relatedPainId: p.source.painId,
119
+ },
120
+ });
121
+ }
122
+ recordProbationFeedback(principleId, success) {
123
+ const p = this.principles.get(principleId);
124
+ if (!p || p.status !== 'probation')
125
+ return;
126
+ if (success) {
127
+ p.validation.successCount += 1;
128
+ p.feedbackScore += 10;
129
+ if (p.validation.successCount >= PROBATION_SUCCESS_THRESHOLD) {
130
+ this.promote(principleId, 'auto_threshold');
131
+ }
132
+ return;
133
+ }
134
+ p.validation.conflictCount += 1;
135
+ if (p.validation.conflictCount >= 1) {
136
+ this.deprecate(principleId, 'conflict_detected');
137
+ }
138
+ }
139
+ getStats() {
140
+ return {
141
+ candidateCount: this.getCandidatePrinciples().length,
142
+ probationCount: this.getProbationPrinciples().length,
143
+ activeCount: this.getActivePrinciples().length,
144
+ deprecatedCount: this.getByStatus('deprecated').length,
145
+ lastPromotedAt: this.lastPromotedAt,
146
+ };
147
+ }
148
+ ensureDirs() {
149
+ fs.mkdirSync(path.dirname(this.streamPath), { recursive: true });
150
+ fs.mkdirSync(path.dirname(this.lockTargetPath), { recursive: true });
151
+ fs.mkdirSync(path.dirname(this.blacklistPath), { recursive: true });
152
+ }
153
+ loadFromStream() {
154
+ if (!fs.existsSync(this.streamPath))
155
+ return;
156
+ const raw = fs.readFileSync(this.streamPath, 'utf8').trim();
157
+ if (!raw)
158
+ return;
159
+ this.isReplaying = true;
160
+ for (const line of raw.split('\n')) {
161
+ try {
162
+ const event = JSON.parse(line);
163
+ this.applyEvent(event);
164
+ }
165
+ catch (e) {
166
+ SystemLogger.log(this.workspaceDir, 'EVOLUTION_WARN', `skip malformed event line: ${String(e)}`);
167
+ }
168
+ }
169
+ this.isReplaying = false;
170
+ }
171
+ applyEvent(event) {
172
+ this.memoryEvents.push(event);
173
+ switch (event.type) {
174
+ case 'pain_detected':
175
+ this.updateFailureStreakFromPain(event.data);
176
+ if (!this.isReplaying) {
177
+ this.onPainDetected(event.data, event.ts);
178
+ }
179
+ return;
180
+ case 'candidate_created':
181
+ this.onCandidateCreated(event.data, event.ts);
182
+ return;
183
+ case 'principle_promoted':
184
+ this.onPrinciplePromoted(event.data, event.ts);
185
+ return;
186
+ case 'principle_deprecated':
187
+ this.onPrincipleDeprecated(event.data, event.ts);
188
+ return;
189
+ case 'principle_rolled_back':
190
+ this.onPrincipleRolledBack(event.data, event.ts);
191
+ return;
192
+ case 'circuit_breaker_opened':
193
+ case 'legacy_import':
194
+ return;
195
+ default:
196
+ return;
197
+ }
198
+ }
199
+ onCandidateCreated(data, ts) {
200
+ const existing = this.principles.get(data.principleId);
201
+ if (existing) {
202
+ existing.status = 'candidate';
203
+ return;
204
+ }
205
+ const principle = {
206
+ id: data.principleId,
207
+ version: 1,
208
+ text: `When ${data.trigger}, then ${data.action}.`,
209
+ source: {
210
+ painId: data.painId,
211
+ painType: 'tool_failure',
212
+ timestamp: ts,
213
+ },
214
+ trigger: data.trigger,
215
+ action: data.action,
216
+ contextTags: [],
217
+ validation: { successCount: 0, conflictCount: 0 },
218
+ status: 'candidate',
219
+ feedbackScore: 0,
220
+ usageCount: 0,
221
+ createdAt: ts,
222
+ };
223
+ this.principles.set(principle.id, principle);
224
+ }
225
+ onPrinciplePromoted(data, ts) {
226
+ const p = this.principles.get(data.principleId);
227
+ if (!p)
228
+ return;
229
+ p.status = data.to;
230
+ if (data.to === 'active') {
231
+ p.activatedAt = ts;
232
+ }
233
+ this.lastPromotedAt = ts;
234
+ }
235
+ onPrincipleDeprecated(data, ts) {
236
+ const p = this.principles.get(data.principleId);
237
+ if (!p)
238
+ return;
239
+ p.status = 'deprecated';
240
+ p.deprecatedAt = ts;
241
+ }
242
+ onPrincipleRolledBack(data, ts) {
243
+ const p = this.principles.get(data.principleId);
244
+ if (p) {
245
+ p.status = 'deprecated';
246
+ p.deprecatedAt = ts;
247
+ }
248
+ this.persistBlacklist({
249
+ painId: data.relatedPainId,
250
+ pattern: data.blacklistPattern,
251
+ reason: data.reason,
252
+ rolledBackAt: ts,
253
+ });
254
+ }
255
+ onPainDetected(data, eventTs) {
256
+ const trigger = String(data.reason ?? data.source ?? 'unknown trigger');
257
+ const action = `Prevent recurrence for: ${String(data.source ?? 'unknown')}`;
258
+ // Defense in depth: protocol/system tokens must never become principles,
259
+ // even if a pain_detected event is emitted from a new callsite in the future.
260
+ if (shouldIgnorePainProtocolText(trigger)) {
261
+ return;
262
+ }
263
+ if (this.isBlacklisted(data.painId, trigger)) {
264
+ return;
265
+ }
266
+ const principleId = this.nextPrincipleId();
267
+ const principle = {
268
+ id: principleId,
269
+ version: 1,
270
+ text: `When ${trigger}, then ${action}.`,
271
+ source: {
272
+ painId: data.painId,
273
+ painType: data.painType,
274
+ timestamp: eventTs,
275
+ },
276
+ trigger,
277
+ action,
278
+ contextTags: [data.source],
279
+ validation: { successCount: 0, conflictCount: 0 },
280
+ status: 'candidate',
281
+ feedbackScore: 0,
282
+ usageCount: 0,
283
+ createdAt: eventTs,
284
+ };
285
+ this.principles.set(principleId, principle);
286
+ this.emitSync({
287
+ ts: new Date().toISOString(),
288
+ type: 'candidate_created',
289
+ data: {
290
+ painId: principle.source.painId,
291
+ principleId,
292
+ trigger,
293
+ action,
294
+ status: 'candidate',
295
+ },
296
+ });
297
+ this.promote(principleId, 'auto_from_pain');
298
+ if (data.painType === 'subagent_error') {
299
+ const key = String(data.taskId ?? data.source ?? 'subagent');
300
+ const next = this.failureStreak.get(key) ?? 0;
301
+ if (next >= CIRCUIT_BREAKER_THRESHOLD) {
302
+ const nextRetryAt = new Date(Date.now() + 15 * 60 * 1000).toISOString();
303
+ this.emitSync({
304
+ ts: new Date().toISOString(),
305
+ type: 'circuit_breaker_opened',
306
+ data: {
307
+ taskId: key,
308
+ painId: principle.source.painId,
309
+ failCount: next,
310
+ reason: 'Max retries exceeded',
311
+ requireHuman: true,
312
+ nextRetryAt,
313
+ },
314
+ });
315
+ }
316
+ }
317
+ }
318
+ updateFailureStreakFromPain(data) {
319
+ if (data.painType !== 'subagent_error')
320
+ return;
321
+ const key = String(data.taskId ?? data.source ?? 'subagent');
322
+ const next = (this.failureStreak.get(key) ?? 0) + 1;
323
+ this.failureStreak.set(key, next);
324
+ }
325
+ nextPrincipleId() {
326
+ const ids = [...this.principles.keys()]
327
+ .map((id) => Number(id.replace(/^P_/, '')))
328
+ .filter((n) => Number.isFinite(n));
329
+ const next = (ids.length ? Math.max(...ids) : 0) + 1;
330
+ return `P_${String(next).padStart(3, '0')}`;
331
+ }
332
+ getByStatus(status) {
333
+ return [...this.principles.values()].filter((p) => p.status === status);
334
+ }
335
+ sweepExpiredProbation() {
336
+ const now = Date.now();
337
+ const maxAgeMs = PROBATION_MAX_AGE_DAYS * 24 * 60 * 60 * 1000;
338
+ // Use getByStatus directly to avoid infinite recursion with getProbationPrinciples()
339
+ for (const p of this.getByStatus('probation')) {
340
+ const age = now - new Date(p.createdAt).getTime();
341
+ if (age > maxAgeMs) {
342
+ this.deprecate(p.id, 'probation_expired');
343
+ }
344
+ }
345
+ }
346
+ persistBlacklist(entry) {
347
+ const list = this.loadBlacklist();
348
+ list.push(entry);
349
+ fs.writeFileSync(this.blacklistPath, JSON.stringify(list, null, 2), 'utf8');
350
+ }
351
+ loadBlacklist() {
352
+ if (!fs.existsSync(this.blacklistPath))
353
+ return [];
354
+ try {
355
+ return JSON.parse(fs.readFileSync(this.blacklistPath, 'utf8'));
356
+ }
357
+ catch (e) {
358
+ SystemLogger.log(this.workspaceDir, 'EVOLUTION_WARN', `failed to parse blacklist: ${String(e)}`);
359
+ return [];
360
+ }
361
+ }
362
+ isBlacklisted(painId, trigger) {
363
+ return this.loadBlacklist().some((entry) => (entry.painId && entry.painId === painId) ||
364
+ (entry.pattern && trigger.includes(entry.pattern)));
365
+ }
366
+ }
367
+ export function stableContentHash(input) {
368
+ return crypto.createHash('sha1').update(input).digest('hex');
369
+ }
@@ -124,3 +124,106 @@ export interface TierPromotionEvent {
124
124
  timestamp: string;
125
125
  newPermissions: TierPermissions;
126
126
  }
127
+ export type PrincipleStatus = 'candidate' | 'probation' | 'active' | 'deprecated';
128
+ export interface Principle {
129
+ id: string;
130
+ version: number;
131
+ text: string;
132
+ source: {
133
+ painId: string;
134
+ painType: 'tool_failure' | 'subagent_error' | 'user_frustration';
135
+ timestamp: string;
136
+ };
137
+ trigger: string;
138
+ action: string;
139
+ guardrails?: string[];
140
+ contextTags: string[];
141
+ validation: {
142
+ successCount: number;
143
+ conflictCount: number;
144
+ };
145
+ status: PrincipleStatus;
146
+ feedbackScore: number;
147
+ usageCount: number;
148
+ createdAt: string;
149
+ activatedAt?: string;
150
+ deprecatedAt?: string;
151
+ }
152
+ export type EvolutionLoopEventType = 'pain_detected' | 'candidate_created' | 'principle_promoted' | 'principle_deprecated' | 'principle_rolled_back' | 'circuit_breaker_opened' | 'legacy_import';
153
+ export interface PainDetectedData {
154
+ painId: string;
155
+ painType: 'tool_failure' | 'subagent_error' | 'user_frustration';
156
+ source: string;
157
+ reason: string;
158
+ score?: number;
159
+ sessionId?: string;
160
+ taskId?: string;
161
+ }
162
+ export interface CandidateCreatedData {
163
+ painId: string;
164
+ principleId: string;
165
+ trigger: string;
166
+ action: string;
167
+ status: 'candidate';
168
+ }
169
+ export interface PrinciplePromotedData {
170
+ principleId: string;
171
+ from: 'candidate' | 'probation';
172
+ to: 'probation' | 'active';
173
+ reason: string;
174
+ successCount?: number;
175
+ }
176
+ export interface PrincipleDeprecatedData {
177
+ principleId: string;
178
+ reason: string;
179
+ triggeredBy: 'auto' | 'manual';
180
+ }
181
+ export interface PrincipleRolledBackData {
182
+ principleId: string;
183
+ reason: string;
184
+ triggeredBy: 'user_command' | 'auto_conflict';
185
+ blacklistPattern?: string;
186
+ relatedPainId?: string;
187
+ }
188
+ export interface CircuitBreakerOpenedData {
189
+ taskId: string;
190
+ painId: string;
191
+ failCount: number;
192
+ reason: string;
193
+ requireHuman: boolean;
194
+ nextRetryAt?: string;
195
+ }
196
+ export interface LegacyImportData {
197
+ sourceFile: string;
198
+ content: string;
199
+ contentHash?: string;
200
+ }
201
+ export type EvolutionLoopEvent = {
202
+ ts: string;
203
+ type: 'pain_detected';
204
+ data: PainDetectedData;
205
+ } | {
206
+ ts: string;
207
+ type: 'candidate_created';
208
+ data: CandidateCreatedData;
209
+ } | {
210
+ ts: string;
211
+ type: 'principle_promoted';
212
+ data: PrinciplePromotedData;
213
+ } | {
214
+ ts: string;
215
+ type: 'principle_deprecated';
216
+ data: PrincipleDeprecatedData;
217
+ } | {
218
+ ts: string;
219
+ type: 'principle_rolled_back';
220
+ data: PrincipleRolledBackData;
221
+ } | {
222
+ ts: string;
223
+ type: 'circuit_breaker_opened';
224
+ data: CircuitBreakerOpenedData;
225
+ } | {
226
+ ts: string;
227
+ type: 'legacy_import';
228
+ data: LegacyImportData;
229
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * CURRENT_FOCUS 历史版本管理
3
+ *
4
+ * 功能:
5
+ * - 压缩时备份当前版本到历史目录
6
+ * - 清理过期历史版本
7
+ * - 读取历史版本(用于 full 模式)
8
+ */
9
+ /**
10
+ * 获取历史目录路径
11
+ */
12
+ export declare function getHistoryDir(focusPath: string): string;
13
+ /**
14
+ * 从 CURRENT_FOCUS.md 提取版本号
15
+ * 支持整数和小数版本(如 v1, v1.1, v1.2)
16
+ */
17
+ export declare function extractVersion(content: string): string;
18
+ /**
19
+ * 从 CURRENT_FOCUS.md 提取更新日期
20
+ */
21
+ export declare function extractDate(content: string): string;
22
+ /**
23
+ * 备份当前版本到历史目录
24
+ *
25
+ * @param focusPath CURRENT_FOCUS.md 的完整路径
26
+ * @param content 当前内容
27
+ * @returns 备份文件路径,失败返回 null
28
+ */
29
+ export declare function backupToHistory(focusPath: string, content: string): string | null;
30
+ /**
31
+ * 清理过期历史版本
32
+ *
33
+ * @param focusPath CURRENT_FOCUS.md 的完整路径
34
+ * @param maxFiles 最大保留数量
35
+ */
36
+ export declare function cleanupHistory(focusPath: string, maxFiles?: number): void;
37
+ /**
38
+ * 获取历史版本列表
39
+ *
40
+ * @param focusPath CURRENT_FOCUS.md 的完整路径
41
+ * @param count 获取数量
42
+ * @returns 历史版本内容数组(按时间倒序)
43
+ */
44
+ export declare function getHistoryVersions(focusPath: string, count?: number): string[];
45
+ /**
46
+ * 压缩 CURRENT_FOCUS.md
47
+ *
48
+ * @param focusPath CURRENT_FOCUS.md 的完整路径
49
+ * @param newContent 新内容
50
+ * @returns 压缩后的信息
51
+ */
52
+ export declare function compressFocus(focusPath: string, newContent: string): {
53
+ backupPath: string | null;
54
+ cleanedCount: number;
55
+ };
56
+ /**
57
+ * 智能摘要提取
58
+ *
59
+ * 优先提取关键章节,确保不丢失重要信息
60
+ * 对于非结构化内容,回退到简单的行截取
61
+ *
62
+ * @param content CURRENT_FOCUS.md 内容
63
+ * @param maxLines 最大行数
64
+ */
65
+ export declare function extractSummary(content: string, maxLines?: number): string;