create-principles-disciple 1.112.0 → 1.112.2

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 (160) hide show
  1. package/package.json +2 -2
  2. package/pd-cli/dist/commands/legacy-cleanup.d.ts.map +1 -1
  3. package/pd-cli/dist/commands/legacy-cleanup.js +19 -2
  4. package/pd-cli/dist/commands/legacy-cleanup.js.map +1 -1
  5. package/pd-cli/dist/commands/pain-evidence.d.ts +3 -1
  6. package/pd-cli/dist/commands/pain-evidence.d.ts.map +1 -1
  7. package/pd-cli/dist/commands/pain-evidence.js +12 -3
  8. package/pd-cli/dist/commands/pain-evidence.js.map +1 -1
  9. package/pd-cli/dist/commands/rulecode.d.ts +13 -0
  10. package/pd-cli/dist/commands/rulecode.d.ts.map +1 -1
  11. package/pd-cli/dist/commands/rulecode.js +23 -2
  12. package/pd-cli/dist/commands/rulecode.js.map +1 -1
  13. package/pd-cli/dist/resolve-workspace.d.ts.map +1 -1
  14. package/pd-cli/dist/resolve-workspace.js +33 -12
  15. package/pd-cli/dist/resolve-workspace.js.map +1 -1
  16. package/pd-cli/dist/services/console-launcher.d.ts +8 -1
  17. package/pd-cli/dist/services/console-launcher.d.ts.map +1 -1
  18. package/pd-cli/dist/services/console-launcher.js +45 -3
  19. package/pd-cli/dist/services/console-launcher.js.map +1 -1
  20. package/pd-cli/dist/services/pd-config-loader.d.ts.map +1 -1
  21. package/pd-cli/dist/services/pd-config-loader.js +34 -1
  22. package/pd-cli/dist/services/pd-config-loader.js.map +1 -1
  23. package/pd-cli/dist/services/quality-scorecard/strong-model-gate.d.ts +19 -0
  24. package/pd-cli/dist/services/quality-scorecard/strong-model-gate.d.ts.map +1 -1
  25. package/pd-cli/dist/services/quality-scorecard/strong-model-gate.js +44 -2
  26. package/pd-cli/dist/services/quality-scorecard/strong-model-gate.js.map +1 -1
  27. package/pd-cli/dist/utils/path-security.d.ts +60 -0
  28. package/pd-cli/dist/utils/path-security.d.ts.map +1 -0
  29. package/pd-cli/dist/utils/path-security.js +90 -0
  30. package/pd-cli/dist/utils/path-security.js.map +1 -0
  31. package/plugin/dist/bundle.js +13 -11
  32. package/plugin/dist/constants/tools.d.ts +1 -1
  33. package/plugin/dist/hooks/prompt-helpers.d.ts +5 -5
  34. package/plugin/dist/templates/langs/en/core/AGENTS.md +40 -180
  35. package/plugin/dist/templates/langs/en/core/BOOT.md +19 -47
  36. package/plugin/dist/templates/langs/en/core/HEARTBEAT.md +26 -58
  37. package/plugin/dist/templates/langs/en/core/IDENTITY.md +15 -8
  38. package/plugin/dist/templates/langs/en/core/PRINCIPLES.md +6 -7
  39. package/plugin/dist/templates/langs/en/core/SOUL.md +10 -41
  40. package/plugin/dist/templates/langs/en/core/TOOLS.md +18 -10
  41. package/plugin/dist/templates/langs/en/core/USER.md +13 -8
  42. package/plugin/dist/templates/langs/en/skills/pd-auditor/SKILL.md +1 -1
  43. package/plugin/dist/templates/langs/en/skills/pd-explorer/SKILL.md +1 -1
  44. package/plugin/dist/templates/langs/en/skills/pd-implementer/SKILL.md +1 -1
  45. package/plugin/dist/templates/langs/en/skills/pd-mentor/SKILL.md +6 -30
  46. package/plugin/dist/templates/langs/en/skills/pd-planner/SKILL.md +1 -1
  47. package/plugin/dist/templates/langs/zh/core/AGENTS.md +38 -173
  48. package/plugin/dist/templates/langs/zh/core/BOOT.md +14 -46
  49. package/plugin/dist/templates/langs/zh/core/HEARTBEAT.md +20 -82
  50. package/plugin/dist/templates/langs/zh/core/IDENTITY.md +15 -8
  51. package/plugin/dist/templates/langs/zh/core/PRINCIPLES.md +7 -7
  52. package/plugin/dist/templates/langs/zh/core/SOUL.md +9 -41
  53. package/plugin/dist/templates/langs/zh/core/TOOLS.md +14 -10
  54. package/plugin/dist/templates/langs/zh/core/USER.md +12 -8
  55. package/plugin/dist/templates/langs/zh/skills/pd-auditor/SKILL.md +1 -1
  56. package/plugin/dist/templates/langs/zh/skills/pd-explorer/SKILL.md +1 -1
  57. package/plugin/dist/templates/langs/zh/skills/pd-implementer/SKILL.md +1 -1
  58. package/plugin/dist/templates/langs/zh/skills/pd-mentor/SKILL.md +6 -30
  59. package/plugin/dist/templates/langs/zh/skills/pd-planner/SKILL.md +1 -1
  60. package/plugin/dist/templates/pain_settings.json +1 -2
  61. package/plugin/openclaw.plugin.json +1 -1
  62. package/plugin/package.json +1 -2
  63. package/plugin/templates/langs/en/core/AGENTS.md +40 -180
  64. package/plugin/templates/langs/en/core/BOOT.md +19 -47
  65. package/plugin/templates/langs/en/core/HEARTBEAT.md +26 -58
  66. package/plugin/templates/langs/en/core/IDENTITY.md +15 -8
  67. package/plugin/templates/langs/en/core/PRINCIPLES.md +6 -7
  68. package/plugin/templates/langs/en/core/SOUL.md +10 -41
  69. package/plugin/templates/langs/en/core/TOOLS.md +18 -10
  70. package/plugin/templates/langs/en/core/USER.md +13 -8
  71. package/plugin/templates/langs/en/skills/pd-auditor/SKILL.md +1 -1
  72. package/plugin/templates/langs/en/skills/pd-explorer/SKILL.md +1 -1
  73. package/plugin/templates/langs/en/skills/pd-implementer/SKILL.md +1 -1
  74. package/plugin/templates/langs/en/skills/pd-mentor/SKILL.md +6 -30
  75. package/plugin/templates/langs/en/skills/pd-planner/SKILL.md +1 -1
  76. package/plugin/templates/langs/zh/core/AGENTS.md +38 -173
  77. package/plugin/templates/langs/zh/core/BOOT.md +14 -46
  78. package/plugin/templates/langs/zh/core/HEARTBEAT.md +20 -82
  79. package/plugin/templates/langs/zh/core/IDENTITY.md +15 -8
  80. package/plugin/templates/langs/zh/core/PRINCIPLES.md +7 -7
  81. package/plugin/templates/langs/zh/core/SOUL.md +9 -41
  82. package/plugin/templates/langs/zh/core/TOOLS.md +14 -10
  83. package/plugin/templates/langs/zh/core/USER.md +12 -8
  84. package/plugin/templates/langs/zh/skills/pd-auditor/SKILL.md +1 -1
  85. package/plugin/templates/langs/zh/skills/pd-explorer/SKILL.md +1 -1
  86. package/plugin/templates/langs/zh/skills/pd-implementer/SKILL.md +1 -1
  87. package/plugin/templates/langs/zh/skills/pd-mentor/SKILL.md +6 -30
  88. package/plugin/templates/langs/zh/skills/pd-planner/SKILL.md +1 -1
  89. package/plugin/templates/pain_settings.json +1 -2
  90. package/plugin/dist/templates/langs/en/core/BOOTSTRAP.md +0 -259
  91. package/plugin/dist/templates/langs/en/skills/admin/SKILL.md +0 -39
  92. package/plugin/dist/templates/langs/en/skills/bootstrap-tools/SKILL.md +0 -53
  93. package/plugin/dist/templates/langs/en/skills/deductive-audit/SKILL.md +0 -36
  94. package/plugin/dist/templates/langs/en/skills/evolution-framework-update/SKILL.md +0 -31
  95. package/plugin/dist/templates/langs/en/skills/evolve-task/SKILL.md +0 -83
  96. package/plugin/dist/templates/langs/en/skills/feedback/SKILL.md +0 -51
  97. package/plugin/dist/templates/langs/en/skills/init-strategy/SKILL.md +0 -54
  98. package/plugin/dist/templates/langs/en/skills/inject-rule/SKILL.md +0 -19
  99. package/plugin/dist/templates/langs/en/skills/profile/SKILL.md +0 -24
  100. package/plugin/dist/templates/langs/en/skills/reflection/SKILL.md +0 -40
  101. package/plugin/dist/templates/langs/en/skills/reflection-log/SKILL.md +0 -37
  102. package/plugin/dist/templates/langs/en/skills/report/SKILL.md +0 -13
  103. package/plugin/dist/templates/langs/en/skills/root-cause/SKILL.md +0 -33
  104. package/plugin/dist/templates/langs/en/skills/triage/SKILL.md +0 -29
  105. package/plugin/dist/templates/langs/en/skills/watch-evolution/SKILL.md +0 -33
  106. package/plugin/dist/templates/langs/zh/core/BOOTSTRAP.md +0 -260
  107. package/plugin/dist/templates/langs/zh/skills/admin/SKILL.md +0 -41
  108. package/plugin/dist/templates/langs/zh/skills/bootstrap-tools/SKILL.md +0 -52
  109. package/plugin/dist/templates/langs/zh/skills/deductive-audit/SKILL.md +0 -36
  110. package/plugin/dist/templates/langs/zh/skills/evolution-framework-update/SKILL.md +0 -31
  111. package/plugin/dist/templates/langs/zh/skills/evolve-task/SKILL.md +0 -83
  112. package/plugin/dist/templates/langs/zh/skills/feedback/SKILL.md +0 -53
  113. package/plugin/dist/templates/langs/zh/skills/init-strategy/SKILL.md +0 -54
  114. package/plugin/dist/templates/langs/zh/skills/inject-rule/SKILL.md +0 -19
  115. package/plugin/dist/templates/langs/zh/skills/profile/SKILL.md +0 -24
  116. package/plugin/dist/templates/langs/zh/skills/reflection/SKILL.md +0 -40
  117. package/plugin/dist/templates/langs/zh/skills/reflection-log/SKILL.md +0 -37
  118. package/plugin/dist/templates/langs/zh/skills/report/SKILL.md +0 -13
  119. package/plugin/dist/templates/langs/zh/skills/root-cause/SKILL.md +0 -33
  120. package/plugin/dist/templates/langs/zh/skills/triage/SKILL.md +0 -29
  121. package/plugin/dist/templates/langs/zh/skills/watch-evolution/SKILL.md +0 -33
  122. package/plugin/scripts/acceptance-test.mjs +0 -257
  123. package/plugin/scripts/bootstrap-rules.mjs +0 -70
  124. package/plugin/scripts/compile-principles.mjs +0 -104
  125. package/plugin/scripts/install-dependencies.cjs +0 -47
  126. package/plugin/scripts/postinstall.cjs +0 -177
  127. package/plugin/scripts/sync-plugin.mjs +0 -1442
  128. package/plugin/scripts/verify-build.mjs +0 -120
  129. package/plugin/templates/langs/en/core/BOOTSTRAP.md +0 -259
  130. package/plugin/templates/langs/en/skills/admin/SKILL.md +0 -39
  131. package/plugin/templates/langs/en/skills/bootstrap-tools/SKILL.md +0 -53
  132. package/plugin/templates/langs/en/skills/deductive-audit/SKILL.md +0 -36
  133. package/plugin/templates/langs/en/skills/evolution-framework-update/SKILL.md +0 -31
  134. package/plugin/templates/langs/en/skills/evolve-task/SKILL.md +0 -83
  135. package/plugin/templates/langs/en/skills/feedback/SKILL.md +0 -51
  136. package/plugin/templates/langs/en/skills/init-strategy/SKILL.md +0 -54
  137. package/plugin/templates/langs/en/skills/inject-rule/SKILL.md +0 -19
  138. package/plugin/templates/langs/en/skills/profile/SKILL.md +0 -24
  139. package/plugin/templates/langs/en/skills/reflection/SKILL.md +0 -40
  140. package/plugin/templates/langs/en/skills/reflection-log/SKILL.md +0 -37
  141. package/plugin/templates/langs/en/skills/report/SKILL.md +0 -13
  142. package/plugin/templates/langs/en/skills/root-cause/SKILL.md +0 -33
  143. package/plugin/templates/langs/en/skills/triage/SKILL.md +0 -29
  144. package/plugin/templates/langs/en/skills/watch-evolution/SKILL.md +0 -33
  145. package/plugin/templates/langs/zh/core/BOOTSTRAP.md +0 -260
  146. package/plugin/templates/langs/zh/skills/admin/SKILL.md +0 -41
  147. package/plugin/templates/langs/zh/skills/bootstrap-tools/SKILL.md +0 -52
  148. package/plugin/templates/langs/zh/skills/deductive-audit/SKILL.md +0 -36
  149. package/plugin/templates/langs/zh/skills/evolution-framework-update/SKILL.md +0 -31
  150. package/plugin/templates/langs/zh/skills/evolve-task/SKILL.md +0 -83
  151. package/plugin/templates/langs/zh/skills/feedback/SKILL.md +0 -53
  152. package/plugin/templates/langs/zh/skills/init-strategy/SKILL.md +0 -54
  153. package/plugin/templates/langs/zh/skills/inject-rule/SKILL.md +0 -19
  154. package/plugin/templates/langs/zh/skills/profile/SKILL.md +0 -24
  155. package/plugin/templates/langs/zh/skills/reflection/SKILL.md +0 -40
  156. package/plugin/templates/langs/zh/skills/reflection-log/SKILL.md +0 -37
  157. package/plugin/templates/langs/zh/skills/report/SKILL.md +0 -13
  158. package/plugin/templates/langs/zh/skills/root-cause/SKILL.md +0 -33
  159. package/plugin/templates/langs/zh/skills/triage/SKILL.md +0 -29
  160. package/plugin/templates/langs/zh/skills/watch-evolution/SKILL.md +0 -33
