@arcteninc/core 0.0.133 ā 0.0.135
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
|
@@ -1566,4 +1566,12 @@ if (import.meta.main) {
|
|
|
1566
1566
|
autoDiscoverAndExtract(projectRoot, outputPath);
|
|
1567
1567
|
}
|
|
1568
1568
|
|
|
1569
|
-
export {
|
|
1569
|
+
export {
|
|
1570
|
+
autoDiscoverAndExtract,
|
|
1571
|
+
findToolUsageFiles,
|
|
1572
|
+
extractToolNamesFromFile,
|
|
1573
|
+
findFunctionDefinition,
|
|
1574
|
+
extractFunctionMetadata,
|
|
1575
|
+
};
|
|
1576
|
+
|
|
1577
|
+
export type { FunctionMetadata, ToolUsage };
|
|
@@ -14,6 +14,7 @@ import * as fs from 'fs';
|
|
|
14
14
|
import * as path from 'path';
|
|
15
15
|
import { glob } from 'glob';
|
|
16
16
|
import * as readline from 'readline';
|
|
17
|
+
import { autoDiscoverAndExtract } from './cli-extract-types-auto.ts';
|
|
17
18
|
|
|
18
19
|
// Types
|
|
19
20
|
interface FunctionMetadata {
|
|
@@ -197,6 +198,19 @@ async function scanCodebase(): Promise<CodebaseContext> {
|
|
|
197
198
|
// Build file tree
|
|
198
199
|
const fileTree = buildFileTree(projectRoot);
|
|
199
200
|
|
|
201
|
+
// Debug: log context size
|
|
202
|
+
const contextStr = JSON.stringify({
|
|
203
|
+
fileTree,
|
|
204
|
+
framework,
|
|
205
|
+
dependencies,
|
|
206
|
+
functions,
|
|
207
|
+
stats: {
|
|
208
|
+
totalFunctions: functions.length,
|
|
209
|
+
skippedPaths: EXCLUDE_PATHS,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
console.log(`š Context size: ${(contextStr.length / 1024).toFixed(2)} KB (~${Math.round(contextStr.length / 4)} tokens)`);
|
|
213
|
+
|
|
200
214
|
return {
|
|
201
215
|
fileTree,
|
|
202
216
|
framework,
|
|
@@ -252,65 +266,58 @@ function getDependencies(projectRoot: string): Record<string, string> {
|
|
|
252
266
|
return { ...packageJson.dependencies };
|
|
253
267
|
}
|
|
254
268
|
|
|
255
|
-
// Helper: Scan for functions
|
|
269
|
+
// Helper: Scan for functions (reuses existing autoDiscoverAndExtract)
|
|
256
270
|
async function scanFunctions(projectRoot: string): Promise<FunctionMetadata[]> {
|
|
257
|
-
|
|
258
|
-
const files = await glob(pattern, {
|
|
259
|
-
ignore: EXCLUDE_PATHS.map(p => `**/${p}/**`),
|
|
260
|
-
});
|
|
271
|
+
console.log('š Using TypeScript AST to scan for exported functions...');
|
|
261
272
|
|
|
262
|
-
|
|
273
|
+
// Generate metadata file using existing extraction logic
|
|
274
|
+
const outputPath = path.join(projectRoot, '.arcten', 'tool-metadata.ts');
|
|
263
275
|
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
// Match exported functions
|
|
269
|
-
const functionRegex = /export\s+(async\s+)?function\s+(\w+)\s*\(([^)]*)\)/g;
|
|
270
|
-
let match;
|
|
271
|
-
|
|
272
|
-
while ((match = functionRegex.exec(content)) !== null) {
|
|
273
|
-
const [, isAsync, name, params] = match;
|
|
274
|
-
functions.push({
|
|
275
|
-
name,
|
|
276
|
-
file: path.relative(projectRoot, file),
|
|
277
|
-
params: parseParams(params),
|
|
278
|
-
jsDoc: extractJsDoc(content, match.index),
|
|
279
|
-
sourceCode: extractFunctionSource(content, match.index),
|
|
280
|
-
});
|
|
281
|
-
}
|
|
276
|
+
// Ensure .arcten directory exists
|
|
277
|
+
const arctenDir = path.dirname(outputPath);
|
|
278
|
+
if (!fs.existsSync(arctenDir)) {
|
|
279
|
+
fs.mkdirSync(arctenDir, { recursive: true });
|
|
282
280
|
}
|
|
283
281
|
|
|
284
|
-
|
|
285
|
-
|
|
282
|
+
// Run the auto-discovery
|
|
283
|
+
await autoDiscoverAndExtract(projectRoot, outputPath);
|
|
286
284
|
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
285
|
+
// Read the generated metadata
|
|
286
|
+
const content = fs.readFileSync(outputPath, 'utf-8');
|
|
287
|
+
const metadataMatch = content.match(/export const toolMetadata = ([\s\S]+) as const;/);
|
|
290
288
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
});
|
|
295
|
-
}
|
|
289
|
+
if (!metadataMatch) {
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
296
292
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
293
|
+
const metadata = JSON.parse(metadataMatch[1]);
|
|
294
|
+
const functions: FunctionMetadata[] = [];
|
|
295
|
+
|
|
296
|
+
// Convert to wizard format
|
|
297
|
+
for (const [name, fn] of Object.entries(metadata.functions as any)) {
|
|
298
|
+
// Find which file this function was discovered from
|
|
299
|
+
const fileIndex = metadata.discoveredFrom.findIndex((f: string) =>
|
|
300
|
+
content.includes(name)
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
functions.push({
|
|
304
|
+
name,
|
|
305
|
+
file: metadata.discoveredFrom[fileIndex] || metadata.discoveredFrom[0] || 'unknown',
|
|
306
|
+
params: Object.entries(fn.parameters?.properties || {}).map(([paramName, prop]: [string, any]) => ({
|
|
307
|
+
name: paramName,
|
|
308
|
+
type: prop.type || 'any',
|
|
309
|
+
})),
|
|
310
|
+
returnType: fn.returnType,
|
|
311
|
+
jsDoc: fn.description,
|
|
312
|
+
sourceCode: `${name}(${Object.keys(fn.parameters?.properties || {}).join(', ')})`,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
303
315
|
|
|
304
|
-
|
|
305
|
-
function extractFunctionSource(content: string, index: number): string {
|
|
306
|
-
const remaining = content.substring(index);
|
|
307
|
-
const endMatch = remaining.match(/\n\}/);
|
|
308
|
-
const endIndex = endMatch ? endMatch.index! + 2 : Math.min(1000, remaining.length);
|
|
309
|
-
return remaining.substring(0, endIndex);
|
|
316
|
+
return functions;
|
|
310
317
|
}
|
|
311
318
|
|
|
312
|
-
// Helper: Build file tree
|
|
313
|
-
function buildFileTree(projectRoot: string, depth: number = 0, maxDepth: number =
|
|
319
|
+
// Helper: Build file tree (limited depth to reduce tokens)
|
|
320
|
+
function buildFileTree(projectRoot: string, depth: number = 0, maxDepth: number = 2): string {
|
|
314
321
|
if (depth >= maxDepth) return '';
|
|
315
322
|
|
|
316
323
|
const entries = fs.readdirSync(projectRoot, { withFileTypes: true });
|
|
@@ -332,6 +339,53 @@ function buildFileTree(projectRoot: string, depth: number = 0, maxDepth: number
|
|
|
332
339
|
return tree;
|
|
333
340
|
}
|
|
334
341
|
|
|
342
|
+
// Step 2.5: Handle missing descriptions
|
|
343
|
+
async function handleMissingDescriptions(
|
|
344
|
+
rl: readline.Interface,
|
|
345
|
+
functions: FunctionMetadata[]
|
|
346
|
+
): Promise<FunctionMetadata[]> {
|
|
347
|
+
// Find functions with generic "Execute X" descriptions
|
|
348
|
+
const missingDescriptions = functions.filter(fn =>
|
|
349
|
+
!fn.jsDoc || fn.jsDoc.startsWith('Execute ')
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
if (missingDescriptions.length === 0) {
|
|
353
|
+
return functions;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
console.log(`\nā ļø ${missingDescriptions.length} function${missingDescriptions.length > 1 ? 's are' : ' is'} missing JSDoc descriptions\n`);
|
|
357
|
+
console.log('How would you like to handle these?');
|
|
358
|
+
console.log(' 1. Edit descriptions now (one by one)');
|
|
359
|
+
console.log(' 2. Skip - I\'ll add JSDoc comments manually');
|
|
360
|
+
console.log(' 3. Skip - I\'ll edit in dashboard later');
|
|
361
|
+
|
|
362
|
+
const choice = await ask(rl, '\n? Choose option (1-3): ');
|
|
363
|
+
|
|
364
|
+
if (choice === '1') {
|
|
365
|
+
// Edit descriptions one by one
|
|
366
|
+
console.log('');
|
|
367
|
+
for (const fn of missingDescriptions) {
|
|
368
|
+
console.log(`š ${fn.name} (${fn.file})`);
|
|
369
|
+
console.log(` Current: ${fn.jsDoc || `Execute ${fn.name}`}\n`);
|
|
370
|
+
|
|
371
|
+
const newDesc = await ask(rl, ' Enter description (or press Enter to skip): ');
|
|
372
|
+
|
|
373
|
+
if (newDesc.trim()) {
|
|
374
|
+
fn.jsDoc = newDesc.trim();
|
|
375
|
+
console.log(` ā Updated description for ${fn.name}\n`);
|
|
376
|
+
} else {
|
|
377
|
+
console.log(` āļø Skipped ${fn.name}\n`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
console.log('ā
Description editing complete\n');
|
|
381
|
+
} else {
|
|
382
|
+
console.log('\nāļø Skipping description editing\n');
|
|
383
|
+
console.log('š” You can add JSDoc comments to your code or edit descriptions in the dashboard.');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return functions;
|
|
387
|
+
}
|
|
388
|
+
|
|
335
389
|
// Step 3: Get user intent
|
|
336
390
|
async function getUserIntent(rl: readline.Interface): Promise<string> {
|
|
337
391
|
console.log('\nš Step 3: Define your agent\'s purpose\n');
|
|
@@ -352,6 +406,8 @@ async function getUserIntent(rl: readline.Interface): Promise<string> {
|
|
|
352
406
|
|
|
353
407
|
// Helper: Get JWT token from API key
|
|
354
408
|
async function getAuthToken(apiKey: string, serverUrl: string): Promise<string> {
|
|
409
|
+
console.log(`š Getting auth token from ${serverUrl}/token...`);
|
|
410
|
+
|
|
355
411
|
try {
|
|
356
412
|
const response = await fetch(`${serverUrl}/token`, {
|
|
357
413
|
method: 'POST',
|
|
@@ -361,13 +417,23 @@ async function getAuthToken(apiKey: string, serverUrl: string): Promise<string>
|
|
|
361
417
|
body: JSON.stringify({ apiKey }),
|
|
362
418
|
});
|
|
363
419
|
|
|
420
|
+
console.log(`š Token response status: ${response.status}`);
|
|
421
|
+
|
|
364
422
|
if (!response.ok) {
|
|
365
423
|
const error = await response.json();
|
|
424
|
+
console.error(`š Token error:`, error);
|
|
366
425
|
throw new Error(error.error || 'Failed to authenticate');
|
|
367
426
|
}
|
|
368
427
|
|
|
369
428
|
const data = await response.json();
|
|
370
|
-
|
|
429
|
+
const token = data.clientToken || data.token; // Server returns "clientToken"
|
|
430
|
+
console.log(`š Got token: ${token ? token.substring(0, 20) + '...' : 'NO TOKEN'}`);
|
|
431
|
+
|
|
432
|
+
if (!token) {
|
|
433
|
+
throw new Error('No token received from server');
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return token;
|
|
371
437
|
} catch (error: any) {
|
|
372
438
|
if (error.cause?.code === 'ENOTFOUND' || error.cause?.code === 'ECONNREFUSED' || error.message.includes('fetch')) {
|
|
373
439
|
console.error(`\nā Unable to connect to: ${serverUrl}`);
|
|
@@ -453,6 +519,8 @@ async function runAnalysis(
|
|
|
453
519
|
|
|
454
520
|
// Get JWT token from API key
|
|
455
521
|
const token = await getAuthToken(apiKey, serverUrl);
|
|
522
|
+
console.log(`š” Using token for analysis: ${token ? token.substring(0, 20) + '...' : 'NO TOKEN'}`);
|
|
523
|
+
console.log(`š” Calling: ${serverUrl}/tools/analyze`);
|
|
456
524
|
|
|
457
525
|
const response = await fetch(`${serverUrl}/tools/analyze`, {
|
|
458
526
|
method: 'POST',
|
|
@@ -463,15 +531,26 @@ async function runAnalysis(
|
|
|
463
531
|
body: JSON.stringify({ context, userIntent }),
|
|
464
532
|
});
|
|
465
533
|
|
|
534
|
+
console.log(`š” Analysis response status: ${response.status}`);
|
|
535
|
+
|
|
466
536
|
if (!response.ok) {
|
|
467
537
|
const error = await response.json();
|
|
538
|
+
console.error(`š” Analysis error response:`, error);
|
|
468
539
|
throw new Error(error.error || 'Analysis failed');
|
|
469
540
|
}
|
|
470
541
|
|
|
471
542
|
const result = await response.json();
|
|
543
|
+
console.log(`š” Analysis result:`, JSON.stringify(result).substring(0, 200));
|
|
544
|
+
|
|
545
|
+
if (!result || !result.success) {
|
|
546
|
+
throw new Error(result?.error || 'Analysis failed - invalid response');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const tokensUsed = result.tokensUsed || 0;
|
|
550
|
+
const actualCost = result.stats?.actualCost || 0;
|
|
472
551
|
|
|
473
|
-
console.log(`ā Analysis complete - ${
|
|
474
|
-
console.log(` Actual cost: $${
|
|
552
|
+
console.log(`ā Analysis complete - ${tokensUsed.toLocaleString()} tokens used`);
|
|
553
|
+
console.log(` Actual cost: $${actualCost.toFixed(2)}`);
|
|
475
554
|
|
|
476
555
|
return result;
|
|
477
556
|
}
|
|
@@ -479,9 +558,10 @@ async function runAnalysis(
|
|
|
479
558
|
// Step 7: Sync to dashboard
|
|
480
559
|
async function syncToDashboard(
|
|
481
560
|
apiKey: string,
|
|
482
|
-
|
|
561
|
+
serverUrl: string,
|
|
483
562
|
projectId: string,
|
|
484
|
-
tools: ToolRecommendation[]
|
|
563
|
+
tools: ToolRecommendation[],
|
|
564
|
+
agents?: AgentConfig[]
|
|
485
565
|
): Promise<void> {
|
|
486
566
|
console.log('\nāļø Step 7: Syncing to dashboard...\n');
|
|
487
567
|
|
|
@@ -498,41 +578,58 @@ async function syncToDashboard(
|
|
|
498
578
|
sensitiveParams: tool.sensitiveParams || [],
|
|
499
579
|
}));
|
|
500
580
|
|
|
581
|
+
// Convert agents to the format expected by bulkUpsert
|
|
582
|
+
const agentsData = agents?.map(agent => ({
|
|
583
|
+
name: agent.name,
|
|
584
|
+
systemPrompt: agent.systemPrompt,
|
|
585
|
+
enabledTools: agent.enabledTools,
|
|
586
|
+
description: agent.description || `Agent: ${agent.name}`,
|
|
587
|
+
})) || [];
|
|
588
|
+
|
|
501
589
|
try {
|
|
502
|
-
//
|
|
503
|
-
const
|
|
590
|
+
// Get JWT token
|
|
591
|
+
const token = await getAuthToken(apiKey, serverUrl);
|
|
592
|
+
|
|
593
|
+
// Call server endpoint which will handle Convex mutation
|
|
594
|
+
const response = await fetch(`${serverUrl}/tools/sync`, {
|
|
504
595
|
method: 'POST',
|
|
505
596
|
headers: {
|
|
506
597
|
'Content-Type': 'application/json',
|
|
507
|
-
'Authorization': `Bearer ${
|
|
598
|
+
'Authorization': `Bearer ${token}`,
|
|
508
599
|
},
|
|
509
600
|
body: JSON.stringify({
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
601
|
+
projectId,
|
|
602
|
+
tools: toolsData,
|
|
603
|
+
agents: agentsData,
|
|
513
604
|
}),
|
|
514
605
|
});
|
|
515
606
|
|
|
516
607
|
if (!response.ok) {
|
|
517
608
|
const errorText = await response.text().catch(() => 'Unknown error');
|
|
518
|
-
throw new Error(`Failed to sync
|
|
609
|
+
throw new Error(`Failed to sync: ${response.status} ${errorText}`);
|
|
519
610
|
}
|
|
520
611
|
|
|
521
612
|
const result = await response.json();
|
|
522
613
|
|
|
523
614
|
if (!result.success) {
|
|
524
|
-
throw new Error(result.error || 'Failed to sync
|
|
615
|
+
throw new Error(result.error || 'Failed to sync to dashboard');
|
|
525
616
|
}
|
|
526
617
|
|
|
527
|
-
console.log(`ā
Synced ${tools.length} tools to dashboard`);
|
|
618
|
+
console.log(`ā
Synced ${tools.length} tools${agents && agents.length > 0 ? ` and ${agents.length} agents` : ''} to dashboard`);
|
|
528
619
|
|
|
529
620
|
if (result.upserted) {
|
|
530
621
|
const created = result.upserted.filter((t: any) => t.created).length;
|
|
531
622
|
const updated = result.upserted.filter((t: any) => !t.created).length;
|
|
532
|
-
console.log(` š Created: ${created} | Updated: ${updated}`);
|
|
623
|
+
console.log(` š Tools - Created: ${created} | Updated: ${updated}`);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (result.agents && agents && agents.length > 0) {
|
|
627
|
+
const agentCreated = result.agents.filter((a: any) => a.created).length;
|
|
628
|
+
const agentUpdated = result.agents.filter((a: any) => !a.created).length;
|
|
629
|
+
console.log(` š Agents - Created: ${agentCreated} | Updated: ${agentUpdated}`);
|
|
533
630
|
}
|
|
534
631
|
} catch (error: any) {
|
|
535
|
-
console.error(`ā Failed to sync
|
|
632
|
+
console.error(`ā Failed to sync: ${error.message}`);
|
|
536
633
|
throw error;
|
|
537
634
|
}
|
|
538
635
|
}
|
|
@@ -699,7 +796,6 @@ async function main() {
|
|
|
699
796
|
|
|
700
797
|
const rl = createPrompt();
|
|
701
798
|
const serverUrl = process.env.ARCTEN_SERVER_URL || 'https://api.arcten.com';
|
|
702
|
-
const convexUrl = process.env.CONVEX_URL || 'https://convex.arcten.com';
|
|
703
799
|
|
|
704
800
|
try {
|
|
705
801
|
// Check for existing setup
|
|
@@ -731,6 +827,11 @@ async function main() {
|
|
|
731
827
|
// Step 2: Scan codebase
|
|
732
828
|
const context = await scanCodebase();
|
|
733
829
|
|
|
830
|
+
// Step 2.5: Handle missing descriptions (only in full wizard mode, not sync/allow-all)
|
|
831
|
+
if (!skipAnalysis && !allowAll) {
|
|
832
|
+
context.functions = await handleMissingDescriptions(rl, context.functions);
|
|
833
|
+
}
|
|
834
|
+
|
|
734
835
|
let analysis: AnalysisResult | null = null;
|
|
735
836
|
|
|
736
837
|
if (allowAll) {
|
|
@@ -749,7 +850,7 @@ async function main() {
|
|
|
749
850
|
sensitiveParams: [],
|
|
750
851
|
}));
|
|
751
852
|
|
|
752
|
-
await syncToDashboard(apiKey,
|
|
853
|
+
await syncToDashboard(apiKey, serverUrl, projectId, allTools);
|
|
753
854
|
await generateLocalConfig(projectId, 'default');
|
|
754
855
|
|
|
755
856
|
console.log('\nā
Setup complete!\n');
|
|
@@ -770,7 +871,7 @@ async function main() {
|
|
|
770
871
|
sensitiveParams: [],
|
|
771
872
|
}));
|
|
772
873
|
|
|
773
|
-
await syncToDashboard(apiKey,
|
|
874
|
+
await syncToDashboard(apiKey, serverUrl, projectId, basicTools);
|
|
774
875
|
await generateLocalConfig(projectId, 'default');
|
|
775
876
|
|
|
776
877
|
console.log('\nā
Sync complete!\n');
|
|
@@ -798,7 +899,7 @@ async function main() {
|
|
|
798
899
|
console.log(` Placements suggested: ${analysis.suggestedPlacements.length}`);
|
|
799
900
|
|
|
800
901
|
// Step 7: Sync to dashboard
|
|
801
|
-
await syncToDashboard(apiKey,
|
|
902
|
+
await syncToDashboard(apiKey, serverUrl, projectId, analysis.recommendations, analysis.suggestedAgents);
|
|
802
903
|
|
|
803
904
|
// Step 7.5: Display placement suggestions
|
|
804
905
|
await displayPlacementSuggestions(analysis.suggestedPlacements);
|
|
@@ -816,7 +917,7 @@ async function main() {
|
|
|
816
917
|
sensitiveParams: [],
|
|
817
918
|
}));
|
|
818
919
|
|
|
819
|
-
await syncToDashboard(apiKey,
|
|
920
|
+
await syncToDashboard(apiKey, serverUrl, projectId, basicTools);
|
|
820
921
|
await generateLocalConfig(projectId, 'default');
|
|
821
922
|
}
|
|
822
923
|
|