principles-disciple 1.11.0 → 1.13.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 (233) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +66 -0
  3. package/esbuild.config.js +1 -1
  4. package/openclaw.plugin.json +4 -4
  5. package/package.json +2 -3
  6. package/run-nocturnal.mjs +30 -0
  7. package/scripts/db-migrate.mjs +170 -0
  8. package/scripts/sync-plugin.mjs +250 -6
  9. package/src/commands/archive-impl.ts +136 -0
  10. package/src/commands/capabilities.ts +4 -2
  11. package/src/commands/context.ts +5 -1
  12. package/src/commands/disable-impl.ts +151 -0
  13. package/src/commands/evolution-status.ts +64 -19
  14. package/src/commands/export.ts +8 -6
  15. package/src/commands/focus.ts +8 -20
  16. package/src/commands/nocturnal-review.ts +5 -7
  17. package/src/commands/nocturnal-rollout.ts +1 -12
  18. package/src/commands/nocturnal-train.ts +21 -47
  19. package/src/commands/pain.ts +10 -5
  20. package/src/commands/principle-rollback.ts +4 -2
  21. package/src/commands/promote-impl.ts +274 -0
  22. package/src/commands/rollback-impl.ts +234 -0
  23. package/src/commands/rollback.ts +6 -3
  24. package/src/commands/samples.ts +2 -0
  25. package/src/commands/thinking-os.ts +3 -4
  26. package/src/commands/workflow-debug.ts +2 -1
  27. package/src/config/errors.ts +1 -0
  28. package/src/core/AGENTS.md +34 -0
  29. package/src/core/adaptive-thresholds.ts +4 -3
  30. package/src/core/code-implementation-storage.ts +241 -0
  31. package/src/core/config.ts +5 -2
  32. package/src/core/control-ui-db.ts +29 -10
  33. package/src/core/detection-funnel.ts +12 -7
  34. package/src/core/diagnostician-task-store.ts +156 -0
  35. package/src/core/dictionary.ts +4 -4
  36. package/src/core/empathy-keyword-matcher.ts +7 -3
  37. package/src/core/empathy-types.ts +13 -2
  38. package/src/core/event-log.ts +14 -6
  39. package/src/core/evolution-engine.ts +27 -31
  40. package/src/core/evolution-logger.ts +3 -2
  41. package/src/core/evolution-reducer.ts +110 -31
  42. package/src/core/evolution-types.ts +10 -0
  43. package/src/core/external-training-contract.ts +1 -0
  44. package/src/core/focus-history.ts +38 -24
  45. package/src/core/hygiene/tracker.ts +10 -6
  46. package/src/core/init.ts +5 -2
  47. package/src/core/migration.ts +3 -3
  48. package/src/core/model-deployment-registry.ts +6 -4
  49. package/src/core/model-training-registry.ts +5 -3
  50. package/src/core/nocturnal-arbiter.ts +13 -14
  51. package/src/core/nocturnal-artifact-lineage.ts +117 -0
  52. package/src/core/nocturnal-artificer.ts +257 -0
  53. package/src/core/nocturnal-candidate-scoring.ts +4 -2
  54. package/src/core/nocturnal-compliance.ts +67 -19
  55. package/src/core/nocturnal-dataset.ts +95 -2
  56. package/src/core/nocturnal-executability.ts +2 -3
  57. package/src/core/nocturnal-export.ts +6 -3
  58. package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
  59. package/src/core/nocturnal-trajectory-extractor.ts +10 -3
  60. package/src/core/nocturnal-trinity.ts +319 -61
  61. package/src/core/pain-context-extractor.ts +29 -15
  62. package/src/core/pain.ts +7 -5
  63. package/src/core/path-resolver.ts +16 -15
  64. package/src/core/paths.ts +2 -1
  65. package/src/core/pd-task-reconciler.ts +463 -0
  66. package/src/core/pd-task-service.ts +42 -0
  67. package/src/core/pd-task-store.ts +77 -0
  68. package/src/core/pd-task-types.ts +128 -0
  69. package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
  70. package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
  71. package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
  72. package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
  73. package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
  74. package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
  75. package/src/core/principle-training-state.ts +95 -370
  76. package/src/core/principle-tree-ledger.ts +733 -0
  77. package/src/core/principle-tree-migration.ts +195 -0
  78. package/src/core/profile.ts +3 -1
  79. package/src/core/promotion-gate.ts +14 -18
  80. package/src/core/replay-engine.ts +562 -0
  81. package/src/core/risk-calculator.ts +6 -4
  82. package/src/core/rule-host-helpers.ts +39 -0
  83. package/src/core/rule-host-types.ts +82 -0
  84. package/src/core/rule-host.ts +245 -0
  85. package/src/core/rule-implementation-runtime.ts +38 -0
  86. package/src/core/schema/db-types.ts +16 -0
  87. package/src/core/schema/index.ts +26 -0
  88. package/src/core/schema/migration-runner.ts +207 -0
  89. package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
  90. package/src/core/schema/migrations/002-init-central.ts +122 -0
  91. package/src/core/schema/migrations/003-init-workflow.ts +55 -0
  92. package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
  93. package/src/core/schema/migrations/index.ts +31 -0
  94. package/src/core/schema/schema-definitions.ts +650 -0
  95. package/src/core/session-tracker.ts +6 -4
  96. package/src/core/shadow-observation-registry.ts +6 -3
  97. package/src/core/system-logger.ts +2 -2
  98. package/src/core/thinking-models.ts +182 -46
  99. package/src/core/thinking-os-parser.ts +156 -0
  100. package/src/core/training-program.ts +7 -7
  101. package/src/core/trajectory.ts +42 -36
  102. package/src/core/workspace-context.ts +77 -11
  103. package/src/core/workspace-dir-validation.ts +152 -0
  104. package/src/hooks/AGENTS.md +31 -0
  105. package/src/hooks/bash-risk.ts +3 -1
  106. package/src/hooks/edit-verification.ts +9 -5
  107. package/src/hooks/gate-block-helper.ts +5 -1
  108. package/src/hooks/gate.ts +152 -5
  109. package/src/hooks/gfi-gate.ts +9 -2
  110. package/src/hooks/lifecycle-routing.ts +124 -0
  111. package/src/hooks/lifecycle.ts +12 -12
  112. package/src/hooks/llm.ts +17 -109
  113. package/src/hooks/message-sanitize.ts +5 -3
  114. package/src/hooks/pain.ts +19 -15
  115. package/src/hooks/progressive-trust-gate.ts +7 -1
  116. package/src/hooks/prompt.ts +169 -60
  117. package/src/hooks/subagent.ts +5 -4
  118. package/src/hooks/thinking-checkpoint.ts +2 -0
  119. package/src/hooks/trajectory-collector.ts +15 -12
  120. package/src/http/principles-console-route.ts +31 -68
  121. package/src/i18n/commands.ts +2 -2
  122. package/src/index.ts +130 -40
  123. package/src/service/central-database.ts +131 -43
  124. package/src/service/central-health-service.ts +47 -0
  125. package/src/service/central-overview-service.ts +135 -0
  126. package/src/service/central-sync-service.ts +87 -0
  127. package/src/service/control-ui-query-service.ts +46 -36
  128. package/src/service/event-log-auditor.ts +261 -0
  129. package/src/service/evolution-query-service.ts +23 -22
  130. package/src/service/evolution-worker.ts +565 -261
  131. package/src/service/health-query-service.ts +213 -36
  132. package/src/service/nocturnal-runtime.ts +8 -4
  133. package/src/service/nocturnal-service.ts +503 -59
  134. package/src/service/nocturnal-target-selector.ts +5 -7
  135. package/src/service/runtime-summary-service.ts +2 -1
  136. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
  137. package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
  138. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
  139. package/src/service/subagent-workflow/index.ts +2 -0
  140. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +169 -284
  141. package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
  142. package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
  143. package/src/service/subagent-workflow/types.ts +9 -4
  144. package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
  145. package/src/service/subagent-workflow/workflow-store.ts +71 -11
  146. package/src/service/trajectory-service.ts +2 -1
  147. package/src/tools/critique-prompt.ts +1 -1
  148. package/src/tools/deep-reflect.ts +175 -209
  149. package/src/tools/model-index.ts +2 -1
  150. package/src/types/event-types.ts +2 -2
  151. package/src/types/principle-tree-schema.ts +29 -23
  152. package/src/utils/file-lock.ts +5 -3
  153. package/src/utils/io.ts +5 -2
  154. package/src/utils/nlp.ts +5 -46
  155. package/src/utils/node-vm-polyfill.ts +11 -0
  156. package/src/utils/plugin-logger.ts +2 -0
  157. package/src/utils/retry.ts +572 -0
  158. package/src/utils/subagent-probe.ts +1 -1
  159. package/templates/langs/en/core/AGENTS.md +0 -13
  160. package/templates/langs/en/core/SOUL.md +1 -31
  161. package/templates/langs/en/core/TOOLS.md +0 -4
  162. package/templates/langs/en/principles/THINKING_OS.md +77 -0
  163. package/templates/langs/en/skills/admin/SKILL.md +0 -1
  164. package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
  165. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
  166. package/templates/langs/zh/core/AGENTS.md +0 -22
  167. package/templates/langs/zh/core/SOUL.md +1 -31
  168. package/templates/langs/zh/core/TOOLS.md +0 -4
  169. package/templates/langs/zh/principles/THINKING_OS.md +77 -0
  170. package/templates/langs/zh/skills/admin/SKILL.md +0 -1
  171. package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
  172. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
  173. package/tests/commands/evolution-status.test.ts +119 -0
  174. package/tests/commands/implementation-lifecycle.test.ts +362 -0
  175. package/tests/core/code-implementation-storage.test.ts +398 -0
  176. package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
  177. package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
  178. package/tests/core/nocturnal-artificer.test.ts +241 -0
  179. package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
  180. package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
  181. package/tests/core/pd-task-store.test.ts +126 -0
  182. package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
  183. package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
  184. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
  185. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
  186. package/tests/core/principle-training-state.test.ts +228 -1
  187. package/tests/core/principle-tree-ledger.test.ts +423 -0
  188. package/tests/core/regression-v1-9-1.test.ts +265 -0
  189. package/tests/core/replay-engine.test.ts +234 -0
  190. package/tests/core/rule-host-helpers.test.ts +120 -0
  191. package/tests/core/rule-host.test.ts +389 -0
  192. package/tests/core/rule-implementation-runtime.test.ts +64 -0
  193. package/tests/core/workspace-context.test.ts +53 -0
  194. package/tests/core/workspace-dir-validation.test.ts +272 -0
  195. package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
  196. package/tests/hooks/pain.test.ts +74 -10
  197. package/tests/hooks/prompt.test.ts +63 -1
  198. package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
  199. package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
  200. package/tests/service/data-endpoints-regression.test.ts +834 -0
  201. package/tests/service/evolution-worker.test.ts +0 -123
  202. package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
  203. package/tests/utils/nlp.test.ts +1 -19
  204. package/tests/utils/retry.test.ts +327 -0
  205. package/ui/src/App.tsx +1 -1
  206. package/ui/src/api.ts +4 -0
  207. package/ui/src/charts.tsx +366 -0
  208. package/ui/src/components/WorkspaceConfig.tsx +107 -75
  209. package/ui/src/i18n/ui.ts +89 -31
  210. package/ui/src/pages/EvolutionPage.tsx +1 -1
  211. package/ui/src/pages/OverviewPage.tsx +441 -81
  212. package/ui/src/pages/ThinkingModelsPage.tsx +287 -69
  213. package/ui/src/styles.css +43 -0
  214. package/ui/src/types.ts +17 -1
  215. package/src/agents/nocturnal-dreamer.md +0 -152
  216. package/src/agents/nocturnal-philosopher.md +0 -138
  217. package/src/agents/nocturnal-reflector.md +0 -126
  218. package/src/agents/nocturnal-scribe.md +0 -164
  219. package/templates/workspace/.principles/00-kernel.md +0 -51
  220. package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
  221. package/templates/workspace/.principles/PRINCIPLES.md +0 -20
  222. package/templates/workspace/.principles/PROFILE.json +0 -54
  223. package/templates/workspace/.principles/PROFILE.schema.json +0 -56
  224. package/templates/workspace/.principles/THINKING_OS.md +0 -64
  225. package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
  226. package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
  227. package/templates/workspace/.principles/models/_INDEX.md +0 -27
  228. package/templates/workspace/.principles/models/first_principles.md +0 -62
  229. package/templates/workspace/.principles/models/marketing_4p.md +0 -52
  230. package/templates/workspace/.principles/models/porter_five.md +0 -63
  231. package/templates/workspace/.principles/models/swot.md +0 -60
  232. package/templates/workspace/.principles/models/user_story_map.md +0 -63
  233. package/templates/workspace/.state/WORKBOARD.json +0 -4
