@arcteninc/core 0.0.58 → 0.0.60

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.58",
3
+ "version": "0.0.60",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -20,17 +20,21 @@
20
20
  },
21
21
  "sideEffects": false,
22
22
  "bin": {
23
- "arcten-extract-types": "./scripts/cli-extract-types-auto.js"
23
+ "arcten": "./scripts/arcten-cli.js"
24
24
  },
25
25
  "files": [
26
26
  "dist",
27
27
  "scripts/cli-extract-types-auto.ts",
28
- "scripts/cli-extract-types-auto.js"
28
+ "scripts/cli-extract-types-auto.js",
29
+ "scripts/arcten-cli.js",
30
+ "scripts/postinstall-check-version.js",
31
+ "scripts/update-core.js"
29
32
  ],
30
33
  "scripts": {
31
34
  "dev": "vite build --watch",
32
35
  "build": "vite build",
33
- "prepublishOnly": "bun run build"
36
+ "prepublishOnly": "bun run build",
37
+ "postinstall": "node scripts/postinstall-check-version.js"
34
38
  },
35
39
  "peerDependencies": {
36
40
  "ai": "^6.0.0-beta.94",
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Arcten CLI - Unified command interface
4
+ * Usage: arcten <command> [options]
5
+ *
6
+ * Commands:
7
+ * extract-types, tools Extract tool metadata from your project
8
+ * update Update @arcteninc/core to latest version
9
+ * help Show help message
10
+ */
11
+
12
+ const { spawn } = require('child_process');
13
+ const path = require('path');
14
+ const fs = require('fs');
15
+
16
+ const commands = {
17
+ 'extract-types': () => require('./cli-extract-types-auto.js'),
18
+ 'tools': () => require('./cli-extract-types-auto.js'), // Alias
19
+ 'update': () => require('./update-core.js'),
20
+ 'help': showHelp,
21
+ '--help': showHelp,
22
+ '-h': showHelp,
23
+ };
24
+
25
+ function showHelp() {
26
+ console.log(`
27
+ šŸ“¦ Arcten CLI
28
+
29
+ Usage: arcten <command> [options]
30
+
31
+ Commands:
32
+ extract-types, tools Extract tool metadata from your project
33
+ Alias: arcten tools
34
+
35
+ update Update @arcteninc/core to latest version
36
+ Updates package.json and shows next steps
37
+
38
+ help Show this help message
39
+
40
+ Examples:
41
+ arcten tools Extract tool types (same as arcten extract-types)
42
+ arcten update Update @arcteninc/core to latest version
43
+ arcten help Show help
44
+
45
+ For more information, visit https://github.com/arcteninc/core
46
+ `);
47
+ process.exit(0);
48
+ }
49
+
50
+ function main() {
51
+ const args = process.argv.slice(2);
52
+
53
+ if (args.length === 0) {
54
+ showHelp();
55
+ return;
56
+ }
57
+
58
+ const command = args[0];
59
+ const commandArgs = args.slice(1);
60
+
61
+ if (!commands[command]) {
62
+ console.error(`āŒ Unknown command: ${command}`);
63
+ console.error(`\nRun 'arcten help' for available commands`);
64
+ process.exit(1);
65
+ }
66
+
67
+ try {
68
+ const commandHandler = commands[command];
69
+ if (typeof commandHandler === 'function' && commandHandler !== showHelp) {
70
+ // For commands that are modules, we need to handle them differently
71
+ // Since they're designed to run as standalone scripts, we'll spawn them
72
+ let scriptPath;
73
+ let runner = 'node';
74
+
75
+ if (command === 'update') {
76
+ scriptPath = path.join(__dirname, 'update-core.js');
77
+ } else {
78
+ // For extract-types/tools, try .js first, fallback to .ts with bun
79
+ const jsPath = path.join(__dirname, 'cli-extract-types-auto.js');
80
+ const tsPath = path.join(__dirname, 'cli-extract-types-auto.ts');
81
+
82
+ if (fs.existsSync(jsPath)) {
83
+ scriptPath = jsPath;
84
+ } else if (fs.existsSync(tsPath)) {
85
+ scriptPath = tsPath;
86
+ runner = 'bun'; // Use bun for TypeScript files
87
+ } else {
88
+ console.error(`āŒ Script not found: ${jsPath} or ${tsPath}`);
89
+ process.exit(1);
90
+ }
91
+ }
92
+
93
+ if (!fs.existsSync(scriptPath)) {
94
+ console.error(`āŒ Script not found: ${scriptPath}`);
95
+ process.exit(1);
96
+ }
97
+
98
+ // Spawn the script with remaining args
99
+ const child = spawn(runner, [scriptPath, ...commandArgs], {
100
+ stdio: 'inherit',
101
+ cwd: process.cwd(),
102
+ shell: true // Use shell to find bun/node in PATH
103
+ });
104
+
105
+ child.on('error', (error) => {
106
+ console.error(`āŒ Failed to run command:`, error.message);
107
+ if (runner === 'bun' && error.code === 'ENOENT') {
108
+ console.error(`\nšŸ’” Bun not found. Install it: https://bun.sh`);
109
+ console.error(` Or ensure cli-extract-types-auto.js is compiled`);
110
+ }
111
+ process.exit(1);
112
+ });
113
+
114
+ child.on('exit', (code) => {
115
+ process.exit(code || 0);
116
+ });
117
+ } else {
118
+ // For help command
119
+ commandHandler();
120
+ }
121
+ } catch (error) {
122
+ console.error(`āŒ Error running command:`, error.message);
123
+ process.exit(1);
124
+ }
125
+ }
126
+
127
+ if (require.main === module) {
128
+ main();
129
+ }
130
+
131
+ module.exports = { main };
132
+
@@ -1,93 +1,93 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Wrapper script to run the TypeScript CLI
4
- * Works with Node.js - no bun required!
5
- */
6
-
7
- import { spawn } from 'child_process';
8
- import { fileURLToPath } from 'url';
9
- import { dirname, resolve } from 'path';
10
- import { existsSync } from 'fs';
11
-
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = dirname(__filename);
14
-
15
- // Path to the TypeScript script - resolve relative to this wrapper
16
- const tsScript = resolve(__dirname, 'cli-extract-types-auto.ts');
17
-
18
- // Check if a command is available
19
- function checkCommand(cmd) {
20
- return new Promise((resolve) => {
21
- const check = spawn(cmd, ['--version'], { stdio: 'ignore', shell: true });
22
- check.on('close', (code) => {
23
- resolve(code === 0);
24
- });
25
- check.on('error', () => {
26
- resolve(false);
27
- });
28
- });
29
- }
30
-
31
- async function main() {
32
- if (!existsSync(tsScript)) {
33
- console.error(`āŒ Error: Script not found at ${tsScript}`);
34
- process.exit(1);
35
- }
36
-
37
- const args = process.argv.slice(2);
38
-
39
- // Check for bun first (faster and preferred if available)
40
- const hasBun = await checkCommand('bun');
41
-
42
- if (hasBun) {
43
- // Use bun directly - it's faster and can run TypeScript natively
44
- const bunProcess = spawn('bun', [tsScript, ...args], {
45
- stdio: 'inherit',
46
- shell: true
47
- });
48
-
49
- bunProcess.on('close', (code) => {
50
- process.exit(code || 0);
51
- });
52
-
53
- bunProcess.on('error', (err) => {
54
- // If bun fails, try tsx as fallback
55
- tryTsxFallback(args);
56
- });
57
- } else {
58
- // Fallback to tsx via npx if bun isn't available
59
- tryTsxFallback(args);
60
- }
61
- }
62
-
63
- async function tryTsxFallback(args) {
64
- const hasNpx = await checkCommand('npx');
65
-
66
- if (hasNpx) {
67
- // Use tsx via npx - it's lightweight and works great with Node.js
68
- const tsxProcess = spawn('npx', ['-y', 'tsx', tsScript, ...args], {
69
- stdio: 'inherit',
70
- shell: true
71
- });
72
-
73
- tsxProcess.on('close', (code) => {
74
- process.exit(code || 0);
75
- });
76
-
77
- tsxProcess.on('error', (err) => {
78
- console.error('āŒ Error: Could not run script');
79
- console.error('Please ensure you have Node.js (with npx) or bun installed');
80
- process.exit(1);
81
- });
82
- } else {
83
- console.error('āŒ Error: Could not find a TypeScript runner');
84
- console.error('');
85
- console.error('Please install one of the following:');
86
- console.error(' - bun: curl -fsSL https://bun.sh/install | bash (recommended)');
87
- console.error(' - Node.js (comes with npx)');
88
- process.exit(1);
89
- }
90
- }
91
-
92
- main();
93
-
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Wrapper script to run the TypeScript CLI
4
+ * Works with Node.js - no bun required!
5
+ */
6
+
7
+ import { spawn } from 'child_process';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, resolve } from 'path';
10
+ import { existsSync } from 'fs';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+
15
+ // Path to the TypeScript script - resolve relative to this wrapper
16
+ const tsScript = resolve(__dirname, 'cli-extract-types-auto.ts');
17
+
18
+ // Check if a command is available
19
+ function checkCommand(cmd) {
20
+ return new Promise((resolve) => {
21
+ const check = spawn(cmd, ['--version'], { stdio: 'ignore', shell: true });
22
+ check.on('close', (code) => {
23
+ resolve(code === 0);
24
+ });
25
+ check.on('error', () => {
26
+ resolve(false);
27
+ });
28
+ });
29
+ }
30
+
31
+ async function main() {
32
+ if (!existsSync(tsScript)) {
33
+ console.error(`āŒ Error: Script not found at ${tsScript}`);
34
+ process.exit(1);
35
+ }
36
+
37
+ const args = process.argv.slice(2);
38
+
39
+ // Check for bun first (faster and preferred if available)
40
+ const hasBun = await checkCommand('bun');
41
+
42
+ if (hasBun) {
43
+ // Use bun directly - it's faster and can run TypeScript natively
44
+ const bunProcess = spawn('bun', [tsScript, ...args], {
45
+ stdio: 'inherit',
46
+ shell: true
47
+ });
48
+
49
+ bunProcess.on('close', (code) => {
50
+ process.exit(code || 0);
51
+ });
52
+
53
+ bunProcess.on('error', (err) => {
54
+ // If bun fails, try tsx as fallback
55
+ tryTsxFallback(args);
56
+ });
57
+ } else {
58
+ // Fallback to tsx via npx if bun isn't available
59
+ tryTsxFallback(args);
60
+ }
61
+ }
62
+
63
+ async function tryTsxFallback(args) {
64
+ const hasNpx = await checkCommand('npx');
65
+
66
+ if (hasNpx) {
67
+ // Use tsx via npx - it's lightweight and works great with Node.js
68
+ const tsxProcess = spawn('npx', ['-y', 'tsx', tsScript, ...args], {
69
+ stdio: 'inherit',
70
+ shell: true
71
+ });
72
+
73
+ tsxProcess.on('close', (code) => {
74
+ process.exit(code || 0);
75
+ });
76
+
77
+ tsxProcess.on('error', (err) => {
78
+ console.error('āŒ Error: Could not run script');
79
+ console.error('Please ensure you have Node.js (with npx) or bun installed');
80
+ process.exit(1);
81
+ });
82
+ } else {
83
+ console.error('āŒ Error: Could not find a TypeScript runner');
84
+ console.error('');
85
+ console.error('Please install one of the following:');
86
+ console.error(' - bun: curl -fsSL https://bun.sh/install | bash (recommended)');
87
+ console.error(' - Node.js (comes with npx)');
88
+ process.exit(1);
89
+ }
90
+ }
91
+
92
+ main();
93
+
@@ -83,7 +83,6 @@ async function findToolUsageFiles(projectRoot: string): Promise<string[]> {
83
83
  ignore: ['**/node_modules/**', '**/dist/**', '**/.next/**', '**/build/**'],
84
84
  });
