@plexor-dev/claude-code-plugin-staging 0.1.0-beta.2 → 0.1.0-beta.20

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
@@ -32,14 +32,11 @@ This installs slash commands to `~/.claude/commands/`.
32
32
 
33
33
  | Command | Description |
34
34
  |---------|-------------|
35
- | `/plexor-login` | Authenticate with Plexor |
36
- | `/plexor-status` | View usage stats and savings |
37
- | `/plexor-mode` | Set optimization mode (eco/balanced/quality/passthrough) |
38
- | `/plexor-provider` | Force specific provider (auto/claude/deepseek/mistral/gemini) |
39
- | `/plexor-config` | Quick config (enable/disable/cache/reset) |
40
- | `/plexor-settings` | Advanced settings (API URL, mode, provider) |
41
- | `/plexor-enabled` | Enable/disable the proxy |
35
+ | `/plexor-setup` | First-time setup wizard |
36
+ | `/plexor-login` | Authenticate with Plexor API key |
42
37
  | `/plexor-logout` | Sign out and clear credentials |
38
+ | `/plexor-status` | View usage stats and savings |
39
+ | `/plexor-enabled` | Enable/disable Plexor routing |
43
40
 
44
41
  ## How It Works
45
42
 
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Plexor Agent — set agent autonomy level.
5
+ *
6
+ * Agent modes control how the AI agent behaves:
7
+ * supervised: Agent describes actions, waits for your confirmation
8
+ * autonomous: Agent acts independently within safe bounds (default)
9
+ * danger-full-auto: Full autonomy, no guardrails — forces tool execution
10
+ *
11
+ * Usage:
12
+ * /plexor-agent Show current agent mode
13
+ * /plexor-agent supervised Confirm before every action
14
+ * /plexor-agent autonomous Smart autonomy (default)
15
+ * /plexor-agent danger-full-auto No guardrails
16
+ */
17
+
18
+ const { loadConfig, saveConfig, readSetting } = require('../lib/config-utils');
19
+
20
+ const VALID = ['supervised', 'autonomous', 'danger-full-auto'];
21
+ const DESC = {
22
+ supervised: 'Confirm before acting',
23
+ autonomous: 'Acts within safe bounds (default)',
24
+ 'danger-full-auto': 'Full autonomy, no guardrails'
25
+ };
26
+ const ALIASES = {
27
+ auto: 'autonomous',
28
+ fullauto: 'danger-full-auto',
29
+ 'full-auto': 'danger-full-auto',
30
+ full_auto: 'danger-full-auto',
31
+ danger: 'danger-full-auto'
32
+ };
33
+
34
+ function main() {
35
+ const args = process.argv.slice(2);
36
+ const config = loadConfig();
37
+ const current = readSetting(config, 'orchestrationMode', 'orchestration_mode', 'PLEXOR_ORCHESTRATION_MODE', VALID, 'autonomous');
38
+
39
+ if (args.length === 0) {
40
+ console.log(`Agent Mode: ${current.value} (${current.source})`);
41
+ console.log(` ${DESC[current.value]}`);
42
+ console.log('');
43
+ console.log('Options:');
44
+ for (const v of VALID) {
45
+ const marker = v === current.value ? '>>' : ' ';
46
+ console.log(` ${marker} ${v.padEnd(20)} ${DESC[v]}`);
47
+ }
48
+ console.log('');
49
+ console.log('Set: /plexor-agent <mode>');
50
+ return;
51
+ }
52
+
53
+ let requested = args[0].toLowerCase().trim();
54
+ if (ALIASES[requested]) requested = ALIASES[requested];
55
+
56
+ if (!VALID.includes(requested)) {
57
+ console.error(`Invalid agent mode: "${args[0]}"`);
58
+ console.error(`Valid: ${VALID.join(', ')}`);
59
+ process.exit(1);
60
+ }
61
+
62
+ if (current.source === 'environment' && requested !== current.value) {
63
+ console.log(`Warning: PLEXOR_ORCHESTRATION_MODE env var is set to "${current.value}". Config updated but env var takes priority.`);
64
+ }
65
+
66
+ if (requested === current.value && current.source === 'config') {
67
+ console.log(`Already set to "${requested}".`);
68
+ return;
69
+ }
70
+
71
+ config.settings = config.settings || {};
72
+ config.settings.orchestrationMode = requested;
73
+ if (config.settings.orchestration_mode) delete config.settings.orchestration_mode;
74
+ if (!saveConfig(config)) { process.exit(1); }
75
+
76
+ console.log(`Agent mode: ${current.value} → ${requested}`);
77
+ console.log(` ${DESC[requested]}`);
78
+ if (requested === 'danger-full-auto') {
79
+ console.log(' Warning: This forces tool execution on every turn with tools.');
80
+ }
81
+ console.log(' Takes effect on next request.');
82
+ }
83
+
84
+ main();
@@ -0,0 +1,36 @@
1
+ ---
2
+ description: Set Plexor agent autonomy — supervised, autonomous, or danger-full-auto (user)
3
+ ---
4
+
5
+ **Step 1: Check if arguments were provided.**
6
+
7
+ If `$ARGUMENTS` contains a value (e.g., `/plexor-agent autonomous`), apply it directly:
8
+
9
+ ```bash
10
+ node ~/.claude/plugins/plexor/commands/plexor-agent.js $ARGUMENTS
11
+ ```
12
+
13
+ Present the output to the user and STOP. Do NOT call any other tools.
14
+
15
+ **Step 2: If NO arguments were provided** (just `/plexor-agent`), first show the current state:
16
+
17
+ ```bash
18
+ node ~/.claude/plugins/plexor/commands/plexor-agent.js
19
+ ```
20
+
21
+ Then immediately use **AskUserQuestion** to let the user pick a mode:
22
+
23
+ - **Question**: "Which agent autonomy mode?"
24
+ - **Header**: "Agent mode"
25
+ - **Options** (exactly 3):
26
+ 1. **supervised** — "Confirm before every action"
27
+ 2. **autonomous (Recommended)** — "Acts within safe bounds, default mode"
28
+ 3. **danger-full-auto** — "Full autonomy, no guardrails"
29
+
30
+ **Step 3: After the user selects**, apply their choice:
31
+
32
+ ```bash
33
+ node ~/.claude/plugins/plexor/commands/plexor-agent.js <selected_value>
34
+ ```
35
+
36
+ Present the output and STOP. Do NOT re-execute commands or call other tools.
@@ -14,26 +14,108 @@
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
16
 
