spritecook-mcp 0.2.11 → 0.2.13
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/package.json +3 -3
- package/src/editors.mjs +531 -531
- package/src/setup.mjs +160 -160
- package/src/skill.mjs +124 -102
package/src/setup.mjs
CHANGED
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import prompts from 'prompts';
|
|
3
|
-
import { printBanner, step, success, info, warn, error } from './ui.mjs';
|
|
4
|
-
import { authenticate } from './auth.mjs';
|
|
5
|
-
import { verifyApiKey } from './verify.mjs';
|
|
6
|
-
import { detectEditors, writeConfigs, readExistingKey } from './editors.mjs';
|
|
7
|
-
import { maybeInstallSkill } from './skill.mjs';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Main setup flow orchestrator.
|
|
11
|
-
* Called by bin/cli.mjs as the entry point.
|
|
12
|
-
*/
|
|
13
|
-
export async function run() {
|
|
14
|
-
printBanner();
|
|
15
|
-
|
|
16
|
-
// ── Step 1: Authenticate ──────────────────────────────────────────
|
|
17
|
-
step(1, 'Authentication');
|
|
18
|
-
|
|
19
|
-
let apiKey = null;
|
|
20
|
-
|
|
21
|
-
// Check if there's already a working SpriteCook key in an editor config
|
|
22
|
-
const existingKey = readExistingKey();
|
|
23
|
-
if (existingKey) {
|
|
24
|
-
info('Found existing SpriteCook API key in your editor config.');
|
|
25
|
-
const verification = await verifyApiKey(existingKey);
|
|
26
|
-
if (verification.ok) {
|
|
27
|
-
const response = await prompts({
|
|
28
|
-
type: 'select',
|
|
29
|
-
name: 'action',
|
|
30
|
-
message: 'Existing key is valid. What would you like to do?',
|
|
31
|
-
choices: [
|
|
32
|
-
{ title: 'Keep existing key and update configs', value: 'keep' },
|
|
33
|
-
{ title: 'Create a new key (replaces current)', value: 'new' },
|
|
34
|
-
],
|
|
35
|
-
initial: 0,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
if (response.action === 'keep') {
|
|
39
|
-
apiKey = existingKey;
|
|
40
|
-
success('Using existing API key.');
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
warn('Existing key is no longer valid. Please authenticate again.');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!apiKey) {
|
|
48
|
-
apiKey = await authenticate();
|
|
49
|
-
if (!apiKey) {
|
|
50
|
-
console.log();
|
|
51
|
-
error('Setup cancelled. No API key obtained.');
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ── Step 2: Verify the key works ──────────────────────────────────
|
|
56
|
-
step(2, 'Verification');
|
|
57
|
-
|
|
58
|
-
const verification = await verifyApiKey(apiKey);
|
|
59
|
-
if (!verification.ok) {
|
|
60
|
-
error('API key verification failed. Please check your key and try again.');
|
|
61
|
-
process.exit(1);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ── Step 3: Detect editors and write configs ──────────────────────
|
|
66
|
-
step(3, 'Editor Configuration');
|
|
67
|
-
|
|
68
|
-
const editors = detectEditors();
|
|
69
|
-
|
|
70
|
-
// Let user select which editors to configure
|
|
71
|
-
const editorResponse = await prompts({
|
|
72
|
-
type: 'multiselect',
|
|
73
|
-
name: 'editors',
|
|
74
|
-
message: 'Configure MCP for:',
|
|
75
|
-
choices: editors.map((e) => ({
|
|
76
|
-
title: e.detected ? chalk.white(e.name) : chalk.dim(e.name),
|
|
77
|
-
value: e.name,
|
|
78
|
-
selected: e.detected,
|
|
79
|
-
})),
|
|
80
|
-
hint: 'Space to toggle, Enter to confirm',
|
|
81
|
-
instructions: false,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const selected = editors.filter((e) => editorResponse.editors?.includes(e.name));
|
|
85
|
-
|
|
86
|
-
if (selected.length === 0) {
|
|
87
|
-
warn('No editors selected. Skipping config.');
|
|
88
|
-
console.log();
|
|
89
|
-
info('You can configure manually later. Your API key:');
|
|
90
|
-
console.log(` ${chalk.dim(apiKey.slice(0, 16) + '...')}`);
|
|
91
|
-
} else {
|
|
92
|
-
// Ask about scope for editors that support both project and global
|
|
93
|
-
const multiScopeEditors = selected.filter((e) => e.scopes.length > 1);
|
|
94
|
-
if (multiScopeEditors.length > 0) {
|
|
95
|
-
console.log();
|
|
96
|
-
const scopeResponse = await prompts({
|
|
97
|
-
type: 'select',
|
|
98
|
-
name: 'scope',
|
|
99
|
-
message: 'Install MCP config:',
|
|
100
|
-
choices: [
|
|
101
|
-
{ title: 'This project only (recommended)', value: 'project', description: 'Writes to project config files' },
|
|
102
|
-
{ title: 'Global (all projects)', value: 'global', description: 'Writes to user-level config' },
|
|
103
|
-
],
|
|
104
|
-
initial: 0,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const chosenScope = scopeResponse.scope || 'project';
|
|
108
|
-
for (const e of selected) {
|
|
109
|
-
if (e.scopes.includes(chosenScope)) {
|
|
110
|
-
e._chosenScope = chosenScope;
|
|
111
|
-
}
|
|
112
|
-
// Editors with only 1 scope keep their default
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
console.log();
|
|
117
|
-
info('Writing MCP config...');
|
|
118
|
-
const written = writeConfigs(selected, apiKey);
|
|
119
|
-
|
|
120
|
-
if (written === 0) {
|
|
121
|
-
warn('No configs were written. You may need to configure manually.');
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// ── Step 4: Optional agent skill ─────────────────────────────────
|
|
126
|
-
step(4, 'Agent Skill (optional)');
|
|
127
|
-
await maybeInstallSkill(selected);
|
|
128
|
-
|
|
129
|
-
// ── Done ──────────────────────────────────────────────────────────
|
|
130
|
-
const configuredNames = selected.map((e) => e.name);
|
|
131
|
-
const hasCursor = configuredNames.includes('Cursor');
|
|
132
|
-
const hasVSCode = configuredNames.includes('VS Code');
|
|
133
|
-
|
|
134
|
-
console.log();
|
|
135
|
-
console.log(chalk.bold.green(' Setup complete!'));
|
|
136
|
-
console.log();
|
|
137
|
-
|
|
138
|
-
if (hasCursor || hasVSCode) {
|
|
139
|
-
console.log(chalk.bgYellow.black.bold(' ACTION REQUIRED '));
|
|
140
|
-
console.log();
|
|
141
|
-
if (hasCursor) {
|
|
142
|
-
console.log(chalk.white.bold(' Cursor:'));
|
|
143
|
-
console.log(chalk.white(' 1. Open Settings (Ctrl+Shift+J / Cmd+Shift+J)'));
|
|
144
|
-
console.log(chalk.white(' 2. Go to Tools & MCP'));
|
|
145
|
-
console.log(chalk.white(' 3. Enable "spritecook"'));
|
|
146
|
-
}
|
|
147
|
-
if (hasVSCode) {
|
|
148
|
-
if (hasCursor) console.log();
|
|
149
|
-
console.log(chalk.white.bold(' VS Code:'));
|
|
150
|
-
console.log(chalk.white(' Reload the window (Ctrl+Shift+P > "Reload Window")'));
|
|
151
|
-
}
|
|
152
|
-
console.log();
|
|
153
|
-
console.log(chalk.dim(' ─────────────────────────────────────────'));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
console.log();
|
|
157
|
-
console.log(chalk.dim(' Then try asking your AI agent:'));
|
|
158
|
-
console.log(chalk.white(' "Generate a chicken sprite"'));
|
|
159
|
-
console.log();
|
|
160
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import prompts from 'prompts';
|
|
3
|
+
import { printBanner, step, success, info, warn, error } from './ui.mjs';
|
|
4
|
+
import { authenticate } from './auth.mjs';
|
|
5
|
+
import { verifyApiKey } from './verify.mjs';
|
|
6
|
+
import { detectEditors, writeConfigs, readExistingKey } from './editors.mjs';
|
|
7
|
+
import { maybeInstallSkill } from './skill.mjs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Main setup flow orchestrator.
|
|
11
|
+
* Called by bin/cli.mjs as the entry point.
|
|
12
|
+
*/
|
|
13
|
+
export async function run() {
|
|
14
|
+
printBanner();
|
|
15
|
+
|
|
16
|
+
// ── Step 1: Authenticate ──────────────────────────────────────────
|
|
17
|
+
step(1, 'Authentication');
|
|
18
|
+
|
|
19
|
+
let apiKey = null;
|
|
20
|
+
|
|
21
|
+
// Check if there's already a working SpriteCook key in an editor config
|
|
22
|
+
const existingKey = readExistingKey();
|
|
23
|
+
if (existingKey) {
|
|
24
|
+
info('Found existing SpriteCook API key in your editor config.');
|
|
25
|
+
const verification = await verifyApiKey(existingKey);
|
|
26
|
+
if (verification.ok) {
|
|
27
|
+
const response = await prompts({
|
|
28
|
+
type: 'select',
|
|
29
|
+
name: 'action',
|
|
30
|
+
message: 'Existing key is valid. What would you like to do?',
|
|
31
|
+
choices: [
|
|
32
|
+
{ title: 'Keep existing key and update configs', value: 'keep' },
|
|
33
|
+
{ title: 'Create a new key (replaces current)', value: 'new' },
|
|
34
|
+
],
|
|
35
|
+
initial: 0,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (response.action === 'keep') {
|
|
39
|
+
apiKey = existingKey;
|
|
40
|
+
success('Using existing API key.');
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
warn('Existing key is no longer valid. Please authenticate again.');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
apiKey = await authenticate();
|
|
49
|
+
if (!apiKey) {
|
|
50
|
+
console.log();
|
|
51
|
+
error('Setup cancelled. No API key obtained.');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Step 2: Verify the key works ──────────────────────────────────
|
|
56
|
+
step(2, 'Verification');
|
|
57
|
+
|
|
58
|
+
const verification = await verifyApiKey(apiKey);
|
|
59
|
+
if (!verification.ok) {
|
|
60
|
+
error('API key verification failed. Please check your key and try again.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Step 3: Detect editors and write configs ──────────────────────
|
|
66
|
+
step(3, 'Editor Configuration');
|
|
67
|
+
|
|
68
|
+
const editors = detectEditors();
|
|
69
|
+
|
|
70
|
+
// Let user select which editors to configure
|
|
71
|
+
const editorResponse = await prompts({
|
|
72
|
+
type: 'multiselect',
|
|
73
|
+
name: 'editors',
|
|
74
|
+
message: 'Configure MCP for:',
|
|
75
|
+
choices: editors.map((e) => ({
|
|
76
|
+
title: e.detected ? chalk.white(e.name) : chalk.dim(e.name),
|
|
77
|
+
value: e.name,
|
|
78
|
+
selected: e.detected,
|
|
79
|
+
})),
|
|
80
|
+
hint: 'Space to toggle, Enter to confirm',
|
|
81
|
+
instructions: false,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const selected = editors.filter((e) => editorResponse.editors?.includes(e.name));
|
|
85
|
+
|
|
86
|
+
if (selected.length === 0) {
|
|
87
|
+
warn('No editors selected. Skipping config.');
|
|
88
|
+
console.log();
|
|
89
|
+
info('You can configure manually later. Your API key:');
|
|
90
|
+
console.log(` ${chalk.dim(apiKey.slice(0, 16) + '...')}`);
|
|
91
|
+
} else {
|
|
92
|
+
// Ask about scope for editors that support both project and global
|
|
93
|
+
const multiScopeEditors = selected.filter((e) => e.scopes.length > 1);
|
|
94
|
+
if (multiScopeEditors.length > 0) {
|
|
95
|
+
console.log();
|
|
96
|
+
const scopeResponse = await prompts({
|
|
97
|
+
type: 'select',
|
|
98
|
+
name: 'scope',
|
|
99
|
+
message: 'Install MCP config:',
|
|
100
|
+
choices: [
|
|
101
|
+
{ title: 'This project only (recommended)', value: 'project', description: 'Writes to project config files' },
|
|
102
|
+
{ title: 'Global (all projects)', value: 'global', description: 'Writes to user-level config' },
|
|
103
|
+
],
|
|
104
|
+
initial: 0,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const chosenScope = scopeResponse.scope || 'project';
|
|
108
|
+
for (const e of selected) {
|
|
109
|
+
if (e.scopes.includes(chosenScope)) {
|
|
110
|
+
e._chosenScope = chosenScope;
|
|
111
|
+
}
|
|
112
|
+
// Editors with only 1 scope keep their default
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log();
|
|
117
|
+
info('Writing MCP config...');
|
|
118
|
+
const written = writeConfigs(selected, apiKey);
|
|
119
|
+
|
|
120
|
+
if (written === 0) {
|
|
121
|
+
warn('No configs were written. You may need to configure manually.');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Step 4: Optional agent skill ─────────────────────────────────
|
|
126
|
+
step(4, 'Agent Skill (optional)');
|
|
127
|
+
await maybeInstallSkill(selected);
|
|
128
|
+
|
|
129
|
+
// ── Done ──────────────────────────────────────────────────────────
|
|
130
|
+
const configuredNames = selected.map((e) => e.name);
|
|
131
|
+
const hasCursor = configuredNames.includes('Cursor');
|
|
132
|
+
const hasVSCode = configuredNames.includes('VS Code');
|
|
133
|
+
|
|
134
|
+
console.log();
|
|
135
|
+
console.log(chalk.bold.green(' Setup complete!'));
|
|
136
|
+
console.log();
|
|
137
|
+
|
|
138
|
+
if (hasCursor || hasVSCode) {
|
|
139
|
+
console.log(chalk.bgYellow.black.bold(' ACTION REQUIRED '));
|
|
140
|
+
console.log();
|
|
141
|
+
if (hasCursor) {
|
|
142
|
+
console.log(chalk.white.bold(' Cursor:'));
|
|
143
|
+
console.log(chalk.white(' 1. Open Settings (Ctrl+Shift+J / Cmd+Shift+J)'));
|
|
144
|
+
console.log(chalk.white(' 2. Go to Tools & MCP'));
|
|
145
|
+
console.log(chalk.white(' 3. Enable "spritecook"'));
|
|
146
|
+
}
|
|
147
|
+
if (hasVSCode) {
|
|
148
|
+
if (hasCursor) console.log();
|
|
149
|
+
console.log(chalk.white.bold(' VS Code:'));
|
|
150
|
+
console.log(chalk.white(' Reload the window (Ctrl+Shift+P > "Reload Window")'));
|
|
151
|
+
}
|
|
152
|
+
console.log();
|
|
153
|
+
console.log(chalk.dim(' ─────────────────────────────────────────'));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log();
|
|
157
|
+
console.log(chalk.dim(' Then try asking your AI agent:'));
|
|
158
|
+
console.log(chalk.white(' "Generate a chicken sprite"'));
|
|
159
|
+
console.log();
|
|
160
|
+
}
|
package/src/skill.mjs
CHANGED
|
@@ -1,102 +1,124 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process';
|
|
2
|
-
import { platform } from 'node:os';
|
|
3
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import prompts from 'prompts';
|
|
6
|
-
import { success, info, warn } from './ui.mjs';
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { platform } from 'node:os';
|
|
3
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { basename, dirname, join } from 'node:path';
|
|
5
|
+
import prompts from 'prompts';
|
|
6
|
+
import { success, info, warn } from './ui.mjs';
|
|
7
|
+
|
|
8
|
+
const SKILLS = [
|
|
9
|
+
{
|
|
10
|
+
name: 'spritecook-workflow-essentials',
|
|
11
|
+
rawUrl: 'https://raw.githubusercontent.com/SpriteCook/skills/main/skills/spritecook-workflow-essentials/SKILL.md',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'spritecook-generate-sprites',
|
|
15
|
+
rawUrl: 'https://raw.githubusercontent.com/SpriteCook/skills/main/skills/spritecook-generate-sprites/SKILL.md',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'spritecook-animate-assets',
|
|
19
|
+
rawUrl: 'https://raw.githubusercontent.com/SpriteCook/skills/main/skills/spritecook-animate-assets/SKILL.md',
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Download SKILL.md directly from GitHub and write it into each
|
|
25
|
+
* selected editor's skill directory. Used as a fallback when
|
|
26
|
+
* `npx skills add` fails (e.g. git not installed).
|
|
27
|
+
*
|
|
28
|
+
* @param {Array<{skillDirs:{project:string|null,global:string|null}, _chosenScope?:string}>} editors
|
|
29
|
+
*/
|
|
30
|
+
async function fallbackInstall(editors) {
|
|
31
|
+
info('Trying direct download fallback...');
|
|
32
|
+
|
|
33
|
+
const skillBodies = [];
|
|
34
|
+
try {
|
|
35
|
+
for (const skill of SKILLS) {
|
|
36
|
+
const res = await fetch(skill.rawUrl);
|
|
37
|
+
if (!res.ok) throw new Error(`${skill.name}: HTTP ${res.status}`);
|
|
38
|
+
skillBodies.push({
|
|
39
|
+
name: skill.name,
|
|
40
|
+
body: await res.text(),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {
|
|
44
|
+
warn(`Could not download skill files: ${e.message}`);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let wrote = 0;
|
|
49
|
+
for (const editor of editors) {
|
|
50
|
+
const dirs = editor.skillDirs;
|
|
51
|
+
if (!dirs) continue;
|
|
52
|
+
|
|
53
|
+
const scope = editor._chosenScope || 'project';
|
|
54
|
+
const dir = dirs[scope] || dirs.project || dirs.global;
|
|
55
|
+
if (!dir) continue;
|
|
56
|
+
|
|
57
|
+
const skillsRoot = basename(dir) === 'spritecook' ? dirname(dir) : dir;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
for (const skill of skillBodies) {
|
|
61
|
+
const skillDir = join(skillsRoot, skill.name);
|
|
62
|
+
mkdirSync(skillDir, { recursive: true });
|
|
63
|
+
writeFileSync(join(skillDir, 'SKILL.md'), skill.body, 'utf-8');
|
|
64
|
+
wrote++;
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// best-effort, skip on failure
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (wrote > 0) {
|
|
72
|
+
success(`Agent skills installed via direct download (${wrote} files written).`);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Install or update the SpriteCook agent skill.
|
|
80
|
+
*
|
|
81
|
+
* Primary: `npx skills add SpriteCook/skills --skill '*'` (requires git).
|
|
82
|
+
* Fallback: download SKILL.md directly from GitHub and place it
|
|
83
|
+
* in each selected editor's skill directory.
|
|
84
|
+
*
|
|
85
|
+
* @param {Array} [selectedEditors] - editors the user chose in step 3
|
|
86
|
+
*/
|
|
87
|
+
export async function maybeInstallSkill(selectedEditors = []) {
|
|
88
|
+
info('The agent skill teaches your AI how to generate sprites autonomously.');
|
|
89
|
+
const response = await prompts({
|
|
90
|
+
type: 'confirm',
|
|
91
|
+
name: 'install',
|
|
92
|
+
message: 'Install SpriteCook agent skill? (highly recommended)',
|
|
93
|
+
initial: true,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!response.install) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
info('Installing skills via npx skills add SpriteCook/skills --skill "*" ...');
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const npxCmd = platform() === 'win32' ? 'npx.cmd' : 'npx';
|
|
104
|
+
execSync(`${npxCmd} -y skills add SpriteCook/skills --skill "*"`, {
|
|
105
|
+
stdio: 'inherit',
|
|
106
|
+
timeout: 60_000,
|
|
107
|
+
});
|
|
108
|
+
success('Agent skills installed.');
|
|
109
|
+
return;
|
|
110
|
+
} catch {
|
|
111
|
+
// Primary method failed -- try fallback
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── Fallback: direct download ────────────────────────────────────
|
|
115
|
+
if (selectedEditors.length > 0) {
|
|
116
|
+
const ok = await fallbackInstall(selectedEditors);
|
|
117
|
+
if (ok) return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
warn('Skill install failed (git may not be installed).');
|
|
121
|
+
console.log(' You can install it manually later:');
|
|
122
|
+
console.log(' npx skills add SpriteCook/skills --skill "*"');
|
|
123
|
+
console.log(' Or install git and re-run this setup.');
|
|
124
|
+
}
|