@arcteninc/core 0.0.175 → 0.0.177

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 (103) hide show
  1. package/README.md +73 -78
  2. package/dist/index.cjs +3 -16
  3. package/dist/index.d.ts +1 -6
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.mjs +856 -8408
  6. package/dist/lib/useAgent.d.ts +1 -2
  7. package/dist/lib/useAgent.d.ts.map +1 -1
  8. package/dist/types/use-agent.d.ts +3 -44
  9. package/dist/types/use-agent.d.ts.map +1 -1
  10. package/dist/utils/extract-tool-metadata.d.ts.map +1 -1
  11. package/package.json +7 -46
  12. package/scripts/arcten-cli.cjs +14 -108
  13. package/scripts/cli-extract-types-auto.ts +22 -4
  14. package/scripts/update-core.cjs +124 -0
  15. package/dist/components/ArctenAgent.d.ts +0 -52
  16. package/dist/components/ArctenAgent.d.ts.map +0 -1
  17. package/dist/components/ai-elements/prompt-input.d.ts +0 -187
  18. package/dist/components/ai-elements/prompt-input.d.ts.map +0 -1
  19. package/dist/components/ai-elements/reasoning.d.ts +0 -17
  20. package/dist/components/ai-elements/reasoning.d.ts.map +0 -1
  21. package/dist/components/ai-elements/response.d.ts +0 -8
  22. package/dist/components/ai-elements/response.d.ts.map +0 -1
  23. package/dist/components/ai-elements/shimmer.d.ts +0 -10
  24. package/dist/components/ai-elements/shimmer.d.ts.map +0 -1
  25. package/dist/components/citation-button.d.ts +0 -14
  26. package/dist/components/citation-button.d.ts.map +0 -1
  27. package/dist/components/citation-text-renderer.d.ts +0 -31
  28. package/dist/components/citation-text-renderer.d.ts.map +0 -1
  29. package/dist/components/secure-modals/EmailModal.d.ts +0 -10
  30. package/dist/components/secure-modals/EmailModal.d.ts.map +0 -1
  31. package/dist/components/secure-modals/FormModal.d.ts +0 -20
  32. package/dist/components/secure-modals/FormModal.d.ts.map +0 -1
  33. package/dist/components/secure-modals/PasswordModal.d.ts +0 -10
  34. package/dist/components/secure-modals/PasswordModal.d.ts.map +0 -1
  35. package/dist/components/secure-modals/PhoneModal.d.ts +0 -10
  36. package/dist/components/secure-modals/PhoneModal.d.ts.map +0 -1
  37. package/dist/components/secure-modals/PinModal.d.ts +0 -11
  38. package/dist/components/secure-modals/PinModal.d.ts.map +0 -1
  39. package/dist/components/secure-modals/SecureModalProvider.d.ts +0 -13
  40. package/dist/components/secure-modals/SecureModalProvider.d.ts.map +0 -1
  41. package/dist/components/secure-modals/TextModal.d.ts +0 -11
  42. package/dist/components/secure-modals/TextModal.d.ts.map +0 -1
  43. package/dist/components/secure-modals/index.d.ts +0 -10
  44. package/dist/components/secure-modals/index.d.ts.map +0 -1
  45. package/dist/components/secure-modals/types.d.ts +0 -34
  46. package/dist/components/secure-modals/types.d.ts.map +0 -1
  47. package/dist/components/tool-call-approval.d.ts +0 -9
  48. package/dist/components/tool-call-approval.d.ts.map +0 -1
  49. package/dist/components/tool-call-result.d.ts +0 -8
  50. package/dist/components/tool-call-result.d.ts.map +0 -1
  51. package/dist/components/ui/autotextarea.d.ts +0 -19
  52. package/dist/components/ui/autotextarea.d.ts.map +0 -1
  53. package/dist/components/ui/badge.d.ts +0 -10
  54. package/dist/components/ui/badge.d.ts.map +0 -1
  55. package/dist/components/ui/button.d.ts +0 -14
  56. package/dist/components/ui/button.d.ts.map +0 -1
  57. package/dist/components/ui/collapsible.d.ts +0 -6
  58. package/dist/components/ui/collapsible.d.ts.map +0 -1
  59. package/dist/components/ui/command.d.ts +0 -19
  60. package/dist/components/ui/command.d.ts.map +0 -1
  61. package/dist/components/ui/dialog.d.ts +0 -16
  62. package/dist/components/ui/dialog.d.ts.map +0 -1
  63. package/dist/components/ui/dropdown-menu.d.ts +0 -26
  64. package/dist/components/ui/dropdown-menu.d.ts.map +0 -1
  65. package/dist/components/ui/hover-card.d.ts +0 -7
  66. package/dist/components/ui/hover-card.d.ts.map +0 -1
  67. package/dist/components/ui/input-group.d.ts +0 -17
  68. package/dist/components/ui/input-group.d.ts.map +0 -1
  69. package/dist/components/ui/input.d.ts +0 -4
  70. package/dist/components/ui/input.d.ts.map +0 -1
  71. package/dist/components/ui/kbd.d.ts +0 -4
  72. package/dist/components/ui/kbd.d.ts.map +0 -1
  73. package/dist/components/ui/select.d.ts +0 -16
  74. package/dist/components/ui/select.d.ts.map +0 -1
  75. package/dist/components/ui/textarea.d.ts +0 -4
  76. package/dist/components/ui/textarea.d.ts.map +0 -1
  77. package/dist/components/ui/tooltip.d.ts +0 -8
  78. package/dist/components/ui/tooltip.d.ts.map +0 -1
  79. package/dist/core.css +0 -1
  80. package/dist/utils/form-generator.d.ts +0 -29
  81. package/dist/utils/form-generator.d.ts.map +0 -1
  82. package/dist/utils/secure-param-detector.d.ts +0 -26
  83. package/dist/utils/secure-param-detector.d.ts.map +0 -1
  84. package/scripts/cli-agent-inject.ts +0 -205
  85. package/scripts/cli-agent-wrapper.cjs +0 -51
  86. package/scripts/cli-agent.ts +0 -483
  87. package/scripts/cli-create-project.ts +0 -608
  88. package/scripts/cli-create-wrapper.cjs +0 -60
  89. package/scripts/cli-init-wizard-wrapper.cjs +0 -58
  90. package/scripts/cli-init-wizard.ts +0 -646
  91. package/scripts/cli-prompt-wrapper.cjs +0 -51
  92. package/scripts/cli-prompt.ts +0 -306
  93. package/scripts/cli-sync-wrapper.cjs +0 -69
  94. package/scripts/cli-sync.ts +0 -915
  95. package/scripts/cli-tools-wrapper.cjs +0 -51
  96. package/scripts/cli-tools.ts +0 -320
  97. package/scripts/cli-uninstall-wrapper.cjs +0 -66
  98. package/scripts/cli-uninstall.ts +0 -173
  99. package/scripts/config-parser.ts +0 -432
  100. package/scripts/dashboard-sync.ts +0 -454
  101. package/scripts/tree-sitter-discover.ts +0 -526
  102. package/scripts/wasm/tree-sitter-tsx.wasm +0 -0
  103. package/scripts/wasm/tree-sitter-typescript.wasm +0 -0