17
- // Import settings manager for automatic Claude Code configuration
18
- const { settingsManager, PLEXOR_STAGING_URL, PLEXOR_PROD_URL } = require('../lib/settings-manager');
17
+ // Import centralized constants with HOME directory validation
18
+ const { PLEXOR_DIR, CONFIG_PATH } = require('../lib/constants');
19
19
 
20
- const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
21
- const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
20
+ // Import settings manager with error handling for missing lib
21
+ let settingsManager, PLEXOR_STAGING_URL, PLEXOR_PROD_URL;
22
+ try {
23
+ const lib = require('../lib/settings-manager');
24
+ settingsManager = lib.settingsManager;
25
+ PLEXOR_STAGING_URL = lib.PLEXOR_STAGING_URL;
26
+ PLEXOR_PROD_URL = lib.PLEXOR_PROD_URL;
27
+ } catch (err) {
28
+ if (err.code === 'MODULE_NOT_FOUND') {
29
+ console.error('Error: Plexor plugin files are missing or corrupted.');
30
+ console.error(' Please reinstall: npm install @plexor-dev/claude-code-plugin-staging');
31
+ process.exit(1);
32
+ }
33
+ throw err;
34
+ }
22
35
 
23
36
  function loadConfig() {
24
37
  try {
38
+ if (!fs.existsSync(CONFIG_PATH)) {
39
+ return { version: 1, auth: {}, settings: {} };
40
+ }
25
41
  const data = fs.readFileSync(CONFIG_PATH, 'utf8');
26
- return JSON.parse(data);
27
- } catch {
42
+ if (!data || data.trim() === '') {
43
+ return { version: 1, auth: {}, settings: {} };
44
+ }
45
+ const config = JSON.parse(data);
46
+ if (typeof config !== 'object' || config === null) {
47
+ console.warn('Warning: Config file has invalid format, using defaults');
48
+ return { version: 1, auth: {}, settings: {} };
49
+ }
50
+ return config;
51
+ } catch (err) {
52
+ if (err instanceof SyntaxError) {
53
+ console.warn('Warning: Config file is corrupted, using defaults');
54
+ // Backup corrupted file
55
+ try {
56
+ fs.copyFileSync(CONFIG_PATH, CONFIG_PATH + '.corrupted');
57
+ } catch { /* ignore */ }
58
+ }
28
59
  return { version: 1, auth: {}, settings: {} };
29
60
  }
30
61
  }
