@tyvm/knowhow 0.0.108-dev.126b29e → 0.0.108-dev.4a8ba55

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 (147) hide show
  1. package/package.json +2 -3
  2. package/src/agents/base/base.ts +9 -0
  3. package/src/agents/tools/index.ts +0 -1
  4. package/src/agents/tools/list.ts +0 -2
  5. package/src/chat/CliChatService.ts +7 -1
  6. package/src/chat/renderer/CompactRenderer.ts +20 -0
  7. package/src/chat/renderer/ConsoleRenderer.ts +19 -0
  8. package/src/chat/renderer/FancyRenderer.ts +19 -0
  9. package/src/chat/renderer/types.ts +11 -0
  10. package/src/cli.ts +79 -666
  11. package/src/clients/types.ts +12 -4
  12. package/src/commands/agent.ts +246 -0
  13. package/src/commands/misc.ts +169 -0
  14. package/src/commands/modules.ts +182 -0
  15. package/src/commands/services.ts +72 -0
  16. package/src/commands/workers.ts +160 -0
  17. package/src/config.ts +37 -0
  18. package/src/index.ts +1 -0
  19. package/src/plugins/plugins.ts +0 -21
  20. package/src/processors/JsonCompressor.ts +6 -6
  21. package/src/services/modules/index.ts +58 -49
  22. package/src/services/modules/types.ts +2 -0
  23. package/src/tunnel.ts +216 -0
  24. package/src/types.ts +0 -1
  25. package/src/worker.ts +65 -336
  26. package/src/workers/auth/WsMiddleware.ts +99 -0
  27. package/src/workers/auth/authMiddleware.ts +104 -0
  28. package/src/workers/auth/types.ts +14 -2
  29. package/tests/unit/modules/moduleLoading.test.ts +0 -25
  30. package/tests/unit/plugins/pluginLoading.test.ts +0 -85
  31. package/ts_build/package.json +2 -3
  32. package/ts_build/src/agents/base/base.js +10 -0
  33. package/ts_build/src/agents/base/base.js.map +1 -1
  34. package/ts_build/src/agents/tools/index.d.ts +0 -1
  35. package/ts_build/src/agents/tools/index.js +0 -1
  36. package/ts_build/src/agents/tools/index.js.map +1 -1
  37. package/ts_build/src/agents/tools/list.js +0 -2
  38. package/ts_build/src/agents/tools/list.js.map +1 -1
  39. package/ts_build/src/chat/CliChatService.js +10 -1
  40. package/ts_build/src/chat/CliChatService.js.map +1 -1
  41. package/ts_build/src/chat/renderer/CompactRenderer.d.ts +4 -0
  42. package/ts_build/src/chat/renderer/CompactRenderer.js +16 -0
  43. package/ts_build/src/chat/renderer/CompactRenderer.js.map +1 -1
  44. package/ts_build/src/chat/renderer/ConsoleRenderer.d.ts +4 -0
  45. package/ts_build/src/chat/renderer/ConsoleRenderer.js +16 -0
  46. package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -1
  47. package/ts_build/src/chat/renderer/FancyRenderer.d.ts +4 -0
  48. package/ts_build/src/chat/renderer/FancyRenderer.js +16 -0
  49. package/ts_build/src/chat/renderer/FancyRenderer.js.map +1 -1
  50. package/ts_build/src/chat/renderer/types.d.ts +2 -0
  51. package/ts_build/src/cli.js +40 -525
  52. package/ts_build/src/cli.js.map +1 -1
  53. package/ts_build/src/clients/types.d.ts +2 -2
  54. package/ts_build/src/commands/agent.d.ts +6 -0
  55. package/ts_build/src/commands/agent.js +229 -0
  56. package/ts_build/src/commands/agent.js.map +1 -0
  57. package/ts_build/src/commands/misc.d.ts +10 -0
  58. package/ts_build/src/commands/misc.js +195 -0
  59. package/ts_build/src/commands/misc.js.map +1 -0
  60. package/ts_build/src/commands/modules.d.ts +3 -0
  61. package/ts_build/src/commands/modules.js +160 -0
  62. package/ts_build/src/commands/modules.js.map +1 -0
  63. package/ts_build/src/commands/services.d.ts +5 -0
  64. package/ts_build/src/commands/services.js +86 -0
  65. package/ts_build/src/commands/services.js.map +1 -0
  66. package/ts_build/src/commands/workers.d.ts +6 -0
  67. package/ts_build/src/commands/workers.js +163 -0
  68. package/ts_build/src/commands/workers.js.map +1 -0
  69. package/ts_build/src/config.d.ts +1 -0
  70. package/ts_build/src/config.js +32 -0
  71. package/ts_build/src/config.js.map +1 -1
  72. package/ts_build/src/index.d.ts +1 -0
  73. package/ts_build/src/index.js +3 -1
  74. package/ts_build/src/index.js.map +1 -1
  75. package/ts_build/src/plugins/plugins.d.ts +0 -2
  76. package/ts_build/src/plugins/plugins.js +0 -11
  77. package/ts_build/src/plugins/plugins.js.map +1 -1
  78. package/ts_build/src/processors/JsonCompressor.js +4 -4
  79. package/ts_build/src/processors/JsonCompressor.js.map +1 -1
  80. package/ts_build/src/services/modules/index.d.ts +33 -0
  81. package/ts_build/src/services/modules/index.js +38 -42
  82. package/ts_build/src/services/modules/index.js.map +1 -1
  83. package/ts_build/src/services/modules/types.d.ts +2 -0
  84. package/ts_build/src/tunnel.d.ts +27 -0
  85. package/ts_build/src/tunnel.js +112 -0
  86. package/ts_build/src/tunnel.js.map +1 -0
  87. package/ts_build/src/types.d.ts +0 -1
  88. package/ts_build/src/types.js.map +1 -1
  89. package/ts_build/src/worker.d.ts +1 -4
  90. package/ts_build/src/worker.js +38 -244
  91. package/ts_build/src/worker.js.map +1 -1
  92. package/ts_build/src/workers/auth/WsMiddleware.d.ts +8 -0
  93. package/ts_build/src/workers/auth/WsMiddleware.js +65 -0
  94. package/ts_build/src/workers/auth/WsMiddleware.js.map +1 -0
  95. package/ts_build/src/workers/auth/authMiddleware.d.ts +3 -0
  96. package/ts_build/src/workers/auth/authMiddleware.js +60 -0
  97. package/ts_build/src/workers/auth/authMiddleware.js.map +1 -0
  98. package/ts_build/src/workers/auth/types.d.ts +8 -1
  99. package/ts_build/tests/unit/modules/moduleLoading.test.js +0 -19
  100. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
  101. package/ts_build/tests/unit/plugins/pluginLoading.test.js +0 -65
  102. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  103. package/src/agents/tools/executeScript/README.md +0 -94
  104. package/src/agents/tools/executeScript/definition.ts +0 -79
  105. package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +0 -272
  106. package/src/agents/tools/executeScript/examples/quick-test.ts +0 -74
  107. package/src/agents/tools/executeScript/examples/serialization-test.ts +0 -321
  108. package/src/agents/tools/executeScript/examples/test-runner.ts +0 -197
  109. package/src/agents/tools/executeScript/index.ts +0 -98
  110. package/src/services/script-execution/SandboxContext.ts +0 -282
  111. package/src/services/script-execution/ScriptExecutor.ts +0 -441
  112. package/src/services/script-execution/ScriptPolicy.ts +0 -194
  113. package/src/services/script-execution/ScriptTracer.ts +0 -249
  114. package/src/services/script-execution/types.ts +0 -134
  115. package/ts_build/src/agents/tools/executeScript/definition.d.ts +0 -2
  116. package/ts_build/src/agents/tools/executeScript/definition.js +0 -76
  117. package/ts_build/src/agents/tools/executeScript/definition.js.map +0 -1
  118. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +0 -18
  119. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +0 -192
  120. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +0 -1
  121. package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +0 -3
  122. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +0 -64
  123. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +0 -1
  124. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +0 -15
  125. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +0 -266
  126. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +0 -1
  127. package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +0 -4
  128. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +0 -208
  129. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +0 -1
  130. package/ts_build/src/agents/tools/executeScript/index.d.ts +0 -28
  131. package/ts_build/src/agents/tools/executeScript/index.js +0 -72
  132. package/ts_build/src/agents/tools/executeScript/index.js.map +0 -1
  133. package/ts_build/src/services/script-execution/SandboxContext.d.ts +0 -34
  134. package/ts_build/src/services/script-execution/SandboxContext.js +0 -189
  135. package/ts_build/src/services/script-execution/SandboxContext.js.map +0 -1
  136. package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +0 -19
  137. package/ts_build/src/services/script-execution/ScriptExecutor.js +0 -269
  138. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +0 -1
  139. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +0 -28
  140. package/ts_build/src/services/script-execution/ScriptPolicy.js +0 -115
  141. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +0 -1
  142. package/ts_build/src/services/script-execution/ScriptTracer.d.ts +0 -19
  143. package/ts_build/src/services/script-execution/ScriptTracer.js +0 -186
  144. package/ts_build/src/services/script-execution/ScriptTracer.js.map +0 -1
  145. package/ts_build/src/services/script-execution/types.d.ts +0 -108
  146. package/ts_build/src/services/script-execution/types.js +0 -3
  147. package/ts_build/src/services/script-execution/types.js.map +0 -1
