@karmaniverous/jeeves 0.4.6 → 0.5.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.
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { writeFileSync, renameSync, unlinkSync, existsSync, readFileSync, rmSync, mkdirSync, readdirSync, copyFileSync } from 'node:fs';
2
+ import { writeFileSync, renameSync, unlinkSync, existsSync, mkdirSync, readFileSync, readdirSync, copyFileSync, rmSync } from 'node:fs';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import * as commander from 'commander';
5
+ import { packageDirectorySync } from 'package-directory';
5
6
  import { lock } from 'proper-lockfile';
6
7
  import 'semver';
7
8
  import { homedir } from 'node:os';
@@ -111,7 +112,13 @@ const WORKSPACE_FILES = {
111
112
  agents: 'AGENTS.md',
112
113
  /** HEARTBEAT.md — platform status and health alerts. */
113
114
  heartbeat: 'HEARTBEAT.md',
115
+ /** MEMORY.md — curated long-term memory. */
116
+ memory: 'MEMORY.md',
114
117
  };
118
+ /** Skill directory name within workspace. */
119
+ const SKILLS_DIR = 'skills';
120
+ /** Jeeves skill directory name. */
121
+ const JEEVES_SKILL_DIR = 'jeeves';
115
122
  /** Component versions state file name. */
116
123
  const COMPONENT_VERSIONS_FILE = 'component-versions.json';
117
124
 
@@ -119,14 +126,14 @@ const COMPONENT_VERSIONS_FILE = 'component-versions.json';
119
126
  * Core library version, inlined at build time.
120
127
  *
121
128
  * @remarks
122
- * The `0.4.5` placeholder is replaced by
129
+ * The `0.4.7` placeholder is replaced by
123
130
  * `@rollup/plugin-replace` during the build with the actual version
124
131
  * from `package.json`. This ensures the correct version survives
125
132
  * when consumers bundle core into their own dist (where runtime
126
133
  * `import.meta.url`-based resolution would find the wrong package.json).
127
134
  */
128
135
  /** The core library version from package.json (inlined at build time). */
129
- const CORE_VERSION = '0.4.5';
136
+ const CORE_VERSION = '0.4.7';
130
137
 
