@descix/cli 1.0.0

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 (94) hide show
  1. package/README.md +95 -0
  2. package/SERVICE_README_sdk.md +399 -0
  3. package/agent-assets/guides/README.md +61 -0
  4. package/agent-assets/guides/dynamic-site-pattern.md +125 -0
  5. package/agent-assets/guides/knowledge-only-pattern.md +93 -0
  6. package/agent-assets/guides/microservice-pattern.md +152 -0
  7. package/agent-assets/guides/static-site-pattern.md +111 -0
  8. package/agent-assets/instructions/app-asset-validation.md +477 -0
  9. package/agent-assets/instructions/onboarding-flow.md +408 -0
  10. package/agent-assets/instructions/standard-folder-structure.md +125 -0
  11. package/agent-assets/reference/app-creation.md +381 -0
  12. package/agent-assets/reference/architecture.md +406 -0
  13. package/agent-assets/reference/local-dev.md +138 -0
  14. package/agent-assets/reference/mcp-integration.md +424 -0
  15. package/agent-assets/reference/workspace-config.md +415 -0
  16. package/agent-assets/rules/descix_mcp.template.mdc +243 -0
  17. package/bin/descix.js +3930 -0
  18. package/bin/mcp-server.js +382 -0
  19. package/descix-cli-1.0.0.tgz +0 -0
  20. package/lib/agent-files.js +162 -0
  21. package/lib/api-client.js +458 -0
  22. package/lib/auth-guard.js +67 -0
  23. package/lib/commands/app-wizard.js +627 -0
  24. package/lib/commands/auth.js +407 -0
  25. package/lib/commands/buy.js +207 -0
  26. package/lib/commands/clone.js +91 -0
  27. package/lib/commands/config.js +155 -0
  28. package/lib/commands/doctor.js +277 -0
  29. package/lib/commands/folder.js +291 -0
  30. package/lib/commands/init.js +257 -0
  31. package/lib/commands/kb.js +654 -0
  32. package/lib/commands/mcp.js +273 -0
  33. package/lib/commands/serve.js +12 -0
  34. package/lib/commands/status.js +239 -0
  35. package/lib/commands/update.js +653 -0
  36. package/lib/core/Chunker.js +610 -0
  37. package/lib/core/Hydrator.js +801 -0
  38. package/lib/core/PathContext.js +335 -0
  39. package/lib/core/Syncer.js +310 -0
  40. package/lib/core/index.js +32 -0
  41. package/lib/global-config.js +86 -0
  42. package/lib/google-storage-adc.js +511 -0
  43. package/lib/wallet-file.js +232 -0
  44. package/lib/wizard/setup.js +502 -0
  45. package/lib/workspace-config.js +712 -0
  46. package/lib/workspace-utils.js +126 -0
  47. package/package.json +41 -0
  48. package/templates/agent-claude.md +226 -0
  49. package/templates/agent-cline.md +133 -0
  50. package/templates/agent-copilot.md +133 -0
  51. package/templates/agent-cursor.md +133 -0
  52. package/templates/descix_mcp.mdc +122 -0
  53. package/templates/drive/README.md +42 -0
  54. package/templates/drive/app/assets/app_description.md +13 -0
  55. package/templates/drive/app/assets/icon.png +0 -0
  56. package/templates/drive/app/assets/system_instructions.md +17 -0
  57. package/templates/drive/app/kb/General/README.md +25 -0
  58. package/templates/drive/community/Apps/.gitkeep +4 -0
  59. package/templates/drive/community/community_assets/community_description.md +15 -0
  60. package/templates/drive/community/community_assets/icon.png +0 -0
  61. package/templates/drive_templates/README.md +104 -0
  62. package/templates/drive_templates/agent_app_template/assets/app_description.txt +13 -0
  63. package/templates/drive_templates/agent_app_template/assets/system_instructions.txt +17 -0
  64. package/templates/drive_templates/agent_app_template/kb/General/README.md +25 -0
  65. package/templates/drive_templates/agent_app_template/microservice/.gitkeep +10 -0
  66. package/templates/drive_templates/agent_app_template/site/.gitkeep +4 -0
  67. package/templates/drive_templates/community_template/Apps/.gitkeep +4 -0
  68. package/templates/drive_templates/community_template/community_assets/community_description.txt +15 -0
  69. package/templates/scaffolds/README.md +62 -0
  70. package/templates/scaffolds/microservice/Dockerfile +19 -0
  71. package/templates/scaffolds/microservice/SERVICE_README_sdk.md +399 -0
  72. package/templates/scaffolds/microservice/app.js +50 -0
  73. package/templates/scaffolds/microservice/app.yaml +8 -0
  74. package/templates/scaffolds/microservice/defaults-config.json +5 -0
  75. package/templates/scaffolds/microservice/dev-overrides.json.example +5 -0
  76. package/templates/scaffolds/microservice/manifest.json +49 -0
  77. package/templates/scaffolds/microservice/package-lock.json +2645 -0
  78. package/templates/scaffolds/microservice/package.json +29 -0
  79. package/templates/scaffolds/microservice/scripts/register.js +55 -0
  80. package/templates/scaffolds/microservice/services/apiFront.js +75 -0
  81. package/templates/scaffolds/microservice/services/commandHandlers/sampleCommands.js +57 -0
  82. package/templates/scaffolds/microservice/services/mcpClient.js +78 -0
  83. package/templates/scaffolds/microservice/services/utils.js +247 -0
  84. package/templates/scaffolds/microservice/setup-schema.json +18 -0
  85. package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +143 -0
  86. package/templates/scaffolds/site/DeSciXAppSDK.js +62 -0
  87. package/templates/scaffolds/site/README.md +51 -0
  88. package/templates/scaffolds/site/app.js +22 -0
  89. package/templates/scaffolds/site/index.html +42 -0
  90. package/templates/scaffolds/site/styles.css +123 -0
  91. package/test-hydration.js +150 -0
  92. package/vendor/mcp/transports/http-sse-transport.js +196 -0
  93. package/vendor/mcp/transports/index.js +15 -0
  94. package/vendor/mcp/transports/stdio-transport.js +118 -0