@@ -1,1442 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Principles Disciple Plugin Installer
5
- *
6
- * A complete installation script that can run after cloning the repo.
7
- * Handles dependencies, build, and sync to OpenClaw installation directory.
8
- *
9
- * Usage:
10
- * node scripts/sync-plugin.mjs [options]
11
- *
12
- * Options:
13
- * --lang <zh|en> Language for skills (default: zh)
14
- * --skip-build Skip build step (use existing dist/)
15
- * --skip-deps Skip dependency installation
16
- * --force Force overwrite without prompts
17
- * --help Show help message
18
- */
19
-
20
- import { chmodSync, copyFileSync, cpSync, existsSync, lstatSync, rmSync, readFileSync, readFileSync as readFileSyncRaw, mkdirSync, writeFileSync, readdirSync } from 'fs';
21
- import { createHash } from 'crypto';
22
- import { join, dirname } from 'path';
23
- import { fileURLToPath } from 'url';
24
- import { execSync } from 'child_process';
25
-
26
- const __filename = fileURLToPath(import.meta.url);
27
- const __dirname = dirname(__filename);
28
-
29
- const SOURCE_DIR = join(__dirname, '..');
30
- const ROOT_DIR = join(SOURCE_DIR, '..', '..');
31
- const PD_CLI_SOURCE_DIR = join(ROOT_DIR, 'packages', 'pd-cli');
32
-
33
- /**
34
- * Cross-platform home directory resolution.
35
- * Linux/macOS: HOME=/home/user
36
- * Windows: USERPROFILE=C:\Users\user or HOMEDRIVE/HOMEPATH
37
- */
38
- function getHomeDir() {
39
- return process.env.HOME
40
- || process.env.USERPROFILE
41
- || (process.env.HOMEDRIVE && process.env.HOMEPATH ? process.env.HOMEDRIVE + process.env.HOMEPATH : null)
42
- || '.';
43
- }
44
-
45
- const OPENCLAW_DIR = join(getHomeDir(), '.openclaw');
46
- const INSTALL_DIR = join(OPENCLAW_DIR, 'extensions', 'principles-disciple');
47
- const INSTALLED_PD_CLI_DIR = join(INSTALL_DIR, 'pd-cli');
48
- const INSTALLED_BIN_DIR = join(INSTALL_DIR, 'bin');
49
- function getConfiguredWorkspaceDir() {
50
- const configPath = join(OPENCLAW_DIR, 'openclaw.json');
51
- try {
52
- const raw = readFileSync(configPath, 'utf-8');
53
- const config = JSON.parse(raw);
54
- const workspace = config?.agents?.defaults?.workspace;
55
- if (workspace && existsSync(workspace)) {
56
- return workspace;
57
- }
58
- } catch {
59
- // Fall through to fallback
60
- }
61
- // Fallback: try workspace-main (legacy default)
62
- return join(OPENCLAW_DIR, 'workspace-main');
63
- }
64
-
65
- // Files and directories to sync.
66
- // NOTE: openclaw.plugin.json is NOT included here — it is synced separately
67
- // via syncFilteredManifest() which filters the skills array by language
68
- // BEFORE writing to the install target, preventing the en/zh collision.
69
- const SYNC_ITEMS = [
70
- 'dist',
71
- 'templates',
72
- 'scripts',
73
- 'docs',
74
- 'package.json',
75
- ];
76
-
77
- // Minimum Node.js version
78
- const MIN_NODE_VERSION = '18.0.0';
79
-
80
- /**
81
- * Parse command line arguments
82
- */
83
- function parseArgs() {
84
- const args = {
85
- lang: process.env.OPENCLAW_LANGUAGE || 'zh',
86
- skipBuild: false,
87
- skipDeps: false,
88
- force: false,
89
- restart: true,
90
- dev: false,
91
- bump: false,
92
- help: false,
93
- };
94
-
95
- const argv = process.argv.slice(2);
96
- for (let i = 0; i < argv.length; i++) {
97
- const arg = argv[i];
98
- switch (arg) {
99
- case '--lang':
100
- args.lang = argv[++i];
101
- break;
102
- case '--skip-build':
103
- args.skipBuild = true;
104
- break;
105
- case '--skip-deps':
106
- args.skipDeps = true;
107
- break;
108
- case '--restart':
109
- case '--no-restart':
110
- args.restart = !arg.startsWith('--no-');
111
- break;
112
- case '--dev':
113
- case '-d':
114
- args.dev = true;
115
- args.force = true;
116
- args.restart = true;
117
- args.bump = true;
118
- break;
119
- case '--bump':
120
- case '-b':
121
- args.bump = true;
122
- break;
123
- case '--force':
124
- case '-f':
125
- args.force = true;
126
- break;
127
- case '--help':
128
- case '-h':
129
- args.help = true;
130
- break;
131
- default:
132
- if (arg.startsWith('--')) {
133
- console.error(`Unknown option: ${arg}`);
134
- args.help = true;
135
- }
136
- }
137
- }
138
- return args;
139
- }
140
-
141
- /**
142
- * Show help message
143
- */
144
- function showHelp() {
145
- console.log(`
146
- Principles Disciple Plugin Installer
147
-
148
- Usage:
149
- node scripts/sync-plugin.mjs [options]
150
-
151
- Options:
152
- --lang <zh|en> Language for skills (default: zh)
153
- --skip-build Skip build step (use existing dist/)
154
- --skip-deps Skip dependency installation
155
- --restart Automatically restart OpenClaw gateway after installation (default: true, use --no-restart to skip)
156
- --dev, -d Developer mode: --force + --restart + --bump + clean stale backups
157
- --bump, -b Auto-bump patch version if there are uncommitted source changes
158
- --force, -f Force overwrite without prompts
159
- --help, -h Show this help message
160
-
161
- Examples:
162
- # Full installation with Chinese skills
163
- node scripts/sync-plugin.mjs
164
-
165
- # Install with English skills
166
- node scripts/sync-plugin.mjs --lang en
167
-
168
- # Developer mode: bump version, build, deploy, restart, clean up (recommended)
169
- node scripts/sync-plugin.mjs --dev
170
-
171
- # Just bump version without deploying
172
- node scripts/sync-plugin.mjs --bump --skip-build --skip-deps
173
-
174
- # Quick sync after local build
175
- node scripts/sync-plugin.mjs --skip-deps --skip-build
176
-
177
- Environment Variables:
178
- OPENCLAW_LANGUAGE Default language for skills (zh or en)
179
- `);
180
- }
181
-
182
- /**
183
- * Compare semver versions
184
- */
185
- function compareVersions(a, b) {
186
- const partsA = a.split('.').map(Number);
187
- const partsB = b.split('.').map(Number);
188
- for (let i = 0; i < 3; i++) {
189
- if (partsA[i] > partsB[i]) return 1;
190
- if (partsA[i] < partsB[i]) return -1;
191
- }
192
- return 0;
193
- }
194
-
195
- /**
196
- * Main function
197
- */
198
- function checkPrerequisites() {
199
- // Check Node.js version
200
- const nodeVersion = process.version.replace(/^v/, '');
201
- if (compareVersions(nodeVersion, MIN_NODE_VERSION) < 0) {
202
- console.error(`āŒ Node.js ${MIN_NODE_VERSION}+ required, got ${nodeVersion}`);
203
- process.exit(1);
204
- }
205
- console.log(`āœ… Node.js ${nodeVersion} (>= ${MIN_NODE_VERSION})`);
206
-
207
- // Check npm
208
- try {
209
- const npmVersion = execSync('npm --version', { encoding: 'utf-8' }).trim();
210
- console.log(`āœ… npm ${npmVersion}`);
211
-
212
- // Check for global package conflicts that cause module resolution traps
213
- console.log('šŸ” Checking for global package conflicts...');
214
- try {
215
- // Cross-platform: use stdio: 'pipe' to capture stderr, then check output
216
- const globalConflict = execSync('npm list -g principles-disciple --depth=0', {
217
- encoding: 'utf-8',
218
- stdio: ['pipe', 'pipe', 'pipe'] // Capture all streams
219
- });
220
- if (globalConflict.includes('principles-disciple')) {
221
- console.error('\nāŒ CONFLICT DETECTED: A version of "principles-disciple" is installed globally via npm.');
222
- console.error('This will block OpenClaw from loading the extension version you are trying to install.');
223
- console.error('\nACTION REQUIRED: Please run the following command first:');
224
- console.error(' npm uninstall -g principles-disciple\n');
225
- process.exit(1);
226
- }
227
- } catch (e) {
228
- // npm list returns non-zero if not found, which is what we want
229
- // Check if the error output contains the package name
230
- const output = e.stdout || e.stderr || '';
231
- if (!output.includes('principles-disciple')) {
232
- console.log('āœ… No global package conflicts detected.');
233
- } else {
234
- console.error('\nāŒ CONFLICT DETECTED: A version of "principles-disciple" is installed globally via npm.');
235
- console.error('This will block OpenClaw from loading the extension version you are trying to install.');
236
- console.error('\nACTION REQUIRED: Please run the following command first:');
237
- console.error(' npm uninstall -g principles-disciple\n');
238
- process.exit(1);
239
- }
240
- }
241
- } catch {
242
- console.error('āŒ npm not found. Please install Node.js with npm.');
243
- process.exit(1);
244
- }
245
-
246
- // Check if we're in the plugin directory
247
- if (!existsSync(join(SOURCE_DIR, 'package.json'))) {
248
- console.error('āŒ Not in plugin directory. package.json not found.');
249
- console.error(' Run this script from packages/openclaw-plugin/');
250
- process.exit(1);
251
- }
252
- }
253
-
254
- /**
255
- * Extract version from package.json
256
- */
257
- function getVersion(dir) {
258
- const pkgPath = join(dir, 'package.json');
259
- if (!existsSync(pkgPath)) return null;
260
- try {
261
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
262
- return pkg.version;
263
- } catch {
264
- return null;
265
- }
266
- }
267
-
268
- /**
269
- * Auto-bump patch version if there are uncommitted source changes.
270
- * Updates all version files: package.json (root + plugin), openclaw.plugin.json, README_ZH.md.
271
- */
272
- function autoBumpVersion(sourceDir) {
273
- const rootDir = join(sourceDir, '..', '..');
274
- const pluginDir = sourceDir;
275
-
276
- // Check for uncommitted changes in source files
277
- try {
278
- const diffOutput = execSync('git diff --name-only HEAD', {
279
- cwd: rootDir,
280
- encoding: 'utf-8',
281
- }).trim();
282
- const changedFiles = diffOutput ? diffOutput.split('\n').filter(f => {
283
- return f.startsWith('packages/openclaw-plugin/src/') ||
284
- f.startsWith('packages/openclaw-plugin/skills/') ||
285
- f.startsWith('scripts/') ||
286
- f === 'packages/openclaw-plugin/package.json' ||
287
- f === 'package.json' ||
288
- f === 'README_ZH.md';
289
- }) : [];
290
-
291
- if (changedFiles.length === 0) {
292
- console.log('šŸ“‹ No uncommitted source changes — skipping version bump');
293
- return;
294
- }
295
-
296
- console.log(`šŸ“‹ Found uncommitted changes in ${changedFiles.length} file(s) — bumping version...`);
297
-
298
- const currentVersion = getVersion(pluginDir);
299
- if (!currentVersion) {
300
- console.error('āŒ Cannot determine current version');
301
- process.exit(1);
302
- }
303
-
304
- const [major, minor, patch] = currentVersion.split('.').map(Number);
305
- const newVersion = `${major}.${minor}.${patch + 1}`;
306
-
307
- // Update package.json (plugin)
308
- const pkgPath = join(pluginDir, 'package.json');
309
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
310
- pkg.version = newVersion;
311
- writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
312
-
313
- // Update openclaw.plugin.json
314
- const manifestPath = join(pluginDir, 'openclaw.plugin.json');
315
- const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
316
- manifest.version = newVersion;
317
- writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
318
-
319
- // Update root package.json
320
- const rootPkgPath = join(rootDir, 'package.json');
321
- if (existsSync(rootPkgPath)) {
322
- const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'));
323
- rootPkg.version = newVersion;
324
- writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n', 'utf-8');
325
- }
326
-
327
- // Update README_ZH.md
328
- const readmePath = join(rootDir, 'README_ZH.md');
329
- if (existsSync(readmePath)) {
330
- let readme = readFileSync(readmePath, 'utf-8');
331
- readme = readme.replace(/v\d+\.\d+\.\d+/g, `v${newVersion}`);
332
- writeFileSync(readmePath, readme, 'utf-8');
333
- }
334
-
335
- console.log(`āœ… Version bumped: ${currentVersion} → ${newVersion}`);
336
- } catch (err) {
337
- console.warn(`āš ļø Auto-bump failed: ${err.message}`);
338
- console.warn(' Continuing with current version');
339
- }
340
- }
341
-
342
- /**
343
- * Install project dependencies
344
- */
345
- function installDependencies() {
346
- console.log('\nšŸ“¦ Installing project dependencies...');
347
-
348
- const nodeModulesDir = join(SOURCE_DIR, 'node_modules');
349
- const needsInstall = !existsSync(nodeModulesDir) ||
350
- !existsSync(join(nodeModulesDir, 'better-sqlite3'));
351
-
352
- if (!needsInstall) {
353
- console.log('āœ… Dependencies already installed');
354
- return;
355
- }
356
-
357
- try {
358
- execSync('npm install', {
359
- cwd: SOURCE_DIR,
360
- stdio: 'inherit'
361
- });
362
- console.log('āœ… Dependencies installed');
363
- } catch (error) {
364
- console.error('āŒ Failed to install dependencies');
365
- process.exit(1);
366
- }
367
- }
368
-
369
- /**
370
- * Build the plugin.
371
- */
372
- function buildPlugin() {
373
- console.log('\nšŸ”Ø Building plugin (esbuild only — bypassing tsc which may fail on unrelated files)...');
374
-
375
- try {
376
- execSync('node esbuild.config.js --production', {
377
- cwd: SOURCE_DIR,
378
- stdio: 'inherit'
379
- });
380
- } catch (error) {
381
- console.error('\nāŒ Build failed');
382
- console.error(` ${error.message}`);
383
- process.exit(1);
384
- }
385
-
386
- // Generate .d.ts declarations separately so a tsc failure on unrelated
387
- // files does not invalidate the esbuild bundle. Matches install.mjs pattern.
388
- console.log('šŸ“ Generating TypeScript declaration files...');
389
- try {
390
- execSync('npx tsc --emitDeclarationOnly', {
391
- cwd: SOURCE_DIR,
392
- stdio: 'inherit'
393
- });
394
- } catch (error) {
395
- console.warn(' āš ļø Declaration generation failed (non-blocking):', error.message);
396
- }
397
-
398
- verifyBundleContents();
399
- }
400
-
401
- /**
402
- * Build the Runtime V2 CLI that agents invoke through the `pd` command.
403
- * This is intentionally part of plugin sync so installed OpenClaw agents do not
404
- * depend on a repo checkout, stale global npm package, or guessed CLI path.
405
- */
406
- function buildPdCli() {
407
- console.log('\nšŸ”Ø Building PD CLI...');
408
-
409
- if (!existsSync(join(PD_CLI_SOURCE_DIR, 'package.json'))) {
410
- console.error(`āŒ PD CLI package not found: ${PD_CLI_SOURCE_DIR}`);
411
- process.exit(1);
412
- }
413
-
414
- try {
415
- execSync('npm run build --workspace=@principles/core', {
416
- cwd: ROOT_DIR,
417
- stdio: 'inherit'
418
- });
419
- execSync('npm run build --workspace=@principles/pd-cli', {
420
- cwd: ROOT_DIR,
421
- stdio: 'inherit'
422
- });
423
- console.log('āœ… PD CLI built');
424
- } catch (error) {
425
- console.error('\nāŒ PD CLI build failed');
426
- console.error(` ${error.message}`);
427
- process.exit(1);
428
- }
429
- }
430
-
431
- /**
432
- * Verify the built bundle contains all critical symbols.
433
- */
434
- function verifyBundleContents() {
435
- const bundleJs = join(SOURCE_DIR, 'dist', 'bundle.js');
436
- if (!existsSync(bundleJs)) {
437
- console.error('āŒ dist/bundle.js missing after build.');
438
- process.exit(1);
439
- }
440
-
441
- const content = readFileSync(bundleJs, 'utf-8');
442
- const requiredSymbols = [
443
- { name: 'EvolutionWorkerService', reason: 'main plugin service export' },
444
- { name: 'checkPainFlag', reason: 'pain flag detection' },
445
- { name: 'processEvolutionQueue', reason: 'queue processing' },
446
- { name: 'acquireQueueLock', reason: 'queue lock for pd-reflect and worker' },
447
- ];
448
-
449
- const missing = [];
450
- for (const sym of requiredSymbols) {
451
- if (!content.includes(sym.name)) {
452
- missing.push(` - ${sym.name}: ${sym.reason}`);
453
- }
454
- }
455
-
456
- if (missing.length > 0) {
457
- console.warn('\nāš ļø Bundle verification warning — symbols not found (may be minified):');
458
- missing.forEach(m => console.warn(m));
459
- }
460
-
461
- console.log('āœ… Bundle verification passed — all critical symbols present');
462
- writeBuildFingerprint();
463
- }
464
-
465
- /**
466
- * Write a build fingerprint.
467
- *
468
- * The fingerprint is PD-internal metadata and lives in
469
- * dist/build-fingerprint.json — NOT inside openclaw.plugin.json. Unknown
470
- * top-level manifest fields are rejected by ClawHub / OpenClaw manifest
471
- * validation (manifest-unknown-fields warning).
472
- */
473
- function writeBuildFingerprint() {
474
- const bundleJs = join(SOURCE_DIR, 'dist', 'bundle.js');
475
- const fingerprintPath = join(SOURCE_DIR, 'dist', 'build-fingerprint.json');
476
-
477
- let gitSha = 'unknown';
478
- try {
479
- gitSha = execSync('git rev-parse HEAD', {
480
- cwd: SOURCE_DIR,
481
- encoding: 'utf-8',
482
- timeout: 10000,
483
- }).trim().slice(0, 12);
484
- } catch {
485
- console.warn('āš ļø Could not get git SHA');
486
- }
487
-
488
- let bundleMd5 = 'unknown';
489
- try {
490
- const bundleContent = readFileSyncRaw(bundleJs);
491
- bundleMd5 = createHash('md5').update(bundleContent).digest('hex');
492
- } catch {
493
- console.warn('āš ļø Could not compute bundle MD5');
494
- }
495
-
496
- const fingerprint = {
497
- pluginId: 'principles-disciple',
498
- gitSha,
499
- bundleMd5,
500
- builtAt: new Date().toISOString(),
501
- };
502
-
503
- try {
504
- mkdirSync(join(SOURCE_DIR, 'dist'), { recursive: true });
505
- writeFileAtomic(fingerprintPath, JSON.stringify(fingerprint, null, 2) + '\n');
506
- console.log(`āœ… Build fingerprint: git=${gitSha} bundleMd5=${bundleMd5}`);
507
- } catch (err) {
508
- console.warn(`āš ļø Could not write fingerprint: ${err.message}`);
509
- }
510
-
511
- }
512
-
513
- /**
514
- * Atomic file write.
515
- */
516
- function writeFileAtomic(filePath, content) {
517
- const tmp = filePath + '.tmp.' + Date.now();
518
- writeFileSync(tmp, content, 'utf-8');
519
- rmSync(filePath, { force: true });
520
- copyFileSync(tmp, filePath);
521
- rmSync(tmp, { force: true });
522
- }
523
-
524
- /**
525
- * Verify installed fingerprint.
526
- *
527
- * Compares the PD-internal sidecars dist/build-fingerprint.json in source
528
- * and install dirs. The sidecar travels with the dist/ sync item, so after
529
- * a full sync both sides exist and must match; a mismatch means the
530
- * installed copy is stale.
531
- */
532
- function verifyInstalledFingerprint() {
533
- const sourceFpPath = join(SOURCE_DIR, 'dist', 'build-fingerprint.json');
534
- const installedFpPath = join(INSTALL_DIR, 'dist', 'build-fingerprint.json');
535
-
536
- if (!existsSync(installedFpPath)) {
537
- console.error('\nāŒ Installed fingerprint not found — dist sync incomplete.');
538
- process.exit(1);
539
- }
540
-
541
- if (!existsSync(sourceFpPath)) {
542
- console.warn('āš ļø Source build fingerprint missing — staleness check skipped.');
543
- console.warn(' Run a full build (drop --skip-build) to regenerate dist/build-fingerprint.json.');
544
- return;
545
- }
546
-
547
- try {
548
- const sourceFp = JSON.parse(readFileSync(sourceFpPath, 'utf-8'));
549
- const installedFp = JSON.parse(readFileSync(installedFpPath, 'utf-8'));
550
-
551
- const gitMismatch = sourceFp.gitSha !== installedFp.gitSha;
552
- const md5Mismatch = sourceFp.bundleMd5 !== installedFp.bundleMd5;
553
-
554
- if (gitMismatch || md5Mismatch) {
555
- console.error('\nāŒ INSTALLED PLUGIN IS STALE — FINGERPRINT MISMATCH');
556
- process.exit(1);
557
- }
558
- } catch (err) {
559
- console.warn(`āš ļø Fingerprint verification skipped: ${err.message}`);
560
- return;
561
- }
562
-
563
- console.log('āœ… Installed fingerprint verified');
564
- }
565
-
566
- /**
567
- * Update the plugin version in installs.json after successful sync.
568
- * OpenClaw manages install records in ~/.openclaw/plugins/installs.json,
569
- * NOT in openclaw.json (plugins.installs is a legacy/transient field).
570
- */
571
- function updateInstalledPluginVersion(version) {
572
- const installsDir = join(OPENCLAW_DIR, 'plugins');
573
- const installsPath = join(installsDir, 'installs.json');
574
- try {
575
- if (!existsSync(installsDir)) {
576
- mkdirSync(installsDir, { recursive: true });
577
- }
578
- let installs = { version: 1, installRecords: {} };
579
- if (existsSync(installsPath)) {
580
- const raw = readFileSync(installsPath, 'utf-8');
581
- installs = JSON.parse(raw);
582
- }
583
- if (!installs.installRecords) installs.installRecords = {};
584
- installs.installRecords['principles-disciple'] = {
585
- source: 'path',
586
- installPath: INSTALL_DIR,
587
- version: version,
588
- installedAt: new Date().toISOString(),
589
- };
590
- writeFileAtomic(installsPath, JSON.stringify(installs, null, 2) + '\n');
591
- console.log(`āœ… installs.json updated: version=${version}`);
592
- } catch (err) {
593
- console.warn(`āš ļø Could not update installs.json: ${err.message}`);
594
- }
595
- }
596
-
597
- /**
598
- * Remove existing installation directory with Windows-friendly retry logic.
599
- */
600
- function cleanTargetDir(force) {
601
- if (!existsSync(INSTALL_DIR)) return;
602
-
603
- if (!force) {
604
- const installedVersion = getVersion(INSTALL_DIR);
605
- if (installedVersion && installedVersion !== getVersion(SOURCE_DIR)) {
606
- console.error(`\nāŒ VERSION CONFLICT: Installed v${installedVersion}, Source v${getVersion(SOURCE_DIR)}`);
607
- process.exit(1);
608
- }
609
- }
610
-
611
- console.log('\nšŸ—‘ļø Removing existing installation...');
612
-
613
- // Windows often returns EPERM due to file locks, add retry logic
614
- const maxRetries = isWindows() ? 3 : 1;
615
- let lastError = null;
616
-
617
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
618
- try {
619
- rmSync(INSTALL_DIR, { recursive: true, force: true });
620
- console.log(' āœ… Removed successfully.');
621
- return;
622
- } catch (err) {
623
- lastError = err;
624
- if (err.code === 'EPERM' && attempt < maxRetries) {
625
- console.log(` āš ļø Attempt ${attempt}/${maxRetries} failed (EPERM), retrying in 2s...`);
626
- // Synchronous sleep for retry
627
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 2000);
628
- }
629
- }
630
- }
631
-
632
- // If all retries failed on Windows, try graceful fallback
633
- if (isWindows() && lastError?.code === 'EPERM') {
634
- console.log(' āš ļø Windows file lock detected, skipping removal.');
635
- console.log(' šŸ“ Will overwrite files in place.');
636
- return; // Continue with overwrite installation
637
- }
638
-
639
- throw lastError;
640
- }
641
-
642
- /**
643
- * Ensure installation directory exists.
644
- */
645
- function ensureInstallDir() {
646
- if (existsSync(INSTALL_DIR)) return;
647
- if (!existsSync(OPENCLAW_DIR)) {
648
- console.error(`āŒ OpenClaw installation not found: ${OPENCLAW_DIR}`);
649
- process.exit(1);
650
- }
651
- const extensionsDir = join(OPENCLAW_DIR, 'extensions');
652
- if (!existsSync(extensionsDir)) mkdirSync(extensionsDir, { recursive: true });
653
- mkdirSync(INSTALL_DIR, { recursive: true });
654
- }
655
-
656
- /**
657
- * Sync the openclaw.plugin.json manifest but filter the skills array to only
658
- * include paths matching the selected language (zh or en).
659
- *
660
- * This MUST run BEFORE syncSkillDirs, and it should
661
- * never be done via syncItem('openclaw.plugin.json') + subsequent modification
662
- * because the post-modification step can fail silently (catch block) leaving
663
- * both en/zh skills in the manifest, which triggers OpenClaw's "plugin skill
664
- * name collision" warning on startup.
665
- */
666
- function syncFilteredManifest(lang) {
667
- const sourcePath = join(SOURCE_DIR, 'openclaw.plugin.json');
668
- const targetPath = join(INSTALL_DIR, 'openclaw.plugin.json');
669
-
670
- if (!existsSync(sourcePath)) return;
671
-
672
- console.log(` šŸ“„ openclaw.plugin.json (filtered for lang: ${lang})`);
673
-
674
- let manifest;
675
- try {
676
- manifest = JSON.parse(readFileSync(sourcePath, 'utf-8'));
677
- } catch (err) {
678
- console.error(`āŒ Failed to read source manifest: ${err.message}`);
679
- process.exit(1);
680
- }
681
-
682
- if (!manifest.skills || !Array.isArray(manifest.skills)) return;
683
-
684
- const selectedLang = (lang || 'zh').toLowerCase();
685
- if (!['zh', 'en'].includes(selectedLang)) {
686
- console.error(`āŒ Invalid language: ${selectedLang}. Expected zh or en.`);
687
- process.exit(1);
688
- }
689
-
690
- // PR #1332 companion: the source manifest declares exactly one root
691
- // (zh, product default). REPLACE language roots with the selected one
692
- // instead of only filtering, so `--lang en` works against a zh-default
693
- // source manifest. Mirrors the production transform in
694
- // create-principles-disciple/src/skill-language.ts.
695
- const langRoots = {
696
- zh: 'templates/langs/zh/skills',
697
- en: 'templates/langs/en/skills',
698
- };
699
- const isLangRoot = (sp) =>
700
- typeof sp === 'string' &&
701
- (sp.replaceAll('\\', '/') === langRoots.zh || sp.replaceAll('\\', '/') === langRoots.en);
702
-
703
- if (!existsSync(join(SOURCE_DIR, langRoots[selectedLang]))) {
704
- console.error(`āŒ Skill templates missing for language "${selectedLang}": ${langRoots[selectedLang]}`);
705
- process.exit(1);
706
- }
707
-
708
- const kept = manifest.skills.filter((sp) => !isLangRoot(sp));
709
- manifest.skills = [...kept, langRoots[selectedLang]];
710
-
711
- // Use syncItem semantics: remove target, then write freshly
712
- if (existsSync(targetPath)) {
713
- rmSync(targetPath, { force: true });
714
- }
715
- writeFileSync(targetPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
716
- console.log(` āœ… skills filtered to lang: ${selectedLang} (${manifest.skills.length} path(s))`);
717
- }
718
-
719
- /**
720
- * @deprecated Replaced by syncFilteredManifest(). Kept for backwards
721
- * compatibility with old installs that may call it externally.
722
- * syncFilteredManifest filters the skills array BEFORE writing the manifest
723
- * to disk, avoiding the en/zh collision window entirely.
724
- */
725
- function filterInstalledManifestSkills(lang) {
726
- const installedManifestPath = join(INSTALL_DIR, 'openclaw.plugin.json');
727
- if (!existsSync(installedManifestPath)) return;
728
-
729
- try {
730
- const manifest = JSON.parse(readFileSync(installedManifestPath, 'utf-8'));
731
- if (!manifest.skills || !Array.isArray(manifest.skills)) return;
732
-
733
- const selectedLang = (lang || 'zh').toLowerCase();
734
- if (!['zh', 'en'].includes(selectedLang)) {
735
- console.error(`āŒ Invalid language: ${selectedLang}. Expected zh or en.`);
736
- process.exit(1);
737
- }
738
- const filtered = manifest.skills.filter(sp => {
739
- if (typeof sp !== 'string') return false;
740
- return sp.includes(`/langs/${selectedLang}/`);
741
- });
742
-
743
- if (filtered.length === 0) {
744
- console.error(`āŒ No skills match language "${selectedLang}" in installed manifest`);
745
- process.exit(1);
746
- }
747
-
748
- manifest.skills = filtered;
749
- writeFileSync(installedManifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
750
- console.log(` šŸ“„ openclaw.plugin.json skills filtered to lang: ${selectedLang}`);
751
- } catch (err) {
752
- console.warn(` āš ļø Failed to filter installed manifest skills: ${err.message}`);
753
- }
754
- }
755
-
756
- /**
757
- * Sync a single item.
758
- */
759
- function syncItem(item) {
760
- const source = join(SOURCE_DIR, item);
761
- const target = join(INSTALL_DIR, item);
762
- if (!existsSync(source)) return;
763
- console.log(` šŸ“„ ${item}`);
764
- if (existsSync(target)) rmSync(target, { recursive: true, force: true });
765
- try {
766
- cpSync(source, target, { recursive: true });
767
- } catch {
768
- copyFileSync(source, target);
769
- }
770
- }
771
-
772
- function quoteCmdPath(filePath) {
773
- return filePath.replace(/"/g, '""');
774
- }
775
-
776
- /**
777
- * Copy the built pd-cli package into the plugin install directory and create
778
- * deterministic shims. The global shim points at the installed plugin copy, not
779
- * at a developer checkout.
780
- */
781
- function syncPdCli() {
782
- console.log('\nšŸ“¦ Syncing PD CLI...');
783
-
784
- const distDir = join(PD_CLI_SOURCE_DIR, 'dist');
785
- if (!existsSync(join(distDir, 'index.js'))) {
786
- console.error('āŒ PD CLI dist/index.js missing. Run without --skip-build or build @principles/pd-cli first.');
787
- process.exit(1);
788
- }
789
-
790
- rmSync(INSTALLED_PD_CLI_DIR, { recursive: true, force: true });
791
- mkdirSync(INSTALLED_PD_CLI_DIR, { recursive: true });
792
- cpSync(distDir, join(INSTALLED_PD_CLI_DIR, 'dist'), { recursive: true });
793
- copyFileSync(join(PD_CLI_SOURCE_DIR, 'package.json'), join(INSTALLED_PD_CLI_DIR, 'package.json'));
794
-
795
- mkdirSync(INSTALLED_BIN_DIR, { recursive: true });
796
- const installedEntry = join(INSTALLED_PD_CLI_DIR, 'dist', 'index.js');
797
-
798
- if (isWindows()) {
799
- const cmdShim = [
800
- '@echo off',
801
- `node "${quoteCmdPath(installedEntry)}" %*`,
802
- '',
803
- ].join('\r\n');
804
- const psShim = [
805
- '$ErrorActionPreference = "Stop"',
806
- `$entry = "${installedEntry.replace(/`/g, '``').replace(/"/g, '`"')}"`,
807
- '& node $entry @args',
808
- 'exit $LASTEXITCODE',
809
- '',
810
- ].join('\r\n');
811
- writeFileSync(join(INSTALLED_BIN_DIR, 'pd.cmd'), cmdShim, 'utf-8');
812
- writeFileSync(join(INSTALLED_BIN_DIR, 'pd.ps1'), psShim, 'utf-8');
813
- } else {
814
- const shShim = [
815
- '#!/usr/bin/env sh',
816
- `exec node "${installedEntry.replace(/"/g, '\\"')}" "$@"`,
817
- '',
818
- ].join('\n');
819
- const target = join(INSTALLED_BIN_DIR, 'pd');
820
- writeFileSync(target, shShim, 'utf-8');
821
- chmodSync(target, 0o755);
822
- }
823
-
824
- installGlobalPdShim();
825
- }
826
-
827
- function getNpmGlobalBinDir() {
828
- try {
829
- const prefix = execSync('npm prefix -g', { encoding: 'utf-8' }).trim();
830
- if (!prefix) return null;
831
- return process.platform === 'win32' ? prefix : join(prefix, 'bin');
832
- } catch {
833
- return null;
834
- }
835
- }
836
-
837
- function installGlobalPdShim() {
838
- const globalBin = getNpmGlobalBinDir();
839
- if (!globalBin) {
840
- console.warn('āš ļø Could not resolve npm global bin directory. Use plugin-local bin/pd shim.');
841
- return;
842
- }
843
-
844
- try {
845
- mkdirSync(globalBin, { recursive: true });
846
- if (isWindows()) {
847
- const pluginCmd = join(INSTALLED_BIN_DIR, 'pd.cmd');
848
- const pluginPs = join(INSTALLED_BIN_DIR, 'pd.ps1');
849
- writeFileSync(join(globalBin, 'pd.cmd'), `@echo off\r\ncall "${quoteCmdPath(pluginCmd)}" %*\r\n`, 'utf-8');
850
- writeFileSync(
851
- join(globalBin, 'pd.ps1'),
852
- `$shim = "${pluginPs.replace(/`/g, '``').replace(/"/g, '`"')}"\r\n& $shim @args\r\nexit $LASTEXITCODE\r\n`,
853
- 'utf-8'
854
- );
855
- } else {
856
- const pluginSh = join(INSTALLED_BIN_DIR, 'pd');
857
- const globalSh = join(globalBin, 'pd');
858
- writeFileSync(globalSh, `#!/usr/bin/env sh\nexec "${pluginSh.replace(/"/g, '\\"')}" "$@"\n`, 'utf-8');
859
- chmodSync(globalSh, 0o755);
860
- }
861
- console.log(`āœ… Global pd shim installed: ${globalBin}`);
862
- } catch (err) {
863
- console.warn(`āš ļø Could not install global pd shim: ${err.message}`);
864
- console.warn(` Use plugin-local shim: ${join(INSTALLED_BIN_DIR, isWindows() ? 'pd.cmd' : 'pd')}`);
865
- }
866
- }
867
-
868
- function verifyPdCliShim() {
869
- const localShim = join(INSTALLED_BIN_DIR, isWindows() ? 'pd.cmd' : 'pd');
870
- try {
871
- execSync(`"${localShim}" --version`, { stdio: 'pipe', shell: true });
872
- console.log(`āœ… PD CLI shim verified: ${localShim}`);
873
- } catch (err) {
874
- console.error(`āŒ PD CLI shim verification failed: ${err.message}`);
875
- process.exit(1);
876
- }
877
- }
878
-
879
- /**
880
- * Recursive directory copy (Windows-safe, no symlinks).
881
- */
882
- function copyDir(src, dest) {
883
- mkdirSync(dest, { recursive: true });
884
- for (const entry of readdirSync(src)) {
885
- const srcPath = join(src, entry);
886
- const destPath = join(dest, entry);
887
- if (lstatSync(srcPath).isDirectory()) {
888
- copyDir(srcPath, destPath);
889
- } else {
890
- copyFileSync(srcPath, destPath);
891
- }
892
- }
893
- }
894
-
895
- /**
896
- * Inject local workspace packages (monorepo) into node_modules after npm install.
897
- * @principles/core from the local monorepo is authoritative because the
898
- * npm-published package line lacks exports required by the current pd-cli.
899
- */
900
- function injectLocalWorkspacePackages() {
901
- const monorepoModules = join(SOURCE_DIR, '..', '..', 'node_modules', '@principles', 'core');
902
- const targetModules = join(INSTALL_DIR, 'node_modules', '@principles', 'core');
903
-
904
- if (!existsSync(monorepoModules)) {
905
- // Not in monorepo context (e.g., npm pack / CI tarball) — skip
906
- return;
907
- }
908
-
909
- console.log(' šŸ“¦ Injecting local workspace packages (@principles/core)...');
910
-
911
- // Remove the target FIRST, because cp -rL on Windows copies the source
912
- // directory INTO the existing target, creating a nested core/core/ that
913
- // leaves the original npm-version package.json untouched. We must start
914
- // with a clean slate so the local version is authoritative.
915
- if (existsSync(targetModules)) {
916
- rmSync(targetModules, { recursive: true, force: true });
917
- }
918
-
919
- mkdirSync(dirname(targetModules), { recursive: true });
920
- // Use copyDir (Node-based, no cp -rL semantics trap) for reliable
921
- // cross-platform overwrite. cpSync creates symlinks on Windows for
922
- // symlinked dirs — copyDir dereferences by reading file contents.
923
- try {
924
- copyDir(monorepoModules, targetModules);
925
- console.log(' āœ… @principles/core local build injected');
926
- } catch (copyErr) {
927
- console.warn(' āš ļø Failed to inject @principles/core from monorepo: ' + copyErr.message);
928
- console.warn(' āš ļø npm version remains in place — pd-cli may fail if exports differ');
929
- }
930
- }
931
-
932
- /**
933
- * Install production dependencies in target.
934
- */
935
- function installTargetDependencies() {
936
- console.log('\nšŸ“¦ Installing production dependencies in target...');
937
- try {
938
- // --legacy-peer-deps: the plugin's devDependencies include @typescript-eslint
939
- // (peer typescript <6.1) which npm 7+ strictly validates even under --omit=dev,
940
- // causing ETARGET. Since production install omits devDeps anyway, legacy peer
941
- // resolution is safe and only bypasses the over-strict peer check.
942
- //
943
- // --prefer-offline removed: it caused ETARGET for better-sqlite3@^13.0.1 when
944
- // the local npm cache was stale (registry had 13.0.3 but cache only had an
945
- // older resolution). Letting npm use its default offline/online heuristic
946
- // avoids the stale-cache hard failure.
947
- execSync('npm install --omit=dev --no-audit --no-fund --legacy-peer-deps', {
948
- cwd: INSTALL_DIR,
949
- stdio: 'inherit'
950
- });
951
- console.log('āœ… Dependencies installed');
952
- } catch (error) {
953
- console.error(`\nāŒ npm install failed: ${error.message}`);
954
- process.exit(1);
955
- }
956
- }
957
-
958
- /**
959
- * Verify injected workspace packages have their transitive dependencies installed.
960
- * npm install --omit=dev in the target doesn't resolve deps of manually-copied packages,
961
- * so transitive deps like @earendil-works/pi-agent-core may be missing after production install.
962
- */
963
- function verifyInjectedWorkspaceDeps() {
964
- const corePkgDir = join(INSTALL_DIR, 'node_modules', '@principles', 'core');
965
- if (!existsSync(corePkgDir)) return;
966
-
967
- const corePkgPath = join(corePkgDir, 'package.json');
968
- if (!existsSync(corePkgPath)) return;
969
-
970
- let corePkg;
971
- try {
972
- corePkg = JSON.parse(readFileSync(corePkgPath, 'utf-8'));
973
- } catch (error) {
974
- console.error(` āŒ Failed to parse @principles/core/package.json: ${error.message}`);
975
- process.exit(1);
976
- }
977
-
978
- const deps = corePkg.dependencies || {};
979
- const missing = [];
980
- for (const [dep, version] of Object.entries(deps)) {
981
- if (!existsSync(join(INSTALL_DIR, 'node_modules', dep))) {
982
- missing.push(`${dep}@${version}`);
983
- }
984
- }
985
-
986
- if (missing.length === 0) return;
987
-
988
- console.log(` āš ļø ${missing.length} transitive dependenc${missing.length === 1 ? 'y' : 'ies'} of @principles/core missing, installing...`);
989
- try {
990
- execSync(`npm install ${missing.join(' ')} --no-audit --no-fund --prefer-offline`, {
991
- cwd: INSTALL_DIR,
992
- stdio: 'pipe'
993
- });
994
- console.log(' āœ… Transitive dependencies installed');
995
- } catch (error) {
996
- console.warn(` āš ļø Failed to install transitive dependencies: ${error.message}`);
997
- }
998
- }
999
-
1000
- /**
1001
- * Clean stale backups.
1002
- */
1003
- function cleanStaleBackups() {
1004
- const extensionsDir = join(OPENCLAW_DIR, 'extensions');
1005
- if (!existsSync(extensionsDir)) return;
1006
- const entries = readdirSync(extensionsDir);
1007
- const backups = entries.filter(e => e.startsWith('principles-disciple.backup') || e.startsWith('principles-disciple.old'));
1008
- for (const backup of backups) {
1009
- rmSync(join(extensionsDir, backup), { recursive: true, force: true });
1010
- console.log(` Removed: ${backup}`);
1011
- }
1012
- }
1013
-
1014
- /**
1015
- * Check if running on Windows.
1016
- */
1017
- function isWindows() {
1018
- return process.platform === 'win32';
1019
- }
1020
-
1021
- /**
1022
- * Check if a process is running by PID.
1023
- */
1024
- function isProcessRunning(pid) {
1025
- try {
1026
- if (isWindows()) {
1027
- execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { stdio: 'ignore' });
1028
- return true;
1029
- } else {
1030
- process.kill(pid, 0);
1031
- return true;
1032
- }
1033
- } catch {
1034
- return false;
1035
- }
1036
- }
1037
-
1038
- /**
1039
- * Get temporary directory path (cross-platform).
1040
- */
1041
- function getTempDir() {
1042
- if (isWindows()) {
1043
- return process.env.TEMP || process.env.TMP || 'C:\\Windows\\Temp';
1044
- }
1045
- return '/tmp';
1046
- }
1047
-
1048
- /**
1049
- * Strip Anaconda/Miniconda entries from PATH on Windows.
1050
- *
1051
- * Anaconda ships an ancient cygpath (2016, at
1052
- * D:\ProgramData\anaconda3\Library\usr\bin\cygpath) that converts Unix-style
1053
- * paths INCORRECTLY in MINGW/MSYS — e.g. "/c/Users/..." becomes
1054
- * "D:\ProgramData\anaconda3\Library\c\Users\..." instead of "C:\Users\...".
1055
- * npm-installed CLI POSIX shims (openclaw, pd, etc.) call cygpath -w for path
1056
- * conversion and break when the wrong cygpath is found first in PATH.
1057
- *
1058
- * This does not affect schtasks (runs in a clean session) or PowerShell
1059
- * Start-Process (uses Split-Path, not cygpath). It protects the port check
1060
- * and error-recovery paths that execSync child processes.
1061
- */
1062
- function sanitizePathForWindows() {
1063
- const pathSep = isWindows() ? ';' : ':';
1064
- const originalPath = process.env.PATH || '';
1065
- const entries = originalPath.split(pathSep).filter(Boolean);
1066
- const sanitized = entries.filter(entry => {
1067
- const lower = entry.toLowerCase().replace(/[/\\]/g, '/');
1068
- return !lower.includes('/anaconda') && !lower.includes('/conda') && !lower.includes('/miniconda');
1069
- });
1070
- if (sanitized.length !== entries.length) {
1071
- const removed = entries.length - sanitized.length;
1072
- process.env.PATH = sanitized.join(pathSep);
1073
- console.log(` 🧹 Sanitized PATH: removed ${removed} Anaconda/Conda entr${removed > 1 ? 'ies' : 'y'} (fixes cygpath bug on MINGW/MSYS)`);
1074
- }
1075
- }
1076
-
1077
- /**
1078
- * Restart OpenClaw Gateway (cross-platform).
1079
- */
1080
- function restartGateway() {
1081
- console.log('\nšŸ”„ Restarting OpenClaw Gateway...');
1082
-
1083
- if (isWindows()) {
1084
- return restartGatewayWindows();
1085
- } else {
1086
- return restartGatewayLinux();
1087
- }
1088
- }
1089
-
1090
- /**
1091
- * Restart Gateway on Windows via schtasks.
1092
- * Direct task trigger bypasses OpenClaw CLI's busy-port detection which can
1093
- * falsely report "still busy" due to TIME_WAIT connections on the port.
1094
- */
1095
- function restartGatewayWindows() {
1096
- const logPath = join(getTempDir(), 'openclaw-auto-restart.log');
1097
-
1098
- // Strip Anaconda from PATH so any execSync child process (especially the
1099
- // port-check Test-NetConnection shell) does not inherit poisoned cygpath.
1100
- sanitizePathForWindows();
1101
-
1102
- try {
1103
- // Kill existing gateway processes first — taskkill fails across Windows sessions,
1104
- // use WMIC which can terminate processes regardless of session boundary.
1105
- console.log(' Stopping existing gateway processes...');
1106
- try {
1107
- const findPids = `Get-NetTCPConnection -LocalPort 18789 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique`;
1108
- const pids = execSync(`powershell -NoProfile -Command "${findPids}"`, { encoding: 'utf-8' }).trim();
1109
- if (pids) {
1110
- const pidList = pids.split('\n').filter(p => p.trim() && p !== '0');
1111
- for (const pid of pidList) {
1112
- const pidTrim = pid.trim();
1113
- try {
1114
- execSync(`wmic process where "ProcessId=${pidTrim}" call terminate`, { stdio: 'ignore' });
1115
- } catch { /* ignore individual failures */ }
1116
- }
1117
- execSync('timeout /t 3 /nobreak', { shell: true, stdio: 'ignore' });
1118
- }
1119
- } catch { /* no existing processes */ }
1120
-
1121
- // Clean stale lock files — prevents ghost gateway false positives
1122
- try {
1123
- const lockDir = join(getTempDir(), 'openclaw');
1124
- if (existsSync(lockDir)) {
1125
- const files = readdirSync(lockDir).filter(f => f.startsWith('gateway.') && f.endsWith('.lock'));
1126
- for (const file of files) {
1127
- const lockPath = join(lockDir, file);
1128
- try {
1129
- const content = readFileSync(lockPath, 'utf-8');
1130
- const pid = parseInt(content.trim());
1131
- // Only remove if PID is not running
1132
- if (!pid || !isProcessRunning(pid)) {
1133
- rmSync(lockPath, { force: true });
1134
- console.log(` Removed stale lock: ${file}`);
1135
- }
1136
- } catch { /* ignore */ }
1137
- }
1138
- }
1139
- } catch { /* ignore lock cleanup failures */ }
1140
-
1141
- // Trigger via schtasks — reliable, avoids CLI busy-port misdetection
1142
- console.log(' Starting gateway via scheduled task...');
1143
- let gatewayStarted = false;
1144
- try {
1145
- execSync('schtasks /Run /TN "OpenClaw Gateway"', { stdio: 'pipe' });
1146
- gatewayStarted = true;
1147
- } catch {
1148
- // schtasks failed — try direct CLI fallback via PowerShell
1149
- // PowerShell shim resolves paths correctly in Anaconda environments
1150
- // where cmd /c can produce wrong path prefixes (e.g. conda prefix + npm path)
1151
- console.log(' Scheduled task not available, trying direct gateway start via PowerShell...');
1152
- try {
1153
- execSync(
1154
- 'powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath \'openclaw\' -ArgumentList \'gateway\'"',
1155
- { stdio: 'pipe', timeout: 5000 }
1156
- );
1157
- gatewayStarted = true;
1158
- } catch {
1159
- // Gateway may already be running or no perms — not fatal
1160
- }
1161
- }
1162
-
1163
- // Wait for gateway to be listening on port (åŒę­„ē­‰å¾…ļ¼Œäøå¼‚ę­„)
1164
- const port = 18789;
1165
- const deadline = Date.now() + 60000; // Gateway åˆå§‹åŒ–åÆčƒ½éœ€č¦ 20+ ē§’ (如 Windows schtasks é‡åÆ)
1166
- let gatewayListening = false;
1167
-
1168
- while (Date.now() < deadline) {
1169
- try {
1170
- // 使用 Test-NetConnection ę›æä»£ęœ‰čÆ­ę³•é—®é¢˜ēš„ Get-NetTCPConnection + Measure-Object
1171
- const result = execSync(
1172
- `powershell -NoProfile -Command "(Test-NetConnection -ComputerName localhost -Port ${port} -WarningAction SilentlyContinue).TcpTestSucceeded"`,
1173
- { encoding: 'utf-8', timeout: 5000 }
1174
- ).trim();
1175
- if (result === 'True') {
1176
- gatewayListening = true;
1177
- break;
1178
- }
1179
- } catch { /* port not listening yet */ }
1180
- try {
1181
- execSync('timeout /t 1 /nobreak', { shell: true, stdio: 'ignore' });
1182
- } catch { /* ignore */ }
1183
- }
1184
-
1185
- if (!gatewayListening) {
1186
- console.warn('\nāš ļø Gateway may not be running on port 18789 (plugin files are installed).');
1187
- console.warn(' If the gateway was started but is unreachable, try:');
1188
- console.warn(' 1. From PowerShell: openclaw gateway start');
1189
- console.warn(' 2. If using Git Bash, strip Anaconda from PATH first:');
1190
- console.warn(' PATH=$(echo "$PATH" | tr ":" "\\n" | grep -iv anaconda | tr "\\n" ":") openclaw gateway start');
1191
- return false;
1192
- }
1193
-
1194
- console.log(' āœ… Gateway is listening on port 18789');
1195
- return true;
1196
-
1197
- } catch (error) {
1198
- console.warn(`\nāš ļø Gateway restart failed: ${error.message}`);
1199
- console.warn(' Plugin files are installed. Run "openclaw gateway start" manually.');
1200
- console.warn(' If using Git Bash with Anaconda in PATH, first run:');
1201
- console.warn(' export PATH=$(echo "$PATH" | tr ":" "\\n" | grep -iv anaconda | tr "\\n" ":")');
1202
- return false;
1203
- }
1204
- }
1205
-
1206
- /**
1207
- * Restart Gateway on Linux using systemctl or process management.
1208
- */
1209
- function restartGatewayLinux() {
1210
- const logPath = '/tmp/openclaw-auto-restart.log';
1211
-
1212
- try {
1213
- // Try systemctl first (Linux systemd)
1214
- try {
1215
- execSync('systemctl --user is-active openclaw-gateway.service', { stdio: 'pipe' });
1216
- console.log(' Restarting via systemctl...');
1217
- execSync('systemctl --user restart openclaw-gateway.service', { stdio: 'inherit' });
1218
- console.log('āœ… Gateway restarted via systemctl.');
1219
-
1220
- console.log(' Waiting for Gateway to initialize and load PD plugin (8s)...');
1221
- setTimeout(() => {
1222
- try {
1223
- const status = execSync('systemctl --user is-active openclaw-gateway.service', { encoding: 'utf-8' }).trim();
1224
- if (status === 'active') {
1225
- console.log('āœ… Gateway is running.');
1226
- const logs = execSync('journalctl --user -u openclaw-gateway.service --since "10 seconds ago"', { encoding: 'utf-8' });
1227
- if (logs.includes('Principles Disciple Plugin registered')) {
1228
- console.log('āœ… SUCCESS: Principles Disciple plugin registered successfully!');
1229
- } else if (logs.includes('failed to load') || logs.includes('Error: Cannot find module')) {
1230
- console.error('\nāŒ CRITICAL: Gateway is running but PD plugin FAILED to load!');
1231
- process.exit(1);
1232
- } else {
1233
- console.warn('āš ļø Gateway started but PD registration not confirmed in recent logs.');
1234
- }
1235
- } else {
1236
- console.error(`āŒ Gateway status: ${status}.`);
1237
- process.exit(1);
1238
- }
1239
- } catch (e) {
1240
- console.warn(`āš ļø Post-restart verification skipped: ${e.message}`);
1241
- }
1242
- }, 8000);
1243
- return true; // systemctl restart triggered successfully
1244
- } catch { /* systemctl not available, fall through to manual restart */ }
1245
-
1246
- // Manual process management
1247
- const pids = execSync('pgrep -f "openclaw-gateway|openclaw gateway"', { encoding: 'utf-8' }).trim();
1248
- if (pids) {
1249
- console.log(` Terminating existing gateway process(es)...`);
1250
- execSync(`echo "${pids}" | xargs kill -TERM 2>/dev/null || true`);
1251
- execSync('sleep 3');
1252
- }
1253
-
1254
- console.log(` Starting new gateway (logs: ${logPath})...`);
1255
- execSync(`nohup openclaw gateway --force > ${logPath} 2>&1 &`, { stdio: 'ignore' });
1256
-
1257
- // Wait for process to actually start (up to 15s)
1258
- const deadline = Date.now() + 15000;
1259
- while (Date.now() < deadline) {
1260
- try {
1261
- const running = execSync(
1262
- 'pgrep -f "openclaw-gateway|openclaw gateway"',
1263
- { encoding: 'utf-8', stdio: 'pipe' }
1264
- ).trim();
1265
- if (running) {
1266
- console.log('āœ… Gateway restart triggered.');
1267
- return true;
1268
- }
1269
- } catch { /* not ready yet */ }
1270
- execSync('sleep 1');
1271
- }
1272
-
1273
- console.error(`āŒ Gateway did not come back up. Check ${logPath}`);
1274
- return false;
1275
-
1276
- } catch (error) {
1277
- console.error(`\nāŒ Failed to restart gateway: ${error.message}`);
1278
- return false;
1279
- }
1280
- }
1281
-
1282
- /**
1283
- * Main function
1284
- */
1285
- function main() {
1286
- const args = parseArgs();
1287
- if (args.help) {
1288
- showHelp();
1289
- process.exit(0);
1290
- }
1291
-
1292
- console.log('╔════════════════════════════════════════════════════════════╗');
1293
- console.log('ā•‘ Principles Disciple Plugin Installer ā•‘');
1294
- console.log('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n');
1295
-
1296
- if (args.dev) console.log('šŸ› ļø DEV MODE: force + restart + bump + stale backup cleanup\n');
1297
- if (args.bump) autoBumpVersion(SOURCE_DIR);
1298
-
1299
- const sourceVersion = getVersion(SOURCE_DIR);
1300
- if (!sourceVersion) {
1301
- console.error('āŒ Cannot determine source version.');
1302
- process.exit(1);
1303
- }
1304
- console.log(`šŸ“‹ Plugin version: v${sourceVersion}`);
1305
- console.log(`šŸŒ Language: ${args.lang}`);
1306
-
1307
- console.log('\nšŸ” Checking prerequisites...');
1308
- checkPrerequisites();
1309
-
1310
- if (!args.skipDeps) installDependencies();
1311
-
1312
- if (!args.skipBuild) {
1313
- buildPlugin();
1314
- buildPdCli();
1315
- } else {
1316
- console.log('\nā­ļø Skipping build step (--skip-build)');
1317
- }
1318
- cleanTargetDir(args.force);
1319
- ensureInstallDir();
1320
-
1321
- console.log('\nšŸ“¦ Syncing files to OpenClaw...');
1322
- for (const item of SYNC_ITEMS) syncItem(item);
1323
-
1324
- // After syncing templates, remove the non-selected language to avoid
1325
- // waste on disk. OpenClaw only loads skills declared in the filtered
1326
- // manifest (syncFilteredManifest), but having unselected lang files on
1327
- // disk inflates the install footprint and confuses inspection.
1328
- {
1329
- const langsDir = join(INSTALL_DIR, 'templates', 'langs');
1330
- if (existsSync(langsDir)) {
1331
- const normalizedLang = (args.lang || 'zh').toLowerCase();
1332
- const unselectedLang = normalizedLang === 'zh' ? 'en' : 'zh';
1333
- const unselectedPath = join(langsDir, unselectedLang);
1334
- if (existsSync(unselectedPath)) {
1335
- rmSync(unselectedPath, { recursive: true, force: true });
1336
- console.log(` šŸ—‘ļø removed templates/langs/${unselectedLang} (lang: ${normalizedLang})`);
1337
- }
1338
- }
1339
- }
1340
-
1341
- syncFilteredManifest(args.lang);
1342
- syncPdCli();
1343
-
1344
- // IMPORTANT: npm install must run FIRST, then injectLocalWorkspacePackages
1345
- // overwrites @principles/core with the local monorepo version.
1346
- //
1347
- // Why: @principles/core on npm (v1.164.0-1.174.0, from a different publish
1348
- // line) is semantically "^1.74.1"-compatible but its exports field does NOT
1349
- // include ./principle-tree-ledger that pd-cli/health.js requires. The npm
1350
- // registry version must never be used — only the local monorepo build has
1351
- // the correct exports. Injecting BEFORE npm install is useless because npm
1352
- // overwrites the injected copy. The authoritative source is always the
1353
- // local monorepo, applied AFTER npm install.
1354
- installTargetDependencies();
1355
- injectLocalWorkspacePackages();
1356
- verifyInjectedWorkspaceDeps();
1357
- verifyPdCliShim();
1358
-
1359
- console.log('\nšŸ” Verifying installed plugin can load native dependencies...');
1360
- try {
1361
- execSync(`node -e "require('better-sqlite3')"`, { cwd: INSTALL_DIR, stdio: 'pipe' });
1362
- console.log('āœ… Native dependencies verified');
1363
- } catch (error) {
1364
- console.warn('\nāš ļø Native module better-sqlite3 failed to load. Attempting automatic rebuild...');
1365
- try {
1366
- execSync('npm rebuild better-sqlite3', { cwd: INSTALL_DIR, stdio: 'inherit' });
1367
- execSync(`node -e "require('better-sqlite3')"`, { cwd: INSTALL_DIR, stdio: 'pipe' });
1368
- console.log('āœ… Rebuild successful!');
1369
- } catch (rebuildErr) {
1370
- console.error('\nāŒ CRITICAL: better-sqlite3 rebuild failed!');
1371
- process.exit(1);
1372
- }
1373
- }
1374
-
1375
- const bootstrapScript = join(SOURCE_DIR, 'scripts', 'bootstrap-rules.mjs');
1376
- if (existsSync(bootstrapScript)) {
1377
- console.log('\n🧠 Synchronizing principles to active rules (Bootstrap)...');
1378
- try {
1379
- const workspaceDir = getConfiguredWorkspaceDir();
1380
- const targetStateDir = join(workspaceDir, '.state');
1381
- if (existsSync(targetStateDir)) {
1382
- execSync(`node scripts/bootstrap-rules.mjs`, { cwd: SOURCE_DIR, stdio: 'inherit', env: { ...process.env, STATE_DIR: targetStateDir, BOOTSTRAP_LIMIT: '100' } });
1383
- console.log('āœ… Principles synchronized.');
1384
- }
1385
- } catch (e) {
1386
- console.warn(`āš ļø Principle synchronization failed: ${e.message}`);
1387
- }
1388
- }
1389
-
1390
- const compileScript = join(SOURCE_DIR, 'scripts', 'compile-principles.mjs');
1391
- if (existsSync(compileScript)) {
1392
- console.log('\nāš™ļø Compiling pain-derived principles into rules...');
1393
- try {
1394
- const workspaceDir = getConfiguredWorkspaceDir();
1395
- const targetWorkspaceDir = workspaceDir;
1396
- if (existsSync(targetWorkspaceDir)) {
1397
- execSync(`node scripts/compile-principles.mjs ${targetWorkspaceDir}`, { cwd: SOURCE_DIR, stdio: 'inherit' });
1398
- console.log('āœ… Principle compilation complete.');
1399
- }
1400
- } catch (e) {
1401
- console.warn(`āš ļø Principle compilation failed: ${e.message}`);
1402
- }
1403
- }
1404
-
1405
- const installedVersion = getVersion(INSTALL_DIR);
1406
- if (installedVersion !== sourceVersion) {
1407
- console.error(`\nāŒ VERSION MISMATCH: Expected v${sourceVersion}, Got v${installedVersion}`);
1408
- process.exit(1);
1409
- }
1410
-
1411
- verifyInstalledFingerprint();
1412
- updateInstalledPluginVersion(sourceVersion);
1413
- if (args.dev || args.restart) cleanStaleBackups();
1414
-
1415
- console.log('\n╔════════════════════════════════════════════════════════════╗');
1416
- console.log('ā•‘ āœ… Installation Complete ā•‘');
1417
- console.log('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•');
1418
-
1419
- if (args.restart) {
1420
- const restarted = restartGateway();
1421
- if (!restarted) {
1422
- process.exitCode = 1;
1423
- console.warn('\nāš ļø Plugin files installed but gateway restart was not confirmed.');
1424
- console.warn(' The gateway may already be running. Verify with:');
1425
- console.warn(' PowerShell: openclaw gateway status');
1426
- console.warn(' Git Bash: Test-NetConnection -ComputerName localhost -Port 18789');
1427
- console.warn('');
1428
- console.warn(' If "openclaw gateway start" fails with a path error on Git Bash:');
1429
- console.warn(' this is caused by Anaconda\'s old cygpath tool polluting PATH.');
1430
- console.warn(' Run: export PATH=$(echo "$PATH" | tr ":" "\\n" | grep -iv anaconda | tr "\\n" ":")');
1431
- console.warn(' Then: openclaw gateway start');
1432
- console.warn('');
1433
- console.warn(' Or simply restart from PowerShell (not affected by the cygpath bug).');
1434
- console.error('\nINSTALLATION INCOMPLETE: plugin files were copied, but the running product was not verified.');
1435
- }
1436
- } else {
1437
- console.log('\nšŸ’” Restart OpenClaw Gateway to load the new version.');
1438
- console.log(' (Plugin code changes require a full gateway restart)');
1439
- }
1440
- }
1441
-
1442
- main();