85
85
 
86
- console.log(`šŸ“‚ Found ${files.length} TypeScript files to scan`);
87
86
  return files;
88
87
  }
89
88
 
@@ -192,25 +191,10 @@ function extractToolNamesFromExpression(
192
191
  const callExpr = node.expression;
193
192
  if (ts.isIdentifier(callExpr)) {
194
193
  const funcName = callExpr.text;
195
- if (funcName === 'useMemo' && node.arguments.length > 0) {
196
- const firstArg = node.arguments[0];
197
- // Arrow function: () => [...]
198
- if (ts.isArrowFunction(firstArg) && firstArg.body) {
199
- if (ts.isBlock(firstArg.body)) {
200
- // Block body: () => { return [...] }
201
- for (const stmt of firstArg.body.statements) {
202
- if (ts.isReturnStatement(stmt) && stmt.expression) {
203
- extractFromExpr(stmt.expression);
204
- }
205
- }
206
- } else {
207
- // Expression body: () => [...]
208
- extractFromExpr(firstArg.body);
209
- }
210
- }
211
- // Regular function call: useMemo(fn, [])
212
- else if (ts.isFunctionExpression(firstArg) || ts.isArrowFunction(firstArg)) {
213
- // Can't easily extract from function body without more analysis
194
+ if (funcName === 'useMemo') {
195
+ const memoizedExpr = extractToolsFromUseMemo(node, sourceFile, variableMap);
196
+ if (memoizedExpr) {
197
+ extractFromExpr(memoizedExpr);
214
198
  }
215
199
  }
216
200
  }
@@ -230,6 +214,7 @@ function extractToolNamesFromExpression(
230
214
 
231
215
  /**
232
216
  * Build a map of variable declarations in the file
217
+ * Handles: const x = [...], const x = useMemo(...), etc.
233
218
  */
234
219
  function buildVariableMap(sourceFile: ts.SourceFile): Map<string, ts.Expression> {
235
220
  const variableMap = new Map<string, ts.Expression>();
@@ -258,6 +243,46 @@ function buildVariableMap(sourceFile: ts.SourceFile): Map<string, ts.Expression>
258
243
  return variableMap;
259
244
  }
260
245
 
246
+ /**
247
+ * Extract tools from useMemo callback, handling both arrow functions and regular functions
248
+ */
249
+ function extractToolsFromUseMemo(
250
+ useMemoCall: ts.CallExpression,
251
+ sourceFile: ts.SourceFile,
252
+ variableMap: Map<string, ts.Expression>
253
+ ): ts.Expression | null {
254
+ if (useMemoCall.arguments.length === 0) return null;
255
+
256
+ const firstArg = useMemoCall.arguments[0];
257
+
258
+ // Arrow function: () => [...]
259
+ if (ts.isArrowFunction(firstArg)) {
260
+ if (firstArg.body) {
261
+ if (ts.isBlock(firstArg.body)) {
262
+ // Block body: () => { return [...] }
263
+ for (const stmt of firstArg.body.statements) {
264
+ if (ts.isReturnStatement(stmt) && stmt.expression) {
265
+ return stmt.expression;
266
+ }
267
+ }
268
+ } else {
269
+ // Expression body: () => [...]
270
+ return firstArg.body;
271
+ }
272
+ }
273
+ }
274
+ // Regular function: function() { return [...] }
275
+ else if (ts.isFunctionExpression(firstArg) && firstArg.body) {
276
+ for (const stmt of firstArg.body.statements) {
277
+ if (ts.isReturnStatement(stmt) && stmt.expression) {
278
+ return stmt.expression;
279
+ }
280
+ }
281
+ }
282
+
283
+ return null;
284
+ }
285
+
261
286
  /**
262
287
  * Extract tool names from ArctenAgent or useAgent usage
263
288
  */
@@ -1356,10 +1381,11 @@ async function extractFunctionMetadata(
1356
1381
  * Main function
1357
1382
  */
1358
1383
  async function autoDiscoverAndExtract(projectRoot: string, outputPath: string) {
1359
- console.log(`\nšŸ” Auto-discovering tools in: ${projectRoot}\n`);
1384
+ console.log(`\nšŸ” Discovering tools in your project...\n`);
1360
1385
 
1361
1386
  // Find all TypeScript files
1362
1387
  const files = await findToolUsageFiles(projectRoot);
1388
+ console.log(`šŸ“‚ Scanning ${files.length} TypeScript file${files.length !== 1 ? 's' : ''}...`);
1363
1389
 
1364
1390
  // Read tsconfig
1365
1391
  const configPath = ts.findConfigFile(projectRoot, ts.sys.fileExists, 'tsconfig.json');
@@ -1394,19 +1420,34 @@ async function autoDiscoverAndExtract(projectRoot: string, outputPath: string) {
1394
1420
  }
1395
1421
  }
1396
1422
 
1397
- console.log(`āœ“ Found ${allToolUsages.length} usage site(s) with ${allToolNames.size} unique tool(s)`);
1398
- allToolUsages.forEach(usage => {
1399
- console.log(` - ${usage.component} in ${path.relative(projectRoot, usage.file)}`);
1400
- console.log(` Tools: ${Array.from(usage.toolNames).join(', ')}`);
1423
+ if (allToolUsages.length === 0) {
1424
+ console.log(`\nāš ļø No tool usages found in ArctenAgent or useAgent components`);
1425
+ console.log(`\nšŸ’” Make sure you're using tools like this:`);
1426
+ console.log(` <ArctenAgent safeTools={[getLocation, resetPassword]} />`);
1427
+ console.log(` or`);
1428
+ console.log(` useAgent({ safeTools: [getLocation, resetPassword] })\n`);
1429
+ return;
1430
+ }
1431
+
1432
+ console.log(`\n✨ Found ${allToolUsages.length} usage site${allToolUsages.length !== 1 ? 's' : ''} with ${allToolNames.size} unique tool${allToolNames.size !== 1 ? 's' : ''}:`);
1433
+ allToolUsages.forEach((usage, idx) => {
1434
+ const relativePath = path.relative(projectRoot, usage.file);
1435
+ const tools = Array.from(usage.toolNames);
1436
+ console.log(`\n ${idx + 1}. ${usage.component} in ${relativePath}`);
1437
+ console.log(` Tools: ${tools.map(t => `\x1b[36m${t}\x1b[0m`).join(', ')}`);
1401
1438
  });
1402
1439
 
1403
1440
  // Extract metadata for discovered tools
1404
- console.log(`\nšŸ“ Extracting types for discovered tools...\n`);
1441
+ console.log(`\nšŸ“ Extracting type metadata...\n`);
1405
1442
 
1406
1443
  const functionsMap: Record<string, FunctionMetadata> = {};
1407
1444
  const discoveredFrom: string[] = [];
1408
1445
 
1409
- for (const toolName of allToolNames) {
1446
+ const toolNamesArray = Array.from(allToolNames);
1447
+ let successCount = 0;
1448
+ let warningCount = 0;
1449
+
1450
+ for (const toolName of toolNamesArray) {
1410
1451
  let found = false;
1411
1452
 
1412
1453
  // Search in all files for the definition
@@ -1419,8 +1460,10 @@ async function autoDiscoverAndExtract(projectRoot: string, outputPath: string) {
1419
1460
  if (metadata) {
1420
1461
  functionsMap[toolName] = metadata;
1421
1462
  discoveredFrom.push(path.relative(projectRoot, result.sourceFile.fileName));
1422
- console.log(` āœ“ ${toolName} (from ${path.relative(projectRoot, result.sourceFile.fileName)})`);
1463
+ const relativePath = path.relative(projectRoot, result.sourceFile.fileName);
1464
+ console.log(` āœ“ \x1b[32m${toolName}\x1b[0m \x1b[90m(${relativePath})\x1b[0m`);
1423
1465
  found = true;
1466
+ successCount++;
1424
1467
  break;
1425
1468
  }
1426
1469
  }
@@ -1428,7 +1471,8 @@ async function autoDiscoverAndExtract(projectRoot: string, outputPath: string) {
1428
1471
  }
1429
1472
 
1430
1473
  if (!found) {
1431
- console.log(` ⚠ ${toolName} (definition not found)`);
1474
+ console.log(` ⚠ \x1b[33m${toolName}\x1b[0m \x1b[90m(definition not found)\x1b[0m`);
1475
+ warningCount++;
1432
1476
  }
1433
1477
  }
1434
1478
 
@@ -1479,11 +1523,24 @@ export const toolMetadata = ${JSON.stringify(output, null, 2)} as const;
1479
1523
  `;
1480
1524
 
1481
1525
  fs.writeFileSync(outputPath, tsContent);
1482
- console.log(`\nāœ… Generated metadata for ${Object.keys(functionsMap).length} tool(s)`);
1483
- console.log(`šŸ“ Output: ${outputPath}`);
1484
- console.log(`\nšŸ’” Usage in your component:`);
1485
- console.log(` import { toolMetadata } from './.arcten/tool-metadata';`);
1486
- console.log(` useAgent({ tools: [...], toolMetadata: toolMetadata.functions })\n`);
1526
+
1527
+ const relativeOutput = path.relative(projectRoot, outputPath);
1528
+
1529
+ console.log(`\n${'='.repeat(60)}`);
1530
+ if (successCount > 0) {
1531
+ console.log(`āœ… Successfully extracted ${successCount} tool${successCount !== 1 ? 's' : ''}`);
1532
+ }
1533
+ if (warningCount > 0) {
1534
+ console.log(`āš ļø ${warningCount} tool${warningCount !== 1 ? 's' : ''} not found (check function names)`);
1535
+ }
1536
+ console.log(`šŸ“ Output: \x1b[36m${relativeOutput}\x1b[0m`);
1537
+ console.log(`${'='.repeat(60)}\n`);
1538
+
1539
+ if (successCount > 0) {
1540
+ console.log(`šŸ’” Usage in your component:`);
1541
+ console.log(` import { toolMetadata } from './.arcten/tool-metadata';`);
1542
+ console.log(` useAgent({ tools: [...], toolMetadata: toolMetadata.functions })\n`);
1543
+ }
1487
1544
  }
1488
1545
 
1489
1546
  // CLI