@arcteninc/core 0.0.16 → 0.0.17

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 (38) hide show
  1. package/README.md +50 -4
  2. package/dist/components/ArctenAgent.d.ts.map +1 -0
  3. package/dist/components/ai-elements/prompt-input.d.ts.map +1 -0
  4. package/dist/components/ai-elements/reasoning.d.ts.map +1 -0
  5. package/dist/components/ai-elements/response.d.ts.map +1 -0
  6. package/dist/components/ai-elements/shimmer.d.ts.map +1 -0
  7. package/dist/components/tool-call-approval.d.ts.map +1 -0
  8. package/dist/components/tool-call-result.d.ts.map +1 -0
  9. package/dist/components/ui/autotextarea.d.ts.map +1 -0
  10. package/dist/components/ui/badge.d.ts.map +1 -0
  11. package/dist/components/ui/button.d.ts +14 -0
  12. package/dist/components/ui/button.d.ts.map +1 -0
  13. package/dist/components/ui/collapsible.d.ts.map +1 -0
  14. package/dist/components/ui/command.d.ts.map +1 -0
  15. package/dist/components/ui/dialog.d.ts.map +1 -0
  16. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  17. package/dist/components/ui/hover-card.d.ts.map +1 -0
  18. package/dist/components/ui/input-group.d.ts +17 -0
  19. package/dist/components/ui/input-group.d.ts.map +1 -0
  20. package/dist/components/ui/input.d.ts.map +1 -0
  21. package/dist/components/ui/kbd.d.ts.map +1 -0
  22. package/dist/components/ui/select.d.ts.map +1 -0
  23. package/dist/components/ui/textarea.d.ts.map +1 -0
  24. package/dist/components/ui/tooltip.d.ts.map +1 -0
  25. package/dist/core.css +1 -1
  26. package/dist/index.cjs +7 -7
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.mjs +2372 -2226
  29. package/dist/lib/useAgent.d.ts.map +1 -0
  30. package/dist/lib/utils.d.ts.map +1 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/types/tool-description.d.ts.map +1 -0
  33. package/dist/types/use-agent.d.ts.map +1 -0
  34. package/dist/utils/extract-tool-metadata.d.ts +25 -0
  35. package/dist/utils/extract-tool-metadata.d.ts.map +1 -0
  36. package/dist/verifyToken.d.ts.map +1 -0
  37. package/package.json +11 -13
  38. package/scripts/cli-extract-types-auto.ts +542 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAgent.d.ts","sourceRoot":"","sources":["../../src/lib/useAgent.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAGf,MAAM,oBAAoB,CAAC;AAE5B,wBAAgB,QAAQ,CAAC,EACvB,UAAqC,EACrC,aAAmC,EACnC,WAAW,EAAE,aAAa,EAC1B,cAAsB,EACtB,IAAI,EACJ,KAAU,EACV,SAAc,EACd,YAAiB,EACjB,eAAoB,EACpB,cAAc,EAAE,qBAAqB,EACrC,UAAU,EACV,QAAQ,EACR,UAAU,GACX,GAAE,eAAoB,GAAG,cAAc,CAkZvC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAA;AAG5C,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-description.d.ts","sourceRoot":"","sources":["../../src/types/tool-description.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent.d.ts","sourceRoot":"","sources":["../../src/types/use-agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ export interface ToolMetadata {
3
+ name: string;
4
+ description: string;
5
+ parameters: z.ZodObject<any>;
6
+ parametersDef: Record<string, {
7
+ type: string;
8
+ description: string;
9
+ required: boolean;
10
+ }>;
11
+ }
12
+ /**
13
+ * Extract tool metadata from a function
14
+ * Reads function name, JSDoc comments, and infers parameter schema
15
+ *
16
+ * Priority:
17
+ * 1. Build-time extracted metadata (if available)
18
+ * 2. Runtime inference (fallback)
19
+ */
20
+ export declare function extractToolMetadata(fn: Function): ToolMetadata;
21
+ /**
22
+ * Extract tool metadata from multiple functions
23
+ */
24
+ export declare function extractToolsMetadata(functions: Function[]): ToolMetadata[];
25
+ //# sourceMappingURL=extract-tool-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-tool-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/extract-tool-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACzF;AAwID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,GAAG,YAAY,CAsB9D;AAuRD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAE1E"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifyToken.d.ts","sourceRoot":"","sources":["../src/verifyToken.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,aAAa,CAAC,CA0BxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcteninc/core",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -15,19 +15,19 @@
15
15
  "types": "./dist/server.d.ts",