131
138
  /**
132
139
  * Shared file I/O helpers for managed section operations.
@@ -224,6 +231,30 @@ function readComponentVersions(coreConfigDir) {
224
231
  return {};
225
232
  }
226
233
  }
234
+ /**
235
+ * Write a component's version entry to the shared state file.
236
+ *
237
+ * @remarks
238
+ * Reads the existing file, merges the new entry, and writes atomically.
239
+ *
240
+ * @param coreConfigDir - Path to the core config directory.
241
+ * @param options - Component version data to write.
242
+ */
243
+ function writeComponentVersion(coreConfigDir, options) {
244
+ const existing = readComponentVersions(coreConfigDir);
245
+ existing[options.componentName] = {
246
+ pluginVersion: options.pluginVersion,
247
+ servicePackage: options.servicePackage,
248
+ pluginPackage: options.pluginPackage,
249
+ updatedAt: new Date().toISOString(),
250
+ };
251
+ const filePath = join(coreConfigDir, COMPONENT_VERSIONS_FILE);
252
+ const dir = dirname(filePath);
253
+ if (!existsSync(dir)) {
254
+ mkdirSync(dir, { recursive: true });
255
+ }
256
+ atomicWrite(filePath, JSON.stringify(existing, null, 2) + '\n');
257
+ }
227
258
  /**
228
259
  * Remove a component's version entry from the shared state file.
229
260
  *
@@ -683,6 +714,129 @@ function buildWithSections(beforeContent, userContent, sections, markers, coreVe
683
714
  return parts.join('\n');
684
715
  }
685
716
 
717
+ var skillContent = `---
718
+ name: jeeves
719
+ description: Jeeves platform architecture, data flow, component interaction, scripts repo, and coordination knowledge. Use when making architectural decisions, coordinating across components, checking platform health, managing service lifecycle, or working with the scripts repo.
720
+ ---
721
+
722
+ # Jeeves Platform Skill
723
+
724
+ ## Platform Architecture
725
+
726
+ Jeeves is a four-component platform coordinated by a shared library (\`@karmaniverous/jeeves\`):
727
+
728
+ | Component | Role | Port |
729
+ |-----------|------|------|
730
+ | **jeeves-runner** | Execute: scheduled jobs, SQLite state, HTTP API | 1937 |
731
+ | **jeeves-watcher** | Index: file→Qdrant semantic indexing, inference rules | 1936 |
732
+ | **jeeves-server** | Present: web UI, file browser, doc render, export | 1934 |
733
+ | **jeeves-meta** | Distill: LLM synthesis, .meta/ directories, scheduling | 1938 |
734
+
735
+ Core (\`@karmaniverous/jeeves\`) is a **library + CLI**, not a service. No port.
736
+
737
+ ## Data Flow
738
+
739
+ \`\`\`
740
+ Files → Watcher (index) → Qdrant → Meta (synthesize) → .meta/ → Watcher (re-index)
741
+ ↓
742
+ Runner (schedule) → Scripts → Services ← Server (present) ← Browser
743
+ \`\`\`
744
+
745
+ ## Component Interaction
746
+
747
+ - **Watcher** indexes files into Qdrant with inference rules and enrichments.
748
+ - **Meta** reads from Qdrant, synthesizes \`.meta/\` directories, which watcher re-indexes.
749
+ - **Runner** executes scheduled scripts that may call any service's HTTP API.
750
+ - **Server** presents files, renders documents, and provides the event gateway.
751
+ - **Core** provides shared content management (TOOLS.md, SOUL.md, AGENTS.md), service discovery, config resolution, and the component SDK.
752
+
753
+ ## Service Discovery
754
+
755
+ Services find each other via config resolution:
756
+ 1. Component's own config file (\`{configRoot}/jeeves-{name}/config.json\`)
757
+ 2. Core config file (\`{configRoot}/jeeves-core/config.json\`)
758
+ 3. Default port constants
759
+
760
+ ## Scripts Repo
761
+
762
+ Location: \`{configRoot}/jeeves-core/scripts/\`
763
+ Template: \`@karmaniverous/jeeves-scripts-template\`
764
+
765
+ Scripts use utilities from \`@karmaniverous/jeeves\` (general) and \`@karmaniverous/jeeves-runner\` (runner-specific). Any script that could be useful outside runner scheduling belongs in core.
766
+
767
+ ## Managed Content System
768
+
769
+ Core maintains managed sections in workspace files using comment markers:
770
+ - **TOOLS.md** — Component sections (section mode) + Platform section
771
+ - **SOUL.md** — Professional discipline and behavioral foundations (block mode)
772
+ - **AGENTS.md** — Operational protocols and memory architecture (block mode)
773
+ - **HEARTBEAT.md** — Platform health status (heading-based)
774
+
775
+ Managed blocks are stationary after initial insertion. Cleanup detection uses Jaccard similarity on 3-word shingles. Cleanup escalation spawns a gateway session when orphaned content is detected.
776
+
777
+ ## Workspace Configuration
778
+
779
+ \`jeeves.config.json\` at workspace root provides shared defaults:
780
+ - Precedence: CLI flags → env vars → file → defaults
781
+ - Namespaced: \`core.*\` (workspace, configRoot, gatewayUrl) and \`memory.*\` (budget, warningThreshold, staleDays)
782
+ - Inspect with \`jeeves config [jsonpath]\`
783
+
784
+ ## HEARTBEAT Protocol
785
+
786
+ The HEARTBEAT system uses a state machine per component:
787
+ \`not_installed → deps_missing → config_missing → service_not_installed → service_stopped → healthy\`
788
+
789
+ Dependency-aware: hard deps block alerts, soft deps add informational notes. Declined components are tracked via heading suffix.
790
+
791
+ ## Plugin Lifecycle
792
+
793
+ \`\`\`bash
794
+ # Core install (seed workspace content)
795
+ npx @karmaniverous/jeeves install
796
+
797
+ # Component plugin install
798
+ npx @karmaniverous/jeeves-{component}-openclaw install
799
+
800
+ # Component plugin uninstall
801
+ npx @karmaniverous/jeeves-{component}-openclaw uninstall
802
+
803
+ # Core uninstall (remove managed sections)
804
+ npx @karmaniverous/jeeves uninstall
805
+ \`\`\`
806
+
807
+ ## Memory Hygiene
808
+
809
+ MEMORY.md has a character budget (default 20,000). Core tracks:
810
+ - Character count and usage percentage
811
+ - Warning at 80% of budget
812
+ - Stale section candidates (H2 sections whose most recent ISO date exceeds the staleness threshold)
813
+ - Evergreen sections (no dates) are never flagged
814
+
815
+ Review is human/agent-mediated — core does not auto-delete.
816
+ `;
817
+
818
+ /**
819
+ * Skill seeding: write the `jeeves` workspace skill unconditionally.
820
+ *
821
+ * @remarks
822
+ * The skill file is entirely generated — no user-authored content (Decision 48).
823
+ * Every installer (core CLI and component plugins) writes it unconditionally.
824
+ * Content is inlined at build time via `rollup-plugin-md.ts`.
825
+ */
826
+ /**
827
+ * Seed the jeeves workspace skill file.
828
+ *
829
+ * @param workspacePath - Workspace root directory.
830
+ */
831
+ function seedSkill(workspacePath) {
832
+ const skillDir = join(workspacePath, SKILLS_DIR, JEEVES_SKILL_DIR);
833
+ if (!existsSync(skillDir)) {
834
+ mkdirSync(skillDir, { recursive: true });
835
+ }
836
+ const skillPath = join(skillDir, 'SKILL.md');
837
+ writeFileSync(skillPath, skillContent, 'utf-8');
838
+ }
839
+
686
840
  /**
687
841
  * OpenClaw configuration helpers for plugin CLI installers.
688
842
  *
@@ -802,13 +956,9 @@ function patchConfig(config, pluginId, mode) {
802
956
  }
803
957
 
804
958
  /**
805
- * Factory for the standard `-openclaw` plugin installer CLI.
959
+ * Internal helpers for the plugin installer CLI.
806
960
  *
807
- * @remarks
808
- * Produces a Commander program with `install` and `uninstall` commands
809
- * that handle the full plugin lifecycle: copy dist to extensions,
810
- * patch OpenClaw config, manage HEARTBEAT entries, and clean up
811
- * managed sections on uninstall.
961
+ * @module
812
962
  */
