@nitrostack/cli 1.0.12 → 1.0.14

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.
Files changed (42) hide show
  1. package/dist/commands/cursor.d.ts +11 -0
  2. package/dist/commands/cursor.d.ts.map +1 -0
  3. package/dist/commands/cursor.js +238 -0
  4. package/dist/commands/init.d.ts +1 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +82 -9
  7. package/dist/commands/upgrade.d.ts +12 -0
  8. package/dist/commands/upgrade.d.ts.map +1 -1
  9. package/dist/commands/upgrade.js +210 -106
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +14 -0
  13. package/dist/skills/clone.d.ts +28 -0
  14. package/dist/skills/clone.d.ts.map +1 -0
  15. package/dist/skills/clone.js +60 -0
  16. package/dist/skills/detect-agents.d.ts +8 -0
  17. package/dist/skills/detect-agents.d.ts.map +1 -0
  18. package/dist/skills/detect-agents.js +129 -0
  19. package/dist/skills/discover.d.ts +12 -0
  20. package/dist/skills/discover.d.ts.map +1 -0
  21. package/dist/skills/discover.js +45 -0
  22. package/dist/skills/index.d.ts +21 -0
  23. package/dist/skills/index.d.ts.map +1 -0
  24. package/dist/skills/index.js +97 -0
  25. package/dist/skills/installer.d.ts +21 -0
  26. package/dist/skills/installer.d.ts.map +1 -0
  27. package/dist/skills/installer.js +48 -0
  28. package/dist/skills/types.d.ts +39 -0
  29. package/dist/skills/types.d.ts.map +1 -0
  30. package/dist/skills/types.js +1 -0
  31. package/dist/skills/ui.d.ts +55 -0
  32. package/dist/skills/ui.d.ts.map +1 -0
  33. package/dist/skills/ui.js +102 -0
  34. package/package.json +3 -3
  35. package/templates/typescript-oauth/.env.example +71 -14
  36. package/templates/typescript-oauth/src/app.module.ts +7 -0
  37. package/templates/typescript-oauth/src/guards/oauth.guard.ts +19 -0
  38. package/templates/typescript-oauth/src/index.ts +9 -11
  39. package/templates/typescript-oauth/src/modules/flights/flights.prompts.ts +19 -1
  40. package/templates/typescript-oauth/src/services/duffel.service.ts +4 -2
  41. package/templates/typescript-pizzaz/.env.example +10 -0
  42. package/templates/typescript-starter/.env.example +10 -0
@@ -2,22 +2,61 @@ import chalk from 'chalk';
2
2
  import { execSync } from 'child_process';
3
3
  import path from 'path';
4
4
  import fs from 'fs-extra';