16
16
  "import": "./dist/server.mjs",
17
17
  "require": "./dist/server.cjs"
18
- },
19
- "./styles": "./dist/core.css"
18
+ }
19
+ },
20
+ "sideEffects": false,
21
+ "bin": {
22
+ "arcten-extract-types": "./scripts/cli-extract-types-auto.ts"
20
23
  },
21
- "sideEffects": [
22
- "*.css",
23
- "./dist/core.css"
24
- ],
25
24
  "files": [
26
- "dist"
25
+ "dist",
26
+ "scripts/cli-extract-types-auto.ts"
27
27
  ],
28
28
  "scripts": {
29
29
  "dev": "vite build --watch",
30
- "build": "vite build && bunx tsc --emitDeclarationOnly --outDir dist",
30
+ "build": "vite build && bunx tsc -p tsconfig.build.json",
31
31
  "prepublishOnly": "bun run build"
32
32
  },
33
33
  "peerDependencies": {
@@ -41,8 +41,6 @@
41
41
  "@types/node": "^24.9.2",
42
42
  "@types/react": "^19.2.2",
43
43
  "@types/react-dom": "^19.2.2",
44
- "autoprefixer": "^10.4.21",
45
- "postcss": "^8.5.6",
46
44
  "react": "^19.2.0",
47
45
  "react-dom": "^19.2.0",
48
46
  "typescript": "^5.9.3",
@@ -60,12 +58,13 @@
60
58
  "@radix-ui/react-slot": "^1.2.3",
61
59
  "@radix-ui/react-tooltip": "^1.2.8",
62
60
  "@radix-ui/react-use-controllable-state": "^1.2.2",
63
- "@tailwindcss/vite": "^4.1.16",
64
61
  "@use-gesture/react": "^10.3.1",
62
+ "acorn": "^8.15.0",
65
63
  "ai": "^6.0.0-beta.94",
66
64
  "class-variance-authority": "^0.7.1",
67
65
  "clsx": "^2.1.1",
68
66
  "cmdk": "^1.1.1",
67
+ "glob": "^11.0.3",
69
68
  "lucide-react": "^0.552.0",
70
69
  "motion": "^12.23.24",
71
70
  "nanoid": "^5.1.6",
@@ -74,7 +73,6 @@
74
73
  "shadcn": "^3.5.0",
75
74
  "streamdown": "^1.4.0",
76
75
  "tailwind-merge": "^3.3.1",
77
- "tailwindcss": "^4.1.16",
78
76
  "tw-animate-css": "^1.4.0",
79
77
  "zod": "^3.24.1"
80
78
  }
