@nitrostack/cli 1.0.13 → 1.0.15

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 (73) hide show
  1. package/assets/canonical.gitignore +57 -0
  2. package/dist/commands/init.d.ts +1 -0
  3. package/dist/commands/init.d.ts.map +1 -1
  4. package/dist/commands/init.js +90 -16
  5. package/dist/commands/pack.d.ts +10 -0
  6. package/dist/commands/pack.d.ts.map +1 -0
  7. package/dist/commands/pack.js +82 -0
  8. package/dist/commands/upgrade.d.ts.map +1 -1
  9. package/dist/commands/upgrade.js +66 -0
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +13 -0
  13. package/dist/pack/canonical-gitignore.d.ts +14 -0
  14. package/dist/pack/canonical-gitignore.d.ts.map +1 -0
  15. package/dist/pack/canonical-gitignore.js +28 -0
  16. package/dist/pack/exclusions.d.ts +8 -0
  17. package/dist/pack/exclusions.d.ts.map +1 -0
  18. package/dist/pack/exclusions.js +84 -0
  19. package/dist/pack/gitignore.d.ts +21 -0
  20. package/dist/pack/gitignore.d.ts.map +1 -0
  21. package/dist/pack/gitignore.js +123 -0
  22. package/dist/pack/ignore-matcher.d.ts +19 -0
  23. package/dist/pack/ignore-matcher.d.ts.map +1 -0
  24. package/dist/pack/ignore-matcher.js +149 -0
  25. package/dist/pack/index.d.ts +11 -0
  26. package/dist/pack/index.d.ts.map +1 -0
  27. package/dist/pack/index.js +8 -0
  28. package/dist/pack/pack-project.d.ts +6 -0
  29. package/dist/pack/pack-project.d.ts.map +1 -0
  30. package/dist/pack/pack-project.js +59 -0
  31. package/dist/pack/standalone.d.ts +3 -0
  32. package/dist/pack/standalone.d.ts.map +1 -0
  33. package/dist/pack/standalone.js +95 -0
  34. package/dist/pack/tree.d.ts +5 -0
  35. package/dist/pack/tree.d.ts.map +1 -0
  36. package/dist/pack/tree.js +70 -0
  37. package/dist/pack/types.d.ts +35 -0
  38. package/dist/pack/types.d.ts.map +1 -0
  39. package/dist/pack/types.js +1 -0
  40. package/dist/pack/validate-project.d.ts +9 -0
  41. package/dist/pack/validate-project.d.ts.map +1 -0
  42. package/dist/pack/validate-project.js +44 -0
  43. package/dist/pack/zipper.d.ts +20 -0
  44. package/dist/pack/zipper.d.ts.map +1 -0
  45. package/dist/pack/zipper.js +121 -0
  46. package/dist/skills/clone.d.ts +28 -0
  47. package/dist/skills/clone.d.ts.map +1 -0
  48. package/dist/skills/clone.js +60 -0
  49. package/dist/skills/detect-agents.d.ts +8 -0
  50. package/dist/skills/detect-agents.d.ts.map +1 -0
  51. package/dist/skills/detect-agents.js +129 -0
  52. package/dist/skills/discover.d.ts +12 -0
  53. package/dist/skills/discover.d.ts.map +1 -0
  54. package/dist/skills/discover.js +45 -0
  55. package/dist/skills/index.d.ts +21 -0
  56. package/dist/skills/index.d.ts.map +1 -0
  57. package/dist/skills/index.js +97 -0
  58. package/dist/skills/installer.d.ts +21 -0
  59. package/dist/skills/installer.d.ts.map +1 -0
  60. package/dist/skills/installer.js +48 -0
  61. package/dist/skills/types.d.ts +39 -0
  62. package/dist/skills/types.d.ts.map +1 -0
  63. package/dist/skills/types.js +1 -0
  64. package/dist/skills/ui.d.ts +55 -0
  65. package/dist/skills/ui.d.ts.map +1 -0
  66. package/dist/skills/ui.js +102 -0
  67. package/package.json +6 -3
  68. package/templates/typescript-oauth/.env.example +1 -1
  69. package/templates/typescript-oauth/_gitignore +57 -0
  70. package/templates/typescript-pizzaz/.env.example +1 -1
  71. package/templates/typescript-pizzaz/_gitignore +57 -0
  72. package/templates/typescript-starter/.env.example +1 -1
  73. package/templates/typescript-starter/_gitignore +57 -0
