@tyvm/knowhow 0.0.108-dev.126b29e → 0.0.108-dev.80f1ac6

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 (172) 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 +10 -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 +91 -664
  11. package/src/clients/index.ts +6 -5
  12. package/src/clients/types.ts +12 -4
  13. package/src/commands/agent.ts +246 -0
  14. package/src/commands/misc.ts +174 -0
  15. package/src/commands/modules.ts +182 -0
  16. package/src/commands/services.ts +77 -0
  17. package/src/commands/workers.ts +160 -0
  18. package/src/config.ts +37 -0
  19. package/src/fileSync.ts +14 -2
  20. package/src/index.ts +1 -0
  21. package/src/logger.ts +197 -0
  22. package/src/plugins/plugins.ts +0 -21
  23. package/src/processors/JsonCompressor.ts +6 -6
  24. package/src/services/EventService.ts +61 -1
  25. package/src/services/KnowhowClient.ts +12 -2
  26. package/src/services/S3.ts +0 -10
  27. package/src/services/modules/index.ts +70 -50
  28. package/src/services/modules/types.ts +4 -0
  29. package/src/tunnel.ts +216 -0
  30. package/src/types.ts +0 -1
  31. package/src/worker.ts +65 -336
  32. package/src/workers/auth/WsMiddleware.ts +99 -0
  33. package/src/workers/auth/authMiddleware.ts +104 -0
  34. package/src/workers/auth/types.ts +14 -2
  35. package/tests/unit/commands/github-credentials.test.ts +211 -0
  36. package/tests/unit/modules/moduleLoading.test.ts +39 -37
  37. package/tests/unit/plugins/pluginLoading.test.ts +0 -85
  38. package/ts_build/package.json +2 -3
  39. package/ts_build/src/agents/base/base.js +10 -0
  40. package/ts_build/src/agents/base/base.js.map +1 -1
  41. package/ts_build/src/agents/tools/index.d.ts +0 -1
  42. package/ts_build/src/agents/tools/index.js +0 -1
  43. package/ts_build/src/agents/tools/index.js.map +1 -1
  44. package/ts_build/src/agents/tools/list.js +0 -2
  45. package/ts_build/src/agents/tools/list.js.map +1 -1
  46. package/ts_build/src/chat/CliChatService.js +13 -1
  47. package/ts_build/src/chat/CliChatService.js.map +1 -1
  48. package/ts_build/src/chat/renderer/CompactRenderer.d.ts +4 -0
  49. package/ts_build/src/chat/renderer/CompactRenderer.js +16 -0
  50. package/ts_build/src/chat/renderer/CompactRenderer.js.map +1 -1
  51. package/ts_build/src/chat/renderer/ConsoleRenderer.d.ts +4 -0
  52. package/ts_build/src/chat/renderer/ConsoleRenderer.js +16 -0
  53. package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -1
  54. package/ts_build/src/chat/renderer/FancyRenderer.d.ts +4 -0
  55. package/ts_build/src/chat/renderer/FancyRenderer.js +16 -0
  56. package/ts_build/src/chat/renderer/FancyRenderer.js.map +1 -1
  57. package/ts_build/src/chat/renderer/types.d.ts +2 -0
  58. package/ts_build/src/cli.js +47 -525
  59. package/ts_build/src/cli.js.map +1 -1
  60. package/ts_build/src/clients/index.js +2 -4
  61. package/ts_build/src/clients/index.js.map +1 -1
  62. package/ts_build/src/clients/types.d.ts +2 -2
  63. package/ts_build/src/commands/agent.d.ts +6 -0
  64. package/ts_build/src/commands/agent.js +229 -0
  65. package/ts_build/src/commands/agent.js.map +1 -0
  66. package/ts_build/src/commands/misc.d.ts +10 -0
  67. package/ts_build/src/commands/misc.js +197 -0
  68. package/ts_build/src/commands/misc.js.map +1 -0
  69. package/ts_build/src/commands/modules.d.ts +3 -0
  70. package/ts_build/src/commands/modules.js +160 -0
  71. package/ts_build/src/commands/modules.js.map +1 -0
  72. package/ts_build/src/commands/services.d.ts +5 -0
  73. package/ts_build/src/commands/services.js +87 -0
  74. package/ts_build/src/commands/services.js.map +1 -0
  75. package/ts_build/src/commands/workers.d.ts +6 -0
  76. package/ts_build/src/commands/workers.js +163 -0
  77. package/ts_build/src/commands/workers.js.map +1 -0
  78. package/ts_build/src/config.d.ts +1 -0
  79. package/ts_build/src/config.js +32 -0
  80. package/ts_build/src/config.js.map +1 -1
  81. package/ts_build/src/fileSync.js +10 -2
  82. package/ts_build/src/fileSync.js.map +1 -1
  83. package/ts_build/src/index.d.ts +1 -0
  84. package/ts_build/src/index.js +3 -1
  85. package/ts_build/src/index.js.map +1 -1
  86. package/ts_build/src/logger.d.ts +21 -0
  87. package/ts_build/src/logger.js +106 -0
  88. package/ts_build/src/logger.js.map +1 -0
  89. package/ts_build/src/plugins/plugins.d.ts +0 -2
  90. package/ts_build/src/plugins/plugins.js +0 -11
  91. package/ts_build/src/plugins/plugins.js.map +1 -1
  92. package/ts_build/src/processors/JsonCompressor.js +4 -4
  93. package/ts_build/src/processors/JsonCompressor.js.map +1 -1
  94. package/ts_build/src/services/EventService.d.ts +6 -1
  95. package/ts_build/src/services/EventService.js +29 -0
  96. package/ts_build/src/services/EventService.js.map +1 -1
  97. package/ts_build/src/services/KnowhowClient.d.ts +1 -1
  98. package/ts_build/src/services/KnowhowClient.js +8 -2
  99. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  100. package/ts_build/src/services/S3.js +0 -7
  101. package/ts_build/src/services/S3.js.map +1 -1
  102. package/ts_build/src/services/modules/index.d.ts +33 -0
  103. package/ts_build/src/services/modules/index.js +46 -45
  104. package/ts_build/src/services/modules/index.js.map +1 -1
  105. package/ts_build/src/services/modules/types.d.ts +4 -0
  106. package/ts_build/src/tunnel.d.ts +27 -0
  107. package/ts_build/src/tunnel.js +112 -0
  108. package/ts_build/src/tunnel.js.map +1 -0
  109. package/ts_build/src/types.d.ts +0 -1
  110. package/ts_build/src/types.js.map +1 -1
  111. package/ts_build/src/worker.d.ts +1 -4
  112. package/ts_build/src/worker.js +38 -244
  113. package/ts_build/src/worker.js.map +1 -1
  114. package/ts_build/src/workers/auth/WsMiddleware.d.ts +8 -0
  115. package/ts_build/src/workers/auth/WsMiddleware.js +65 -0
  116. package/ts_build/src/workers/auth/WsMiddleware.js.map +1 -0
  117. package/ts_build/src/workers/auth/authMiddleware.d.ts +3 -0
  118. package/ts_build/src/workers/auth/authMiddleware.js +60 -0
  119. package/ts_build/src/workers/auth/authMiddleware.js.map +1 -0
  120. package/ts_build/src/workers/auth/types.d.ts +8 -1
  121. package/ts_build/tests/unit/commands/github-credentials.test.d.ts +1 -0
  122. package/ts_build/tests/unit/commands/github-credentials.test.js +146 -0
  123. package/ts_build/tests/unit/commands/github-credentials.test.js.map +1 -0
  124. package/ts_build/tests/unit/modules/moduleLoading.test.js +20 -26
  125. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
  126. package/ts_build/tests/unit/plugins/pluginLoading.test.js +0 -65
  127. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  128. package/src/agents/tools/executeScript/README.md +0 -94
  129. package/src/agents/tools/executeScript/definition.ts +0 -79
  130. package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +0 -272
  131. package/src/agents/tools/executeScript/examples/quick-test.ts +0 -74
  132. package/src/agents/tools/executeScript/examples/serialization-test.ts +0 -321
  133. package/src/agents/tools/executeScript/examples/test-runner.ts +0 -197
  134. package/src/agents/tools/executeScript/index.ts +0 -98
  135. package/src/services/script-execution/SandboxContext.ts +0 -282
  136. package/src/services/script-execution/ScriptExecutor.ts +0 -441
  137. package/src/services/script-execution/ScriptPolicy.ts +0 -194
  138. package/src/services/script-execution/ScriptTracer.ts +0 -249
  139. package/src/services/script-execution/types.ts +0 -134
  140. package/ts_build/src/agents/tools/executeScript/definition.d.ts +0 -2
  141. package/ts_build/src/agents/tools/executeScript/definition.js +0 -76
  142. package/ts_build/src/agents/tools/executeScript/definition.js.map +0 -1
  143. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +0 -18
  144. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +0 -192
  145. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +0 -1
  146. package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +0 -3
  147. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +0 -64
  148. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +0 -1
  149. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +0 -15
  150. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +0 -266
  151. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +0 -1
  152. package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +0 -4
  153. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +0 -208
  154. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +0 -1
  155. package/ts_build/src/agents/tools/executeScript/index.d.ts +0 -28
  156. package/ts_build/src/agents/tools/executeScript/index.js +0 -72
  157. package/ts_build/src/agents/tools/executeScript/index.js.map +0 -1
  158. package/ts_build/src/services/script-execution/SandboxContext.d.ts +0 -34
  159. package/ts_build/src/services/script-execution/SandboxContext.js +0 -189
  160. package/ts_build/src/services/script-execution/SandboxContext.js.map +0 -1
  161. package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +0 -19
  162. package/ts_build/src/services/script-execution/ScriptExecutor.js +0 -269
  163. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +0 -1
  164. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +0 -28
  165. package/ts_build/src/services/script-execution/ScriptPolicy.js +0 -115
  166. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +0 -1
  167. package/ts_build/src/services/script-execution/ScriptTracer.d.ts +0 -19
  168. package/ts_build/src/services/script-execution/ScriptTracer.js +0 -186
  169. package/ts_build/src/services/script-execution/ScriptTracer.js.map +0 -1
  170. package/ts_build/src/services/script-execution/types.d.ts +0 -108
  171. package/ts_build/src/services/script-execution/types.js +0 -3
  172. 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] : "";
