principles-disciple 1.191.0 → 1.193.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.
@@ -171,6 +171,18 @@ export function handlePainCommand(ctx) {
171
171
  if (suggestionText) {
172
172
  text += suggestionText;
173
173
  }
174
+ else if (stats.totalRules === 0 && stats.totalHits === 0) {
175
+ // Fix-14 (P1-DESIGN-2): Empty-state onboarding guidance for fresh installs.
176
+ // Merges Fix-13's welcome message here instead of injecting into the
177
+ // before_prompt_build hook (avoids MVP-Core behavior change).
178
+ text += `\n🦞 **欢迎使用 Principles Disciple!**\n\n`;
179
+ text += `当前是全新工作区,还没有痛觉信号 —— 这是正常的。\n\n`;
180
+ text += `**下一步**:\n`;
181
+ text += `1. 让 Agent 工作,当它犯错时运行 \`/pd-pain <描述问题>\`\n`;
182
+ text += `2. 或运行演示: \`pd demo first-principle\`\n`;
183
+ text += `3. 配置 LLM runtime profile: \`pd console open --workspace "<path>"\`\n\n`;
184
+ text += `审批通过的原则会出现在这里。`;
185
+ }
174
186
  else {
175
187
  text += `*💡 提示: 使用 \`/pd-status empathy\` 查看详细情绪事件统计。*`;
176
188
  }
@@ -189,6 +201,18 @@ export function handlePainCommand(ctx) {
189
201
  if (suggestionText) {
190
202
  text += suggestionText;
191
203
  }
204
+ else if (stats.totalRules === 0 && stats.totalHits === 0) {
205
+ // Fix-14 (P1-DESIGN-2): Empty-state onboarding guidance for fresh installs.
206
+ // Merges Fix-13's welcome message here instead of injecting into the
207
+ // before_prompt_build hook (avoids MVP-Core behavior change).
208
+ text += `\n🦞 **Welcome to Principles Disciple!**\n\n`;
209
+ text += `This is a fresh workspace with no pain signals yet — that's expected.\n\n`;
210
+ text += `**Next steps**:\n`;
211
+ text += `1. Let your agent work and run \`/pd-pain <description>\` when it makes a mistake\n`;
212
+ text += `2. Or run the demo: \`pd demo first-principle\`\n`;
213
+ text += `3. Configure LLM runtime profile: \`pd console open --workspace "<path>"\`\n\n`;
214
+ text += `Approved principles will appear here once activated.`;
215
+ }
192
216
  else {
193
217
  text += `*💡 Hint: Use \`/pd-status empathy\` to view detailed empathy event statistics.*`;
194
218
  }
@@ -3,6 +3,22 @@ export declare const SystemLogger: {
3
3
  /**
4
4
  * Force refresh of the cached log file path.
5
5
  * Call this at midnight or when the date changes to ensure proper rotation.
6
+ *
7
+ * PRI-504: clears log-file and log-date caches for ALL workspaces (preserves
8
+ * the original "force refresh" semantics). Does NOT clear lastCleanupDates
9
+ * to avoid re-triggering cleanup for every workspace on the same day.
6
10
  */
7
11
  refreshCache(): void;
8
12
  };
13
+ /**
14
+ * PRI-504: dispose the SystemLogger cache for a single workspace.
15
+ * Useful for tests and for workspace teardown in multi-workspace processes.
16
+ * Pattern follows evolution-engine.ts:564-570 (disposeEvolutionEngine).
17
+ */
18
+ export declare function disposeSystemLogger(workspaceDir: string): void;
19
+ /**
20
+ * PRI-504: dispose ALL SystemLogger caches. Call this in test afterEach
21
+ * hooks to prevent state leakage between tests. Pattern follows
22
+ * evolution-engine.ts:572-577 (disposeAllEvolutionEngines).
23
+ */
24
+ export declare function disposeAllSystemLoggers(): void;
@@ -11,9 +11,13 @@ import { resolvePdPath, PD_FILES } from './paths.js';
11
11
  * Automatically rotates to a new file at midnight.
12
12
  * Old log files are automatically cleaned up based on retention policy.
13
13
  */
14
- // Cache for current log file path, invalidated when date changes
15
- let cachedLogFile;
16
- let cachedLogDate;
14
+ // PRI-504: per-workspace caches keyed by path.resolve(workspaceDir).
15
+ // Previously these were module-level `let` variables, so the first workspace
16
+ // to call log() would pin the log file path for ALL subsequent workspaces in
17
+ // the same process — causing cross-workspace log leakage (ERR-092).
18
+ // Pattern follows evolution-engine.ts:551-577 (Map + path.resolve + dispose).
19
+ const cachedLogFiles = new Map();
20
+ const cachedLogDates = new Map();
17
21
  /**
18
22
  * Log retention: delete SYSTEM logs older than this many days.
19
23
  * Set to 0 to disable cleanup.
@@ -64,28 +68,33 @@ function cleanupOldLogs(workspaceDir) {
64
68
  // Silently fail cleanup - don't crash logging for cleanup failures
65
69
  }
66
70
  }
67
- // Track if cleanup has run today
68
- let lastCleanupDate;
71
+ // PRI-504: per-workspace cleanup tracker (was module-level `let`).
72
+ // Keyed by path.resolve(workspaceDir) so each workspace runs its own
73
+ // daily cleanup independently.
74
+ const lastCleanupDates = new Map();
69
75
  export const SystemLogger = {
70
76
  log(workspaceDir, eventType, message) {
71
77
  if (!workspaceDir)
72
78
  return;
73
79
  try {
80
+ const resolved = path.resolve(workspaceDir);
74
81
  const today = getTodayStr();
75
- // Check if date changed - invalidate cache and run cleanup
76
- if (cachedLogDate !== today) {
77
- cachedLogDate = today;
78
- cachedLogFile = undefined;
79
- // Run cleanup once per day when date changes
80
- if (lastCleanupDate !== today) {
81
- lastCleanupDate = today;
82
+ // Check if date changed for THIS workspace - invalidate its cache and run cleanup
83
+ if (cachedLogDates.get(resolved) !== today) {
84
+ cachedLogDates.set(resolved, today);
85
+ cachedLogFiles.delete(resolved);
86
+ // Run cleanup once per day per workspace when date changes
87
+ if (lastCleanupDates.get(resolved) !== today) {
88
+ lastCleanupDates.set(resolved, today);
82
89
  cleanupOldLogs(workspaceDir);
83
90
  }
84
91
  }
85
- // Get or create log file path
86
- if (!cachedLogFile) {
87
- cachedLogFile = getSystemLogPath(workspaceDir, new Date());
88
- const logDir = path.dirname(cachedLogFile);
92
+ // Get or create log file path for this workspace
93
+ let logFile = cachedLogFiles.get(resolved);
94
+ if (!logFile) {
95
+ logFile = getSystemLogPath(workspaceDir, new Date());
96
+ cachedLogFiles.set(resolved, logFile);
97
+ const logDir = path.dirname(logFile);
89
98
  if (!fs.existsSync(logDir)) {
90
99
  fs.mkdirSync(logDir, { recursive: true });
91
100
  }
@@ -94,7 +103,7 @@ export const SystemLogger = {
94
103
  // Format: [YYYY-MM-DDTHH:mm:ss.sssZ] [EVENT_TYPE] Message
95
104
  const logEntry = `[${timestamp}] [${eventType.padEnd(15)}] ${message}\n`;
96
105
  // Use fire-and-forget async append to prevent blocking
97
- fs.appendFile(cachedLogFile, logEntry, 'utf8', (_err) => {
106
+ fs.appendFile(logFile, logEntry, 'utf8', (_err) => {
98
107
  // Silently drop errors (e.g. disk full) to not crash the gateway
99
108
  });
100
109
  }
@@ -105,9 +114,34 @@ export const SystemLogger = {
105
114
  /**
106
115
  * Force refresh of the cached log file path.
107
116
  * Call this at midnight or when the date changes to ensure proper rotation.
117
+ *
118
+ * PRI-504: clears log-file and log-date caches for ALL workspaces (preserves
119
+ * the original "force refresh" semantics). Does NOT clear lastCleanupDates
120
+ * to avoid re-triggering cleanup for every workspace on the same day.
108
121
  */
109
122
  refreshCache() {
110
- cachedLogDate = undefined;
111
- cachedLogFile = undefined;
123
+ cachedLogFiles.clear();
124
+ cachedLogDates.clear();
112
125
  }
113
126
  };
127
+ /**
128
+ * PRI-504: dispose the SystemLogger cache for a single workspace.
129
+ * Useful for tests and for workspace teardown in multi-workspace processes.
130
+ * Pattern follows evolution-engine.ts:564-570 (disposeEvolutionEngine).
131
+ */
132
+ export function disposeSystemLogger(workspaceDir) {
133
+ const resolved = path.resolve(workspaceDir);
134
+ cachedLogFiles.delete(resolved);
135
+ cachedLogDates.delete(resolved);
136
+ lastCleanupDates.delete(resolved);
137
+ }
138
+ /**
139
+ * PRI-504: dispose ALL SystemLogger caches. Call this in test afterEach
140
+ * hooks to prevent state leakage between tests. Pattern follows
141
+ * evolution-engine.ts:572-577 (disposeAllEvolutionEngines).
142
+ */
143
+ export function disposeAllSystemLoggers() {
144
+ cachedLogFiles.clear();
145
+ cachedLogDates.clear();
146
+ lastCleanupDates.clear();
147
+ }
@@ -2,7 +2,7 @@
2
2
  "id": "principles-disciple",
3
3
  "name": "Principles Disciple",
4
4
  "description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
5
- "version": "1.191.0",
5
+ "version": "1.193.0",
6
6
  "activation": {
7
7
  "onCapabilities": [
8
8
  "hook"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "principles-disciple",
3
- "version": "1.191.0",
3
+ "version": "1.193.0",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",