@poolzin/pool-bot 2026.3.7 → 2026.3.10

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 (150) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +147 -69
  3. package/dist/.buildstamp +1 -1
  4. package/dist/agents/error-classifier.js +251 -0
  5. package/dist/agents/skills/security.js +211 -0
  6. package/dist/build-info.json +3 -3
  7. package/dist/cli/cron-cli/register.cron-dashboard.js +339 -0
  8. package/dist/cli/cron-cli/register.js +2 -0
  9. package/dist/cli/errors.js +187 -0
  10. package/dist/cli/lazy-commands.example.js +113 -0
  11. package/dist/cli/lazy-commands.js +329 -0
  12. package/dist/cli/program/command-registry.js +26 -0
  13. package/dist/cli/program/register.maintenance.js +21 -0
  14. package/dist/cli/program/register.skills.js +4 -0
  15. package/dist/cli/program/register.subclis.js +9 -0
  16. package/dist/cli/swarm-cli/register.js +8 -0
  17. package/dist/cli/swarm-cli/register.swarm-status.js +488 -0
  18. package/dist/cli/telemetry-cli/register.js +10 -0
  19. package/dist/cli/telemetry-cli/register.telemetry-alerts.js +176 -0
  20. package/dist/cli/telemetry-cli/register.telemetry-metrics.js +323 -0
  21. package/dist/cli/telemetry-cli/register.telemetry-status.js +179 -0
  22. package/dist/commands/doctor-checks.js +498 -0
  23. package/dist/config/config.js +1 -0
  24. package/dist/config/secrets-integration.js +88 -0
  25. package/dist/context-engine/index.js +33 -0
  26. package/dist/context-engine/legacy.js +179 -0
  27. package/dist/context-engine/registry.js +86 -0
  28. package/dist/context-engine/summarizing.js +290 -0
  29. package/dist/context-engine/types.js +7 -0
  30. package/dist/cron/service/timer.js +18 -0
  31. package/dist/gateway/protocol/index.js +5 -2
  32. package/dist/gateway/protocol/schema/error-codes.js +1 -0
  33. package/dist/gateway/protocol/schema/swarm.js +80 -0
  34. package/dist/gateway/protocol/schema.js +1 -0
  35. package/dist/gateway/server-close.js +4 -0
  36. package/dist/gateway/server-constants.js +1 -0
  37. package/dist/gateway/server-cron.js +29 -0
  38. package/dist/gateway/server-maintenance.js +35 -2
  39. package/dist/gateway/server-methods/swarm.js +58 -0
  40. package/dist/gateway/server-methods/telemetry.js +71 -0
  41. package/dist/gateway/server-methods-list.js +8 -0
  42. package/dist/gateway/server-methods.js +9 -2
  43. package/dist/gateway/server.impl.js +33 -16
  44. package/dist/infra/abort-pattern.js +106 -0
  45. package/dist/infra/retry.js +96 -0
  46. package/dist/secrets/index.js +28 -0
  47. package/dist/secrets/resolver.js +185 -0
  48. package/dist/secrets/runtime.js +142 -0
  49. package/dist/secrets/types.js +11 -0
  50. package/dist/security/dangerous-tools.js +80 -0
  51. package/dist/security/types.js +12 -0
  52. package/dist/skills/commands.js +333 -0
  53. package/dist/skills/index.js +164 -0
  54. package/dist/skills/loader.js +282 -0
  55. package/dist/skills/parser.js +446 -0
  56. package/dist/skills/registry.js +394 -0
  57. package/dist/skills/security.js +312 -0
  58. package/dist/skills/types.js +21 -0
  59. package/dist/swarm/service.js +247 -0
  60. package/dist/telemetry/alert-engine.js +258 -0
  61. package/dist/telemetry/cron-instrumentation.js +49 -0
  62. package/dist/telemetry/gateway-instrumentation.js +80 -0
  63. package/dist/telemetry/instrumentation.js +66 -0
  64. package/dist/telemetry/service.js +345 -0
  65. package/dist/test-utils/index.js +219 -0
  66. package/dist/tui/components/assistant-message.js +6 -2
  67. package/dist/tui/components/hyperlink-markdown.js +32 -0
  68. package/dist/tui/components/searchable-select-list.js +12 -1
  69. package/dist/tui/components/user-message.js +6 -2
  70. package/dist/tui/index.js +611 -0
  71. package/dist/tui/theme/theme-detection.js +226 -0
  72. package/dist/tui/tui-command-handlers.js +20 -0
  73. package/dist/tui/tui-formatters.js +4 -3
  74. package/dist/tui/utils/ctrl-c-handler.js +67 -0
  75. package/dist/tui/utils/osc8-hyperlinks.js +208 -0
  76. package/dist/tui/utils/safe-stop.js +180 -0
  77. package/dist/tui/utils/session-key-utils.js +81 -0
  78. package/dist/tui/utils/text-sanitization.js +284 -0
  79. package/dist/utils/lru-cache.js +116 -0
  80. package/dist/utils/performance.js +199 -0
  81. package/dist/utils/retry.js +240 -0
  82. package/docs/INTEGRATION_PLAN.md +475 -0
  83. package/docs/INTEGRATION_SUMMARY.md +215 -0
  84. package/docs/MELHORIAS_IMPLEMENTADAS.md +228 -0
  85. package/docs/MELHORIAS_PROFISSIONAIS.md +282 -0
  86. package/docs/PLANO_ACAO_TUI.md +357 -0
  87. package/docs/PROGRESSO_TUI.md +66 -0
  88. package/docs/RELATORIO_FINAL.md +217 -0
  89. package/docs/diagnostico-shell-completion.md +265 -0
  90. package/docs/features/advanced-memory.md +585 -0
  91. package/docs/features/discord-components-v2.md +277 -0
  92. package/docs/features/swarm.md +100 -0
  93. package/docs/features/telemetry.md +284 -0
  94. package/docs/integrations/HEXSTRIKE_PLAN.md +796 -0
  95. package/docs/integrations/INTEGRATION_PLAN.md +744 -0
  96. package/docs/integrations/PAGE_AGENT_PLAN.md +370 -0
  97. package/docs/integrations/XYOPS_PLAN.md +978 -0
  98. package/docs/models/provider-infrastructure.md +400 -0
  99. package/docs/security/exec-approvals.md +294 -0
  100. package/docs/skills/IMPLEMENTATION_SUMMARY.md +145 -0
  101. package/docs/skills/SKILL.md +524 -0
  102. package/docs/skills.md +405 -0
  103. package/extensions/bluebubbles/package.json +1 -1
  104. package/extensions/copilot-proxy/package.json +1 -1
  105. package/extensions/diagnostics-otel/package.json +1 -1
  106. package/extensions/discord/package.json +1 -1
  107. package/extensions/feishu/package.json +1 -1
  108. package/extensions/google-antigravity-auth/package.json +1 -1
  109. package/extensions/google-gemini-cli-auth/package.json +1 -1
  110. package/extensions/googlechat/package.json +1 -1
  111. package/extensions/hexstrike-bridge/README.md +119 -0
  112. package/extensions/hexstrike-bridge/index.test.ts +247 -0
  113. package/extensions/hexstrike-bridge/index.ts +487 -0
  114. package/extensions/hexstrike-bridge/package.json +17 -0
  115. package/extensions/imessage/package.json +1 -1
  116. package/extensions/irc/package.json +1 -1
  117. package/extensions/line/package.json +1 -1
  118. package/extensions/llm-task/package.json +1 -1
  119. package/extensions/lobster/package.json +1 -1
  120. package/extensions/matrix/CHANGELOG.md +5 -0
  121. package/extensions/matrix/package.json +1 -1
  122. package/extensions/mattermost/package.json +1 -1
  123. package/extensions/mcp-server/index.ts +14 -0
  124. package/extensions/mcp-server/package.json +11 -0
  125. package/extensions/mcp-server/src/service.ts +540 -0
  126. package/extensions/memory-core/package.json +1 -1
  127. package/extensions/memory-lancedb/package.json +1 -1
  128. package/extensions/minimax-portal-auth/package.json +1 -1
  129. package/extensions/msteams/CHANGELOG.md +5 -0
  130. package/extensions/msteams/package.json +1 -1
  131. package/extensions/nextcloud-talk/package.json +1 -1
  132. package/extensions/nostr/CHANGELOG.md +5 -0
  133. package/extensions/nostr/package.json +1 -1
  134. package/extensions/open-prose/package.json +1 -1
  135. package/extensions/openai-codex-auth/package.json +1 -1
  136. package/extensions/signal/package.json +1 -1
  137. package/extensions/slack/package.json +1 -1
  138. package/extensions/telegram/package.json +1 -1
  139. package/extensions/tlon/package.json +1 -1
  140. package/extensions/twitch/CHANGELOG.md +5 -0
  141. package/extensions/twitch/package.json +1 -1
  142. package/extensions/voice-call/CHANGELOG.md +5 -0
  143. package/extensions/voice-call/package.json +1 -1
  144. package/extensions/whatsapp/package.json +1 -1
  145. package/extensions/zalo/CHANGELOG.md +5 -0
  146. package/extensions/zalo/package.json +1 -1
  147. package/extensions/zalouser/CHANGELOG.md +5 -0
  148. package/extensions/zalouser/package.json +1 -1
  149. package/package.json +8 -1
  150. package/skills/example-skill/SKILL.md +195 -0
