spaceshipai 0.1.0
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/bin/init.js +99 -0
- package/package.json +26 -0
- package/src/detect.js +23 -0
- package/src/install.js +58 -0
- package/src/prompt.js +12 -0
package/bin/init.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { detect } from '../src/detect.js'
|
|
4
|
+
import {
|
|
5
|
+
installClaudeCode,
|
|
6
|
+
installCursor,
|
|
7
|
+
installVscode,
|
|
8
|
+
installWindsurf,
|
|
9
|
+
} from '../src/install.js'
|
|
10
|
+
import { promptApiKey } from '../src/prompt.js'
|
|
11
|
+
|
|
12
|
+
const TEAL = '\x1b[36m'
|
|
13
|
+
const GREEN = '\x1b[32m'
|
|
14
|
+
const DIM = '\x1b[2m'
|
|
15
|
+
const BOLD = '\x1b[1m'
|
|
16
|
+
const RESET = '\x1b[0m'
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
console.log(`\n${BOLD}${TEAL}Spaceship AI${RESET} — MCP installer\n`)
|
|
20
|
+
|
|
21
|
+
// Resolve API key: env var → prompt
|
|
22
|
+
let apiKey = process.env.SPACESHIP_API_KEY
|
|
23
|
+
if (!apiKey) {
|
|
24
|
+
apiKey = await promptApiKey()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!apiKey || !apiKey.startsWith('sk_')) {
|
|
28
|
+
console.error('\nInvalid API key. Keys must start with sk_live_ or sk_test_.')
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Detect installed tools
|
|
33
|
+
const tools = detect()
|
|
34
|
+
const anyDetected = Object.values(tools).some(Boolean)
|
|
35
|
+
|
|
36
|
+
if (!anyDetected) {
|
|
37
|
+
console.log(
|
|
38
|
+
`${DIM}No supported AI coding tools detected.${RESET}\n` +
|
|
39
|
+
`Supported tools: Claude Code, Cursor, VS Code, Windsurf\n`
|
|
40
|
+
)
|
|
41
|
+
process.exit(0)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('Installing Spaceship MCP server...\n')
|
|
45
|
+
|
|
46
|
+
const results = []
|
|
47
|
+
|
|
48
|
+
if (tools.claudeCode) {
|
|
49
|
+
try {
|
|
50
|
+
installClaudeCode(apiKey)
|
|
51
|
+
results.push(`${GREEN}✓${RESET} Claude Code`)
|
|
52
|
+
} catch {
|
|
53
|
+
results.push(`✗ Claude Code ${DIM}(install failed — run: claude mcp add --transport stdio spaceship --env SPACESHIP_API_KEY=${apiKey} -- uvx spaceship-mcp)${RESET}`)
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
results.push(`${DIM}— Claude Code not detected (skipped)${RESET}`)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (tools.cursor) {
|
|
60
|
+
try {
|
|
61
|
+
installCursor(apiKey)
|
|
62
|
+
results.push(`${GREEN}✓${RESET} Cursor`)
|
|
63
|
+
} catch (e) {
|
|
64
|
+
results.push(`✗ Cursor ${DIM}(${e.message})${RESET}`)
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
results.push(`${DIM}— Cursor not detected (skipped)${RESET}`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (tools.vscode) {
|
|
71
|
+
try {
|
|
72
|
+
installVscode(apiKey)
|
|
73
|
+
results.push(`${GREEN}✓${RESET} VS Code`)
|
|
74
|
+
} catch (e) {
|
|
75
|
+
results.push(`✗ VS Code ${DIM}(${e.message})${RESET}`)
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
results.push(`${DIM}— VS Code not detected (skipped)${RESET}`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (tools.windsurf) {
|
|
82
|
+
try {
|
|
83
|
+
installWindsurf(apiKey)
|
|
84
|
+
results.push(`${GREEN}✓${RESET} Windsurf`)
|
|
85
|
+
} catch (e) {
|
|
86
|
+
results.push(`✗ Windsurf ${DIM}(${e.message})${RESET}`)
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
results.push(`${DIM}— Windsurf not detected (skipped)${RESET}`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(results.join('\n'))
|
|
93
|
+
console.log(`\n${TEAL}Spaceship MCP is ready.${RESET} Restart your IDE if it was open.\n`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
main().catch((err) => {
|
|
97
|
+
console.error('Error:', err.message)
|
|
98
|
+
process.exit(1)
|
|
99
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "spaceshipai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Spaceship AI CLI — connect your AI coding tools, manage agents, and more",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"spaceshipai": "bin/init.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"spaceship",
|
|
14
|
+
"ai",
|
|
15
|
+
"mcp",
|
|
16
|
+
"agents",
|
|
17
|
+
"claude",
|
|
18
|
+
"cursor"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"homepage": "https://spaceshipai.io",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/Spaceship-AI/spaceshipai-cli.git"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/detect.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { existsSync } from 'fs'
|
|
2
|
+
import { homedir } from 'os'
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
|
|
5
|
+
const home = homedir()
|
|
6
|
+
|
|
7
|
+
function cliExists(cmd) {
|
|
8
|
+
try {
|
|
9
|
+
execSync(`which ${cmd}`, { stdio: 'ignore' })
|
|
10
|
+
return true
|
|
11
|
+
} catch {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function detect() {
|
|
17
|
+
return {
|
|
18
|
+
claudeCode: cliExists('claude') || existsSync(`${home}/.claude`),
|
|
19
|
+
cursor: existsSync(`${home}/.cursor`),
|
|
20
|
+
vscode: cliExists('code') || existsSync(`${home}/.vscode`),
|
|
21
|
+
windsurf: existsSync(`${home}/.codeium/windsurf`),
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/install.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'
|
|
2
|
+
import { homedir } from 'os'
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
import { dirname } from 'path'
|
|
5
|
+
|
|
6
|
+
const home = homedir()
|
|
7
|
+
|
|
8
|
+
const MCP_ENTRY = (key) => ({
|
|
9
|
+
command: 'uvx',
|
|
10
|
+
args: ['spaceship-mcp'],
|
|
11
|
+
env: { SPACESHIP_API_KEY: key },
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
function readJsonFile(path, fallback = {}) {
|
|
15
|
+
if (!existsSync(path)) return fallback
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(path, 'utf8'))
|
|
18
|
+
} catch {
|
|
19
|
+
return fallback
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function writeJsonFile(path, data) {
|
|
24
|
+
mkdirSync(dirname(path), { recursive: true })
|
|
25
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf8')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function installClaudeCode(key) {
|
|
29
|
+
execSync(
|
|
30
|
+
`claude mcp add --transport stdio spaceship --env SPACESHIP_API_KEY=${key} -- uvx spaceship-mcp`,
|
|
31
|
+
{ stdio: 'ignore' }
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function installCursor(key) {
|
|
36
|
+
const configPath = `${home}/.cursor/mcp.json`
|
|
37
|
+
const config = readJsonFile(configPath, { mcpServers: {} })
|
|
38
|
+
config.mcpServers = config.mcpServers || {}
|
|
39
|
+
config.mcpServers.spaceship = MCP_ENTRY(key)
|
|
40
|
+
writeJsonFile(configPath, config)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function installVscode(key) {
|
|
44
|
+
// VS Code user-level MCP config (cross-platform compatible path)
|
|
45
|
+
const configPath = `${home}/.vscode/mcp.json`
|
|
46
|
+
const config = readJsonFile(configPath, { servers: {} })
|
|
47
|
+
config.servers = config.servers || {}
|
|
48
|
+
config.servers.spaceship = MCP_ENTRY(key)
|
|
49
|
+
writeJsonFile(configPath, config)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function installWindsurf(key) {
|
|
53
|
+
const configPath = `${home}/.codeium/windsurf/mcp_config.json`
|
|
54
|
+
const config = readJsonFile(configPath, { mcpServers: {} })
|
|
55
|
+
config.mcpServers = config.mcpServers || {}
|
|
56
|
+
config.mcpServers.spaceship = MCP_ENTRY(key)
|
|
57
|
+
writeJsonFile(configPath, config)
|
|
58
|
+
}
|
package/src/prompt.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createInterface } from 'readline'
|
|
2
|
+
|
|
3
|
+
export async function promptApiKey() {
|
|
4
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr })
|
|
5
|
+
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
rl.question('Enter your Spaceship API key (sk_live_...): ', (answer) => {
|
|
8
|
+
rl.close()
|
|
9
|
+
resolve(answer.trim())
|
|
10
|
+
})
|
|
11
|
+
})
|
|
12
|
+
}
|