@tyvm/knowhow 0.0.108-dev.879609c → 0.0.108-dev.99ad788

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 (142) hide show
  1. package/package.json +2 -3
  2. package/src/agents/tools/index.ts +0 -1
  3. package/src/agents/tools/list.ts +0 -2
  4. package/src/chat/CliChatService.ts +3 -0
  5. package/src/cli.ts +91 -664
  6. package/src/clients/index.ts +6 -5
  7. package/src/commands/agent.ts +246 -0
  8. package/src/commands/misc.ts +174 -0
  9. package/src/commands/modules.ts +182 -0
  10. package/src/commands/services.ts +77 -0
  11. package/src/commands/workers.ts +160 -0
  12. package/src/config.ts +37 -0
  13. package/src/index.ts +1 -0
  14. package/src/logger.ts +200 -0
  15. package/src/plugins/plugins.ts +0 -21
  16. package/src/processors/JsonCompressor.ts +3 -3
  17. package/src/services/EventService.ts +57 -1
  18. package/src/services/modules/index.ts +67 -49
  19. package/src/services/modules/types.ts +4 -0
  20. package/src/tunnel.ts +216 -0
  21. package/src/types.ts +0 -1
  22. package/src/worker.ts +65 -336
  23. package/src/workers/auth/WsMiddleware.ts +99 -0
  24. package/src/workers/auth/authMiddleware.ts +104 -0
  25. package/src/workers/auth/types.ts +14 -2
  26. package/tests/unit/commands/github-credentials.test.ts +211 -0
  27. package/tests/unit/modules/moduleLoading.test.ts +39 -37
  28. package/tests/unit/plugins/pluginLoading.test.ts +0 -85
  29. package/ts_build/package.json +2 -3
  30. package/ts_build/src/agents/tools/index.d.ts +0 -1
  31. package/ts_build/src/agents/tools/index.js +0 -1
  32. package/ts_build/src/agents/tools/index.js.map +1 -1
  33. package/ts_build/src/agents/tools/list.js +0 -2
  34. package/ts_build/src/agents/tools/list.js.map +1 -1
  35. package/ts_build/src/chat/CliChatService.js +3 -0
  36. package/ts_build/src/chat/CliChatService.js.map +1 -1
  37. package/ts_build/src/cli.js +47 -525
  38. package/ts_build/src/cli.js.map +1 -1
  39. package/ts_build/src/clients/index.js +2 -4
  40. package/ts_build/src/clients/index.js.map +1 -1
  41. package/ts_build/src/commands/agent.d.ts +6 -0
  42. package/ts_build/src/commands/agent.js +229 -0
  43. package/ts_build/src/commands/agent.js.map +1 -0
  44. package/ts_build/src/commands/misc.d.ts +10 -0
  45. package/ts_build/src/commands/misc.js +197 -0
  46. package/ts_build/src/commands/misc.js.map +1 -0
  47. package/ts_build/src/commands/modules.d.ts +3 -0
  48. package/ts_build/src/commands/modules.js +160 -0
  49. package/ts_build/src/commands/modules.js.map +1 -0
  50. package/ts_build/src/commands/services.d.ts +5 -0
  51. package/ts_build/src/commands/services.js +87 -0
  52. package/ts_build/src/commands/services.js.map +1 -0
  53. package/ts_build/src/commands/workers.d.ts +6 -0
  54. package/ts_build/src/commands/workers.js +163 -0
  55. package/ts_build/src/commands/workers.js.map +1 -0
  56. package/ts_build/src/config.d.ts +1 -0
  57. package/ts_build/src/config.js +32 -0
  58. package/ts_build/src/config.js.map +1 -1
  59. package/ts_build/src/index.d.ts +1 -0
  60. package/ts_build/src/index.js +3 -1
  61. package/ts_build/src/index.js.map +1 -1
  62. package/ts_build/src/logger.d.ts +21 -0
  63. package/ts_build/src/logger.js +109 -0
  64. package/ts_build/src/logger.js.map +1 -0
  65. package/ts_build/src/plugins/plugins.d.ts +0 -2
  66. package/ts_build/src/plugins/plugins.js +0 -11
  67. package/ts_build/src/plugins/plugins.js.map +1 -1
  68. package/ts_build/src/processors/JsonCompressor.js +1 -1
  69. package/ts_build/src/services/EventService.d.ts +6 -1
  70. package/ts_build/src/services/EventService.js +28 -0
  71. package/ts_build/src/services/EventService.js.map +1 -1
  72. package/ts_build/src/services/modules/index.d.ts +33 -0
  73. package/ts_build/src/services/modules/index.js +44 -44
  74. package/ts_build/src/services/modules/index.js.map +1 -1
  75. package/ts_build/src/services/modules/types.d.ts +4 -0
  76. package/ts_build/src/tunnel.d.ts +27 -0
  77. package/ts_build/src/tunnel.js +112 -0
  78. package/ts_build/src/tunnel.js.map +1 -0
  79. package/ts_build/src/types.d.ts +0 -1
  80. package/ts_build/src/types.js.map +1 -1
  81. package/ts_build/src/worker.d.ts +1 -4
  82. package/ts_build/src/worker.js +38 -244
  83. package/ts_build/src/worker.js.map +1 -1
  84. package/ts_build/src/workers/auth/WsMiddleware.d.ts +8 -0
  85. package/ts_build/src/workers/auth/WsMiddleware.js +65 -0
  86. package/ts_build/src/workers/auth/WsMiddleware.js.map +1 -0
  87. package/ts_build/src/workers/auth/authMiddleware.d.ts +3 -0
  88. package/ts_build/src/workers/auth/authMiddleware.js +60 -0
  89. package/ts_build/src/workers/auth/authMiddleware.js.map +1 -0
  90. package/ts_build/src/workers/auth/types.d.ts +8 -1
  91. package/ts_build/tests/unit/commands/github-credentials.test.d.ts +1 -0
  92. package/ts_build/tests/unit/commands/github-credentials.test.js +146 -0
  93. package/ts_build/tests/unit/commands/github-credentials.test.js.map +1 -0
  94. package/ts_build/tests/unit/modules/moduleLoading.test.js +20 -26
  95. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
  96. package/ts_build/tests/unit/plugins/pluginLoading.test.js +0 -65
  97. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  98. package/src/agents/tools/executeScript/README.md +0 -94
  99. package/src/agents/tools/executeScript/definition.ts +0 -79
  100. package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +0 -272
  101. package/src/agents/tools/executeScript/examples/quick-test.ts +0 -74
  102. package/src/agents/tools/executeScript/examples/serialization-test.ts +0 -321
  103. package/src/agents/tools/executeScript/examples/test-runner.ts +0 -197
  104. package/src/agents/tools/executeScript/index.ts +0 -98
  105. package/src/services/script-execution/SandboxContext.ts +0 -282
  106. package/src/services/script-execution/ScriptExecutor.ts +0 -441
  107. package/src/services/script-execution/ScriptPolicy.ts +0 -194
  108. package/src/services/script-execution/ScriptTracer.ts +0 -249
  109. package/src/services/script-execution/types.ts +0 -134
  110. package/ts_build/src/agents/tools/executeScript/definition.d.ts +0 -2
  111. package/ts_build/src/agents/tools/executeScript/definition.js +0 -76
  112. package/ts_build/src/agents/tools/executeScript/definition.js.map +0 -1
  113. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +0 -18
  114. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +0 -192
  115. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +0 -1
  116. package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +0 -3
  117. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +0 -64
  118. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +0 -1
  119. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +0 -15
  120. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +0 -266
  121. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +0 -1
  122. package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +0 -4
  123. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +0 -208
  124. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +0 -1
  125. package/ts_build/src/agents/tools/executeScript/index.d.ts +0 -28
  126. package/ts_build/src/agents/tools/executeScript/index.js +0 -72
  127. package/ts_build/src/agents/tools/executeScript/index.js.map +0 -1
  128. package/ts_build/src/services/script-execution/SandboxContext.d.ts +0 -34
  129. package/ts_build/src/services/script-execution/SandboxContext.js +0 -189
  130. package/ts_build/src/services/script-execution/SandboxContext.js.map +0 -1
  131. package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +0 -19
  132. package/ts_build/src/services/script-execution/ScriptExecutor.js +0 -269
  133. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +0 -1
  134. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +0 -28
  135. package/ts_build/src/services/script-execution/ScriptPolicy.js +0 -115
  136. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +0 -1
  137. package/ts_build/src/services/script-execution/ScriptTracer.d.ts +0 -19
  138. package/ts_build/src/services/script-execution/ScriptTracer.js +0 -186
  139. package/ts_build/src/services/script-execution/ScriptTracer.js.map +0 -1
  140. package/ts_build/src/services/script-execution/types.d.ts +0 -108
  141. package/ts_build/src/services/script-execution/types.js +0 -3
  142. package/ts_build/src/services/script-execution/types.js.map +0 -1
