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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/package.json +2 -3
  2. package/src/agents/base/base.ts +9 -0
  3. package/src/agents/tools/index.ts +0 -1
  4. package/src/agents/tools/list.ts +0 -2
  5. package/src/chat/CliChatService.ts +7 -1
  6. package/src/chat/renderer/CompactRenderer.ts +20 -0
  7. package/src/chat/renderer/ConsoleRenderer.ts +19 -0
  8. package/src/chat/renderer/FancyRenderer.ts +19 -0
  9. package/src/chat/renderer/types.ts +11 -0
  10. package/src/cli.ts +79 -666
  11. package/src/clients/types.ts +12 -4
  12. package/src/commands/agent.ts +246 -0
  13. package/src/commands/misc.ts +169 -0
  14. package/src/commands/modules.ts +182 -0
  15. package/src/commands/services.ts +72 -0
  16. package/src/commands/workers.ts +160 -0
  17. package/src/config.ts +37 -0
  18. package/src/index.ts +1 -0
  19. package/src/plugins/plugins.ts +0 -21
  20. package/src/processors/JsonCompressor.ts +6 -6
  21. package/src/services/modules/index.ts +58 -49
  22. package/src/services/modules/types.ts +2 -0
  23. package/src/tunnel.ts +216 -0
  24. package/src/types.ts +0 -1
  25. package/src/worker.ts +65 -336
  26. package/src/workers/auth/WsMiddleware.ts +99 -0
  27. package/src/workers/auth/authMiddleware.ts +104 -0
  28. package/src/workers/auth/types.ts +14 -2
  29. package/tests/unit/modules/moduleLoading.test.ts +0 -25
  30. package/tests/unit/plugins/pluginLoading.test.ts +0 -85
  31. package/ts_build/package.json +2 -3
  32. package/ts_build/src/agents/base/base.js +10 -0
  33. package/ts_build/src/agents/base/base.js.map +1 -1
  34. package/ts_build/src/agents/tools/index.d.ts +0 -1
  35. package/ts_build/src/agents/tools/index.js +0 -1
  36. package/ts_build/src/agents/tools/index.js.map +1 -1
  37. package/ts_build/src/agents/tools/list.js +0 -2
  38. package/ts_build/src/agents/tools/list.js.map +1 -1
  39. package/ts_build/src/chat/CliChatService.js +10 -1
  40. package/ts_build/src/chat/CliChatService.js.map +1 -1
  41. package/ts_build/src/chat/renderer/CompactRenderer.d.ts +4 -0
  42. package/ts_build/src/chat/renderer/CompactRenderer.js +16 -0
  43. package/ts_build/src/chat/renderer/CompactRenderer.js.map +1 -1
  44. package/ts_build/src/chat/renderer/ConsoleRenderer.d.ts +4 -0
  45. package/ts_build/src/chat/renderer/ConsoleRenderer.js +16 -0
  46. package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -1
  47. package/ts_build/src/chat/renderer/FancyRenderer.d.ts +4 -0
  48. package/ts_build/src/chat/renderer/FancyRenderer.js +16 -0
  49. package/ts_build/src/chat/renderer/FancyRenderer.js.map +1 -1
  50. package/ts_build/src/chat/renderer/types.d.ts +2 -0
  51. package/ts_build/src/cli.js +40 -525
  52. package/ts_build/src/cli.js.map +1 -1
  53. package/ts_build/src/clients/types.d.ts +2 -2
  54. package/ts_build/src/commands/agent.d.ts +6 -0
  55. package/ts_build/src/commands/agent.js +229 -0
  56. package/ts_build/src/commands/agent.js.map +1 -0
  57. package/ts_build/src/commands/misc.d.ts +10 -0
  58. package/ts_build/src/commands/misc.js +195 -0
  59. package/ts_build/src/commands/misc.js.map +1 -0
  60. package/ts_build/src/commands/modules.d.ts +3 -0
  61. package/ts_build/src/commands/modules.js +160 -0
  62. package/ts_build/src/commands/modules.js.map +1 -0
  63. package/ts_build/src/commands/services.d.ts +5 -0
  64. package/ts_build/src/commands/services.js +86 -0
  65. package/ts_build/src/commands/services.js.map +1 -0
  66. package/ts_build/src/commands/workers.d.ts +6 -0
  67. package/ts_build/src/commands/workers.js +163 -0
  68. package/ts_build/src/commands/workers.js.map +1 -0
  69. package/ts_build/src/config.d.ts +1 -0
  70. package/ts_build/src/config.js +32 -0
  71. package/ts_build/src/config.js.map +1 -1
  72. package/ts_build/src/index.d.ts +1 -0
  73. package/ts_build/src/index.js +3 -1
  74. package/ts_build/src/index.js.map +1 -1
  75. package/ts_build/src/plugins/plugins.d.ts +0 -2
  76. package/ts_build/src/plugins/plugins.js +0 -11
  77. package/ts_build/src/plugins/plugins.js.map +1 -1
  78. package/ts_build/src/processors/JsonCompressor.js +4 -4
  79. package/ts_build/src/processors/JsonCompressor.js.map +1 -1
  80. package/ts_build/src/services/modules/index.d.ts +33 -0
  81. package/ts_build/src/services/modules/index.js +38 -42
  82. package/ts_build/src/services/modules/index.js.map +1 -1
  83. package/ts_build/src/services/modules/types.d.ts +2 -0
  84. package/ts_build/src/tunnel.d.ts +27 -0
  85. package/ts_build/src/tunnel.js +112 -0
  86. package/ts_build/src/tunnel.js.map +1 -0
  87. package/ts_build/src/types.d.ts +0 -1
  88. package/ts_build/src/types.js.map +1 -1
  89. package/ts_build/src/worker.d.ts +1 -4
  90. package/ts_build/src/worker.js +38 -244
  91. package/ts_build/src/worker.js.map +1 -1
  92. package/ts_build/src/workers/auth/WsMiddleware.d.ts +8 -0
  93. package/ts_build/src/workers/auth/WsMiddleware.js +65 -0
  94. package/ts_build/src/workers/auth/WsMiddleware.js.map +1 -0
  95. package/ts_build/src/workers/auth/authMiddleware.d.ts +3 -0
  96. package/ts_build/src/workers/auth/authMiddleware.js +60 -0
  97. package/ts_build/src/workers/auth/authMiddleware.js.map +1 -0
  98. package/ts_build/src/workers/auth/types.d.ts +8 -1
  99. package/ts_build/tests/unit/modules/moduleLoading.test.js +0 -19
  100. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
  101. package/ts_build/tests/unit/plugins/pluginLoading.test.js +0 -65
  102. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  103. package/src/agents/tools/executeScript/README.md +0 -94
  104. package/src/agents/tools/executeScript/definition.ts +0 -79
  105. package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +0 -272
  106. package/src/agents/tools/executeScript/examples/quick-test.ts +0 -74
  107. package/src/agents/tools/executeScript/examples/serialization-test.ts +0 -321
  108. package/src/agents/tools/executeScript/examples/test-runner.ts +0 -197
  109. package/src/agents/tools/executeScript/index.ts +0 -98
  110. package/src/services/script-execution/SandboxContext.ts +0 -282
  111. package/src/services/script-execution/ScriptExecutor.ts +0 -441
  112. package/src/services/script-execution/ScriptPolicy.ts +0 -194
  113. package/src/services/script-execution/ScriptTracer.ts +0 -249
  114. package/src/services/script-execution/types.ts +0 -134
  115. package/ts_build/src/agents/tools/executeScript/definition.d.ts +0 -2
  116. package/ts_build/src/agents/tools/executeScript/definition.js +0 -76
  117. package/ts_build/src/agents/tools/executeScript/definition.js.map +0 -1
  118. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +0 -18
  119. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +0 -192
  120. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +0 -1
  121. package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +0 -3
  122. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +0 -64
  123. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +0 -1
  124. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +0 -15
  125. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +0 -266
  126. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +0 -1
  127. package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +0 -4
  128. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +0 -208
  129. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +0 -1
  130. package/ts_build/src/agents/tools/executeScript/index.d.ts +0 -28
  131. package/ts_build/src/agents/tools/executeScript/index.js +0 -72
  132. package/ts_build/src/agents/tools/executeScript/index.js.map +0 -1
  133. package/ts_build/src/services/script-execution/SandboxContext.d.ts +0 -34
  134. package/ts_build/src/services/script-execution/SandboxContext.js +0 -189
  135. package/ts_build/src/services/script-execution/SandboxContext.js.map +0 -1
  136. package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +0 -19
  137. package/ts_build/src/services/script-execution/ScriptExecutor.js +0 -269
  138. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +0 -1
  139. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +0 -28
  140. package/ts_build/src/services/script-execution/ScriptPolicy.js +0 -115
  141. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +0 -1
  142. package/ts_build/src/services/script-execution/ScriptTracer.d.ts +0 -19
  143. package/ts_build/src/services/script-execution/ScriptTracer.js +0 -186
  144. package/ts_build/src/services/script-execution/ScriptTracer.js.map +0 -1
  145. package/ts_build/src/services/script-execution/types.d.ts +0 -108
  146. package/ts_build/src/services/script-execution/types.js +0 -3
  147. package/ts_build/src/services/script-execution/types.js.map +0 -1