@@ -0,0 +1,57 @@
1
+ # Dependencies
2
+ node_modules/
3
+ src/widgets/node_modules/
4
+
5
+ # Build outputs
6
+ dist/
7
+ src/widgets/.next/
8
+ src/widgets/out/
9
+
10
+ # Environment files
11
+ .env
12
+ .env.local
13
+ .env.*.local
14
+
15
+ # IDE
16
+ .idea/
17
+ .vscode/
18
+ *.swp
19
+ *.swo
20
+ *~
21
+
22
+ # OS files
23
+ .DS_Store
24
+ Thumbs.db
25
+
26
+ # Logs
27
+ *.log
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+
32
+ # Runtime data
33
+ pids/
34
+ *.pid
35
+ *.seed
36
+ *.pid.lock
37
+
38
+ # Coverage
39
+ coverage/
40
+ .nyc_output/
41
+
42
+ # Uploads
43
+ uploads/
44
+
45
+ # TypeScript cache
46
+ *.tsbuildinfo
47
+
48
+ # Optional npm cache
49
+ .npm/
50
+
51
+ # Optional eslint cache
52
+ .eslintcache
53
+
54
+ # OAuth tokens/secrets (never commit these!)
55
+ *.pem
56
+ *.key
57
+ tokens.json
@@ -3,6 +3,7 @@ interface InitOptions {
3
3
  description?: string;
4
4
  author?: string;
5
5
  skipInstall?: boolean;
6
+ force?: boolean;
6
7
  }
7
8
  export declare function initCommand(projectName: string | undefined, options: InitOptions): Promise<void>;
8
9
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAqDA,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBA+OtF"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA6IA,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBA2PtF"}
@@ -7,6 +7,81 @@ import inquirer from 'inquirer';
7
7
  import { execSync } from 'child_process';
8
8
  import { NITRO_BANNER_FULL, createSuccessBox, NitroSpinner, log, spacer, nextSteps, brand, showFooter } from '../ui/branding.js';
9
9
  import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
10
+ import { runSkillsFlow } from '../skills/index.js';
11
+ import { writeCanonicalGitignore } from '../pack/canonical-gitignore.js';
12
+ import readline from 'readline';
13
+ /**
14
+ * Prompts the user with a horizontal YES/NO choice using left/right arrow keys.
15
+ */
16
+ async function promptYesNoHorizontal(message, defaultValue = false) {
17
+ return new Promise((resolve) => {
18
+ let value = defaultValue;
19
+ const stdin = process.stdin;
20
+ const stdout = process.stdout;
21
+ // Save TTY and raw mode state
22
+ const isRaw = stdin.isRaw;
23
+ if (stdin.isTTY) {
24
+ stdin.setRawMode(true);
25
+ }
26
+ readline.emitKeypressEvents(stdin);
27
+ stdin.resume();
28
+ // Hide cursor
29
+ stdout.write('\u001B[?25l');
30
+ const render = () => {
31
+ readline.clearLine(stdout, 0);
32
+ readline.cursorTo(stdout, 0);
33
+ const qMark = chalk.cyan('?');
34
+ const msg = chalk.bold(message);
35
+ const pointer = chalk.dim('›');
36
+ const separator = chalk.dim(' / ');
37
+ const noPart = !value
38
+ ? chalk.cyan.underline('No')
39
+ : chalk.dim('No');
40
+ const yesPart = value
41
+ ? chalk.cyan.underline('Yes')
42
+ : chalk.dim('Yes');
43
+ stdout.write(`${qMark} ${msg} ${pointer} ${noPart}${separator}${yesPart}`);
44
+ };
45
+ render();
46
+ const onKeypress = (str, key) => {
47
+ if (!key)
48
+ return;
49
+ if (key.name === 'left' || key.name === 'right') {
50
+ value = !value;
51
+ render();
52
+ }
53
+ else if (key.name === 'return' || key.name === 'enter') {
54
+ cleanup();
55
+ // Clear prompt line and print final answer
56
+ readline.clearLine(stdout, 0);
57
+ readline.cursorTo(stdout, 0);
58
+ const checkMark = chalk.green('✔');
59
+ const finalAns = value ? chalk.cyan('Yes') : chalk.cyan('No');
60
+ stdout.write(`${checkMark} ${chalk.white(message)} ${finalAns}\n`);
61
+ // Resolve after a small delay to prevent keypress bleeding into the next prompt
62
+ setTimeout(() => {
63
+ resolve(value);
64
+ }, 100);
65
+ }
66
+ else if (key.ctrl && key.name === 'c') {
67
+ cleanup();
68
+ process.exit(130); // SIGINT exit code
69
+ }
70
+ };
71
+ const cleanup = () => {
72
+ // Show cursor
73
+ stdout.write('\u001B[?25h');
74
+ stdin.removeListener('keypress', onKeypress);
75
+ // Consume any data currently sitting in the stream buffer
76
+ stdin.read();
77
+ if (stdin.isTTY) {
78
+ stdin.setRawMode(isRaw);
79
+ }
80
+ stdin.pause();
81
+ };
82
+ stdin.on('keypress', onKeypress);
83
+ });
84
+ }
10
85
  // ES module equivalent of __dirname
