@plexor-dev/claude-code-plugin 0.1.0-beta.17 → 0.1.0-beta.19

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.
@@ -40,3 +40,5 @@ Other commands:
40
40
  - /plexor-provider - Change provider
41
41
  - /plexor-enabled - Enable/disable proxy
42
42
  ```
43
+
44
+ **IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
@@ -52,3 +52,5 @@ Plexor Proxy: DISABLED
52
52
  Your prompts will go directly to Anthropic at full price.
53
53
  Run /plexor-enabled to re-enable optimization.
54
54
  ```
55
+
56
+ **IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
@@ -82,3 +82,5 @@ Dashboard: https://plexor.dev/dashboard
82
82
  ```
83
83
 
84
84
  If the API call fails, tell the user the key may be invalid and ask them to check it.
85
+
86
+ **IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
@@ -40,3 +40,5 @@ To use Plexor again, run /plexor-login
40
40
 
41
41
  Dashboard: https://plexor.dev/dashboard
42
42
  ```
43
+
44
+ **IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
@@ -45,3 +45,5 @@ Mode Details:
45
45
  Current mode: [mode]
46
46
  Run /plexor-status to see your savings.
47
47
  ```
48
+
49
+ **IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
@@ -45,3 +45,5 @@ Provider options:
45
45
  Current provider: [preferredProvider]
46
46
  Run /plexor-status to see your usage.
47
47
  ```
48
+
49
+ **IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
@@ -56,3 +56,5 @@ Options:
56
56
  4. **Timeout** - Change request timeout
57
57
 
58
58
  Then update `~/.plexor/config.json` with the new value using the Write tool.
