@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,273 @@
1
+ /**
2
+ * MCP Commands
3
+ *
4
+ * MCP server setup and configuration commands
5
+ * All operations use HTTP API client - no service imports
6
+ */
7
+
8
+ import chalk from 'chalk';
9
+ import ora from 'ora';
10
+ import { DeSciXApiClient } from '../api-client.js';
11
+ import { requireAuth } from '../auth-guard.js';
12
+ import * as fs from 'fs/promises';
13
+ import * as path from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ import { dirname } from 'path';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+
20
+ /**
21
+ * Load workspace config from JSON file
22
+ */
23
+ async function loadWorkspaceConfig(workspaceRoot = process.cwd()) {
24
+ const configPath = path.join(workspaceRoot, '.descix', 'workspace.json');
25
+ try {
26
+ const data = await fs.readFile(configPath, 'utf-8');
27
+ return JSON.parse(data);
28
+ } catch (error) {
29
+ // Return default config if file doesn't exist
30
+ return {
31
+ primaryCommunity: 'descix',
32
+ directoryMappings: {},
33
+ additionalContexts: [],
34
+ defaultContext: {
35
+ communityId: 'descix',
36
+ appId: 'agent',
37
+ kbId: 'General'
38
+ }
39
+ };
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Deploy descix_mcp.mdc rules file to .cursor/rules/
45
+ */
46
+ async function deployRulesFile(workspaceRoot = process.cwd()) {
47
+ const rulesDir = path.join(workspaceRoot, '.cursor', 'rules');
48
+ await fs.mkdir(rulesDir, { recursive: true });
49
+
50
+ const templatePath = path.join(__dirname, '../../templates/descix_mcp.mdc');
51
+ const rulesPath = path.join(rulesDir, 'descix_mcp.mdc');
52
+
53
+ try {
54
+ // Check if template exists
55
+ await fs.access(templatePath);
56
+
57
+ // Copy template to rules directory
58
+ const templateContent = await fs.readFile(templatePath, 'utf-8');
59
+
60
+ // Check if rules file already exists
61
+ let existingContent = '';
62
+ try {
63
+ existingContent = await fs.readFile(rulesPath, 'utf-8');
64
+ } catch {
65
+ // File doesn't exist, that's fine
66
+ }
67
+
68
+ // Only write if content is different (avoid unnecessary writes)
69
+ if (existingContent !== templateContent) {
70
+ await fs.writeFile(rulesPath, templateContent, 'utf-8');
71
+ return true; // File was deployed
72
+ }
73
+
74
+ return false; // File already exists with same content
75
+ } catch (error) {
76
+ // Template might not exist in development, that's okay
77
+ console.error(chalk.yellow(`Warning: Could not deploy rules file: ${error.message}`));
78
+ return false;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Initialize MCP server for workspace
84
+ */
85
+ export async function init() {
86
+ const apiClient = new DeSciXApiClient();
87
+ await requireAuth(apiClient);
88
+
89
+ console.log(chalk.cyan('\n🚀 DeSciX MCP Workspace Setup\n'));
90
+
91
+ const spinner = ora('Initializing...').start();
92
+
93
+ try {
94
+ spinner.text = 'Loading communities...';
95
+
96
+ // Query communities via HTTP
97
+ const response = await apiClient.invoke('find_communities', {}, { allowGuest: false });
98
+ const result = response.message || response;
99
+ const communities = result.communities || [];
100
+ spinner.succeed(`Found ${communities.length} communities`);
101
+
102
+ console.log(chalk.cyan('\nAvailable communities:'));
103
+ communities.forEach((c, idx) => {
104
+ console.log(chalk.gray(` ${idx + 1}. ${c.community_name} (${c.community_id})`));
105
+ });
106
+
107
+ // Use default community for now (can be enhanced with prompts)
108
+ const primaryCommunity = 'descix';
109
+
110
+ // Generate .descix/workspace.json
111
+ const workspaceRoot = process.cwd();
112
+ const config = {
113
+ primaryCommunity,
114
+ directoryMappings: {},
115
+ additionalContexts: [],
116
+ defaultContext: {
117
+ communityId: primaryCommunity,
118
+ appId: 'docs',
119
+ kbId: 'sdk'
120
+ }
121
+ };
122
+
123
+ spinner.start('Creating configuration files...');
124
+
125
+ const descixDir = path.join(workspaceRoot, '.descix');
126
+ await fs.mkdir(descixDir, { recursive: true });
127
+ const configPath = path.join(descixDir, 'workspace.json');
128
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
129
+ console.log(chalk.green('✅ Created .descix/workspace.json'));
130
+
131
+ // Create .cursor/mcp.json
132
+ const cursorDir = path.join(workspaceRoot, '.cursor');
133
+ await fs.mkdir(cursorDir, { recursive: true });
134
+
135
+ const cursorConfig = {
136
+ mcpServers: {
137
+ 'descix-rag': {
138
+ command: 'node',
139
+ args: ['node_modules/@descix/cli/bin/mcp-server.js'],
140
+ cwd: '${workspaceFolder}'
141
+ }
142
+ }
143
+ };
144
+
145
+ const cursorConfigPath = path.join(cursorDir, 'mcp.json');
146
+ await fs.writeFile(cursorConfigPath, JSON.stringify(cursorConfig, null, 2));
147
+ console.log(chalk.green('✅ Created .cursor/mcp.json'));
148
+
149
+ // Deploy rules file
150
+ spinner.text = 'Deploying agent rules file...';
151
+ const rulesDeployed = await deployRulesFile(workspaceRoot);
152
+ if (rulesDeployed) {
153
+ console.log(chalk.green('✅ Deployed .cursor/rules/descix_mcp.mdc'));
154
+ } else {
155
+ console.log(chalk.gray(' Rules file already exists'));
156
+ }
157
+
158
+ spinner.succeed('Configuration complete!');
159
+
160
+ console.log(chalk.cyan('\n📋 Next Steps:\n'));
161
+ console.log(chalk.white('1. Restart Cursor (Cmd/Ctrl + Shift + P → "Reload Window")\n'));
162
+ console.log(chalk.white('2. MCP will automatically use your credentials!\n'));
163
+ console.log(chalk.gray('Test with: descix mcp test\n'));
164
+
165
+ } catch (error) {
166
+ spinner.fail('Setup failed');
167
+ console.error(chalk.red(error.message));
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Test MCP server via HTTP
174
+ */
175
+ export async function test(options = {}) {
176
+ const apiClient = new DeSciXApiClient();
177
+ await requireAuth(apiClient);
178
+
179
+ const spinner = ora('Testing MCP server...').start();
180
+
181
+ try {
182
+ const config = await loadWorkspaceConfig(process.cwd());
183
+
184
+ spinner.text = `Querying ${config.defaultContext.communityId}/${config.defaultContext.appId}/${config.defaultContext.kbId}...`;
185
+
186
+ // Get RAG status via HTTP
187
+ const statusResponse = await apiClient.invoke('get_kb_rag_status', {
188
+ app_id: config.defaultContext.appId,
189
+ kb_id: config.defaultContext.kbId
190
+ }, { allowGuest: false });
191
+
192
+ const status = statusResponse.message || statusResponse;
193
+
194
+ if (!status.enabled) {
195
+ spinner.warn(chalk.yellow('RAG not enabled for this knowledge base'));
196
+ console.log(chalk.gray('Enable RAG with: descix rag sync -c ' + config.defaultContext.communityId + ' -a ' + config.defaultContext.appId + ' -k ' + config.defaultContext.kbId));
197
+ throw new Error('RAG not enabled');
198
+ }
199
+
200
+ const query = options.query || 'What is this knowledge base about?';
201
+ const queryResponse = await apiClient.invoke('query_knowledge_base', {
202
+ app_id: config.defaultContext.appId,
203
+ kb_id: config.defaultContext.kbId,
204
+ query: query,
205
+ limit: 3
206
+ }, { allowGuest: false });
207
+
208
+ const result = queryResponse.message?.results || queryResponse.results || [];
209
+
210
+ spinner.succeed(chalk.green('MCP server working!'));
211
+ console.log(chalk.cyan(`\nQuery: "${query}"\n`));
212
+ console.log(chalk.white(`Found ${result.length} results\n`));
213
+
214
+ result.forEach((r, idx) => {
215
+ console.log(chalk.yellow(`${idx + 1}. ${r.fileName || r.file_path} (${((r.similarity || 0) * 100).toFixed(1)}% match)`));
216
+ console.log(chalk.gray(` ${(r.content || '').substring(0, 100)}...`));
217
+ console.log();
218
+ });
219
+
220
+ } catch (error) {
221
+ spinner.fail('Test failed');
222
+ console.error(chalk.red(error.message));
223
+ throw error;
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Show MCP configuration
229
+ */
230
+ export async function config() {
231
+ try {
232
+ const config = await loadWorkspaceConfig(process.cwd());
233
+ console.log(chalk.cyan('\nWorkspace Configuration:\n'));
234
+ console.log(JSON.stringify({
235
+ primaryCommunity: config.primaryCommunity,
236
+ directoryMappings: config.directoryMappings,
237
+ additionalContexts: config.additionalContexts,
238
+ defaultContext: config.defaultContext
239
+ }, null, 2));
240
+ console.log();
241
+ } catch (error) {
242
+ console.error(chalk.red('Error loading config:', error.message));
243
+ throw error;
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Quickstart - one-command setup
249
+ */
250
+ export async function quickstart(options = {}) {
251
+ const apiClient = new DeSciXApiClient();
252
+
253
+ console.log(chalk.cyan('\n🚀 DeSciX MCP Quick Start\n'));
254
+
255
+ // Check if authenticated
256
+ try {
257
+ await requireAuth(apiClient);
258
+ } catch (error) {
259
+ console.log(chalk.yellow('⚠️ Authentication required first.\n'));
260
+ console.log(chalk.white('Run: descix login\n'));
261
+ throw new Error('Authentication required');
262
+ }
263
+
264
+ // Run setup wizard
265
+ const { runSetupWizard } = await import('../wizard/setup.js');
266
+ await runSetupWizard();
267
+
268
+ // Deploy rules file after wizard completes
269
+ const workspaceRoot = process.cwd();
270
+ await deployRulesFile(workspaceRoot);
271
+ console.log(chalk.green('✅ Deployed .cursor/rules/descix_mcp.mdc'));
272
+ }
273
+
@@ -0,0 +1,12 @@
1
+ /**
2
+ * descix serve — thin CLI wrapper around @descix/app-sdk gateway.
3
+ */
4
+
5
+ import { runGateway } from '@descix/app-sdk/dev';
6
+
7
+ export async function runServe(options = {}) {
8
+ await runGateway({
9
+ port: options.port || 5173,
10
+ workspaceRoot: options.workspaceRoot || process.cwd(),
11
+ });
12
+ }
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Status Command
3
+ *
4
+ * Displays comprehensive status of the developer environment:
5
+ * - System info (CLI version, Node version, etc.)
6
+ * - Authentication status
7
+ * - Workspace context and mode
8
+ * - Configuration state
9
+ *
10
+ * Uses PathContext for workspace resolution - no scattered process.cwd() calls
11
+ */
12
+
13
+ import chalk from 'chalk';
14
+ import os from 'os';
15
+ import path from 'path';
16
+ import fs from 'fs/promises';
17
+ import { exec } from 'child_process';
18
+ import { promisify } from 'util';
19
+ import { DeSciXApiClient } from '../api-client.js';
20
+ import { GlobalConfig } from '../global-config.js';
21
+ import { PathContext } from '../core/PathContext.js';
22
+ import { detectWorkspaceMode } from '../workspace-utils.js';
23
+ import { isAuthenticated } from '../auth-guard.js';
24
+
25
+ const execAsync = promisify(exec);
26
+
27
+ /**
28
+ * Get CLI version from package.json
29
+ */
30
+ async function getCliVersion() {
31
+ try {
32
+ // Navigate up from lib/commands/status.js to package.json
33
+ const packageJsonPath = path.resolve(
34
+ path.dirname(new URL(import.meta.url).pathname),
35
+ '../../package.json'
36
+ );
37
+ const data = await fs.readFile(packageJsonPath, 'utf-8');
38
+ const pkg = JSON.parse(data);
39
+ return pkg.version;
40
+ } catch (error) {
41
+ return 'unknown';
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Check if gcloud is installed
47
+ */
48
+ async function checkGcloud() {
49
+ try {
50
+ const { stdout } = await execAsync('gcloud --version');
51
+ const version = stdout.split('\n')[0].trim();
52
+ return { installed: true, version };
53
+ } catch (error) {
54
+ return { installed: false };
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Check ADC credentials
60
+ */
61
+ async function checkAdc() {
62
+ // Simple check: see if GOOGLE_APPLICATION_CREDENTIALS is set
63
+ // or if default credentials file exists
64
+ if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
65
+ return { configured: true, source: 'env_var' };
66
+ }
67
+
68
+ const defaultPath = path.join(os.homedir(), '.config', 'gcloud', 'application_default_credentials.json');
69
+ try {
70
+ await fs.access(defaultPath);
71
+ return { configured: true, source: 'default_file' };
72
+ } catch {
73
+ return { configured: false };
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Run the status command
79
+ */
80
+ export async function runStatus(options = {}) {
81
+ console.log(chalk.cyan('\nDeSciX CLI Status'));
82
+ console.log(chalk.cyan('=================\n'));
83
+
84
+ // 1. System Info
85
+ console.log(chalk.white('System'));
86
+ console.log(chalk.white('------'));
87
+
88
+ const cliVersion = await getCliVersion();
89
+ console.log(`CLI Version: ${chalk.green(cliVersion)}`);
90
+ console.log(`Node Version: ${chalk.green(process.version)}`);
91
+ console.log(`OS: ${os.type()} ${os.release()}`);
92
+
93
+ const globalConfig = await GlobalConfig.load();
94
+ console.log(`Environment: ${globalConfig.environment} (${globalConfig.api_url})`);
95
+ console.log();
96
+
97
+ // Try to load workspace context — WorkspaceConfig first (handles v2), fallback to PathContext
98
+ let ctx = null;
99
+ let workspaceRoot = null;
100
+ let workspaceConfig = null;
101
+ try {
102
+ const { WorkspaceConfig } = await import('../workspace-config.js');
103
+ const wsConfig = await WorkspaceConfig.load();
104
+ workspaceRoot = wsConfig.getWorkspaceRoot();
105
+ workspaceConfig = wsConfig;
106
+ } catch {
107
+ // Fallback to PathContext
108
+ ctx = await PathContext.tryLoad();
109
+ workspaceRoot = ctx?.getWorkspaceRoot();
110
+ }
111
+
112
+ // 2. Authentication
113
+ console.log(chalk.white('Authentication'));
114
+ console.log(chalk.white('--------------'));
115
+
116
+ const apiClient = new DeSciXApiClient({ baseUrl: globalConfig.api_url });
117
+ const isAuth = await isAuthenticated(apiClient);
118
+
119
+ if (isAuth) {
120
+ console.log(`Status: ${chalk.green('Authenticated')}`);
121
+
122
+ // Try to get wallet address from stored credentials if available
123
+ if (workspaceRoot) {
124
+ try {
125
+ const { WalletFileManager } = await import('../wallet-file.js');
126
+ const walletData = await WalletFileManager.loadFromWorkspace(workspaceRoot);
127
+ if (walletData && walletData.walletAddress) {
128
+ console.log(`Wallet: ${chalk.green(walletData.walletAddress)}`);
129
+ }
130
+ } catch (e) {
131
+ // Ignore wallet load error
132
+ }
133
+ }
134
+ } else {
135
+ console.log(`Status: ${chalk.yellow('Not Authenticated')}`);
136
+ console.log(`Action: Run 'descix login' to authenticate`);
137
+ }
138
+ console.log();
139
+
140
+ // 3. Workspace Context
141
+ console.log(chalk.white('Workspace Context'));
142
+ console.log(chalk.white('-----------------'));
143
+
144
+ if (workspaceConfig || ctx) {
145
+ console.log(`Config File: ${path.join(workspaceRoot, '.descix', 'workspace.json')}`);
146
+
147
+ if (workspaceConfig) {
148
+ // V2 path — use WorkspaceConfig which handles env.platform/products format
149
+ try {
150
+ const defaultCtx = workspaceConfig.defaultContext;
151
+ if (defaultCtx) {
152
+ console.log(`Community: ${defaultCtx.communityId || 'unknown'}`);
153
+ console.log(`App: ${defaultCtx.appId || 'unknown'}`);
154
+ }
155
+
156
+ // List all configured apps
157
+ const platform = workspaceConfig.env?.platform;
158
+ const products = workspaceConfig.env?.products || [];
159
+ const allApps = [platform?.appId, ...products.map(p => p.appId)].filter(Boolean);
160
+ if (allApps.length > 0) {
161
+ console.log(`Local Apps: ${allApps.join(', ')}`);
162
+ }
163
+
164
+ console.log(`API: ${workspaceConfig.apiUrl || 'unknown'}`);
165
+ console.log(`Local Root: ${workspaceRoot}`);
166
+ } catch (error) {
167
+ console.log(`Mode: ${chalk.red('Unknown/Invalid')} (${error.message})`);
168
+ }
169
+ } else {
170
+ // Legacy PathContext path
171
+ const wsRawConfig = ctx.getWorkspaceConfig();
172
+ try {
173
+ const mode = detectWorkspaceMode(wsRawConfig);
174
+ console.log(`Mode: ${chalk.green(mode)}`);
175
+
176
+ if (wsRawConfig.communities) {
177
+ for (const [commId, comm] of Object.entries(wsRawConfig.communities)) {
178
+ const appCount = Object.keys(comm.apps || {}).length;
179
+ console.log(chalk.gray(` └─ ${commId}: ${appCount} app(s)`));
180
+ }
181
+ }
182
+ console.log(`Local Root: ${workspaceRoot}`);
183
+ } catch (error) {
184
+ console.log(`Mode: ${chalk.red('Unknown/Invalid')} (${error.message})`);
185
+ }
186
+ }
187
+ } else {
188
+ console.log(`Status: ${chalk.yellow('No workspace found')}`);
189
+ console.log(`Action: Run 'descix init -c <community> -a <app> -p .' to create a workspace`);
190
+ }
191
+ console.log();
192
+
193
+ // 4. Configuration & Tools
194
+ console.log(chalk.white('Configuration'));
195
+ console.log(chalk.white('-------------'));
196
+
197
+ if (globalConfig.hasBaseFolder()) {
198
+ console.log(`Base Drive Folder: ${chalk.green('Registered')} (ID: ${globalConfig.user_base_folder_id})`);
199
+ } else {
200
+ console.log(`Base Drive Folder: ${chalk.yellow('Not Registered')}`);
201
+ }
202
+
203
+ // Check for service key (dev-overrides.json) - use workspace root if available
204
+ const checkDir = workspaceRoot || process.cwd();
205
+ const overridesPath = path.join(checkDir, 'dev-overrides.json');
206
+ try {
207
+ await fs.access(overridesPath);
208
+ console.log(`Service Key: ${chalk.green('Present')} (dev-overrides.json)`);
209
+ } catch {
210
+ console.log(`Service Key: ${chalk.gray('None')} (dev-overrides.json)`);
211
+ }
212
+
213
+ // Check for MCP rules
214
+ const rulesPath = path.join(checkDir, '.cursor', 'rules', 'descix_mcp.mdc');
215
+ try {
216
+ await fs.access(rulesPath);
217
+ console.log(`MCP Rules: ${chalk.green('Deployed')} (.cursor/rules/descix_mcp.mdc)`);
218
+ } catch {
219
+ console.log(`MCP Rules: ${chalk.gray('Not Deployed')}`);
220
+ }
221
+
222
+ // Check gcloud
223
+ const gcloudStatus = await checkGcloud();
224
+ if (gcloudStatus.installed) {
225
+ console.log(`gcloud SDK: ${chalk.green('Installed')} (${gcloudStatus.version})`);
226
+ } else {
227
+ console.log(`gcloud SDK: ${chalk.yellow('Not Installed')}`);
228
+ }
229
+
230
+ // Check ADC
231
+ const adcStatus = await checkAdc();
232
+ if (adcStatus.configured) {
233
+ console.log(`ADC Credentials: ${chalk.green('Configured')} (${adcStatus.source})`);
234
+ } else {
235
+ console.log(`ADC Credentials: ${chalk.yellow('Not Configured')}`);
236
+ }
237
+
238
+ console.log();
239
+ }