principles-disciple 1.5.4

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 (189) hide show
  1. package/dist/commands/capabilities.d.ts +3 -0
  2. package/dist/commands/capabilities.js +73 -0
  3. package/dist/commands/evolver.d.ts +9 -0
  4. package/dist/commands/evolver.js +26 -0
  5. package/dist/commands/pain.d.ts +5 -0
  6. package/dist/commands/pain.js +114 -0
  7. package/dist/commands/strategy.d.ts +3 -0
  8. package/dist/commands/strategy.js +29 -0
  9. package/dist/commands/thinking-os.d.ts +2 -0
  10. package/dist/commands/thinking-os.js +162 -0
  11. package/dist/commands/trust.d.ts +4 -0
  12. package/dist/commands/trust.js +95 -0
  13. package/dist/core/agent-loader.d.ts +44 -0
  14. package/dist/core/agent-loader.js +147 -0
  15. package/dist/core/config-service.d.ts +15 -0
  16. package/dist/core/config-service.js +26 -0
  17. package/dist/core/config.d.ts +103 -0
  18. package/dist/core/config.js +186 -0
  19. package/dist/core/detection-funnel.d.ts +33 -0
  20. package/dist/core/detection-funnel.js +100 -0
  21. package/dist/core/detection-service.d.ts +15 -0
  22. package/dist/core/detection-service.js +28 -0
  23. package/dist/core/dictionary-service.d.ts +15 -0
  24. package/dist/core/dictionary-service.js +26 -0
  25. package/dist/core/dictionary.d.ts +36 -0
  26. package/dist/core/dictionary.js +136 -0
  27. package/dist/core/event-log.d.ts +53 -0
  28. package/dist/core/event-log.js +196 -0
  29. package/dist/core/evolution-engine.d.ts +119 -0
  30. package/dist/core/evolution-engine.js +542 -0
  31. package/dist/core/evolution-types.d.ts +126 -0
  32. package/dist/core/evolution-types.js +56 -0
  33. package/dist/core/hygiene/tracker.d.ts +22 -0
  34. package/dist/core/hygiene/tracker.js +106 -0
  35. package/dist/core/init.d.ts +12 -0
  36. package/dist/core/init.js +117 -0
  37. package/dist/core/migration.d.ts +6 -0
  38. package/dist/core/migration.js +90 -0
  39. package/dist/core/pain.d.ts +4 -0
  40. package/dist/core/pain.js +70 -0
  41. package/dist/core/path-resolver.d.ts +43 -0
  42. package/dist/core/path-resolver.js +259 -0
  43. package/dist/core/paths.d.ts +60 -0
  44. package/dist/core/paths.js +67 -0
  45. package/dist/core/profile.d.ts +62 -0
  46. package/dist/core/profile.js +210 -0
  47. package/dist/core/risk-calculator.d.ts +7 -0
  48. package/dist/core/risk-calculator.js +39 -0
  49. package/dist/core/session-tracker.d.ts +76 -0
  50. package/dist/core/session-tracker.js +286 -0
  51. package/dist/core/system-logger.d.ts +8 -0
  52. package/dist/core/system-logger.js +31 -0
  53. package/dist/core/trust-engine.d.ts +91 -0
  54. package/dist/core/trust-engine.js +284 -0
  55. package/dist/core/workspace-context.d.ts +64 -0
  56. package/dist/core/workspace-context.js +134 -0
  57. package/dist/hooks/gate.d.ts +6 -0
  58. package/dist/hooks/gate.js +487 -0
  59. package/dist/hooks/lifecycle.d.ts +5 -0
  60. package/dist/hooks/lifecycle.js +180 -0
  61. package/dist/hooks/llm.d.ts +4 -0
  62. package/dist/hooks/llm.js +153 -0
  63. package/dist/hooks/pain.d.ts +5 -0
  64. package/dist/hooks/pain.js +173 -0
  65. package/dist/hooks/prompt.d.ts +38 -0
  66. package/dist/hooks/prompt.js +285 -0
  67. package/dist/hooks/subagent.d.ts +2 -0
  68. package/dist/hooks/subagent.js +70 -0
  69. package/dist/i18n/commands.d.ts +26 -0
  70. package/dist/i18n/commands.js +88 -0
  71. package/dist/index.d.ts +7 -0
  72. package/dist/index.js +204 -0
  73. package/dist/service/evolution-worker.d.ts +17 -0
  74. package/dist/service/evolution-worker.js +293 -0
  75. package/dist/tools/agent-spawn.d.ts +33 -0
  76. package/dist/tools/agent-spawn.js +170 -0
  77. package/dist/tools/critique-prompt.d.ts +14 -0
  78. package/dist/tools/critique-prompt.js +81 -0
  79. package/dist/tools/deep-reflect.d.ts +19 -0
  80. package/dist/tools/deep-reflect.js +174 -0
  81. package/dist/tools/model-index.d.ts +9 -0
  82. package/dist/tools/model-index.js +82 -0
  83. package/dist/types/event-types.d.ts +229 -0
  84. package/dist/types/event-types.js +73 -0
  85. package/dist/types/hygiene-types.d.ts +20 -0
  86. package/dist/types/hygiene-types.js +12 -0
  87. package/dist/types.d.ts +1 -0
  88. package/dist/types.js +1 -0
  89. package/dist/utils/file-lock.d.ts +64 -0
  90. package/dist/utils/file-lock.js +270 -0
  91. package/dist/utils/glob-match.d.ts +28 -0
  92. package/dist/utils/glob-match.js +49 -0
  93. package/dist/utils/hashing.d.ts +9 -0
  94. package/dist/utils/hashing.js +25 -0
  95. package/dist/utils/io.d.ts +6 -0
  96. package/dist/utils/io.js +106 -0
  97. package/dist/utils/nlp.d.ts +9 -0
  98. package/dist/utils/nlp.js +59 -0
  99. package/dist/utils/plugin-logger.d.ts +39 -0
  100. package/dist/utils/plugin-logger.js +70 -0
  101. package/openclaw.plugin.json +46 -0
  102. package/package.json +63 -0
  103. package/templates/langs/en/core/AGENTS.md +206 -0
  104. package/templates/langs/en/core/BOOT.md +60 -0
  105. package/templates/langs/en/core/BOOTSTRAP.md +250 -0
  106. package/templates/langs/en/core/HEARTBEAT.md +74 -0
  107. package/templates/langs/en/core/IDENTITY.md +8 -0
  108. package/templates/langs/en/core/PRINCIPLES.md +10 -0
  109. package/templates/langs/en/core/SOUL.md +76 -0
  110. package/templates/langs/en/core/TOOLS.md +53 -0
  111. package/templates/langs/en/core/USER.md +10 -0
  112. package/templates/langs/en/pain/00_seed_samples.md +23 -0
  113. package/templates/langs/en/pain_dictionary.json +22 -0
  114. package/templates/langs/en/skills/admin/SKILL.md +40 -0
  115. package/templates/langs/en/skills/bootstrap-tools/SKILL.md +53 -0
  116. package/templates/langs/en/skills/deductive-audit/SKILL.md +36 -0
  117. package/templates/langs/en/skills/evolution-framework-update/SKILL.md +31 -0
  118. package/templates/langs/en/skills/evolve-system/SKILL.md +46 -0
  119. package/templates/langs/en/skills/evolve-task/SKILL.md +83 -0
  120. package/templates/langs/en/skills/feedback/SKILL.md +51 -0
  121. package/templates/langs/en/skills/init-strategy/SKILL.md +54 -0
  122. package/templates/langs/en/skills/inject-rule/SKILL.md +19 -0
  123. package/templates/langs/en/skills/manage-okr/SKILL.md +96 -0
  124. package/templates/langs/en/skills/pain/SKILL.md +19 -0
  125. package/templates/langs/en/skills/pd-daily/SKILL.md +199 -0
  126. package/templates/langs/en/skills/pd-grooming/SKILL.md +46 -0
  127. package/templates/langs/en/skills/pd-mentor/SKILL.md +230 -0
  128. package/templates/langs/en/skills/plan-script/SKILL.md +32 -0
  129. package/templates/langs/en/skills/profile/SKILL.md +24 -0
  130. package/templates/langs/en/skills/reflection/SKILL.md +40 -0
  131. package/templates/langs/en/skills/reflection-log/SKILL.md +37 -0
  132. package/templates/langs/en/skills/report/SKILL.md +13 -0
  133. package/templates/langs/en/skills/root-cause/SKILL.md +33 -0
  134. package/templates/langs/en/skills/triage/SKILL.md +29 -0
  135. package/templates/langs/en/skills/watch-evolution/SKILL.md +33 -0
  136. package/templates/langs/zh/core/AGENTS.md +207 -0
  137. package/templates/langs/zh/core/BOOT.md +60 -0
  138. package/templates/langs/zh/core/BOOTSTRAP.md +250 -0
  139. package/templates/langs/zh/core/HEARTBEAT.md +74 -0
  140. package/templates/langs/zh/core/IDENTITY.md +8 -0
  141. package/templates/langs/zh/core/SOUL.md +76 -0
  142. package/templates/langs/zh/core/TOOLS.md +53 -0
  143. package/templates/langs/zh/core/USER.md +10 -0
  144. package/templates/langs/zh/pain/00_seed_samples.md +24 -0
  145. package/templates/langs/zh/pain_dictionary.json +18 -0
  146. package/templates/langs/zh/skills/admin/SKILL.md +42 -0
  147. package/templates/langs/zh/skills/bootstrap-tools/SKILL.md +52 -0
  148. package/templates/langs/zh/skills/deductive-audit/SKILL.md +36 -0
  149. package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +31 -0
  150. package/templates/langs/zh/skills/evolve-system/SKILL.md +46 -0
  151. package/templates/langs/zh/skills/evolve-task/SKILL.md +83 -0
  152. package/templates/langs/zh/skills/feedback/SKILL.md +53 -0
  153. package/templates/langs/zh/skills/init-strategy/SKILL.md +54 -0
  154. package/templates/langs/zh/skills/inject-rule/SKILL.md +19 -0
  155. package/templates/langs/zh/skills/manage-okr/SKILL.md +109 -0
  156. package/templates/langs/zh/skills/pain/SKILL.md +19 -0
  157. package/templates/langs/zh/skills/pd-daily/SKILL.md +199 -0
  158. package/templates/langs/zh/skills/pd-grooming/SKILL.md +46 -0
  159. package/templates/langs/zh/skills/pd-mentor/SKILL.md +230 -0
  160. package/templates/langs/zh/skills/plan-script/SKILL.md +32 -0
  161. package/templates/langs/zh/skills/profile/SKILL.md +24 -0
  162. package/templates/langs/zh/skills/reflection/SKILL.md +40 -0
  163. package/templates/langs/zh/skills/reflection-log/SKILL.md +37 -0
  164. package/templates/langs/zh/skills/report/SKILL.md +13 -0
  165. package/templates/langs/zh/skills/root-cause/SKILL.md +33 -0
  166. package/templates/langs/zh/skills/triage/SKILL.md +29 -0
  167. package/templates/langs/zh/skills/watch-evolution/SKILL.md +33 -0
  168. package/templates/pain_dictionary.json +36 -0
  169. package/templates/pain_settings.json +77 -0
  170. package/templates/workspace/.principles/00-kernel.md +51 -0
  171. package/templates/workspace/.principles/DECISION_POLICY.json +44 -0
  172. package/templates/workspace/.principles/PRINCIPLES.md +20 -0
  173. package/templates/workspace/.principles/PROFILE.json +52 -0
  174. package/templates/workspace/.principles/PROFILE.schema.json +56 -0
  175. package/templates/workspace/.principles/THINKING_OS.md +64 -0
  176. package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +7 -0
  177. package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +9 -0
  178. package/templates/workspace/.principles/models/_INDEX.md +27 -0
  179. package/templates/workspace/.principles/models/first_principles.md +62 -0
  180. package/templates/workspace/.principles/models/marketing_4p.md +52 -0
  181. package/templates/workspace/.principles/models/porter_five.md +63 -0
  182. package/templates/workspace/.principles/models/swot.md +60 -0
  183. package/templates/workspace/.principles/models/user_story_map.md +63 -0
  184. package/templates/workspace/.state/WORKBOARD.json +4 -0
  185. package/templates/workspace/AUDIT.md +15 -0
  186. package/templates/workspace/PLAN.md +2 -0
  187. package/templates/workspace/okr/RECOVERY_PROTOCOL.md +56 -0
  188. package/templates/workspace/okr/TASK_CHANGES.jsonl +6 -0
  189. package/templates/workspace/okr/WEEK_TASKS.json +6 -0
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Evolution Points System V2.0 - MVP
3
+ *
4
+ * Core Philosophy: Growth-driven替代Penalty-driven
5
+ * - 起点0分,只能增加,不扣分
6
+ * - 失败记录教训,不扣分
7
+ * - 同类任务失败后首次成功 = 双倍奖励(1小时冷却)
8
+ * - 5级成长路径:Seed → Forest
9
+ */
10
+ // ===== 等级定义 =====
11
+ export var EvolutionTier;
12
+ (function (EvolutionTier) {
13
+ EvolutionTier[EvolutionTier["Seed"] = 1] = "Seed";
14
+ EvolutionTier[EvolutionTier["Sprout"] = 2] = "Sprout";
15
+ EvolutionTier[EvolutionTier["Sapling"] = 3] = "Sapling";
16
+ EvolutionTier[EvolutionTier["Tree"] = 4] = "Tree";
17
+ EvolutionTier[EvolutionTier["Forest"] = 5] = "Forest"; // 森林:完全自主
18
+ })(EvolutionTier || (EvolutionTier = {}));
19
+ export const TIER_DEFINITIONS = [
20
+ { tier: EvolutionTier.Seed, name: 'Seed', requiredPoints: 0, permissions: { maxLinesPerWrite: 20, maxFilesPerTask: 1, allowRiskPath: false, allowSubagentSpawn: false } },
21
+ { tier: EvolutionTier.Sprout, name: 'Sprout', requiredPoints: 50, permissions: { maxLinesPerWrite: 50, maxFilesPerTask: 2, allowRiskPath: false, allowSubagentSpawn: false } },
22
+ { tier: EvolutionTier.Sapling, name: 'Sapling', requiredPoints: 200, permissions: { maxLinesPerWrite: 200, maxFilesPerTask: 5, allowRiskPath: false, allowSubagentSpawn: true } },
23
+ { tier: EvolutionTier.Tree, name: 'Tree', requiredPoints: 500, permissions: { maxLinesPerWrite: 500, maxFilesPerTask: 10, allowRiskPath: true, allowSubagentSpawn: true } },
24
+ { tier: EvolutionTier.Forest, name: 'Forest', requiredPoints: 1000, permissions: { maxLinesPerWrite: Infinity, maxFilesPerTask: Infinity, allowRiskPath: true, allowSubagentSpawn: true } },
25
+ ];
26
+ export function getTierDefinition(tier) {
27
+ return TIER_DEFINITIONS[tier - 1];
28
+ }
29
+ export function getTierByPoints(totalPoints) {
30
+ // 从高到低检查,找到最高匹配等级
31
+ for (let i = TIER_DEFINITIONS.length - 1; i >= 0; i--) {
32
+ if (totalPoints >= TIER_DEFINITIONS[i].requiredPoints) {
33
+ return TIER_DEFINITIONS[i].tier;
34
+ }
35
+ }
36
+ return EvolutionTier.Seed;
37
+ }
38
+ export const TASK_DIFFICULTY_CONFIG = {
39
+ trivial: { basePoints: 1, description: '简单任务:读取、搜索、状态查询' },
40
+ normal: { basePoints: 3, description: '常规任务:单文件编辑、测试编写' },
41
+ hard: { basePoints: 8, description: '困难任务:多文件重构、架构变更' },
42
+ };
43
+ export const DEFAULT_EVOLUTION_CONFIG = {
44
+ doubleRewardCooldownMs: 60 * 60 * 1000, // 1小时
45
+ maxRecentEvents: 50,
46
+ difficultyPenalty: {
47
+ tier4Trivial: 0.1,
48
+ tier4Normal: 0.5,
49
+ tier5Trivial: 0.1,
50
+ tier5Normal: 0.5,
51
+ },
52
+ dualTrack: {
53
+ enabled: true,
54
+ primarySystem: 'evolution',
55
+ },
56
+ };
@@ -0,0 +1,22 @@
1
+ import { HygieneStats, PersistenceAction } from '../../types/hygiene-types.js';
2
+ import { PluginLogger } from '../../openclaw-sdk.js';
3
+ /**
4
+ * HygieneTracker - Tracks agent behavior regarding workspace organization and persistence.
5
+ */
6
+ export declare class HygieneTracker {
7
+ private readonly statsFile;
8
+ private currentStats;
9
+ private readonly logger?;
10
+ constructor(stateDir: string, logger?: PluginLogger);
11
+ private loadStats;
12
+ private saveStats;
13
+ /**
14
+ * Records a persistence action (writing to memory or plan).
15
+ */
16
+ recordPersistence(action: PersistenceAction): void;
17
+ /**
18
+ * Records a grooming action (cleaning up the workspace).
19
+ */
20
+ recordGrooming(): void;
21
+ getStats(): HygieneStats;
22
+ }
@@ -0,0 +1,106 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { createEmptyHygieneStats } from '../../types/hygiene-types.js';
4
+ /**
5
+ * HygieneTracker - Tracks agent behavior regarding workspace organization and persistence.
6
+ */
7
+ export class HygieneTracker {
8
+ statsFile;
9
+ currentStats;
10
+ logger;
11
+ constructor(stateDir, logger) {
12
+ this.statsFile = path.join(stateDir, 'hygiene-stats.json');
13
+ this.logger = logger;
14
+ // Ensure state directory exists
15
+ if (!fs.existsSync(stateDir)) {
16
+ try {
17
+ fs.mkdirSync(stateDir, { recursive: true });
18
+ }
19
+ catch (e) {
20
+ this.logger?.error(`[PD] Failed to create state directory ${stateDir}: ${String(e)}`);
21
+ }
22
+ }
23
+ this.currentStats = this.loadStats();
24
+ }
25
+ loadStats() {
26
+ const today = new Date().toISOString().split('T')[0];
27
+ if (fs.existsSync(this.statsFile)) {
28
+ try {
29
+ const content = fs.readFileSync(this.statsFile, 'utf-8');
30
+ if (content.trim()) {
31
+ const allStats = JSON.parse(content);
32
+ if (allStats[today]) {
33
+ return allStats[today];
34
+ }
35
+ }
36
+ }
37
+ catch (e) {
38
+ this.logger?.error(`[PD] Failed to load/parse hygiene-stats.json: ${String(e)}`);
39
+ // If file is corrupted, we might want to back it up and start fresh
40
+ try {
41
+ const backupPath = `${this.statsFile}.bak`;
42
+ fs.renameSync(this.statsFile, backupPath);
43
+ this.logger?.warn(`[PD] Corrupted hygiene stats backed up to ${backupPath}`);
44
+ }
45
+ catch (_renameErr) { }
46
+ }
47
+ }
48
+ return createEmptyHygieneStats(today);
49
+ }
50
+ saveStats() {
51
+ let allStats = {};
52
+ // Check if we need to rotate date (reset currentStats if date changed)
53
+ const today = new Date().toISOString().split('T')[0];
54
+ if (this.currentStats.date !== today) {
55
+ this.currentStats = createEmptyHygieneStats(today);
56
+ }
57
+ if (fs.existsSync(this.statsFile)) {
58
+ try {
59
+ const content = fs.readFileSync(this.statsFile, 'utf-8');
60
+ if (content.trim()) {
61
+ allStats = JSON.parse(content);
62
+ }
63
+ }
64
+ catch (e) {
65
+ this.logger?.error(`[PD] Failed to parse hygiene-stats.json for saving: ${String(e)}`);
66
+ }
67
+ }
68
+ allStats[this.currentStats.date] = this.currentStats;
69
+ try {
70
+ // Use a temporary file for atomic write if possible, or simple write
71
+ fs.writeFileSync(this.statsFile, JSON.stringify(allStats, null, 2), 'utf-8');
72
+ }
73
+ catch (e) {
74
+ this.logger?.error(`[PD] Failed to write hygiene-stats.json: ${String(e)}`);
75
+ }
76
+ }
77
+ /**
78
+ * Records a persistence action (writing to memory or plan).
79
+ */
80
+ recordPersistence(action) {
81
+ this.currentStats.persistenceCount++;
82
+ this.currentStats.lastPersistenceTime = action.ts;
83
+ this.currentStats.totalCharsPersisted += action.contentLength;
84
+ const fileName = path.basename(action.path);
85
+ this.currentStats.persistenceByFile[fileName] = (this.currentStats.persistenceByFile[fileName] || 0) + 1;
86
+ this.saveStats();
87
+ this.logger?.info(`[PD] Hygiene: Persisted state to ${fileName} (${action.contentLength} chars)`);
88
+ }
89
+ /**
90
+ * Records a grooming action (cleaning up the workspace).
91
+ */
92
+ recordGrooming() {
93
+ this.currentStats.groomingExecutedCount++;
94
+ this.currentStats.lastGroomingTime = new Date().toISOString();
95
+ this.saveStats();
96
+ this.logger?.info(`[PD] Hygiene: Workspace grooming executed.`);
97
+ }
98
+ getStats() {
99
+ // Check for date change on every get
100
+ const today = new Date().toISOString().split('T')[0];
101
+ if (this.currentStats.date !== today) {
102
+ this.currentStats = createEmptyHygieneStats(today);
103
+ }
104
+ return this.currentStats;
105
+ }
106
+ }
@@ -0,0 +1,12 @@
1
+ import type { OpenClawPluginApi, PluginLogger } from '../openclaw-sdk.js';
2
+ /**
3
+ * Ensures that the workspace has the necessary template files for Principles Disciple.
4
+ * This function flattens 'core' templates to the root so OpenClaw can find them.
5
+ */
6
+ export declare function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: string, language?: string): void;
7
+ /**
8
+ * Ensures that the state directory has the necessary files (like pain_dictionary.json).
9
+ */
10
+ export declare function ensureStateTemplates(ctx: {
11
+ logger: PluginLogger;
12
+ }, stateDir: string, language?: string): void;
@@ -0,0 +1,117 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { PD_DIRS } from './paths.js';
5
+ /**
6
+ * Ensures that the workspace has the necessary template files for Principles Disciple.
7
+ * This function flattens 'core' templates to the root so OpenClaw can find them.
8
+ */
9
+ export function ensureWorkspaceTemplates(api, workspaceDir, language = 'en') {
10
+ try {
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ // 1. Copy common workspace templates (e.g., docs/*)
14
+ const commonTemplatesDir = path.resolve(__dirname, '..', '..', 'templates', 'workspace');
15
+ if (fs.existsSync(commonTemplatesDir)) {
16
+ api.logger.info(`[PD] Syncing workspace templates: ${workspaceDir}...`);
17
+ copyRecursiveSync(commonTemplatesDir, workspaceDir, api);
18
+ }
19
+ // 2. Copy language-specific core templates (AGENTS.md, SOUL.md, etc.)
20
+ // CRITICAL: These MUST be at the root of the workspace for OpenClaw to recognize them.
21
+ let coreTemplatesDir = path.resolve(__dirname, '..', '..', 'templates', 'langs', language, 'core');
22
+ if (!fs.existsSync(coreTemplatesDir)) {
23
+ coreTemplatesDir = path.resolve(__dirname, '..', '..', 'templates', 'langs', 'zh', 'core');
24
+ }
25
+ if (fs.existsSync(coreTemplatesDir)) {
26
+ api.logger.info(`[PD] Flattening ${language} core templates to workspace root...`);
27
+ // We copy contents of 'core' directory directly to workspaceDir root
28
+ const coreFiles = fs.readdirSync(coreTemplatesDir);
29
+ for (const file of coreFiles) {
30
+ const srcPath = path.join(coreTemplatesDir, file);
31
+ const destPath = path.join(workspaceDir, file);
32
+ // If it's a core file and we want to ensure latest templates are used
33
+ // but don't want to destroy user custom data, we check if we should overwrite.
34
+ // For now, we only copy if missing to be safe, but log it.
35
+ if (!fs.existsSync(destPath)) {
36
+ fs.copyFileSync(srcPath, destPath);
37
+ api.logger.info(`[PD] Initialized core file: ${file}`);
38
+ }
39
+ }
40
+ }
41
+ // 3. Copy pain memory seed files
42
+ const painTemplatesDir = path.resolve(__dirname, '..', '..', 'templates', 'langs', language, 'pain');
43
+ const painDestDir = path.join(workspaceDir, PD_DIRS.PAIN_SAMPLES);
44
+ if (fs.existsSync(painTemplatesDir)) {
45
+ if (!fs.existsSync(painDestDir)) {
46
+ fs.mkdirSync(painDestDir, { recursive: true });
47
+ }
48
+ copyRecursiveSync(painTemplatesDir, painDestDir, api);
49
+ }
50
+ }
51
+ catch (err) {
52
+ api.logger.error(`[PD] Failed to initialize workspace templates: ${String(err)}`);
53
+ }
54
+ }
55
+ /**
56
+ * Standard recursive copy that preserves directory structure.
57
+ */
58
+ function copyRecursiveSync(srcDir, destDir, api) {
59
+ const items = fs.readdirSync(srcDir);
60
+ for (const item of items) {
61
+ const srcPath = path.join(srcDir, item);
62
+ const destPath = path.join(destDir, item);
63
+ const stat = fs.statSync(srcPath);
64
+ if (stat.isDirectory()) {
65
+ if (!fs.existsSync(destPath)) {
66
+ fs.mkdirSync(destPath, { recursive: true });
67
+ }
68
+ copyRecursiveSync(srcPath, destPath, api);
69
+ }
70
+ else {
71
+ if (!fs.existsSync(destPath)) {
72
+ try {
73
+ fs.copyFileSync(srcPath, destPath);
74
+ }
75
+ catch (err) {
76
+ if ('logger' in api)
77
+ api.logger.warn(`[PD] Failed to copy ${item}: ${String(err)}`);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Ensures that the state directory has the necessary files (like pain_dictionary.json).
85
+ */
86
+ export function ensureStateTemplates(ctx, stateDir, language = 'en') {
87
+ try {
88
+ const __filename = fileURLToPath(import.meta.url);
89
+ const __dirname = path.dirname(__filename);
90
+ if (!fs.existsSync(stateDir)) {
91
+ fs.mkdirSync(stateDir, { recursive: true });
92
+ }
93
+ // 1. Copy common state files
94
+ const commonFiles = ['pain_settings.json'];
95
+ for (const filename of commonFiles) {
96
+ const templatePath = path.resolve(__dirname, '..', '..', 'templates', filename);
97
+ const destPath = path.join(stateDir, filename);
98
+ if (!fs.existsSync(destPath) && fs.existsSync(templatePath)) {
99
+ fs.copyFileSync(templatePath, destPath);
100
+ ctx.logger.info(`[PD] Initialized ${filename} in stateDir: ${destPath}`);
101
+ }
102
+ }
103
+ // 2. Copy language-specific dictionary
104
+ let dictTemplate = path.resolve(__dirname, '..', '..', 'templates', 'langs', language, 'pain_dictionary.json');
105
+ if (!fs.existsSync(dictTemplate)) {
106
+ dictTemplate = path.resolve(__dirname, '..', '..', 'templates', 'langs', 'en', 'pain_dictionary.json');
107
+ }
108
+ const dictDest = path.join(stateDir, 'pain_dictionary.json');
109
+ if (!fs.existsSync(dictDest) && fs.existsSync(dictTemplate)) {
110
+ fs.copyFileSync(dictTemplate, dictDest);
111
+ ctx.logger.info(`[PD] Initialized pain dictionary in stateDir: ${dictDest} (Lang: ${language})`);
112
+ }
113
+ }
114
+ catch (err) {
115
+ ctx.logger.error(`[PD] Failed to initialize state templates: ${String(err)}`);
116
+ }
117
+ }
@@ -0,0 +1,6 @@
1
+ import type { OpenClawPluginApi } from '../openclaw-sdk.js';
2
+ /**
3
+ * Handles migration of Principles Disciple files from legacy directories
4
+ * (docs/ and memory/.state/) to the new hidden directory structure (.principles/ and .state/).
5
+ */
6
+ export declare function migrateDirectoryStructure(api: OpenClawPluginApi, workspaceDir: string): void;
@@ -0,0 +1,90 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { resolvePdPath } from './paths.js';
4
+ /**
5
+ * Handles migration of Principles Disciple files from legacy directories
6
+ * (docs/ and memory/.state/) to the new hidden directory structure (.principles/ and .state/).
7
+ */
8
+ export function migrateDirectoryStructure(api, workspaceDir) {
9
+ try {
10
+ const legacyDocsDir = path.join(workspaceDir, 'docs');
11
+ const legacyStateDir = path.join(workspaceDir, 'memory', '.state');
12
+ // Comprehensive migration map covering ALL legacy locations
13
+ const migrationMap = [
14
+ // From docs/
15
+ { legacy: path.join(legacyDocsDir, 'PROFILE.json'), newKey: 'PROFILE' },
16
+ { legacy: path.join(legacyDocsDir, 'PRINCIPLES.md'), newKey: 'PRINCIPLES' },
17
+ { legacy: path.join(legacyDocsDir, 'THINKING_OS.md'), newKey: 'THINKING_OS' },
18
+ { legacy: path.join(legacyDocsDir, '00-kernel.md'), newKey: 'KERNEL' },
19
+ { legacy: path.join(legacyDocsDir, 'DECISION_POLICY.json'), newKey: 'DECISION_POLICY' },
20
+ { legacy: path.join(legacyDocsDir, 'PLAN.md'), newKey: 'PLAN' },
21
+ { legacy: path.join(legacyDocsDir, 'evolution_queue.json'), newKey: 'EVOLUTION_QUEUE' },
22
+ { legacy: path.join(legacyDocsDir, 'AGENT_SCORECARD.json'), newKey: 'AGENT_SCORECARD' },
23
+ { legacy: path.join(legacyDocsDir, '.pain_flag'), newKey: 'PAIN_FLAG' },
24
+ { legacy: path.join(legacyDocsDir, 'SYSTEM_CAPABILITIES.json'), newKey: 'SYSTEM_CAPABILITIES' },
25
+ { legacy: path.join(legacyDocsDir, 'SYSTEM.log'), newKey: 'SYSTEM_LOG' },
26
+ { legacy: path.join(legacyDocsDir, 'THINKING_OS_CANDIDATES.md'), newKey: 'THINKING_OS_CANDIDATES' },
27
+ // From memory/.state/ (The hidden legacy state)
28
+ { legacy: path.join(legacyStateDir, 'pain_dictionary.json'), newKey: 'DICTIONARY' },
29
+ { legacy: path.join(legacyStateDir, 'pain_settings.json'), newKey: 'PAIN_SETTINGS' },
30
+ { legacy: path.join(legacyStateDir, 'thinking_os_usage.json'), newKey: 'THINKING_OS_USAGE' },
31
+ { legacy: path.join(legacyStateDir, 'pain_candidates.json'), newKey: 'PAIN_CANDIDATES' },
32
+ { legacy: path.join(legacyStateDir, 'evolution_directive.json'), newKey: 'EVOLUTION_QUEUE' },
33
+ { legacy: path.join(legacyStateDir, 'sessions'), newKey: 'SESSION_DIR' },
34
+ { legacy: path.join(legacyStateDir, 'logs', 'events.jsonl'), newKey: 'SYSTEM_LOG' }, // Backup plan for logs
35
+ ];
36
+ let migratedCount = 0;
37
+ for (const entry of migrationMap) {
38
+ if (fs.existsSync(entry.legacy)) {
39
+ const newPath = resolvePdPath(workspaceDir, entry.newKey);
40
+ const newDir = path.dirname(newPath);
41
+ if (!fs.existsSync(newDir)) {
42
+ fs.mkdirSync(newDir, { recursive: true });
43
+ }
44
+ // If it's a file, rename it. If it's a directory (sessions), handle carefully.
45
+ if (!fs.existsSync(newPath)) {
46
+ try {
47
+ fs.renameSync(entry.legacy, newPath);
48
+ api.logger.info(`[PD:Migration] Migrated ${path.basename(entry.legacy)} to ${newPath.replace(workspaceDir, '')}`);
49
+ migratedCount++;
50
+ }
51
+ catch (renameErr) {
52
+ api.logger.error(`[PD:Migration] Failed to rename ${entry.legacy}: ${String(renameErr)}`);
53
+ }
54
+ }
55
+ else {
56
+ // Special case: Log append instead of overwrite if it's SYSTEM_LOG?
57
+ // For now, keep it simple and skip if exists to avoid data corruption.
58
+ api.logger?.warn?.(`[PD:Migration] Skipping ${path.basename(entry.legacy)}: already exists at destination.`);
59
+ }
60
+ }
61
+ }
62
+ if (migratedCount > 0) {
63
+ api.logger.info(`[PD:Migration] Successfully migrated ${migratedCount} files/folders to new architecture.`);
64
+ }
65
+ // Final cleanup: Try to remove legacy state dir if empty
66
+ if (fs.existsSync(legacyStateDir)) {
67
+ try {
68
+ // Check if dir is empty (recursive check not needed here as we moved main subdirs)
69
+ const items = fs.readdirSync(legacyStateDir);
70
+ if (items.length === 0 || (items.length === 1 && items[0] === 'logs')) {
71
+ // Try to clean up logs dir if empty too
72
+ const logsDir = path.join(legacyStateDir, 'logs');
73
+ if (fs.existsSync(logsDir) && fs.readdirSync(logsDir).length === 0) {
74
+ fs.rmdirSync(logsDir);
75
+ }
76
+ if (fs.readdirSync(legacyStateDir).length === 0) {
77
+ fs.rmdirSync(legacyStateDir);
78
+ api.logger.info(`[PD:Migration] Cleaned up empty legacy state directory.`);
79
+ }
80
+ }
81
+ }
82
+ catch (_e) {
83
+ api.logger.debug?.(`[PD:Migration] Failed to cleanup legacy state dir: ${String(_e)}`);
84
+ }
85
+ }
86
+ }
87
+ catch (err) {
88
+ api.logger.error(`[PD:Migration] Directory refactor migration failed: ${String(err)}`);
89
+ }
90
+ }
@@ -0,0 +1,4 @@
1
+ export declare function computePainScore(rc: number, isSpiral: boolean, missingTestCommand: boolean, softScore: number, projectDir?: string): number;
2
+ export declare function painSeverityLabel(painScore: number, isSpiral?: boolean, projectDir?: string): string;
3
+ export declare function writePainFlag(projectDir: string, painData: Record<string, string>): void;
4
+ export declare function readPainFlagData(projectDir: string): Record<string, string>;
@@ -0,0 +1,70 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { serializeKvLines, parseKvLines } from '../utils/io.js';
4
+ import { resolvePdPath } from './paths.js';
5
+ import { ConfigService } from './config-service.js';
6
+ export function computePainScore(rc, isSpiral, missingTestCommand, softScore, projectDir) {
7
+ let score = Math.max(0, softScore || 0);
8
+ const stateDir = projectDir ? resolvePdPath(projectDir, 'STATE_DIR') : undefined;
9
+ const config = stateDir ? ConfigService.get(stateDir) : null;
10
+ const scoreSettings = config ? config.get('scores') : {
11
+ exit_code_penalty: 70,
12
+ spiral_penalty: 40,
13
+ missing_test_command_penalty: 30
14
+ };
15
+ if (rc !== 0) {
16
+ score += scoreSettings.exit_code_penalty;
17
+ }
18
+ if (isSpiral) {
19
+ score += scoreSettings.spiral_penalty;
20
+ }
21
+ if (missingTestCommand) {
22
+ score += scoreSettings.missing_test_command_penalty;
23
+ }
24
+ return Math.min(100, score);
25
+ }
26
+ export function painSeverityLabel(painScore, isSpiral = false, projectDir) {
27
+ if (isSpiral) {
28
+ return "critical";
29
+ }
30
+ const stateDir = projectDir ? resolvePdPath(projectDir, 'STATE_DIR') : undefined;
31
+ const config = stateDir ? ConfigService.get(stateDir) : null;
32
+ const thresholds = config ? config.get('severity_thresholds') : {
33
+ high: 70,
34
+ medium: 40,
35
+ low: 20
36
+ };
37
+ if (painScore >= thresholds.high) {
38
+ return "high";
39
+ }
40
+ else if (painScore >= thresholds.medium) {
41
+ return "medium";
42
+ }
43
+ else if (painScore >= thresholds.low) {
44
+ return "low";
45
+ }
46
+ else {
47
+ return "info";
48
+ }
49
+ }
50
+ export function writePainFlag(projectDir, painData) {
51
+ const painFlagPath = resolvePdPath(projectDir, 'PAIN_FLAG');
52
+ const dir = path.dirname(painFlagPath);
53
+ if (!fs.existsSync(dir)) {
54
+ fs.mkdirSync(dir, { recursive: true });
55
+ }
56
+ fs.writeFileSync(painFlagPath, serializeKvLines(painData), "utf-8");
57
+ }
58
+ export function readPainFlagData(projectDir) {
59
+ const painFlagPath = resolvePdPath(projectDir, 'PAIN_FLAG');
60
+ try {
61
+ if (!fs.existsSync(painFlagPath)) {
62
+ return {};
63
+ }
64
+ const content = fs.readFileSync(painFlagPath, "utf-8");
65
+ return parseKvLines(content);
66
+ }
67
+ catch (e) {
68
+ return {};
69
+ }
70
+ }
@@ -0,0 +1,43 @@
1
+ export interface PathResolverOptions {
2
+ workspaceDir?: string;
3
+ normalizeWorkspace?: boolean;
4
+ logger?: {
5
+ debug?: (msg: string) => void;
6
+ info?: (msg: string) => void;
7
+ warn?: (msg: string) => void;
8
+ error?: (msg: string) => void;
9
+ };
10
+ }
11
+ export declare const PD_ENV_VARS: {
12
+ readonly WORKSPACE_DIR: "PD_WORKSPACE_DIR";
13
+ readonly STATE_DIR: "PD_STATE_DIR";
14
+ readonly DEBUG: "DEBUG";
15
+ };
16
+ export declare const PD_ENV_DESCRIPTIONS: Record<keyof typeof PD_ENV_VARS, {
17
+ desc: string;
18
+ example: string;
19
+ }>;
20
+ export declare function printEnvVarHelp(): void;
21
+ export interface PDConfig {
22
+ workspace?: string;
23
+ state?: string;
24
+ debug?: boolean;
25
+ }
26
+ export declare class PathResolver {
27
+ private workspaceDir;
28
+ private stateDir;
29
+ private readonly logger?;
30
+ private readonly normalizeWorkspace;
31
+ private initialized;
32
+ constructor(options?: PathResolverOptions);
33
+ private log;
34
+ private detectWorkspaceDir;
35
+ private normalizePath;
36
+ normalizeWorkspacePath(inputPath: string): string;
37
+ getWorkspaceDir(): string;
38
+ getStateDir(): string;
39
+ resolve(key: string): string;
40
+ ensureStateDir(): void;
41
+ static createFromHookContext(ctx: any): PathResolver;
42
+ }
43
+ export declare function createDefaultConfig(targetPath?: string): string;