59
+
60
+ **IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
@@ -92,8 +92,18 @@ async function main() {
92
92
  const cacheEnabled = localCache ? 'Enabled' : 'Disabled';
93
93
  const cacheRate = formatPct((summary.cache_hit_rate || 0) * 100);
94
94
 
95
- // Build dashboard URL
96
- const dashboardUrl = apiUrl.replace('.api.', '.').replace('/api', '') + '/dashboard.html';
95
+ // Build dashboard URL from API URL
96
+ // API: https://api.plexor.dev or https://staging.api.plexor.dev
97
+ // Dashboard: https://plexor.dev/dashboard or https://staging.plexor.dev/dashboard
98
+ let dashboardUrl = 'https://plexor.dev/dashboard';
99
+ try {
100
+ const url = new URL(apiUrl);
101
+ // Remove 'api.' prefix from hostname if present
102
+ const host = url.hostname.replace(/^api\./, '').replace(/\.api\./, '.');
103
+ dashboardUrl = `${url.protocol}//${host}/dashboard`;
104
+ } catch {
105
+ // If URL parsing fails, use default
106
+ }
97
107
 
98
108
  // Output formatted status - each line is exactly 43 chars inner width
99
109
  const line = (content) => ` │ ${content.padEnd(43)}│`;
@@ -10,4 +10,12 @@ Run this command to display Plexor statistics:
10
10
  node ~/.claude/plugins/plexor/commands/plexor-status.js
11
11
  ```
12
12
 
13
- Use the Bash tool to execute this single command. Do not read files manually or format output yourself.
13
+ Use the Bash tool to execute this single command.
14
+
15
+ **IMPORTANT**: After running this command and displaying the output, STOP. Do not:
16
+ - Read any files
17
+ - Explore the codebase
18
+ - Run additional commands
19
+ - Ask follow-up questions
20
+
21
+ The command output is the complete response. Simply show the output and wait for the user's next input.
@@ -1,13 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Plexor Interception Hook
4
+ * Plexor Interception Hook - Phase 3 Hypervisor Architecture
5
5
  *
6
- * This script intercepts Claude Code prompts before they are sent to the LLM.
7
- * It optimizes the prompt and optionally routes to a cheaper provider.
6
+ * This script intercepts Claude Code requests before they reach the LLM.
7
+ *
8
+ * In HYPERVISOR MODE (ANTHROPIC_BASE_URL points to Plexor):
9
+ * - All requests pass through unchanged
10
+ * - Server handles all optimization, routing, and quality control
11
+ * - Plugin just adds session tracking metadata
12
+ * - This is the recommended setup for best user experience
13
+ *
14
+ * In PLUGIN MODE (direct to Anthropic):
15
+ * - Plugin does local optimization (legacy behavior)
16
+ * - Less reliable due to client-side state management
8
17
  *
9
18
  * Input: JSON object with messages, model, max_tokens, etc.
10
- * Output: Modified JSON object with optimized messages
19
+ * Output: Passthrough (hypervisor mode) or optimized messages (plugin mode)
11
20
  */
12
21
 
13
22
  const fs = require('fs');
@@ -170,14 +179,27 @@ async function main() {
170
179
  input = await readStdin();
171
180
  request = JSON.parse(input);
172
181
 
173
- // Issue #687: Auto-disable hook when ANTHROPIC_BASE_URL points to Plexor
174
- // If already using Plexor as proxy, don't run local hook (avoid double-processing)
182
+ // Phase 3 Hypervisor Mode Detection
183
+ // When ANTHROPIC_BASE_URL points to Plexor, all intelligence is server-side
184
+ // The plugin just passes through - server handles optimization, routing, quality
175
185
  const baseUrl = process.env.ANTHROPIC_BASE_URL || '';
176
- if (baseUrl.includes('plexor')) {
177
- logger.debug('Plexor proxy detected via ANTHROPIC_BASE_URL, passing through');
178
- // Note: Don't add _plexor metadata here - the proxy will add its own
179
- // This passthrough is transparent to avoid double-processing
180
- return output(request);
186
+ const isHypervisorMode = baseUrl.includes('plexor') || baseUrl.includes('staging.api');
187
+
188
+ if (isHypervisorMode) {
189
+ // HYPERVISOR MODE: Server handles everything
190
+ // Just pass through with minimal metadata for session tracking
191
+ logger.debug('[Plexor] Hypervisor mode active - server handles all optimization');
192
+
193
+ // Add session tracking metadata (server will use this for analytics)
194
+ return output({
195
+ ...request,
196
+ _plexor_client: {
197
+ mode: 'hypervisor',
198
+ plugin_version: '0.1.0-beta.18',
199
+ cwd: process.cwd(),
200
+ timestamp: Date.now(),
201
+ }
202
+ });
181
203
  }
182
204
 
183
205
  // CRITICAL: Check for slash commands FIRST (before agentic check)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexor-dev/claude-code-plugin",
3
- "version": "0.1.0-beta.17",
3
+ "version": "0.1.0-beta.19",
4
4
  "description": "LLM cost optimization plugin for Claude Code - Save up to 90% on AI costs",
5
5
  "main": "lib/constants.js",
6
6
  "scripts": {
@@ -1,18 +1,31 @@
1
1
  #!/bin/bash
2
2
  # Plexor CLI - Claude Code with intelligent optimization
3
+ # This wrapper auto-enables hypervisor mode for direct gateway routing
3
4
 
4
- # Only show Plexor branding if ANTHROPIC_BASE_URL points to Plexor
5
- if [[ "$ANTHROPIC_BASE_URL" == *"plexor"* ]]; then
6
- # Colors
7
- CYAN='\033[0;36m'
8
- GREEN='\033[0;32m'
9
- YELLOW='\033[0;33m'
10
- DIM='\033[2m'
11
- NC='\033[0m' # No Color
5
+ # Colors
6
+ CYAN='\033[0;36m'
7
+ GREEN='\033[0;32m'
8
+ YELLOW='\033[0;33m'
9
+ DIM='\033[2m'
10
+ NC='\033[0m' # No Color
12
11
 
13
- # Plexor ASCII art
14
- echo -e "${CYAN}"
15
- cat << 'EOF'
12
+ CONFIG_FILE="$HOME/.plexor/config.json"
13
+
14
+ # Auto-configure ANTHROPIC_BASE_URL if Plexor is enabled
15
+ if [ -f "$CONFIG_FILE" ]; then
16
+ ENABLED=$(python3 -c "import json; c=json.load(open('$CONFIG_FILE')); print(c.get('settings',{}).get('enabled', False))" 2>/dev/null)
17
+ API_URL=$(python3 -c "import json; c=json.load(open('$CONFIG_FILE')); print(c.get('settings',{}).get('apiUrl', ''))" 2>/dev/null)
18
+ API_KEY=$(python3 -c "import json; c=json.load(open('$CONFIG_FILE')); print(c.get('auth',{}).get('api_key', ''))" 2>/dev/null)
19
+ MODE=$(python3 -c "import json; c=json.load(open('$CONFIG_FILE')); print(c.get('settings',{}).get('mode', 'balanced'))" 2>/dev/null)
20
+
21
+ if [ "$ENABLED" = "True" ] && [ -n "$API_URL" ] && [ -n "$API_KEY" ]; then
22
+ # Set ANTHROPIC_BASE_URL to Plexor gateway (hypervisor mode)
23
+ export ANTHROPIC_BASE_URL="${API_URL}/gateway/anthropic/v1"
24
+ export ANTHROPIC_API_KEY="$API_KEY"
25
+
26
+ # Show Plexor branding
27
+ echo -e "${CYAN}"
28
+ cat << 'EOF'
16
29
  ____ __
17
30
  / __ \/ /__ _ ______ _____
18
31
  / /_/ / / _ \| |/_/ __ \/ ___/
@@ -20,19 +33,13 @@ if [[ "$ANTHROPIC_BASE_URL" == *"plexor"* ]]; then
20
33
  /_/ /_/\___/_/|_|\____/_/
21
34
 
22
35
  EOF
23
- echo -e "${NC}"
24
-
25
- # Show status
26
- CONFIG_FILE="$HOME/.plexor/config.json"
27
- if [ -f "$CONFIG_FILE" ]; then
28
- ENABLED=$(jq -r '.settings.enabled // false' "$CONFIG_FILE" 2>/dev/null)
29
- MODE=$(jq -r '.settings.mode // "balanced"' "$CONFIG_FILE" 2>/dev/null)
30
-
31
- if [ "$ENABLED" = "true" ]; then
32
- echo -e "${GREEN}●${NC} Optimization: ${GREEN}Active${NC} (${MODE} mode)"
33
- else
34
- echo -e "${YELLOW}○${NC} Optimization: ${DIM}Disabled${NC}"
35
- fi
36
+ echo -e "${NC}"
37
+ echo -e "${GREEN}●${NC} Hypervisor Mode: ${GREEN}Active${NC}"
38
+ echo -e " Gateway: ${DIM}${ANTHROPIC_BASE_URL}${NC}"
39
+ echo -e " Mode: ${MODE}"
40
+ echo ""
41
+ else
42
+ echo -e "${YELLOW}○${NC} Plexor: ${DIM}Disabled${NC} (run /plexor-login to enable)"
36
43
  echo ""
37
44
  fi
38
45
  fi
@@ -11,23 +11,49 @@ const fs = require('fs');
11
11
  const path = require('path');
12
12
  const os = require('os');
13
13
 
14
+ /**
15
+ * Get the correct home directory, accounting for sudo.
16
+ * When running with sudo, os.homedir() returns /root, but we want
17
+ * the actual user's home directory.
18
+ */
19
+ function getHomeDir() {
20
+ // Check if running with sudo - SUDO_USER contains the original username
21
+ if (process.env.SUDO_USER) {
22
+ // On Linux/Mac, home directories are typically /home/<user> or /Users/<user>
23
+ const platform = os.platform();
24
+ if (platform === 'darwin') {
25
+ return path.join('/Users', process.env.SUDO_USER);
26
+ } else if (platform === 'linux') {
27
+ return path.join('/home', process.env.SUDO_USER);
28
+ }
29
+ }
30
+ return os.homedir();
31
+ }
32
+
33
+ const HOME_DIR = getHomeDir();
14
34
  const COMMANDS_SOURCE = path.join(__dirname, '..', 'commands');
15
- const CLAUDE_COMMANDS_DIR = path.join(os.homedir(), '.claude', 'commands');
16
- const PLEXOR_CONFIG_DIR = path.join(os.homedir(), '.plexor');
35
+ const CLAUDE_COMMANDS_DIR = path.join(HOME_DIR, '.claude', 'commands');
36
+ const PLEXOR_PLUGINS_DIR = path.join(HOME_DIR, '.claude', 'plugins', 'plexor', 'commands');
37
+ const PLEXOR_CONFIG_DIR = path.join(HOME_DIR, '.plexor');
17
38
 
18
39
  function main() {
19
40
  try {
20
41
  // Create ~/.claude/commands/ if not exists
21
42
  fs.mkdirSync(CLAUDE_COMMANDS_DIR, { recursive: true });
22
43
 
44
+ // Create ~/.claude/plugins/plexor/commands/ for JS executors
45
+ fs.mkdirSync(PLEXOR_PLUGINS_DIR, { recursive: true });
46
+
23
47
  // Create ~/.plexor/ with secure permissions (owner only)
24
48
  fs.mkdirSync(PLEXOR_CONFIG_DIR, { recursive: true, mode: 0o700 });
25
49
 
26
- // Get list of command files
27
- const files = fs.readdirSync(COMMANDS_SOURCE)
50
+ // Get list of command files (.md for Claude, .js for executors)
51
+ const mdFiles = fs.readdirSync(COMMANDS_SOURCE)
28
52
  .filter(f => f.endsWith('.md'));
53
+ const jsFiles = fs.readdirSync(COMMANDS_SOURCE)
54
+ .filter(f => f.endsWith('.js'));
29
55
 
30
- if (files.length === 0) {
56
+ if (mdFiles.length === 0) {
31
57
  console.error('No command files found in package. Installation may be corrupt.');
32
58
  process.exit(1);
33
59
  }
@@ -35,7 +61,8 @@ function main() {
35
61
  const installed = [];
36
62
  const backed_up = [];
37
63
 
38
- for (const file of files) {
64
+ // Copy .md command files to ~/.claude/commands/
65
+ for (const file of mdFiles) {
39
66
  const src = path.join(COMMANDS_SOURCE, file);
40
67
  const dest = path.join(CLAUDE_COMMANDS_DIR, file);
41
68
 
@@ -55,6 +82,17 @@ function main() {
55
82
  installed.push(file.replace('.md', ''));
56
83
  }
57
84
 
85
+ // Copy .js executor files to ~/.claude/plugins/plexor/commands/
86
+ const jsInstalled = [];
87
+ for (const file of jsFiles) {
88
+ const src = path.join(COMMANDS_SOURCE, file);
89
+ const dest = path.join(PLEXOR_PLUGINS_DIR, file);
90
+ fs.copyFileSync(src, dest);
91
+ // Make executable
92
+ fs.chmodSync(dest, 0o755);
93
+ jsInstalled.push(file);
94
+ }
95
+
58
96
  // Print success message
59
97
  console.log('');
60
98
  console.log(' ╔═══════════════════════════════════════════════════════════╗');
@@ -74,6 +112,12 @@ function main() {
74
112
  backed_up.forEach(f => console.log(` ${f}`));
75
113
  }
76
114
 
115
+ if (jsInstalled.length > 0) {
116
+ console.log('');
117
+ console.log(' Executors installed to ~/.claude/plugins/plexor/commands/:');
118
+ jsInstalled.forEach(f => console.log(` ${f}`));
119
+ }
120
+
77
121
  console.log('');
78
122
  console.log(' Next steps:');
79
123
  console.log(' 1. Open Claude Code');