@@ -0,0 +1,187 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Structured error class for PoolBot CLI
4
+ * Provides rich error information with recovery suggestions
5
+ */
6
+ export class PoolBotError extends Error {
7
+ code;
8
+ category;
9
+ recoverable;
10
+ severity;
11
+ suggestions;
12
+ context;
13
+ exitCode;
14
+ constructor(options) {
15
+ super(options.message);
16
+ this.name = "PoolBotError";
17
+ this.code = options.code;
18
+ this.category = options.category;
19
+ this.recoverable = options.recoverable;
20
+ this.severity = options.severity ?? "error";
21
+ this.suggestions = options.suggestions ?? [];
22
+ this.context = options.context;
23
+ this.exitCode = options.exitCode ?? 1;
24
+ if (options.cause) {
25
+ this.cause = options.cause;
26
+ }
27
+ // Maintain proper stack trace
28
+ Error.captureStackTrace?.(this, PoolBotError);
29
+ }
30
+ /**
31
+ * Format error for user display with suggestions
32
+ */
33
+ formatForDisplay() {
34
+ const lines = [`❌ ${this.message}`, ` Code: ${this.code}`];
35
+ if (this.suggestions.length > 0) {
36
+ lines.push("", "💡 Suggestions:");
37
+ for (const suggestion of this.suggestions) {
38
+ lines.push(` • ${suggestion}`);
39
+ }
40
+ }
41
+ if (this.recoverable) {
42
+ lines.push("", "ℹ️ This error can be recovered from.");
43
+ }
44
+ return lines.join("\n");
45
+ }
46
+ /**
47
+ * Format error for JSON output
48
+ */
49
+ toJSON() {
50
+ const errorObj = {
51
+ message: this.message,
52
+ code: this.code,
53
+ category: this.category,
54
+ recoverable: this.recoverable,
55
+ severity: this.severity,
56
+ suggestions: this.suggestions,
57
+ context: this.context,
58
+ exitCode: this.exitCode,
59
+ };
60
+ if (this.cause) {
61
+ errorObj.cause = String(this.cause);
62
+ }
63
+ return { error: errorObj };
64
+ }
65
+ }
66
+ /**
67
+ * Error code registry with predefined common errors
68
+ */
69
+ export const ErrorCodes = {
70
+ // Config errors (E1xxx)
71
+ CONFIG_INVALID: "E1001",
72
+ CONFIG_MISSING: "E1002",
73
+ CONFIG_DEPRECATED: "E1003",
74
+ CONFIG_VERSION_MISMATCH: "E1004",
75
+ // Gateway errors (E2xxx)
76
+ GATEWAY_NOT_RUNNING: "E2001",
77
+ GATEWAY_CONNECTION_FAILED: "E2002",
78
+ GATEWAY_TIMEOUT: "E2003",
79
+ GATEWAY_AUTH_FAILED: "E2004",
80
+ // Auth errors (E3xxx)
81
+ AUTH_TOKEN_MISSING: "E3001",
82
+ AUTH_TOKEN_INVALID: "E3002",
83
+ AUTH_OAUTH_EXPIRED: "E3003",
84
+ // Network errors (E4xxx)
85
+ NETWORK_UNREACHABLE: "E4001",
86
+ NETWORK_TIMEOUT: "E4002",
87
+ DNS_RESOLUTION_FAILED: "E4003",
88
+ // Validation errors (E5xxx)
89
+ VALIDATION_INVALID_INPUT: "E5001",
90
+ VALIDATION_MISSING_REQUIRED: "E5002",
91
+ VALIDATION_TYPE_MISMATCH: "E5003",
92
+ // Filesystem errors (E6xxx)
93
+ FILE_NOT_FOUND: "E6001",
94
+ FILE_PERMISSION_DENIED: "E6002",
95
+ FILE_ALREADY_EXISTS: "E6003",
96
+ // Plugin errors (E7xxx)
97
+ PLUGIN_LOAD_FAILED: "E7001",
98
+ PLUGIN_NOT_FOUND: "E7002",
99
+ PLUGIN_INCOMPATIBLE: "E7003",
100
+ // Completion errors (E8xxx)
101
+ COMPLETION_CACHE_MISSING: "E8001",
102
+ COMPLETION_SHELL_UNSUPPORTED: "E8002",
103
+ COMPLETION_PROFILE_WRITE_FAILED: "E8003",
104
+ };
105
+ /**
106
+ * Helper functions for common error scenarios
107
+ */
108
+ export function createConfigError(message, suggestions, context) {
109
+ return new PoolBotError({
110
+ message,
111
+ code: ErrorCodes.CONFIG_INVALID,
112
+ category: "config",
113
+ recoverable: true,
114
+ suggestions: suggestions ?? ["Run `poolbot doctor` to check configuration"],
115
+ context,
116
+ });
117
+ }
118
+ export function createGatewayError(message, code, suggestions, context) {
119
+ return new PoolBotError({
120
+ message,
121
+ code,
122
+ category: "gateway",
123
+ recoverable: true,
124
+ suggestions: suggestions ?? [
125
+ "Run `poolbot gateway status` to check gateway health",
126
+ "Run `poolbot doctor` for diagnostics",
127
+ ],
128
+ context,
129
+ });
130
+ }
131
+ export function createValidationError(message, context) {
132
+ return new PoolBotError({
133
+ message,
134
+ code: ErrorCodes.VALIDATION_INVALID_INPUT,
135
+ category: "validation",
136
+ recoverable: true,
137
+ suggestions: ["Check command help with `--help` for usage information"],
138
+ context,
139
+ });
140
+ }
141
+ export function createCompletionError(message, code, suggestions) {
142
+ return new PoolBotError({
143
+ message,
144
+ code,
145
+ category: "config",
146
+ recoverable: true,
147
+ suggestions: suggestions ?? [
148
+ "Run `poolbot completion --write-state` to generate cache",
149
+ "Run `poolbot completion --install` to install to shell profile",
150
+ ],
151
+ });
152
+ }
153
+ /**
154
+ * Async wrapper that converts exceptions to PoolBotError
155
+ */
156
+ export async function withErrorHandling(operation, errorMapper) {
157
+ try {
158
+ return await operation();
159
+ }
160
+ catch (err) {
161
+ if (err instanceof PoolBotError) {
162
+ throw err;
163
+ }
164
+ throw errorMapper(err);
165
+ }
166
+ }
167
+ /**
168
+ * Zod schema for validating error options
169
+ */
170
+ export const PoolBotErrorSchema = z.object({
171
+ message: z.string(),
172
+ code: z.string(),
173
+ category: z.enum([
174
+ "config",
175
+ "gateway",
176
+ "auth",
177
+ "network",
178
+ "validation",
179
+ "filesystem",
180
+ "runtime",
181
+ "plugin",
182
+ ]),
183
+ recoverable: z.boolean(),
184
+ severity: z.enum(["error", "warning", "info"]).optional(),
185
+ suggestions: z.array(z.string()).optional(),
186
+ exitCode: z.number().optional(),
187
+ });
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Example: Integrating Lazy Commands with PoolBot CLI
3
+ *
4
+ * This file shows how to integrate the lazy command loading system
5
+ * into the main PoolBot CLI.
6
+ *
7
+ * ## Integration Steps
8
+ *
9
+ * 1. Import the lazy command helpers
10
+ * 2. Replace static imports with lazy loaders
11
+ * 3. Add performance monitoring
12
+ *
13
+ * ## Example Integration
14
+ *
15
+ * ```typescript
16
+ * #!/usr/bin/env node
17
+ * import { Command } from "commander";
18
+ * import { lazyCommand, getLazyCommandStats, preloadCommands } from "./cli/lazy-commands.js";
19
+ * import { buildCoreCommand } from "./commands/core.js"; // Keep core commands eager
20
+ *
21
+ * const program = new Command();
22
+ *
23
+ * // Core commands - loaded immediately
24
+ * program.addCommand(buildCoreCommand());
25
+ *
26
+ * // Lazy-loaded commands - loaded on first use
27
+ * lazyCommand(program, {
28
+ * name: "agent",
29
+ * description: "Manage AI agents",
30
+ * loader: async () => {
31
+ * const { buildAgentCommand } = await import("./commands/agent.js");
32
+ * return buildAgentCommand();
33
+ * },
34
+ * aliases: ["a"]
35
+ * });
36
+ *
37
+ * lazyCommand(program, {
38
+ * name: "config",
39
+ * description: "Manage configuration",
40
+ * loader: async () => {
41
+ * const { buildConfigCommand } = await import("./commands/config.js");
42
+ * return buildConfigCommand();
43
+ * }
44
+ * });
45
+ *
46
+ * lazyCommand(program, {
47
+ * name: "channels",
48
+ * description: "Manage messaging channels",
49
+ * loader: async () => {
50
+ * const { buildChannelsCommand } = await import("./commands/channels.js");
51
+ * return buildChannelsCommand();
52
+ * }
53
+ * });
54
+ *
55
+ * // Preload commonly used commands in interactive mode
56
+ * if (process.argv.length < 3) {
57
+ * // Interactive mode - preload likely commands
58
+ * preloadCommands(["agent", "config"]);
59
+ * }
60
+ *
61
+ * // Show stats in debug mode
62
+ * if (process.env.DEBUG) {
63
+ * console.log("Command loading stats:", getLazyCommandStats());
64
+ * }
65
+ *
66
+ * await program.parseAsync();
67
+ * ```
68
+ *
69
+ * ## Performance Benefits
70
+ *
71
+ * Before lazy loading:
72
+ * - Startup time: ~500ms (loading all commands)
73
+ * - Memory: ~50MB (all commands in memory)
74
+ *
75
+ * After lazy loading:
76
+ * - Startup time: ~100ms (loading only core)
77
+ * - Memory: ~20MB (only used commands loaded)
78
+ *
79
+ * ## Migration Guide
80
+ *
81
+ * To migrate an existing command to lazy loading:
82
+ *
83
+ * 1. **Before:**
84
+ * ```typescript
85
+ * import { buildAgentCommand } from "./commands/agent.js";
86
+ * program.addCommand(buildAgentCommand());
87
+ * ```
88
+ *
89
+ * 2. **After:**
90
+ * ```typescript
91
+ * lazyCommand(program, {
92
+ * name: "agent",
93
+ * description: "Manage AI agents",
94
+ * loader: async () => {
95
+ * const { buildAgentCommand } = await import("./commands/agent.js");
96
+ * return buildAgentCommand();
97
+ * }
98
+ * });
99
+ * ```
100
+ *
101
+ * ## Testing with Lazy Commands
102
+ *
103
+ * For testing, you may want to eagerly load all commands:
104
+ *
105
+ * ```typescript
106
+ * import { loadAllCommands } from "./cli/lazy-commands.js";
107
+ *
108
+ * beforeAll(async () => {
109
+ * await loadAllCommands();
110
+ * });
111
+ * ```
112
+ */
113
+ export {};
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Lazy Command Loading System
3
+ *
4
+ * Optimizes CLI startup time by loading commands on-demand
5
+ * rather than loading all commands at startup.
6
+ *
7
+ * This significantly improves startup performance when only
8
+ * a subset of commands is used.
9
+ *
10
+ * ## Usage
11
+ *
12
+ * ```typescript
13
+ * import { Command } from "commander";
14
+ * import { lazyCommand } from "./lazy-commands.js";
15
+ *
16
+ * const program = new Command();
17
+ *
18
+ * // Register lazy-loaded command
19
+ * lazyCommand(program, "agent", "Manage AI agents", async () => {
20
+ * const { buildAgentCommand } = await import("./commands/agent.js");
21
+ * return buildAgentCommand();
22
+ * });
23
+ * ```
24
+ */
25
+ /**
26
+ * Global registry for lazy commands
27
+ */
28
+ class LazyCommandRegistry {
29
+ commands = new Map();
30
+ loadTimes = new Map();
31
+ /**
32
+ * Register a lazy command
33
+ */
34
+ register(options) {
35
+ if (this.commands.has(options.name)) {
36
+ throw new Error(`Command "${options.name}" is already registered`);
37
+ }
38
+ const entry = {
39
+ name: options.name,
40
+ description: options.description,
41
+ loader: options.loader,
42
+ aliases: options.aliases ?? [],
43
+ loaded: false,
44
+ };
45
+ this.commands.set(options.name, entry);
46
+ // Register aliases pointing to main entry
47
+ for (const alias of options.aliases ?? []) {
48
+ if (this.commands.has(alias)) {
49
+ throw new Error(`Alias "${alias}" is already registered`);
50
+ }
51
+ this.commands.set(alias, entry);
52
+ }
53
+ }
54
+ /**
55
+ * Check if a command is registered
56
+ */
57
+ has(name) {
58
+ return this.commands.has(name);
59
+ }
60
+ /**
61
+ * Get a registered command entry
62
+ */
63
+ get(name) {
64
+ return this.commands.get(name);
65
+ }
66
+ /**
67
+ * Get command loading statistics
68
+ */
69
+ getStats() {
70
+ const uniqueCommands = new Set(Array.from(this.commands.values()).map((c) => c.name));
71
+ const total = uniqueCommands.size;
72
+ const loadedEntries = Array.from(this.commands.values()).filter((c) => c.loaded && !c.aliases.includes(c.name) // Count main entries only
73
+ );
74
+ const loaded = loadedEntries.length;
75
+ const loadTimes = {};
76
+ for (const [name, time] of this.loadTimes) {
77
+ loadTimes[name] = time;
78
+ }
79
+ const times = Object.values(loadTimes);
80
+ const averageLoadTime = times.length > 0 ? times.reduce((a, b) => a + b, 0) / times.length : undefined;
81
+ return {
82
+ total,
83
+ loaded,
84
+ lazy: total - loaded,
85
+ averageLoadTime,
86
+ loadTimes,
87
+ };
88
+ }
89
+ /**
90
+ * Mark a command as loaded with its load time
91
+ */
92
+ markLoaded(name, loadTime) {
93
+ const entry = this.commands.get(name);
94
+ if (entry) {
95
+ entry.loaded = true;
96
+ entry.loadTime = loadTime;
97
+ this.loadTimes.set(name, loadTime);
98
+ }
99
+ }
100
+ /**
101
+ * Mark a command as failed to load
102
+ */
103
+ markFailed(name, error) {
104
+ const entry = this.commands.get(name);
105
+ if (entry) {
106
+ entry.error = error;
107
+ }
108
+ }
109
+ /**
110
+ * Get list of registered command names (main commands only, not aliases)
111
+ */
112
+ getCommandNames() {
113
+ const seen = new Set();
114
+ const names = [];
115
+ for (const entry of this.commands.values()) {
116
+ if (!seen.has(entry.name)) {
117
+ seen.add(entry.name);
118
+ names.push(entry.name);
119
+ }
120
+ }
121
+ return names;
122
+ }
123
+ /**
124
+ * Get all entries (for debugging)
125
+ */
126
+ getAllEntries() {
127
+ return Array.from(this.commands.values());
128
+ }
129
+ /**
130
+ * Clear all registrations
131
+ */
132
+ clear() {
133
+ this.commands.clear();
134
+ this.loadTimes.clear();
135
+ }
136
+ }
137
+ /**
138
+ * Global registry instance
139
+ */
140
+ export const globalLazyCommandRegistry = new LazyCommandRegistry();
141
+ /**
142
+ * Register a lazy-loaded command with a Commander program
143
+ *
144
+ * This creates a placeholder command that will load the actual
145
+ * implementation only when the command is invoked.
146
+ *
147
+ * @param program - The Commander program instance
148
+ * @param options - Lazy command configuration
149
+ * @returns The placeholder command (for chaining)
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * lazyCommand(program, {
154
+ * name: "agent",
155
+ * description: "Manage AI agents",
156
+ * loader: async () => {
157
+ * const { buildAgentCommand } = await import("./commands/agent.js");
158
+ * return buildAgentCommand();
159
+ * },
160
+ * aliases: ["a"]
161
+ * });
162
+ * ```
163
+ */
164
+ export function lazyCommand(program, options) {
165
+ // Register in global registry
166
+ globalLazyCommandRegistry.register(options);
167
+ // Create placeholder command
168
+ const placeholder = program
169
+ .command(options.name)
170
+ .description(options.description);
171
+ // Add arguments if specified
172
+ if (options.arguments) {
173
+ placeholder.arguments(options.arguments);
174
+ }
175
+ // Add aliases
176
+ for (const alias of options.aliases ?? []) {
177
+ placeholder.alias(alias);
178
+ }
179
+ // Set up lazy loading action
180
+ placeholder.action(async (...actionArgs) => {
181
+ const startTime = performance.now();
182
+ try {
183
+ // Load the actual command module
184
+ const realCommand = await options.loader();
185
+ // Calculate load time
186
+ const loadTime = performance.now() - startTime;
187
+ globalLazyCommandRegistry.markLoaded(options.name, loadTime);
188
+ // Execute the loaded command's action with the same arguments
189
+ // The loaded command should have its own action handler
190
+ if (realCommand && typeof realCommand.parseAsync === "function") {
191
+ // If it's a full Command instance, we need to execute its action
192
+ // Get the action handler from the loaded command
193
+ const actionHandler = realCommand._actionHandler;
194
+ if (actionHandler) {
195
+ await actionHandler(...actionArgs);
196
+ }
197
+ else {
198
+ // Fallback: parse with the same arguments
199
+ // Remove command object from args and re-parse
200
+ const argv = process.argv.slice(2);
201
+ await realCommand.parseAsync(argv);
202
+ }
203
+ }
204
+ else {
205
+ throw new Error(`Loader for "${options.name}" did not return a valid Command instance`);
206
+ }
207
+ }
208
+ catch (error) {
209
+ const err = error instanceof Error ? error : new Error(String(error));
210
+ globalLazyCommandRegistry.markFailed(options.name, err);
211
+ throw new Error(`Failed to load command "${options.name}": ${err.message}`);
212
+ }
213
+ });
214
+ return placeholder;
215
+ }
216
+ /**
217
+ * Simple shorthand for creating a lazy command with minimal config
218
+ *
219
+ * @param program - The Commander program
220
+ * @param name - Command name
221
+ * @param description - Command description
222
+ * @param loader - Async function that loads and returns the command
223
+ * @returns The placeholder command
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * lazy(program, "agent", "Manage agents", async () => {
228
+ * const mod = await import("./commands/agent.js");
229
+ * return mod.buildAgentCommand();
230
+ * });
231
+ * ```
232
+ */
233
+ export function lazy(program, name, description, loader) {
234
+ return lazyCommand(program, { name, description, loader });
235
+ }
236
+ /**
237
+ * Preload commands that are likely to be used
238
+ *
239
+ * This can be called after initial setup to preload commands
240
+ * in the background, improving responsiveness for common commands.
241
+ *
242
+ * @param commandNames - Names of commands to preload
243
+ * @returns Promise that resolves when all preloads are complete
244
+ */
245
+ export async function preloadCommands(commandNames) {
246
+ await Promise.all(commandNames.map(async (name) => {
247
+ const entry = globalLazyCommandRegistry.get(name);
248
+ if (entry && !entry.loaded) {
249
+ try {
250
+ const startTime = performance.now();
251
+ await entry.loader();
252
+ globalLazyCommandRegistry.markLoaded(name, performance.now() - startTime);
253
+ }
254
+ catch (error) {
255
+ // Preload errors are non-fatal
256
+ console.warn(`Failed to preload command "${name}": ${error instanceof Error ? error.message : String(error)}`);
257
+ }
258
+ }
259
+ }));
260
+ }
261
+ /**
262
+ * Eagerly load all registered commands
263
+ *
264
+ * Use this for testing or when startup time doesn't matter.
265
+ *
266
+ * @returns Promise that resolves when all commands are loaded
267
+ */
268
+ export async function loadAllCommands() {
269
+ const names = globalLazyCommandRegistry.getCommandNames();
270
+ await preloadCommands(names);
271
+ }
272
+ /**
273
+ * Get current loading statistics
274
+ *
275
+ * @returns Statistics about lazy-loaded commands
276
+ */
277
+ export function getLazyCommandStats() {
278
+ return globalLazyCommandRegistry.getStats();
279
+ }
280
+ /**
281
+ * Performance measurement utilities for command loading
282
+ */
283
+ export class LoadTimeTracker {
284
+ measurements = new Map();
285
+ /**
286
+ * Measure execution time of a function
287
+ */
288
+ async measure(name, fn) {
289
+ const start = performance.now();
290
+ try {
291
+ return await fn();
292
+ }
293
+ finally {
294
+ const duration = performance.now() - start;
295
+ this.measurements.set(name, duration);
296
+ }
297
+ }
298
+ /**
299
+ * Get all measurements
300
+ */
301
+ getMeasurements() {
302
+ return Object.fromEntries(this.measurements);
303
+ }
304
+ /**
305
+ * Get total loading time
306
+ */
307
+ getTotalTime() {
308
+ return Array.from(this.measurements.values()).reduce((a, b) => a + b, 0);
309
+ }
310
+ /**
311
+ * Get slowest operations
312
+ */
313
+ getSlowest(count = 5) {
314
+ return Array.from(this.measurements.entries())
315
+ .sort((a, b) => b[1] - a[1])
316
+ .slice(0, count)
317
+ .map(([name, time]) => ({ name, time }));
318
+ }
319
+ /**
320
+ * Clear all measurements
321
+ */
322
+ clear() {
323
+ this.measurements.clear();
324
+ }
325
+ }
326
+ /**
327
+ * Global load time tracker
328
+ */
329
+ export const globalLoadTimeTracker = new LoadTimeTracker();
@@ -174,6 +174,32 @@ const coreEntries = [
174
174
  mod.registerBrowserCli(program);
175
175
  },
