autohand-cli 0.6.1 → 0.6.2

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 (136) hide show
  1. package/dist/agents-32JB7KMB.js +9 -0
  2. package/dist/agents-3K6PHXQ6.js +9 -0
  3. package/dist/agents-A7AUMVQD.js +8 -0
  4. package/dist/agents-AHFECO5Q.js +9 -0
  5. package/dist/agents-DQFYBMJG.js +8 -0
  6. package/dist/agents-GX6L7BXZ.js +9 -0
  7. package/dist/agents-IWJJ7YJM.js +8 -0
  8. package/dist/agents-J2QLDVOP.js +9 -0
  9. package/dist/agents-OWTSY74S.js +9 -0
  10. package/dist/agents-RB34F4XE.js +9 -0
  11. package/dist/agents-XL27P67O.js +9 -0
  12. package/dist/agents-new-5I3B2W2I.js +9 -0
  13. package/dist/agents-new-CHV2AV34.js +9 -0
  14. package/dist/agents-new-F6F2VMDB.js +9 -0
  15. package/dist/agents-new-FDMUH7NL.js +9 -0
  16. package/dist/agents-new-GBF6JJIA.js +9 -0
  17. package/dist/agents-new-GCZEY4TE.js +8 -0
  18. package/dist/agents-new-OCIB72NU.js +9 -0
  19. package/dist/agents-new-PQGD47BZ.js +9 -0
  20. package/dist/agents-new-V6BNVJAB.js +9 -0
  21. package/dist/agents-new-WQUEZ2XH.js +8 -0
  22. package/dist/chunk-2DEJU7WQ.js +373 -0
  23. package/dist/chunk-2EPIFDFM.js +68 -0
  24. package/dist/chunk-2FUWKZFN.js +68 -0
  25. package/dist/chunk-2HIILNYH.js +197 -0
  26. package/dist/chunk-2QAL3HH4.js +79 -0
  27. package/dist/chunk-3FRM7HJY.js +57 -0
  28. package/dist/chunk-4LDR3Y3A.js +79 -0
  29. package/dist/{chunk-PDF2U23T.js → chunk-4LLTISFP.js} +1 -1
  30. package/dist/chunk-4UISIRMD.js +288 -0
  31. package/dist/chunk-4VWPX2X3.js +131 -0
  32. package/dist/chunk-7FMMKTRG.js +64 -0
  33. package/dist/chunk-7MFSCH7E.js +382 -0
  34. package/dist/chunk-7TV5KURP.js +79 -0
  35. package/dist/chunk-7WBK33MM.js +57 -0
  36. package/dist/chunk-7XN6PAKV.js +79 -0
  37. package/dist/chunk-A7HRTONQ.js +382 -0
  38. package/dist/chunk-ALMJANSA.js +197 -0
  39. package/dist/chunk-CD7GNBIE.js +288 -0
  40. package/dist/chunk-CV3LEQRD.js +57 -0
  41. package/dist/chunk-DAHMHLM6.js +102 -0
  42. package/dist/chunk-DAMLAWGE.js +68 -0
  43. package/dist/chunk-F4YPGOQJ.js +105 -0
  44. package/dist/chunk-FQDXFNOI.js +57 -0
  45. package/dist/chunk-FQI7EJY2.js +105 -0
  46. package/dist/chunk-GAJCZDZ5.js +286 -0
  47. package/dist/chunk-GEOP77H3.js +79 -0
  48. package/dist/chunk-GRX3IQHC.js +131 -0
  49. package/dist/chunk-GX24PC3L.js +288 -0
  50. package/dist/chunk-H53NQAC2.js +131 -0
  51. package/dist/chunk-HJYISR7Y.js +382 -0
  52. package/dist/chunk-HLSI4HQM.js +105 -0
  53. package/dist/chunk-IAOMCEYU.js +68 -0
  54. package/dist/chunk-J7RENRJG.js +382 -0
  55. package/dist/chunk-JUDX6E53.js +105 -0
  56. package/dist/chunk-JZQKOM7X.js +382 -0
  57. package/dist/chunk-K75NWR5V.js +108 -0
  58. package/dist/chunk-KCMWJB53.js +288 -0
  59. package/dist/chunk-KERHTHMK.js +302 -0
  60. package/dist/chunk-KJ4M6KAK.js +57 -0
  61. package/dist/chunk-L3JXU574.js +77 -0
  62. package/dist/chunk-LR2XPUPT.js +236 -0
  63. package/dist/chunk-MCWNGAZG.js +198 -0
  64. package/dist/chunk-MQTBFYEG.js +288 -0
  65. package/dist/chunk-NCUJWSGP.js +287 -0
  66. package/dist/chunk-NWEUBPSG.js +79 -0
  67. package/dist/chunk-OJ5EBWOQ.js +232 -0
  68. package/dist/chunk-PKQZWNS2.js +131 -0
  69. package/dist/chunk-PQJIQBQ5.js +57 -0
  70. package/dist/chunk-PX5AGAEX.js +105 -0
  71. package/dist/chunk-Q7HX4VZD.js +197 -0
  72. package/dist/chunk-QE5RKNVN.js +30 -0
  73. package/dist/chunk-QLVXFPE3.js +145 -0
  74. package/dist/chunk-QUC577LO.js +79 -0
  75. package/dist/chunk-RAL2W5UX.js +55 -0
  76. package/dist/chunk-RANNBYFJ.js +105 -0
  77. package/dist/chunk-RCSSFUGL.js +131 -0
  78. package/dist/chunk-RUQVXU6K.js +131 -0
  79. package/dist/chunk-RZ7TASUI.js +57 -0
  80. package/dist/chunk-SN7D2PJO.js +68 -0
  81. package/dist/chunk-T4WQUJAE.js +79 -0
  82. package/dist/chunk-U2Z5BABG.js +57 -0
  83. package/dist/chunk-UHC4DIK5.js +105 -0
  84. package/dist/chunk-UNS4S6J3.js +197 -0
  85. package/dist/chunk-UW2LYWIM.js +131 -0
  86. package/dist/chunk-VRCQBFSX.js +102 -0
  87. package/dist/chunk-W76N6IZV.js +197 -0
  88. package/dist/chunk-WTEZYXD2.js +67 -0
  89. package/dist/chunk-X7EWON4T.js +105 -0
  90. package/dist/chunk-XARAKKJ4.js +197 -0
  91. package/dist/chunk-Y6JDGDEE.js +197 -0
  92. package/dist/chunk-Y7FSNXMR.js +382 -0
  93. package/dist/chunk-YPZMUIB5.js +50 -0
  94. package/dist/chunk-ZMDUVLR4.js +148 -0
  95. package/dist/chunk-ZRFAICDG.js +382 -0
  96. package/dist/export-HEFUNSR4.js +8 -0
  97. package/dist/feedback-3U2WYQK6.js +9 -0
  98. package/dist/feedback-5IIX372K.js +9 -0
  99. package/dist/feedback-6S6DWGIU.js +9 -0
  100. package/dist/feedback-7DEOY2AP.js +9 -0
  101. package/dist/feedback-BBQT42R6.js +9 -0
  102. package/dist/feedback-CGCSAWQT.js +9 -0
  103. package/dist/feedback-NEDFOKMA.js +9 -0
  104. package/dist/feedback-OXGGJVNA.js +9 -0
  105. package/dist/feedback-ZJECE2FS.js +8 -0
  106. package/dist/help-NQUZ7TNJ.js +10 -0
  107. package/dist/index.cjs +16 -2
  108. package/dist/index.js +2 -2
  109. package/dist/login-D53NQ7UY.js +10 -0
  110. package/dist/login-GPXDNB2F.js +10 -0
  111. package/dist/login-HDF4GSTP.js +10 -0
  112. package/dist/login-SDAZTJAK.js +10 -0
  113. package/dist/login-SM6LEDDA.js +10 -0
  114. package/dist/login-TDI7HBRZ.js +10 -0
  115. package/dist/login-Y7XXSNOZ.js +10 -0
  116. package/dist/logout-32RNT7G2.js +10 -0
  117. package/dist/logout-43W7N6JU.js +10 -0
  118. package/dist/logout-BMHTSXIY.js +10 -0
  119. package/dist/logout-JNNJJYYL.js +10 -0
  120. package/dist/logout-LW42QASH.js +10 -0
  121. package/dist/logout-QLWM6P26.js +10 -0
  122. package/dist/logout-TL7GLGWU.js +10 -0
  123. package/dist/memory-GVYG653L.js +8 -0
  124. package/dist/new-MCN36AOD.js +8 -0
  125. package/dist/{status-5UF37IH2.js → status-DCVSUWZG.js} +1 -1
  126. package/dist/status-F6TQOCON.js +8 -0
  127. package/dist/status-IT5CYW37.js +8 -0
  128. package/dist/status-MRJOSVE3.js +8 -0
  129. package/dist/status-MWFV2DZG.js +8 -0
  130. package/dist/status-N3PMJRSB.js +8 -0
  131. package/dist/status-NK7YSBXZ.js +8 -0
  132. package/dist/status-QLQ5ZKJ3.js +8 -0
  133. package/dist/status-SQEFMII5.js +8 -0
  134. package/dist/status-U33PEUBO.js +8 -0
  135. package/dist/status-XAJH67SE.js +8 -0
  136. package/package.json +16 -2