@@ -0,0 +1,72 @@
1
+ import { includedTools } from "../agents/tools/list";
2
+ import * as allTools from "../agents/tools";
3
+ import { LazyToolsService, services } from "../services";
4
+ import { agents } from "../agents";
5
+ import { ModulesService } from "../services/modules";
6
+
7
+ /**
8
+ * Shared service setup used by commands that need full services (chat, agent, worker, etc.)
9
+ */
10
+ export async function setupServices() {
11
+ const {
12
+ Agents,
13
+ Mcp,
14
+ Clients,
15
+ Tools: AllTools,
16
+ Embeddings,
17
+ Plugins,
18
+ MediaProcessor,
19
+ } = services();
20
+ const Tools = new LazyToolsService();
21
+
22
+ Tools.setContext({
23
+ ...AllTools.getContext(),
24
+ });
25
+
26
+ const agentContext: import("../agents/base/base").AgentContext = {
27
+ ...services(),
28
+ Tools,
29
+ };
30
+
31
+ const { Researcher, Developer, Patcher, Setup } = agents({
32
+ ...agentContext,
33
+ });
34
+
35
+ Agents.registerAgent(Researcher);
36
+ Agents.registerAgent(Patcher);
37
+ Agents.registerAgent(Developer);
38
+ Agents.registerAgent(Setup);
39
+ Agents.loadAgentsFromConfig(agentContext);
40
+
41
+ Tools.defineTools(includedTools, allTools);
42
+
43
+ Tools.addContext("Mcp", Mcp);
44
+
45
+ Agents.setAgentContext(agentContext);
46
+
47
+ console.log("🔌 Connecting to MCP...");
48
+ try {
49
+ await Mcp.connectToConfigured(Tools);
50
+ } catch (mcpError) {
51
+ const msg = mcpError instanceof Error ? mcpError.message : String(mcpError);
52
+ console.warn(
53
+ `⚠ Some MCP servers failed to connect (continuing without them): ${msg}`
54
+ );
55
+ }
56
+ console.log("Connecting to clients...");
57
+ await Clients.registerConfiguredModels();
58
+ console.log("✓ Services are set up and ready to go!");
59
+
60
+ console.log("📦 Loading modules from config...");
61
+ const modulesService = new ModulesService();
62
+ await modulesService.loadModulesFromConfig({
63
+ Agents,
64
+ Embeddings,
65
+ Plugins,
66
+ Clients,
67
+ Tools,
68
+ MediaProcessor,
69
+ });
70
+
71
+ return { Tools, Clients };
72
+ }
@@ -0,0 +1,160 @@
1
+ import { Command } from "commander";
2
+ import { worker } from "../worker";
3
+ import { TUNNEL_MINIMAL_TOOLS } from "../tunnel";
4
+ import { fileSync } from "../fileSync";
5
+ import {
6
+ startAllWorkers,
7
+ listWorkerPaths,
8
+ unregisterWorkerPath,
9
+ clearWorkerRegistry,
10
+ } from "../workerRegistry";
11
+
12
+ export function addWorkerCommand(program: Command): void {
13
+ program
14
+ .command("worker")
15
+ .description(
16
+ "Start worker process and optionally register current directory"
17
+ )
18
+ .option("--register", "Register current directory as a worker path")
19
+ .option(
20
+ "--share",
21
+ "Share this worker with your organization (allows other users to use it)"
22
+ )
23
+ .option("--unshare", "Make this worker private (only you can use it)")
24
+ .option("--sandbox", "Run worker in a Docker container for isolation")
25
+ .option(
26
+ "--no-sandbox",
27
+ "Run worker directly on host (disable sandbox mode)"
28
+ )
29
+ .option("--passkey", "Set up passkey authentication for this worker")
30
+ .option("--passkey-reset", "Remove passkey authentication requirement")
31
+ .action(async (options) => {
32
+ const { setupServices } = await import("./services");
33
+ await setupServices();
34
+ await worker(options);
35
+ });
36
+ }
37
+
38
+ export function addWorkersCommand(program: Command): void {
39
+ program
40
+ .command("workers")
41
+ .description("Manage and start all registered workers")
42
+ .option("--list", "List all registered worker paths")
43
+ .option("--unregister <path>", "Unregister a worker path")
44
+ .option("--clear", "Clear all registered worker paths")
45
+ .action(async (options) => {
46
+ try {
47
+ if (options.list) {
48
+ const workers = await listWorkerPaths();
49
+ if (workers.length === 0) {
50
+ console.log("No workers registered.");
51
+ console.log(
52
+ "\nTo register a worker, run 'knowhow worker --register' from the worker directory."
53
+ );
54
+ } else {
55
+ console.log(`Registered workers (${workers.length}):`);
56
+ workers.forEach((workerPath, index) => {
57
+ console.log(` ${index + 1}. ${workerPath}`);
58
+ });
59
+ }
60
+ return;
61
+ }
62
+
63
+ if (options.unregister) {
64
+ await unregisterWorkerPath(options.unregister);
65
+ return;
66
+ }
67
+
68
+ if (options.clear) {
69
+ await clearWorkerRegistry();
70
+ return;
71
+ }
72
+
73
+ // Default action: start all workers
74
+ const { setupServices } = await import("./services");
75
+ await setupServices();
76
+ await startAllWorkers();
77
+ } catch (error) {
78
+ console.error("Error managing workers:", error);
79
+ process.exit(1);
80
+ }
81
+ });
82
+ }
83
+
84
+ export function addTunnelCommand(program: Command): void {
85
+ program
86
+ .command("tunnel")
87
+ .description(
88
+ "Start a minimal worker with tunnel enabled: exposes local ports to the cloud. " +
89
+ "Registers essential tools (unlock, lock, listAllowedPorts) so the backend is aware of the worker and ports. " +
90
+ "If passkey auth is configured, the tunnel is locked until unlocked via tool call or WebSocket auth protocol."
91
+ )
92
+ .option(
93
+ "--share",
94
+ "Share this tunnel with your organization (allows other users to use it)"
95
+ )
96
+ .option("--unshare", "Make this tunnel private (only you can use it)")
97
+ .action(async (options) => {
98
+ console.log("🌐 Starting tunnel (minimal worker) mode...");
99
+ console.log(` Tools: ${TUNNEL_MINIMAL_TOOLS.join(", ")}`);
100
+ await worker({
101
+ ...options,
102
+ allowedTools: TUNNEL_MINIMAL_TOOLS,
103
+ });
104
+ });
105
+ }
106
+
107
+ export function addFilesCommand(program: Command): void {
108
+ program
109
+ .command("files")
110
+ .description(
111
+ "Sync files between local filesystem and Knowhow FS (uses fileMounts config)"
112
+ )
113
+ .option("--upload", "Force upload direction for all mounts")
114
+ .option("--download", "Force download direction for all mounts")
115
+ .option("--config <path>", "Path to knowhow.json", "./knowhow.json")
116
+ .option("--dry-run", "Print what would be synced without doing it")
117
+ .action(async (options) => {
118
+ try {
119
+ await fileSync(options);
120
+ } catch (error) {
121
+ console.error("Error syncing files:", error);
122
+ process.exit(1);
123
+ }
124
+ });
125
+ }
126
+
127
+ export function addCloudWorkerCommand(program: Command): void {
128
+ program
129
+ .command("cloudworker")
130
+ .description("Create or sync a cloud worker with your local knowhow config")
131
+ .option(
132
+ "--create",
133
+ "Create a new cloud worker with synced config and files"
134
+ )
135
+ .option(
136
+ "--push <uid>",
137
+ "Push/sync local config and files to an existing cloud worker"
138
+ )
139
+ .option(
140
+ "--pull <id>",
141
+ "Pull the latest workerConfigJson from a cloud worker and update local config"
142
+ )
143
+ .option("--name <name>", "Name for the cloud worker (used with --create)")
144
+ .option("--dry-run", "Print what would be synced without doing it")
145
+ .action(async (options) => {
146
+ try {
147
+ const { cloudWorker, pullCloudWorkerConfig } = await import(
148
+ "../cloudWorker"
149
+ );
150
+ if (options.pull) {
151
+ await pullCloudWorkerConfig({ id: options.pull });
152
+ } else {
153
+ await cloudWorker(options);
154
+ }
155
+ } catch (error) {
156
+ console.error("Error running cloudworker:", error);
157
+ process.exit(1);
158
+ }
159
+ });
160
+ }
package/src/config.ts CHANGED
@@ -168,6 +168,27 @@ export async function init() {
168
168
  console.log("Initializing global knowhow config at ~/.knowhow");
169
169
  const globalConfigDir = await ensureGlobalConfigDir();
170
170
 
171
+ // Ensure the script module is registered in the global config so that
172
+ // `knowhow script` and the `executeScript` tool are available everywhere.
173
+ const SCRIPT_MODULE = "@tyvm/knowhow-module-script";
174
+ const globalConfigPath = path.join(globalConfigDir, "knowhow.json");
175
+ try {
176
+ const rawGlobal = fs.existsSync(globalConfigPath)
177
+ ? fs.readFileSync(globalConfigPath, "utf8")
178
+ : JSON.stringify(defaultConfig, null, 2);
179
+ const globalConf = JSON.parse(rawGlobal) as Config;
180
+ if (!globalConf.modules) {
181
+ globalConf.modules = [];
182
+ }
183
+ if (!globalConf.modules.includes(SCRIPT_MODULE)) {
184
+ globalConf.modules.push(SCRIPT_MODULE);
185
+ fs.writeFileSync(globalConfigPath, JSON.stringify(globalConf, null, 2));
186
+ console.log(`✅ Added ${SCRIPT_MODULE} to ~/.knowhow/knowhow.json modules`);
187
+ }
188
+ } catch (e) {
189
+ console.warn(`⚠ Could not update global config to add script module:`, e);
190
+ }
191
+
171
192
  // create the folder structure
172
193
  console.log("Initializing local knowhow config at ./.knowhow");
173
194
  await mkdir(".knowhow", { recursive: true });
@@ -287,6 +308,22 @@ export async function getGlobalConfig(): Promise<Config> {
287
308
  }
288
309
  }
