@arcteninc/core 0.0.22 → 0.0.24

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcteninc/core",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -57,11 +57,142 @@ async function findToolUsageFiles(projectRoot: string): Promise<string[]> {
57
57
  return files;
58
58
  }
59
59
 
60
+ /**
61
+ * Extract tool names from an expression (handles arrays, variables, useMemo, etc.)
62
+ */
63
+ function extractToolNamesFromExpression(
64
+ expr: ts.Expression,
65
+ sourceFile: ts.SourceFile,
66
+ variableMap: Map<string, ts.Expression>
67
+ ): Set<string> {
68
+ const toolNames = new Set<string>();
69
+ const visited = new Set<ts.Node>();
70
+
71
+ function extractFromExpr(node: ts.Node): void {
72
+ if (visited.has(node)) return;
73
+ visited.add(node);
74
+
75
+ // Direct array literal: [tool1, tool2]
76
+ if (ts.isArrayLiteralExpression(node)) {
77
+ for (const element of node.elements) {
78
+ if (ts.isSpreadElement(element)) {
79
+ // Handle spread: [...someArray]
80
+ extractFromExpr(element.expression);
81
+ } else {
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
+ if (simpleName && simpleName !== 'undefined' && simpleName !== 'null') {
86
+ toolNames.add(simpleName);
87
+ }
88
+ }
89
+ }
90
+ return;
91
+ }
92
+
93
+ // Variable reference: toolsList or function name: generateReport
94
+ if (ts.isIdentifier(node)) {
95
+ const varName = node.getText(sourceFile);
96
+ const varExpr = variableMap.get(varName);
97
+ if (varExpr) {
98
+ extractFromExpr(varExpr);
99
+ } else {
100
+ // If not in variableMap, it might be a function declaration name
101
+ // Add it directly as a tool name (findFunctionDefinition will find it later)
102
+ if (varName && varName !== 'undefined' && varName !== 'null') {
103
+ toolNames.add(varName);
104
+ }
105
+ }
106
+ return;
107
+ }
108
+
109
+ // Property access: wrappedTools.getRAGInfo
110
+ if (ts.isPropertyAccessExpression(node)) {
111
+ const propName = node.name.getText(sourceFile);
112
+ toolNames.add(propName);
113
+ return;
114
+ }
115
+
116
+ // useMemo(() => [...], [])
117
+ if (ts.isCallExpression(node)) {
118
+ const callExpr = node.expression;
119
+ if (ts.isIdentifier(callExpr)) {
120
+ const funcName = callExpr.getText(sourceFile);
121
+ if (funcName === 'useMemo' && node.arguments.length > 0) {
122
+ const firstArg = node.arguments[0];
123
+ // Arrow function: () => [...]
124
+ if (ts.isArrowFunction(firstArg) && firstArg.body) {
125
+ if (ts.isBlock(firstArg.body)) {
126
+ // Block body: () => { return [...] }
127
+ for (const stmt of firstArg.body.statements) {
128
+ if (ts.isReturnStatement(stmt) && stmt.expression) {
129
+ extractFromExpr(stmt.expression);
130
+ }
131
+ }
132
+ } else {
133
+ // Expression body: () => [...]
134
+ extractFromExpr(firstArg.body);
135
+ }
136
+ }
137
+ // Regular function call: useMemo(fn, [])
138
+ else if (ts.isFunctionExpression(firstArg) || ts.isArrowFunction(firstArg)) {
139
+ // Can't easily extract from function body without more analysis
140
+ }
141
+ }
142
+ }
143
+ return;
144
+ }
145
+
146
+ // Parenthesized: (toolsList)
147
+ if (ts.isParenthesizedExpression(node)) {
148
+ extractFromExpr(node.expression);
149
+ return;
150
+ }
151
+ }
152
+
153
+ extractFromExpr(expr);
154
+ return toolNames;
155
+ }
156
+
157
+ /**
158
+ * Build a map of variable declarations in the file
159
+ */
160
+ function buildVariableMap(sourceFile: ts.SourceFile): Map<string, ts.Expression> {
161
+ const variableMap = new Map<string, ts.Expression>();
162
+
163
+ function visit(node: ts.Node) {
164
+ // const toolsList = [...] or const { tools } = something
165
+ if (ts.isVariableStatement(node)) {
166
+ for (const declaration of node.declarationList.declarations) {
167
+ if (ts.isIdentifier(declaration.name)) {
168
+ const varName = declaration.name.getText(sourceFile);
169
+ if (declaration.initializer) {
170
+ variableMap.set(varName, declaration.initializer);
171
+ }
172
+ }
173
+ // Handle destructuring: const { tools } = something
174
+ else if (ts.isObjectBindingPattern(declaration.name)) {
175
+ // For now, skip destructuring - can be enhanced later
176
+ }
177
+ }
178
+ }
179
+
180
+ ts.forEachChild(node, visit);
181
+ }
182
+
183
+ visit(sourceFile);
184
+ return variableMap;
185
+ }
186
+
60
187
  /**
61
188
  * Extract tool names from ArctenAgent or useAgent usage
62
189
  */