@@ -577,11 +577,12 @@ export class AIClient {
577
577
  * @param modelQuery - the model name to search for (can be partial/normalized)
578
578
  * @param provider - optional provider to restrict search to
579
579
  */
580
- findModelFuzzy(modelQuery: string, provider?: string): { provider: string; model: string } | undefined {
580
+ findModelFuzzy(
581
+ modelQuery: string,
582
+ provider?: string
583
+ ): { provider: string; model: string } | undefined {
581
584
  const queryNorm = AIClient.normalizeModelId(modelQuery);
582
- const providers = provider
583
- ? [provider]
584
- : Object.keys(this.clientModels);
585
+ const providers = provider ? [provider] : Object.keys(this.clientModels);
585
586
 
586
587
  for (const p of providers) {
587
588
  const models = (this.clientModels[p] as string[]) ?? [];
@@ -835,7 +836,7 @@ export class AIClient {
835
836
  const splitModel = m.id.split("/");
836
837
 
837
838
  if (splitModel.length < 2) {
838
- console.error(`Cannot parse model format: ${m.id}`);
839
+ console.warn(`Cannot parse model format: ${m.id}`);
839
840
  }
840
841
 
841
842
  const provider = splitModel.length > 1 ? splitModel[0] : "";
@@ -0,0 +1,246 @@
1
+ import { Command } from "commander";
2
+ import { readPromptFile } from "../ai";
3
+ import { AgentModule } from "../chat/modules/AgentModule";
4
+ import { AskModule } from "../chat/modules/AskModule";
5
+ import { SearchModule } from "../chat/modules/SearchModule";
6
+ import { SessionsModule } from "../chat/modules/SessionsModule";
7
+ import { SetupModule } from "../chat/modules/SetupModule";
8
+
9
+ async function readStdin(): Promise<string> {
10
+ return new Promise((resolve) => {
11
+ let data = "";
12
+ process.stdin.setEncoding("utf8");
13
+
14
+ if (process.stdin.isTTY) {
15
+ resolve("");
16
+ return;
17
+ }
18
+
19
+ process.stdin.on("readable", () => {
20
+ const chunk = process.stdin.read();
21
+ if (chunk !== null) data += chunk;
22
+ });
23
+
24
+ process.stdin.on("end", () => resolve(data.trim()));
25
+ });
26
+ }
27
+
28
+ export function addAgentCommand(program: Command, getChatService: () => any): void {
29
+ program
30
+ .command("agent")
31
+ .description("Spin up agents directly from CLI")
32
+ .option(
33
+ "--provider <provider>",
34
+ "AI provider (openai, anthropic, google, xai)"
35
+ )
36
+ .option("--model <model>", "Specific model for the provider")
37
+ .option("--agent-name <name>", "Which agent to use", "Patcher")
38
+ .option(
39
+ "--max-time-limit <minutes>",
40
+ "Time limit for agent execution (minutes)",
41
+ "30"
42
+ )
43
+ .option(
44
+ "--max-spend-limit <dollars>",
45
+ "Cost limit for agent execution (dollars)",
46
+ "10"
47
+ )
48
+ .option("--message-id <messageId>", "Knowhow message ID for task tracking")
49
+ .option("--sync-fs", "Enable filesystem-based synchronization")
50
+ .option(
51
+ "--task-id <taskId>",
52
+ "Pre-generated task ID (used with --sync-fs for predictable agent directory path)"
53
+ )
54
+ .option("--prompt-file <path>", "Custom prompt template file with {text}")
55
+ .option("--input <text>", "Task input (fallback to stdin if not provided)")
56
+ .option(
57
+ "--resume",
58
+ "Resume a previously started task using the --task-id (local FS or remote)"
59
+ )
60
+ .action(async (options) => {
61
+ try {
62
+ const { setupServices } = await import("./services");
63
+ await setupServices();
64
+ const chatService = getChatService();
65
+ const agentModule = new AgentModule();
66
+
67
+ if (options.resume) {
68
+ const threads = await agentModule.loadThreadsForTask(
69
+ options.taskId,
70
+ options.messageId
71
+ );
72
+ const resumeInput =
73
+ options.input || "Please continue from where you left off.";
74
+
75
+ await agentModule.initialize(chatService);
76
+ const { taskCompleted: resumed } =
77
+ await agentModule.resumeFromMessages({
78
+ agentName: options.agentName || "Patcher",
79
+ input: resumeInput,
80
+ threads,
81
+ messageId: options.messageId,
82
+ taskId: options.taskId,
83
+ });
84
+ await resumed;
85
+ return;
86
+ }
87
+
88
+ let input = options.input;
89
+
90
+ if (!input && !options.promptFile) {
91
+ input = await readStdin();
92
+ }
93
+
94
+ input = readPromptFile(options.promptFile, input);
95
+
96
+ if (!input) {
97
+ console.error(
98
+ "Error: No input provided. Use --input flag, pipe input via stdin, or provide --prompt-file."
99
+ );
100
+ process.exit(1);
101
+ }
102
+
103
+ await agentModule.initialize(chatService);
104
+ const { taskCompleted } = await agentModule.setupAgent({
105
+ ...options,
106
+ input,
107
+ maxTimeLimit: parseInt(options.maxTimeLimit, 10),
108
+ maxSpendLimit: parseFloat(options.maxSpendLimit),
109
+ run: true,
110
+ });
111
+ await taskCompleted;
112
+ } catch (error) {
113
+ console.error("Error running agent:", error);
114
+ process.exit(1);
115
+ }
116
+ });
117
+ }
118
+
119
+ export function addAskCommand(program: Command, getChatService: () => any, getConfig: () => any): void {
120
+ program
121
+ .command("ask")
122
+ .description("Direct AI questioning without agent overhead")
123
+ .option("--provider <provider>", "AI provider to use")
124
+ .option("--model <model>", "Specific model")
125
+ .option("--input <text>", "Question (fallback to stdin if not provided)")
126
+ .option("--prompt-file <path>", "Custom prompt template file")
127
+ .action(async (options) => {
128
+ try {
129
+ const { setupServices } = await import("./services");
130
+ await setupServices();
131
+ const chatService = getChatService();
132
+ const config = getConfig();
133
+ let input = options.input;
134
+
135
+ if (!input && !options.promptFile) {
136
+ input = await readStdin();
137
+ }
138
+
139
+ input = readPromptFile(options.promptFile, input);
140
+
141
+ if (!input) {
142
+ console.error(
143
+ "Error: No question provided. Use --input flag, pipe input via stdin, or provide --prompt-file."
144
+ );
145
+ process.exit(1);
146
+ }
147
+
148
+ const askModule = new AskModule();
149
+ await askModule.initialize(chatService);
150
+ await askModule.processAIQuery(input, {
151
+ plugins: config.plugins.enabled,
152
+ currentModel: options.model,
153
+ currentProvider: options.provider,
154
+ chatHistory: [],
155
+ });
156
+ } catch (error) {
157
+ console.error("Error asking AI:", error);
158
+ process.exit(1);
159
+ }
160
+ });
161
+ }
162
+
163
+ export function addSetupCommand(program: Command, getChatService: () => any): void {
164
+ program
165
+ .command("setup")
166
+ .description("Ask the agent to configure knowhow")
167
+ .action(async () => {
168
+ try {
169
+ const { setupServices } = await import("./services");
170
+ await setupServices();
171
+ const chatService = getChatService();
172
+ const agentModule = new AgentModule();
173
+ await agentModule.initialize(chatService);
174
+ const setupModule = new SetupModule(agentModule);
175
+ await setupModule.initialize(chatService);
176
+ await setupModule.handleSetupCommand([]);
177
+ } catch (error) {
178
+ console.error("Error running agent:", error);
179
+ process.exit(1);
180
+ }
181
+ });
182
+ }
183
+
184
+ export function addSearchCommand(program: Command): void {
185
+ program
186
+ .command("search")
187
+ .description("Search embeddings directly from CLI")
188
+ .option(
189
+ "--input <text>",
190
+ "Search query (fallback to stdin if not provided)"
191
+ )
192
+ .option(
193
+ "-e, --embedding <path>",
194
+ "Specific embedding path (default: all)",
195
+ "all"
196
+ )
197
+ .action(async (options) => {
198
+ try {
199
+ const { setupServices } = await import("./services");
200
+ await setupServices();
201
+ let input = options.input;
202
+ if (!input) {
203
+ input = await readStdin();
204
+ if (!input) {
205
+ console.error(
206
+ "Error: No search query provided. Use --input flag or pipe input via stdin."
207
+ );
208
+ process.exit(1);
209
+ }
210
+ }
211
+
212
+ await new SearchModule().searchEmbeddingsCLI(input, options.embedding);
213
+ } catch (error) {
214
+ console.error("Error searching embeddings:", error);
215
+ process.exit(1);
216
+ }
217
+ });
218
+ }
219
+
220
+ export function addSessionsCommand(program: Command, getChatService: () => any): void {
221
+ program
222
+ .command("sessions")
223
+ .description("Manage agent sessions from CLI")
224
+ .option(
225
+ "--all",
226
+ "Show all historical sessions (default: current process only)"
227
+ )
228
+ .option("--csv", "Output sessions as CSV")
229
+ .action(async (options) => {
230
+ try {
231
+ const chatService = getChatService();
232
+ const agentModule = new AgentModule();
233
+ await agentModule.initialize(chatService);
234
+ const sessionsModule = new SessionsModule(agentModule);
235
+ await sessionsModule.initialize(chatService);
236
+ await sessionsModule.logSessionTable(
237
+ options.all || false,
238
+ options.csv || false,
239
+ true
240
+ );
241
+ } catch (error) {
242
+ console.error("Error listing sessions:", error);
243
+ process.exit(1);
244
+ }
245
+ });
246
+ }
@@ -0,0 +1,174 @@
1
+ import { Command } from "commander";
2
+ import { execSync } from "child_process";
3
+ import { version } from "../../package.json";
4
+ import { logger } from "../logger";
5
+ import { generate, embed, upload, download, purge } from "../index";
6
+ import { init } from "../config";
7
+ import { login } from "../login";
8
+ import { KnowhowSimpleClient } from "../services/KnowhowClient";
9
+ import { startChat } from "../chat";
10
+
11
+ export function addInitCommand(program: Command): void {
12
+ program
13
+ .command("init")
14
+ .description("Initialize knowhow configuration")
15
+ .action(async () => {
16
+ await init();
17
+ });
18
+ }
19
+
20
+ export function addLoginCommand(program: Command): void {
21
+ program
22
+ .command("login")
23
+ .description("Login to knowhow")
24
+ .option("--jwt", "Use manual JWT input instead of browser login")
25
+ .action(async (opts) => {
26
+ await login(opts.jwt);
27
+ });
28
+ }
29
+
30
+ export function addUpdateCommand(program: Command): void {
31
+ program
32
+ .command("update")
33
+ .description("Update knowhow to the latest version from npm")
34
+ .action(async () => {
35
+ try {
36
+ console.log("šŸ”„ Checking for knowhow updates...");
37
+ console.log(`Current version: ${version}`);
38
+ console.log("šŸ“¦ Installing latest version from npm...");
39
+ execSync("npm install -g @tyvm/knowhow@latest", {
40
+ stdio: "inherit",
41
+ encoding: "utf-8",
42
+ });
43
+ console.log("āœ“ knowhow has been updated successfully!");
44
+ console.log("Run 'knowhow --version' to see the new version.");
45
+ } catch (error) {
46
+ console.error("Error updating knowhow:", error.message);
47
+ process.exit(1);
48
+ }
49
+ });
50
+ }
51
+
52
+ export function addGenerateCommand(program: Command): void {
53
+ program
54
+ .command("generate")
55
+ .description("Generate documentation")
56
+ .action(async () => {
57
+ const { setupServices } = await import("./services");
58
+ await setupServices();
59
+ await generate();
60
+ });
61
+ }
62
+
63
+ export function addEmbedCommands(program: Command): void {
64
+ program
65
+ .command("embed")
66
+ .description("Create embeddings")
67
+ .action(async () => {
68
+ const { setupServices } = await import("./services");
69
+ await setupServices();
70
+ await embed();
71
+ });
72
+
73
+ program
74
+ .command("embed:purge")
75
+ .description("Purge embeddings matching a glob pattern")
76
+ .argument("<pattern>", "Glob pattern to match files for purging")
77
+ .action(async (pattern) => {
78
+ await purge(pattern);
79
+ });
80
+ }
81
+
82
+ export function addUploadCommand(program: Command): void {
83
+ program
84
+ .command("upload")
85
+ .description("Upload data")
86
+ .action(async () => {
87
+ await upload();
88
+ });
89
+ }
90
+
91
+ export function addDownloadCommand(program: Command): void {
92
+ program
93
+ .command("download")
94
+ .description("Download data")
95
+ .action(async () => {
96
+ await download();
97
+ });
98
+ }
99
+
100
+ export function addChatCommand(program: Command): void {
101
+ program
102
+ .command("chat")
103
+ .description("Start new chat interface")
104
+ .action(async () => {
105
+ const { setupServices } = await import("./services");
106
+ await setupServices();
107
+ await startChat();
108
+ });
109
+ }
110
+
111
+ export function addGithubCredentialsCommand(program: Command): void {
112
+ program
113
+ .command("github-credentials [action]")
114
+ .description(
115
+ "Git credential helper for GitHub. Use as: git config credential.helper 'knowhow github-credentials'"
116
+ )
117
+ .option(
118
+ "--repo <repo>",
119
+ "Repository in owner/repo format (e.g. myorg/myrepo)"
120
+ )
121
+ .action(async (action: string | undefined, options: { repo?: string }) => {
122
+ // Silence ALL output immediately — git credential helpers must produce
123
+ // only the protocol=.../host=.../username=.../password=... lines on stdout.
124
+ logger.silence();
125
+
126
+ const client = new KnowhowSimpleClient();
127
+
128
+ let repo = options.repo;
129
+
130
+ if (action === "get") {
131
+ const lines: string[] = [];
132
+ const readline = await import("readline");
133
+ const rl = readline.createInterface({
134
+ input: process.stdin,
135
+ terminal: false,
136
+ });
137
+ await new Promise<void>((resolve) => {
138
+ rl.on("line", (line) => {
139
+ if (line.trim()) lines.push(line.trim());
140
+ });
141
+ rl.on("close", resolve);
142
+ });
143
+ } else if (action === "store" || action === "erase") {
144
+ process.exit(0);
145
+ }
146
+
147
+ if (!repo) {
148
+ try {
149
+ const remoteUrl = execSync("git remote get-url origin", {
150
+ encoding: "utf-8",
151
+ stdio: ["pipe", "pipe", "pipe"],
152
+ }).trim();
153
+ const match =
154
+ remoteUrl.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/) ||
155
+ remoteUrl.match(/github\.com\/([^/]+\/[^/]+)/);
156
+ if (match) {
157
+ repo = match[1];
158
+ }
159
+ } catch {
160
+ // Not in a git repo or no remote — proceed without repo
161
+ }
162
+ }
163
+
164
+ try {
165
+ const credential = await client.getGitCredential(repo || "");
166
+ process.stdout.write(
167
+ `protocol=${credential.protocol}\nhost=${credential.host}\nusername=${credential.username}\npassword=${credential.password}\n`
168
+ );
169
+ } catch (error) {
170
+ console.error("Failed to get git credentials:", error.message);
171
+ process.exit(1);
172
+ }
173
+ });
174
+ }
@@ -0,0 +1,182 @@
1
+ import { Command } from "commander";
2
+ import { execSync } from "child_process";
3
+ import { getConfig, getGlobalConfig, updateConfig, updateGlobalConfig } from "../config";
4
+
5
+ // Default built-in modules that `knowhow modules setup` adds to the config.
6
+ export const BUILTIN_MODULES = [
7
+ "@tyvm/knowhow-module-script",
8
+ "@tyvm/knowhow-module-terminal",
9
+ ];
10
+
11
+ export function addModulesCommand(program: Command): void {
12
+ const modulesCmd = program
13
+ .command("modules")
14
+ .description("Manage knowhow modules (install, add to config, list)");
15
+
16
+ modulesCmd
17
+ .command("setup")
18
+ .description(
19
+ "Add default built-in modules to your config and install them via npm"
20
+ )
21
+ .option("--global", "Use the global config (~/.knowhow/knowhow.json)")
22
+ .action(async (opts) => {
23
+ try {
24
+ const isGlobal: boolean = opts.global ?? false;
25
+ const cfg = isGlobal ? await getGlobalConfig() : await getConfig();
26
+ const configLabel = isGlobal
27
+ ? "~/.knowhow/knowhow.json"
28
+ : ".knowhow/knowhow.json";
29
+
30
+ if (!cfg.modules) cfg.modules = [];
31
+
32
+ const toAdd = BUILTIN_MODULES.filter(
33
+ (m) => !cfg.modules!.includes(m)
34
+ );
35
+
36
+ if (toAdd.length === 0) {
37
+ console.log(
38
+ `āœ… All default modules are already in ${configLabel}. Nothing to do.`
39
+ );
40
+ return;
41
+ }
42
+
43
+ // Install packages that are not local file paths
44
+ for (const mod of toAdd) {
45
+ if (!mod.startsWith(".") && !mod.startsWith("/")) {
46
+ console.log(`šŸ“¦ Installing ${mod}...`);
47
+ const installFlag = isGlobal ? "-g" : "";
48
+ execSync(`npm install ${installFlag} ${mod}`, {
49
+ stdio: "inherit",
50
+ encoding: "utf-8",
51
+ });
52
+ }
53
+ cfg.modules!.push(mod);
54
+ console.log(`āœ… Added ${mod} to ${configLabel}`);
55
+ }
56
+
57
+ if (isGlobal) {
58
+ await updateGlobalConfig(cfg);
59
+ } else {
60
+ await updateConfig(cfg);
61
+ }
62
+
63
+ console.log(
64
+ `\nšŸŽ‰ Setup complete! ${toAdd.length} module(s) added to ${configLabel}`
65
+ );
66
+ } catch (error) {
67
+ console.error("Error during modules setup:", error.message ?? error);
68
+ process.exit(1);
69
+ }
70
+ });
71
+
72
+ modulesCmd
73
+ .command("install [module]")
74
+ .description(
75
+ "Install a module via npm and add it to your config. " +
76
+ "If no module name is given, installs all modules already in the config."
77
+ )
78
+ .option("--global", "Use the global config (~/.knowhow/knowhow.json)")
79
+ .action(async (moduleName: string | undefined, opts) => {
80
+ try {
81
+ const isGlobal: boolean = opts.global ?? false;
82
+ const cfg = isGlobal ? await getGlobalConfig() : await getConfig();
83
+ const configLabel = isGlobal
84
+ ? "~/.knowhow/knowhow.json"
85
+ : ".knowhow/knowhow.json";
86
+
87
+ if (!cfg.modules) cfg.modules = [];
88
+
89
+ if (!moduleName) {
90
+ // No module specified — install everything already in the config
91
+ const installable = cfg.modules.filter(
92
+ (m) => !m.startsWith(".") && !m.startsWith("/")
93
+ );
94
+ if (installable.length === 0) {
95
+ console.log(
96
+ `ℹ No installable modules found in ${configLabel}.`
97
+ );
98
+ return;
99
+ }
100
+ console.log(
101
+ `šŸ“¦ Installing ${installable.length} module(s) from ${configLabel}...`
102
+ );
103
+ const installFlag = isGlobal ? "-g" : "";
104
+ for (const mod of installable) {
105
+ console.log(` šŸ“¦ Installing ${mod}...`);
106
+ execSync(`npm install ${installFlag} ${mod}`, {
107
+ stdio: "inherit",
108
+ encoding: "utf-8",
109
+ });
110
+ console.log(` āœ… Installed ${mod}`);
111
+ }
112
+ console.log(`\nšŸŽ‰ All modules installed!`);
113
+ return;
114
+ }
115
+
116
+ // Install the specified module
117
+ const installFlag = isGlobal ? "-g" : "";
118
+ console.log(`šŸ“¦ Installing ${moduleName}...`);
119
+ execSync(`npm install ${installFlag} ${moduleName}`, {
120
+ stdio: "inherit",
121
+ encoding: "utf-8",
122
+ });
123
+ console.log(`āœ… Installed ${moduleName}`);
124
+
125
+ // Add to config if not already there
126
+ if (!cfg.modules.includes(moduleName)) {
127
+ cfg.modules.push(moduleName);
128
+ if (isGlobal) {
129
+ await updateGlobalConfig(cfg);
130
+ } else {
131
+ await updateConfig(cfg);
132
+ }
133
+ console.log(`āœ… Added ${moduleName} to ${configLabel}`);
134
+ } else {
135
+ console.log(`ℹ ${moduleName} is already in ${configLabel}`);
136
+ }
137
+ } catch (error) {
138
+ console.error("Error during module install:", error.message ?? error);
139
+ process.exit(1);
140
+ }
141
+ });
142
+
143
+ modulesCmd
144
+ .command("list")
145
+ .description("List all modules in your config")
146
+ .option("--global", "Show global config modules only")
147
+ .action(async (opts) => {
148
+ try {
149
+ const isGlobal: boolean = opts.global ?? false;
150
+ const globalCfg = await getGlobalConfig();
151
+ const localCfg = isGlobal ? null : await getConfig();
152
+
153
+ const globalModules = globalCfg.modules || [];
154
+ const localModules = localCfg?.modules || [];
155
+
156
+ if (isGlobal) {
157
+ console.log(`\n🌐 Global modules (~/.knowhow/knowhow.json):`);
158
+ if (globalModules.length === 0) {
159
+ console.log(" (none)");
160
+ } else {
161
+ globalModules.forEach((m, i) => console.log(` ${i + 1}. ${m}`));
162
+ }
163
+ } else {
164
+ console.log(`\n🌐 Global modules (~/.knowhow/knowhow.json):`);
165
+ if (globalModules.length === 0) {
166
+ console.log(" (none)");
167
+ } else {
168
+ globalModules.forEach((m, i) => console.log(` ${i + 1}. ${m}`));
169
+ }
170
+ console.log(`\nšŸ“ Local modules (.knowhow/knowhow.json):`);
171
+ if (localModules.length === 0) {
172
+ console.log(" (none)");
173
+ } else {
174
+ localModules.forEach((m, i) => console.log(` ${i + 1}. ${m}`));
175
+ }
176
+ }
177
+ } catch (error) {
178
+ console.error("Error listing modules:", error.message ?? error);
179
+ process.exit(1);
180
+ }
181
+ });
182
+ }
@@ -0,0 +1,77 @@
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
+ Events,
19
+ MediaProcessor,
20
+ } = services();
21
+
22
+
23
+ // cli uses LazyTools to keep context slim
24
+ const Tools = new LazyToolsService();
25
+
26
+ Tools.setContext({
27
+ ...AllTools.getContext(),
28
+ });
29
+
30
+ const agentContext: import("../agents/base/base").AgentContext = {
31
+ ...services(),
32
+ Tools,
33
+ };
34
+
35
+ const { Researcher, Developer, Patcher, Setup } = agents({
36
+ ...agentContext,
37
+ });
38
+
39
+ Agents.registerAgent(Researcher);
40
+ Agents.registerAgent(Patcher);
41
+ Agents.registerAgent(Developer);
42
+ Agents.registerAgent(Setup);
43
+ Agents.loadAgentsFromConfig(agentContext);
44
+
45
+ Tools.defineTools(includedTools, allTools);
46
+
47
+ Tools.addContext("Mcp", Mcp);
48
+
49
+ Agents.setAgentContext(agentContext);
50
+
51
+ console.log("šŸ”Œ Connecting to MCP...");
52
+ try {
53
+ await Mcp.connectToConfigured(Tools);
54
+ } catch (mcpError) {
55
+ const msg = mcpError instanceof Error ? mcpError.message : String(mcpError);
56
+ console.warn(
57
+ `⚠ Some MCP servers failed to connect (continuing without them): ${msg}`
58
+ );
59
+ }
60
+ console.log("Connecting to clients...");
61
+ await Clients.registerConfiguredModels();
62
+ console.log("āœ“ Services are set up and ready to go!");
63
+
64
+ console.log("šŸ“¦ Loading modules from config...");
65
+ const modulesService = new ModulesService();
66
+ await modulesService.loadModulesFromConfig({
67
+ Agents,
68
+ Embeddings,
69
+ Plugins,
70
+ Clients,
71
+ Tools,
72
+ MediaProcessor,
73
+ Events
74
+ });
75
+
76
+ return { Tools, Clients };
77
+ }