principles-disciple 1.72.0 → 1.73.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/openclaw.plugin.json +10 -5
  2. package/package.json +17 -19
  3. package/scripts/acceptance-test.mjs +16 -73
  4. package/scripts/sync-plugin.mjs +382 -77
  5. package/src/commands/archive-impl.ts +2 -1
  6. package/src/commands/capabilities.ts +2 -2
  7. package/src/commands/context.ts +2 -2
  8. package/src/commands/disable-impl.ts +2 -1
  9. package/src/commands/evolution-status.ts +16 -16
  10. package/src/commands/export.ts +12 -67
  11. package/src/commands/pain.ts +91 -1
  12. package/src/commands/principle-rollback.ts +2 -1
  13. package/src/commands/promote-impl.ts +7 -43
  14. package/src/commands/rollback-impl.ts +2 -1
  15. package/src/commands/rollback.ts +2 -1
  16. package/src/commands/samples.ts +2 -1
  17. package/src/commands/thinking-os.ts +2 -1
  18. package/src/config/errors.ts +18 -2
  19. package/src/constants/diagnostician.ts +2 -2
  20. package/src/constants/tools.ts +2 -1
  21. package/src/core/__tests__/focus-history.test.ts +210 -0
  22. package/src/core/config.ts +1 -1
  23. package/src/core/confirm-first-gate.ts +255 -0
  24. package/src/core/correction-cue-learner.ts +2 -136
  25. package/src/core/correction-types.ts +16 -88
  26. package/src/core/dictionary.ts +19 -20
  27. package/src/core/empathy-keyword-matcher.ts +17 -289
  28. package/src/core/empathy-types.ts +18 -229
  29. package/src/core/event-log.ts +38 -132
  30. package/src/core/evolution-reducer.ts +21 -2
  31. package/src/core/evolution-types.ts +76 -464
  32. package/src/core/file-store.ts +80 -0
  33. package/src/core/focus-history.ts +228 -955
  34. package/src/core/local-worker-routing.ts +34 -314
  35. package/src/core/merge-gate-audit.ts +0 -195
  36. package/src/core/pain-diagnostic-gate.ts +154 -0
  37. package/src/core/pain-signal.ts +21 -138
  38. package/src/core/pain.ts +15 -88
  39. package/src/core/pd-task-reconciler.ts +26 -115
  40. package/src/core/pd-task-service.ts +9 -9
  41. package/src/core/pd-task-types.ts +23 -127
  42. package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
  43. package/src/core/principle-compiler/code-validator.ts +15 -42
  44. package/src/core/principle-compiler/compiler.ts +100 -15
  45. package/src/core/principle-compiler/index.ts +5 -2
  46. package/src/core/principle-compiler/template-generator.ts +4 -104
  47. package/src/core/principle-injection.ts +10 -202
  48. package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
  49. package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
  50. package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
  51. package/src/core/principle-tree-ledger-adapter.ts +145 -0
  52. package/src/core/principle-tree-ledger.ts +8 -6
  53. package/src/core/reflection/reflection-context.ts +14 -109
  54. package/src/core/replay-engine.ts +8 -500
  55. package/src/core/rule-host-helpers.ts +5 -35
  56. package/src/core/rule-host-types.ts +10 -82
  57. package/src/core/rule-host.ts +6 -63
  58. package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
  59. package/src/core/session-tracker.ts +87 -101
  60. package/src/core/shadow-observation-registry.ts +19 -48
  61. package/src/core/trajectory.ts +3 -1
  62. package/src/core/workflow-funnel-loader.ts +62 -68
  63. package/src/core/workspace-context.ts +46 -0
  64. package/src/core/workspace-dir-service.ts +1 -1
  65. package/src/core/workspace-dir-validation.ts +18 -9
  66. package/src/hooks/AGENTS.md +1 -1
  67. package/src/hooks/gate-block-helper.ts +46 -44
  68. package/src/hooks/gate.ts +207 -7
  69. package/src/hooks/lifecycle.ts +30 -32
  70. package/src/hooks/llm.ts +60 -32
  71. package/src/hooks/pain.ts +297 -103
  72. package/src/hooks/prompt.ts +459 -439
  73. package/src/hooks/subagent.ts +2 -29
  74. package/src/i18n/commands.ts +2 -10
  75. package/src/index.ts +95 -85
  76. package/src/openclaw-sdk.ts +311 -0
  77. package/src/service/central-database.ts +8 -4
  78. package/src/service/evolution-queue-migration.ts +2 -1
  79. package/src/service/evolution-worker.ts +163 -1786
  80. package/src/service/internalization-trigger-adapter.ts +302 -0
  81. package/src/service/keyword-optimization-service.ts +4 -4
  82. package/src/service/monitoring-query-service.ts +1 -215
  83. package/src/service/queue-io.ts +60 -331
  84. package/src/service/runtime-summary-service.ts +59 -16
  85. package/src/service/subagent-workflow/index.ts +0 -41
  86. package/src/service/subagent-workflow/types.ts +9 -120
  87. package/src/service/subagent-workflow/workflow-store.ts +2 -119
  88. package/src/service/workflow-watchdog.ts +0 -43
  89. package/src/types/event-payload.ts +16 -74
  90. package/src/types/event-types.ts +39 -547
  91. package/src/types/hygiene-types.ts +7 -30
  92. package/src/types/principle-tree-schema.ts +20 -222
  93. package/src/types/queue.ts +15 -70
  94. package/src/types/runtime-summary.ts +5 -49
  95. package/src/utils/io.ts +10 -0
  96. package/src/utils/retry.ts +1 -1
  97. package/src/utils/shadow-fingerprint.ts +2 -2
  98. package/src/utils/workspace-resolver.ts +50 -0
  99. package/templates/langs/en/core/AGENTS.md +2 -2
  100. package/templates/langs/en/core/BOOT.md +1 -1
  101. package/templates/langs/en/core/HEARTBEAT.md +2 -2
  102. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  103. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  104. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  105. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  106. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  107. package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
  108. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  109. package/templates/langs/en/skills/evolve-task/SKILL.md +1 -1
  110. package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
  111. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
  112. package/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
  113. package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
  114. package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
  115. package/templates/langs/zh/core/AGENTS.md +2 -2
  116. package/templates/langs/zh/core/BOOT.md +1 -1
  117. package/templates/langs/zh/core/HEARTBEAT.md +2 -2
  118. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  119. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  120. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  121. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
  122. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  123. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  124. package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
  125. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  126. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
  127. package/templates/langs/zh/skills/evolve-task/SKILL.md +2 -2
  128. package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
  129. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
  130. package/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
  131. package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
  132. package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
  133. package/tests/build-artifacts.test.ts +1 -3
  134. package/tests/commands/evolution-status.test.ts +0 -118
  135. package/tests/core/bootstrap-rules.test.ts +1 -1
  136. package/tests/core/config.test.ts +1 -1
  137. package/tests/core/event-log.test.ts +35 -0
  138. package/tests/core/evolution-engine.test.ts +610 -0
  139. package/tests/core/file-store.test.ts +102 -0
  140. package/tests/core/focus-history.test.ts +203 -11
  141. package/tests/core/merge-gate-audit.test.ts +2 -169
  142. package/tests/core/model-deployment-registry.test.ts +7 -1
  143. package/tests/core/model-training-registry.test.ts +19 -0
  144. package/tests/core/observability.test.ts +0 -1
  145. package/tests/core/pain-diagnostic-gate.test.ts +498 -0
  146. package/tests/core/pain.test.ts +0 -1
  147. package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
  148. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
  149. package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
  150. package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
  151. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
  152. package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
  153. package/tests/core/reflection-context.test.ts +0 -14
  154. package/tests/core/replay-engine.test.ts +127 -215
  155. package/tests/core/rule-host-helpers.test.ts +2 -2
  156. package/tests/core/rule-implementation-runtime.test.ts +0 -27
  157. package/tests/core/workflow-funnel-loader.test.ts +162 -0
  158. package/tests/core/workspace-dir-validation.test.ts +8 -1
  159. package/tests/core-anti-growth.test.ts +192 -0
  160. package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
  161. package/tests/hooks/confirm-first-gate.test.ts +333 -0
  162. package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
  163. package/tests/hooks/gate-auto-correct.test.ts +665 -0
  164. package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
  165. package/tests/hooks/pain.test.ts +269 -12
  166. package/tests/hooks/prompt-characterization.test.ts +500 -0
  167. package/tests/hooks/prompt-size-guard.test.ts +32 -17
  168. package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
  169. package/tests/index.test.ts +94 -1
  170. package/tests/integration/auto-entry-gate.test.ts +248 -0
  171. package/tests/integration/internalization-trigger-guard.test.ts +69 -0
  172. package/tests/integration/m8-legacy-paths.test.ts +63 -0
  173. package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
  174. package/tests/plugin-config-resolution-cutover.test.ts +359 -0
  175. package/tests/runtime-v2-discovery-guard.test.ts +154 -0
  176. package/tests/service/central-database.test.ts +457 -0
  177. package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
  178. package/tests/service/evolution-worker.timeout.test.ts +11 -129
  179. package/tests/service/internalization-trigger-adapter.test.ts +251 -0
  180. package/tests/service/monitoring-query-service.test.ts +1 -47
  181. package/tests/service/queue-io.test.ts +1 -62
  182. package/tests/service/runtime-summary-service.test.ts +3 -1
  183. package/tests/service/workflow-watchdog.test.ts +0 -91
  184. package/tests/utils/file-lock.test.ts +5 -3
  185. package/tests/utils/session-key.test.ts +52 -0
  186. package/tests/utils/subagent-probe.test.ts +48 -1
  187. package/vitest.config.ts +4 -11
  188. package/.planning/codebase/ARCHITECTURE.md +0 -157
  189. package/.planning/codebase/CONCERNS.md +0 -145
  190. package/.planning/codebase/CONVENTIONS.md +0 -148
  191. package/.planning/codebase/INTEGRATIONS.md +0 -81
  192. package/.planning/codebase/STACK.md +0 -87
  193. package/.planning/codebase/STRUCTURE.md +0 -193
  194. package/.planning/codebase/TESTING.md +0 -243
  195. package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
  196. package/docs/COMMAND_REFERENCE.md +0 -76
  197. package/docs/COMMAND_REFERENCE_EN.md +0 -79
  198. package/scripts/build-web.mjs +0 -46
  199. package/scripts/diagnose-nocturnal.mjs +0 -537
  200. package/scripts/seed-nocturnal-scenarios.mjs +0 -384
  201. package/src/commands/nocturnal-review.ts +0 -322
  202. package/src/commands/nocturnal-rollout.ts +0 -790
  203. package/src/commands/nocturnal-train.ts +0 -986
  204. package/src/commands/pd-reflect.ts +0 -88
  205. package/src/core/adaptive-thresholds.ts +0 -478
  206. package/src/core/diagnostician-task-store.ts +0 -192
  207. package/src/core/nocturnal-arbiter.ts +0 -715
  208. package/src/core/nocturnal-artifact-lineage.ts +0 -116
  209. package/src/core/nocturnal-artificer.ts +0 -257
  210. package/src/core/nocturnal-candidate-scoring.ts +0 -530
  211. package/src/core/nocturnal-compliance.ts +0 -1146
  212. package/src/core/nocturnal-dataset.ts +0 -763
  213. package/src/core/nocturnal-executability.ts +0 -428
  214. package/src/core/nocturnal-export.ts +0 -499
  215. package/src/core/nocturnal-paths.ts +0 -240
  216. package/src/core/nocturnal-reasoning-deriver.ts +0 -343
  217. package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
  218. package/src/core/nocturnal-snapshot-contract.ts +0 -99
  219. package/src/core/nocturnal-trajectory-extractor.ts +0 -512
  220. package/src/core/nocturnal-trinity-types.ts +0 -218
  221. package/src/core/nocturnal-trinity.ts +0 -2680
  222. package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
  223. package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
  224. package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
  225. package/src/http/principles-console-route.ts +0 -709
  226. package/src/service/central-health-service.ts +0 -49
  227. package/src/service/central-overview-service.ts +0 -138
  228. package/src/service/control-ui-query-service.ts +0 -900
  229. package/src/service/cooldown-strategy.ts +0 -97
  230. package/src/service/evolution-pain-context.ts +0 -79
  231. package/src/service/evolution-query-service.ts +0 -407
  232. package/src/service/health-query-service.ts +0 -1038
  233. package/src/service/nocturnal-config.ts +0 -214
  234. package/src/service/nocturnal-runtime.ts +0 -734
  235. package/src/service/nocturnal-service.ts +0 -1605
  236. package/src/service/nocturnal-target-selector.ts +0 -545
  237. package/src/service/sleep-cycle.ts +0 -157
  238. package/src/service/startup-reconciler.ts +0 -112
  239. package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
  240. package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
  241. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
  242. package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
  243. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
  244. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
  245. package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
  246. package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
  247. package/src/tools/write-pain-flag.ts +0 -215
  248. package/tests/commands/nocturnal-review.test.ts +0 -448
  249. package/tests/commands/nocturnal-train.test.ts +0 -97
  250. package/tests/commands/pd-reflect.test.ts +0 -49
  251. package/tests/core/adaptive-thresholds.test.ts +0 -261
  252. package/tests/core/nocturnal-arbiter.test.ts +0 -559
  253. package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
  254. package/tests/core/nocturnal-artificer.test.ts +0 -241
  255. package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
  256. package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
  257. package/tests/core/nocturnal-compliance.test.ts +0 -646
  258. package/tests/core/nocturnal-dataset.test.ts +0 -892
  259. package/tests/core/nocturnal-e2e.test.ts +0 -234
  260. package/tests/core/nocturnal-executability.test.ts +0 -357
  261. package/tests/core/nocturnal-export.test.ts +0 -517
  262. package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
  263. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
  264. package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
  265. package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
  266. package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
  267. package/tests/core/nocturnal-trinity.test.ts +0 -2053
  268. package/tests/core/pain-auto-repair.test.ts +0 -96
  269. package/tests/core/pain-integration.test.ts +0 -510
  270. package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
  271. package/tests/http/principles-console-route.test.ts +0 -162
  272. package/tests/integration/chaos-resilience.test.ts +0 -348
  273. package/tests/integration/empathy-workflow-integration.test.ts +0 -626
  274. package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
  275. package/tests/service/control-ui-query-service.test.ts +0 -121
  276. package/tests/service/cooldown-strategy.test.ts +0 -164
  277. package/tests/service/data-endpoints-regression.test.ts +0 -834
  278. package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
  279. package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
  280. package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
  281. package/tests/service/nocturnal-runtime.test.ts +0 -473
  282. package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
  283. package/tests/service/nocturnal-target-selector.test.ts +0 -615
  284. package/tests/service/startup-reconciler.test.ts +0 -148
  285. package/tests/tools/write-pain-flag.test.ts +0 -358
  286. package/ui/src/App.tsx +0 -45
  287. package/ui/src/api.ts +0 -220
  288. package/ui/src/charts.tsx +0 -955
  289. package/ui/src/components/ErrorState.tsx +0 -6
  290. package/ui/src/components/Loading.tsx +0 -13
  291. package/ui/src/components/ProtectedRoute.tsx +0 -12
  292. package/ui/src/components/Shell.tsx +0 -91
  293. package/ui/src/components/WorkspaceConfig.tsx +0 -178
  294. package/ui/src/components/index.ts +0 -5
  295. package/ui/src/context/auth.tsx +0 -80
  296. package/ui/src/context/theme.tsx +0 -66
  297. package/ui/src/hooks/useAutoRefresh.ts +0 -39
  298. package/ui/src/i18n/ui.ts +0 -473
  299. package/ui/src/main.tsx +0 -16
  300. package/ui/src/pages/EvolutionPage.tsx +0 -333
  301. package/ui/src/pages/FeedbackPage.tsx +0 -138
  302. package/ui/src/pages/GateMonitorPage.tsx +0 -136
  303. package/ui/src/pages/LoginPage.tsx +0 -89
  304. package/ui/src/pages/OverviewPage.tsx +0 -599
  305. package/ui/src/pages/SamplesPage.tsx +0 -174
  306. package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
  307. package/ui/src/styles.css +0 -2020
  308. package/ui/src/types.ts +0 -384
  309. package/ui/src/utils/format.ts +0 -15
