@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.
- package/README.md +73 -78
- package/dist/index.cjs +3 -16
- package/dist/index.d.ts +1 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +856 -8408
- package/dist/lib/useAgent.d.ts +1 -2
- package/dist/lib/useAgent.d.ts.map +1 -1
- package/dist/types/use-agent.d.ts +3 -44
- package/dist/types/use-agent.d.ts.map +1 -1
- package/dist/utils/extract-tool-metadata.d.ts.map +1 -1
- package/package.json +7 -46
- package/scripts/arcten-cli.cjs +14 -108
- package/scripts/cli-extract-types-auto.ts +22 -4
- package/scripts/update-core.cjs +124 -0
- package/dist/components/ArctenAgent.d.ts +0 -52
- package/dist/components/ArctenAgent.d.ts.map +0 -1
- package/dist/components/ai-elements/prompt-input.d.ts +0 -187
- package/dist/components/ai-elements/prompt-input.d.ts.map +0 -1
- package/dist/components/ai-elements/reasoning.d.ts +0 -17
- package/dist/components/ai-elements/reasoning.d.ts.map +0 -1
- package/dist/components/ai-elements/response.d.ts +0 -8
- package/dist/components/ai-elements/response.d.ts.map +0 -1
- package/dist/components/ai-elements/shimmer.d.ts +0 -10
- package/dist/components/ai-elements/shimmer.d.ts.map +0 -1
- package/dist/components/citation-button.d.ts +0 -14
- package/dist/components/citation-button.d.ts.map +0 -1
- package/dist/components/citation-text-renderer.d.ts +0 -31
- package/dist/components/citation-text-renderer.d.ts.map +0 -1
- package/dist/components/secure-modals/EmailModal.d.ts +0 -10
- package/dist/components/secure-modals/EmailModal.d.ts.map +0 -1
- package/dist/components/secure-modals/FormModal.d.ts +0 -20
- package/dist/components/secure-modals/FormModal.d.ts.map +0 -1
- package/dist/components/secure-modals/PasswordModal.d.ts +0 -10
- package/dist/components/secure-modals/PasswordModal.d.ts.map +0 -1
- package/dist/components/secure-modals/PhoneModal.d.ts +0 -10
- package/dist/components/secure-modals/PhoneModal.d.ts.map +0 -1
- package/dist/components/secure-modals/PinModal.d.ts +0 -11
- package/dist/components/secure-modals/PinModal.d.ts.map +0 -1
- package/dist/components/secure-modals/SecureModalProvider.d.ts +0 -13
- package/dist/components/secure-modals/SecureModalProvider.d.ts.map +0 -1
- package/dist/components/secure-modals/TextModal.d.ts +0 -11
- package/dist/components/secure-modals/TextModal.d.ts.map +0 -1
- package/dist/components/secure-modals/index.d.ts +0 -10
- package/dist/components/secure-modals/index.d.ts.map +0 -1
- package/dist/components/secure-modals/types.d.ts +0 -34
- package/dist/components/secure-modals/types.d.ts.map +0 -1
- package/dist/components/tool-call-approval.d.ts +0 -9
- package/dist/components/tool-call-approval.d.ts.map +0 -1
- package/dist/components/tool-call-result.d.ts +0 -8
- package/dist/components/tool-call-result.d.ts.map +0 -1
- package/dist/components/ui/autotextarea.d.ts +0 -19
- package/dist/components/ui/autotextarea.d.ts.map +0 -1
- package/dist/components/ui/badge.d.ts +0 -10
- package/dist/components/ui/badge.d.ts.map +0 -1
- package/dist/components/ui/button.d.ts +0 -14
- package/dist/components/ui/button.d.ts.map +0 -1
- package/dist/components/ui/collapsible.d.ts +0 -6
- package/dist/components/ui/collapsible.d.ts.map +0 -1
- package/dist/components/ui/command.d.ts +0 -19
- package/dist/components/ui/command.d.ts.map +0 -1
- package/dist/components/ui/dialog.d.ts +0 -16
- package/dist/components/ui/dialog.d.ts.map +0 -1
- package/dist/components/ui/dropdown-menu.d.ts +0 -26
- package/dist/components/ui/dropdown-menu.d.ts.map +0 -1
- package/dist/components/ui/hover-card.d.ts +0 -7
- package/dist/components/ui/hover-card.d.ts.map +0 -1
- package/dist/components/ui/input-group.d.ts +0 -17
- package/dist/components/ui/input-group.d.ts.map +0 -1
- package/dist/components/ui/input.d.ts +0 -4
- package/dist/components/ui/input.d.ts.map +0 -1
- package/dist/components/ui/kbd.d.ts +0 -4
- package/dist/components/ui/kbd.d.ts.map +0 -1
- package/dist/components/ui/select.d.ts +0 -16
- package/dist/components/ui/select.d.ts.map +0 -1
- package/dist/components/ui/textarea.d.ts +0 -4
- package/dist/components/ui/textarea.d.ts.map +0 -1
- package/dist/components/ui/tooltip.d.ts +0 -8
- package/dist/components/ui/tooltip.d.ts.map +0 -1
- package/dist/core.css +0 -1
- package/dist/utils/form-generator.d.ts +0 -29
- package/dist/utils/form-generator.d.ts.map +0 -1
- package/dist/utils/secure-param-detector.d.ts +0 -26
- package/dist/utils/secure-param-detector.d.ts.map +0 -1
- package/scripts/cli-agent-inject.ts +0 -205
- package/scripts/cli-agent-wrapper.cjs +0 -51
- package/scripts/cli-agent.ts +0 -483
- package/scripts/cli-create-project.ts +0 -608
- package/scripts/cli-create-wrapper.cjs +0 -60
- package/scripts/cli-init-wizard-wrapper.cjs +0 -58
- package/scripts/cli-init-wizard.ts +0 -646
- package/scripts/cli-prompt-wrapper.cjs +0 -51
- package/scripts/cli-prompt.ts +0 -306
- package/scripts/cli-sync-wrapper.cjs +0 -69
- package/scripts/cli-sync.ts +0 -915
- package/scripts/cli-tools-wrapper.cjs +0 -51
- package/scripts/cli-tools.ts +0 -320
- package/scripts/cli-uninstall-wrapper.cjs +0 -66
- package/scripts/cli-uninstall.ts +0 -173
- package/scripts/config-parser.ts +0 -432
- package/scripts/dashboard-sync.ts +0 -454
- package/scripts/tree-sitter-discover.ts +0 -526
- package/scripts/wasm/tree-sitter-tsx.wasm +0 -0
- 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 };
|