63
- function extractToolNamesFromFile(sourceFile: ts.SourceFile): ToolUsage[] {
190
+ function extractToolNamesFromFile(
191
+ sourceFile: ts.SourceFile,
192
+ program?: ts.Program
193
+ ): ToolUsage[] {
64
194
  const usages: ToolUsage[] = [];
195
+ const variableMap = buildVariableMap(sourceFile);
65
196
 
66
197
  function visit(node: ts.Node) {
67
198
  // Check for <ArctenAgent tools={[...]} safeTools={[...]} />
@@ -82,13 +213,9 @@ function extractToolNamesFromFile(sourceFile: ts.SourceFile): ToolUsage[] {
82
213
  if (attrName === 'tools' || attrName === 'safeTools') {
83
214
  if (attr.initializer && ts.isJsxExpression(attr.initializer)) {
84
215
  const expr = attr.initializer.expression;
85
- if (expr && ts.isArrayLiteralExpression(expr)) {
86
- for (const element of expr.elements) {
87
- const name = element.getText(sourceFile).trim();
88
- // Remove any object property access (e.g., tools.getOrders -> getOrders)
89
- const simpleName = name.split('.').pop() || name;
90
- toolNames.add(simpleName);
91
- }
216
+ if (expr) {
217
+ const extracted = extractToolNamesFromExpression(expr, sourceFile, variableMap);
218
+ extracted.forEach(name => toolNames.add(name));
92
219
  }
93
220
  }
94
221
  }
@@ -118,13 +245,12 @@ function extractToolNamesFromFile(sourceFile: ts.SourceFile): ToolUsage[] {
118
245
  if (ts.isPropertyAssignment(prop)) {
119
246
  const propName = prop.name.getText(sourceFile);
120
247
  if (propName === 'tools' || propName === 'safeTools') {
121
- if (ts.isArrayLiteralExpression(prop.initializer)) {
122
- for (const element of prop.initializer.elements) {
123
- const name = element.getText(sourceFile).trim();
124
- const simpleName = name.split('.').pop() || name;
125
- toolNames.add(simpleName);
126
- }
127
- }
248
+ const extracted = extractToolNamesFromExpression(
249
+ prop.initializer,
250
+ sourceFile,
251
+ variableMap
252
+ );
253
+ extracted.forEach(name => toolNames.add(name));
128
254
  }
129
255
  }
130
256
  }
@@ -155,16 +281,38 @@ function findFunctionDefinition(
155
281
  functionName: string,
156
282
  sourceFile: ts.SourceFile,
157
283
  program: ts.Program
158
- ): { sourceFile: ts.SourceFile; node: ts.FunctionDeclaration } | null {
284
+ ): { sourceFile: ts.SourceFile; node: ts.FunctionDeclaration | ts.VariableDeclaration } | null {
159
285
  // First, check if it's defined in the current file
160
- let foundNode: ts.FunctionDeclaration | null = null;
286
+ let foundNode: ts.FunctionDeclaration | ts.VariableDeclaration | null = null;
161
287
 
162
288
  function visitForDefinition(node: ts.Node) {
289
+ // Function declaration: function generateReport() {}
163
290
  if (ts.isFunctionDeclaration(node) && node.name) {
164
291
  if (node.name.getText(sourceFile) === functionName) {
165
292
  foundNode = node;
166
293
  }
167
294
  }
295
+
296
+ // Variable declaration with arrow function: const addFilter = async () => {}
297
+ // or function expression: const addFilter = async function() {}
298
+ if (ts.isVariableStatement(node)) {
299
+ for (const declaration of node.declarationList.declarations) {
300
+ if (ts.isIdentifier(declaration.name)) {
301
+ const varName = declaration.name.getText(sourceFile);
302
+ if (varName === functionName && declaration.initializer) {
303
+ // Check if it's an arrow function or function expression
304
+ if (
305
+ ts.isArrowFunction(declaration.initializer) ||
306
+ ts.isFunctionExpression(declaration.initializer)
307
+ ) {
308
+ foundNode = declaration;
309
+ break;
310
+ }
311
+ }
312
+ }
313
+ }
314
+ }
315
+
168
316
  if (!foundNode) {
169
317
  ts.forEachChild(node, visitForDefinition);
170
318
  }
@@ -382,16 +530,46 @@ function serializeType(
382
530
  * Extract metadata from a function
383
531
  */
384
532
  function extractFunctionMetadata(
385
- node: ts.FunctionDeclaration,
533
+ node: ts.FunctionDeclaration | ts.VariableDeclaration,
386
534
  checker: ts.TypeChecker,
387
535
  sourceFile: ts.SourceFile
388
536
  ): FunctionMetadata | null {
389
- if (!node.name) {
537
+ let functionNode: ts.FunctionDeclaration | ts.ArrowFunction | ts.FunctionExpression | null = null;
538
+ let functionName: string;
539
+
540
+ // Handle FunctionDeclaration: function generateReport() {}
541
+ if (ts.isFunctionDeclaration(node)) {
542
+ if (!node.name) {
543
+ return null;
544
+ }
545
+ functionNode = node;
546
+ functionName = node.name.getText(sourceFile);
547
+ }
548
+ // Handle VariableDeclaration: const addFilter = async () => {}
549
+ else if (ts.isVariableDeclaration(node)) {
550
+ if (!ts.isIdentifier(node.name)) {
551
+ return null;
552
+ }
553
+ functionName = node.name.getText(sourceFile);
554
+
555
+ if (node.initializer) {
556
+ if (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) {
557
+ functionNode = node.initializer;
558
+ } else {
559
+ return null;
560
+ }
561
+ } else {
562
+ return null;
563
+ }
564
+ } else {
390
565
  return null;
391
566
  }
392
567
 
393
- const functionName = node.name.getText(sourceFile);
568
+ if (!functionNode) {
569
+ return null;
570
+ }
394
571
 
572
+ // Extract JSDoc comments
395
573
  const jsDocTags = ts.getJSDocTags(node);
396
574
  const jsDocComments = ts.getJSDocCommentsAndTags(node);
397
575
 
@@ -418,9 +596,24 @@ function extractFunctionMetadata(
418
596
 
419
597
  const properties: Record<string, JsonSchemaProperty> = {};
420
598
  const required: string[] = [];
421
- const isAsync = node.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) || false;
422
-
423
- for (const param of node.parameters) {
599
+
600
+ // Check if async - works for both function declarations and arrow functions
601
+ const isAsync =
602
+ (ts.isFunctionDeclaration(node) && node.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword)) ||
603
+ (ts.isVariableDeclaration(node) && node.initializer && ts.isArrowFunction(node.initializer) &&
604
+ (node.initializer.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) || false)) ||
605
+ (ts.isVariableDeclaration(node) && node.initializer && ts.isFunctionExpression(node.initializer) &&
606
+ (node.initializer.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) || false)) ||
607
+ false;
608
+
609
+ // Extract parameters from the function node
610
+ const parameters = ts.isFunctionDeclaration(node)
611
+ ? node.parameters
612
+ : (ts.isArrowFunction(functionNode) || ts.isFunctionExpression(functionNode))
613
+ ? functionNode.parameters
614
+ : [];
615
+
616
+ for (const param of parameters) {
424
617
  const paramName = param.name.getText(sourceFile);
425
618
  const hasDefault = param.initializer !== undefined;
426
619
 
@@ -461,8 +654,16 @@ function extractFunctionMetadata(
461
654
  }
462
655
 
463
656
  let returnType = 'any';
464
- if (node.type) {
657
+ if (ts.isFunctionDeclaration(node) && node.type) {
465
658
  returnType = node.type.getText(sourceFile);
659
+ } else if (
660
+ ts.isVariableDeclaration(node) &&
661
+ node.initializer &&
662
+ (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))
663
+ ) {
664
+ if (node.initializer.type) {
665
+ returnType = node.initializer.type.getText(sourceFile);
666
+ }
466
667
  }
467
668
 
468
669
  return {
@@ -512,7 +713,7 @@ async function autoDiscoverAndExtract(projectRoot: string, outputPath: string) {
512
713
  for (const file of files) {
513
714
  const sourceFile = program.getSourceFile(file);
514
715
  if (sourceFile) {
515
- const usages = extractToolNamesFromFile(sourceFile);
716
+ const usages = extractToolNamesFromFile(sourceFile, program);
516
717
  if (usages.length > 0) {
517
718
  allToolUsages.push(...usages);
518
719
  usages.forEach(u => u.toolNames.forEach(name => allToolNames.add(name)));