813
963
  /**
814
964
  * Derive a component name from a plugin ID.
@@ -823,7 +973,7 @@ function deriveComponentName(pluginId) {
823
973
  return pluginId.replace(/^jeeves-/, '').replace(/-openclaw$/, '');
824
974
  }
825
975
  /**
826
- * Copy all files from source directory to destination.
976
+ * Copy all files from source directory to destination, recursively.
827
977
  *
828
978
  * @param srcDir - Source directory.
829
979
  * @param destDir - Destination directory.
@@ -857,6 +1007,12 @@ function readJsonFile(filePath) {
857
1007
  return {};
858
1008
  }
859
1009
  }
1010
+
1011
+ /**
1012
+ * Factory for the standard `-openclaw` plugin installer CLI.
1013
+ *
1014
+ * @module
1015
+ */
860
1016
  /**
861
1017
  * Create a standard plugin installer CLI program.
862
1018
  *
@@ -882,6 +1038,16 @@ function createPluginCli(options) {
882
1038
  const extensionsDir = join(openClawHome, 'extensions', pluginId);
883
1039
  console.log(`Copying dist to ${extensionsDir}...`);
884
1040
  copyDistFiles(distDir, extensionsDir);
1041
+ // Copy package.json and openclaw.plugin.json from package root
1042
+ const pkgRoot = packageDirectorySync({ cwd: distDir });
1043
+ if (pkgRoot) {
1044
+ for (const file of ['package.json', 'openclaw.plugin.json']) {
1045
+ const src = join(pkgRoot, file);
1046
+ if (existsSync(src)) {
1047
+ copyFileSync(src, join(extensionsDir, file));
1048
+ }
1049
+ }
1050
+ }
885
1051
  console.log(' ✓ Dist files copied');
886
1052
  // 2. Patch openclaw.json
887
1053
  console.log('Patching OpenClaw config...');
@@ -910,7 +1076,7 @@ function createPluginCli(options) {
910
1076
  for (const msg of messages) {
911
1077
  console.log(` ✓ ${msg}`);
912
1078
  }
913
- // 4. Write initial HEARTBEAT entry
1079
+ // 4. Write initial HEARTBEAT entry and seed jeeves skill
914
1080
  try {
915
1081
  const cfgRoot = opts.configRoot;
916
1082
  const agents = config.agents;
@@ -925,7 +1091,6 @@ function createPluginCli(options) {
925
1091
  : '';
926
1092
  const parsed = parseHeartbeat(existing);
927
1093
  const fullName = `jeeves-${componentName}`;
928
- // Only add if not already present
929
1094
  const hasEntry = parsed.entries.some((e) => e.name === fullName);
930
1095
  if (!hasEntry) {
931
1096
  parsed.entries.push({
@@ -941,10 +1106,36 @@ function createPluginCli(options) {
941
1106
  catch {
942
1107
  console.log(' ⚠ Could not write HEARTBEAT entry');
943
1108
  }
1109
+ try {
1110
+ seedSkill(ws);
1111
+ console.log(' ✓ Jeeves skill seeded');
1112
+ }
1113
+ catch {
1114
+ console.log(' ⚠ Could not seed Jeeves skill');
1115
+ }
944
1116
  }
945
1117
  }
946
1118
  catch {
947
- // HEARTBEAT is best-effort during install
1119
+ // HEARTBEAT + skill seeding are best-effort during install
1120
+ }
1121
+ // 5. Write component version
1122
+ try {
1123
+ init({
1124
+ workspacePath: opts.workspace ?? '.',
1125
+ configRoot: opts.configRoot,
1126
+ });
1127
+ const pkgJsonPath = join(extensionsDir, 'package.json');
1128
+ const pkgJson = readJsonFile(pkgJsonPath);
1129
+ const pluginVersion = typeof pkgJson.version === 'string' ? pkgJson.version : undefined;
1130
+ writeComponentVersion(getCoreConfigDir(), {
1131
+ componentName,
1132
+ pluginPackage,
1133
+ pluginVersion,
1134
+ });
1135
+ console.log(' ✓ Component version written');
1136
+ }
1137
+ catch {
1138
+ console.log(' ⚠ Could not write component version');
948
1139
  }
949
1140
  console.log();
950
1141
  console.log(`✅ ${pluginPackage} installed.`);
@@ -974,10 +1165,9 @@ function createPluginCli(options) {
974
1165
  }
975
1166
  // 3. Remove TOOLS.md section
976
1167
  try {
977
- const cfgRoot = opts.configRoot;
978
1168
  const ws = opts.workspace;
979
1169
  if (ws) {
980
- init({ workspacePath: ws, configRoot: cfgRoot });
1170
+ init({ workspacePath: ws, configRoot: opts.configRoot });
981
1171
  const sectionId = componentName.charAt(0).toUpperCase() + componentName.slice(1);
982
1172
  const toolsPath = join(ws, WORKSPACE_FILES.tools);
983
1173
  if (existsSync(toolsPath)) {
@@ -994,10 +1184,9 @@ function createPluginCli(options) {
994
1184
  }
995
1185
  // 4. Remove component-versions.json entry
996
1186
  try {
997
- const cfgRoot = opts.configRoot;
998
1187
  init({
999
1188
  workspacePath: opts.workspace ?? '.',
1000
- configRoot: cfgRoot,
1189
+ configRoot: opts.configRoot,
1001
1190
  });
1002
1191
  removeComponentVersion(getCoreConfigDir(), componentName);
1003
1192
  console.log(' ✓ Component version entry removed');
@@ -697,17 +697,73 @@ function createServiceManager(descriptor) {
697
697
  }
698
698
 
699
699
  /**
700
- * Shared CLI defaults and option registration for Jeeves CLI commands.
700
+ * Workspace-level shared configuration: `jeeves.config.json`.
701
701
  *
702
702
  * @remarks
703
- * All three CLI commands (install, uninstall, status) share the same
704
- * `--workspace` and `--config-root` options with the same defaults.
705
- * This module centralizes them to eliminate duplication.
703
+ * Lives at the OpenClaw workspace root alongside TOOLS.md and SOUL.md.
704
+ * Provides namespaced shared defaults consumed by the root Jeeves CLI.
705
+ * Resolution precedence: CLI flags → env vars → jeeves.config.json → defaults.
706
+ *
707
+ * This does not replace component-owned config schemas (Decision 41).
708
+ */
709
+ /** Core shared config section. */
710
+ const workspaceCoreConfigSchema = z
711
+ .object({
712
+ /** Workspace root path. */
713
+ workspace: z.string().optional().describe('Workspace root path'),
714
+ /** Platform config root path. */
715
+ configRoot: z.string().optional().describe('Platform config root path'),
716
+ /** OpenClaw gateway URL. */
717
+ gatewayUrl: z.string().optional().describe('OpenClaw gateway URL'),
718
+ })
719
+ .partial();
720
+ /** Memory shared config section. */
721
+ const workspaceMemoryConfigSchema = z
722
+ .object({
723
+ /** MEMORY.md character budget. */
724
+ budget: z.number().int().positive().optional().describe('Memory budget'),
725
+ /** Warning threshold as a fraction of budget. */
726
+ warningThreshold: z
727
+ .number()
728
+ .min(0)
729
+ .max(1)
730
+ .optional()
731
+ .describe('Memory warning threshold'),
732
+ /** Staleness threshold in days. */
733
+ staleDays: z
734
+ .number()
735
+ .int()
736
+ .positive()
737
+ .optional()
738
+ .describe('Memory staleness threshold in days'),
739
+ })
740
+ .partial();
741
+ /** Workspace config Zod schema. */
742
+ z.object({
743
+ /** JSON Schema pointer for IDE autocomplete. */
744
+ $schema: z.string().optional().describe('JSON Schema pointer'),
745
+ /** Core shared defaults. */
746
+ core: workspaceCoreConfigSchema.optional(),
747
+ /** Memory hygiene shared defaults. */
748
+ memory: workspaceMemoryConfigSchema.optional(),
749
+ });
750
+ /** Built-in workspace config defaults. */
751
+ const WORKSPACE_CONFIG_DEFAULTS = {
752
+ core: {
753
+ workspace: '.',
754
+ configRoot: './config'}};
755
+
756
+ /**
757
+ * Shared CLI defaults and resolution for Jeeves CLI commands.
758
+ *
759
+ * @remarks
760
+ * All root CLI commands share workspace/config-root resolution. Values follow
761
+ * the shared precedence model: flags → env → jeeves.config.json → defaults.
706
762
  */
707
- /** Default workspace path (current directory). */
708
- const DEFAULT_WORKSPACE = '.';
763
+ /** Default workspace path. */
764
+ const DEFAULT_WORKSPACE = WORKSPACE_CONFIG_DEFAULTS.core.workspace;
709
765
  /** Default config root path. */
710
- const DEFAULT_CONFIG_ROOT = './config';
766
+ const DEFAULT_CONFIG_ROOT = WORKSPACE_CONFIG_DEFAULTS.core.configRoot;
711
767
 
712
768
  /**
713
769
  * Factory for the standard Jeeves service CLI.