289
310
 
311
+ export async function updateGlobalConfig(config: Config) {
312
+ if (!config || typeof config !== "object") {
313
+ throw new Error("Invalid config object");
314
+ }
315
+
316
+ const globalConfigDir = getGlobalConfigDir();
317
+ await mkdir(globalConfigDir, { recursive: true });
318
+ const globalConfigPath = path.join(globalConfigDir, "knowhow.json");
319
+
320
+ if (fs.existsSync(globalConfigPath)) {
321
+ await fs.promises.copyFile(globalConfigPath, globalConfigPath + ".bak");
322
+ }
323
+
324
+ await writeFile(globalConfigPath, JSON.stringify(config, null, 2));
325
+ }
326
+
290
327
  export async function migrateConfig() {
291
328
  // Apply migrations, used to keep config structure up to date.
292
329
  if (!fs.existsSync(".knowhow/knowhow.json")) {
package/src/index.ts CHANGED
@@ -53,6 +53,7 @@ export * as ai from "./ai";
53
53
 
54
54
  // Export module system types for external modules
55
55
  export * from "./services/modules/types";
56
+ export { ModulesService } from "./services/modules";
56
57
  // Export plugin types for external plugins
57
58
  export { PluginBase } from "./plugins/PluginBase";
58
59
  export { PluginMeta, Plugin, PluginContext } from "./plugins/types";
@@ -1,5 +1,4 @@
1
1
  import { Plugin, PluginContext } from "./types";
2
- import { Config } from "../types";
3
2
  import { VimPlugin } from "./vim";
4
3
  import { LinterPlugin } from "./LinterPlugin";
5
4
  import { LanguagePlugin } from "./language";
@@ -49,26 +48,6 @@ export class PluginService {
49
48
  return instance.meta.key;
50
49
  }
51
50
 
52
- /**
53
- * Load plugins from config's pluginPackages map.
54
- * Each entry maps a plugin key to an npm package name or file path.
55
- * Errors are caught and logged as warnings without crashing.
56
- */
57
- async loadPluginsFromConfig(config: Config): Promise<void> {
58
- const pluginPackages = config.pluginPackages || {};
59
- for (const [key, spec] of Object.entries(pluginPackages)) {
60
- try {
61
- await this.loadPlugin(spec);
62
- } catch (error) {
63
- this.events?.log(
64
- "PluginService",
65
- `Failed to load plugin "${key}" from "${spec}": ${error instanceof Error ? error.message : error}`,
66
- "warn"
67
- );
68
- }
69
- }
70
- }
71
-
72
51
  /** Disable a plugin by its key; returns `true` if found. */
73
52
  disablePlugin(key: string): boolean {
74
53
  const p = this.pluginMap.get(key);
@@ -382,7 +382,7 @@ export class JsonCompressor {
382
382
  i + currentChunk.length - 1
383
383
  }]\nPreview: ${chunkString.substring(0, 100)}...\n[Use ${
384
384
  this.toolName
385
- } tool with key "${key}" to retrieve this chunk]`;
385
+ } tool with key "${key}" to retrieve this chunk]\n[TIP: try jqToolResponse,grepToolResponse,tailToolResponse to filter/search/map without repeated ${this.toolName} calls - especially useful for JSON data]`;
386
386
  finalArray.unshift(stub); // Add stub to the start of our final result.
387
387
 
388
388
  currentChunk = [];
@@ -415,11 +415,11 @@ export class JsonCompressor {
415
415
 
416
416
  // Store objects on FIRST occurrence so second occurrence can reference it
417
417
  // We increment seenCount above, so after increment:
418
- // seenCount=1: first occurrence (just incremented from 0 to 1), store it
419
- // seenCount>=2: we already stored it on first occurrence, should be in dedup map
418
+ // seenCount=0: first occurrence (before increment), store it
419
+ // seenCount>=1: we already stored it on first occurrence, should be in dedup map
420
420
  // Note: This means we store proactively - first occurrence gets stored AND returned in full
421
421
  // Second+ occurrences will find it in the dedup map and return a reference
422
- const isFirstOccurrence = seenCount === 1;
422
+ const isFirstOccurrence = seenCount === 0;
423
423
 
424
424
  // Process the object - apply low-signal detection
425
425
  const objWithLowSignalCompressed = this.compressObjectWithLowSignalDetection(obj, path);
@@ -453,7 +453,7 @@ export class JsonCompressor {
453
453
  result
454
454
  ).join(", ")}\nPreview: ${objectAsString.substring(0, 200)}...\n[Use ${
455
455
  this.toolName
456
- } tool with key "${key}" to retrieve full content]`;
456
+ } tool with key "${key}" to retrieve full content]\n[TIP: try jqToolResponse,grepToolResponse,tailToolResponse to filter/search/map without repeated ${this.toolName} calls - especially useful for JSON data]`;
457
457
  }
458
458
  return result;
459
459
  }