@@ -20,19 +20,29 @@ import type {
20
20
  PrincipleSuggestedRule,
21
21
  } from './evolution-types.js';
22
22
  import { isCompleteDetectorMetadata } from './evolution-types.js';
23
+ import { updateTrainingStore, createPrinciple } from './principle-tree-ledger.js';
24
+ import type { LedgerPrinciple } from './principle-tree-ledger.js';
23
25
 
26
+
24
27
  export interface EvolutionReducer {
25
- emit(event: EvolutionLoopEvent): void;
26
- emitSync(event: EvolutionLoopEvent): void;
28
+
29
+ emit(_event: EvolutionLoopEvent): void;
30
+
31
+ emitSync(_event: EvolutionLoopEvent): void;
27
32
  getEventLog(): EvolutionLoopEvent[];
28
33
  getCandidatePrinciples(): Principle[];
29
34
  getProbationPrinciples(): Principle[];
30
35
  getActivePrinciples(): Principle[];
31
- getPrincipleById(id: string): Principle | null;
32
- promote(principleId: string, reason?: string): void;
33
- deprecate(principleId: string, reason: string): void;
34
- rollbackPrinciple(principleId: string, reason: string): void;
35
- recordProbationFeedback(principleId: string, success: boolean): void;
36
+
37
+ getPrincipleById(_id: string): Principle | null;
38
+
39
+ promote(_principleId: string, _reason?: string): void;
40
+
41
+ deprecate(_principleId: string, _reason: string): void;
42
+
43
+ rollbackPrinciple(_principleId: string, _reason: string): void;
44
+
45
+ recordProbationFeedback(_principleId: string, _success: boolean): void;
36
46
  /**
37
47
  * Creates a new principle with generalized trigger/action from diagnostician.
38
48
  * Called after diagnostician analysis to create principle directly.
@@ -47,6 +57,8 @@ export interface EvolutionReducer {
47
57
  detectorMetadata?: PrincipleDetectorSpec;
48
58
  /** Highly abstracted principle text — if absent, falls back to action-based title */
49
59
  abstractedPrinciple?: string;
60
+ /** Associated core axiom (e.g., "T-01") */
61
+ coreAxiomId?: string;
50
62
  // ── Principle-Tree metadata (Phase 1, optional) ──
51
63
  priority?: 'P0' | 'P1' | 'P2';
52
64
  scope?: 'general' | 'domain';
@@ -61,6 +73,7 @@ export interface EvolutionReducer {
61
73
  lastPromotedAt: string | null;
62
74
  };
63
75
  }