@@ -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,169 @@
1
+ import { Command } from "commander";
2
+ import { execSync } from "child_process";
3
+ import { version } from "../../package.json";
4
+ import { generate, embed, upload, download, purge } from "../index";
5
+ import { init } from "../config";
6
+ import { login } from "../login";
7
+ import { KnowhowSimpleClient } from "../services/KnowhowClient";
8
+ import { startChat } from "../chat";
9
+
10
+ export function addInitCommand(program: Command): void {
11
+ program
12
+ .command("init")
13
+ .description("Initialize knowhow configuration")
14
+ .action(async () => {
15
+ await init();
16
+ });
17
+ }
18
+
19
+ export function addLoginCommand(program: Command): void {
20
+ program
21
+ .command("login")
22
+ .description("Login to knowhow")
23
+ .option("--jwt", "Use manual JWT input instead of browser login")
24
+ .action(async (opts) => {
25
+ await login(opts.jwt);
26
+ });
27
+ }
28
+
29
+ export function addUpdateCommand(program: Command): void {
30
+ program
31
+ .command("update")
32
+ .description("Update knowhow to the latest version from npm")
33
+ .action(async () => {
34
+ try {
35
+ console.log("šŸ”„ Checking for knowhow updates...");
36
+ console.log(`Current version: ${version}`);
37
+ console.log("šŸ“¦ Installing latest version from npm...");
38
+ execSync("npm install -g @tyvm/knowhow@latest", {
39
+ stdio: "inherit",
40
+ encoding: "utf-8",
41
+ });
42
+ console.log("āœ“ knowhow has been updated successfully!");
43
+ console.log("Run 'knowhow --version' to see the new version.");
44
+ } catch (error) {
45
+ console.error("Error updating knowhow:", error.message);
46
+ process.exit(1);
47
+ }
48
+ });
49
+ }
50
+
51
+ export function addGenerateCommand(program: Command): void {
52
+ program
53
+ .command("generate")
54
+ .description("Generate documentation")
55
+ .action(async () => {
56
+ const { setupServices } = await import("./services");
57
+ await setupServices();
58
+ await generate();
59
+ });
60
+ }
61
+
62
+ export function addEmbedCommands(program: Command): void {
63
+ program
64
+ .command("embed")
65
+ .description("Create embeddings")
66
+ .action(async () => {
67
+ const { setupServices } = await import("./services");
68
+ await setupServices();
69
+ await embed();
70
+ });
71
+
72
+ program
73
+ .command("embed:purge")
74
+ .description("Purge embeddings matching a glob pattern")
75
+ .argument("<pattern>", "Glob pattern to match files for purging")
76
+ .action(async (pattern) => {
77
+ await purge(pattern);
78
+ });
79
+ }
80
+
81
+ export function addUploadCommand(program: Command): void {
82
+ program
83
+ .command("upload")
84
+ .description("Upload data")
85
+ .action(async () => {
86
+ await upload();
87
+ });
88
+ }
89
+
90
+ export function addDownloadCommand(program: Command): void {
91
+ program
92
+ .command("download")
93
+ .description("Download data")
94
+ .action(async () => {
95
+ await download();
96
+ });
97
+ }
98
+
99
+ export function addChatCommand(program: Command): void {
100
+ program
101
+ .command("chat")
102
+ .description("Start new chat interface")
103
+ .action(async () => {
104
+ const { setupServices } = await import("./services");
105
+ await setupServices();
106
+ await startChat();
107
+ });
108
+ }
109
+
110
+ export function addGithubCredentialsCommand(program: Command): void {
111
+ program
112
+ .command("github-credentials [action]")
113
+ .description(
114
+ "Git credential helper for GitHub. Use as: git config credential.helper 'knowhow github-credentials'"
115
+ )
116
+ .option(
117
+ "--repo <repo>",
118
+ "Repository in owner/repo format (e.g. myorg/myrepo)"
119
+ )
120
+ .action(async (action: string | undefined, options: { repo?: string }) => {
121
+ const client = new KnowhowSimpleClient();
122
+
123
+ let repo = options.repo;
124
+
125
+ if (action === "get") {
126
+ const lines: string[] = [];
127
+ const readline = await import("readline");
128
+ const rl = readline.createInterface({
129
+ input: process.stdin,
130
+ terminal: false,
131
+ });
132
+ await new Promise<void>((resolve) => {
133
+ rl.on("line", (line) => {
134
+ if (line.trim()) lines.push(line.trim());
135
+ });
136
+ rl.on("close", resolve);
137
+ });
138
+ } else if (action === "store" || action === "erase") {
139
+ process.exit(0);
140
+ }
141
+
142
+ if (!repo) {
143
+ try {
144
+ const remoteUrl = execSync("git remote get-url origin", {
145
+ encoding: "utf-8",
146
+ stdio: ["pipe", "pipe", "pipe"],
147
+ }).trim();
148
+ const match =
149
+ remoteUrl.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/) ||
150
+ remoteUrl.match(/github\.com\/([^/]+\/[^/]+)/);
151
+ if (match) {
152
+ repo = match[1];
153
+ }
154
+ } catch {
155
+ // Not in a git repo or no remote — proceed without repo
156
+ }
157
+ }
158
+
159
+ try {
160
+ const credential = await client.getGitCredential(repo || "");
161
+ process.stdout.write(
162
+ `protocol=${credential.protocol}\nhost=${credential.host}\nusername=${credential.username}\npassword=${credential.password}\n`
163
+ );
164
+ } catch (error) {
165
+ console.error("Failed to get git credentials:", error.message);
166
+ process.exit(1);
167
+ }
168
+ });
169
+ }
@@ -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
+ }