principles-disciple 1.67.0 → 1.69.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.
@@ -2,9 +2,10 @@
2
2
  "id": "principles-disciple",
3
3
  "name": "Principles Disciple",
4
4
  "description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
5
- "version": "1.67.0",
5
+ "version": "1.69.0",
6
6
  "skills": [
7
- "./skills"
7
+ "templates/langs/en/skills",
8
+ "templates/langs/zh/skills"
8
9
  ],
9
10
  "configSchema": {
10
11
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "principles-disciple",
3
- "version": "1.67.0",
3
+ "version": "1.69.0",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/bundle.js",
@@ -400,6 +400,7 @@ function verifyBundleContents() {
400
400
  { name: 'checkPainFlag', reason: 'pain flag detection' },
401
401
  { name: 'processEvolutionQueue', reason: 'queue processing' },
402
402
  { name: 'acquireQueueLock', reason: 'queue lock for pd-reflect and worker' },
403
+ { name: 'write_pain_flag', reason: 'pain signal recording tool' },
403
404
  ];
404
405
 
405
406
  const missing = [];
@@ -523,6 +524,26 @@ function verifyInstalledFingerprint() {
523
524
  console.log('✅ Installed fingerprint verified');
524
525
  }
525
526
 
527
+ /**
528
+ * Update the plugin version in openclaw.json after successful sync.
529
+ * This ensures the recorded version always matches the installed version.
530
+ */
531
+ function updateInstalledPluginVersion(version) {
532
+ const configPath = join(OPENCLAW_DIR, 'openclaw.json');
533
+ 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}`);
541
+ }
542
+ } catch (err) {
543
+ console.warn(`⚠️ Could not update openclaw.json: ${err.message}`);
544
+ }
545
+ }
546
+
526
547
  /**
527
548
  * Remove existing installation directory with Windows-friendly retry logic.
528
549
  */
@@ -583,16 +604,37 @@ function ensureInstallDir() {
583
604
  }
584
605
 
585
606
  /**
586
- * Sync skills.
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.
587
610
  */
588
- function syncSkills(lang) {
589
- const skillsSource = join(SOURCE_DIR, 'templates', 'langs', lang, 'skills');
590
- const skillsTarget = join(INSTALL_DIR, 'skills');
591
- if (!existsSync(skillsSource)) return false;
592
- if (existsSync(skillsTarget)) rmSync(skillsTarget, { recursive: true, force: true });
593
- cpSync(skillsSource, skillsTarget, { recursive: true });
594
- console.log(` 📄 skills (from ${lang})`);
595
- return true;
611
+ function syncSkillDirs() {
612
+ const manifestPath = join(SOURCE_DIR, 'openclaw.plugin.json');
613
+ if (!existsSync(manifestPath)) return;
614
+
615
+ let skillsPaths;
616
+ try {
617
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
618
+ skillsPaths = manifest.skills;
619
+ } catch {
620
+ return;
621
+ }
622
+
623
+ if (!skillsPaths || !Array.isArray(skillsPaths)) return;
624
+
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;
633
+ }
634
+ if (existsSync(target)) rmSync(target, { recursive: true, force: true });
635
+ cpSync(source, target, { recursive: true });
636
+ console.log(` 📄 skills/${name} (from ${sp})`);
637
+ }
596
638
  }
597
639
 
598
640
  /**
@@ -701,6 +743,23 @@ function isWindows() {
701
743
  return process.platform === 'win32';
702
744
  }
703
745
 
746
+ /**
747
+ * Check if a process is running by PID.
748
+ */
749
+ function isProcessRunning(pid) {
750
+ try {
751
+ if (isWindows()) {
752
+ execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { stdio: 'ignore' });
753
+ return true;
754
+ } else {
755
+ process.kill(pid, 0);
756
+ return true;
757
+ }
758
+ } catch {
759
+ return false;
760
+ }
761
+ }
762
+
704
763
  /**
705
764
  * Get temporary directory path (cross-platform).
706
765
  */
@@ -751,32 +810,57 @@ function restartGatewayWindows() {
751
810
  }
752
811
  } catch { /* no existing processes */ }
753
812
 
813
+ // Clean stale lock files — prevents ghost gateway false positives
814
+ try {
815
+ const lockDir = join(getTempDir(), 'openclaw');
816
+ if (existsSync(lockDir)) {
817
+ const files = readdirSync(lockDir).filter(f => f.startsWith('gateway.') && f.endsWith('.lock'));
818
+ for (const file of files) {
819
+ const lockPath = join(lockDir, file);
820
+ try {
821
+ const content = readFileSync(lockPath, 'utf-8');
822
+ const pid = parseInt(content.trim());
823
+ // Only remove if PID is not running
824
+ if (!pid || !isProcessRunning(pid)) {
825
+ rmSync(lockPath, { force: true });
826
+ console.log(` Removed stale lock: ${file}`);
827
+ }
828
+ } catch { /* ignore */ }
829
+ }
830
+ }
831
+ } catch { /* ignore lock cleanup failures */ }
832
+
754
833
  // Trigger via schtasks — reliable, avoids CLI busy-port misdetection
755
834
  console.log(' Starting gateway via scheduled task...');
756
835
  execSync('schtasks /Run /TN "OpenClaw Gateway"', { stdio: 'inherit' });
757
836
 
758
- // Wait for gateway to start and PD plugin to register
759
- const deadline = Date.now() + 20000;
837
+ // Wait for gateway to be listening on port (同步等待,不异步)
838
+ const port = 18789;
839
+ const deadline = Date.now() + 30000;
760
840
  const pollInterval = 1000;
841
+ let gatewayListening = false;
761
842
 
762
- const waitForRegistration = () => {
763
- if (Date.now() > deadline) {
764
- console.warn('⚠️ Gateway started but PD registration not confirmed after 20s.');
765
- console.log(' Check logs at: ' + logPath);
766
- return;
767
- }
843
+ while (Date.now() < deadline) {
768
844
  try {
769
- if (existsSync(logPath)) {
770
- const logs = readFileSync(logPath, 'utf-8');
771
- if (logs.includes('Principles Disciple Plugin registered')) {
772
- console.log('✅ SUCCESS: Principles Disciple plugin registered successfully!');
773
- return;
774
- }
845
+ const result = execSync(
846
+ `powershell -NoProfile -Command "Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Measure-Object -Line).Count"`,
847
+ { encoding: 'utf-8', timeout: 5000 }
848
+ ).trim();
849
+ if (parseInt(result) > 0) {
850
+ gatewayListening = true;
851
+ break;
775
852
  }
776
- } catch { /* ignore */ }
777
- setTimeout(waitForRegistration, pollInterval);
778
- };
779
- waitForRegistration();
853
+ } catch { /* port not listening yet */ }
854
+ execSync('timeout /t 1 /nobreak > nul', { shell: true, stdio: 'ignore' });
855
+ }
856
+
857
+ if (!gatewayListening) {
858
+ console.error('\n❌ Gateway did not start on port 18789 within 30s.');
859
+ console.error(' Please restart manually: openclaw gateway start');
860
+ process.exit(1);
861
+ }
862
+
863
+ console.log(' ✅ Gateway is listening on port 18789');
780
864
 
781
865
  } catch (error) {
782
866
  console.error(`\n❌ Gateway restart failed: ${error.message}`);
@@ -890,7 +974,7 @@ function main() {
890
974
 
891
975
  console.log('\n📦 Syncing files to OpenClaw...');
892
976
  for (const item of SYNC_ITEMS) syncItem(item);
893
- syncSkills(args.lang);
977
+ syncSkillDirs();
894
978
 
895
979
  injectLocalWorkspacePackages();
896
980
  installTargetDependencies();
@@ -948,6 +1032,7 @@ function main() {
948
1032
  }
949
1033
 
950
1034
  verifyInstalledFingerprint();
1035
+ updateInstalledPluginVersion(sourceVersion);
951
1036
  if (args.dev || args.restart) cleanStaleBackups();
952
1037
 
953
1038
  console.log('\n╔════════════════════════════════════════════════════════════╗');
@@ -435,7 +435,7 @@ export async function handleBeforePromptBuild(
435
435
  const contextConfig = loadContextInjectionConfig(workspaceDir);
436
436
 
437
437
  // Minimal mode: heartbeat and subagents skip most context to reduce tokens
438
- const isMinimalMode = trigger === "heartbeat" || sessionId?.includes(":subagent:") === true;
438
+ const isMinimalMode = trigger === "heartbeat" || trigger === "cron" || sessionId?.includes(":subagent:") === true;
439
439
 
440
440
  const session = sessionId ? getSession(sessionId) : undefined;
441
441
 
@@ -700,8 +700,8 @@ The empathy observer subagent handles pain detection independently.
700
700
  // }
701
701
  }
702
702
 
703
- // ──── 4. Heartbeat-specific checklist ────
704
- if (trigger === 'heartbeat') {
703
+ // ──── 4. Heartbeat-specific checklist (also fires for cron-triggered sessions) ────
704
+ if (trigger === 'heartbeat' || trigger === 'cron') {
705
705
  // ──── 4a. GFI Time-based Decay ────
706
706
  // Apply segmented exponential decay to GFI on each heartbeat
707
707
  if (sessionId) {