76
+
64
77
 
65
78
  const PROBATION_SUCCESS_THRESHOLD = 3;
66
79
  const CIRCUIT_BREAKER_THRESHOLD = 3;
@@ -72,14 +85,16 @@ export class EvolutionReducerImpl implements EvolutionReducer {
72
85
  private readonly blacklistPath: string;
73
86
  private readonly principlesPath: string;
74
87
  private readonly workspaceDir: string;
88
+ private readonly stateDir: string | undefined;
75
89
  private readonly memoryEvents: EvolutionLoopEvent[] = [];
76
90
  private readonly principles = new Map<string, Principle>();
77
91
  private readonly failureStreak = new Map<string, number>();
78
92
  private lastPromotedAt: string | null = null;
79
93
  private isReplaying = false;
80
94
 
81
- constructor(opts: { workspaceDir: string }) {
95
+ constructor(opts: { workspaceDir: string; stateDir?: string }) {
82
96
  this.workspaceDir = opts.workspaceDir;
97
+ this.stateDir = opts.stateDir;
83
98
  const resolver = new PathResolver({ workspaceDir: opts.workspaceDir });
84
99
  this.streamPath = resolver.resolve('EVOLUTION_STREAM');
85
100
  this.lockTargetPath = resolver.resolve('EVOLUTION_LOCK');
@@ -222,6 +237,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
222
237
  evaluability?: PrincipleEvaluatorLevel;
223
238
  detectorMetadata?: PrincipleDetectorSpec;
224
239
  abstractedPrinciple?: string;
240
+ coreAxiomId?: string;
225
241
  priority?: 'P0' | 'P1' | 'P2';
226
242
  scope?: 'general' | 'domain';
227
243
  domain?: string;
@@ -237,17 +253,18 @@ export class EvolutionReducerImpl implements EvolutionReducer {
237
253
  return null;
238
254
  }
239
255
 
240
- // Evaluability defaults to 'manual_only' — the only way to get auto-trainable
241
- // is to explicitly provide valid detectorMetadata.
242
- // Enforce: deterministic/weak_heuristic requires complete detectorMetadata to be present.
243
- let evaluability: PrincipleEvaluatorLevel = params.evaluability ?? 'manual_only';
244
- if (evaluability !== 'manual_only' && !isCompleteDetectorMetadata(params.detectorMetadata)) {
256
+ // Evaluability defaults to 'weak_heuristic' — allows basic pattern-matching
257
+ // evaluation without requiring full detectorMetadata.
258
+ // #212: Only 'deterministic' requires complete detectorMetadata.
259
+ // 'weak_heuristic' can work with just the trigger pattern.
260
+ let evaluability: PrincipleEvaluatorLevel = params.evaluability ?? 'weak_heuristic';
261
+ if (evaluability === 'deterministic' && !isCompleteDetectorMetadata(params.detectorMetadata)) {
245
262
  SystemLogger.log(
246
263
  this.workspaceDir,
247
264
  'EVALUABILITY_DOWNGRADED',
248
- `Principle for painId "${params.painId}" requested evaluability="${evaluability}" without detectorMetadata — downgrading to "manual_only". Provide valid detectorMetadata to enable auto-training.`
265
+ `Principle for painId "${params.painId}" requested evaluability="deterministic" without detectorMetadata — downgrading to "weak_heuristic". Provide valid detectorMetadata for deterministic evaluation.`
249
266
  );
250
- evaluability = 'manual_only';
267
+ evaluability = 'weak_heuristic';
251
268
  }
252
269
 
253
270
  // Check if a principle already exists for this painId
@@ -265,16 +282,17 @@ export class EvolutionReducerImpl implements EvolutionReducer {
265
282
  existingPrinciple.version += 1;
266
283
  if (params.evaluability !== undefined) {
267
284
  // Apply normalization (params.evaluability may be invalid without complete metadata)
285
+ // #212: Only 'deterministic' requires detectorMetadata; 'weak_heuristic' does not.
268
286
  const normalizedEvaluability = (() => {
269
- if (params.evaluability === 'manual_only' || isCompleteDetectorMetadata(params.detectorMetadata)) {
270
- return params.evaluability;
287
+ if (params.evaluability === 'deterministic' && !isCompleteDetectorMetadata(params.detectorMetadata)) {
288
+ SystemLogger.log(
289
+ this.workspaceDir,
290
+ 'EVALUABILITY_DOWNGRADED',
291
+ `Principle update for painId "${params.painId}" requested evaluability="deterministic" without detectorMetadata — downgrading to "weak_heuristic".`
292
+ );
293
+ return 'weak_heuristic';
271
294
  }
272
- SystemLogger.log(
273
- this.workspaceDir,
274
- 'EVALUABILITY_DOWNGRADED',
275
- `Principle update for painId "${params.painId}" requested evaluability="${params.evaluability}" without detectorMetadata — downgrading to "manual_only".`
276
- );
277
- return 'manual_only';
295
+ return params.evaluability;
278
296
  })();
279
297
  existingPrinciple.evaluability = normalizedEvaluability;
280
298
  }
@@ -328,6 +346,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
328
346
  ? structuredClone(params.detectorMetadata)
329
347
  : undefined,
330
348
  abstractedPrinciple: params.abstractedPrinciple,
349
+ coreAxiomId: params.coreAxiomId,
331
350
 
332
351
  // ── Principle-Tree metadata (Phase 1, optional) ──
333
352
  priority: params.priority ?? 'P1',
@@ -362,6 +381,61 @@ export class EvolutionReducerImpl implements EvolutionReducer {
362
381
  SystemLogger.log(this.workspaceDir, 'PRINCIPLE_SYNC_WARN', `Principle ${principleId} created in memory but failed to sync to PRINCIPLES.md — manual file check required`);
363
382
  }
364
383
 
384
+ // #204: Write to training store so listEvaluablePrinciples() can find this principle
385
+ // Phase 11: Also write to tree.principles for Rule/Implementation layer support
386
+ if (this.stateDir) {
387
+ try {
388
+ // Determine initial internalization status based on evaluability:
389
+ // - manual_only: prompt_only (can only be evaluated manually)
390
+ // - deterministic/weak_heuristic: needs_training (auto-evaluable, ready for training)
391
+ const initialStatus = evaluability === 'manual_only' ? 'prompt_only' : 'needs_training';
392
+
393
+ updateTrainingStore(this.stateDir, (trainingStore) => {
394
+ trainingStore[principleId] = {
395
+ principleId,
396
+ evaluability,
397
+ internalizationStatus: initialStatus,
398
+ applicableOpportunityCount: 0,
399
+ observedViolationCount: 0,
400
+ complianceRate: 0,
401
+ violationTrend: 0,
402
+ generatedSampleCount: 0,
403
+ approvedSampleCount: 0,
404
+ includedTrainRunIds: [],
405
+ deployedCheckpointIds: [],
406
+ };
407
+ });
408
+ SystemLogger.log(this.workspaceDir, 'TRAINING_STORE_UPDATED', `Principle ${principleId} added to training store with evaluability=${evaluability}, internalizationStatus=${initialStatus}`);
409
+
410
+ // Phase 11: Also create principle in tree.principles for Rule/Implementation layer
411
+ const treePrinciple: LedgerPrinciple = {
412
+ id: principleId,
413
+ version: 1,
414
+ text: principle.text,
415
+ coreAxiomId: principle.coreAxiomId,
416
+ triggerPattern: params.triggerPattern,
417
+ action: params.action,
418
+ status: principle.status,
419
+ priority: principle.priority ?? 'P1',
420
+ scope: principle.scope ?? 'general',
421
+ domain: principle.domain,
422
+ evaluability,
423
+ valueScore: 0,
424
+ adherenceRate: 0,
425
+ painPreventedCount: 0,
426
+ derivedFromPainIds: [params.painId],
427
+ ruleIds: [],
428
+ conflictsWithPrincipleIds: [],
429
+ createdAt: now,
430
+ updatedAt: now,
431
+ };
432
+ createPrinciple(this.stateDir, treePrinciple);
433
+ SystemLogger.log(this.workspaceDir, 'TREE_PRINCIPLE_CREATED', `Principle ${principleId} added to tree.principles`);
434
+ } catch (err) {
435
+ SystemLogger.log(this.workspaceDir, 'TRAINING_STORE_UPDATE_FAILED', `Failed to update training store for ${principleId}: ${String(err)}`);
436
+ }
437
+ }
438
+
365
439
  SystemLogger.log(
366
440
  this.workspaceDir,
367
441
  'PRINCIPLE_CREATED',
@@ -400,7 +474,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
400
474
 
401
475
  const header = `### ${principle.id}:`;
402
476
  const existingIdx = content.indexOf(header);
403
- const formatted = this.formatPrincipleForFile(principle, content);
477
+ const formatted = EvolutionReducerImpl.formatPrincipleForFile(principle, content);
404
478
 
405
479
  if (existingIdx >= 0) {
406
480
  const nextPrincipleRe = /\n### [A-Za-z0-9_-]+:/g;
@@ -423,7 +497,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
423
497
  }
424
498
  }
425
499
 
426
- private detectPrincipleLanguage(content: string): 'zh' | 'en' {
500
+ private static detectPrincipleLanguage(content: string): 'zh' | 'en' {
427
501
  if (!content) return 'zh';
428
502
  const zhMarkers = ['原则', '触发', '必须', '禁止', '验证', '例外', '来源'];
429
503
  const enMarkers = ['Trigger', 'Constraint', 'Must', 'Forbidden', 'Verification', 'Exceptions', 'Source'];
@@ -434,8 +508,8 @@ export class EvolutionReducerImpl implements EvolutionReducer {
434
508
  return zhCount >= enCount ? 'zh' : 'en';
435
509
  }
436
510
 
437
- private formatPrincipleForFile(principle: Principle, existingContent: string): string {
438
- const lang = this.detectPrincipleLanguage(existingContent);
511
+ private static formatPrincipleForFile(principle: Principle, existingContent: string): string {
512
+ const lang = EvolutionReducerImpl.detectPrincipleLanguage(existingContent);
439
513
  const title = principle.abstractedPrinciple
440
514
  ? (principle.abstractedPrinciple.length > 60 ? principle.abstractedPrinciple.slice(0, 57) + '...' : principle.abstractedPrinciple)
441
515
  : (principle.text.length > 60 ? principle.text.slice(0, 57) + '...' : principle.text);
@@ -550,6 +624,9 @@ export class EvolutionReducerImpl implements EvolutionReducer {
550
624
  if (data.abstractedPrinciple) {
551
625
  existing.abstractedPrinciple = data.abstractedPrinciple;
552
626
  }
627
+ if (data.coreAxiomId) {
628
+ existing.coreAxiomId = data.coreAxiomId;
629
+ }
553
630
  return;
554
631
  }
555
632
 
@@ -577,6 +654,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
577
654
  : undefined,
578
655
  // Restore abstractedPrinciple from event if present
579
656
  abstractedPrinciple: data.abstractedPrinciple,
657
+ coreAxiomId: data.coreAxiomId,
580
658
  };
581
659
  this.principles.set(principle.id, principle);
582
660
  }
@@ -612,7 +690,8 @@ export class EvolutionReducerImpl implements EvolutionReducer {
612
690
  });
613
691
  }
614
692
 
615
- private onPainDetected(data: PainDetectedData, eventTs: string): void {
693
+
694
+ private onPainDetected(data: PainDetectedData, _eventTs: string): void {
616
695
  const trigger = String(data.reason ?? data.source ?? 'unknown trigger');
617
696
 
618
697
  // Defense in depth: protocol/system tokens must never become principles,
@@ -703,15 +782,15 @@ export class EvolutionReducerImpl implements EvolutionReducer {
703
782
  fs.writeFileSync(this.blacklistPath, JSON.stringify(list, null, 2), 'utf8');
704
783
  }
705
784
 
706
- private loadBlacklist(): Array<{ painId?: string; pattern?: string; reason: string; rolledBackAt: string }> {
785
+ private loadBlacklist(): { painId?: string; pattern?: string; reason: string; rolledBackAt: string }[] {
707
786
  if (!fs.existsSync(this.blacklistPath)) return [];
708
787
  try {
709
- return JSON.parse(fs.readFileSync(this.blacklistPath, 'utf8')) as Array<{
788
+ return JSON.parse(fs.readFileSync(this.blacklistPath, 'utf8')) as {
710
789
  painId?: string;
711
790
  pattern?: string;
712
791
  reason: string;
713
792
  rolledBackAt: string;
714
- }>;
793
+ }[];
715
794
  } catch (e) {
716
795
  SystemLogger.log(this.workspaceDir, 'EVOLUTION_WARN', `failed to parse blacklist: ${String(e)}`);
717
796
  return [];
@@ -10,11 +10,17 @@
10
10
 
11
11
  // ===== 等级定义 =====
12
12
 
13
+
13
14
  export enum EvolutionTier {
15
+ // eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Seed syntax - bare names unused but required for enum structure
14
16
  Seed = 1, // 起步:150行 + 3文件 + 子智能体(现代 AI 能力已足够强)
17
+ // eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Sprout syntax - bare names unused but required for enum structure
15
18
  Sprout = 2, // 成长:300行 + 5文件
19
+ // eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Sapling syntax - bare names unused but required for enum structure
16
20
  Sapling = 3, // 独当:500行 + 10文件 + 风险路径
21
+ // eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Tree syntax - bare names unused but required for enum structure
17
22
  Tree = 4, // 专家:1000行 + 20文件
23
+ // eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Forest syntax - bare names unused but required for enum structure
18
24
  Forest = 5 // 大师:完全自主
19
25
  }
20
26
 
@@ -312,6 +318,8 @@ export interface Principle {
312
318
  detectorMetadata?: PrincipleDetectorSpec;
313
319
  /** Highly abstracted principle text suitable for PRINCIPLES.md */
314
320
  abstractedPrinciple?: string;
321
+ /** Associated core axiom (e.g., "T-01") */
322
+ coreAxiomId?: string;
315
323
 
316
324
  // ── Principle-Tree metadata (Phase 1, optional) ──
317
325
 
@@ -406,6 +414,8 @@ export interface CandidateCreatedData {
406
414
  detectorMetadata?: PrincipleDetectorSpec;
407
415
  /** Optional abstracted principle title (≤40 chars) — preserved on replay */
408
416
  abstractedPrinciple?: string;
417
+ /** Associated core axiom (e.g., "T-01") */
418
+ coreAxiomId?: string;
409
419
  }
410
420
 
411
421
  export interface PrinciplePromotedData {
@@ -403,6 +403,7 @@ export function computeConfigFingerprint(config: Partial<TrainingHyperparameters
403
403
  * If the file cannot be read, falls back to path+count hash (legacy behavior).
404
404
  */
405
405
  export function computeDatasetFingerprint(exportPath: string, sampleCount: number): string {
406
+ // eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in try, catch has fallback return
406
407
  let contentHash: string;
407
408
  try {
408
409
  const content = fs.readFileSync(exportPath, 'utf-8');
@@ -41,10 +41,10 @@ export interface WorkingMemorySnapshot {
41
41
  };
42
42
 
43
43
  // 活动问题
44
- activeProblems: Array<{
44
+ activeProblems: {
45
45
  problem: string;
46
46
  approach?: string;
47
- }>;
47
+ }[];
48
48
 
49
49
  // 下一步行动
50
50
  nextActions: string[];
@@ -56,7 +56,7 @@ export interface WorkingMemorySnapshot {
56
56
  function logError(message: string, error?: unknown): void {
57
57
  const timestamp = new Date().toISOString();
58
58
  const errorStr = error instanceof Error ? error.message : String(error);
59
- // eslint-disable-next-line no-console
59
+
60
60
  console.error(`[focus-history] ${timestamp} ERROR: ${message}${errorStr ? ' - ' + errorStr : ''}`);
61
61
  }
62
62
 
@@ -78,7 +78,7 @@ export function getHistoryDir(focusPath: string): string {
78
78
  * 支持整数和小数版本(如 v1, v1.1, v1.2)
79
79
  */
80
80
  export function extractVersion(content: string): string {
81
- const match = content.match(/\*\*版本\*\*:\s*v([\d.]+)/i);
81
+ const match = /\*\*版本\*\*:\s*v([\d.]+)/i.exec(content);
82
82
  return match ? match[1] : '1';
83
83
  }
84
84
 
@@ -86,8 +86,8 @@ export function extractVersion(content: string): string {
86
86
  * 从 CURRENT_FOCUS.md 提取更新日期
87
87
  */
88
88
  export function extractDate(content: string): string {
89
- const match = content.match(/\*\*更新\*\*:\s*(\d{4}-\d{2}-\d{2})/);
90
- return match ? match[1] : new Date().toISOString().split('T')[0];
89
+ const [, dateStr] = /\*\*更新\*\*:\s*(\d{4}-\d{2}-\d{2})/.exec(content) ?? [];
90
+ return dateStr ?? new Date().toISOString().split('T')[0];
91
91
  }
92
92
 
93
93
  /**
@@ -228,7 +228,7 @@ export function compressFocus(focusPath: string, newContent: string): {
228
228
  const versionParts = oldVersion.split('.');
229
229
  const majorVersion = parseInt(versionParts[0], 10) || 1;
230
230
  const newVersion = `${majorVersion + 1}`;
231
- const today = new Date().toISOString().split('T')[0];
231
+ const [today] = new Date().toISOString().split('T');
232
232
 
233
233
  // 更新版本号和日期
234
234
  const updatedContent = newContent
@@ -265,7 +265,7 @@ export function compressFocus(focusPath: string, newContent: string): {
265
265
  * @param content CURRENT_FOCUS.md 内容
266
266
  * @param maxLines 最大行数
267
267
  */
268
- export function extractSummary(content: string, maxLines: number = 30): string {
268
+ export function extractSummary(content: string, maxLines = 30): string {
269
269
  const lines = content.split('\n');
270
270
  const sections: { [key: string]: string[] } = {
271
271
  header: [], // 标题和元数据
@@ -361,7 +361,7 @@ const MAX_NEXT_ACTIONS = 5;
361
361
  * @returns 提取的工作记忆快照
362
362
  */
363
363
  export function extractWorkingMemory(
364
- messages: Array<{ role?: string; content?: string | unknown[] }>,
364
+ messages: { role?: string; content?: string | unknown[] }[],
365
365
  workspaceDir?: string
366
366
  ): WorkingMemorySnapshot {
367
367
  const snapshot: WorkingMemorySnapshot = {
@@ -378,7 +378,7 @@ export function extractWorkingMemory(
378
378
 
379
379
  for (const msg of recentMessages) {
380
380
  let text = '';
381
- const toolUses: Array<{ name: string; input: Record<string, unknown> }> = [];
381
+ const toolUses: { name: string; input: Record<string, unknown> }[] = [];
382
382
 
383
383
  if (typeof msg.content === 'string') {
384
384
  text = msg.content;
@@ -428,6 +428,7 @@ export function extractWorkingMemory(
428
428
  toolUse.name === 'write_file' || toolUse.name === 'create_file' ? 'created' : 'modified';
429
429
 
430
430
  // 尝试从文本中提取描述
431
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
431
432
  const description = extractDescription(text, filePath);
432
433
 
433
434
  snapshot.artifacts.push({
@@ -442,16 +443,20 @@ export function extractWorkingMemory(
442
443
  if (!text) continue;
443
444
 
444
445
  // 从文本中提取文件操作(备用方式)
446
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
445
447
  extractFileArtifacts(text, snapshot.artifacts, workspaceDir);
446
448
 
447
449
  // 提取问题
450
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
448
451
  extractProblems(text, snapshot.activeProblems);
449
452
 
450
453
  // 提取下一步
454
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
451
455
  extractNextActions(text, snapshot.nextActions);
452
456
  }
453
457
 
454
458
  // 去重和限制数量
459
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
455
460
  snapshot.artifacts = deduplicateArtifacts(snapshot.artifacts).slice(-MAX_ARTIFACTS);
456
461
  snapshot.activeProblems = snapshot.activeProblems.slice(-MAX_PROBLEMS);
457
462
  snapshot.nextActions = snapshot.nextActions.slice(-MAX_NEXT_ACTIONS);
@@ -470,10 +475,11 @@ function extractFileArtifacts(
470
475
  // 匹配 write_file, replace 工具调用
471
476
  // 格式: file_path: "/path/to/file" 或 absolute_path: "/path/to/file"
472
477
  const filePathRegex = /(?:file_path|absolute_path)["']?\s*[:=]\s*["']([^"']+\.(ts|js|json|md|yaml|yml|py|sh|mjs|cjs))["']/gi;
473
-
478
+
479
+ // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in while loop condition before use
474
480
  let match;
475
481
  while ((match = filePathRegex.exec(text)) !== null) {
476
- const filePath = match[1];
482
+ const [, filePath] = match;
477
483
 
478
484
  // 跳过 node_modules 和配置文件
479
485
  if (filePath.includes('node_modules') ||
@@ -500,6 +506,7 @@ function extractFileArtifacts(
500
506
  }
501
507
 
502
508
  // 尝试提取描述(从附近的文本)
509
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
503
510
  const description = extractDescription(text, filePath);
504
511
 
505
512
  artifacts.push({
@@ -512,10 +519,10 @@ function extractFileArtifacts(
512
519
  // 匹配更通用的文件路径格式(如代码块中的路径)
513
520
  // 格式: `path/to/file.ts` 或 "path/to/file.ts"
514
521
  // 只匹配明确的代码相关路径
515
- const genericPathRegex = /[`"']([a-zA-Z0-9_\-\/]+\.(ts|js|mjs|cjs|py))[`"']/g;
522
+ const genericPathRegex = /[`"']([a-zA-Z0-9_./]+\.(ts|js|mjs|cjs|py))[`"']/g;
516
523
 
517
524
  while ((match = genericPathRegex.exec(text)) !== null) {
518
- const filePath = match[1];
525
+ const [, filePath] = match;
519
526
 
520
527
  // 跳过太短、node_modules、配置文件
521
528
  if (filePath.length < 10 ||
@@ -532,8 +539,9 @@ function extractFileArtifacts(
532
539
  continue;
533
540
  }
534
541
 
542
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
535
543
  const description = extractDescription(text, filePath);
536
-
544
+
537
545
  artifacts.push({
538
546
  path: filePath,
539
547
  action: 'modified',
@@ -575,10 +583,11 @@ function extractDescription(text: string, filePath: string): string {
575
583
  */
576
584
  function extractProblems(
577
585
  text: string,
578
- problems: Array<{ problem: string; approach?: string }>
586
+ problems: { problem: string; approach?: string }[]
579
587
  ): void {
580
588
  // 问题模式(匹配问题描述)
581
589
  const problemPattern = /(?:问题|problem|error|错误|失败|failed)[::]\s*([^\n]{5,100})/gi;
590
+ // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in while loop condition before use
582
591
  let match;
583
592
  while ((match = problemPattern.exec(text)) !== null) {
584
593
  const content = match[1].trim();
@@ -621,6 +630,7 @@ function extractNextActions(text: string, actions: string[]): void {
621
630
  ];
622
631
 
623
632
  for (const pattern of patterns) {
633
+ // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in while loop condition before use
624
634
  let match;
625
635
  while ((match = pattern.exec(text)) !== null) {
626
636
  const action = match[1].trim();
@@ -671,7 +681,7 @@ export function parseWorkingMemorySection(content: string): WorkingMemorySnapsho
671
681
  };
672
682
 
673
683
  // 解析 last updated
674
- const updatedMatch = wmContent.match(/Last updated:\s*([^\n]+)/i);
684
+ const updatedMatch = /Last updated:\s*([^\n]+)/i.exec(wmContent);
675
685
  if (updatedMatch) {
676
686
  snapshot.lastUpdated = updatedMatch[1].trim();
677
687
  }
@@ -679,6 +689,7 @@ export function parseWorkingMemorySection(content: string): WorkingMemorySnapsho
679
689
  // 解析文件记录表格
680
690
  // | 文件路径 | 操作 | 描述 |
681
691
  const tableRegex = /\|\s*`?([^`|\n]+)`?\s*\|\s*(created|modified|deleted)\s*\|\s*([^|\n]*)\s*\|/gi;
692
+ // eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in while loop condition before use
682
693
  let match;
683
694
  while ((match = tableRegex.exec(wmContent)) !== null) {
684
695
  snapshot.artifacts.push({
@@ -715,8 +726,9 @@ export function parseWorkingMemorySection(content: string): WorkingMemorySnapsho
715
726
  */
716
727
  export function mergeWorkingMemory(content: string, snapshot: WorkingMemorySnapshot): string {
717
728
  const wmIndex = content.indexOf(WORKING_MEMORY_SECTION);
718
-
729
+
719
730
  // 生成 Working Memory 章节
731
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
720
732
  const wmSection = generateWorkingMemorySection(snapshot);
721
733
 
722
734
  if (wmIndex === -1) {
@@ -727,7 +739,7 @@ export function mergeWorkingMemory(content: string, snapshot: WorkingMemorySnaps
727
739
  const beforeWm = content.substring(0, wmIndex);
728
740
  // 查找下一个 ## 标题(如果有的话)
729
741
  const afterWm = content.substring(wmIndex);
730
- const nextSectionMatch = afterWm.substring(WORKING_MEMORY_SECTION.length).match(/\n##\s/);
742
+ const nextSectionMatch = /\n##\s/.exec(afterWm.substring(WORKING_MEMORY_SECTION.length));
731
743
 
732
744
  if (nextSectionMatch && nextSectionMatch.index !== undefined) {
733
745
  const nextSectionStart = WORKING_MEMORY_SECTION.length + nextSectionMatch.index;
@@ -973,7 +985,7 @@ export function extractMilestones(content: string): {
973
985
 
974
986
  // 提取文件引用(从工作记忆)
975
987
  if (inWorkingMemory) {
976
- const fileMatch = trimmed.match(/^\|\s*`?([^`|\n]+)`?\s*\|/);
988
+ const fileMatch = /^\|\s*`?([^`|\n]+)`?\s*\|/.exec(trimmed);
977
989
  if (fileMatch && !fileMatch[1].includes('文件路径')) {
978
990
  fileArtifacts.push(fileMatch[1].trim());
979
991
  }
@@ -998,7 +1010,7 @@ export function archiveMilestonesToDaily(
998
1010
  return null;
999
1011
  }
1000
1012
 
1001
- const dateStr = new Date().toISOString().split('T')[0];
1013
+ const [dateStr] = new Date().toISOString().split('T');
1002
1014
  const memoryDir = path.join(workspaceDir, 'memory');
1003
1015
  const dailyLogPath = path.join(memoryDir, `${dateStr}.md`);
1004
1016
  const timestamp = new Date().toISOString();
@@ -1088,7 +1100,7 @@ export function cleanupStaleInfo(
1088
1100
  }
1089
1101
 
1090
1102
  // 提取文件路径
1091
- const match = trimmed.match(/^\|\s*`?([^`|\n]+)`?\s*\|/);
1103
+ const match = /^\|\s*`?([^`|\n]+)`?\s*\|/.exec(trimmed);
1092
1104
  if (match) {
1093
1105
  const filePath = match[1].trim();
1094
1106
  artifactCount++;
@@ -1211,7 +1223,7 @@ export function autoCompressFocus(
1211
1223
 
1212
1224
  // 5. 递增版本号和日期
1213
1225
  const newVersion = `${parseInt(version, 10) + 1}`;
1214
- const today = new Date().toISOString().split('T')[0];
1226
+ const [today] = new Date().toISOString().split('T');
1215
1227
  newContent = newContent
1216
1228
  .replace(/\*\*版本\*\*:\s*v[\d.]+/i, `**版本**: v${newVersion}`)
1217
1229
  .replace(/\*\*更新\*\*:\s*\d{4}-\d{2}-\d{2}/, `**更新**: ${today}`);
@@ -1350,7 +1362,7 @@ export function recoverFromTemplate(
1350
1362
  let template = fs.readFileSync(templatePath, 'utf-8');
1351
1363
 
1352
1364
  // 替换日期占位符
1353
- const today = new Date().toISOString().split('T')[0];
1365
+ const [today] = new Date().toISOString().split('T');
1354
1366
  template = template.replace(/{YYYY-MM-DD}/g, today);
1355
1367
 
1356
1368
  // 备份损坏的文件(如果存在)
@@ -1393,7 +1405,9 @@ export function recoverFromTemplate(
1393
1405
  export function safeReadCurrentFocus(
1394
1406
  focusPath: string,
1395
1407
  extensionRoot: string,
1408
+ /* eslint-disable no-unused-vars -- Reason: type-only callback parameter names in function type */
1396
1409
  logger?: { warn?: (msg: string) => void; info?: (msg: string) => void }
1410
+ /* eslint-enable no-unused-vars */
1397
1411
  ): {
1398
1412
  content: string;
1399
1413
  recovered: boolean;
@@ -1,7 +1,8 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
- import { HygieneStats, createEmptyHygieneStats, PersistenceAction } from '../../types/hygiene-types.js';
4
- import { PluginLogger } from '../../openclaw-sdk.js';
3
+ import type { HygieneStats, PersistenceAction } from '../../types/hygiene-types.js';
4
+ import { createEmptyHygieneStats } from '../../types/hygiene-types.js';
5
+ import type { PluginLogger } from '../../openclaw-sdk.js';
5
6
 
6
7
  /**
7
8
  * HygieneTracker - Tracks agent behavior regarding workspace organization and persistence.
@@ -28,7 +29,7 @@ export class HygieneTracker {
28
29
  }
29
30
 
30
31
  private loadStats(): HygieneStats {
31
- const today = new Date().toISOString().split('T')[0];
32
+ const [today] = new Date().toISOString().split('T');
32
33
  if (fs.existsSync(this.statsFile)) {
33
34
  try {
34
35
  const content = fs.readFileSync(this.statsFile, 'utf-8');
@@ -45,7 +46,10 @@ export class HygieneTracker {
45
46
  const backupPath = `${this.statsFile}.bak`;
46
47
  fs.renameSync(this.statsFile, backupPath);
47
48
  this.logger?.warn(`[PD] Corrupted hygiene stats backed up to ${backupPath}`);
48
- } catch (_renameErr) {}
49
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: catch parameter intentionally unused - error handling only
50
+ } catch (_renameErr) {
51
+ // Empty - corrupted stats backup is non-fatal
52
+ }
49
53
  }
50
54
  }
51
55
  return createEmptyHygieneStats(today);
@@ -55,7 +59,7 @@ export class HygieneTracker {
55
59
  let allStats: Record<string, HygieneStats> = {};
56
60
 
57
61
  // Check if we need to rotate date (reset currentStats if date changed)
58
- const today = new Date().toISOString().split('T')[0];
62
+ const [today] = new Date().toISOString().split('T');
59
63
  if (this.currentStats.date !== today) {
60
64
  this.currentStats = createEmptyHygieneStats(today);
61
65
  }
@@ -108,7 +112,7 @@ export class HygieneTracker {
108
112
 
109
113
  getStats(): HygieneStats {
110
114
  // Check for date change on every get
111
- const today = new Date().toISOString().split('T')[0];
115
+ const [today] = new Date().toISOString().split('T');
112
116
  if (this.currentStats.date !== today) {
113
117
  this.currentStats = createEmptyHygieneStats(today);
114
118
  }
package/src/core/init.ts CHANGED
@@ -34,7 +34,7 @@ function hasOutdatedCoreGuidance(file: string, content: string): boolean {
34
34
  * Ensures that the workspace has the necessary template files for Principles Disciple.
35
35
  * This function flattens 'core' templates to the root so OpenClaw can find them.
36
36
  */
37
- export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: string, language: string = 'en') {
37
+ export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: string, language = 'en') {
38
38
  try {
39
39
  const __filename = fileURLToPath(import.meta.url);
40
40
  const __dirname = path.dirname(__filename);
@@ -43,6 +43,7 @@ export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: s
43
43
  const commonTemplatesDir = path.resolve(__dirname, '..', '..', 'templates', 'workspace');
44
44
  if (fs.existsSync(commonTemplatesDir)) {
45
45
  api.logger.info(`[PD] Syncing workspace templates: ${workspaceDir}...`);
46
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: copyRecursiveSync is defined later in this file, called here for organizational reasons
46
47
  copyRecursiveSync(commonTemplatesDir, workspaceDir, api);
47
48
  }
48
49
 
@@ -84,6 +85,7 @@ export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: s
84
85
  if (!fs.existsSync(painDestDir)) {
85
86
  fs.mkdirSync(painDestDir, { recursive: true });
86
87
  }
88
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: copyRecursiveSync is defined later in this file, called here for organizational reasons
87
89
  copyRecursiveSync(painTemplatesDir, painDestDir, api);
88
90
  }
89
91
 
@@ -108,6 +110,7 @@ export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: s
108
110
  * Standard recursive copy that preserves directory structure.
109
111
  * Special handling: maps 'okr' directory to 'memory/okr' for runtime compatibility.
110
112
  */
113
+
111
114
  function copyRecursiveSync(srcDir: string, destDir: string, api: OpenClawPluginApi | { logger: PluginLogger }) {
112
115
  const items = fs.readdirSync(srcDir);
113
116
 
@@ -140,7 +143,7 @@ function copyRecursiveSync(srcDir: string, destDir: string, api: OpenClawPluginA
140
143
  /**
141
144
  * Ensures that the state directory has the necessary files (like pain_dictionary.json).
142
145
  */
143
- export function ensureStateTemplates(ctx: { logger: PluginLogger }, stateDir: string, language: string = 'en') {
146
+ export function ensureStateTemplates(ctx: { logger: PluginLogger }, stateDir: string, language = 'en') {
144
147
  try {
145
148
  const __filename = fileURLToPath(import.meta.url);
146
149
  const __dirname = path.dirname(__filename);