@@ -1,4 +1,10 @@
1
- export type ModelModality = "completion" | "embedding" | "image" | "audio" | "video" | "transcription";
1
+ export type ModelModality =
2
+ | "completion"
3
+ | "embedding"
4
+ | "image"
5
+ | "audio"
6
+ | "video"
7
+ | "transcription";
2
8
 
3
9
  export type MessageContent =
4
10
  | { type: "text"; text: string }
@@ -8,7 +14,7 @@ export type MessageContent =
8
14
 
9
15
  export interface Message {
10
16
  role: "system" | "user" | "assistant" | "tool";
11
- content?: string | MessageContent[];
17
+ content?: string | MessageContent[] | null;
12
18
 
13
19
  name?: string;
14
20
  tool_call_id?: string;
@@ -16,7 +22,7 @@ export interface Message {
16
22
  }
17
23
 
18
24
  export interface OutputMessage extends Message {
19
- content: string;
25
+ content?: string | null;
20
26
  }
21
27
 
22
28
  export interface ToolProp {
@@ -301,7 +307,9 @@ export interface GenericClient {
301
307
  * When modality is provided, return only models for that modality (static list).
302
308
  * When omitted, return ALL models (backward compat — may do a live API call).
303
309
  */
304
- getModels(modality?: ModelModality): Promise<{ id: string; modality?: ModelModality[] }[]>;
310
+ getModels(
311
+ modality?: ModelModality
312
+ ): Promise<{ id: string; modality?: ModelModality[] }[]>;
305
313
  /**
306
314
  * Returns the context window limit and compression threshold for a given model,
307
315
  * or undefined if the model is not known to this client.
@@ -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
+ }