@tsfpp/agents 1.2.1 → 1.2.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/init.mjs +59 -30
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,14 @@ Versioning follows [Semantic Versioning](https://semver.org/).
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [1.2.2] - 2026-05-16
14
+
15
+ ### Fixed
16
+
17
+ - Updated `init.mjs` questionnaire flow to avoid overwriting existing `tsconfig` and ESLint config files.
18
+ - Added `N`/skip escape hatch for existing-file prompts.
19
+ - Added `SIGINT` handling by wrapping execution in `main` so Ctrl+C exits cleanly without hanging awaits.
20
+
13
21
  ## [1.2.1] - 2026-05-16
14
22
 
15
23
  ### Fixed
package/init.mjs CHANGED
@@ -99,7 +99,9 @@ async function askProfile(label) {
99
99
  console.log(` ${dim('1')} base ${dim('— TypeScript / Node.js')}`);
100
100
  console.log(` ${dim('2')} react ${dim('— React / TSX')}`);
101
101
  console.log(` ${dim('3')} api ${dim('— HTTP API / Node.js servers')}`);
102
- const choice = await ask(` ${dim('[1/2/3, default: 1]')} `);
102
+ console.log(` ${dim('n')} skip ${dim('— keep existing / do not generate')}`);
103
+ const choice = await ask(` ${dim('[1/2/3/n, default: 1]')} `);
104
+ if (choice === 'n') return null;
103
105
  return choice === '2' ? 'react' : choice === '3' ? 'api' : 'base';
104
106
  }
105
107
 
@@ -146,7 +148,7 @@ function generateSingleConfig(profile) {
146
148
  return `${imp}\nexport default [...${spread}]\n`;
147
149
  }
148
150
 
149
- async function writeEslintConfig() {
151
+ async function writeEslintConfig(results) {
150
152
  const packages = await detectWorkspacePackages();
151
153
 
152
154
  let content;
@@ -158,12 +160,17 @@ async function writeEslintConfig() {
158
160
  for (const pkg of packages) {
159
161
  packageProfiles[pkg] = await askProfile(pkg);
160
162
  }
161
- content = generateMonorepoConfig(packageProfiles);
163
+ const activeProfiles = Object.fromEntries(
164
+ Object.entries(packageProfiles).filter(([, p]) => p !== null)
165
+ );
166
+ if (Object.keys(activeProfiles).length === 0) return;
167
+ content = generateMonorepoConfig(activeProfiles);
162
168
  description = 'monorepo';
163
169
  } else {
164
170
  const profile = await askProfile('this project');
165
- content = generateSingleConfig(profile);
166
- description = `profile: ${profile}`;
171
+ if (profile === null) return;
172
+ content = generateSingleConfig(profile);
173
+ description = `profile: ${profile}`;
167
174
  }
168
175
 
169
176
  try {
@@ -199,11 +206,17 @@ async function confirm(question) {
199
206
 
200
207
  // ─── Main ─────────────────────────────────────────────────────────────────────
201
208
 
202
- console.log();
203
- console.log(bold(' @tsfpp/agents — init'));
204
- console.log(dim(' Sets up Copilot agents, instructions, prompts, skills, and ESLint config.\n'));
209
+ process.on('SIGINT', () => {
210
+ console.log('\n\n Aborted.\n');
211
+ process.exit(0);
212
+ });
205
213
 
206
- const results = { copied: [], skipped: [], failed: [] };
214
+ async function main() {
215
+ console.log();
216
+ console.log(bold(' @tsfpp/agents — init'));
217
+ console.log(dim(' Sets up Copilot agents, instructions, prompts, skills, and ESLint config.\n'));
218
+
219
+ const results = { copied: [], skipped: [], failed: [] };
207
220
 
208
221
  // ── Copy files ────────────────────────────────────────────────────────────────
209
222
 
@@ -240,17 +253,21 @@ console.log();
240
253
  const eslintDest = join(cwd, 'eslint.config.js');
241
254
 
242
255
  if (existsSync(eslintDest)) {
243
- const overwrite = await confirm(
244
- ` ${yellow('!')} eslint.config.js already exists. Overwrite? ${dim('[y/N]')} `
245
- );
246
- if (!overwrite) {
256
+ if (yes) {
247
257
  results.skipped.push('eslint.config.js');
248
- console.log(` ${dim('–')} ${dim('eslint.config.js')} ${dim('(skipped)')}`);
258
+ console.log(` ${dim('–')} ${dim('eslint.config.js')} ${dim('(skipped — project-managed)')}`);
249
259
  } else {
250
- await writeEslintConfig();
260
+ const overwrite = await confirm(
261
+ ` ${yellow('!')} eslint.config.js already exists. Overwrite? ${dim('[y/N]')} `
262
+ );
263
+ if (overwrite) await writeEslintConfig(results);
264
+ else {
265
+ results.skipped.push('eslint.config.js');
266
+ console.log(` ${dim('–')} ${dim('eslint.config.js')} ${dim('(skipped)')}`);
267
+ }
251
268
  }
252
269
  } else {
253
- await writeEslintConfig();
270
+ await writeEslintConfig(results);
254
271
  }
255
272
 
256
273
 
@@ -259,9 +276,9 @@ if (existsSync(eslintDest)) {
259
276
 
260
277
  console.log();
261
278
 
262
- await writeTsConfigs(await detectWorkspacePackages());
279
+ await writeTsConfigs(await detectWorkspacePackages(), results);
263
280
 
264
- async function writeTsConfigs(packages) {
281
+ async function writeTsConfigs(packages, results) {
265
282
  const PRESETS = {
266
283
  app: { extends: '@tsfpp/tsconfig/app', label: 'app — application / tool (noEmit)' },
267
284
  lib: { extends: '@tsfpp/tsconfig/lib', label: 'lib — publishable package (declaration, composite)' },
@@ -271,7 +288,9 @@ async function writeTsConfigs(packages) {
271
288
  console.log(`\n tsconfig preset for ${bold(label)}:`);
272
289
  console.log(` ${dim('1')} app ${dim('— application / tool (noEmit: true)')}`);
273
290
  console.log(` ${dim('2')} lib ${dim('— publishable package (declaration, composite)')}`);
274
- const choice = await ask(` ${dim('[1/2, default: 1]')} `);
291
+ console.log(` ${dim('n')} skip ${dim('— keep existing / do not generate')}`);
292
+ const choice = await ask(` ${dim('[1/2/n, default: 1]')} `);
293
+ if (choice === 'n') return null;
275
294
  return choice === '2' ? 'lib' : 'app';
276
295
  }
277
296
 
@@ -292,6 +311,11 @@ async function writeTsConfigs(packages) {
292
311
 
293
312
  async function writeIfConfirmed(destPath, content, label) {
294
313
  if (existsSync(destPath)) {
314
+ if (yes) {
315
+ results.skipped.push(label);
316
+ console.log(` ${dim('–')} ${dim(label)} ${dim('(skipped — project-managed)')}`);
317
+ return;
318
+ }
295
319
  const overwrite = await confirm(
296
320
  ` ${yellow('!')} ${label} already exists. Overwrite? ${dim('[y/N]')} `
297
321
  );
@@ -321,6 +345,7 @@ async function writeTsConfigs(packages) {
321
345
  }
322
346
 
323
347
  for (const [pkg, preset] of Object.entries(packagePresets)) {
348
+ if (preset === null) continue;
324
349
  const destPath = join(cwd, pkg, 'tsconfig.json');
325
350
  const content = generateTsConfig(preset);
326
351
  await writeIfConfirmed(destPath, content, `${pkg}/tsconfig.json`);
@@ -332,22 +357,26 @@ async function writeTsConfigs(packages) {
332
357
  await writeIfConfirmed(rootDest, rootContent, 'tsconfig.json (root references)');
333
358
  } else {
334
359
  // Single package
335
- const preset = await askPreset('this project');
360
+ const preset = await askPreset('this project');
361
+ if (preset === null) return;
336
362
  const dest = join(cwd, 'tsconfig.json');
337
363
  const content = generateTsConfig(preset);
338
364
  await writeIfConfirmed(dest, content, 'tsconfig.json');
339
365
  }
340
366
  }
341
367
 
342
- console.log();
343
- console.log(dim(' ─────────────────────────────────────────'));
344
- console.log(` ${green(results.copied.length + ' copied')} ${yellow(results.skipped.length + ' skipped')} ${results.failed.length > 0 ? `\x1b[31m${results.failed.length} failed\x1b[0m` : dim('0 failed')}`);
345
- console.log();
368
+ console.log();
369
+ console.log(dim(' ─────────────────────────────────────────'));
370
+ console.log(` ${green(results.copied.length + ' copied')} ${yellow(results.skipped.length + ' skipped')} ${results.failed.length > 0 ? `\x1b[31m${results.failed.length} failed\x1b[0m` : dim('0 failed')}`);
371
+ console.log();
346
372
 
347
- if (results.failed.length === 0) {
348
- console.log(' ' + bold('Done.') + ' Reload VS Code to activate Copilot instructions.');
349
- console.log(dim(' Commit the generated files — they are workspace configuration.\n'));
350
- } else {
351
- console.log(' Some files could not be copied. Check the errors above.\n');
352
- process.exit(1);
373
+ if (results.failed.length === 0) {
374
+ console.log(' ' + bold('Done.') + ' Reload VS Code to activate Copilot instructions.');
375
+ console.log(dim(' Commit the generated files — they are workspace configuration.\n'));
376
+ } else {
377
+ console.log(' Some files could not be copied. Check the errors above.\n');
378
+ process.exit(1);
379
+ }
353
380
  }
381
+
382
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsfpp/agents",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Workspace AI tooling for TSF++ projects: scoped instructions, coding agents, and reusable prompts",
5
5
  "keywords": [
6
6
  "tsfpp",