@@ -17,7 +17,7 @@
17
17
  * --help Show help message
18
18
  */
19
19
 
20
- import { copyFileSync, cpSync, existsSync, lstatSync, rmSync, readFileSync, readFileSync as readFileSyncRaw, mkdirSync, writeFileSync, readdirSync } from 'fs';
20
+ import { chmodSync, copyFileSync, cpSync, existsSync, lstatSync, rmSync, readFileSync, readFileSync as readFileSyncRaw, mkdirSync, writeFileSync, readdirSync } from 'fs';
21
21
  import { createHash } from 'crypto';
22
22
  import { join, dirname } from 'path';
23
23
  import { fileURLToPath } from 'url';
@@ -27,6 +27,8 @@ const __filename = fileURLToPath(import.meta.url);
27
27
  const __dirname = dirname(__filename);
28
28
 
29
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');
30
32
 
31
33
  /**
32
34
  * Cross-platform home directory resolution.
@@ -42,6 +44,8 @@ function getHomeDir() {
42
44
 
43
45
  const OPENCLAW_DIR = join(getHomeDir(), '.openclaw');
44
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');
45
49
  function getConfiguredWorkspaceDir() {
46
50
  const configPath = join(OPENCLAW_DIR, 'openclaw.json');
47
51
  try {
@@ -58,13 +62,15 @@ function getConfiguredWorkspaceDir() {
58
62
  return join(OPENCLAW_DIR, 'workspace-main');
59
63
  }
60
64
 
61
- // Files and directories to sync
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.
62
69
  const SYNC_ITEMS = [
63
70
  'dist',
64
71
  'templates',
65
72
  'scripts',
66
73
  'docs',
67
- 'openclaw.plugin.json',
68
74
  'package.json',
69
75
  ];
70
76
 
@@ -371,10 +377,6 @@ function buildPlugin() {
371
377
  cwd: SOURCE_DIR,
372
378
  stdio: 'inherit'
373
379
  });
374
- execSync('node scripts/build-web.mjs --production', {
375
- cwd: SOURCE_DIR,
376
- stdio: 'inherit'
377
- });
378
380
  } catch (error) {
379
381
  console.error('\n❌ Build failed');
380
382
  console.error(` ${error.message}`);
@@ -384,6 +386,36 @@ function buildPlugin() {
384
386
  verifyBundleContents();
385
387
  }
386
388
 
389
+ /**
390
+ * Build the Runtime V2 CLI that agents invoke through the `pd` command.
391
+ * This is intentionally part of plugin sync so installed OpenClaw agents do not
392
+ * depend on a repo checkout, stale global npm package, or guessed CLI path.
393
+ */
394
+ function buildPdCli() {
395
+ console.log('\n🔨 Building PD CLI...');
396
+
397
+ if (!existsSync(join(PD_CLI_SOURCE_DIR, 'package.json'))) {
398
+ console.error(`❌ PD CLI package not found: ${PD_CLI_SOURCE_DIR}`);
399
+ process.exit(1);
400
+ }
401
+
402
+ try {
403
+ execSync('npm run build --workspace=@principles/core', {
404
+ cwd: ROOT_DIR,
405
+ stdio: 'inherit'
406
+ });
407
+ execSync('npm run build --workspace=@principles/pd-cli', {
408
+ cwd: ROOT_DIR,
409
+ stdio: 'inherit'
410
+ });
411
+ console.log('✅ PD CLI built');
412
+ } catch (error) {
413
+ console.error('\n❌ PD CLI build failed');
414
+ console.error(` ${error.message}`);
415
+ process.exit(1);
416
+ }
417
+ }
418
+
387
419
  /**
388
420
  * Verify the built bundle contains all critical symbols.
389
421
  */
