decoupled-cli 2.4.1 → 2.4.3

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 CHANGED
@@ -1,6 +1,15 @@
1
1
  # Decoupled Drupal CLI (decoupled-cli)
2
2
 
3
- A command-line interface for managing Decoupled Drupal spaces, monitoring usage, and interacting with the Decoupled Drupal API using personal access tokens.
3
+ A command-line interface for managing Decoupled Drupal spaces, monitoring usage, and enabling AI assistants to interact with your platform through the Model Context Protocol (MCP).
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Secure Authentication** - OAuth (Google/GitHub) and Personal Access Tokens
8
+ - 🤖 **AI Integration** - Model Context Protocol support for Claude Code and Cursor
9
+ - 📦 **Space Management** - Create, clone, delete, and monitor Drupal spaces
10
+ - 📊 **Usage Monitoring** - Track storage, bandwidth, and API requests
11
+ - 🚀 **Quick Start** - AI-powered space creation with generated content
12
+ - 📥 **Content Import** - Import content models and data to Drupal
4
13
 
5
14
  ## Installation
6
15
 
@@ -128,6 +137,72 @@ The CLI stores configuration in `~/.decoupled-cli/config.json`:
128
137
 
129
138
  Actual tokens are stored securely using the system keychain (macOS Keychain, Windows Credential Manager, or Linux Secret Service).
130
139
 
140
+ ## AI Integration with MCP
141
+
142
+ The CLI supports the Model Context Protocol (MCP), enabling AI assistants in **Claude Code** and **Cursor** to manage your Drupal spaces using natural language.
143
+
144
+ ### Setup
145
+
146
+ ```bash
147
+ # Log in first (if not already)
148
+ decoupled-cli auth login
149
+
150
+ # Configure MCP for Claude Code
151
+ decoupled-cli mcp configure --ide claude-code
152
+
153
+ # Or configure for Cursor
154
+ decoupled-cli mcp configure --ide cursor
155
+ ```
156
+
157
+ After configuration, restart your IDE and you can interact with your platform naturally:
158
+
159
+ **Example conversations:**
160
+ ```
161
+ You: "Show me all my Drupal spaces"
162
+ AI: Lists all your spaces with status and URLs
163
+
164
+ You: "Create a blog space called Tech News"
165
+ AI: Creates the space and returns the ID and URL
166
+
167
+ You: "What's my current storage usage?"
168
+ AI: Shows organization-wide usage statistics
169
+
170
+ You: "Clone space 5 and name it Staging"
171
+ AI: Clones the space with all content
172
+ ```
173
+
174
+ ### Available MCP Commands
175
+
176
+ ```bash
177
+ # Configure MCP for an IDE
178
+ decoupled-cli mcp configure [--ide <claude-code|cursor>]
179
+
180
+ # Show configuration status
181
+ decoupled-cli mcp status
182
+
183
+ # Remove MCP configuration
184
+ decoupled-cli mcp remove
185
+
186
+ # Show config without writing (for manual setup)
187
+ decoupled-cli mcp configure --show-only
188
+ ```
189
+
190
+ ### What AI Can Do
191
+
192
+ - **Read Operations:**
193
+ - List all spaces with details
194
+ - Get space information
195
+ - View usage statistics
196
+ - Check organization details
197
+
198
+ - **Write Operations:**
199
+ - Create new spaces
200
+ - Clone existing spaces
201
+ - Delete spaces
202
+ - Import content models and data
203
+
204
+ All operations use your existing authentication and permissions.
205
+
131
206
  ## Commands
132
207
 
133
208
  ### Spaces Management
@@ -289,6 +364,58 @@ decoupled-cli spaces retry 123
289
364
  decoupled-cli spaces refresh-usage 123