@@ -1,58 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Wrapper script to execute cli-init-wizard.ts
4
- * Tries to use bun/tsx for TypeScript execution
5
- */
6
-
7
- const { spawn } = require('child_process');
8
- const path = require('path');
9
- const fs = require('fs');
10
-
11
- const scriptPath = path.join(__dirname, 'cli-init-wizard.ts');
12
-
13
- if (!fs.existsSync(scriptPath)) {
14
- console.error(`❌ Script not found: ${scriptPath}`);
15
- process.exit(1);
16
- }
17
-
18
- // Try tsx first (most compatible), then ts-node, then bun
19
- // Note: bun has issues with native modules on Windows
20
- const runners = [
21
- { cmd: 'tsx', args: [scriptPath] },
22
- { cmd: 'npx', args: ['tsx', scriptPath] },
23
- { cmd: 'ts-node', args: [scriptPath] },
24
- { cmd: 'bun', args: [scriptPath] },
25
- ];
26
-
27
- function tryRunner(index) {
28
- if (index >= runners.length) {
29
- console.error('❌ No TypeScript runner found. Please install one:');
30
- console.error(' bun: https://bun.sh');
31
- console.error(' tsx: npm install -g tsx');
32
- console.error(' ts-node: npm install -g ts-node');
33
- process.exit(1);
34
- }
35
-
36
- const runner = runners[index];
37
- const child = spawn(runner.cmd, runner.args, {
38
- stdio: 'inherit',
39
- cwd: process.cwd(),
40
- shell: true,
41
- });
42
-
43
- child.on('error', (error) => {
44
- if (error.code === 'ENOENT') {
45
- // Runner not found, try next one
46
- tryRunner(index + 1);
47
- } else {
48
- console.error(`❌ Error running ${runner.cmd}:`, error.message);
49
- process.exit(1);
50
- }
51
- });
52
-
53
- child.on('exit', (code) => {
54
- process.exit(code || 0);
55
- });
56
- }
57
-
58
- tryRunner(0);
@@ -1,646 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Arcten Init - Complete End-to-End Setup
4
- *
5
- * Creates a FULLY WORKING setup with zero manual steps:
6
- * 1. Deterministic discovery (tree-sitter scan)
7
- * 2. Deterministic classification (AI API + pattern fallback)
8
- * 3. Placement menu (user choice)
9
- * 4. Deterministic file creation (templates)
10
- * 5. AI review (checks generated files)
11
- * 6. Run sync (generates arcten.tools.ts)
12
- */
13
-
14
- import * as fs from 'fs';
15
- import * as path from 'path';
16
- import * as readline from 'readline';
17
- import { execSync } from 'child_process';
18
- import { discoverFunctionsInDirectory, filterLikelyTools, type DiscoveredFunction } from './tree-sitter-discover.js';
19
- import { runInjectionAgent, applyAllModifications, type FileModification } from './cli-agent-inject.js';
20
-
21
- // Types
22
- interface InitContext {
23
- projectRoot: string;
24
- framework: 'nextjs-app' | 'nextjs-pages' | 'vite' | 'unknown';
25
- toolsFile: string;
26
- toolsFileRelative: string;
27
- functions: DiscoveredFunction[];
28
- apiKey: string;
29
- projectId: string;
30
- serverUrl: string;
31
- }
32
-
33
- interface GeneratedPrompt {
34
- systemPrompt: string;
35
- source: 'ai' | 'template';
36
- }
37
-
38
- interface Classification {
39
- safe: string[];
40
- sensitive: string[];
41
- source: 'ai' | 'pattern-fallback';
42
- }
43
-
44
- type Placement = 'everywhere' | 'specific' | 'developer';
45
-
46
- interface InitResult {
47
- context: InitContext;
48
- classification: Classification;
49
- placement: Placement;
50
- specificPages?: string[];
51
- }
52
-
53
- // State
54
- let rl: readline.Interface;
55
-
56
- // ============================================================================
57
- // PHASE 1: Deterministic Discovery
58
- // ============================================================================
59
-
60
- async function discoverContext(): Promise<InitContext> {
61
- const projectRoot = process.cwd();
62
-
63
- console.log('Scanning your project...\n');
64
-
65
- // Detect framework
66
- const packageJsonPath = path.join(projectRoot, 'package.json');
67
- let framework: InitContext['framework'] = 'unknown';
68
-
69
- if (fs.existsSync(packageJsonPath)) {
70
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
71
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
72
- if (deps.next) {
73
- const hasAppDir = fs.existsSync(path.join(projectRoot, 'app'));
74
- framework = hasAppDir ? 'nextjs-app' : 'nextjs-pages';
75
- } else if (deps.vite) {
76
- framework = 'vite';
77
- }
78
- }
79
-
80
- // Discover functions
81
- let functions = await discoverFunctionsInDirectory(projectRoot, {
82
- functionsOnly: true,
83
- exportedOnly: true,
84
- });
85
-
86
- functions = filterLikelyTools(functions);
87
-
88
- if (functions.length === 0) {
89
- console.log('No exported functions found in your project.');
90
- console.log('');
91
- console.log('Create a tools file (e.g., app/tools.ts) with exported functions:');
92
- console.log('');
93
- console.log(' export function getOrders(status: string) { ... }');
94
- console.log(' export function createOrder(data: OrderData) { ... }');
95
- console.log('');
96
- process.exit(1);
97
- }
98
-
99
- // Find the primary tools file (most functions)
100
- const fileGroups = new Map<string, DiscoveredFunction[]>();
101
- for (const fn of functions) {
102
- const file = fn.file;
103
- if (!fileGroups.has(file)) {
104
- fileGroups.set(file, []);
105
- }
106
- fileGroups.get(file)!.push(fn);
107
- }
108
-
109
- // Sort by function count and pick the best
110
- const sortedFiles = [...fileGroups.entries()].sort((a, b) => b[1].length - a[1].length);
111
- const [toolsFile, toolFunctions] = sortedFiles[0];
112
-
113
- const toolsFileRelative = path.relative(projectRoot, toolsFile);
114
-
115
- // Check for API key
116
- const auth = checkApiKey(projectRoot);
117
- if (!auth) {
118
- console.log('No API key found.');
119
- console.log('');
120
- console.log('Get your API key from: https://arcten.com/dashboard');
121
- console.log('');
122
-
123
- const apiKeyInput = await askQuestion('Paste your API key: ');
124
- const projectIdMatch = apiKeyInput.match(/sk_(proj_[a-zA-Z0-9]+)_/);
125
-
126
- if (!projectIdMatch) {
127
- console.log('Invalid API key format. Expected: sk_proj_xxxxx_yyyyy');
128
- process.exit(1);
129
- }
130
-
131
- // Save to .env.local
132
- const envPath = path.join(projectRoot, '.env.local');
133
- let envContent = '';
134
- if (fs.existsSync(envPath)) {
135
- envContent = fs.readFileSync(envPath, 'utf-8');
136
- }
137
- if (!envContent.includes('ARCTEN_API_KEY')) {
138
- envContent += `\nARCTEN_API_KEY=${apiKeyInput}\n`;
139
- // Use correct env prefix based on framework
140
- if (framework === 'vite') {
141
- envContent += `VITE_ARCTEN_API_URL=https://api.arcten.com\n`;
142
- } else {
143
- envContent += `NEXT_PUBLIC_ARCTEN_API_URL=https://api.arcten.com\n`;
144
- }
145
- fs.writeFileSync(envPath, envContent.trim() + '\n');
146
- console.log(' Saved API key to .env.local');
147
- }
148
-
149
- return {
150
- projectRoot,
151
- framework,
152
- toolsFile,
153
- toolsFileRelative,
154
- functions: toolFunctions,
155
- apiKey: apiKeyInput,
156
- projectId: projectIdMatch[1],
157
- serverUrl: process.env.ARCTEN_SERVER_URL || 'https://api.arcten.com',
158
- };
159
- }
160
-
161
- console.log(`Found ${framework === 'nextjs-app' ? 'Next.js App Router' : framework} project`);
162
- console.log(`Found ${toolFunctions.length} functions in ${toolsFileRelative}`);
163
- console.log('');
164
-
165
- return {
166
- projectRoot,
167
- framework,
168
- toolsFile,
169
- toolsFileRelative,
170
- functions: toolFunctions,
171
- apiKey: auth.apiKey,
172
- projectId: auth.projectId,
173
- serverUrl: process.env.ARCTEN_SERVER_URL || 'https://api.arcten.com',
174
- };
175
- }
176
-
177
- // ============================================================================
178
- // PHASE 2: Deterministic Classification
179
- // ============================================================================
180
-
181
- async function classifyTools(context: InitContext): Promise<Classification> {
182
- console.log('Classifying tools...');
183
-
184
- const toolsToClassify = context.functions.map(f => ({
185
- name: f.name,
186
- jsDoc: f.jsDocComment,
187
- }));
188
-
189
- // Try AI classification first
190
- try {
191
- const response = await fetch(`${context.serverUrl}/ai-init/classify`, {
192
- method: 'POST',
193
- headers: {
194
- 'Content-Type': 'application/json',
195
- 'x-arcten-api-key': context.apiKey,
196
- },
197
- body: JSON.stringify({ tools: toolsToClassify }),
198
- });
199
-
200
- if (response.ok) {
201
- const result = await response.json() as {
202
- classifications: Record<string, 'safe' | 'sensitive'>;
203
- safe: string[];
204
- sensitive: string[];
205
- };
206
-
207
- console.log(` ${result.safe.length} safe (auto-execute)`);
208
- console.log(` ${result.sensitive.length} sensitive (needs approval)`);
209
- console.log('');
210
-
211
- return {
212
- safe: result.safe,
213
- sensitive: result.sensitive,
214
- source: 'ai',
215
- };
216
- }
217
- } catch {
218
- // Fall through to pattern-based
219
- }
220
-
221
- // Pattern-based fallback
222
- const safePatterns = /^(get|list|search|find|fetch|load|calculate|count)/i;
223
- const sensitivePatterns = /^(create|update|delete|set|add|remove|regenerate)/i;
224
- const sensitiveReads = /^(get|fetch).*(api.?key|credential|secret|password|token)/i;
225
-
226
- const safe: string[] = [];
227
- const sensitive: string[] = [];
228
-
229
- for (const tool of toolsToClassify) {
230
- if (sensitiveReads.test(tool.name)) {
231
- sensitive.push(tool.name);
232
- } else if (sensitivePatterns.test(tool.name)) {
233
- sensitive.push(tool.name);
234
- } else if (safePatterns.test(tool.name)) {
235
- safe.push(tool.name);
236
- } else {
237
- sensitive.push(tool.name); // Default to sensitive
238
- }
239
- }
240
-
241
- console.log(` ${safe.length} safe (auto-execute)`);
242
- console.log(` ${sensitive.length} sensitive (needs approval)`);
243
- console.log('');
244
-
245
- return { safe, sensitive, source: 'pattern-fallback' };
246
- }
247
-
248
- // ============================================================================
249
- // PHASE 2.5: AI Prompt Generation
250
- // ============================================================================
251
-
252
- const DEFAULT_PROMPT_TEMPLATE = `# AI Assistant
253
-
254
- You are a helpful AI assistant for this application.
255
-
256
- ## Your Role
257
- - Help users accomplish their tasks efficiently
258
- - Use the available tools when appropriate
259
- - Ask for clarification when needed
260
-
261
- ## Guidelines
262
- - Be concise and helpful
263
- - Explain what you're doing when using tools
264
- - Handle errors gracefully and suggest alternatives
265
-
266
- ## Available Tools
267
- The tools available to you are defined in the codebase and synced via \`arcten sync\`.
268
- Safe tools execute automatically. Sensitive tools require user approval.
269
- `;
270
-
271
- async function generateSystemPrompt(
272
- context: InitContext,
273
- classification: Classification
274
- ): Promise<GeneratedPrompt> {
275
- console.log('Generating system prompt...');
276
-
277
- const toolDescriptions = context.functions.map(f => ({
278
- name: f.name,
279
- jsDoc: f.jsDocComment,
280
- classification: classification.safe.includes(f.name) ? 'safe' : 'sensitive',
281
- }));
282
-
283
- // Try AI generation first
284
- try {
285
- const response = await fetch(`${context.serverUrl}/ai-init/generate-prompt`, {
286
- method: 'POST',
287
- headers: {
288
- 'Content-Type': 'application/json',
289
- 'x-arcten-api-key': context.apiKey,
290
- },
291
- body: JSON.stringify({
292
- tools: toolDescriptions,
293
- framework: context.framework,
294
- }),
295
- });
296
-
297
- if (response.ok) {
298
- const result = await response.json() as { systemPrompt: string };
299
- console.log(' AI generated initial prompt');
300
- console.log('');
301
- return {
302
- systemPrompt: result.systemPrompt,
303
- source: 'ai',
304
- };
305
- }
306
- } catch {
307
- // Fall through to template
308
- }
309
-
310
- // Template fallback
311
- const safeToolsList = classification.safe.length > 0
312
- ? `\n\nSafe tools (auto-execute):\n${classification.safe.map(t => `- ${t}`).join('\n')}`
313
- : '';
314
-
315
- const sensitiveToolsList = classification.sensitive.length > 0
316
- ? `\n\nSensitive tools (require approval):\n${classification.sensitive.map(t => `- ${t}`).join('\n')}`
317
- : '';
318
-
319
- const templatePrompt = DEFAULT_PROMPT_TEMPLATE + safeToolsList + sensitiveToolsList;
320
-
321
- console.log(' Using template prompt (AI not available)');
322
- console.log('');
323
- return {
324
- systemPrompt: templatePrompt,
325
- source: 'template',
326
- };
327
- }
328
-
329
- async function saveSystemPrompt(projectRoot: string, prompt: GeneratedPrompt): Promise<void> {
330
- const arctenDir = path.join(projectRoot, '.arcten');
331
- if (!fs.existsSync(arctenDir)) {
332
- fs.mkdirSync(arctenDir, { recursive: true });
333
- }
334
-
335
- const promptPath = path.join(arctenDir, 'prompt.md');
336
- fs.writeFileSync(promptPath, prompt.systemPrompt);
337
- console.log(' Created .arcten/prompt.md');
338
- }
339
-
340
- // ============================================================================
341
- // PHASE 3: Placement Menu
342
- // ============================================================================
343
-
344
- async function showPlacementMenu(): Promise<Placement> {
345
- console.log('Where should your agent appear?\n');
346
- console.log(' [1] 🌍 Everywhere - sidebar on every page');
347
- console.log(' [2] 📄 Specific pages - only where you choose');
348
- console.log(' [3] 🔧 Developer mode - just the hook, build your own UI');
349
- console.log('');
350
-
351
- const answer = await askQuestion('Choose (1/2/3): ');
352
- const choice = answer.trim();
353
-
354
- if (choice === '2' || choice.toLowerCase().includes('specific')) {
355
- return 'specific';
356
- } else if (choice === '3' || choice.toLowerCase().includes('dev')) {
357
- return 'developer';
358
- }
359
-
360
- return 'everywhere';
361
- }
362
-
363
- // ============================================================================
364
- // PHASE 4: File Creation (Agent-based - No Fallback)
365
- // ============================================================================
366
-
367
- async function createAllFiles(result: InitResult): Promise<string[]> {
368
- const { context, classification } = result;
369
- const { projectRoot, toolsFileRelative, projectId, framework, apiKey, serverUrl } = context;
370
- const createdFiles: string[] = [];
371
-
372
- console.log('Creating files...\n');
373
-
374
- // 1. Create .arcten/config.ts (simple config, always deterministic)
375
- const arctenDir = path.join(projectRoot, '.arcten');
376
- if (!fs.existsSync(arctenDir)) {
377
- fs.mkdirSync(arctenDir, { recursive: true });
378
- }
379
-
380
- const configContent = `// Arcten configuration
381
- export const arctenConfig = {
382
- projectId: "${projectId}",
383
- toolFile: "./${toolsFileRelative.replace(/\\/g, '/')}",
384
- routes: [
385
- { pattern: "/*", agentName: "default" },
386
- ],
387
- } as const;
388
- `;
389
- fs.writeFileSync(path.join(arctenDir, 'config.ts'), configContent);
390
- createdFiles.push('.arcten/config.ts');
391
- console.log(' Created .arcten/config.ts');
392
-
393
- // 2. Use Claude agent to generate all other files
394
- console.log('\n Using AI to analyze project and generate setup...');
395
-
396
- const toolNames = [...classification.safe, ...classification.sensitive];
397
-
398
- const injectionResult = await runInjectionAgent(
399
- {
400
- framework,
401
- projectRoot,
402
- toolsFile: toolsFileRelative,
403
- discoveredTools: toolNames,
404
- },
405
- apiKey,
406
- serverUrl
407
- );
408
-
409
- if (!injectionResult.success) {
410
- console.error(`\n Error: ${injectionResult.error}`);
411
- console.error(' Failed to generate setup files. Please check your API key and try again.');
412
- process.exit(1);
413
- }
414
-
415
- if (injectionResult.modifications.length === 0) {
416
- console.error('\n Error: AI returned no modifications.');
417
- console.error(' This may indicate an issue with the project structure.');
418
- process.exit(1);
419
- }
420
-
421
- console.log(` AI generated ${injectionResult.modifications.length} file modifications\n`);
422
-
423
- // Show proposed changes
424
- console.log(' Proposed changes:');
425
- for (const mod of injectionResult.modifications) {
426
- const symbol = mod.action === 'create' ? '+' : '~';
427
- console.log(` ${symbol} ${mod.path}`);
428
- console.log(` ${mod.description}`);
429
- }
430
- console.log('');
431
-
432
- // Apply modifications
433
- const applied = applyAllModifications(projectRoot, injectionResult.modifications);
434
- createdFiles.push(...applied);
435
-
436
- console.log(` Applied ${applied.length} modifications`);
437
- return createdFiles;
438
- }
439
-
440
- // ============================================================================
441
- // PHASE 5: AI Review (disabled - endpoint not implemented)
442
- // ============================================================================
443
-
444
- async function aiReviewFiles(_result: InitResult, _createdFiles: string[]): Promise<void> {
445
- // AI review is optional and requires server endpoint /ai-init/review
446
- // Skipping for now since the generated files are deterministic
447
- }
448
-
449
- // ============================================================================
450
- // PHASE 6: Run Sync
451
- // ============================================================================
452
-
453
- async function runSync(): Promise<void> {
454
- console.log('Running sync...\n');
455
-
456
- try {
457
- // Get script directory (ESM compatible)
458
- const scriptDir = path.dirname(new URL(import.meta.url).pathname).replace(/^\/([A-Z]:)/, '$1');
459
-
460
- // Run arcten sync which handles extract-types + tool array generation
461
- const syncScript = path.join(scriptDir, 'cli-sync-wrapper.cjs');
462
- if (fs.existsSync(syncScript)) {
463
- execSync(`node "${syncScript}" --yes`, {
464
- cwd: process.cwd(),
465
- stdio: 'inherit',
466
- });
467
- } else {
468
- // Fallback: run extract-types directly
469
- console.log(' Running arcten-extract-types...');
470
- const extractScript = path.join(scriptDir, 'cli-extract-types-auto-wrapper.js');
471
- if (fs.existsSync(extractScript)) {
472
- execSync(`node "${extractScript}"`, {
473
- cwd: process.cwd(),
474
- stdio: ['inherit', 'pipe', 'pipe'],
475
- });
476
- } else {
477
- execSync('npx arcten-extract-types', {
478
- cwd: process.cwd(),
479
- stdio: ['inherit', 'pipe', 'pipe'],
480
- });
481
- }
482
- console.log(' Done');
483
- }
484
- } catch (error: any) {
485
- console.log(' Warning: Sync encountered an error');
486
- console.log(' You can run `arcten sync` manually after setup');
487
- }
488
- }
489
-
490
- // ============================================================================
491
- // Helper Functions
492
- // ============================================================================
493
-
494
- function checkApiKey(projectRoot: string): { apiKey: string; projectId: string } | null {
495
- // First check environment variable (for CI/Vercel builds)
496
- const envKey = process.env.ARCTEN_API_KEY;
497
- if (envKey) {
498
- const projectIdMatch = envKey.match(/sk_(proj_[a-zA-Z0-9]+)_/);
499
- if (projectIdMatch) {
500
- return { apiKey: envKey, projectId: projectIdMatch[1] };
501
- }
502
- }
503
-
504
- // Fall back to .env files for local development
505
- const envFiles = ['.env.local', '.env'];
506
-
507
- for (const envFile of envFiles) {
508
- const envPath = path.join(projectRoot, envFile);
509
- if (fs.existsSync(envPath)) {
510
- const envContent = fs.readFileSync(envPath, 'utf-8');
511
- const match = envContent.match(/ARCTEN_API_KEY=([^\s\n]+)/);
512
- if (match) {
513
- const key = match[1];
514
- const projectIdMatch = key.match(/sk_(proj_[a-zA-Z0-9]+)_/);
515
- if (projectIdMatch) {
516
- return { apiKey: key, projectId: projectIdMatch[1] };
517
- }
518
- }
519
- }
520
- }
521
-
522
- return null;
523
- }
524
-
525
- function askQuestion(question: string): Promise<string> {
526
- return new Promise((resolve) => {
527
- rl.question(question, (answer) => {
528
- resolve(answer.trim());
529
- });
530
- });
531
- }
532
-
533
- // ============================================================================
534
- // Main
535
- // ============================================================================
536
-
537
- async function main() {
538
- console.log('');
539
- console.log(' ___ ____________ _____ _____ _ _ ');
540
- console.log(' / _ \\ | ___ \\ ___ \\_ _| ___| \\ | |');
541
- console.log('/ /_\\ \\| |_/ / | / | | | |__ | \\| |');
542
- console.log('| _ || /| | | | | | __|| . ` |');
543
- console.log('| | | || |\\ \\| |__/\\ | | | |___| |\\ |');
544
- console.log('\\_| |_/\\_| \\_\\____/ \\_/ \\____/\\_| \\_/');
545
- console.log('');
546
- console.log(' Setup Wizard');
547
- console.log('');
548
-
549
- // Create readline interface
550
- rl = readline.createInterface({
551
- input: process.stdin,
552
- output: process.stdout,
553
- });
554
-
555
- try {
556
- // PHASE 1: Deterministic Discovery
557
- const context = await discoverContext();
558
-
559
- // PHASE 2: Deterministic Classification
560
- const classification = await classifyTools(context);
561
-
562
- // Show classification summary
563
- console.log('Classification:');
564
- if (classification.safe.length > 0) {
565
- console.log(` Safe: ${classification.safe.slice(0, 5).join(', ')}${classification.safe.length > 5 ? '...' : ''}`);
566
- }
567
- if (classification.sensitive.length > 0) {
568
- console.log(` Sensitive: ${classification.sensitive.slice(0, 5).join(', ')}${classification.sensitive.length > 5 ? '...' : ''}`);
569
- }
570
- console.log('');
571
-
572
- const classificationOk = await askQuestion('Does this look right? (y/n): ');
573
- if (classificationOk.toLowerCase() === 'n') {
574
- console.log('');
575
- console.log('You can adjust classifications later with `arcten tools`');
576
- console.log('and then run `arcten sync` to apply changes.');
577
- console.log('');
578
- }
579
-
580
- // PHASE 2.5: AI Prompt Generation
581
- const generatedPrompt = await generateSystemPrompt(context, classification);
582
-
583
- // Show prompt preview
584
- console.log('System Prompt Preview:');
585
- console.log('─'.repeat(50));
586
- const previewLines = generatedPrompt.systemPrompt.split('\n').slice(0, 8);
587
- previewLines.forEach(line => console.log(` ${line}`));
588
- if (generatedPrompt.systemPrompt.split('\n').length > 8) {
589
- console.log(' ...');
590
- }
591
- console.log('─'.repeat(50));
592
- console.log('');
593
-
594
- const promptOk = await askQuestion('Use this prompt? (y/n): ');
595
- if (promptOk.toLowerCase() === 'y' || promptOk.toLowerCase() === '') {
596
- await saveSystemPrompt(context.projectRoot, generatedPrompt);
597
- } else {
598
- console.log('');
599
- console.log('You can edit the prompt later with `arcten prompt`');
600
- // Still save a default so the file exists
601
- await saveSystemPrompt(context.projectRoot, {
602
- systemPrompt: DEFAULT_PROMPT_TEMPLATE,
603
- source: 'template',
604
- });
605
- }
606
- console.log('');
607
-
608
- // PHASE 3: Placement Menu
609
- const placement = await showPlacementMenu();
610
- console.log('');
611
-
612
- const result: InitResult = {
613
- context,
614
- classification,
615
- placement,
616
- };
617
-
618
- // PHASE 4: Deterministic File Creation
619
- const createdFiles = await createAllFiles(result);
620
-
621
- // PHASE 5: AI Review
622
- await aiReviewFiles(result, createdFiles);
623
-
624
- // PHASE 6: Run Sync
625
- await runSync();
626
-
627
- console.log('');
628
- console.log('Setup complete!');
629
- console.log('');
630
- console.log('Run `bun dev` (or `npm run dev`) to start your app.');
631
- console.log('');
632
- } catch (error: any) {
633
- console.error('Error:', error.message);
634
- process.exit(1);
635
- } finally {
636
- rl.close();
637
- }
638
- }
639
-
640
- // Run
641
- main().catch((error) => {
642
- console.error('Fatal error:', error);
643
- process.exit(1);
644
- });
645
-
646
- export { main };