31
62
 
32
63
  function saveConfig(config) {
33
- if (!fs.existsSync(PLEXOR_DIR)) {
34
- fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
64
+ try {
65
+ if (!fs.existsSync(PLEXOR_DIR)) {
66
+ fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
67
+ }
68
+
69
+ // Atomic write: write to temp file, then rename
70
+ const crypto = require('crypto');
71
+ const tempId = crypto.randomBytes(8).toString('hex');
72
+ const tempPath = path.join(PLEXOR_DIR, `.config.${tempId}.tmp`);
73
+
74
+ fs.writeFileSync(tempPath, JSON.stringify(config, null, 2), { mode: 0o600 });
75
+ fs.renameSync(tempPath, CONFIG_PATH);
76
+ return true;
77
+ } catch (err) {
78
+ if (err.code === 'EACCES' || err.code === 'EPERM') {
79
+ console.error(`Error: Cannot write to ~/.plexor/config.json`);
80
+ console.error(' Check file permissions or run with appropriate access.');
81
+ } else {
82
+ console.error('Failed to save config:', err.message);
83
+ }
84
+ return false;
35
85
  }
36
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
86
+ }
87
+
88
+ /**
89
+ * Validate API key format
90
+ * @param {string} key - API key to validate
91
+ * @returns {boolean} true if valid format
92
+ */
93
+ function isValidApiKeyFormat(key) {
94
+ return key && typeof key === 'string' && key.startsWith('plx_') && key.length >= 20;
95
+ }
96
+
97
+ /**
98
+ * Check for state mismatch between config.json enabled flag and settings.json routing
99
+ * @param {boolean} configEnabled - enabled flag from config.json
100
+ * @param {boolean} routingActive - whether settings.json has Plexor routing configured
101
+ * @returns {Object|null} mismatch details or null if states are consistent
102
+ */
103
+ function checkStateMismatch(configEnabled, routingActive) {
104
+ if (configEnabled && !routingActive) {
105
+ return {
106
+ type: 'config-enabled-routing-inactive',
107
+ message: 'Config shows enabled but Claude routing is not configured',
108
+ suggestion: 'Run /plexor-enabled true to sync and configure routing'
109
+ };
110
+ }
111
+ if (!configEnabled && routingActive) {
112
+ return {
113
+ type: 'config-disabled-routing-active',
114
+ message: 'Config shows disabled but Claude routing is active',
115
+ suggestion: 'Run /plexor-enabled false to sync and disable routing'
116
+ };
117
+ }
118
+ return null;
37
119
  }
38
120
 
39
121
  function main() {
@@ -49,6 +131,10 @@ function main() {
49
131
  if (args.length === 0) {
50
132
  const status = currentEnabled ? '● Enabled' : '○ Disabled';
51
133
  const routingStr = routingStatus.enabled ? '● Active' : '○ Inactive';
134
+
135
+ // Check for state mismatch between config enabled flag and routing status
136
+ const stateMismatch = checkStateMismatch(currentEnabled, routingStatus.enabled);
137
+
52
138
  console.log(`┌─────────────────────────────────────────────┐`);
53
139
  console.log(`│ Plexor Proxy Status │`);
54
140
  console.log(`├─────────────────────────────────────────────┤`);
@@ -57,6 +143,12 @@ function main() {
57
143
  if (routingStatus.enabled) {
58
144
  console.log(`│ Endpoint: ${(routingStatus.isStaging ? 'Staging' : 'Production').padEnd(32)}│`);
59
145
  }
146
+ if (stateMismatch) {
147
+ console.log(`├─────────────────────────────────────────────┤`);
148
+ console.log(`│ ⚠ State mismatch detected: │`);
149
+ console.log(`│ ${stateMismatch.message.padEnd(42)}│`);
150
+ console.log(`│ ${stateMismatch.suggestion.padEnd(42)}│`);
151
+ }
60
152
  console.log(`├─────────────────────────────────────────────┤`);
61
153
  console.log(`│ Usage: │`);
62
154
  console.log(`│ /plexor-enabled true - Enable proxy │`);
@@ -80,26 +172,93 @@ function main() {
80
172
  process.exit(1);
81
173
  }
82
174
 
83
- // Update Plexor plugin config
84
- config.settings = config.settings || {};
85
- config.settings.enabled = newEnabled;
86
- saveConfig(config);
87
-
88
175
  // THE KEY FEATURE: Update Claude Code settings.json routing
89
176
  let routingUpdated = false;
177
+ let missingApiKey = false;
178
+ let invalidApiKey = false;
179
+
90
180
  if (newEnabled) {
91
- // Enable routing - need API key from config
92
- if (apiKey) {
181
+ // Enable routing - need valid API key from config
182
+ if (!apiKey) {
183
+ missingApiKey = true;
184
+ } else if (!isValidApiKeyFormat(apiKey)) {
185
+ invalidApiKey = true;
186
+ } else {
187
+ // Update Plexor plugin config first
188
+ config.settings = config.settings || {};
189
+ config.settings.enabled = newEnabled;
190
+ if (!saveConfig(config)) {
191
+ process.exit(1);
192
+ }
193
+
93
194
  // STAGING PACKAGE - uses staging API
94
195
  const apiUrl = config.settings?.apiUrl || 'https://staging.api.plexor.dev';
95
196
  const useStaging = apiUrl.includes('staging');
96
197
  routingUpdated = settingsManager.enablePlexorRouting(apiKey, { useStaging });
97
- } else {
98
- console.log('⚠ No API key found. Run /plexor-login first.');
198
+
199
+ // Check if settings.json update failed
200
+ if (!routingUpdated) {
201
+ console.log(`┌─────────────────────────────────────────────┐`);
202
+ console.log(`│ ✗ Failed to Enable Plexor Routing │`);
203
+ console.log(`├─────────────────────────────────────────────┤`);
204
+ console.log(`│ Could not update ~/.claude/settings.json │`);
205
+ console.log(`│ Config.json was updated but routing failed.│`);
206
+ console.log(`├─────────────────────────────────────────────┤`);
207
+ console.log(`│ Check file permissions and try again. │`);
208
+ console.log(`└─────────────────────────────────────────────┘`);
209
+ process.exit(1);
210
+ }
99
211
  }
100
212
  } else {
213
+ // Update Plexor plugin config
214
+ config.settings = config.settings || {};
215
+ config.settings.enabled = newEnabled;
216
+ if (!saveConfig(config)) {
217
+ process.exit(1);
218
+ }
219
+
101
220
  // Disable routing - remove env vars from settings.json
102
221
  routingUpdated = settingsManager.disablePlexorRouting();
222
+
223
+ // Check if settings.json update failed
224
+ if (!routingUpdated) {
225
+ console.log(`┌─────────────────────────────────────────────┐`);
226
+ console.log(`│ ✗ Failed to Disable Plexor Routing │`);
227
+ console.log(`├─────────────────────────────────────────────┤`);
228
+ console.log(`│ Could not update ~/.claude/settings.json │`);
229
+ console.log(`│ Config.json was updated but routing failed.│`);
230
+ console.log(`├─────────────────────────────────────────────┤`);
231
+ console.log(`│ Check file permissions and try again. │`);
232
+ console.log(`└─────────────────────────────────────────────┘`);
233
+ process.exit(1);
234
+ }
235
+ }
236
+
237
+ // Show error if no API key when enabling
238
+ if (missingApiKey) {
239
+ console.log(`┌─────────────────────────────────────────────┐`);
240
+ console.log(`│ ✗ Cannot Enable Plexor │`);
241
+ console.log(`├─────────────────────────────────────────────┤`);
242
+ console.log(`│ No API key configured. │`);
243
+ console.log(`│ Run /plexor-setup first. │`);
244
+ console.log(`├─────────────────────────────────────────────┤`);
245
+ console.log(`│ Get your API key at: │`);
246
+ console.log(`│ https://plexor.dev/dashboard/api-keys │`);
247
+ console.log(`└─────────────────────────────────────────────┘`);
248
+ process.exit(1);
249
+ }
250
+
251
+ // Show error if API key format is invalid
252
+ if (invalidApiKey) {
253
+ console.log(`┌─────────────────────────────────────────────┐`);
254
+ console.log(`│ ✗ Cannot Enable Plexor │`);
255
+ console.log(`├─────────────────────────────────────────────┤`);
256
+ console.log(`│ Invalid API key format in config. │`);
257
+ console.log(`│ Keys must start with "plx_" (20+ chars). │`);
258
+ console.log(`├─────────────────────────────────────────────┤`);
259
+ console.log(`│ Run /plexor-setup to fix it. │`);
260
+ console.log(`└─────────────────────────────────────────────┘`);
261
+ process.exit(1);
103
262
  }
104
263
 
105
264
  const newStatus = newEnabled ? '● Enabled' : '○ Disabled';
@@ -2,27 +2,45 @@
2
2
  description: Enable or disable Plexor proxy (routes all traffic through Plexor API) (user)
3
3
  ---
4
4
 
5
- # Plexor Enabled
5
+ **RULE: Execute this workflow EXACTLY ONCE. After completing all steps, STOP. DO NOT restart the workflow. DO NOT re-execute any commands. DO NOT call any other tools.**
6
6
 
7
- Run this command to view or toggle the Plexor proxy:
7
+ ## If argument provided (on/off/true/false)
8
+
9
+ Run the command directly with the argument:
8
10
 
9
11
  ```bash
10
- node ~/.claude/plugins/plexor/commands/plexor-enabled.js
12
+ node ~/.claude/plugins/plexor/commands/plexor-enabled.js <argument>
11
13
  ```
12
14
 
13
- To enable or disable, pass an argument:
15
+ Then present the output to the user and STOP.
16
+
17
+ ## If NO argument provided
18
+
19
+ **Step 1**: Get current status by running:
14
20
 
15
21
  ```bash
16
- node ~/.claude/plugins/plexor/commands/plexor-enabled.js true
17
- node ~/.claude/plugins/plexor/commands/plexor-enabled.js false
22
+ node ~/.claude/plugins/plexor/commands/plexor-enabled.js
18
23
  ```
19
24
 
20
- Use the Bash tool to execute this command.
25
+ **Step 2**: Based on the output, use AskUserQuestion to present options:
26
+
27
+ If currently ENABLED (shows "Enabled" or "Active"):
28
+ - Question: "Plexor is currently ON. What would you like to do?"
29
+ - Header: "Plexor"
30
+ - Options:
31
+ 1. **Turn OFF** - Disable Plexor routing (connect directly to Anthropic)
32
+ 2. **Keep ON** - No change
33
+
34
+ If currently DISABLED (shows "Disabled" or "Inactive"):
35
+ - Question: "Plexor is currently OFF. What would you like to do?"
36
+ - Header: "Plexor"
37
+ - Options:
38
+ 1. **Turn ON** - Enable Plexor routing
39
+ 2. **Keep OFF** - No change
21
40
 
22
- **IMPORTANT**: After running this command and displaying the output, STOP. Do not:
23
- - Read any files
24
- - Explore the codebase
25
- - Run additional commands
26
- - Ask follow-up questions
41
+ **Step 3**: Based on user selection:
42
+ - If "Turn OFF" selected: Run `node ~/.claude/plugins/plexor/commands/plexor-enabled.js false`
43
+ - If "Turn ON" selected: Run `node ~/.claude/plugins/plexor/commands/plexor-enabled.js true`
44
+ - If "Keep" selected: Do nothing, just confirm current state
27
45
 
28
- The command output is the complete response. Simply show the output and wait for the user's next input.
46
+ After completing all steps, STOP. DO NOT restart the workflow.