11
86
  const __filename = fileURLToPath(import.meta.url);
12
87
  const __dirname = dirname(__filename);
@@ -69,14 +144,7 @@ export async function initCommand(projectName, options) {
69
144
  const targetDir = path.join(process.cwd(), finalProjectName);
70
145
  // Check if directory exists
71
146
  if (fs.existsSync(targetDir)) {
72
- const { overwrite } = await inquirer.prompt([
73
- {
74
- type: 'confirm',
75
- name: 'overwrite',
76
- message: chalk.yellow(`Directory ${finalProjectName} already exists. Overwrite?`),
77
- default: false,
78
- },
79
- ]);
147
+ const overwrite = await promptYesNoHorizontal(chalk.yellow(`Directory ${finalProjectName} already exists. Overwrite?`), false);
80
148
  if (!overwrite) {
81
149
  log('Cancelled', 'warning');
82
150
  process.exit(0);
@@ -99,7 +167,7 @@ export async function initCommand(projectName, options) {
99
167
  value: 'typescript-pizzaz',
100
168
  },
101
169
  {
102
- name: `${brand.signal('OAuth')} ${chalk.dim('Flight booking with OAuth 2.1 auth')}`,
170
+ name: `${brand.signal('Flight booking')} ${chalk.dim('Flight booking with OAuth 2.1 auth')}`,
103
171
  value: 'typescript-oauth',
104
172
  },
105
173
  ],
@@ -134,6 +202,11 @@ export async function initCommand(projectName, options) {
134
202
  // Copy template files
135
203
  if (fs.existsSync(templateDir)) {
136
204
  fs.copySync(templateDir, targetDir);
205
+ // Template ships `_gitignore` (npm never packs files named `.gitignore`)
206
+ const placeholder = path.join(targetDir, '_gitignore');
207
+ if (fs.existsSync(placeholder)) {
208
+ fs.removeSync(placeholder);
209
+ }
137
210
  }
138
211
  else {
139
212
  await createProjectFromScratch(targetDir, finalProjectName, {
@@ -142,6 +215,8 @@ export async function initCommand(projectName, options) {
142
215
  author: finalAuthor,
143
216
  });
144
217
  }
218
+ // Always write canonical .gitignore — npm strips `.gitignore` from published templates
219
+ await writeCanonicalGitignore(targetDir);
145
220
  // Update package.json
146
221
  const packageJsonPath = path.join(targetDir, 'package.json');
147
222
  if (fs.existsSync(packageJsonPath)) {
@@ -152,6 +227,12 @@ export async function initCommand(projectName, options) {
152
227
  fs.writeJSONSync(packageJsonPath, packageJson, { spaces: 2 });
153
228
  }
154
229
  spinner.succeed('Project created');
230
+ // Agent skills prompt bypassed - installing project-level skills by default
231
+ // const addAgentSkills = await promptYesNoHorizontal('Add agent skills?', true);
232
+ // if (addAgentSkills) {
233
+ // await runSkillsFlow(options.force ?? false, targetDir);
234
+ // }
235
+ await runSkillsFlow(options.force ?? false, targetDir);
155
236
  // Install dependencies
156
237
  if (!options.skipInstall) {
157
238
  spinner = new NitroSpinner('Installing dependencies...').start();
@@ -322,13 +403,6 @@ server.start().catch((error) => {
322
403
  `;
323
404
  fs.writeFileSync(path.join(targetDir, 'src', 'index.ts'), indexTs);
324
405
  fs.writeFileSync(path.join(targetDir, '.env'), '# Environment variables\n');
325
- const gitignore = `node_modules/
326
- dist/
327
- .env
328
- .env.local
329
- *.log
330
- `;
331
- fs.writeFileSync(path.join(targetDir, '.gitignore'), gitignore);
332
406
  const readme = `# ${projectName}
333
407
 
334
408
  ${answers.description}
@@ -0,0 +1,10 @@
1
+ interface PackCommandOptions {
2
+ output?: string;
3
+ dryRun?: boolean;
4
+ syncGitignore?: boolean;
5
+ includeEnv?: boolean;
6
+ cwd?: string;
7
+ }
8
+ export declare function packCommand(options: PackCommandOptions): Promise<void>;
9
+ export {};
10
+ //# sourceMappingURL=pack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../../src/commands/pack.ts"],"names":[],"mappings":"AAiBA,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA2CD,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D5E"}
@@ -0,0 +1,82 @@
1
+ import chalk from 'chalk';
2
+ import path from 'path';
3
+ import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, spacer, brand, NITRO_BANNER_FULL, showFooter, } from '../ui/branding.js';
4
+ import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
5
+ import { packProject } from '../pack/pack-project.js';
6
+ import { PackValidationError } from '../pack/validate-project.js';
7
+ function formatBytes(bytes) {
8
+ if (bytes < 1024)
9
+ return `${bytes} B`;
10
+ if (bytes < 1024 * 1024)
11
+ return `${(bytes / 1024).toFixed(1)} KB`;
12
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
13
+ }
14
+ function buildSummaryItems(result) {
15
+ const items = [
16
+ `${brand.skyBold(String(result.filesIncluded))} file(s) included`,
17
+ ];
18
+ if (result.outputPath) {
19
+ items.push(`Output: ${brand.sky(result.outputPath)}`);
20
+ }
21
+ if (result.zipSizeBytes !== null) {
22
+ items.push(`Size: ${brand.skyBold(formatBytes(result.zipSizeBytes))}`);
23
+ }
24
+ if (result.gitignoreUpdated) {
25
+ items.push(`Added ${result.addedGitignoreRules.length} .gitignore rule(s)`);
26
+ }
27
+ return items;
28
+ }
29
+ function printExcludedCategories(result) {
30
+ console.log(chalk.bold('\nExcluded from zip:'));
31
+ for (const category of result.excludedCategories) {
32
+ console.log(` ${chalk.cyan(category.category)}: ${category.paths.join(', ')}`);
33
+ }
34
+ spacer();
35
+ }
36
+ function printDryRunTree(result) {
37
+ if (!result.dryRunTree)
38
+ return;
39
+ console.log(chalk.bold('File tree:'));
40
+ console.log(result.dryRunTree);
41
+ spacer();
42
+ }
43
+ export async function packCommand(options) {
44
+ console.log(NITRO_BANNER_FULL);
45
+ console.log(createHeader('Pack', 'Create optimized project archive'));
46
+ trackEvent('cli_command_invoked', {
47
+ command: 'pack',
48
+ options: Object.keys(options).filter((key) => options[key] !== undefined),
49
+ });
50
+ const packOptions = {
51
+ output: options.output,
52
+ dryRun: options.dryRun,
53
+ syncGitignore: options.syncGitignore ?? true,
54
+ includeEnv: options.includeEnv ?? false,
55
+ cwd: path.resolve(options.cwd ?? process.cwd()),
56
+ };
57
+ const spinner = new NitroSpinner(packOptions.dryRun ? 'Analyzing project files...' : 'Creating optimized zip...').start();
58
+ try {
59
+ const result = await packProject(packOptions);
60
+ spinner.succeed(packOptions.dryRun
61
+ ? 'Dry run complete'
62
+ : `Created ${path.basename(result.outputPath ?? 'archive')}`);
63
+ printExcludedCategories(result);
64
+ if (packOptions.dryRun) {
65
+ printDryRunTree(result);
66
+ }
67
+ console.log(createSuccessBox(packOptions.dryRun ? 'Dry Run Complete' : 'Project Packed', buildSummaryItems(result)));
68
+ showFooter();
69
+ await shutdownAnalytics();
70
+ }
71
+ catch (error) {
72
+ spinner.fail('Pack failed');
73
+ if (error instanceof PackValidationError) {
74
+ console.log(createErrorBox('Pack Failed', error.message));
75
+ await shutdownAnalytics();
76
+ process.exit(1);
77
+ }
78
+ console.log(createErrorBox('Pack Failed', error instanceof Error ? error.message : String(error)));
79
+ await shutdownAnalytics();
80
+ process.exit(1);
81
+ }
82
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AA2BA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAUD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgD1E;AA2BD;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAa9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAM1D;AAqED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsI3E"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAmCA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAUD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgD1E;AA2BD;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAa9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAM1D;AAqED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmM3E"}
@@ -5,6 +5,10 @@ import fs from 'fs-extra';
5
5
  import https from 'https';
6
6
  import { createHeader, createBox, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
7
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';
8
12
  /**
9
13
  * Fetch a package's latest published version from NPM using standard https module.
10
14
  */
@@ -236,6 +240,68 @@ export async function upgradeCommand(options) {
236
240
  console.error(error);
237
241
  }
238
242
  }
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
+ }
286
+ }
287
+ else {
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
+ }
303
+ }
304
+ }
239
305
  // Summary
240
306
  spacer();
241
307
  if (allResults.length === 0) {
package/dist/index.d.ts CHANGED
@@ -9,4 +9,6 @@ export { generate } from './commands/generate.js';
9
9
  export { upgradeCommand } from './commands/upgrade.js';
10
10
  export { installCommand } from './commands/install.js';
11
11
  export { cursorCommand } from './commands/cursor.js';
12
+ export { packCommand } from './commands/pack.js';
13
+ export { packProject } from './pack/pack-project.js';
12
14
  //# 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;AAcpC,wBAAgB,aAAa,YA4E5B;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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,wBAAgB,aAAa,YAuF5B;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;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { generate } from './commands/generate.js';
9
9
  import { upgradeCommand } from './commands/upgrade.js';
10
10
  import { installCommand } from './commands/install.js';
11
11
  import { cursorCommand } from './commands/cursor.js';
12
+ import { packCommand } from './commands/pack.js';
12
13
  const require = createRequire(import.meta.url);
13
14
  const packageJson = require('../package.json');
14
15
  export function createProgram() {
@@ -25,6 +26,7 @@ export function createProgram() {
25
26
  .option('--description <description>', 'Description of the project')
26
27
  .option('--author <author>', 'Author of the project')
27
28
  .option('--skip-install', 'Skip installing dependencies')
29
+ .option('--force', 'Overwrite existing skill files when adding agent skills')
28
30
  .action(initCommand);
29
31
  program
30
32
  .command('dev')
@@ -76,6 +78,15 @@ export function createProgram() {
76
78
  .option('-p, --port <port>', 'Port for default HTTP URL (for legacy-sse or streamable-http)')
77
79
  .option('--force', 'Force overwrite of existing configuration')
78
80
  .action(cursorCommand);
81
+ program
82
+ .command('pack')
83
+ .description('Create an optimized zip of the project (excludes build artifacts)')
84
+ .option('-o, --output <path>', 'Zip filename (always written to the project root)')
85
+ .option('--dry-run', 'Show excluded/included summary without creating zip')
86
+ .option('--include-env', 'Include .env files in the zip')
87
+ .option('--no-sync-gitignore', 'Skip merging canonical rules into local .gitignore')
88
+ .option('--cwd <dir>', 'Project directory', process.cwd())
89
+ .action(packCommand);
79
90
  return program;
80
91
  }
81
92
  // Re-export commands for programmatic use
@@ -87,6 +98,8 @@ export { generate } from './commands/generate.js';
87
98
  export { upgradeCommand } from './commands/upgrade.js';
88
99
  export { installCommand } from './commands/install.js';
89
100
  export { cursorCommand } from './commands/cursor.js';
101
+ export { packCommand } from './commands/pack.js';
102
+ export { packProject } from './pack/pack-project.js';
90
103
  // Run the CLI when this module is the entry point
91
104
  import { fileURLToPath } from 'url';
92
105
  import { realpathSync } from 'fs';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve the bundled canonical .gitignore shipped with the CLI package.
3
+ */
4
+ export declare function getCanonicalGitignorePath(): string;
5
+ /**
6
+ * Load canonical .gitignore rules from the bundled starter template copy.
7
+ */
8
+ export declare function loadCanonicalGitignore(): Promise<string>;
9
+ /**
10
+ * Write the bundled canonical .gitignore into a project root as `.gitignore`.
11
+ * Used by init because npm never ships files named `.gitignore` in the package tarball.
12
+ */
13
+ export declare function writeCanonicalGitignore(projectRoot: string): Promise<void>;
14
+ //# sourceMappingURL=canonical-gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-gitignore.d.ts","sourceRoot":"","sources":["../../src/pack/canonical-gitignore.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAM9D;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhF"}
@@ -0,0 +1,28 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import fs from 'fs-extra';
4
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
5
+ /**
6
+ * Resolve the bundled canonical .gitignore shipped with the CLI package.
7
+ */
8
+ export function getCanonicalGitignorePath() {
9
+ return path.join(moduleDir, '../../assets/canonical.gitignore');
10
+ }
11
+ /**
12
+ * Load canonical .gitignore rules from the bundled starter template copy.
13
+ */
14
+ export async function loadCanonicalGitignore() {
15
+ const canonicalPath = getCanonicalGitignorePath();
16
+ if (!(await fs.pathExists(canonicalPath))) {
17
+ throw new Error(`Canonical .gitignore not found at ${canonicalPath}`);
18
+ }
19
+ return fs.readFile(canonicalPath, 'utf-8');
20
+ }
21
+ /**
22
+ * Write the bundled canonical .gitignore into a project root as `.gitignore`.
23
+ * Used by init because npm never ships files named `.gitignore` in the package tarball.
24
+ */
25
+ export async function writeCanonicalGitignore(projectRoot) {
26
+ const content = await loadCanonicalGitignore();
27
+ await fs.writeFile(path.join(projectRoot, '.gitignore'), content, 'utf-8');
28
+ }
@@ -0,0 +1,8 @@
1
+ import type { ExclusionCategory } from './types.js';
2
+ /** Env/secret patterns excluded by default; skipped when --include-env is set. */
3
+ export declare const ENV_EXCLUDED_PATTERNS: string[];
4
+ /** Hard-coded exclusions always applied in addition to merged .gitignore rules. */
5
+ export declare const HARD_EXCLUDED_PATTERNS: string[];
6
+ export declare const EXCLUSION_CATEGORIES: ExclusionCategory[];
7
+ export declare function getExclusionReport(includeEnv?: boolean): ExclusionCategory[];
8
+ //# sourceMappingURL=exclusions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exclusions.d.ts","sourceRoot":"","sources":["../../src/pack/exclusions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,EAAE,MAAM,EAIzC,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,EAAE,MAAM,EA0B1C,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAyCnD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,UAAU,GAAE,OAAe,GAAG,iBAAiB,EAAE,CAOnF"}
@@ -0,0 +1,84 @@
1
+ /** Env/secret patterns excluded by default; skipped when --include-env is set. */
2
+ export const ENV_EXCLUDED_PATTERNS = [
3
+ '.env',
4
+ '.env.local',
5
+ '.env.*.local',
6
+ ];
7
+ /** Hard-coded exclusions always applied in addition to merged .gitignore rules. */
8
+ export const HARD_EXCLUDED_PATTERNS = [
9
+ '.git/',
10
+ '**/node_modules/',
11
+ '**/dist/',
12
+ '**/build/',
13
+ '**/out/',
14
+ '**/.next/',
15
+ '*.tsbuildinfo',
16
+ '.npm/',
17
+ '.eslintcache',
18
+ 'coverage/',
19
+ '.nyc_output/',
20
+ 'tmp/',
21
+ 'temp/',
22
+ 'pids/',
23
+ 'uploads/',
24
+ ...ENV_EXCLUDED_PATTERNS,
25
+ '.idea/',
26
+ '.vscode/',
27
+ '.DS_Store',
28
+ 'Thumbs.db',
29
+ '*.log',
30
+ 'npm-debug.log*',
31
+ 'yarn-debug.log*',
32
+ 'yarn-error.log*',
33
+ '*.zip',
34
+ ];
35
+ export const EXCLUSION_CATEGORIES = [
36
+ {
37
+ category: 'VCS',
38
+ paths: ['.git/'],
39
+ },
40
+ {
41
+ category: 'Dependencies',
42
+ paths: ['**/node_modules/'],
43
+ },
44
+ {
45
+ category: 'Build outputs',
46
+ paths: ['**/dist/', '**/build/', '**/out/', '**/.next/'],
47
+ },
48
+ {
49
+ category: 'Caches',
50
+ paths: ['*.tsbuildinfo', '.npm/', '.eslintcache'],
51
+ },
52
+ {
53
+ category: 'Test',
54
+ paths: ['coverage/', '.nyc_output/'],
55
+ },
56
+ {
57
+ category: 'Temp/runtime',
58
+ paths: ['tmp/', 'temp/', 'pids/', 'uploads/'],
59
+ },
60
+ {
61
+ category: 'Secrets',
62
+ paths: [...ENV_EXCLUDED_PATTERNS],
63
+ },
64
+ {
65
+ category: 'IDE/OS',
66
+ paths: ['.idea/', '.vscode/', '.DS_Store', 'Thumbs.db'],
67
+ },
68
+ {
69
+ category: 'Logs',
70
+ paths: ['*.log', 'npm-debug.log*', 'yarn-debug.log*', 'yarn-error.log*'],
71
+ },
72
+ {
73
+ category: 'Archives',
74
+ paths: ['*.zip'],
75
+ },
76
+ ];
77
+ export function getExclusionReport(includeEnv = false) {
78
+ return EXCLUSION_CATEGORIES
79
+ .filter((entry) => !(includeEnv && entry.category === 'Secrets'))
80
+ .map((entry) => ({
81
+ category: entry.category,
82
+ paths: [...entry.paths],
83
+ }));
84
+ }
@@ -0,0 +1,21 @@
1
+ import { type IgnoreMatcher } from './ignore-matcher.js';
2
+ import type { GitignoreMergeResult } from './types.js';
3
+ export interface BuildIgnoreMatcherOptions {
4
+ includeEnv?: boolean;
5
+ }
6
+ export type { IgnoreMatcher };
7
+ /**
8
+ * Non-destructively merge canonical rules into a local .gitignore.
9
+ * Existing local rules are preserved; only missing canonical rules are appended.
10
+ */
11
+ export declare function mergeGitignoreRules(localContent: string, canonicalContent: string): GitignoreMergeResult;
12
+ /**
13
+ * Sync local .gitignore with canonical rules when enabled.
14
+ */
15
+ export declare function syncProjectGitignore(projectRoot: string, syncGitignore: boolean): Promise<GitignoreMergeResult>;
16
+ /**
17
+ * Build a gitignore matcher from merged project rules and hard-coded exclusions.
18
+ */
19
+ export declare function buildIgnoreMatcher(mergedGitignoreContent: string, options?: BuildIgnoreMatcherOptions): IgnoreMatcher;
20
+ export declare function isPathIgnored(matcher: IgnoreMatcher, projectRoot: string, absolutePath: string, isDirectory?: boolean): boolean;
21
+ //# sourceMappingURL=gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../src/pack/gitignore.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIvD,MAAM,WAAW,yBAAyB;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,YAAY,EAAE,aAAa,EAAE,CAAC;AA2B9B;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,GACvB,oBAAoB,CAuCtB;AAQD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC,oBAAoB,CAAC,CAoB/B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,sBAAsB,EAAE,MAAM,EAC9B,OAAO,GAAE,yBAA8B,GACtC,aAAa,CAyBf;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,WAAW,GAAE,OAAe,GAC3B,OAAO,CAMT"}