290
365
  ```
291
366
 
367
+ ### Content Management
368
+
369
+ #### Import Content
370
+ ```bash
371
+ # Import content model and data from JSON file
372
+ decoupled-cli content import --file content-import.json
373
+
374
+ # Preview import without applying changes
375
+ decoupled-cli content import --file content-import.json --preview
376
+
377
+ # Show example import JSON structure
378
+ decoupled-cli content import --example
379
+ ```
380
+
381
+ **Import JSON Format:**
382
+ ```json
383
+ {
384
+ "model": [
385
+ {
386
+ "bundle": "blog_post",
387
+ "label": "Blog Post",
388
+ "description": "Blog article content type",
389
+ "body": true,
390
+ "fields": [
391
+ {
392
+ "id": "subtitle",
393
+ "label": "Subtitle",
394
+ "type": "string"
395
+ },
396
+ {
397
+ "id": "featured_image",
398
+ "label": "Featured Image",
399
+ "type": "image"
400
+ }
401
+ ]
402
+ }
403
+ ],
404
+ "content": [
405
+ {
406
+ "id": "post1",
407
+ "type": "node.blog_post",
408
+ "path": "/blog/my-first-post",
409
+ "values": {
410
+ "title": "My First Post",
411
+ "body": "<p>Post content here</p>",
412
+ "subtitle": "An introduction"
413
+ }
414
+ }
415
+ ]
416
+ }
417
+ ```
418
+
292
419
  ### Usage Monitoring
293
420
 
294
421
  #### Organization Usage
@@ -506,6 +633,7 @@ decoupled-cli auth test --permissions
506
633
 
507
634
  The CLI directly uses the Decoupled Drupal API with these endpoints:
508
635
 
636
+ **Space Management:**
509
637
  - `GET /api/spaces` - List spaces
510
638
  - `POST /api/spaces` - Create space
511
639
  - `GET /api/spaces/{id}` - Get space details
@@ -514,17 +642,121 @@ The CLI directly uses the Decoupled Drupal API with these endpoints:
514
642
  - `POST /api/spaces/{id}/clone` - Clone space
515
643
  - `POST /api/spaces/{id}/archive` - Archive space
516
644
  - `POST /api/spaces/{id}/unarchive` - Unarchive space
645
+
646
+ **Content Operations:**
647
+ - `POST /api/spaces/{id}/content-import` - Import content model and data
648
+
649
+ **Usage & Monitoring:**
517
650
  - `GET /api/usage` - Get usage data
518
- - Token management endpoints (web interface only)
651
+ - `GET /api/organization` - Get organization details
652
+
653
+ **MCP (Model Context Protocol):**
654
+ - `GET /api/mcp/sse` - SSE connection for MCP clients
655
+ - `POST /api/mcp/messages` - JSON-RPC 2.0 message handler
656
+
657
+ **Authentication:**
658
+ - Token management endpoints (web interface only at `/organization/tokens`)
519
659
 
520
660
  ## Contributing
521
661
 
522
662
  The CLI is built with:
523
- - Node.js/TypeScript
524
- - Commander.js for CLI framework
525
- - Axios for HTTP requests
526
- - Keytar for secure credential storage
527
- - Chalk for colored output
528
- - CLI-table3 for formatted tables
529
-
530
- For development and contribution guidelines, see the CLI repository.
663
+ - **Node.js/TypeScript** - Core runtime and type safety
664
+ - **Commander.js** - CLI framework and command parsing
665
+ - **Inquirer** - Interactive prompts
666
+ - **Axios** - HTTP requests for API calls
667
+ - **Keytar** - Secure credential storage (system keychain)
668
+ - **Chalk** - Colored terminal output
669
+ - **CLI-table3** - Formatted table output
670
+ - **Open** - Cross-platform browser opening
671
+
672
+ ### Development
673
+
674
+ ```bash
675
+ # Clone repository
676
+ git clone https://github.com/nextagencyio/decoupled-dashboard.git
677
+ cd decoupled-dashboard/cli
678
+
679
+ # Install dependencies
680
+ npm install
681
+
682
+ # Build TypeScript
683
+ npm run build
684
+
685
+ # Run locally
686
+ npm start -- [command]
687
+
688
+ # Watch mode for development
689
+ npm run dev
690
+ ```
691
+
692
+ ### Project Structure
693
+
694
+ ```
695
+ cli/
696
+ ├── src/
697
+ │ ├── commands/ # CLI command implementations
698
+ │ │ ├── auth.ts # Authentication commands
699
+ │ │ ├── auth-interactive.ts # OAuth flow
700
+ │ │ ├── spaces.ts # Space management
701
+ │ │ ├── content.ts # Content import
702
+ │ │ ├── mcp.ts # MCP configuration
703
+ │ │ ├── usage.ts # Usage statistics
704
+ │ │ ├── org.ts # Organization management
705
+ │ │ ├── health.ts # Health checks
706
+ │ │ └── config.ts # CLI configuration
707
+ │ ├── lib/ # Shared utilities
708
+ │ │ ├── api.ts # API client
709
+ │ │ └── config.ts # Config management
710
+ │ └── index.ts # CLI entry point
711
+ ├── examples/ # Example files
712
+ │ └── content-import-sample.json
713
+ └── package.json
714
+ ```
715
+
716
+ ## Version & Updates
717
+
718
+ ```bash
719
+ # Check current version
720
+ decoupled-cli --version
721
+
722
+ # Update to latest
723
+ npm install -g decoupled-cli@latest
724
+
725
+ # View changelog
726
+ npm view decoupled-cli versions
727
+ ```
728
+
729
+ **Current Version:** 2.4.1
730
+
731
+ **Recent Changes:**
732
+ - Added Model Context Protocol (MCP) support
733
+ - Implemented OAuth authentication flow
734
+ - Added content import functionality
735
+ - Removed legacy AI configuration files
736
+
737
+ ## Getting Help
738
+
739
+ ```bash
740
+ # General help
741
+ decoupled-cli --help
742
+
743
+ # Command-specific help
744
+ decoupled-cli [command] --help
745
+
746
+ # Examples
747
+ decoupled-cli spaces --help
748
+ decoupled-cli mcp configure --help
749
+ ```
750
+
751
+ **Additional Resources:**
752
+ - Documentation: Check the main repository docs
753
+ - Issues: Report bugs on GitHub
754
+ - Support: Contact through the dashboard
755
+
756
+ ## License
757
+
758
+ MIT
759
+
760
+ ---
761
+
762
+ Made with ❤️ by the Decoupled.io team
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decoupled-cli",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "description": "Command-line interface for managing Decoupled Drupal spaces, deploying content, and automating workflows. Features AI-powered quick-start, content import, and CI/CD integration.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,4 +0,0 @@
1
- import { Command } from 'commander';
2
- declare const downloadCommand: Command;
3
- export { downloadCommand };
4
- //# sourceMappingURL=download.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/commands/download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,QAAA,MAAM,eAAe,SAA0B,CAAC;AAiGhD,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -1,127 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.downloadCommand = void 0;
40
- const commander_1 = require("commander");
41
- const chalk_1 = __importDefault(require("chalk"));
42
- const fs = __importStar(require("fs"));
43
- const path = __importStar(require("path"));
44
- const downloadCommand = new commander_1.Command('download');
45
- exports.downloadCommand = downloadCommand;
46
- downloadCommand
47
- .description('Download AI configuration files')
48
- .argument('<file>', 'AI config file to download (cursorrules, claude, gemini)')
49
- .action(async (file) => {
50
- try {
51
- // Normalize the file argument
52
- const availableFiles = {
53
- 'cursorrules': { filename: '.cursorrules', displayName: 'Cursor Rules' },
54
- 'cursor': { filename: '.cursorrules', displayName: 'Cursor Rules' },
55
- 'claude': { filename: 'CLAUDE.md', displayName: 'Claude Code Configuration' },
56
- 'gemini': { filename: 'GEMINI.md', displayName: 'Google Gemini Configuration' }
57
- };
58
- const normalizedFile = file.toLowerCase();
59
- if (!availableFiles[normalizedFile]) {
60
- console.error(chalk_1.default.red(`❌ Unknown file: ${file}`));
61
- console.log(chalk_1.default.gray('\nAvailable files:'));
62
- Object.entries(availableFiles).forEach(([key, config]) => {
63
- console.log(chalk_1.default.gray(` • ${key} → ${config.filename} (${config.displayName})`));
64
- });
65
- process.exit(1);
66
- }
67
- const fileConfig = availableFiles[normalizedFile];
68
- const sourceFileName = fileConfig.filename;
69
- const displayName = fileConfig.displayName;
70
- // Get the directory where the CLI is installed
71
- const cliDir = path.resolve(__dirname, '../..');
72
- const examplesDir = path.join(cliDir, 'examples');
73
- const sourcePath = path.join(examplesDir, sourceFileName);
74
- // Check if source file exists
75
- if (!fs.existsSync(sourcePath)) {
76
- console.error(chalk_1.default.red(`❌ Source file not found: ${sourcePath}`));
77
- process.exit(1);
78
- }
79
- // Destination path (current working directory)
80
- const destPath = path.join(process.cwd(), sourceFileName);
81
- // Check if destination file already exists
82
- if (fs.existsSync(destPath)) {
83
- const { default: inquirer } = await Promise.resolve().then(() => __importStar(require('inquirer')));
84
- const answers = await inquirer.prompt([
85
- {
86
- type: 'confirm',
87
- name: 'overwrite',
88
- message: `File ${sourceFileName} already exists. Overwrite?`,
89
- default: false
90
- }
91
- ]);
92
- if (!answers.overwrite) {
93
- console.log(chalk_1.default.yellow('Operation cancelled.'));
94
- return;
95
- }
96
- }
97
- // Read and write the file
98
- const content = fs.readFileSync(sourcePath, 'utf8');
99
- fs.writeFileSync(destPath, content, 'utf8');
100
- console.log(chalk_1.default.green(`✅ Downloaded ${displayName}`));
101
- console.log(chalk_1.default.gray(`📁 Saved to: ${destPath}`));
102
- // Show file size and first few lines as preview
103
- const stats = fs.statSync(destPath);
104
- const lines = content.split('\n');
105
- const previewLines = lines.slice(0, 3).join('\n');
106
- console.log(chalk_1.default.gray(`📊 Size: ${stats.size} bytes`));
107
- console.log(chalk_1.default.gray('📄 Preview:'));
108
- console.log(chalk_1.default.dim(previewLines + (lines.length > 3 ? '\n...' : '')));
109
- }
110
- catch (error) {
111
- console.error(chalk_1.default.red('❌ Failed to download file:'), error instanceof Error ? error.message : String(error));
112
- process.exit(1);
113
- }
114
- });
115
- // Add help examples
116
- downloadCommand.addHelpText('after', `
117
- Examples:
118
- decoupled-cli download cursorrules Download .cursorrules file for Cursor AI
119
- decoupled-cli download claude Download CLAUDE.md configuration
120
- decoupled-cli download gemini Download GEMINI.md configuration
121
-
122
- Available Files:
123
- • cursorrules/.cursor → .cursorrules (Cursor AI configuration)
124
- • claude → CLAUDE.md (Claude Code configuration)
125
- • gemini → GEMINI.md (Google Gemini configuration)
126
- `);
127
- //# sourceMappingURL=download.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/commands/download.ts"],"names":[],"mappings":"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"}