nexscope 1.0.0 → 1.0.1

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 CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "nexscope",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
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
  }
@@ -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 installOne(skillName, dirFlags) {
67
- return new Promise((resolve) => {
68
- const skillsArgs = ['skills', 'add', SKILLS_REPO, '--skill', skillName, '-y', ...dirFlags];
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
- resolve({ skillName, success: code === 0, output });
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' });
@@ -106,60 +110,45 @@ async function run(args) {
106
110
  console.error("Run 'nexscope install --help' for usage.");
107
111
  process.exit(1);
108
112
  }
109
- // Single skill: pass through output directly
113
+
114
+ const installed = await getInstalledSkills(dirFlags);
115
+ if (installed.includes(skillName)) {
116
+ console.log(`Skill "${skillName}" is already installed. Skipping.`);
117
+ printThankYou();
118
+ return;
119
+ }
120
+
110
121
  const code = await runSkillsSync(['add', SKILLS_REPO, '--skill', skillName, '-y', ...dirFlags]);
111
122
  printThankYou();
112
123
  process.exit(code || 0);
113
124
  } else {
114
- // All skills: fetch list then install concurrently
115
125
  process.stdout.write('Fetching skill list... ');
116
- const skills = await fetchSkillList();
126
+ const [skills, installed] = await Promise.all([
127
+ fetchSkillList(),
128
+ getInstalledSkills(dirFlags),
129
+ ]);
117
130
  console.log(`${skills.length} skills found.\n`);
118
131
 
119
- console.log(`Installing all skills concurrently...\n`);
120
-
121
- // Track per-skill status
122
- const status = {};
123
- skills.forEach((s) => (status[s] = 'pending'));
124
-
125
- function printStatus() {
126
- process.stdout.write('\x1B[2J\x1B[0f'); // clear screen
127
- console.log(`Installing ${skills.length} skills concurrently...\n`);
128
- for (const s of skills) {
129
- const icon =
130
- status[s] === 'pending' ? '⏳' :
131
- status[s] === 'running' ? '⚙ ' :
132
- status[s] === 'done' ? '✓ ' : '✗ ';
133
- console.log(` ${icon} ${s}`);
132
+ const tasks = new Listr(
133
+ skills.map((skillName) => ({
134
+ title: skillName,
135
+ skip: () => installed.includes(skillName) ? 'Already installed' : false,
136
+ task: () => spawnAsync('npx', ['skills', 'add', SKILLS_REPO, '--skill', skillName, '-y', ...dirFlags]),
137
+ })),
138
+ {
139
+ concurrent: true,
140
+ exitOnError: false,
141
+ rendererOptions: { collapseErrors: false },
134
142
  }
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.`);
143
+ );
155
144
 
156
- if (failed.length > 0) {
157
- console.log('\nFailed skills:');
158
- failed.forEach((r) => console.log(` ✗ ${r.skillName}`));
145
+ try {
146
+ await tasks.run();
147
+ } catch {
148
+ // listr2 throws if any task fails; results are already displayed
159
149
  }
160
150
 
161
151
  printThankYou();
162
- process.exit(failed.length > 0 ? 1 : 0);
163
152
  }
164
153
  }
165
154