@@ -0,0 +1,502 @@
1
+ /**
2
+ * Interactive Setup Wizard
3
+ *
4
+ * Bootstraps MCP connectivity so the AI agent can guide workspace configuration.
5
+ *
6
+ * Steps:
7
+ * 0. API Configuration (select environment)
8
+ * 1. Authentication (device login or wallet)
9
+ * 2. Fetch Entitlements (communities/apps user owns)
10
+ * 3. Pull SDK Agent Assets (instructions, templates, rules)
11
+ * 4. MCP Configuration (mcp.json + rules file with entitlements)
12
+ * 5. Create workspace.json (with _needsAgentSetup flag)
13
+ * 6. Register SDK Service (vectorize for tell_me_how)
14
+ *
15
+ * After setup, the AI agent uses the rules file and local SDK assets
16
+ * to help the user map folders to apps and configure workspace.json.
17
+ *
18
+ * All operations use HTTP API client - no service imports
19
+ */
20
+
21
+ import chalk from 'chalk';
22
+ import ora from 'ora';
23
+ import inquirer from 'inquirer';
24
+ import { DeSciXApiClient } from '../api-client.js';
25
+ import { requireAuth, isAuthenticated } from '../auth-guard.js';
26
+ import * as authCommands from '../commands/auth.js';
27
+ import * as fs from 'fs/promises';
28
+ import * as path from 'path';
29
+ import { fileURLToPath } from 'url';
30
+ import { dirname } from 'path';
31
+
32
+ const __filename = fileURLToPath(import.meta.url);
33
+ const __dirname = dirname(__filename);
34
+
35
+ // SDK version for tracking
36
+ const SDK_VERSION = '2.0.0';
37
+
38
+ // ============ Helper Functions ============
39
+
40
+ /**
41
+ * Copy directory recursively
42
+ * @param {string} src - Source directory
43
+ * @param {string} dest - Destination directory
44
+ */
45
+ async function copyDir(src, dest) {
46
+ await fs.mkdir(dest, { recursive: true });
47
+ const entries = await fs.readdir(src, { withFileTypes: true });
48
+
49
+ for (const entry of entries) {
50
+ const srcPath = path.join(src, entry.name);
51
+ const destPath = path.join(dest, entry.name);
52
+
53
+ if (entry.isDirectory()) {
54
+ await copyDir(srcPath, destPath);
55
+ } else {
56
+ await fs.copyFile(srcPath, destPath);
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Pull SDK agent assets to local workspace
63
+ * Copies agent-assets/ to .descix/sdk-assets/ for local AI agent access
64
+ * @param {string} workspacePath - Workspace root path
65
+ * @returns {Promise<boolean>} True if assets were copied
66
+ */
67
+ async function pullSdkAssets(workspacePath) {
68
+ // agent-assets is in the SDK root, which is ../../.. from wizard/setup.js
69
+ const sdkAssetsSource = path.join(__dirname, '../../../agent-assets');
70
+ const sdkAssetsTarget = path.join(workspacePath, '.descix/sdk-assets');
71
+
72
+ try {
73
+ // Check if source exists
74
+ await fs.access(sdkAssetsSource);
75
+
76
+ // Copy recursively
77
+ await copyDir(sdkAssetsSource, sdkAssetsTarget);
78
+
79
+ return true;
80
+ } catch (error) {
81
+ // Source doesn't exist (development mode or not packaged)
82
+ return false;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Generate customized rules file from template
88
+ * Uses agent-assets/rules/descix_mcp.template.mdc with placeholder substitution
89
+ * @param {string} workspacePath - Workspace root path
90
+ * @param {Object} entitlements - User's entitlements from fetch_my_purchases
91
+ * @param {string} primaryCommunity - User's primary community
92
+ * @returns {Promise<boolean>} True if rules file was generated
93
+ */
94
+ async function generateRulesFile(workspacePath, entitlements, primaryCommunity) {
95
+ const templatePath = path.join(__dirname, '../../../agent-assets/rules/descix_mcp.template.mdc');
96
+ const rulesDir = path.join(workspacePath, '.cursor/rules');
97
+ const rulesPath = path.join(rulesDir, 'descix_mcp.mdc');
98
+
99
+ try {
100
+ // Read template
101
+ const template = await fs.readFile(templatePath, 'utf-8');
102
+
103
+ // Summarize entitlements for embedding (avoid huge JSON in rules file)
104
+ const entitlementsSummary = {
105
+ communities: (entitlements.communities || []).map(c => ({
106
+ id: c.community_id || c.id,
107
+ name: c.community_name || c.name
108
+ })),
109
+ apps: (entitlements.apps || []).map(a => ({
110
+ community: a.community_id,
111
+ id: a.app_id || a.id,
112
+ name: a.app_name || a.name
113
+ }))
114
+ };
115
+
116
+ // Customize with workspace-specific values
117
+ const customized = template
118
+ .replace(/\{\{SDK_ASSETS_PATH\}\}/g, '.descix/sdk-assets')
119
+ .replace(/\{\{PRIMARY_COMMUNITY\}\}/g, primaryCommunity || 'descix')
120
+ .replace(/\{\{ENTITLEMENTS_JSON\}\}/g, JSON.stringify(entitlementsSummary, null, 2))
121
+ .replace(/\{\{SDK_VERSION\}\}/g, SDK_VERSION)
122
+ .replace(/\{\{WORKSPACE_PATH\}\}/g, workspacePath);
123
+
124
+ // Write customized rules
125
+ await fs.mkdir(rulesDir, { recursive: true });
126
+ await fs.writeFile(rulesPath, customized, 'utf-8');
127
+
128
+ return true;
129
+ } catch (error) {
130
+ // Template doesn't exist, fall back to simple template
131
+ return false;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Auto-register SDK service for tell_me_how discovery
137
+ * Vectorizes SERVICE_README_sdk.md so SDK tools are discoverable
138
+ * @param {DeSciXApiClient} apiClient - Authenticated API client
139
+ * @returns {Promise<boolean>} True if registration succeeded
140
+ */
141
+ async function autoRegisterSdkService(apiClient) {
142
+ // SDK SERVICE_README is in the SDK root
143
+ const readmePath = path.join(__dirname, '../../../SERVICE_README_sdk.md');
144
+
145
+ try {
146
+ const readmeContent = await fs.readFile(readmePath, 'utf-8');
147
+
148
+ // Vectorize for tell_me_how discovery
149
+ await apiClient.invoke('vectorize_service_readme', {
150
+ service_name: 'sdk',
151
+ community_id: 'descix',
152
+ app_id: 'docs',
153
+ readme_content: readmeContent
154
+ });
155
+
156
+ return true;
157
+ } catch (error) {
158
+ // README doesn't exist or vectorization failed
159
+ return false;
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Get MCP server configuration
165
+ * Uses direct command if CLI is installed, otherwise suggests install
166
+ * @returns {Object} { command, args } for MCP server
167
+ */
168
+ function getMcpServerConfig() {
169
+ // Use direct descix command - simpler and no npm download needed
170
+ // Requires CLI to be installed globally first via:
171
+ // npm install -g https://app.descix.net/sdk/descix-cli-1.0.0.tgz
172
+ // or during development:
173
+ // npm install -g /path/to/descix-cli-1.0.0.tgz
174
+ return {
175
+ command: 'descix',
176
+ args: ['mcp-serve'],
177
+ type: 'global'
178
+ };
179
+ }
180
+
181
+ // ============ Main Wizard ============
182
+
183
+ /**
184
+ * Run the interactive setup wizard
185
+ */
186
+ export async function runSetupWizard(options = {}) {
187
+ console.log(chalk.cyan('\nā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”'));
188
+ console.log(chalk.cyan('│ Welcome to DeSciX CLI/MCP Setup │'));
189
+ console.log(chalk.cyan('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤'));
190
+ console.log(chalk.cyan('│ This wizard will: │'));
191
+ console.log(chalk.cyan('│ 1. Configure API endpoint │'));
192
+ console.log(chalk.cyan('│ 2. Authenticate with DeSciX (required) │'));
193
+ console.log(chalk.cyan('│ 3. Fetch your entitlements │'));
194
+ console.log(chalk.cyan('│ 4. Pull SDK agent assets locally │'));
195
+ console.log(chalk.cyan('│ 5. Configure Cursor MCP integration │'));
196
+ console.log(chalk.cyan('│ 6. Register SDK for tool discovery │'));
197
+ console.log(chalk.cyan('│ │'));
198
+ console.log(chalk.cyan('│ After setup, the AI agent will help you │'));
199
+ console.log(chalk.cyan('│ configure your workspace folders. │'));
200
+ console.log(chalk.cyan('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n'));
201
+
202
+ const workspaceRoot = process.cwd();
203
+ const spinner = ora('Initializing...').start();
204
+
205
+ try {
206
+ // Step 0: Configure API endpoint
207
+ spinner.stop();
208
+ console.log(chalk.cyan('\nšŸ“‹ Step 0: API Configuration\n'));
209
+
210
+ let apiUrl = options.url || process.env.DESCIX_API_URL;
211
+
212
+ if (!apiUrl) {
213
+ const { environment } = await inquirer.prompt([
214
+ {
215
+ type: 'list',
216
+ name: 'environment',
217
+ message: 'Which environment are you connecting to?',
218
+ choices: [
219
+ { name: 'Production (app.descix.net)', value: 'production' },
220
+ { name: 'Local Development (localhost:4000)', value: 'local' },
221
+ { name: 'Custom URL', value: 'custom' }
222
+ ],
223
+ default: 'production'
224
+ }
225
+ ]);
226
+
227
+ if (environment === 'production') {
228
+ apiUrl = 'https://descix.net';
229
+ } else if (environment === 'local') {
230
+ apiUrl = 'https://localhost:4000';
231
+ } else {
232
+ const { customUrl } = await inquirer.prompt([
233
+ {
234
+ type: 'input',
235
+ name: 'customUrl',
236
+ message: 'Enter API URL:',
237
+ default: 'https://localhost:4000',
238
+ validate: (input) => {
239
+ if (!input.startsWith('http://') && !input.startsWith('https://')) {
240
+ return 'URL must start with http:// or https://';
241
+ }
242
+ return true;
243
+ }
244
+ }
245
+ ]);
246
+ apiUrl = customUrl;
247
+ }
248
+ }
249
+
250
+ console.log(chalk.green(`āœ“ API endpoint: ${apiUrl}\n`));
251
+
252
+ spinner.start('Connecting to DeSciX...');
253
+ const apiClient = new DeSciXApiClient({ baseUrl: apiUrl });
254
+ spinner.succeed('Connected to DeSciX');
255
+
256
+ // Step 1: Authentication (required)
257
+ console.log(chalk.cyan('\nšŸ“‹ Step 1: Authentication\n'));
258
+
259
+ // Pass the API URL to login commands
260
+ const authOptions = { url: apiUrl };
261
+
262
+ const isAuth = await isAuthenticated(apiClient);
263
+
264
+ if (!isAuth) {
265
+ console.log(chalk.yellow('āš ļø Authentication is required to use DeSciX CLI/MCP.\n'));
266
+
267
+ const { authMethod } = await inquirer.prompt([
268
+ {
269
+ type: 'list',
270
+ name: 'authMethod',
271
+ message: 'How would you like to authenticate?',
272
+ choices: [
273
+ { name: 'Device Login (recommended - opens browser)', value: 'device' },
274
+ { name: 'Direct Wallet Connection (advanced)', value: 'wallet' }
275
+ ],
276
+ default: 'device'
277
+ }
278
+ ]);
279
+
280
+ if (authMethod === 'device') {
281
+ await authCommands.loginDevice(authOptions);
282
+ } else {
283
+ await authCommands.loginWallet();
284
+ }
285
+ } else {
286
+ console.log(chalk.green('āœ… Already authenticated\n'));
287
+ }
288
+
289
+ // Verify authentication
290
+ await requireAuth(apiClient);
291
+ console.log(chalk.green('āœ… Authentication verified\n'));
292
+
293
+ // Step 2: Fetch Entitlements
294
+ console.log(chalk.cyan('\nšŸ“‹ Step 2: Fetching Entitlements\n'));
295
+
296
+ let entitlements = { communities: [], apps: [], service_slots: [] };
297
+ spinner.start('Fetching your entitlements...');
298
+
299
+ try {
300
+ const purchasesResult = await apiClient.invoke('fetch_my_purchases', {});
301
+ if (purchasesResult && purchasesResult.status !== 'ERROR') {
302
+ entitlements = {
303
+ communities: purchasesResult.communities || [],
304
+ apps: purchasesResult.apps || [],
305
+ service_slots: purchasesResult.service_slots || []
306
+ };
307
+ spinner.succeed(`Found ${entitlements.communities.length} communities, ${entitlements.apps.length} apps`);
308
+
309
+ // Display entitlements
310
+ if (entitlements.communities.length > 0) {
311
+ console.log(chalk.gray('\n Communities:'));
312
+ for (const c of entitlements.communities) {
313
+ console.log(chalk.gray(` - ${c.community_name || c.name || c.community_id} (${c.community_id || c.id})`));
314
+ }
315
+ }
316
+ if (entitlements.apps.length > 0) {
317
+ console.log(chalk.gray('\n Apps:'));
318
+ for (const a of entitlements.apps.slice(0, 5)) {
319
+ console.log(chalk.gray(` - ${a.community_id}/${a.app_id || a.id}`));
320
+ }
321
+ if (entitlements.apps.length > 5) {
322
+ console.log(chalk.gray(` ... and ${entitlements.apps.length - 5} more`));
323
+ }
324
+ }
325
+ console.log('');
326
+ } else {
327
+ spinner.warn('Could not fetch entitlements (will use defaults)');
328
+ }
329
+ } catch (error) {
330
+ spinner.warn('Could not fetch entitlements: ' + error.message);
331
+ }
332
+
333
+ // Step 3: Pull SDK Assets
334
+ console.log(chalk.cyan('\nšŸ“‹ Step 3: Pulling SDK Agent Assets\n'));
335
+
336
+ spinner.start('Pulling SDK agent assets...');
337
+ const assetsPulled = await pullSdkAssets(workspaceRoot);
338
+
339
+ if (assetsPulled) {
340
+ spinner.succeed('SDK agent assets pulled');
341
+ console.log(chalk.gray(' Created: .descix/sdk-assets/'));
342
+ console.log(chalk.gray(' - instructions/ (onboarding, packaging types, etc.)'));
343
+ console.log(chalk.gray(' - templates/ (app scaffolds)'));
344
+ console.log(chalk.gray(' - rules/ (agent instructions template)\n'));
345
+ } else {
346
+ spinner.warn('SDK agent assets not found (development mode)');
347
+ console.log(chalk.gray(' Skipped: .descix/sdk-assets/\n'));
348
+ }
349
+
350
+ // Derive primary community from entitlements
351
+ let primaryCommunity = 'descix';
352
+ if (entitlements.communities.length > 0) {
353
+ primaryCommunity = entitlements.communities[0].community_id || entitlements.communities[0].id || 'descix';
354
+ }
355
+
356
+ // Step 4: MCP Configuration
357
+ console.log(chalk.cyan('\nšŸ“‹ Step 4: MCP Configuration\n'));
358
+
359
+ const { setupMCP } = await inquirer.prompt([
360
+ {
361
+ type: 'confirm',
362
+ name: 'setupMCP',
363
+ message: 'Set up Cursor MCP integration?',
364
+ default: true
365
+ }
366
+ ]);
367
+
368
+ if (setupMCP) {
369
+ spinner.start('Creating MCP configuration...');
370
+
371
+ // Use npx approach for reliability (like Pinecone does)
372
+ const mcpServer = getMcpServerConfig();
373
+
374
+ // Create .cursor/mcp.json
375
+ const cursorDir = path.join(workspaceRoot, '.cursor');
376
+ await fs.mkdir(cursorDir, { recursive: true });
377
+
378
+ // Build MCP server config - npx handles all dependencies
379
+ const mcpServerConfig = {
380
+ command: mcpServer.command,
381
+ args: mcpServer.args
382
+ };
383
+
384
+ // Read existing config if present to preserve other servers
385
+ let existingConfig = { mcpServers: {} };
386
+ const cursorConfigPath = path.join(cursorDir, 'mcp.json');
387
+ try {
388
+ const existing = await fs.readFile(cursorConfigPath, 'utf-8');
389
+ existingConfig = JSON.parse(existing);
390
+ if (!existingConfig.mcpServers) {
391
+ existingConfig.mcpServers = {};
392
+ }
393
+ } catch {
394
+ // No existing config, that's fine
395
+ }
396
+
397
+ // Add/update descix server
398
+ existingConfig.mcpServers['descix'] = mcpServerConfig;
399
+
400
+ await fs.writeFile(cursorConfigPath, JSON.stringify(existingConfig, null, 2));
401
+
402
+ spinner.succeed('MCP configuration created');
403
+ console.log(chalk.gray(` Created: .cursor/mcp.json`));
404
+ console.log(chalk.gray(` Server: ${mcpServer.command} ${mcpServer.args.join(' ')}`));
405
+
406
+ // Deploy customized rules file with entitlements
407
+ spinner.start('Generating customized agent rules...');
408
+
409
+ const rulesGenerated = await generateRulesFile(workspaceRoot, entitlements, primaryCommunity);
410
+
411
+ if (rulesGenerated) {
412
+ spinner.succeed('Agent rules file generated with entitlements');
413
+ console.log(chalk.gray(` Created: .cursor/rules/descix_mcp.mdc`));
414
+ console.log(chalk.gray(` - Embedded ${entitlements.communities.length} communities`));
415
+ console.log(chalk.gray(` - Embedded ${entitlements.apps.length} apps`));
416
+ console.log(chalk.gray(` - References local SDK assets at .descix/sdk-assets/\n`));
417
+ } else {
418
+ // Fall back to simple template
419
+ spinner.info('Using simple rules template');
420
+ const rulesDir = path.join(workspaceRoot, '.cursor', 'rules');
421
+ await fs.mkdir(rulesDir, { recursive: true });
422
+
423
+ const templatePath = path.join(__dirname, '../../templates/descix_mcp.mdc');
424
+ const rulesPath = path.join(rulesDir, 'descix_mcp.mdc');
425
+
426
+ try {
427
+ const templateContent = await fs.readFile(templatePath, 'utf-8');
428
+ await fs.writeFile(rulesPath, templateContent, 'utf-8');
429
+ console.log(chalk.gray(` Created: .cursor/rules/descix_mcp.mdc\n`));
430
+ } catch (error) {
431
+ console.log(chalk.gray(` Skipped: .cursor/rules/descix_mcp.mdc\n`));
432
+ }
433
+ }
434
+ }
435
+
436
+ // Step 5: Create workspace config
437
+ spinner.start('Creating workspace configuration...');
438
+
439
+ const config = {
440
+ version: '2.0',
441
+ primaryCommunity,
442
+ directoryMappings: {}, // Empty - AI agent will configure after setup
443
+ additionalContexts: [],
444
+ defaultContext: {
445
+ communityId: primaryCommunity,
446
+ appId: 'agent',
447
+ kbId: 'General'
448
+ },
449
+ apiUrl: apiUrl,
450
+ environment: apiUrl.includes('localhost') ? 'development' : 'production',
451
+ _setupStatus: 'mcp_ready',
452
+ _needsAgentSetup: true, // Flag for AI agent to help with workspace config
453
+ _sdkVersion: SDK_VERSION
454
+ };
455
+
456
+ const descixDir = path.join(workspaceRoot, '.descix');
457
+ await fs.mkdir(descixDir, { recursive: true });
458
+ const configPath = path.join(descixDir, 'workspace.json');
459
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
460
+
461
+ spinner.succeed('Workspace configured');
462
+ console.log(chalk.gray(` Created: .descix/workspace.json\n`));
463
+
464
+ // Step 6: Auto-register SDK service for tell_me_how discovery
465
+ console.log(chalk.cyan('\nšŸ“‹ Step 6: Registering SDK Service\n'));
466
+
467
+ spinner.start('Registering SDK for tell_me_how discovery...');
468
+ const sdkRegistered = await autoRegisterSdkService(apiClient);
469
+
470
+ if (sdkRegistered) {
471
+ spinner.succeed('SDK service registered');
472
+ console.log(chalk.gray(' SDK tools now discoverable via tell_me_how\n'));
473
+ } else {
474
+ spinner.warn('Could not register SDK service (will work without discovery)');
475
+ console.log(chalk.gray(' SDK tools available but not indexed for search\n'));
476
+ }
477
+
478
+ // Done!
479
+ console.log(chalk.green('\nāœ… MCP Setup Complete!\n'));
480
+ console.log(chalk.white('Next steps:'));
481
+ console.log(chalk.gray(' 1. Restart Cursor (Cmd/Ctrl + Shift + P → "Reload Window")'));
482
+ console.log(chalk.gray(' 2. Ask the AI agent: "Help me configure my workspace"\n'));
483
+
484
+ console.log(chalk.white('The AI agent will:'));
485
+ console.log(chalk.gray(' - Read your entitlements and show available apps'));
486
+ console.log(chalk.gray(' - Help you map local folders to DeSciX apps'));
487
+ console.log(chalk.gray(' - Analyze folder structure and recommend packaging types'));
488
+ console.log(chalk.gray(' - Register apps in workspace.json\n'));
489
+
490
+ console.log(chalk.white('If MCP fails, use CLI directly:'));
491
+ console.log(chalk.gray(' descix purchases # View your entitlements'));
492
+ console.log(chalk.gray(' descix tell-me-how "question" # Discover tools'));
493
+ console.log(chalk.gray(' descix sync assets -c X -a Y # Sync app assets'));
494
+ console.log(chalk.gray(' descix sync kb -c X -a Y # Sync knowledge base\n'));
495
+
496
+ } catch (error) {
497
+ spinner.fail('Setup failed');
498
+ console.error(chalk.red(error.message));
499
+ throw error;
500
+ }
501
+ }
502
+