@@ -0,0 +1,382 @@
1
+ import {
2
+ AUTH_CONFIG,
3
+ AUTOHAND_FILES
4
+ } from "./chunk-7FMMKTRG.js";
5
+
6
+ // src/config.ts
7
+ import fs from "fs-extra";
8
+ import path from "path";
9
+ import YAML from "yaml";
10
+ var DEFAULT_CONFIG_PATH = AUTOHAND_FILES.configJson;
11
+ var YAML_CONFIG_PATH = AUTOHAND_FILES.configYaml;
12
+ var YML_CONFIG_PATH = AUTOHAND_FILES.configYml;
13
+ var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
14
+ var DEFAULT_OLLAMA_URL = "http://localhost:11434";
15
+ var DEFAULT_LLAMACPP_URL = "http://localhost:8080";
16
+ var DEFAULT_OPENAI_URL = "https://api.openai.com/v1";
17
+ async function detectConfigPath(customPath) {
18
+ if (customPath) {
19
+ return path.resolve(customPath);
20
+ }
21
+ const envPath = process.env.AUTOHAND_CONFIG;
22
+ if (envPath) {
23
+ return path.resolve(envPath);
24
+ }
25
+ if (await fs.pathExists(YAML_CONFIG_PATH)) {
26
+ return YAML_CONFIG_PATH;
27
+ }
28
+ if (await fs.pathExists(YML_CONFIG_PATH)) {
29
+ return YML_CONFIG_PATH;
30
+ }
31
+ return DEFAULT_CONFIG_PATH;
32
+ }
33
+ function isYamlFile(filePath) {
34
+ const ext = path.extname(filePath).toLowerCase();
35
+ return ext === ".yaml" || ext === ".yml";
36
+ }
37
+ async function parseConfigFile(configPath) {
38
+ const content = await fs.readFile(configPath, "utf8");
39
+ if (isYamlFile(configPath)) {
40
+ return YAML.parse(content);
41
+ }
42
+ return JSON.parse(content);
43
+ }
44
+ async function loadConfig(customPath) {
45
+ const configPath = await detectConfigPath(customPath);
46
+ await fs.ensureDir(path.dirname(configPath));
47
+ if (!await fs.pathExists(configPath)) {
48
+ const defaultConfig = {
49
+ provider: "openrouter",
50
+ openrouter: {
51
+ apiKey: "replace-me",
52
+ baseUrl: "https://openrouter.ai/api/v1",
53
+ model: "anthropic/claude-3.5-sonnet"
54
+ },
55
+ workspace: {
56
+ defaultRoot: process.cwd(),
57
+ allowDangerousOps: false
58
+ },
59
+ ui: {
60
+ theme: "dark",
61
+ autoConfirm: false
62
+ }
63
+ };
64
+ await fs.writeJson(configPath, defaultConfig, { spaces: 2 });
65
+ throw new Error(
66
+ `Created default config at ${configPath}. Please update it with your OpenRouter credentials before rerunning.`
67
+ );
68
+ }
69
+ let parsed;
70
+ try {
71
+ parsed = await parseConfigFile(configPath);
72
+ } catch (error) {
73
+ throw new Error(`Failed to parse config at ${configPath}: ${error.message}`);
74
+ }
75
+ const normalized = normalizeConfig(parsed);
76
+ const withEnv = mergeEnvVariables(normalized);
77
+ validateConfig(withEnv, configPath);
78
+ return { ...withEnv, configPath };
79
+ }
80
+ function mergeEnvVariables(config) {
81
+ return {
82
+ ...config,
83
+ api: {
84
+ baseUrl: process.env.AUTOHAND_API_URL || config.api?.baseUrl || "https://api.autohand.ai",
85
+ companySecret: process.env.AUTOHAND_SECRET || config.api?.companySecret || ""
86
+ }
87
+ };
88
+ }
89
+ function normalizeConfig(config) {
90
+ if (isModernConfig(config)) {
91
+ const provider = config.provider ?? "openrouter";
92
+ return { provider, ...config };
93
+ }
94
+ if (isLegacyConfig(config)) {
95
+ return {
96
+ provider: "openrouter",
97
+ openrouter: {
98
+ apiKey: config.api_key ?? "replace-me",
99
+ baseUrl: config.base_url ?? DEFAULT_BASE_URL,
100
+ model: config.model ?? "anthropic/claude-3.5-sonnet"
101
+ },
102
+ workspace: {
103
+ defaultRoot: process.cwd(),
104
+ allowDangerousOps: false
105
+ },
106
+ ui: {
107
+ autoConfirm: config.dry_run ?? false,
108
+ theme: "dark"
109
+ }
110
+ };
111
+ }
112
+ return config;
113
+ }
114
+ function isModernConfig(config) {
115
+ return typeof config.openrouter === "object" || typeof config.ollama === "object" || typeof config.llamacpp === "object" || typeof config.openai === "object";
116
+ }
117
+ function isLegacyConfig(config) {
118
+ return typeof config.api_key === "string";
119
+ }
120
+ function validateConfig(config, configPath) {
121
+ const provider = config.provider ?? "openrouter";
122
+ const providerConfig = getProviderConfig(config, provider);
123
+ if (config.workspace) {
124
+ if (config.workspace.defaultRoot && typeof config.workspace.defaultRoot !== "string") {
125
+ throw new Error(`workspace.defaultRoot must be a string in ${configPath}`);
126
+ }
127
+ if (config.workspace.allowDangerousOps !== void 0 && typeof config.workspace.allowDangerousOps !== "boolean") {
128
+ throw new Error(`workspace.allowDangerousOps must be boolean in ${configPath}`);
129
+ }
130
+ }
131
+ if (config.ui) {
132
+ if (config.ui.theme && config.ui.theme !== "dark" && config.ui.theme !== "light") {
133
+ throw new Error(`ui.theme must be 'dark' or 'light' in ${configPath}`);
134
+ }
135
+ if (config.ui.autoConfirm !== void 0 && typeof config.ui.autoConfirm !== "boolean") {
136
+ throw new Error(`ui.autoConfirm must be boolean in ${configPath}`);
137
+ }
138
+ }
139
+ if (config.externalAgents) {
140
+ if (config.externalAgents.enabled !== void 0 && typeof config.externalAgents.enabled !== "boolean") {
141
+ throw new Error(`externalAgents.enabled must be boolean in ${configPath}`);
142
+ }
143
+ if (config.externalAgents.paths !== void 0) {
144
+ if (!Array.isArray(config.externalAgents.paths)) {
145
+ throw new Error(`externalAgents.paths must be an array in ${configPath}`);
146
+ }
147
+ for (const p of config.externalAgents.paths) {
148
+ if (typeof p !== "string") {
149
+ throw new Error(`externalAgents.paths must contain only strings in ${configPath}`);
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+ function resolveWorkspaceRoot(config, requestedPath) {
156
+ const candidate = requestedPath ?? process.cwd() ?? config.workspace?.defaultRoot;
157
+ return path.resolve(candidate);
158
+ }
159
+ function getProviderConfig(config, provider) {
160
+ const chosen = provider ?? config.provider ?? "openrouter";
161
+ const configByProvider = {
162
+ openrouter: config.openrouter,
163
+ ollama: config.ollama,
164
+ llamacpp: config.llamacpp,
165
+ openai: config.openai
166
+ };
167
+ const entry = configByProvider[chosen];
168
+ if (!entry) {
169
+ return null;
170
+ }
171
+ if (chosen === "openrouter") {
172
+ const { apiKey, model } = entry;
173
+ if (!apiKey || apiKey === "replace-me" || !model) {
174
+ return null;
175
+ }
176
+ } else {
177
+ if (!entry.model) {
178
+ return null;
179
+ }
180
+ }
181
+ return {
182
+ ...entry,
183
+ baseUrl: entry.baseUrl ?? defaultBaseUrlFor(chosen, entry.port)
184
+ };
185
+ }
186
+ function defaultBaseUrlFor(provider, port) {
187
+ if (provider === "openrouter") return DEFAULT_BASE_URL;
188
+ const p = port ? port.toString() : void 0;
189
+ switch (provider) {
190
+ case "ollama":
191
+ return p ? `http://localhost:${p}` : DEFAULT_OLLAMA_URL;
192
+ case "llamacpp":
193
+ return p ? `http://localhost:${p}` : DEFAULT_LLAMACPP_URL;
194
+ case "openai":
195
+ return DEFAULT_OPENAI_URL;
196
+ default:
197
+ return void 0;
198
+ }
199
+ }
200
+ async function saveConfig(config) {
201
+ const { configPath, ...data } = config;
202
+ if (isYamlFile(configPath)) {
203
+ const yamlContent = YAML.stringify(data, { indent: 2 });
204
+ await fs.writeFile(configPath, yamlContent, "utf8");
205
+ } else {
206
+ await fs.writeJson(configPath, data, { spaces: 2 });
207
+ }
208
+ }
209
+
210
+ // src/auth/AuthClient.ts
211
+ var DEFAULT_TIMEOUT = 1e4;
212
+ var AuthClient = class {
213
+ constructor(config = {}) {
214
+ this.baseUrl = config.baseUrl || AUTH_CONFIG.apiBaseUrl;
215
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
216
+ }
217
+ /**
218
+ * Initiate device authorization flow
219
+ * Returns device code and user code for display
220
+ */
221
+ async initiateDeviceAuth() {
222
+ const controller = new AbortController();
223
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
224
+ try {
225
+ const response = await fetch(`${this.baseUrl}/cli/initiate`, {
226
+ method: "POST",
227
+ headers: {
228
+ "Content-Type": "application/json"
229
+ },
230
+ body: JSON.stringify({ clientId: "autohand-cli" }),
231
+ signal: controller.signal
232
+ });
233
+ clearTimeout(timeoutId);
234
+ const data = await response.json();
235
+ if (!response.ok) {
236
+ return {
237
+ success: false,
238
+ error: data.error || data.message || `HTTP ${response.status}`
239
+ };
240
+ }
241
+ return {
242
+ success: true,
243
+ deviceCode: data.deviceCode,
244
+ userCode: data.userCode,
245
+ verificationUri: data.verificationUri,
246
+ verificationUriComplete: data.verificationUriComplete,
247
+ expiresIn: data.expiresIn,
248
+ interval: data.interval
249
+ };
250
+ } catch (error) {
251
+ clearTimeout(timeoutId);
252
+ if (error.name === "AbortError") {
253
+ return { success: false, error: "Request timeout" };
254
+ }
255
+ return { success: false, error: error.message };
256
+ }
257
+ }
258
+ /**
259
+ * Poll for device authorization status
260
+ */
261
+ async pollDeviceAuth(deviceCode) {
262
+ const controller = new AbortController();
263
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
264
+ try {
265
+ const response = await fetch(`${this.baseUrl}/cli/poll`, {
266
+ method: "POST",
267
+ headers: {
268
+ "Content-Type": "application/json"
269
+ },
270
+ body: JSON.stringify({ deviceCode }),
271
+ signal: controller.signal
272
+ });
273
+ clearTimeout(timeoutId);
274
+ const data = await response.json();
275
+ if (!response.ok && response.status !== 404) {
276
+ return {
277
+ success: false,
278
+ status: "pending",
279
+ error: data.error || data.message || `HTTP ${response.status}`
280
+ };
281
+ }
282
+ return {
283
+ success: data.success !== false,
284
+ status: data.status || "pending",
285
+ token: data.token,
286
+ user: data.user,
287
+ error: data.error
288
+ };
289
+ } catch (error) {
290
+ clearTimeout(timeoutId);
291
+ if (error.name === "AbortError") {
292
+ return { success: false, status: "pending", error: "Request timeout" };
293
+ }
294
+ return { success: false, status: "pending", error: error.message };
295
+ }
296
+ }
297
+ /**
298
+ * Validate current session token
299
+ */
300
+ async validateSession(token) {
301
+ const controller = new AbortController();
302
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
303
+ try {
304
+ const response = await fetch(`${this.baseUrl}/me`, {
305
+ method: "GET",
306
+ headers: {
307
+ "Authorization": `Bearer ${token}`,
308
+ "Cookie": `auth_session=${token}`
309
+ },
310
+ signal: controller.signal
311
+ });
312
+ clearTimeout(timeoutId);
313
+ if (!response.ok) {
314
+ return { authenticated: false };
315
+ }
316
+ const data = await response.json();
317
+ return {
318
+ authenticated: true,
319
+ user: data.user || data
320
+ };
321
+ } catch {
322
+ clearTimeout(timeoutId);
323
+ return { authenticated: false };
324
+ }
325
+ }
326
+ /**
327
+ * Logout and invalidate session
328
+ */
329
+ async logout(token) {
330
+ const controller = new AbortController();
331
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
332
+ try {
333
+ const response = await fetch(`${this.baseUrl}/logout`, {
334
+ method: "POST",
335
+ headers: {
336
+ "Authorization": `Bearer ${token}`,
337
+ "Cookie": `auth_session=${token}`
338
+ },
339
+ signal: controller.signal
340
+ });
341
+ clearTimeout(timeoutId);
342
+ return { success: response.ok };
343
+ } catch {
344
+ clearTimeout(timeoutId);
345
+ return { success: true };
346
+ }
347
+ }
348
+ };
349
+ var instance = null;
350
+ function getAuthClient(config) {
351
+ if (!instance) {
352
+ instance = new AuthClient(config);
353
+ }
354
+ return instance;
355
+ }
356
+
357
+ export {
358
+ loadConfig,
359
+ resolveWorkspaceRoot,
360
+ getProviderConfig,
361
+ saveConfig,
362
+ getAuthClient
363
+ };
364
+ /**
365
+ * @license
366
+ * Copyright 2025 Autohand AI LLC
367
+ * SPDX-License-Identifier: Apache-2.0
368
+ */
369
+ /**
370
+ * @license
371
+ * Copyright 2025 Autohand AI LLC
372
+ * SPDX-License-Identifier: Apache-2.0
373
+ *
374
+ * Auth API Client for CLI authentication
375
+ */
376
+ /**
377
+ * @license
378
+ * Copyright 2025 Autohand AI LLC
379
+ * SPDX-License-Identifier: Apache-2.0
380
+ *
381
+ * Auth module exports
382
+ */
@@ -0,0 +1,50 @@
1
+ // src/commands/help.ts
2
+ import chalk from "chalk";
3
+ import terminalLink from "terminal-link";
4
+ async function help() {
5
+ console.log(chalk.cyan("\n\u{1F4DA} Available Commands:\n"));
6
+ const commands = [
7
+ { cmd: "/quit", desc: "Exit Autohand" },
8
+ { cmd: "/model", desc: "Choose AI model" },
9
+ { cmd: "/session", desc: "Show current session info" },
10
+ { cmd: "/sessions", desc: "List sessions" },
11
+ { cmd: "/resume", desc: "Resume a session by id" },
12
+ { cmd: "/init", desc: "Create AGENTS.md file" },
13
+ { cmd: "/agents", desc: "List available sub-agents" },
14
+ { cmd: "/feedback", desc: "Send feedback with env details" },
15
+ { cmd: "/help / ?", desc: "Show this help" }
16
+ ];
17
+ commands.forEach(({ cmd, desc }) => {
18
+ console.log(` ${chalk.yellow(cmd.padEnd(14))} ${chalk.gray(desc)}`);
19
+ });
20
+ console.log(chalk.cyan("\n\u{1F4A1} Tips:\n"));
21
+ console.log(chalk.gray(" \u2022 Type @ to mention files for the AI"));
22
+ console.log(chalk.gray(" \u2022 Use arrow keys to navigate file suggestions"));
23
+ console.log(chalk.gray(" \u2022 Press Tab to autocomplete file paths"));
24
+ console.log(chalk.gray(" \u2022 Press Esc to cancel current operation\n"));
25
+ const docLink = terminalLink("docs.autohand.ai", "https://docs.autohand.ai");
26
+ console.log(chalk.gray(`For more information, visit ${docLink}
27
+ `));
28
+ return null;
29
+ }
30
+ var metadata = {
31
+ command: "/help",
32
+ description: "describe available slash commands and tips",
33
+ implemented: true
34
+ };
35
+ var aliasMetadata = {
36
+ command: "/?",
37
+ description: "alias for /help",
38
+ implemented: true
39
+ };
40
+
41
+ export {
42
+ help,
43
+ metadata,
44
+ aliasMetadata
45
+ };
46
+ /**
47
+ * @license
48
+ * Copyright 2025 Autohand AI LLC
49
+ * SPDX-License-Identifier: Apache-2.0
50
+ */
@@ -0,0 +1,148 @@
1
+ import {
2
+ AUTOHAND_PATHS
3
+ } from "./chunk-RAL2W5UX.js";
4
+
5
+ // src/commands/agents.ts
6
+ import chalk from "chalk";
7
+
8
+ // src/core/agents/AgentRegistry.ts
9
+ import fs from "fs/promises";
10
+ import path from "path";
11
+ import { z } from "zod";
12
+ var AgentConfigSchema = z.object({
13
+ description: z.string(),
14
+ systemPrompt: z.string(),
15
+ tools: z.array(z.string()),
16
+ model: z.string().optional()
17
+ });
18
+ var AgentRegistry = class _AgentRegistry {
19
+ constructor() {
20
+ this.agents = /* @__PURE__ */ new Map();
21
+ this.agentsDir = AUTOHAND_PATHS.agents;
22
+ }
23
+ static getInstance() {
24
+ if (!_AgentRegistry.instance) {
25
+ _AgentRegistry.instance = new _AgentRegistry();
26
+ }
27
+ return _AgentRegistry.instance;
28
+ }
29
+ /**
30
+ * Scans the agents directory and loads all valid agent configurations.
31
+ */
32
+ async loadAgents() {
33
+ this.agents.clear();
34
+ try {
35
+ await fs.mkdir(this.agentsDir, { recursive: true });
36
+ const files = await fs.readdir(this.agentsDir);
37
+ for (const file of files) {
38
+ const filePath = path.join(this.agentsDir, file);
39
+ if (file.endsWith(".json")) {
40
+ await this.loadJsonAgent(filePath);
41
+ continue;
42
+ }
43
+ if (file.endsWith(".md") || file.endsWith(".markdown")) {
44
+ await this.loadMarkdownAgent(filePath);
45
+ }
46
+ }
47
+ } catch (error) {
48
+ console.error(`Error loading agents from ${this.agentsDir}:`, error);
49
+ }
50
+ }
51
+ getAgent(name) {
52
+ return this.agents.get(name);
53
+ }
54
+ getAllAgents() {
55
+ return Array.from(this.agents.values());
56
+ }
57
+ getAgentsDirectory() {
58
+ return this.agentsDir;
59
+ }
60
+ async loadJsonAgent(filePath) {
61
+ const name = path.basename(filePath, ".json");
62
+ try {
63
+ const content = await fs.readFile(filePath, "utf-8");
64
+ const json = JSON.parse(content);
65
+ const config = AgentConfigSchema.parse(json);
66
+ this.agents.set(name, { name, path: filePath, ...config });
67
+ } catch (error) {
68
+ console.warn(`Failed to load agent '${name}': ${error.message}`);
69
+ }
70
+ }
71
+ async loadMarkdownAgent(filePath) {
72
+ const name = path.basename(filePath, path.extname(filePath));
73
+ try {
74
+ const content = await fs.readFile(filePath, "utf-8");
75
+ const description = extractMarkdownTitle(content) || `Agent ${name}`;
76
+ const definition = {
77
+ name,
78
+ path: filePath,
79
+ description,
80
+ systemPrompt: content,
81
+ tools: [],
82
+ model: void 0
83
+ };
84
+ this.agents.set(name, definition);
85
+ } catch (error) {
86
+ console.warn(`Failed to load agent '${name}': ${error.message}`);
87
+ }
88
+ }
89
+ };
90
+ function extractMarkdownTitle(content) {
91
+ const lines = content.split(/\r?\n/);
92
+ for (const line of lines) {
93
+ const trimmed = line.trim();
94
+ if (!trimmed) continue;
95
+ if (trimmed.startsWith("#")) {
96
+ return trimmed.replace(/^#+\s*/, "").trim() || null;
97
+ }
98
+ return trimmed;
99
+ }
100
+ return null;
101
+ }
102
+
103
+ // src/commands/agents.ts
104
+ var metadata = {
105
+ command: "/agents",
106
+ description: "list available sub-agents (markdown or json)",
107
+ implemented: true,
108
+ prd: "prd/sub_agents_architecture.md"
109
+ };
110
+ async function handler() {
111
+ const registry = AgentRegistry.getInstance();
112
+ await registry.loadAgents();
113
+ const agents = registry.getAllAgents();
114
+ if (agents.length === 0) {
115
+ return `No agents found in ${chalk.cyan(registry.getAgentsDirectory())}.
116
+ Create a markdown file (e.g., helper.md) there to define a new sub-agent.`;
117
+ }
118
+ let output = chalk.bold("Available Agents:\n\n");
119
+ for (const agent of agents) {
120
+ output += `${chalk.green("\u{1F916} " + agent.name)}
121
+ `;
122
+ output += ` ${chalk.gray(agent.description)}
123
+ `;
124
+ output += ` ${chalk.blue("Path:")} ${agent.path}
125
+ `;
126
+ if (agent.model) {
127
+ output += ` ${chalk.yellow("Model:")} ${agent.model}
128
+ `;
129
+ }
130
+ if (agent.tools?.length) {
131
+ output += ` ${chalk.blue("Tools:")} ${agent.tools.join(", ")}
132
+ `;
133
+ }
134
+ output += "\n";
135
+ }
136
+ return output.trim();
137
+ }
138
+
139
+ export {
140
+ AgentRegistry,
141
+ metadata,
142
+ handler
143
+ };
144
+ /**
145
+ * @license
146
+ * Copyright 2025 Autohand AI LLC
147
+ * SPDX-License-Identifier: Apache-2.0
148
+ */