@@ -486,7 +486,7 @@ export class JsonCompressor {
486
486
  200
487
487
  )}...\n[Use ${
488
488
  this.toolName
489
- } tool with key "${key}" to retrieve full content]`;
489
+ } tool with key "${key}" to retrieve full content]\n[TIP: try jqToolResponse,grepToolResponse,tailToolResponse to filter/search/map without repeated ${this.toolName} calls - especially useful for JSON data]`;
490
490
  }
491
491
  return obj;
492
492
  }
@@ -1,38 +1,28 @@
1
1
  import { getConfig, getGlobalConfig } from "../../config";
2
2
  import { KnowhowModule, ModuleContext } from "./types";
3
- import { ToolsService } from "../Tools";
4
3
  import { services } from "../";
5
4
  import * as path from "path";
6
5
 
7
6
  export class ModulesService {
8
- async loadModulesFromConfig(context?: ModuleContext) {
9
- const config = await getConfig();
7
+ async getDefaultContext() {
8
+ return { ...services() };
9
+ }
10
10
 
11
+ async overrideDefaultContext(overrides: Partial<ModuleContext>) {
12
+ const defaultContext = await this.getDefaultContext();
13
+ return { ...defaultContext, ...overrides };
14
+ }
15
+
16
+ async loadModulesFrom(
17
+ config: { modules: string[] } & any,
18
+ context?: Partial<ModuleContext>
19
+ ) {
11
20
  // If no context provided, fall back to global singletons
12
21
  if (!context) {
13
- const { Clients, Plugins, Agents, Tools, Embeddings, MediaProcessor } = services();
14
- context = {
15
- Agents,
16
- Embeddings,
17
- Plugins,
18
- Clients,
19
- Tools,
20
- MediaProcessor,
21
- };
22
+ context = { ...(await this.getDefaultContext()) };
22
23
  }
23
24
 
24
- // Use the toolsService from context
25
- const toolsService = context.Tools;
26
- const agentService = context.Agents;
27
- const pluginService = context.Plugins;
28
- const clients = context.Clients;
29
-
30
- // Load from global config (~/.knowhow/knowhow.json) first, then local config
31
- const globalConfig = await getGlobalConfig();
32
- const allModulePaths = [
33
- ...(globalConfig.modules || []),
34
- ...(config.modules || []),
35
- ];
25
+ const allModulePaths = config.modules;
36
26
 
37
27
  for (const modulePath of allModulePaths) {
38
28
  // Resolve relative paths relative to process.cwd() so that paths like
@@ -43,40 +33,59 @@ export class ModulesService {
43
33
  : modulePath;
44
34
  const rawModule = require(resolvedPath);
45
35
  const importedModule = (rawModule.default || rawModule) as KnowhowModule;
46
- console.log(`🔌 Loading module: ${modulePath} (resolved: ${resolvedPath})`);
47
- await importedModule.init({ config, cwd: process.cwd(), context });
48
- console.log(`✅ Module initialized: ${modulePath} (tools: ${importedModule.tools.length}, agents: ${importedModule.agents.length}, plugins: ${importedModule.plugins.length}, clients: ${importedModule.clients.length})`);
36
+ console.log(
37
+ `🔌 Loading module: ${modulePath} (resolved: ${resolvedPath})`
38
+ );
39
+ await importedModule.init({ config, cwd: process.cwd(), context: context as ModuleContext });
40
+ console.log(
41
+ `✅ Module initialized: ${modulePath} (tools: ${importedModule.tools.length}, agents: ${importedModule.agents.length}, plugins: ${importedModule.plugins.length}, clients: ${importedModule.clients.length})`
42
+ );
49
43
 
50
- for (const agent of importedModule.agents) {
51
- agentService.registerAgent(agent);
44
+ // Only register tools/agents/plugins/clients if the relevant services
45
+ // are available in context (they may not be during early CLI command registration)
46
+ if (context.Agents) {
47
+ for (const agent of importedModule.agents) {
48
+ context.Agents.registerAgent(agent);
49
+ }
52
50
  }
53
51
 
54
- for (const tool of importedModule.tools) {
55
- toolsService.addTool(tool.definition);
56
- toolsService.setFunction(tool.definition.function.name, tool.handler);
52
+ if (context.Tools) {
53
+ for (const tool of importedModule.tools) {
54
+ context.Tools.addTool(tool.definition);
55
+ context.Tools.setFunction(tool.definition.function.name, tool.handler);
56
+ }
57
57
  }
58
58
 
59
- for (const plugin of importedModule.plugins) {
60
- const pluginContext = {
61
- Agents: agentService,
62
- Clients: clients,
63
- Tools: toolsService,
64
- Plugins: pluginService,
65
- ...(context.MediaProcessor ? { MediaProcessor: context.MediaProcessor } : {}),
66
- };
67
- pluginService.registerPlugin(plugin.name, new plugin.plugin(pluginContext as any));
59
+ if (context.Plugins) {
60
+ for (const plugin of importedModule.plugins) {
61
+ context.Plugins.registerPlugin(
62
+ plugin.name,
63
+ new plugin.plugin(context as any)
64
+ );
65
+ }
68
66
  }
69
67
 
70
- for (const client of importedModule.clients) {
71
- clients.registerClient(client.provider, client.client);
72
- clients.registerModels(client.provider, client.models);
68
+ if (context.Clients) {
69
+ for (const client of importedModule.clients) {
70
+ context.Clients.registerClient(client.provider, client.client);
71
+ context.Clients.registerModels(client.provider, client.models);
72
+ }
73
73
  }
74
74
  }
75
+ }
75
76
 
76
- // Also load plugins directly from config's pluginPackages map
77
- if (pluginService) {
78
- await pluginService.loadPluginsFromConfig(config);
79
- await pluginService.loadPluginsFromConfig(globalConfig);
80
- }
77
+ async loadModulesFromConfig(context?: ModuleContext) {
78
+ const config = await getConfig();
79
+
80
+ const globalConfig = await getGlobalConfig();
81
+ const allModulePaths = [
82
+ ...(globalConfig.modules || []),
83
+ ...(config.modules || []),
84
+ ];
85
+
86
+ return this.loadModulesFrom(
87
+ { ...config, modules: allModulePaths },
88
+ context
89
+ );
81
90
  }
82
91
  }
@@ -1,4 +1,5 @@
1
1
  import { Plugin, PluginContext } from "../../plugins/types";
2
+ import { Command } from "commander";
2
3
  import { IAgent } from "../../agents/interface";
3
4
  import { Tool } from "../../clients/types";
4
5
  import { Config } from "../../types";
@@ -55,6 +56,7 @@ export interface ModuleContext {
55
56
  Tools: ToolsService;
56
57
  MediaProcessor?: MediaProcessorService;
57
58
  Tunnel?: TunnelHandler;
59
+ Program?: Command;
58
60
  }
59
61
 
60
62
  export interface KnowhowModule {