@@ -400,7 +432,6 @@ function verifyBundleContents() {
400
432
  { name: 'checkPainFlag', reason: 'pain flag detection' },
401
433
  { name: 'processEvolutionQueue', reason: 'queue processing' },
402
434
  { name: 'acquireQueueLock', reason: 'queue lock for pd-reflect and worker' },
403
- { name: 'write_pain_flag', reason: 'pain signal recording tool' },
404
435
  ];
405
436
 
406
437
  const missing = [];
@@ -472,11 +503,6 @@ function writeBuildFingerprint() {
472
503
  console.warn(`⚠️ Could not write fingerprint: ${err.message}`);
473
504
  }
474
505
 
475
- try {
476
- const rootManifest = JSON.parse(readFileSync(manifestSrc, 'utf-8'));
477
- rootManifest.buildFingerprint = manifest.buildFingerprint;
478
- writeFileAtomic(manifestSrc, JSON.stringify(rootManifest, null, 2) + '\n');
479
- } catch { /* ignore */ }
480
506
  }
481
507
 
482
508
  /**
@@ -525,22 +551,33 @@ function verifyInstalledFingerprint() {
525
551
  }
526
552
 
527
553
  /**
528
- * Update the plugin version in openclaw.json after successful sync.
529
- * This ensures the recorded version always matches the installed version.
554
+ * Update the plugin version in installs.json after successful sync.
555
+ * OpenClaw manages install records in ~/.openclaw/plugins/installs.json,
556
+ * NOT in openclaw.json (plugins.installs is a legacy/transient field).
530
557
  */