@@ -0,0 +1,542 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Automatically discover and extract types for tools used in ArctenAgent/useAgent
4
+ * Scans the project, finds all tool usages, and generates metadata only for used tools
5
+ */
6
+
7
+ import * as ts from 'typescript';
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import { glob } from 'glob';
11
+
12
+ interface TypeMetadata {
13
+ type: string;
14
+ description?: string;
15
+ required: boolean;
16
+ default?: string;
17
+ values?: string[];
18
+ arrayItemType?: TypeMetadata;
19
+ properties?: Record<string, TypeMetadata>;
20
+ }
21
+
22
+ interface FunctionMetadata {
23
+ name: string;
24
+ description: string;
25
+ parameters: Record<string, TypeMetadata>;
26
+ returnType?: string;
27
+ isAsync?: boolean;
28
+ }
29
+
30
+ interface GeneratedMetadata {
31
+ generated: string;
32
+ discoveredFrom: string[];
33
+ functions: Record<string, FunctionMetadata>;
34
+ }
35
+
36
+ interface ToolUsage {
37
+ toolNames: Set<string>;
38
+ file: string;
39
+ component: 'ArctenAgent' | 'useAgent';
40
+ }
41
+
42
+ /**
43
+ * Find all files that use ArctenAgent or useAgent
44
+ */
45
+ async function findToolUsageFiles(projectRoot: string): Promise<string[]> {
46
+ const pattern = path.join(projectRoot, '**/*.{ts,tsx}').replace(/\\/g, '/');
47
+ const files = await glob(pattern, {
48
+ ignore: ['**/node_modules/**', '**/dist/**', '**/.next/**', '**/build/**'],
49
+ });
50
+
51
+ console.log(`šŸ“‚ Found ${files.length} TypeScript files to scan`);
52
+ return files;
53
+ }
54
+
55
+ /**
56
+ * Extract tool names from ArctenAgent or useAgent usage
57
+ */
58
+ function extractToolNamesFromFile(sourceFile: ts.SourceFile): ToolUsage[] {
59
+ const usages: ToolUsage[] = [];
60
+
61
+ function visit(node: ts.Node) {
62
+ // Check for <ArctenAgent tools={[...]} safeTools={[...]} />
63
+ if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
64
+ const tagName = ts.isJsxElement(node)
65
+ ? node.openingElement.tagName.getText(sourceFile)
66
+ : node.tagName.getText(sourceFile);
67
+
68
+ if (tagName === 'ArctenAgent') {
69
+ const toolNames = new Set<string>();
70
+ const attributes = ts.isJsxElement(node)
71
+ ? node.openingElement.attributes.properties
72
+ : node.attributes.properties;
73
+
74
+ for (const attr of attributes) {
75
+ if (ts.isJsxAttribute(attr)) {
76
+ const attrName = attr.name.getText(sourceFile);
77
+ if (attrName === 'tools' || attrName === 'safeTools') {
78
+ if (attr.initializer && ts.isJsxExpression(attr.initializer)) {
79
+ const expr = attr.initializer.expression;
80
+ if (expr && ts.isArrayLiteralExpression(expr)) {
81
+ for (const element of expr.elements) {
82
+ const name = element.getText(sourceFile).trim();
83
+ // Remove any object property access (e.g., tools.getOrders -> getOrders)
84
+ const simpleName = name.split('.').pop() || name;
85
+ toolNames.add(simpleName);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ if (toolNames.size > 0) {
94
+ usages.push({
95
+ toolNames,
96
+ file: sourceFile.fileName,
97
+ component: 'ArctenAgent',
98
+ });
99
+ }
100
+ }
101
+ }
102
+
103
+ // Check for useAgent({ tools: [...], safeTools: [...] })
104
+ if (ts.isCallExpression(node)) {
105
+ const expr = node.expression;
106
+ if (ts.isIdentifier(expr) && expr.getText(sourceFile) === 'useAgent') {
107
+ const toolNames = new Set<string>();
108
+
109
+ if (node.arguments.length > 0) {
110
+ const arg = node.arguments[0];
111
+ if (ts.isObjectLiteralExpression(arg)) {
112
+ for (const prop of arg.properties) {
113
+ if (ts.isPropertyAssignment(prop)) {
114
+ const propName = prop.name.getText(sourceFile);
115
+ if (propName === 'tools' || propName === 'safeTools') {
116
+ if (ts.isArrayLiteralExpression(prop.initializer)) {
117
+ for (const element of prop.initializer.elements) {
118
+ const name = element.getText(sourceFile).trim();
119
+ const simpleName = name.split('.').pop() || name;
120
+ toolNames.add(simpleName);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+
129
+ if (toolNames.size > 0) {
130
+ usages.push({
131
+ toolNames,
132
+ file: sourceFile.fileName,
133
+ component: 'useAgent',
134
+ });
135
+ }
136
+ }
137
+ }
138
+
139
+ ts.forEachChild(node, visit);
140
+ }
141
+
142
+ visit(sourceFile);
143
+ return usages;
144
+ }
145
+
146
+ /**
147
+ * Find where a tool function is defined
148
+ */
149
+ function findFunctionDefinition(
150
+ functionName: string,
151
+ sourceFile: ts.SourceFile,
152
+ program: ts.Program
153
+ ): { sourceFile: ts.SourceFile; node: ts.FunctionDeclaration } | null {
154
+ // First, check if it's defined in the current file
155
+ let foundNode: ts.FunctionDeclaration | null = null;
156
+
157
+ function visitForDefinition(node: ts.Node) {
158
+ if (ts.isFunctionDeclaration(node) && node.name) {
159
+ if (node.name.getText(sourceFile) === functionName) {
160
+ foundNode = node;
161
+ }
162
+ }
163
+ if (!foundNode) {
164
+ ts.forEachChild(node, visitForDefinition);
165
+ }
166
+ }
167
+
168
+ visitForDefinition(sourceFile);
169
+
170
+ if (foundNode) {
171
+ return { sourceFile, node: foundNode };
172
+ }
173
+
174
+ // If not found, check imports
175
+ const imports: { name: string; from: string }[] = [];
176
+
177
+ function visitForImports(node: ts.Node) {
178
+ if (ts.isImportDeclaration(node)) {
179
+ const moduleSpecifier = node.moduleClause;
180
+ if (moduleSpecifier && ts.isImportClause(moduleSpecifier)) {
181
+ const namedBindings = moduleSpecifier.namedBindings;
182
+ if (namedBindings && ts.isNamedImports(namedBindings)) {
183
+ for (const element of namedBindings.elements) {
184
+ const importedName = element.name.getText(sourceFile);
185
+ if (importedName === functionName) {
186
+ const modulePath = (node.moduleSpecifier as ts.StringLiteral).text;
187
+ imports.push({ name: importedName, from: modulePath });
188
+ }
189
+ }
190
+ }
191
+ }
192
+ }
193
+ ts.forEachChild(node, visitForImports);
194
+ }
195
+
196
+ visitForImports(sourceFile);
197
+
198
+ // Follow imports to find definition
199
+ for (const imp of imports) {
200
+ const resolvedPath = resolveImportPath(imp.from, sourceFile.fileName);
201
+ if (resolvedPath) {
202
+ const importedSourceFile = program.getSourceFile(resolvedPath);
203
+ if (importedSourceFile) {
204
+ const result = findFunctionDefinition(functionName, importedSourceFile, program);
205
+ if (result) {
206
+ return result;
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ return null;
213
+ }
214
+
215
+ /**
216
+ * Resolve import path to actual file path
217
+ */
218
+ function resolveImportPath(importPath: string, fromFile: string): string | null {
219
+ if (importPath.startsWith('.')) {
220
+ // Relative import
221
+ const dir = path.dirname(fromFile);
222
+ const resolved = path.resolve(dir, importPath);
223
+
224
+ // Try common extensions
225
+ const extensions = ['.ts', '.tsx', '.js', '.jsx'];
226
+ for (const ext of extensions) {
227
+ const withExt = resolved + ext;
228
+ if (fs.existsSync(withExt)) {
229
+ return withExt;
230
+ }
231
+ }
232
+
233
+ // Try index files
234
+ for (const ext of extensions) {
235
+ const indexPath = path.join(resolved, `index${ext}`);
236
+ if (fs.existsSync(indexPath)) {
237
+ return indexPath;
238
+ }
239
+ }
240
+
241
+ return resolved;
242
+ }
243
+
244
+ // Node modules - we'll skip for now
245
+ return null;
246
+ }
247
+
248
+ /**
249
+ * Serialize a TypeScript type (same as previous implementation)
250
+ */
251
+ function serializeType(
252
+ type: ts.Type,
253
+ checker: ts.TypeChecker,
254
+ visited = new Set<number>(),
255
+ depth = 0
256
+ ): TypeMetadata {
257
+ const typeString = checker.typeToString(type);
258
+
259
+ if (depth > 10) {
260
+ return { type: 'any', required: true };
261
+ }
262
+
263
+ const typeId = (type as any).id;
264
+ if (typeId !== undefined && visited.has(typeId)) {
265
+ return { type: 'any', required: true };
266
+ }
267
+
268
+ if (type.flags & ts.TypeFlags.String) {
269
+ return { type: 'string', required: true };
270
+ }
271
+ if (type.flags & ts.TypeFlags.Number) {
272
+ return { type: 'number', required: true };
273
+ }
274
+ if (type.flags & ts.TypeFlags.Boolean) {
275
+ return { type: 'boolean', required: true };
276
+ }
277
+ if (type.flags & ts.TypeFlags.Undefined || type.flags & ts.TypeFlags.Void) {
278
+ return { type: 'undefined', required: false };
279
+ }
280
+
281
+ if (type.isUnion()) {
282
+ const types = type.types;
283
+ const stringLiterals = types
284
+ .filter(t => t.flags & ts.TypeFlags.StringLiteral)
285
+ .map(t => (t as ts.StringLiteralType).value);
286
+
287
+ if (stringLiterals.length === types.length) {
288
+ return { type: 'enum', values: stringLiterals, required: true };
289
+ }
290
+
291
+ const hasUndefined = types.some(t => t.flags & ts.TypeFlags.Undefined);
292
+ const nonUndefinedTypes = types.filter(t => !(t.flags & ts.TypeFlags.Undefined));
293
+
294
+ if (hasUndefined && nonUndefinedTypes.length === 1) {
295
+ const metadata = serializeType(nonUndefinedTypes[0], checker, visited, depth + 1);
296
+ metadata.required = false;
297
+ return metadata;
298
+ }
299
+
300
+ return {
301
+ type: types.map(t => checker.typeToString(t)).join(' | '),
302
+ required: true,
303
+ };
304
+ }
305
+
306
+ if (checker.isArrayType(type)) {
307
+ const typeArgs = (type as ts.TypeReference).typeArguments;
308
+ if (typeArgs && typeArgs.length > 0) {
309
+ return {
310
+ type: 'array',
311
+ arrayItemType: serializeType(typeArgs[0], checker, visited, depth + 1),
312
+ required: true,
313
+ };
314
+ }
315
+ return { type: 'array', required: true };
316
+ }
317
+
318
+ if (type.flags & ts.TypeFlags.Object) {
319
+ if (typeId !== undefined) {
320
+ visited.add(typeId);
321
+ }
322
+
323
+ const properties: Record<string, TypeMetadata> = {};
324
+ const props = checker.getPropertiesOfType(type);
325
+
326
+ if (props.length > 0) {
327
+ for (const prop of props) {
328
+ const propDeclaration = prop.valueDeclaration || prop.declarations?.[0];
329
+
330
+ if (propDeclaration) {
331
+ const propType = checker.getTypeOfSymbolAtLocation(prop, propDeclaration);
332
+ const isOptional = (prop.flags & ts.SymbolFlags.Optional) !== 0;
333
+
334
+ const propMetadata = serializeType(propType, checker, visited, depth + 1);
335
+ propMetadata.required = !isOptional;
336
+
337
+ properties[prop.name] = propMetadata;
338
+ }
339
+ }
340
+
341
+ return { type: 'object', properties, required: true };
342
+ }
343
+ }
344
+
345
+ return {
346
+ type: typeString.length < 100 ? typeString : 'any',
347
+ required: true,
348
+ };
349
+ }
350
+
351
+ /**
352
+ * Extract metadata from a function
353
+ */
354
+ function extractFunctionMetadata(
355
+ node: ts.FunctionDeclaration,
356
+ checker: ts.TypeChecker,
357
+ sourceFile: ts.SourceFile
358
+ ): FunctionMetadata | null {
359
+ if (!node.name) {
360
+ return null;
361
+ }
362
+
363
+ const functionName = node.name.getText(sourceFile);
364
+
365
+ const jsDocTags = ts.getJSDocTags(node);
366
+ const jsDocComments = ts.getJSDocCommentsAndTags(node);
367
+
368
+ let description = `Execute ${functionName}`;
369
+ for (const comment of jsDocComments) {
370
+ if (ts.isJSDoc(comment) && comment.comment) {
371
+ if (typeof comment.comment === 'string') {
372
+ description = comment.comment.trim();
373
+ }
374
+ break;
375
+ }
376
+ }
377
+
378
+ const paramDescriptions = new Map<string, string>();
379
+ for (const tag of jsDocTags) {
380
+ if (tag.tagName.text === 'param' && ts.isJSDocParameterTag(tag)) {
381
+ const paramName = tag.name?.getText(sourceFile);
382
+ const paramDesc = typeof tag.comment === 'string' ? tag.comment.trim() : '';
383
+ if (paramName && paramDesc) {
384
+ paramDescriptions.set(paramName, paramDesc);
385
+ }
386
+ }
387
+ }
388
+
389
+ const parameters: Record<string, TypeMetadata> = {};
390
+ const isAsync = node.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) || false;
391
+
392
+ for (const param of node.parameters) {
393
+ const paramName = param.name.getText(sourceFile);
394
+ const hasDefault = param.initializer !== undefined;
395
+
396
+ let typeMetadata: TypeMetadata;
397
+ if (param.type) {
398
+ const type = checker.getTypeFromTypeNode(param.type);
399
+ typeMetadata = serializeType(type, checker);
400
+ } else {
401
+ typeMetadata = { type: 'any', required: true };
402
+ }
403
+
404
+ const isOptional = param.questionToken !== undefined || hasDefault;
405
+ if (isOptional) {
406
+ typeMetadata.required = false;
407
+ }
408
+
409
+ if (hasDefault) {
410
+ typeMetadata.default = param.initializer!.getText(sourceFile);
411
+ }
412
+
413
+ if (paramDescriptions.has(paramName)) {
414
+ typeMetadata.description = paramDescriptions.get(paramName);
415
+ }
416
+
417
+ parameters[paramName] = typeMetadata;
418
+ }
419
+
420
+ let returnType = 'any';
421
+ if (node.type) {
422
+ returnType = node.type.getText(sourceFile);
423
+ }
424
+
425
+ return {
426
+ name: functionName,
427
+ description,
428
+ parameters,
429
+ returnType,
430
+ isAsync,
431
+ };
432
+ }
433
+
434
+ /**
435
+ * Main function
436
+ */
437
+ async function autoDiscoverAndExtract(projectRoot: string, outputPath: string) {
438
+ console.log(`\nšŸ” Auto-discovering tools in: ${projectRoot}\n`);
439
+
440
+ // Find all TypeScript files
441
+ const files = await findToolUsageFiles(projectRoot);
442
+
443
+ // Read tsconfig
444
+ const configPath = ts.findConfigFile(projectRoot, ts.sys.fileExists, 'tsconfig.json');
445
+ let compilerOptions: ts.CompilerOptions = {
446
+ target: ts.ScriptTarget.ESNext,
447
+ module: ts.ModuleKind.ESNext,
448
+ jsx: ts.JsxEmit.React,
449
+ };
450
+
451
+ if (configPath) {
452
+ const config = ts.readConfigFile(configPath, ts.sys.readFile);
453
+ const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, path.dirname(configPath));
454
+ compilerOptions = parsed.options;
455
+ }
456
+
457
+ // Create program with all files
458
+ const program = ts.createProgram(files, compilerOptions);
459
+ const checker = program.getTypeChecker();
460
+
461
+ // Scan for tool usages
462
+ const allToolUsages: ToolUsage[] = [];
463
+ const allToolNames = new Set<string>();
464
+
465
+ for (const file of files) {
466
+ const sourceFile = program.getSourceFile(file);
467
+ if (sourceFile) {
468
+ const usages = extractToolNamesFromFile(sourceFile);
469
+ if (usages.length > 0) {
470
+ allToolUsages.push(...usages);
471
+ usages.forEach(u => u.toolNames.forEach(name => allToolNames.add(name)));
472
+ }
473
+ }
474
+ }
475
+
476
+ console.log(`āœ“ Found ${allToolUsages.length} usage site(s) with ${allToolNames.size} unique tool(s)`);
477
+ allToolUsages.forEach(usage => {
478
+ console.log(` - ${usage.component} in ${path.relative(projectRoot, usage.file)}`);
479
+ console.log(` Tools: ${Array.from(usage.toolNames).join(', ')}`);
480
+ });
481
+
482
+ // Extract metadata for discovered tools
483
+ console.log(`\nšŸ“ Extracting types for discovered tools...\n`);
484
+
485
+ const functionsMap: Record<string, FunctionMetadata> = {};
486
+ const discoveredFrom: string[] = [];
487
+
488
+ for (const toolName of allToolNames) {
489
+ let found = false;
490
+
491
+ // Search in all files for the definition
492
+ for (const file of files) {
493
+ const sourceFile = program.getSourceFile(file);
494
+ if (sourceFile) {
495
+ const result = findFunctionDefinition(toolName, sourceFile, program);
496
+ if (result) {
497
+ const metadata = extractFunctionMetadata(result.node, checker, result.sourceFile);
498
+ if (metadata) {
499
+ functionsMap[toolName] = metadata;
500
+ discoveredFrom.push(path.relative(projectRoot, result.sourceFile.fileName));
501
+ console.log(` āœ“ ${toolName} (from ${path.relative(projectRoot, result.sourceFile.fileName)})`);
502
+ found = true;
503
+ break;
504
+ }
505
+ }
506
+ }
507
+ }
508
+
509
+ if (!found) {
510
+ console.log(` ⚠ ${toolName} (definition not found)`);
511
+ }
512
+ }
513
+
514
+ // Generate output
515
+ const output: GeneratedMetadata = {
516
+ generated: new Date().toISOString(),
517
+ discoveredFrom: Array.from(new Set(discoveredFrom)),
518
+ functions: functionsMap,
519
+ };
520
+
521
+ const outputDir = path.dirname(outputPath);
522
+ if (!fs.existsSync(outputDir)) {
523
+ fs.mkdirSync(outputDir, { recursive: true });
524
+ }
525
+
526
+ fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
527
+ console.log(`\nāœ… Generated metadata for ${Object.keys(functionsMap).length} tool(s)`);
528
+ console.log(`šŸ“ Output: ${outputPath}\n`);
529
+ }
530
+
531
+ // CLI
532
+ if (import.meta.main) {
533
+ const args = process.argv.slice(2);
534
+ const projectRoot = args[0] ? path.resolve(args[0]) : process.cwd();
535
+ const outputPath = args[1]
536
+ ? path.resolve(args[1])
537
+ : path.join(projectRoot, '.arcten', 'tool-types.json');
538
+
539
+ autoDiscoverAndExtract(projectRoot, outputPath);
540
+ }
541
+
542
+ export { autoDiscoverAndExtract };