@jive-ai/cli 0.0.3 → 0.0.5
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 +45 -208
- 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 +3 -2
package/dist/index.mjs
CHANGED
|
@@ -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;
|
|
@@ -811,7 +645,9 @@ async function initCommand() {
|
|
|
811
645
|
try {
|
|
812
646
|
spinner.text = "Scanning for existing MCP servers...";
|
|
813
647
|
const mcpConfig = await getMcpConfig();
|
|
814
|
-
|
|
648
|
+
const currentMcpServers = await apiClient$1.getMcpServers(teamId);
|
|
649
|
+
let uploadedServers = [];
|
|
650
|
+
let skippedServers = [];
|
|
815
651
|
if (mcpConfig?.mcpServers) {
|
|
816
652
|
const serverNames = Object.keys(mcpConfig.mcpServers).filter((name) => name !== "jive-mcp");
|
|
817
653
|
if (serverNames.length > 0) {
|
|
@@ -819,12 +655,21 @@ async function initCommand() {
|
|
|
819
655
|
for (const name of serverNames) {
|
|
820
656
|
const serverConfig = mcpConfig.mcpServers[name];
|
|
821
657
|
try {
|
|
822
|
-
|
|
658
|
+
if (currentMcpServers.some((s) => s.name === name)) skippedServers.push({
|
|
823
659
|
name,
|
|
824
|
-
|
|
825
|
-
config: serverConfig
|
|
660
|
+
id: currentMcpServers.find((s) => s.name === name)?.id || ""
|
|
826
661
|
});
|
|
827
|
-
|
|
662
|
+
else {
|
|
663
|
+
const created = await apiClient$1.createMcpServer(teamId, {
|
|
664
|
+
name,
|
|
665
|
+
description: `Imported from local .mcp.json`,
|
|
666
|
+
config: serverConfig
|
|
667
|
+
});
|
|
668
|
+
uploadedServers.push({
|
|
669
|
+
name,
|
|
670
|
+
id: created.id
|
|
671
|
+
});
|
|
672
|
+
}
|
|
828
673
|
} catch (error) {
|
|
829
674
|
console.warn(chalk.gray(`\nWarning: Could not upload MCP server "${name}": ${error.message}`));
|
|
830
675
|
}
|
|
@@ -833,19 +678,30 @@ async function initCommand() {
|
|
|
833
678
|
}
|
|
834
679
|
spinner.text = "Scanning for existing subagents...";
|
|
835
680
|
const subagentFiles = await getSubagentFiles();
|
|
836
|
-
|
|
681
|
+
const currentSubagents = await apiClient$1.getSubagents(teamId);
|
|
682
|
+
let uploadedSubagents = [];
|
|
683
|
+
let skippedSubagents = [];
|
|
837
684
|
if (subagentFiles.length > 0) {
|
|
838
685
|
spinner.text = `Found ${subagentFiles.length} subagent(s), uploading...`;
|
|
839
686
|
for (const subagent of subagentFiles) {
|
|
840
687
|
if (subagent.name === "subagent-runner") continue;
|
|
841
688
|
try {
|
|
842
|
-
|
|
689
|
+
if (currentSubagents.some((s) => s.name === subagent.name)) skippedSubagents.push({
|
|
843
690
|
name: subagent.name,
|
|
844
|
-
|
|
845
|
-
prompt: subagent.prompt
|
|
691
|
+
id: currentSubagents.find((s) => s.name === subagent.name)?.id || ""
|
|
846
692
|
});
|
|
847
|
-
|
|
848
|
-
|
|
693
|
+
else {
|
|
694
|
+
const created = await apiClient$1.createSubagent(teamId, {
|
|
695
|
+
name: subagent.name,
|
|
696
|
+
description: subagent.description,
|
|
697
|
+
prompt: subagent.prompt
|
|
698
|
+
});
|
|
699
|
+
await updateSubagentJiveId(subagent.name, created.id);
|
|
700
|
+
uploadedSubagents.push({
|
|
701
|
+
name: subagent.name,
|
|
702
|
+
id: created.id
|
|
703
|
+
});
|
|
704
|
+
}
|
|
849
705
|
} catch (error) {
|
|
850
706
|
console.warn(chalk.gray(`\nWarning: Could not upload subagent "${subagent.name}": ${error.message}`));
|
|
851
707
|
}
|
|
@@ -853,59 +709,40 @@ async function initCommand() {
|
|
|
853
709
|
}
|
|
854
710
|
spinner.text = "Adding Jive server to .mcp.json...";
|
|
855
711
|
await addMcpServer("jive-mcp", {
|
|
856
|
-
command: "
|
|
857
|
-
args: ["-y", "@jive/mcp-server"],
|
|
712
|
+
command: "jive mcp start",
|
|
858
713
|
env: {
|
|
859
|
-
JIVE_API_URL: process.env.JIVE_API_URL || "http://localhost:5173",
|
|
860
714
|
JIVE_TEAM_ID: teamId,
|
|
861
715
|
JIVE_AUTH_TOKEN: credentials.token
|
|
862
716
|
}
|
|
863
717
|
});
|
|
718
|
+
spinner.text = "Removing uploaded MCP servers from local .mcp.json...";
|
|
719
|
+
if (mcpConfig?.mcpServers) {
|
|
720
|
+
for (const name of Object.keys(mcpConfig.mcpServers)) if (uploadedSubagents.some((subagent) => subagent.name === name)) await removeMcpServer(name);
|
|
721
|
+
}
|
|
864
722
|
spinner.text = "Creating subagent-runner...";
|
|
865
723
|
await createSubagentRunner();
|
|
866
724
|
spinner.text = "Updating .gitignore...";
|
|
867
|
-
await addToGitignore(".jive
|
|
868
|
-
await addToGitignore(".jive/sync.json");
|
|
869
|
-
await addToGitignore(".claude/plugins/");
|
|
725
|
+
await addToGitignore(".jive");
|
|
870
726
|
spinner.text = "Creating project configuration...";
|
|
871
727
|
await saveProjectConfig({
|
|
872
728
|
teamId,
|
|
873
729
|
activeTeamId: teamId,
|
|
874
730
|
lastSync: (/* @__PURE__ */ new Date()).toISOString()
|
|
875
731
|
});
|
|
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
732
|
spinner.succeed(chalk.green("Project initialized successfully!"));
|
|
885
733
|
console.log(chalk.bold("\n📦 Summary:\n"));
|
|
886
|
-
if (uploadedServers > 0) console.log(chalk.green(` ✓ Uploaded ${uploadedServers} MCP server(s) to team`));
|
|
887
|
-
if (
|
|
888
|
-
console.log(chalk.green(
|
|
734
|
+
if (uploadedServers.length > 0) console.log(chalk.green(` ✓ Uploaded ${uploadedServers.length} MCP server(s) to team`));
|
|
735
|
+
if (skippedServers.length > 0) console.log(chalk.yellow(` ⚠ Skipped ${skippedServers.length} MCP server(s) (already exist)`));
|
|
736
|
+
if (uploadedSubagents.length > 0) console.log(chalk.green(` ✓ Uploaded ${uploadedSubagents.length} subagent(s) to team`));
|
|
737
|
+
if (skippedSubagents.length > 0) console.log(chalk.yellow(` ⚠ Skipped ${skippedSubagents.length} subagent(s) (already exist)`));
|
|
738
|
+
console.log(chalk.green(" ✓ Added Jive MCP server to .mcp.json"));
|
|
889
739
|
console.log(chalk.green(" ✓ Created .claude/agents/subagent-runner.md"));
|
|
890
740
|
console.log(chalk.green(" ✓ Created .jive/config.json"));
|
|
891
741
|
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
742
|
console.log(chalk.bold("\n✨ Next Steps:\n"));
|
|
895
|
-
console.log(chalk.white(" • Restart Claude Code to load the Jive server
|
|
743
|
+
console.log(chalk.white(" • Restart Claude Code to load the Jive MCP server"));
|
|
896
744
|
console.log(chalk.white(" • Run"), chalk.cyan("jive sync"), chalk.white("to sync resources"));
|
|
897
745
|
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
746
|
} catch (error) {
|
|
910
747
|
spinner.fail(chalk.red("Initialization failed"));
|
|
911
748
|
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
|