176
176
  },
177
+ {
178
+ commands: [
179
+ {
180
+ name: "mods",
181
+ description: "Manage capability modules (SKILL.md discovery, enable, configure)",
182
+ hasSubcommands: true,
183
+ },
184
+ ],
185
+ register: async ({ program }) => {
186
+ const mod = await import("./register.skills.js");
187
+ mod.registerSkillsCli(program);
188
+ },
189
+ },
190
+ {
191
+ commands: [
192
+ {
193
+ name: "swarm",
194
+ description: "Agent swarm coordination and management",
195
+ hasSubcommands: true,
196
+ },
197
+ ],
198
+ register: async ({ program }) => {
199
+ const mod = await import("../swarm-cli/register.js");
200
+ mod.registerSwarmCommand(program);
201
+ },
202
+ },
177
203
  ];
178
204
  function collectCoreCliCommandNames(predicate) {
179
205
  const seen = new Set();
@@ -6,6 +6,18 @@ import { defaultRuntime } from "../../runtime.js";
6
6
  import { formatDocsLink } from "../../terminal/links.js";
7
7
  import { theme } from "../../terminal/theme.js";
8
8
  import { runCommandWithRuntime } from "../cli-utils.js";
9
+ const DOCTOR_CHECKS = [
10
+ "config",
11
+ "auth",
12
+ "gateway",
13
+ "completion",
14
+ "security",
15
+ "plugins",
16
+ "memory",
17
+ "workspace",
18
+ "state",
19
+ "all",
20
+ ];
9
21
  export function registerMaintenanceCommands(program) {
10
22
  program
11
23
  .command("doctor")
@@ -19,6 +31,13 @@ export function registerMaintenanceCommands(program) {
19
31
  .option("--non-interactive", "Run without prompts (safe migrations only)", false)
20
32
  .option("--generate-gateway-token", "Generate and configure a gateway token", false)
21
33
  .option("--deep", "Scan system services for extra gateway installs", false)
34
+ .option("--check <check>", `Run specific check: ${DOCTOR_CHECKS.join(", ")}`, (value) => {
35
+ if (!DOCTOR_CHECKS.includes(value)) {
36
+ throw new Error(`Invalid check: ${value}. Valid: ${DOCTOR_CHECKS.join(", ")}`);
37
+ }
38
+ return value;
39
+ })
40
+ .option("--json", "Output results in JSON format", false)
22
41
  .action(async (opts) => {
23
42
  await runCommandWithRuntime(defaultRuntime, async () => {
24
43
  await doctorCommand(defaultRuntime, {
@@ -29,6 +48,8 @@ export function registerMaintenanceCommands(program) {
29
48
  nonInteractive: Boolean(opts.nonInteractive),
30
49
  generateGatewayToken: Boolean(opts.generateGatewayToken),
31
50
  deep: Boolean(opts.deep),
51
+ check: opts.check,
52
+ json: Boolean(opts.json),
32
53
  });
33
54
  });
34
55
  });
@@ -0,0 +1,4 @@
1
+ import { registerSkillsCommands } from "../../skills/commands.js";
2
+ export function registerSkillsCli(program) {
3
+ registerSkillsCommands(program);
4
+ }