5
+ import https from 'https';
5
6
  import { createHeader, createBox, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
6
7
  import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
8
+ import { cloneSkillsRepo } from '../skills/clone.js';
9
+ import { discoverSkills } from '../skills/discover.js';
10
+ import { installSkills } from '../skills/installer.js';
11
+ import { AGENTS } from '../skills/detect-agents.js';
7
12
  /**
8
- * Get the latest version of a package from npm registry
13
+ * Fetch a package's latest published version from NPM using standard https module.
9
14
  */
10
- function getLatestVersion(packageName = '@nitrostack/core') {
11
- try {
12
- const result = execSync(`npm view ${packageName} version`, {
13
- encoding: 'utf-8',
14
- stdio: ['pipe', 'pipe', 'pipe'],
15
- }).trim();
16
- return result;
17
- }
18
- catch {
19
- throw new Error(`Failed to fetch latest version for ${packageName} from npm`);
20
- }
15
+ export function fetchLatestNpmVersion(packageName) {
16
+ return new Promise((resolve, reject) => {
17
+ const url = `https://registry.npmjs.org/${packageName}/latest`;
18
+ const req = https.get(url, {
19
+ headers: {
20
+ 'Accept': 'application/json',
21
+ 'User-Agent': 'nitrostack-cli-upgrade'
22
+ },
23
+ timeout: 5000
24
+ }, (res) => {
25
+ if (res.statusCode !== 200) {
26
+ reject(new Error(`Registry responded with HTTP ${res.statusCode}`));
27
+ return;
28
+ }
29
+ let data = '';
30
+ res.on('data', (chunk) => {
31
+ data += chunk;
32
+ });
33
+ // Handle stream errors
34
+ res.on('error', (err) => {
35
+ reject(new Error(`Response stream error: ${err.message}`));
36
+ });
37
+ res.on('end', () => {
38
+ try {
39
+ const parsed = JSON.parse(data);
40
+ if (parsed.version) {
41
+ resolve(parsed.version);
42
+ }
43
+ else {
44
+ reject(new Error('Invalid response structure from NPM registry'));
45
+ }
46
+ }
47
+ catch (e) {
48
+ reject(new Error(`Failed to parse response: ${e.message}`));
49
+ }
50
+ });
51
+ });
52
+ req.on('error', (err) => {
53
+ reject(err);
54
+ });
55
+ req.on('timeout', () => {
56
+ req.destroy();
57
+ reject(new Error('Request timed out'));
58
+ });
59
+ });
21
60
  }
22
61
  /**
23
62
  * Get the current installed version of @nitrostack/core from package.json
@@ -32,15 +71,19 @@ function getCoreVersion(packageJsonPath) {
32
71
  null;
33
72
  }
34
73
  /**
35
- * Parse version string to extract the actual version number
74
+ * Parse version string to extract the actual numeric version.
75
+ *
76
+ * Strips any leading range operator (^ ~ >= <= > <) and drops the pre-release
77
+ * suffix (e.g. `-beta.1`) so the dot-separated segments always parse to numbers
78
+ * instead of producing `NaN` in `compareVersions`.
36
79
  */
37
80
  function parseVersion(versionString) {
38
- return versionString.replace(/^[\^~>=<]+/, '');
81
+ return versionString.replace(/^[\^~>=<]+/, '').split('-')[0];
39
82
  }
40
83
  /**
41
84
  * Compare two version strings
42
85
  */
43
- function compareVersions(v1, v2) {
86
+ export function compareVersions(v1, v2) {
44
87
  const parts1 = parseVersion(v1).split('.').map(Number);
45
88
  const parts2 = parseVersion(v2).split('.').map(Number);
46
89
  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
@@ -54,37 +97,58 @@ function compareVersions(v1, v2) {
54
97
  return 0;
55
98
  }
56
99
  /**
57
- * Update all @nitrostack/* versions in package.json
100
+ * Determine if a dependency refers to a local file or workspace link.
58
101
  */
59
- function updatePackageJson(packageJsonPath, newVersion, dryRun) {
102
+ export function isLocalDependency(version) {
103
+ return version.startsWith('file:') ||
104
+ version.startsWith('link:') ||
105
+ version.startsWith('workspace:') ||
106
+ version.startsWith('.') ||
107
+ version.startsWith('/');
108
+ }
109
+ /**
110
+ * Update all @nitrostack/* versions in package.json using dynamic npm registry fetch
111
+ */
112
+ async function updatePackageJson(packageJsonPath, dryRun) {
60
113
  if (!fs.existsSync(packageJsonPath)) {
61
114
  return [];
62
115
  }
63
116
  const packageJson = fs.readJSONSync(packageJsonPath);
64
117
  const results = [];
65
118
  let hasChanges = false;
66
- const updateDeps = (deps) => {
119
+ const updateDeps = async (deps) => {
67
120
  if (!deps)
68
121
  return;
69
- for (const pkg of Object.keys(deps)) {
122
+ const promises = Object.keys(deps).map(async (pkg) => {
70
123
  if (pkg.startsWith('@nitrostack/')) {
71
124
  const currentVersion = deps[pkg];
72
- if (compareVersions(currentVersion, newVersion) < 0) {
73
- results.push({
74
- location: path.basename(path.dirname(packageJsonPath)),
75
- packageName: pkg,
76
- previousVersion: currentVersion,
77
- newVersion: `^${newVersion}`,
78
- upgraded: true,
79
- });
80
- deps[pkg] = `^${newVersion}`;
81
- hasChanges = true;
125
+ // Skip local dependencies entirely
126
+ if (isLocalDependency(currentVersion)) {
127
+ return;
128
+ }
129
+ try {
130
+ const latestVersion = await fetchLatestNpmVersion(pkg);
131
+ if (compareVersions(currentVersion, latestVersion) < 0) {
132
+ results.push({
133
+ location: path.basename(path.dirname(packageJsonPath)),
134
+ packageName: pkg,
135
+ previousVersion: currentVersion,
136
+ newVersion: `^${latestVersion}`,
137
+ upgraded: true,
138
+ });
139
+ deps[pkg] = `^${latestVersion}`;
140
+ hasChanges = true;
141
+ }
142
+ }
143
+ catch (error) {
144
+ console.warn(`\n⚠️ Skipped upgrade check for ${pkg}: ${error.message}`);
82
145
  }
83
146
  }
84
- }
147
+ });
148
+ await Promise.all(promises);
85
149
  };
86
- updateDeps(packageJson.dependencies);
87
- updateDeps(packageJson.devDependencies);
150
+ await updateDeps(packageJson.dependencies);
151
+ await updateDeps(packageJson.devDependencies);
88
152
  if (hasChanges && !dryRun) {
89
153
  fs.writeJSONSync(packageJsonPath, packageJson, { spaces: 2 });
90
154
  }
@@ -123,51 +187,22 @@ export async function upgradeCommand(options) {
123
187
  console.log(createErrorBox('Not a NitroStack Project', '@nitrostack/core is not a dependency'));
124
188
  process.exit(1);
125
189
  }
126
- // Fetch latest version
127
- const spinner = new NitroSpinner('Checking for updates...').start();
128
- let latestVersion;
129
- try {
130
- latestVersion = getLatestVersion('@nitrostack/core');
131
- spinner.succeed(`Latest version: ${chalk.cyan(latestVersion)}`);
132
- }
133
- catch (error) {
134
- spinner.fail('Failed to fetch latest version');
135
- process.exit(1);
136
- }
137
- // Check if already on latest
138
- const currentParsedVersion = parseVersion(coreVersion);
139
- if (compareVersions(currentParsedVersion, latestVersion) >= 0) {
140
- spacer();
141
- console.log(createSuccessBox('Already Up to Date', [
142
- `Current version: ${currentParsedVersion}`,
143
- `Latest version: ${latestVersion}`,
144
- ]));
145
- trackEvent('cli_upgrade_completed', {
146
- packages_upgraded: 0,
147
- from_version: currentParsedVersion,
148
- to_version: latestVersion,
149
- dry_run: !!options.dryRun,
150
- already_current: true,
151
- });
152
- await shutdownAnalytics();
153
- return;
154
- }
155
- const allResults = [];
156
190
  const dryRun = options.dryRun ?? false;
157
191
  if (dryRun) {
158
192
  spacer();
159
193
  log('Dry run mode - no changes will be made', 'warning');
160
194
  }
161
195
  spacer();
162
- log('Upgrading dependencies...', 'info');
196
+ log('Checking for package updates...', 'info');
163
197
  spacer();
198
+ const allResults = [];
164
199
  // Upgrade root
165
- const rootSpinner = new NitroSpinner('Updating root package.json...').start();
200
+ const rootSpinner = new NitroSpinner('Checking root package.json...').start();
166
201
  try {
167
- const results = updatePackageJson(rootPackageJsonPath, latestVersion, dryRun);
202
+ const results = await updatePackageJson(rootPackageJsonPath, dryRun);
168
203
  if (results.length > 0) {
169
204
  allResults.push(...results);
170
- rootSpinner.succeed(`Root: Updated ${results.length} @nitrostack packages`);
205
+ rootSpinner.succeed(`Root: Found ${results.length} package update(s)`);
171
206
  if (!dryRun) {
172
207
  const installSpinner = new NitroSpinner('Installing dependencies...').start();
173
208
  runNpmInstall(projectRoot);
@@ -175,7 +210,7 @@ export async function upgradeCommand(options) {
175
210
  }
176
211
  }
177
212
  else {
178
- rootSpinner.info('Root: All @nitrostack packages are up to date');
213
+ rootSpinner.info('Root: All @nitrostack packages are up to date (or local references)');
179
214
  }
180
215
  }
181
216
  catch (error) {
@@ -184,12 +219,12 @@ export async function upgradeCommand(options) {
184
219
  }
185
220
  // Upgrade widgets if they exist
186
221
  if (fs.existsSync(widgetsPackageJsonPath)) {
187
- const widgetsSpinner = new NitroSpinner('Updating widgets package.json...').start();
222
+ const widgetsSpinner = new NitroSpinner('Checking widgets package.json...').start();
188
223
  try {
189
- const results = updatePackageJson(widgetsPackageJsonPath, latestVersion, dryRun);
224
+ const results = await updatePackageJson(widgetsPackageJsonPath, dryRun);
190
225
  if (results.length > 0) {
191
226
  allResults.push(...results);
192
- widgetsSpinner.succeed(`Widgets: Updated ${results.length} @nitrostack packages`);
227
+ widgetsSpinner.succeed(`Widgets: Found ${results.length} package update(s)`);
193
228
  if (!dryRun) {
194
229
  const installSpinner = new NitroSpinner('Installing widget dependencies...').start();
195
230
  runNpmInstall(widgetsPath);
@@ -197,54 +232,123 @@ export async function upgradeCommand(options) {
197
232
  }
198
233
  }
199
234
  else {
200
- widgetsSpinner.info('Widgets: All @nitrostack packages are up to date');
235
+ widgetsSpinner.info('Widgets: All @nitrostack packages are up to date (or local references)');
201
236
  }
202
237
  }
203
238
  catch (error) {
204
239
  widgetsSpinner.fail('Failed to upgrade widgets');
240
+ console.error(error);
205
241
  }
206
242
  }
207
- // Summary
208
- spacer();
209
- if (allResults.length === 0) {
210
- log('No packages were upgraded', 'warning');
211
- }
212
- else {
213
- // Unique packages upgraded
214
- const uniquePackages = Array.from(new Set(allResults.map(r => r.packageName)));
215
- const summaryItems = uniquePackages.map(pkg => {
216
- const result = allResults.find(r => r.packageName === pkg);
217
- return `${pkg}: ${parseVersion(result.previousVersion)} → ${parseVersion(result.newVersion)}`;
218
- });
219
- if (dryRun) {
220
- spacer();
221
- console.log(createBox([
222
- chalk.yellow.bold('Dry run complete'),
223
- '',
224
- chalk.dim('No changes were made to your project.'),
225
- chalk.dim('Run without --dry-run to apply the upgrade.'),
226
- ], 'warning'));
243
+ // Upgrade skills
244
+ const rootPackageJson = fs.readJSONSync(rootPackageJsonPath);
245
+ const localSkillsVersion = rootPackageJson.nitrostack?.skillsVersion || '0.0.0';
246
+ let tempSkillsDir = null;
247
+ const skillsSpinner = new NitroSpinner('Checking agent skills...').start();
248
+ try {
249
+ tempSkillsDir = await cloneSkillsRepo();
250
+ const skillsPkgJsonPath = path.join(tempSkillsDir, 'package.json');
251
+ let remoteSkillsVersion = '1.0.0';
252
+ if (fs.existsSync(skillsPkgJsonPath)) {
253
+ const skillsPkgJson = fs.readJSONSync(skillsPkgJsonPath);
254
+ remoteSkillsVersion = skillsPkgJson.version || '1.0.0';
255
+ }
256
+ if (compareVersions(localSkillsVersion, remoteSkillsVersion) < 0) {
257
+ if (dryRun) {
258
+ allResults.push({
259
+ location: 'skills',
260
+ packageName: '@nitrostack/skills',
261
+ previousVersion: localSkillsVersion,
262
+ newVersion: remoteSkillsVersion,
263
+ upgraded: false,
264
+ });
265
+ skillsSpinner.info(`Skills: Updates available (${localSkillsVersion} → ${remoteSkillsVersion})`);
266
+ }
267
+ else {
268
+ const skills = await discoverSkills(tempSkillsDir);
269
+ await installSkills(AGENTS, skills, true, 'project', projectRoot);
270
+ // Write new version to package.json
271
+ const updatedPackageJson = fs.readJSONSync(rootPackageJsonPath);
272
+ if (!updatedPackageJson.nitrostack) {
273
+ updatedPackageJson.nitrostack = {};
274
+ }
275
+ updatedPackageJson.nitrostack.skillsVersion = remoteSkillsVersion;
276
+ fs.writeJSONSync(rootPackageJsonPath, updatedPackageJson, { spaces: 2 });
277
+ allResults.push({
278
+ location: 'skills',
279
+ packageName: '@nitrostack/skills',
280
+ previousVersion: localSkillsVersion,
281
+ newVersion: remoteSkillsVersion,
282
+ upgraded: true,
283
+ });
284
+ skillsSpinner.succeed(`Skills: Upgraded from ${localSkillsVersion} to ${remoteSkillsVersion}`);
285
+ }
227
286
  }
228
287
  else {
229
- console.log(createSuccessBox('Upgrade Complete', [
230
- ...summaryItems,
231
- '',
232
- chalk.dim(`Total updates across all packages: ${allResults.length}`)
233
- ]));
234
- nextSteps([
235
- 'Review the changes in package.json',
236
- 'Restart your development server',
237
- 'Check docs.nitrostack.ai for migration guides',
238
- ]);
288
+ skillsSpinner.info('Skills: All agent skills are up to date');
289
+ }
290
+ }
291
+ catch (error) {
292
+ skillsSpinner.fail('Failed to upgrade agent skills');
293
+ console.error(error);
294
+ }
295
+ finally {
296
+ if (tempSkillsDir) {
297
+ try {
298
+ fs.removeSync(tempSkillsDir);
299
+ }
300
+ catch {
301
+ // best effort
302
+ }
239
303
  }
240
- showFooter();
304
+ }
305
+ // Summary
306
+ spacer();
307
+ if (allResults.length === 0) {
308
+ console.log(createSuccessBox('Already Up to Date', [
309
+ 'All @nitrostack packages are already running their latest versions.',
310
+ ]));
241
311
  trackEvent('cli_upgrade_completed', {
242
- packages_upgraded: allResults.length,
243
- from_version: currentParsedVersion,
244
- to_version: latestVersion,
312
+ packages_upgraded: 0,
245
313
  dry_run: dryRun,
246
- already_current: false,
314
+ already_current: true,
247
315
  });
248
316
  await shutdownAnalytics();
317
+ return;
249
318
  }
319
+ // Unique packages upgraded
320
+ const uniquePackages = Array.from(new Set(allResults.map(r => r.packageName)));
321
+ const summaryItems = uniquePackages.map(pkg => {
322
+ const result = allResults.find(r => r.packageName === pkg);
323
+ return `${pkg}: ${parseVersion(result.previousVersion)} → ${parseVersion(result.newVersion)}`;
324
+ });
325
+ if (dryRun) {
326
+ spacer();
327
+ console.log(createBox([
328
+ chalk.yellow.bold('Dry Run - Proposed Upgrades:'),
329
+ ...summaryItems.map(item => ` • ${item}`),
330
+ '',
331
+ chalk.dim('No changes were made to your project.'),
332
+ chalk.dim('Run without --dry-run to apply the upgrade.'),
333
+ ], 'warning'));
334
+ }
335
+ else {
336
+ console.log(createSuccessBox('Upgrade Complete', [
337
+ ...summaryItems,
338
+ '',
339
+ chalk.dim(`Total updates across all packages: ${allResults.length}`)
340
+ ]));
341
+ nextSteps([
342
+ 'Review the changes in package.json',
343
+ 'Restart your development server',
344
+ 'Check docs.nitrostack.ai for migration guides',
345
+ ]);
346
+ }
347
+ showFooter();
348
+ trackEvent('cli_upgrade_completed', {
349
+ packages_upgraded: allResults.length,
350
+ dry_run: dryRun,
351
+ already_current: false,
352
+ });
353
+ await shutdownAnalytics();
250
354
  }
package/dist/index.d.ts CHANGED
@@ -8,4 +8,5 @@ export { startCommand } from './commands/start.js';
8
8
  export { generate } from './commands/generate.js';
9
9
  export { upgradeCommand } from './commands/upgrade.js';
10
10
  export { installCommand } from './commands/install.js';
11
+ export { cursorCommand } from './commands/cursor.js';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,aAAa,YAgE5B;AAGD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,aAAa,YA6E5B;AAGD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { startCommand } from './commands/start.js';
8
8
  import { generate } from './commands/generate.js';
9
9
  import { upgradeCommand } from './commands/upgrade.js';
10
10
  import { installCommand } from './commands/install.js';
11
+ import { cursorCommand } from './commands/cursor.js';
11
12
  const require = createRequire(import.meta.url);
12
13
  const packageJson = require('../package.json');
13
14
  export function createProgram() {
@@ -24,6 +25,7 @@ export function createProgram() {
24
25
  .option('--description <description>', 'Description of the project')
25
26
  .option('--author <author>', 'Author of the project')
26
27
  .option('--skip-install', 'Skip installing dependencies')
28
+ .option('--force', 'Overwrite existing skill files when adding agent skills')
27
29
  .action(initCommand);
28
30
  program
29
31
  .command('dev')
@@ -64,6 +66,17 @@ export function createProgram() {
64
66
  .option('--skip-widgets', 'Skip installing widget dependencies')
65
67
  .option('--production', 'Install production dependencies only')
66
68
  .action(installCommand);
69
+ program
70
+ .command('cursor')
71
+ .alias('c')
72
+ .description('Integrate this MCP server with Cursor')
73
+ .option('-g, --global', 'Install globally to ~/.cursor/mcp.json')
74
+ .option('-l, --local', 'Install locally to .cursor/mcp.json')
75
+ .option('-t, --type <type>', 'Connection type: "command", "legacy-sse", or "streamable-http" (alias: "sse" → legacy-sse)')
76
+ .option('-u, --url <url>', 'HTTP connection URL (for legacy-sse or streamable-http)')
77
+ .option('-p, --port <port>', 'Port for default HTTP URL (for legacy-sse or streamable-http)')
78
+ .option('--force', 'Force overwrite of existing configuration')
79
+ .action(cursorCommand);
67
80
  return program;
68
81
  }
69
82
  // Re-export commands for programmatic use
@@ -74,6 +87,7 @@ export { startCommand } from './commands/start.js';
74
87
  export { generate } from './commands/generate.js';
75
88
  export { upgradeCommand } from './commands/upgrade.js';
76
89
  export { installCommand } from './commands/install.js';
90
+ export { cursorCommand } from './commands/cursor.js';
77
91
  // Run the CLI when this module is the entry point
78
92
  import { fileURLToPath } from 'url';
79
93
  import { realpathSync } from 'fs';
@@ -0,0 +1,28 @@
1
+ export declare const SKILLS_REPO_URL = "https://github.com/nitrocloudofficial/skills.git";
2
+ /**
3
+ * Thrown when the skills repository cannot be cloned (git missing, network
4
+ * error, repository not found, etc.). The caller should catch this and
5
+ * display a user-friendly warning rather than crashing the entire init flow.
6
+ */
7
+ export declare class SkillsCloneError extends Error {
8
+ readonly cause?: unknown | undefined;
9
+ constructor(message: string, cause?: unknown | undefined);
10
+ }
11
+ /**
12
+ * Clones the NitroStack skills repository into a unique temporary directory
13
+ * and returns the absolute path to that directory.
14
+ *
15
+ * The caller is responsible for cleaning up the directory when done:
16
+ * ```ts
17
+ * const tempDir = await cloneSkillsRepo();
18
+ * try {
19
+ * // use tempDir …
20
+ * } finally {
21
+ * await fs.remove(tempDir);
22
+ * }
23
+ * ```
24
+ *
25
+ * @throws {SkillsCloneError} when git is unavailable or the clone fails.
26
+ */
27
+ export declare function cloneSkillsRepo(): Promise<string>;
28
+ //# sourceMappingURL=clone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.d.ts","sourceRoot":"","sources":["../../src/skills/clone.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,eAAe,qDAAqD,CAAC;AAElF;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aACI,KAAK,CAAC,EAAE,OAAO;gBAAhD,OAAO,EAAE,MAAM,EAAkB,KAAK,CAAC,EAAE,OAAO,YAAA;CAI7D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAiCvD"}
@@ -0,0 +1,60 @@
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import crypto from 'crypto';
4
+ import { execSync } from 'child_process';
5
+ import fs from 'fs-extra';
6
+ export const SKILLS_REPO_URL = 'https://github.com/nitrocloudofficial/skills.git';
7
+ /**
8
+ * Thrown when the skills repository cannot be cloned (git missing, network
9
+ * error, repository not found, etc.). The caller should catch this and
10
+ * display a user-friendly warning rather than crashing the entire init flow.
11
+ */
12
+ export class SkillsCloneError extends Error {
13
+ cause;
14
+ constructor(message, cause) {
15
+ super(message);
16
+ this.cause = cause;
17
+ this.name = 'SkillsCloneError';
18
+ }
19
+ }
20
+ /**
21
+ * Clones the NitroStack skills repository into a unique temporary directory
22
+ * and returns the absolute path to that directory.
23
+ *
24
+ * The caller is responsible for cleaning up the directory when done:
25
+ * ```ts
26
+ * const tempDir = await cloneSkillsRepo();
27
+ * try {
28
+ * // use tempDir …
29
+ * } finally {
30
+ * await fs.remove(tempDir);
31
+ * }
32
+ * ```
33
+ *
34
+ * @throws {SkillsCloneError} when git is unavailable or the clone fails.
35
+ */
36
+ export async function cloneSkillsRepo() {
37
+ const uniqueId = crypto.randomBytes(6).toString('hex');
38
+ const tempDir = path.join(os.tmpdir(), `nitrostack-skills-${uniqueId}`);
39
+ try {
40
+ execSync(`git clone --depth 1 "${SKILLS_REPO_URL}" "${tempDir}"`, {
41
+ stdio: 'pipe',
42
+ timeout: 60_000,
43
+ });
44
+ }
45
+ catch (err) {
46
+ // Clean up any partial clone before throwing
47
+ try {
48
+ await fs.remove(tempDir);
49
+ }
50
+ catch {
51
+ // best-effort cleanup
52
+ }
53
+ const message = err instanceof Error ? err.message : String(err);
54
+ if (message.includes('git: command not found') || message.includes("'git' is not recognized")) {
55
+ throw new SkillsCloneError('Git is not installed or not in PATH. Install Git from https://git-scm.com and try again.', err);
56
+ }
57
+ throw new SkillsCloneError(`Failed to clone skills repository: ${message.split('\n')[0]}`, err);
58
+ }
59
+ return tempDir;
60
+ }
@@ -0,0 +1,8 @@
1
+ import type { AgentDescriptor } from './types.js';
2
+ export declare const AGENTS: AgentDescriptor[];
3
+ /**
4
+ * Runs all agent detectors in parallel and returns only the agents that are
5
+ * detected on the current machine.
6
+ */
7
+ export declare function detectAgents(): Promise<AgentDescriptor[]>;
8
+ //# sourceMappingURL=detect-agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-agents.d.ts","sourceRoot":"","sources":["../../src/skills/detect-agents.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAkElD,eAAO,MAAM,MAAM,EAAE,eAAe,EAwDnC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAa/D"}