531
558
  function updateInstalledPluginVersion(version) {
532
- const configPath = join(OPENCLAW_DIR, 'openclaw.json');
559
+ const installsDir = join(OPENCLAW_DIR, 'plugins');
560
+ const installsPath = join(installsDir, 'installs.json');
533
561
  try {
534
- const raw = readFileSync(configPath, 'utf-8');
535
- const config = JSON.parse(raw);
536
- if (config?.plugins?.installs?.['principles-disciple']) {
537
- config.plugins.installs['principles-disciple'].version = version;
538
- config.plugins.installs['principles-disciple'].installedAt = new Date().toISOString();
539
- writeFileAtomic(configPath, JSON.stringify(config, null, 2) + '\n');
540
- console.log(`✅ openclaw.json updated: version=${version}`);
562
+ if (!existsSync(installsDir)) {
563
+ mkdirSync(installsDir, { recursive: true });
541
564
  }
565
+ let installs = { version: 1, installRecords: {} };
566
+ if (existsSync(installsPath)) {
567
+ const raw = readFileSync(installsPath, 'utf-8');
568
+ installs = JSON.parse(raw);
569
+ }
570
+ if (!installs.installRecords) installs.installRecords = {};
571
+ installs.installRecords['principles-disciple'] = {
572
+ source: 'path',
573
+ installPath: INSTALL_DIR,
574
+ version: version,
575
+ installedAt: new Date().toISOString(),
576
+ };
577
+ writeFileAtomic(installsPath, JSON.stringify(installs, null, 2) + '\n');
578
+ console.log(`✅ installs.json updated: version=${version}`);
542
579
  } catch (err) {
543
- console.warn(`⚠️ Could not update openclaw.json: ${err.message}`);
580
+ console.warn(`⚠️ Could not update installs.json: ${err.message}`);
544
581
  }
545
582
  }
546
583
 
@@ -604,36 +641,93 @@ function ensureInstallDir() {
604
641
  }
605
642
 
606
643
  /**
607
- * Sync skills directories based on the skills field in openclaw.plugin.json.
608
- * Reads the manifest to find declared skill paths, resolves them relative to
609
- * source root, then copies each to the install target.
644
+ * Sync the openclaw.plugin.json manifest but filter the skills array to only
645
+ * include paths matching the selected language (zh or en).
646
+ *
647
+ * This MUST run BEFORE syncSkillDirs, and it should
648
+ * never be done via syncItem('openclaw.plugin.json') + subsequent modification
649
+ * because the post-modification step can fail silently (catch block) leaving
650
+ * both en/zh skills in the manifest, which triggers OpenClaw's "plugin skill
651
+ * name collision" warning on startup.
610
652
  */
611
- function syncSkillDirs() {
612
- const manifestPath = join(SOURCE_DIR, 'openclaw.plugin.json');
613
- if (!existsSync(manifestPath)) return;
653
+ function syncFilteredManifest(lang) {
654
+ const sourcePath = join(SOURCE_DIR, 'openclaw.plugin.json');
655
+ const targetPath = join(INSTALL_DIR, 'openclaw.plugin.json');
656
+
657
+ if (!existsSync(sourcePath)) return;
658
+
659
+ console.log(` 📄 openclaw.plugin.json (filtered for lang: ${lang})`);
614
660
 
615
- let skillsPaths;
661
+ let manifest;
616
662
  try {
617
- const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
618
- skillsPaths = manifest.skills;
619
- } catch {
620
- return;
663
+ manifest = JSON.parse(readFileSync(sourcePath, 'utf-8'));
664
+ } catch (err) {
665
+ console.error(`❌ Failed to read source manifest: ${err.message}`);
666
+ process.exit(1);
667
+ }
668
+
669
+ if (!manifest.skills || !Array.isArray(manifest.skills)) return;
670
+
671
+ const selectedLang = (lang || 'zh').toLowerCase();
672
+ if (!['zh', 'en'].includes(selectedLang)) {
673
+ console.error(`❌ Invalid language: ${selectedLang}. Expected zh or en.`);
674
+ process.exit(1);
621
675
  }
622
676
 
623
- if (!skillsPaths || !Array.isArray(skillsPaths)) return;
677
+ const filtered = manifest.skills.filter(sp => {
678
+ if (typeof sp !== 'string') return false;
679
+ return sp.includes(`/langs/${selectedLang}/`);
680
+ });
681
+
682
+ if (filtered.length === 0) {
683
+ console.error(`❌ No skills match language "${selectedLang}" in source manifest`);
684
+ process.exit(1);
685
+ }
686
+
687
+ manifest.skills = filtered;
688
+
689
+ // Use syncItem semantics: remove target, then write freshly
690
+ if (existsSync(targetPath)) {
691
+ rmSync(targetPath, { force: true });
692
+ }
693
+ writeFileSync(targetPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
694
+ console.log(` ✅ skills filtered to lang: ${selectedLang} (${filtered.length} path(s))`);
695
+ }
624
696
 
625
- for (const sp of skillsPaths) {
626
- if (typeof sp !== 'string') continue;
627
- const source = join(SOURCE_DIR, sp);
628
- const name = sp.split('/').pop();
629
- const target = join(INSTALL_DIR, 'skills', name);
630
- if (!existsSync(source)) {
631
- console.warn(` ⚠️ skills path not found: ${sp}`);
632
- continue;
697
+ /**
698
+ * @deprecated Replaced by syncFilteredManifest(). Kept for backwards
699
+ * compatibility with old installs that may call it externally.
700
+ * syncFilteredManifest filters the skills array BEFORE writing the manifest
701
+ * to disk, avoiding the en/zh collision window entirely.
702
+ */
703
+ function filterInstalledManifestSkills(lang) {
704
+ const installedManifestPath = join(INSTALL_DIR, 'openclaw.plugin.json');
705
+ if (!existsSync(installedManifestPath)) return;
706
+
707
+ try {
708
+ const manifest = JSON.parse(readFileSync(installedManifestPath, 'utf-8'));
709
+ if (!manifest.skills || !Array.isArray(manifest.skills)) return;
710
+
711
+ const selectedLang = (lang || 'zh').toLowerCase();
712
+ if (!['zh', 'en'].includes(selectedLang)) {
713
+ console.error(`❌ Invalid language: ${selectedLang}. Expected zh or en.`);
714
+ process.exit(1);
633
715
  }
634
- if (existsSync(target)) rmSync(target, { recursive: true, force: true });
635
- cpSync(source, target, { recursive: true });
636
- console.log(` 📄 skills/${name} (from ${sp})`);
716
+ const filtered = manifest.skills.filter(sp => {
717
+ if (typeof sp !== 'string') return false;
718
+ return sp.includes(`/langs/${selectedLang}/`);
719
+ });
720
+
721
+ if (filtered.length === 0) {
722
+ console.error(`❌ No skills match language "${selectedLang}" in installed manifest`);
723
+ process.exit(1);
724
+ }
725
+
726
+ manifest.skills = filtered;
727
+ writeFileSync(installedManifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
728
+ console.log(` 📄 openclaw.plugin.json skills filtered to lang: ${selectedLang}`);
729
+ } catch (err) {
730
+ console.warn(` ⚠️ Failed to filter installed manifest skills: ${err.message}`);
637
731
  }
638
732
  }
639
733
 
@@ -653,6 +747,113 @@ function syncItem(item) {
653
747
  }
654
748
  }
655
749
 
750
+ function quoteCmdPath(filePath) {
751
+ return filePath.replace(/"/g, '""');
752
+ }
753
+
754
+ /**
755
+ * Copy the built pd-cli package into the plugin install directory and create
756
+ * deterministic shims. The global shim points at the installed plugin copy, not
757
+ * at a developer checkout.
758
+ */
759
+ function syncPdCli() {
760
+ console.log('\n📦 Syncing PD CLI...');
761
+
762
+ const distDir = join(PD_CLI_SOURCE_DIR, 'dist');
763
+ if (!existsSync(join(distDir, 'index.js'))) {
764
+ console.error('❌ PD CLI dist/index.js missing. Run without --skip-build or build @principles/pd-cli first.');
765
+ process.exit(1);
766
+ }
767
+
768
+ rmSync(INSTALLED_PD_CLI_DIR, { recursive: true, force: true });
769
+ mkdirSync(INSTALLED_PD_CLI_DIR, { recursive: true });
770
+ cpSync(distDir, join(INSTALLED_PD_CLI_DIR, 'dist'), { recursive: true });
771
+ copyFileSync(join(PD_CLI_SOURCE_DIR, 'package.json'), join(INSTALLED_PD_CLI_DIR, 'package.json'));
772
+
773
+ mkdirSync(INSTALLED_BIN_DIR, { recursive: true });
774
+ const installedEntry = join(INSTALLED_PD_CLI_DIR, 'dist', 'index.js');
775
+
776
+ if (isWindows()) {
777
+ const cmdShim = [
778
+ '@echo off',
779
+ `node "${quoteCmdPath(installedEntry)}" %*`,
780
+ '',
781
+ ].join('\r\n');
782
+ const psShim = [
783
+ '$ErrorActionPreference = "Stop"',
784
+ `$entry = "${installedEntry.replace(/`/g, '``').replace(/"/g, '`"')}"`,
785
+ '& node $entry @args',
786
+ 'exit $LASTEXITCODE',
787
+ '',
788
+ ].join('\r\n');
789
+ writeFileSync(join(INSTALLED_BIN_DIR, 'pd.cmd'), cmdShim, 'utf-8');
790
+ writeFileSync(join(INSTALLED_BIN_DIR, 'pd.ps1'), psShim, 'utf-8');
791
+ } else {
792
+ const shShim = [
793
+ '#!/usr/bin/env sh',
794
+ `exec node "${installedEntry.replace(/"/g, '\\"')}" "$@"`,
795
+ '',
796
+ ].join('\n');
797
+ const target = join(INSTALLED_BIN_DIR, 'pd');
798
+ writeFileSync(target, shShim, 'utf-8');
799
+ chmodSync(target, 0o755);
800
+ }
801
+
802
+ installGlobalPdShim();
803
+ }
804
+
805
+ function getNpmGlobalBinDir() {
806
+ try {
807
+ const prefix = execSync('npm prefix -g', { encoding: 'utf-8' }).trim();
808
+ if (!prefix) return null;
809
+ return process.platform === 'win32' ? prefix : join(prefix, 'bin');
810
+ } catch {
811
+ return null;
812
+ }
813
+ }
814
+
815
+ function installGlobalPdShim() {
816
+ const globalBin = getNpmGlobalBinDir();
817
+ if (!globalBin) {
818
+ console.warn('⚠️ Could not resolve npm global bin directory. Use plugin-local bin/pd shim.');
819
+ return;
820
+ }
821
+
822
+ try {
823
+ mkdirSync(globalBin, { recursive: true });
824
+ if (isWindows()) {
825
+ const pluginCmd = join(INSTALLED_BIN_DIR, 'pd.cmd');
826
+ const pluginPs = join(INSTALLED_BIN_DIR, 'pd.ps1');
827
+ writeFileSync(join(globalBin, 'pd.cmd'), `@echo off\r\ncall "${quoteCmdPath(pluginCmd)}" %*\r\n`, 'utf-8');
828
+ writeFileSync(
829
+ join(globalBin, 'pd.ps1'),
830
+ `$shim = "${pluginPs.replace(/`/g, '``').replace(/"/g, '`"')}"\r\n& $shim @args\r\nexit $LASTEXITCODE\r\n`,
831
+ 'utf-8'
832
+ );
833
+ } else {
834
+ const pluginSh = join(INSTALLED_BIN_DIR, 'pd');
835
+ const globalSh = join(globalBin, 'pd');
836
+ writeFileSync(globalSh, `#!/usr/bin/env sh\nexec "${pluginSh.replace(/"/g, '\\"')}" "$@"\n`, 'utf-8');
837
+ chmodSync(globalSh, 0o755);
838
+ }
839
+ console.log(`✅ Global pd shim installed: ${globalBin}`);
840
+ } catch (err) {
841
+ console.warn(`⚠️ Could not install global pd shim: ${err.message}`);
842
+ console.warn(` Use plugin-local shim: ${join(INSTALLED_BIN_DIR, isWindows() ? 'pd.cmd' : 'pd')}`);
843
+ }
844
+ }
845
+
846
+ function verifyPdCliShim() {
847
+ const localShim = join(INSTALLED_BIN_DIR, isWindows() ? 'pd.cmd' : 'pd');
848
+ try {
849
+ execSync(`"${localShim}" --version`, { stdio: 'pipe', shell: true });
850
+ console.log(`✅ PD CLI shim verified: ${localShim}`);
851
+ } catch (err) {
852
+ console.error(`❌ PD CLI shim verification failed: ${err.message}`);
853
+ process.exit(1);
854
+ }
855
+ }
856
+
656
857
  /**
657
858
  * Recursive directory copy (Windows-safe, no symlinks).
658
859
  */
@@ -711,7 +912,7 @@ function injectLocalWorkspacePackages() {
711
912
  function installTargetDependencies() {
712
913
  console.log('\n📦 Installing production dependencies in target...');
713
914
  try {
714
- execSync('npm install --production --no-audit --no-fund', {
915
+ execSync('npm install --omit=dev --no-audit --no-fund --prefer-offline', {
715
916
  cwd: INSTALL_DIR,
716
917
  stdio: 'inherit'
717
918
  });
@@ -770,6 +971,35 @@ function getTempDir() {
770
971
  return '/tmp';
771
972
  }
772
973
 
974
+ /**
975
+ * Strip Anaconda/Miniconda entries from PATH on Windows.
976
+ *
977
+ * Anaconda ships an ancient cygpath (2016, at
978
+ * D:\ProgramData\anaconda3\Library\usr\bin\cygpath) that converts Unix-style
979
+ * paths INCORRECTLY in MINGW/MSYS — e.g. "/c/Users/..." becomes
980
+ * "D:\ProgramData\anaconda3\Library\c\Users\..." instead of "C:\Users\...".
981
+ * npm-installed CLI POSIX shims (openclaw, pd, etc.) call cygpath -w for path
982
+ * conversion and break when the wrong cygpath is found first in PATH.
983
+ *
984
+ * This does not affect schtasks (runs in a clean session) or PowerShell
985
+ * Start-Process (uses Split-Path, not cygpath). It protects the port check
986
+ * and error-recovery paths that execSync child processes.
987
+ */
988
+ function sanitizePathForWindows() {
989
+ const pathSep = isWindows() ? ';' : ':';
990
+ const originalPath = process.env.PATH || '';
991
+ const entries = originalPath.split(pathSep).filter(Boolean);
992
+ const sanitized = entries.filter(entry => {
993
+ const lower = entry.toLowerCase().replace(/[/\\]/g, '/');
994
+ return !lower.includes('/anaconda') && !lower.includes('/conda') && !lower.includes('/miniconda');
995
+ });
996
+ if (sanitized.length !== entries.length) {
997
+ const removed = entries.length - sanitized.length;
998
+ process.env.PATH = sanitized.join(pathSep);
999
+ console.log(` 🧹 Sanitized PATH: removed ${removed} Anaconda/Conda entr${removed > 1 ? 'ies' : 'y'} (fixes cygpath bug on MINGW/MSYS)`);
1000
+ }
1001
+ }
1002
+
773
1003
  /**
774
1004
  * Restart OpenClaw Gateway (cross-platform).
775
1005
  */
@@ -791,6 +1021,10 @@ function restartGateway() {
791
1021
  function restartGatewayWindows() {
792
1022
  const logPath = join(getTempDir(), 'openclaw-auto-restart.log');
793
1023
 
1024
+ // Strip Anaconda from PATH so any execSync child process (especially the
1025
+ // port-check Test-NetConnection shell) does not inherit poisoned cygpath.
1026
+ sanitizePathForWindows();
1027
+
794
1028
  try {
795
1029
  // Kill existing gateway processes first — taskkill fails across Windows sessions,
796
1030
  // use WMIC which can terminate processes regardless of session boundary.
@@ -832,40 +1066,66 @@ function restartGatewayWindows() {
832
1066
 
833
1067
  // Trigger via schtasks — reliable, avoids CLI busy-port misdetection
834
1068
  console.log(' Starting gateway via scheduled task...');
835
- execSync('schtasks /Run /TN "OpenClaw Gateway"', { stdio: 'inherit' });
1069
+ let gatewayStarted = false;
1070
+ try {
1071
+ execSync('schtasks /Run /TN "OpenClaw Gateway"', { stdio: 'pipe' });
1072
+ gatewayStarted = true;
1073
+ } catch {
1074
+ // schtasks failed — try direct CLI fallback via PowerShell
1075
+ // PowerShell shim resolves paths correctly in Anaconda environments
1076
+ // where cmd /c can produce wrong path prefixes (e.g. conda prefix + npm path)
1077
+ console.log(' Scheduled task not available, trying direct gateway start via PowerShell...');
1078
+ try {
1079
+ execSync(
1080
+ 'powershell -NoProfile -Command "Start-Process -WindowStyle Hidden -FilePath \'openclaw\' -ArgumentList \'gateway\'"',
1081
+ { stdio: 'pipe', timeout: 5000 }
1082
+ );
1083
+ gatewayStarted = true;
1084
+ } catch {
1085
+ // Gateway may already be running or no perms — not fatal
1086
+ }
1087
+ }
836
1088
 
837
1089
  // Wait for gateway to be listening on port (同步等待,不异步)
838
1090
  const port = 18789;
839
- const deadline = Date.now() + 30000;
840
- const pollInterval = 1000;
1091
+ const deadline = Date.now() + 60000; // Gateway 初始化可能需要 20+ 秒 (如 Windows schtasks 重启)
841
1092
  let gatewayListening = false;
842
1093
 
843
1094
  while (Date.now() < deadline) {
844
1095
  try {
1096
+ // 使用 Test-NetConnection 替代有语法问题的 Get-NetTCPConnection + Measure-Object
845
1097
  const result = execSync(
846
- `powershell -NoProfile -Command "Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Measure-Object -Line).Count"`,
1098
+ `powershell -NoProfile -Command "(Test-NetConnection -ComputerName localhost -Port ${port} -WarningAction SilentlyContinue).TcpTestSucceeded"`,
847
1099
  { encoding: 'utf-8', timeout: 5000 }
848
1100
  ).trim();
849
- if (parseInt(result) > 0) {
1101
+ if (result === 'True') {
850
1102
  gatewayListening = true;
851
1103
  break;
852
1104
  }
853
1105
  } catch { /* port not listening yet */ }
854
- execSync('timeout /t 1 /nobreak > nul', { shell: true, stdio: 'ignore' });
1106
+ try {
1107
+ execSync('timeout /t 1 /nobreak > nul', { shell: true, stdio: 'ignore' });
1108
+ } catch { /* ignore */ }
855
1109
  }
856
1110
 
857
1111
  if (!gatewayListening) {
858
- console.error('\nGateway did not start on port 18789 within 30s.');
859
- console.error(' Please restart manually: openclaw gateway start');
860
- process.exit(1);
1112
+ console.warn('\n⚠️ Gateway may not be running on port 18789 (plugin files are installed).');
1113
+ console.warn(' If the gateway was started but is unreachable, try:');
1114
+ console.warn(' 1. From PowerShell: openclaw gateway start');
1115
+ console.warn(' 2. If using Git Bash, strip Anaconda from PATH first:');
1116
+ console.warn(' PATH=$(echo "$PATH" | tr ":" "\\n" | grep -iv anaconda | tr "\\n" ":") openclaw gateway start');
1117
+ return false;
861
1118
  }
862
1119
 
863
1120
  console.log(' ✅ Gateway is listening on port 18789');
1121
+ return true;
864
1122
 
865
1123
  } catch (error) {
866
- console.error(`\nGateway restart failed: ${error.message}`);
867
- console.error(' You may need to manually restart: openclaw gateway start');
868
- process.exit(1);
1124
+ console.warn(`\n⚠️ Gateway restart failed: ${error.message}`);
1125
+ console.warn(' Plugin files are installed. Run "openclaw gateway start" manually.');
1126
+ console.warn(' If using Git Bash with Anaconda in PATH, first run:');
1127
+ console.warn(' export PATH=$(echo "$PATH" | tr ":" "\\n" | grep -iv anaconda | tr "\\n" ":")');
1128
+ return false;
869
1129
  }
870
1130
  }
871
1131
 
@@ -906,7 +1166,7 @@ function restartGatewayLinux() {
906
1166
  console.warn(`⚠️ Post-restart verification skipped: ${e.message}`);
907
1167
  }
908
1168
  }, 8000);
909
- return;
1169
+ return true; // systemctl restart triggered successfully
910
1170
  } catch { /* systemctl not available, fall through to manual restart */ }
911
1171
 
912
1172
  // Manual process management
@@ -919,22 +1179,29 @@ function restartGatewayLinux() {
919
1179
 
920
1180
  console.log(` Starting new gateway (logs: ${logPath})...`);
921
1181
  execSync(`nohup openclaw gateway --force > ${logPath} 2>&1 &`, { stdio: 'ignore' });
922
- console.log('✅ Gateway restart triggered.');
923
-
924
- setTimeout(() => {
925
- if (existsSync(logPath)) {
926
- const logs = readFileSync(logPath, 'utf-8');
927
- if (logs.includes('Principles Disciple Plugin registered')) {
928
- console.log(' SUCCESS: Principles Disciple plugin registered successfully (manual restart)!');
929
- } else if (logs.includes('failed to load')) {
930
- console.error('\n❌ CRITICAL: Manual restart triggered but PD plugin FAILED to load!');
931
- process.exit(1);
1182
+
1183
+ // Wait for process to actually start (up to 15s)
1184
+ const deadline = Date.now() + 15000;
1185
+ while (Date.now() < deadline) {
1186
+ try {
1187
+ const running = execSync(
1188
+ 'pgrep -f "openclaw-gateway|openclaw gateway"',
1189
+ { encoding: 'utf-8', stdio: 'pipe' }
1190
+ ).trim();
1191
+ if (running) {
1192
+ console.log('✅ Gateway restart triggered.');
1193
+ return true;
932
1194
  }
933
- }
934
- }, 8000);
1195
+ } catch { /* not ready yet */ }
1196
+ execSync('sleep 1');
1197
+ }
1198
+
1199
+ console.error(`❌ Gateway did not come back up. Check ${logPath}`);
1200
+ return false;
1201
+
935
1202
  } catch (error) {
936
1203
  console.error(`\n❌ Failed to restart gateway: ${error.message}`);
937
- process.exit(1);
1204
+ return false;
938
1205
  }
939
1206
  }
940
1207
 
@@ -968,16 +1235,41 @@ function main() {
968
1235
 
969
1236
  if (!args.skipDeps) installDependencies();
970
1237
 
971
- buildPlugin();
1238
+ if (!args.skipBuild) {
1239
+ buildPlugin();
1240
+ buildPdCli();
1241
+ } else {
1242
+ console.log('\n⏭️ Skipping build step (--skip-build)');
1243
+ }
972
1244
  cleanTargetDir(args.force);
973
1245
  ensureInstallDir();
974
1246
 
975
1247
  console.log('\n📦 Syncing files to OpenClaw...');
976
1248
  for (const item of SYNC_ITEMS) syncItem(item);
977
- syncSkillDirs();
1249
+
1250
+ // After syncing templates, remove the non-selected language to avoid
1251
+ // waste on disk. OpenClaw only loads skills declared in the filtered
1252
+ // manifest (syncFilteredManifest), but having unselected lang files on
1253
+ // disk inflates the install footprint and confuses inspection.
1254
+ {
1255
+ const langsDir = join(INSTALL_DIR, 'templates', 'langs');
1256
+ if (existsSync(langsDir)) {
1257
+ const normalizedLang = (args.lang || 'zh').toLowerCase();
1258
+ const unselectedLang = normalizedLang === 'zh' ? 'en' : 'zh';
1259
+ const unselectedPath = join(langsDir, unselectedLang);
1260
+ if (existsSync(unselectedPath)) {
1261
+ rmSync(unselectedPath, { recursive: true, force: true });
1262
+ console.log(` 🗑️ removed templates/langs/${unselectedLang} (lang: ${normalizedLang})`);
1263
+ }
1264
+ }
1265
+ }
1266
+
1267
+ syncFilteredManifest(args.lang);
1268
+ syncPdCli();
978
1269
 
979
1270
  injectLocalWorkspacePackages();
980
1271
  installTargetDependencies();
1272
+ verifyPdCliShim();
981
1273
 
982
1274
  console.log('\n🔍 Verifying installed plugin can load native dependencies...');
983
1275
  try {
@@ -1040,7 +1332,20 @@ function main() {
1040
1332
  console.log('╚════════════════════════════════════════════════════════════╝');
1041
1333
 
1042
1334
  if (args.restart) {
1043
- restartGateway();
1335
+ const restarted = restartGateway();
1336
+ if (!restarted) {
1337
+ console.warn('\n⚠️ Plugin files installed but gateway restart was not confirmed.');
1338
+ console.warn(' The gateway may already be running. Verify with:');
1339
+ console.warn(' PowerShell: openclaw gateway status');
1340
+ console.warn(' Git Bash: Test-NetConnection -ComputerName localhost -Port 18789');
1341
+ console.warn('');
1342
+ console.warn(' If "openclaw gateway start" fails with a path error on Git Bash:');
1343
+ console.warn(' this is caused by Anaconda\'s old cygpath tool polluting PATH.');
1344
+ console.warn(' Run: export PATH=$(echo "$PATH" | tr ":" "\\n" | grep -iv anaconda | tr "\\n" ":")');
1345
+ console.warn(' Then: openclaw gateway start');
1346
+ console.warn('');
1347
+ console.warn(' Or simply restart from PowerShell (not affected by the cygpath bug).');
1348
+ }
1044
1349
  } else {
1045
1350
  console.log('\n💡 Restart OpenClaw Gateway to load the new version.');
1046
1351
  console.log(' (Plugin code changes require a full gateway restart)');
@@ -20,6 +20,7 @@ import {
20
20
  import type { Implementation, ImplementationLifecycleState } from '../types/principle-tree-schema.js';
21
21
  import type { PluginCommandContext, PluginCommandResult } from '../openclaw-sdk.js';
22
22
  import { resolvePluginCommandWorkspaceDir } from '../utils/workspace-resolver.js';
23
+ import { normalizeCommandArgs } from '../utils/io.js';
23
24
 
24
25
  /**
25
26
  * Get all implementations from the ledger.
@@ -49,7 +50,7 @@ export function handleArchiveImplCommand(ctx: PluginCommandContext): PluginComma
49
50
  const lang = (ctx.config?.language as string) || 'en';
50
51
  const isZh = lang === 'zh';
51
52
 
52
- const args = (ctx.args || '').trim().split(/\s+/);
53
+ const args = normalizeCommandArgs(ctx.args).trim().split(/\s+/);
53
54
  const subcommand = args[0] || '';
54
55
 
55
56
  // Subcommand: list
@@ -3,7 +3,7 @@ import * as fs from 'fs';
3
3
  import * as path from 'path';
4
4
  import type { PluginCommandContext, PluginCommandResult } from '../openclaw-sdk.js';
5
5
  import { WorkspaceContext } from '../core/workspace-context.js';
6
- import { atomicWriteFileSync } from '../utils/io.js';
6
+ import { atomicWriteFileSync, normalizeCommandArgs } from '../utils/io.js';
7
7
  import { resolvePluginCommandWorkspaceDir } from '../utils/workspace-resolver.js';
8
8
 
9
9
  const TOOLS_TO_SCAN = [
@@ -75,7 +75,7 @@ export function handleBootstrapTools(ctx: PluginCommandContext): PluginCommandRe
75
75
  }
76
76
 
77
77
  export function handleResearchTools(ctx: PluginCommandContext): PluginCommandResult {
78
- const category = ctx.args?.trim() || "modern high-performance CLI tools for coding and architecture";
78
+ const category = normalizeCommandArgs(ctx.args).trim() || "modern high-performance CLI tools for coding and architecture";
79
79
 
80
80
  return {
81
81
  text: