@jive-ai/cli 0.0.2 → 0.0.4
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/dist/index.mjs +18 -202
- package/docs/auth.md +378 -0
- package/docs/getting-started.md +263 -0
- package/docs/init.md +591 -0
- package/docs/mcp.md +899 -0
- package/docs/subagents.md +702 -0
- package/docs/sync.md +673 -0
- package/docs/team.md +514 -0
- package/package.json +6 -3
package/dist/index.mjs
CHANGED
|
@@ -218,13 +218,13 @@ async function signupCommand() {
|
|
|
218
218
|
{
|
|
219
219
|
type: "password",
|
|
220
220
|
name: "confirmPassword",
|
|
221
|
-
message: "Confirm password:"
|
|
222
|
-
validate: (value, prev) => {
|
|
223
|
-
if (value !== prev.password) return "Passwords do not match";
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
221
|
+
message: "Confirm password:"
|
|
226
222
|
}
|
|
227
223
|
]);
|
|
224
|
+
if (response.password !== response.confirmPassword) {
|
|
225
|
+
console.log(chalk.yellow("\n❌ Passwords do not match"));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
228
|
if (!response.email || !response.password) {
|
|
229
229
|
console.log(chalk.yellow("\n❌ Signup cancelled"));
|
|
230
230
|
return;
|
|
@@ -567,172 +567,6 @@ async function addToGitignore(pattern) {
|
|
|
567
567
|
const CLAUDE_PLUGINS_DIR = path.join(process.cwd(), ".claude", "plugins");
|
|
568
568
|
const JIVE_CONFIG_DIR = path.join(process.cwd(), ".jive");
|
|
569
569
|
const JIVE_CONFIG_PATH = path.join(JIVE_CONFIG_DIR, "config.json");
|
|
570
|
-
async function installTelemetryPlugin(apiKey, apiUrl) {
|
|
571
|
-
await fs.mkdir(CLAUDE_PLUGINS_DIR, { recursive: true });
|
|
572
|
-
const pluginSourceDir = path.join(process.cwd(), "plugins", "jive-mcp-telemetry");
|
|
573
|
-
const pluginDestDir = path.join(CLAUDE_PLUGINS_DIR, "jive-mcp-telemetry");
|
|
574
|
-
let pluginExists = false;
|
|
575
|
-
try {
|
|
576
|
-
await fs.access(pluginSourceDir);
|
|
577
|
-
pluginExists = true;
|
|
578
|
-
} catch {
|
|
579
|
-
pluginExists = false;
|
|
580
|
-
}
|
|
581
|
-
if (pluginExists) await copyDirectory(pluginSourceDir, pluginDestDir);
|
|
582
|
-
else await createMinimalPlugin(pluginDestDir);
|
|
583
|
-
await fs.mkdir(JIVE_CONFIG_DIR, { recursive: true });
|
|
584
|
-
let config = {};
|
|
585
|
-
try {
|
|
586
|
-
const existingConfig = await fs.readFile(JIVE_CONFIG_PATH, "utf-8");
|
|
587
|
-
config = JSON.parse(existingConfig);
|
|
588
|
-
} catch (error) {
|
|
589
|
-
if (error.code !== "ENOENT") throw error;
|
|
590
|
-
}
|
|
591
|
-
config.apiUrl = apiUrl;
|
|
592
|
-
config.apiKey = apiKey;
|
|
593
|
-
await fs.writeFile(JIVE_CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
594
|
-
}
|
|
595
|
-
async function copyDirectory(src, dest) {
|
|
596
|
-
await fs.mkdir(dest, { recursive: true });
|
|
597
|
-
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
598
|
-
for (const entry of entries) {
|
|
599
|
-
const srcPath = path.join(src, entry.name);
|
|
600
|
-
const destPath = path.join(dest, entry.name);
|
|
601
|
-
if (entry.isDirectory()) await copyDirectory(srcPath, destPath);
|
|
602
|
-
else await fs.copyFile(srcPath, destPath);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
async function createMinimalPlugin(pluginDir) {
|
|
606
|
-
await fs.mkdir(path.join(pluginDir, ".claude-plugin"), { recursive: true });
|
|
607
|
-
await fs.mkdir(path.join(pluginDir, "hooks"), { recursive: true });
|
|
608
|
-
await fs.mkdir(path.join(pluginDir, "scripts"), { recursive: true });
|
|
609
|
-
await fs.writeFile(path.join(pluginDir, ".claude-plugin", "plugin.json"), JSON.stringify({
|
|
610
|
-
name: "jive-mcp-telemetry",
|
|
611
|
-
version: "1.0.0",
|
|
612
|
-
description: "Captures subagent execution traces and tool calls for Jive",
|
|
613
|
-
author: {
|
|
614
|
-
name: "Jive",
|
|
615
|
-
url: "https://github.com/jive-mcp"
|
|
616
|
-
},
|
|
617
|
-
license: "MIT",
|
|
618
|
-
keywords: [
|
|
619
|
-
"subagents",
|
|
620
|
-
"telemetry",
|
|
621
|
-
"monitoring",
|
|
622
|
-
"debugging"
|
|
623
|
-
],
|
|
624
|
-
hooks: "../hooks/hooks.json"
|
|
625
|
-
}, null, 2));
|
|
626
|
-
await fs.writeFile(path.join(pluginDir, "hooks", "hooks.json"), JSON.stringify({ hooks: { PostToolUse: [{
|
|
627
|
-
matcher: ".*",
|
|
628
|
-
hooks: [{
|
|
629
|
-
type: "command",
|
|
630
|
-
command: "node ${CLAUDE_PLUGIN_ROOT}/scripts/capture-tool-use.js"
|
|
631
|
-
}]
|
|
632
|
-
}] } }, null, 2));
|
|
633
|
-
await fs.writeFile(path.join(pluginDir, "scripts", "capture-tool-use.js"), `#!/usr/bin/env node
|
|
634
|
-
|
|
635
|
-
/**
|
|
636
|
-
* Jive Telemetry Capture Script
|
|
637
|
-
*
|
|
638
|
-
* This script runs as a PostToolUse hook to capture tool calls made during
|
|
639
|
-
* subagent execution and send them to the Jive API for tracking.
|
|
640
|
-
*/
|
|
641
|
-
|
|
642
|
-
const fs = require('fs');
|
|
643
|
-
const path = require('path');
|
|
644
|
-
const https = require('https');
|
|
645
|
-
const http = require('http');
|
|
646
|
-
|
|
647
|
-
// Find the project's .jive/config.json by searching upward from cwd
|
|
648
|
-
function findProjectConfig() {
|
|
649
|
-
let dir = process.cwd();
|
|
650
|
-
while (dir !== path.parse(dir).root) {
|
|
651
|
-
const configPath = path.join(dir, '.jive', 'config.json');
|
|
652
|
-
if (fs.existsSync(configPath)) {
|
|
653
|
-
return configPath;
|
|
654
|
-
}
|
|
655
|
-
dir = path.dirname(dir);
|
|
656
|
-
}
|
|
657
|
-
return null;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
const CONFIG_FILE = findProjectConfig();
|
|
661
|
-
const STATE_DIR = path.join(process.env.HOME || process.env.USERPROFILE, '.jive-mcp', 'telemetry');
|
|
662
|
-
const LOG_FILE = path.join(STATE_DIR, 'telemetry.log');
|
|
663
|
-
|
|
664
|
-
// Ensure state directory exists
|
|
665
|
-
try {
|
|
666
|
-
if (!fs.existsSync(STATE_DIR)) {
|
|
667
|
-
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
668
|
-
}
|
|
669
|
-
} catch (err) {
|
|
670
|
-
process.exit(0);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
function log(message) {
|
|
674
|
-
try {
|
|
675
|
-
const timestamp = new Date().toISOString();
|
|
676
|
-
fs.appendFileSync(LOG_FILE, \`[\${timestamp}] \${message}\\n\`);
|
|
677
|
-
} catch (err) {
|
|
678
|
-
// Ignore logging errors
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
function loadConfig() {
|
|
683
|
-
try {
|
|
684
|
-
if (!CONFIG_FILE || !fs.existsSync(CONFIG_FILE)) {
|
|
685
|
-
log('Config file not found, telemetry disabled');
|
|
686
|
-
return null;
|
|
687
|
-
}
|
|
688
|
-
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
689
|
-
return config;
|
|
690
|
-
} catch (err) {
|
|
691
|
-
log(\`Error loading config: \${err.message}\`);
|
|
692
|
-
return null;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
function getCurrentInvocation() {
|
|
697
|
-
try {
|
|
698
|
-
const stateFile = path.join(STATE_DIR, 'current-invocation.json');
|
|
699
|
-
if (!fs.existsSync(stateFile)) {
|
|
700
|
-
return null;
|
|
701
|
-
}
|
|
702
|
-
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
703
|
-
const age = Date.now() - state.timestamp;
|
|
704
|
-
if (age > 3600000) {
|
|
705
|
-
fs.unlinkSync(stateFile);
|
|
706
|
-
return null;
|
|
707
|
-
}
|
|
708
|
-
return state;
|
|
709
|
-
} catch (err) {
|
|
710
|
-
log(\`Error reading invocation state: \${err.message}\`);
|
|
711
|
-
return null;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
function getNextSequenceNumber(invocationId) {
|
|
716
|
-
const seqFile = path.join(STATE_DIR, \`seq-\${invocationId}.txt\`);
|
|
717
|
-
try {
|
|
718
|
-
let seq = 1;
|
|
719
|
-
if (fs.existsSync(seqFile)) {
|
|
720
|
-
seq = parseInt(fs.readFileSync(seqFile, 'utf8')) + 1;
|
|
721
|
-
}
|
|
722
|
-
fs.writeFileSync(seqFile, seq.toString());
|
|
723
|
-
return seq;
|
|
724
|
-
} catch (err) {
|
|
725
|
-
log(\`Error managing sequence number: \${err.message}\`);
|
|
726
|
-
return 1;
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Simplified version - full implementation in the source repository
|
|
731
|
-
log('Telemetry hook triggered (minimal version)');
|
|
732
|
-
process.exit(0);
|
|
733
|
-
`);
|
|
734
|
-
await fs.chmod(path.join(pluginDir, "scripts", "capture-tool-use.js"), 493);
|
|
735
|
-
}
|
|
736
570
|
async function getPluginStatus() {
|
|
737
571
|
const pluginPath = path.join(CLAUDE_PLUGINS_DIR, "jive-mcp-telemetry");
|
|
738
572
|
const configPath = JIVE_CONFIG_PATH;
|
|
@@ -833,7 +667,7 @@ async function initCommand() {
|
|
|
833
667
|
}
|
|
834
668
|
spinner.text = "Scanning for existing subagents...";
|
|
835
669
|
const subagentFiles = await getSubagentFiles();
|
|
836
|
-
let uploadedSubagents =
|
|
670
|
+
let uploadedSubagents = [];
|
|
837
671
|
if (subagentFiles.length > 0) {
|
|
838
672
|
spinner.text = `Found ${subagentFiles.length} subagent(s), uploading...`;
|
|
839
673
|
for (const subagent of subagentFiles) {
|
|
@@ -845,7 +679,10 @@ async function initCommand() {
|
|
|
845
679
|
prompt: subagent.prompt
|
|
846
680
|
});
|
|
847
681
|
await updateSubagentJiveId(subagent.name, created.id);
|
|
848
|
-
uploadedSubagents
|
|
682
|
+
uploadedSubagents.push({
|
|
683
|
+
name: subagent.name,
|
|
684
|
+
id: created.id
|
|
685
|
+
});
|
|
849
686
|
} catch (error) {
|
|
850
687
|
console.warn(chalk.gray(`\nWarning: Could not upload subagent "${subagent.name}": ${error.message}`));
|
|
851
688
|
}
|
|
@@ -853,59 +690,38 @@ async function initCommand() {
|
|
|
853
690
|
}
|
|
854
691
|
spinner.text = "Adding Jive server to .mcp.json...";
|
|
855
692
|
await addMcpServer("jive-mcp", {
|
|
856
|
-
command: "
|
|
857
|
-
args: ["-y", "@jive/mcp-server"],
|
|
693
|
+
command: "jive mcp start",
|
|
858
694
|
env: {
|
|
859
|
-
JIVE_API_URL: process.env.JIVE_API_URL || "http://localhost:5173",
|
|
860
695
|
JIVE_TEAM_ID: teamId,
|
|
861
696
|
JIVE_AUTH_TOKEN: credentials.token
|
|
862
697
|
}
|
|
863
698
|
});
|
|
699
|
+
spinner.text = "Removing uploaded MCP servers from local .mcp.json...";
|
|
700
|
+
if (mcpConfig?.mcpServers) {
|
|
701
|
+
for (const name of Object.keys(mcpConfig.mcpServers)) if (uploadedSubagents.some((subagent) => subagent.name === name)) await removeMcpServer(name);
|
|
702
|
+
}
|
|
864
703
|
spinner.text = "Creating subagent-runner...";
|
|
865
704
|
await createSubagentRunner();
|
|
866
705
|
spinner.text = "Updating .gitignore...";
|
|
867
|
-
await addToGitignore(".jive
|
|
868
|
-
await addToGitignore(".jive/sync.json");
|
|
869
|
-
await addToGitignore(".claude/plugins/");
|
|
706
|
+
await addToGitignore(".jive");
|
|
870
707
|
spinner.text = "Creating project configuration...";
|
|
871
708
|
await saveProjectConfig({
|
|
872
709
|
teamId,
|
|
873
710
|
activeTeamId: teamId,
|
|
874
711
|
lastSync: (/* @__PURE__ */ new Date()).toISOString()
|
|
875
712
|
});
|
|
876
|
-
spinner.text = "Installing Claude Code telemetry plugin...";
|
|
877
|
-
const apiUrl = process.env.JIVE_API_URL || "http://localhost:5173";
|
|
878
|
-
try {
|
|
879
|
-
await installTelemetryPlugin(credentials.token, apiUrl);
|
|
880
|
-
spinner.text = "Telemetry plugin installed";
|
|
881
|
-
} catch (error) {
|
|
882
|
-
console.warn(chalk.yellow(`\nWarning: Could not install telemetry plugin: ${error.message}`));
|
|
883
|
-
}
|
|
884
713
|
spinner.succeed(chalk.green("Project initialized successfully!"));
|
|
885
714
|
console.log(chalk.bold("\n📦 Summary:\n"));
|
|
886
715
|
if (uploadedServers > 0) console.log(chalk.green(` ✓ Uploaded ${uploadedServers} MCP server(s) to team`));
|
|
887
|
-
if (uploadedSubagents > 0) console.log(chalk.green(` ✓ Uploaded ${uploadedSubagents} subagent(s) to team`));
|
|
716
|
+
if (uploadedSubagents.length > 0) console.log(chalk.green(` ✓ Uploaded ${uploadedSubagents.length} subagent(s) to team`));
|
|
888
717
|
console.log(chalk.green(" ✓ Added @jive/mcp server to .mcp.json"));
|
|
889
718
|
console.log(chalk.green(" ✓ Created .claude/agents/subagent-runner.md"));
|
|
890
719
|
console.log(chalk.green(" ✓ Created .jive/config.json"));
|
|
891
720
|
console.log(chalk.green(" ✓ Updated .gitignore"));
|
|
892
|
-
const pluginStatus = await getPluginStatus();
|
|
893
|
-
if (pluginStatus.installed) console.log(chalk.green(" ✓ Installed telemetry plugin to .claude/plugins/"));
|
|
894
721
|
console.log(chalk.bold("\n✨ Next Steps:\n"));
|
|
895
|
-
console.log(chalk.white(" • Restart Claude Code to load the Jive server
|
|
722
|
+
console.log(chalk.white(" • Restart Claude Code to load the Jive MCP server"));
|
|
896
723
|
console.log(chalk.white(" • Run"), chalk.cyan("jive sync"), chalk.white("to sync resources"));
|
|
897
724
|
console.log(chalk.white(" • Run"), chalk.cyan("jive status"), chalk.white("to check sync status"));
|
|
898
|
-
if (pluginStatus.installed) {
|
|
899
|
-
console.log(chalk.bold("\n🔍 Subagent Visibility:\n"));
|
|
900
|
-
console.log(chalk.white(" • The telemetry plugin will track subagent executions"));
|
|
901
|
-
console.log(chalk.white(" • View execution traces in the web UI (View Executions button)"));
|
|
902
|
-
console.log(chalk.white(" • Generate an API key in the web UI for full telemetry support"));
|
|
903
|
-
console.log(chalk.gray(" (Settings > API Keys > Create New Key)\n"));
|
|
904
|
-
} else {
|
|
905
|
-
console.log(chalk.yellow("\n⚠️ Telemetry plugin installation failed"));
|
|
906
|
-
console.log(chalk.white(" • Subagent execution tracking will not work"));
|
|
907
|
-
console.log(chalk.white(" • See SUBAGENT_VISIBILITY.md for manual installation\n"));
|
|
908
|
-
}
|
|
909
725
|
} catch (error) {
|
|
910
726
|
spinner.fail(chalk.red("Initialization failed"));
|
|
911
727
|
console.error(chalk.red(`\n❌ ${error.message || "An error occurred"}`));
|
package/docs/auth.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# Authentication Commands
|
|
2
|
+
|
|
3
|
+
Jive CLI requires authentication to access team resources and collaborate with team members. This guide covers all authentication-related commands.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Authentication in Jive uses token-based authentication. After logging in or signing up, your credentials are securely stored in `~/.jive/credentials.json` with restricted file permissions (mode 0600).
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
### `jive signup`
|
|
12
|
+
|
|
13
|
+
Create a new Jive account.
|
|
14
|
+
|
|
15
|
+
**Usage:**
|
|
16
|
+
```bash
|
|
17
|
+
jive signup
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Interactive Prompts:**
|
|
21
|
+
- **Email:** Your email address for account registration
|
|
22
|
+
- **Password:** Account password (minimum 8 characters)
|
|
23
|
+
- **Confirm Password:** Re-enter password for verification
|
|
24
|
+
|
|
25
|
+
**Process:**
|
|
26
|
+
1. Prompts for email and password
|
|
27
|
+
2. Validates password requirements (minimum 8 characters)
|
|
28
|
+
3. Calls the Jive API signup endpoint (`/api/auth/signup`)
|
|
29
|
+
4. Automatically saves authentication credentials
|
|
30
|
+
5. Displays success message with suggested next steps
|
|
31
|
+
|
|
32
|
+
**Output:**
|
|
33
|
+
```
|
|
34
|
+
Account created successfully!
|
|
35
|
+
Credentials saved to /Users/username/.jive/credentials.json
|
|
36
|
+
|
|
37
|
+
Next steps:
|
|
38
|
+
1. Create a team: jive team create
|
|
39
|
+
2. Initialize Jive in your project: jive init
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Credentials Stored:**
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"token": "auth-token-here",
|
|
46
|
+
"userId": "user-123",
|
|
47
|
+
"email": "user@example.com"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Error Conditions:**
|
|
52
|
+
- Email already registered
|
|
53
|
+
- Password too short (< 8 characters)
|
|
54
|
+
- Passwords don't match
|
|
55
|
+
- Network connectivity issues
|
|
56
|
+
|
|
57
|
+
**Implementation:** `src/commands/auth.ts` (signupCommand)
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### `jive login`
|
|
62
|
+
|
|
63
|
+
Authenticate with an existing Jive account.
|
|
64
|
+
|
|
65
|
+
**Usage:**
|
|
66
|
+
```bash
|
|
67
|
+
jive login
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Interactive Prompts:**
|
|
71
|
+
- **Email:** Your registered email address
|
|
72
|
+
- **Password:** Your account password
|
|
73
|
+
|
|
74
|
+
**Process:**
|
|
75
|
+
1. Prompts for email and password
|
|
76
|
+
2. Calls the Jive API login endpoint (`/api/auth/login`)
|
|
77
|
+
3. Saves authentication credentials to `~/.jive/credentials.json`
|
|
78
|
+
4. Sets file permissions to 0600 (read/write for owner only)
|
|
79
|
+
|
|
80
|
+
**Output:**
|
|
81
|
+
```
|
|
82
|
+
Login successful!
|
|
83
|
+
Credentials saved to /Users/username/.jive/credentials.json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Credentials Stored:**
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"token": "auth-token-here",
|
|
90
|
+
"userId": "user-123",
|
|
91
|
+
"email": "user@example.com"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Error Conditions:**
|
|
96
|
+
- Invalid email or password
|
|
97
|
+
- Account not found
|
|
98
|
+
- Network connectivity issues
|
|
99
|
+
- API server unavailable
|
|
100
|
+
|
|
101
|
+
**Security Notes:**
|
|
102
|
+
- Credentials file is created with mode 0600 (only readable by owner)
|
|
103
|
+
- Never share your credentials file
|
|
104
|
+
- The authentication token is used for all API requests
|
|
105
|
+
|
|
106
|
+
**Implementation:** `src/commands/auth.ts` (loginCommand)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### `jive logout`
|
|
111
|
+
|
|
112
|
+
Clear authentication credentials and log out.
|
|
113
|
+
|
|
114
|
+
**Usage:**
|
|
115
|
+
```bash
|
|
116
|
+
jive logout
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Interactive Prompts:**
|
|
120
|
+
- **Confirmation:** "Are you sure you want to logout?" (Y/n)
|
|
121
|
+
|
|
122
|
+
**Process:**
|
|
123
|
+
1. Checks if user is currently logged in
|
|
124
|
+
2. Prompts for confirmation
|
|
125
|
+
3. Deletes `~/.jive/credentials.json` file
|
|
126
|
+
4. Displays success message
|
|
127
|
+
|
|
128
|
+
**Output:**
|
|
129
|
+
```
|
|
130
|
+
Logged out successfully
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Notes:**
|
|
134
|
+
- This only removes local credentials; it doesn't invalidate the token on the server
|
|
135
|
+
- You can log back in at any time with `jive login`
|
|
136
|
+
- Active team configurations in projects remain intact
|
|
137
|
+
|
|
138
|
+
**Error Conditions:**
|
|
139
|
+
- Not currently logged in (displays "Not logged in" message)
|
|
140
|
+
- User cancels confirmation
|
|
141
|
+
|
|
142
|
+
**Implementation:** `src/commands/auth.ts` (logoutCommand)
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Authentication Flow
|
|
147
|
+
|
|
148
|
+
### First-Time Setup
|
|
149
|
+
|
|
150
|
+
1. **Sign up for an account:**
|
|
151
|
+
```bash
|
|
152
|
+
jive signup
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
2. **Create or join a team:**
|
|
156
|
+
```bash
|
|
157
|
+
jive team create
|
|
158
|
+
```
|
|
159
|
+
Or accept an email invitation to join an existing team.
|
|
160
|
+
|
|
161
|
+
3. **Initialize in a project:**
|
|
162
|
+
```bash
|
|
163
|
+
jive init
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Returning User
|
|
167
|
+
|
|
168
|
+
1. **Log in:**
|
|
169
|
+
```bash
|
|
170
|
+
jive login
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
2. **Start using Jive commands:**
|
|
174
|
+
```bash
|
|
175
|
+
jive team list
|
|
176
|
+
jive subagents pull
|
|
177
|
+
jive mcp pull
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Credentials Storage
|
|
181
|
+
|
|
182
|
+
### Location
|
|
183
|
+
|
|
184
|
+
Credentials are stored in:
|
|
185
|
+
```
|
|
186
|
+
~/.jive/credentials.json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Absolute paths:**
|
|
190
|
+
- macOS/Linux: `/Users/username/.jive/credentials.json`
|
|
191
|
+
- Windows: `C:\Users\username\.jive\credentials.json`
|
|
192
|
+
|
|
193
|
+
### Format
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
198
|
+
"userId": "user-abc123",
|
|
199
|
+
"email": "user@example.com"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Security
|
|
204
|
+
|
|
205
|
+
- File permissions set to 0600 (owner read/write only)
|
|
206
|
+
- Token is used in `X-API-Key` header for all API requests
|
|
207
|
+
- Automatically injected by the API client
|
|
208
|
+
|
|
209
|
+
**Never commit credentials to version control!**
|
|
210
|
+
|
|
211
|
+
## API Integration
|
|
212
|
+
|
|
213
|
+
### Authentication Header
|
|
214
|
+
|
|
215
|
+
All authenticated requests include:
|
|
216
|
+
```http
|
|
217
|
+
X-API-Key: <token-from-credentials>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Token Handling
|
|
221
|
+
|
|
222
|
+
The API client (`src/lib/api-client.ts`) automatically:
|
|
223
|
+
- Reads credentials from `~/.jive/credentials.json`
|
|
224
|
+
- Injects the `X-API-Key` header
|
|
225
|
+
- Handles 401 Unauthorized responses by prompting re-authentication
|
|
226
|
+
|
|
227
|
+
### Session Management
|
|
228
|
+
|
|
229
|
+
- Tokens do not expire (currently)
|
|
230
|
+
- If you receive authentication errors, try logging out and back in:
|
|
231
|
+
```bash
|
|
232
|
+
jive logout
|
|
233
|
+
jive login
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Troubleshooting
|
|
237
|
+
|
|
238
|
+
### "Not authenticated" Error
|
|
239
|
+
|
|
240
|
+
**Symptom:** Commands fail with "Please login first: jive login"
|
|
241
|
+
|
|
242
|
+
**Solutions:**
|
|
243
|
+
1. Check if credentials file exists:
|
|
244
|
+
```bash
|
|
245
|
+
ls ~/.jive/credentials.json
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
2. Log in again:
|
|
249
|
+
```bash
|
|
250
|
+
jive login
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
3. If signup is needed:
|
|
254
|
+
```bash
|
|
255
|
+
jive signup
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### "Invalid credentials" Error
|
|
259
|
+
|
|
260
|
+
**Symptom:** Login fails with incorrect email/password
|
|
261
|
+
|
|
262
|
+
**Solutions:**
|
|
263
|
+
1. Verify email address is correct
|
|
264
|
+
2. Reset password (if password reset is implemented)
|
|
265
|
+
3. Sign up for a new account if needed:
|
|
266
|
+
```bash
|
|
267
|
+
jive signup
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Credentials File Missing
|
|
271
|
+
|
|
272
|
+
**Symptom:** `~/.jive/credentials.json` doesn't exist
|
|
273
|
+
|
|
274
|
+
**Solutions:**
|
|
275
|
+
1. Log in to recreate the file:
|
|
276
|
+
```bash
|
|
277
|
+
jive login
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
2. Check file permissions:
|
|
281
|
+
```bash
|
|
282
|
+
ls -la ~/.jive/credentials.json
|
|
283
|
+
```
|
|
284
|
+
Should show `-rw-------` (0600 permissions)
|
|
285
|
+
|
|
286
|
+
### 401 Unauthorized Errors
|
|
287
|
+
|
|
288
|
+
**Symptom:** API requests fail with 401 status
|
|
289
|
+
|
|
290
|
+
**Solutions:**
|
|
291
|
+
1. Log out and log back in:
|
|
292
|
+
```bash
|
|
293
|
+
jive logout
|
|
294
|
+
jive login
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
2. Verify credentials are valid:
|
|
298
|
+
```bash
|
|
299
|
+
cat ~/.jive/credentials.json
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
3. Check API connectivity:
|
|
303
|
+
```bash
|
|
304
|
+
jive doctor
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Environment Variables
|
|
308
|
+
|
|
309
|
+
### `JIVE_API_URL`
|
|
310
|
+
|
|
311
|
+
Override the default API URL:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
export JIVE_API_URL=https://your-custom-api.com
|
|
315
|
+
jive login
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Default:** `https://next.getjive.app`
|
|
319
|
+
|
|
320
|
+
This affects all API endpoints:
|
|
321
|
+
- `/api/auth/login`
|
|
322
|
+
- `/api/auth/signup`
|
|
323
|
+
- `/api/teams/*`
|
|
324
|
+
- `/api/subagents/*`
|
|
325
|
+
- `/api/mcp-servers/*`
|
|
326
|
+
|
|
327
|
+
## Security Best Practices
|
|
328
|
+
|
|
329
|
+
1. **Never share your credentials file**
|
|
330
|
+
- Don't commit to version control
|
|
331
|
+
- Don't share via chat or email
|
|
332
|
+
- Don't copy to shared locations
|
|
333
|
+
|
|
334
|
+
2. **Use strong passwords**
|
|
335
|
+
- Minimum 8 characters (enforced)
|
|
336
|
+
- Mix of letters, numbers, and symbols (recommended)
|
|
337
|
+
- Unique password for Jive (recommended)
|
|
338
|
+
|
|
339
|
+
3. **Protect your credentials file**
|
|
340
|
+
- File permissions are automatically set to 0600
|
|
341
|
+
- Don't modify permissions to make it world-readable
|
|
342
|
+
- Keep backups secure
|
|
343
|
+
|
|
344
|
+
4. **Log out when done**
|
|
345
|
+
- On shared machines, always log out:
|
|
346
|
+
```bash
|
|
347
|
+
jive logout
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
5. **Monitor access**
|
|
351
|
+
- Review team member list regularly
|
|
352
|
+
- Remove users who no longer need access
|
|
353
|
+
|
|
354
|
+
## Related Commands
|
|
355
|
+
|
|
356
|
+
- [`jive team create`](./team.md#jive-team-create) - Create a team after signup
|
|
357
|
+
- [`jive team list`](./team.md#jive-team-list) - List teams (requires authentication)
|
|
358
|
+
- [`jive init`](./init.md#jive-init) - Initialize project (requires authentication)
|
|
359
|
+
- [`jive doctor`](./init.md#jive-doctor) - Verify authentication and setup
|
|
360
|
+
|
|
361
|
+
## Implementation Details
|
|
362
|
+
|
|
363
|
+
**Source Files:**
|
|
364
|
+
- Main implementation: `src/commands/auth.ts`
|
|
365
|
+
- Configuration helpers: `src/lib/config.ts`
|
|
366
|
+
- API client: `src/lib/api-client.ts`
|
|
367
|
+
|
|
368
|
+
**Key Functions:**
|
|
369
|
+
- `signupCommand()` - Handles user registration
|
|
370
|
+
- `loginCommand()` - Handles user authentication
|
|
371
|
+
- `logoutCommand()` - Clears credentials
|
|
372
|
+
- `loadCredentials()` - Reads credentials file
|
|
373
|
+
- `saveCredentials()` - Writes credentials file securely
|
|
374
|
+
- `requireAuth()` - Ensures user is authenticated (exits if not)
|
|
375
|
+
|
|
376
|
+
**API Endpoints:**
|
|
377
|
+
- `POST /api/auth/signup` - Create new account
|
|
378
|
+
- `POST /api/auth/login` - Authenticate existing account
|