nexscope 1.0.0 → 1.0.2
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 +5 -2
- package/src/commands/install.js +56 -58
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexscope",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "NexScope CLI tool",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"nexscope": "bin/nexscope.js"
|
|
7
|
+
"nexscope": "./bin/nexscope.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"test": "node bin/nexscope.js --help"
|
|
@@ -16,5 +16,8 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"engines": {
|
|
18
18
|
"node": ">=14.0.0"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"listr2": "^5.0.8"
|
|
19
22
|
}
|
|
20
23
|
}
|
package/src/commands/install.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { spawn } = require('child_process');
|
|
4
4
|
const https = require('https');
|
|
5
|
+
const { Listr } = require('listr2');
|
|
5
6
|
|
|
6
7
|
const SKILLS_REPO = 'nexscope-ai/eCommerce-Skills';
|
|
7
8
|
const REPO_API = 'https://api.github.com/repos/nexscope-ai/eCommerce-Skills/contents';
|
|
@@ -47,37 +48,40 @@ async function fetchSkillList() {
|
|
|
47
48
|
return JSON.parse(data).filter((i) => i.type === 'dir').map((i) => i.name);
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
// Parse directory-related flags to pass through to npx skills
|
|
51
51
|
function parseDirFlags(args) {
|
|
52
52
|
const flags = [];
|
|
53
|
-
|
|
54
|
-
if (args.includes('--global') || args.includes('-g')) {
|
|
55
|
-
flags.push('-g');
|
|
56
|
-
}
|
|
57
|
-
|
|
53
|
+
if (args.includes('--global') || args.includes('-g')) flags.push('-g');
|
|
58
54
|
const agentIndex = args.findIndex((a) => a === '--agent' || a === '-a');
|
|
59
55
|
if (agentIndex !== -1 && args[agentIndex + 1]) {
|
|
60
56
|
flags.push('--agent', args[agentIndex + 1]);
|
|
61
57
|
}
|
|
62
|
-
|
|
63
58
|
return flags;
|
|
64
59
|
}
|
|
65
60
|
|
|
66
|
-
function
|
|
67
|
-
return new Promise((resolve) => {
|
|
68
|
-
const
|
|
69
|
-
const proc = spawn('npx', skillsArgs, { shell: true });
|
|
70
|
-
|
|
61
|
+
function spawnAsync(cmd, args) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const proc = spawn(cmd, args, { shell: true });
|
|
71
64
|
let output = '';
|
|
72
65
|
proc.stdout.on('data', (d) => (output += d.toString()));
|
|
73
66
|
proc.stderr.on('data', (d) => (output += d.toString()));
|
|
74
|
-
|
|
75
67
|
proc.on('close', (code) => {
|
|
76
|
-
|
|
68
|
+
if (code === 0) resolve(output);
|
|
69
|
+
else reject(new Error(output.trim()));
|
|
77
70
|
});
|
|
78
71
|
});
|
|
79
72
|
}
|
|
80
73
|
|
|
74
|
+
async function getInstalledSkills(dirFlags) {
|
|
75
|
+
try {
|
|
76
|
+
const output = await spawnAsync('npx', ['skills', 'ls', '--json', ...dirFlags]);
|
|
77
|
+
const data = JSON.parse(output);
|
|
78
|
+
// Each entry may be a string or object with a name field
|
|
79
|
+
return data.map((s) => (typeof s === 'string' ? s : s.name || s.skill || '')).filter(Boolean);
|
|
80
|
+
} catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
81
85
|
function runSkillsSync(skillsArgs) {
|
|
82
86
|
return new Promise((resolve) => {
|
|
83
87
|
const proc = spawn('npx', ['skills', ...skillsArgs], { shell: true, stdio: 'inherit' });
|
|
@@ -85,6 +89,10 @@ function runSkillsSync(skillsArgs) {
|
|
|
85
89
|
});
|
|
86
90
|
}
|
|
87
91
|
|
|
92
|
+
function runSkillsSilent(skillsArgs) {
|
|
93
|
+
return spawnAsync('npx', ['skills', ...skillsArgs]);
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
async function run(args) {
|
|
89
97
|
if (args.includes('--help') || args.includes('-h')) {
|
|
90
98
|
printHelp();
|
|
@@ -106,60 +114,50 @@ async function run(args) {
|
|
|
106
114
|
console.error("Run 'nexscope install --help' for usage.");
|
|
107
115
|
process.exit(1);
|
|
108
116
|
}
|
|
109
|
-
|
|
110
|
-
const
|
|
117
|
+
|
|
118
|
+
const installed = await getInstalledSkills(dirFlags);
|
|
119
|
+
|
|
120
|
+
const tasks = new Listr([{
|
|
121
|
+
title: skillName,
|
|
122
|
+
skip: () => installed.includes(skillName) ? 'Already installed' : false,
|
|
123
|
+
task: () => runSkillsSilent(['add', SKILLS_REPO, '--skill', skillName, '-y', ...dirFlags]),
|
|
124
|
+
}], { exitOnError: false });
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await tasks.run();
|
|
128
|
+
} catch {
|
|
129
|
+
// error already shown by listr2
|
|
130
|
+
}
|
|
131
|
+
|
|
111
132
|
printThankYou();
|
|
112
|
-
process.exit(code || 0);
|
|
113
133
|
} else {
|
|
114
|
-
// All skills: fetch list then install concurrently
|
|
115
134
|
process.stdout.write('Fetching skill list... ');
|
|
116
|
-
const skills = await
|
|
135
|
+
const [skills, installed] = await Promise.all([
|
|
136
|
+
fetchSkillList(),
|
|
137
|
+
getInstalledSkills(dirFlags),
|
|
138
|
+
]);
|
|
117
139
|
console.log(`${skills.length} skills found.\n`);
|
|
118
140
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const icon =
|
|
130
|
-
status[s] === 'pending' ? '⏳' :
|
|
131
|
-
status[s] === 'running' ? '⚙ ' :
|
|
132
|
-
status[s] === 'done' ? '✓ ' : '✗ ';
|
|
133
|
-
console.log(` ${icon} ${s}`);
|
|
141
|
+
const tasks = new Listr(
|
|
142
|
+
skills.map((skillName) => ({
|
|
143
|
+
title: skillName,
|
|
144
|
+
skip: () => installed.includes(skillName) ? 'Already installed' : false,
|
|
145
|
+
task: () => spawnAsync('npx', ['skills', 'add', SKILLS_REPO, '--skill', skillName, '-y', ...dirFlags]),
|
|
146
|
+
})),
|
|
147
|
+
{
|
|
148
|
+
concurrent: true,
|
|
149
|
+
exitOnError: false,
|
|
150
|
+
rendererOptions: { collapseErrors: false },
|
|
134
151
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
printStatus();
|
|
138
|
-
|
|
139
|
-
const promises = skills.map((skillName) => {
|
|
140
|
-
status[skillName] = 'running';
|
|
141
|
-
printStatus();
|
|
142
|
-
return installOne(skillName, dirFlags).then((result) => {
|
|
143
|
-
status[skillName] = result.success ? 'done' : 'failed';
|
|
144
|
-
printStatus();
|
|
145
|
-
return result;
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
const results = await Promise.all(promises);
|
|
150
|
-
|
|
151
|
-
const succeeded = results.filter((r) => r.success);
|
|
152
|
-
const failed = results.filter((r) => !r.success);
|
|
153
|
-
|
|
154
|
-
console.log(`\n${succeeded.length}/${skills.length} skills installed successfully.`);
|
|
152
|
+
);
|
|
155
153
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
try {
|
|
155
|
+
await tasks.run();
|
|
156
|
+
} catch {
|
|
157
|
+
// listr2 throws if any task fails; results are already displayed
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
printThankYou();
|
|
162
|
-
process.exit(failed.length > 0 ? 1 : 0);
|
|
163
161
|
}
|
|
164
162
|
}
|
|
165
163
|
|