@tsfpp/agents 1.2.0 → 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.
- package/CHANGELOG.md +14 -0
- package/init.mjs +59 -47
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,20 @@ 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
|
+
|
|
21
|
+
## [1.2.1] - 2026-05-16
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Fixed an `init.mjs` idempotency bug in ESLint config declaration handling.
|
|
26
|
+
|
|
13
27
|
## [1.2.0] - 2026-05-16
|
|
14
28
|
|
|
15
29
|
### Added
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
-
|
|
203
|
-
console.log(
|
|
204
|
-
|
|
209
|
+
process.on('SIGINT', () => {
|
|
210
|
+
console.log('\n\n Aborted.\n');
|
|
211
|
+
process.exit(0);
|
|
212
|
+
});
|
|
205
213
|
|
|
206
|
-
|
|
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,45 +253,32 @@ console.log();
|
|
|
240
253
|
const eslintDest = join(cwd, 'eslint.config.js');
|
|
241
254
|
|
|
242
255
|
if (existsSync(eslintDest)) {
|
|
243
|
-
|
|
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
|
|
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
|
-
async function writeEslintConfig() {
|
|
257
|
-
console.log(` Which ESLint profile does this project use?`);
|
|
258
|
-
console.log(` ${dim('1')} base ${dim('— TypeScript / Node.js')}`);
|
|
259
|
-
console.log(` ${dim('2')} react ${dim('— React / TSX')}`);
|
|
260
|
-
console.log(` ${dim('3')} api ${dim('— HTTP API / Node.js servers')}`);
|
|
261
273
|
|
|
262
|
-
const choice = await ask(` ${dim('[1/2/3, default: 1]')} `);
|
|
263
|
-
const profile = choice === '2' ? 'react' : choice === '3' ? 'api' : 'base';
|
|
264
|
-
|
|
265
|
-
try {
|
|
266
|
-
await writeFile(eslintDest, ESLINT_PROFILES[profile], 'utf8');
|
|
267
|
-
results.copied.push('eslint.config.js');
|
|
268
|
-
console.log(` ${green('✓')} eslint.config.js ${dim(`(profile: ${profile})`)}`);
|
|
269
|
-
} catch (err) {
|
|
270
|
-
results.failed.push('eslint.config.js');
|
|
271
|
-
console.log(` \x1b[31m✗\x1b[0m eslint.config.js ${dim(`(${err.message})`)}`);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
274
|
|
|
275
275
|
// ── Generate tsconfig.json ────────────────────────────────────────────────────
|
|
276
276
|
|
|
277
277
|
console.log();
|
|
278
278
|
|
|
279
|
-
await writeTsConfigs(await detectWorkspacePackages());
|
|
279
|
+
await writeTsConfigs(await detectWorkspacePackages(), results);
|
|
280
280
|
|
|
281
|
-
async function writeTsConfigs(packages) {
|
|
281
|
+
async function writeTsConfigs(packages, results) {
|
|
282
282
|
const PRESETS = {
|
|
283
283
|
app: { extends: '@tsfpp/tsconfig/app', label: 'app — application / tool (noEmit)' },
|
|
284
284
|
lib: { extends: '@tsfpp/tsconfig/lib', label: 'lib — publishable package (declaration, composite)' },
|
|
@@ -288,7 +288,9 @@ async function writeTsConfigs(packages) {
|
|
|
288
288
|
console.log(`\n tsconfig preset for ${bold(label)}:`);
|
|
289
289
|
console.log(` ${dim('1')} app ${dim('— application / tool (noEmit: true)')}`);
|
|
290
290
|
console.log(` ${dim('2')} lib ${dim('— publishable package (declaration, composite)')}`);
|
|
291
|
-
|
|
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;
|
|
292
294
|
return choice === '2' ? 'lib' : 'app';
|
|
293
295
|
}
|
|
294
296
|
|
|
@@ -309,6 +311,11 @@ async function writeTsConfigs(packages) {
|
|
|
309
311
|
|
|
310
312
|
async function writeIfConfirmed(destPath, content, label) {
|
|
311
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
|
+
}
|
|
312
319
|
const overwrite = await confirm(
|
|
313
320
|
` ${yellow('!')} ${label} already exists. Overwrite? ${dim('[y/N]')} `
|
|
314
321
|
);
|
|
@@ -338,6 +345,7 @@ async function writeTsConfigs(packages) {
|
|
|
338
345
|
}
|
|
339
346
|
|
|
340
347
|
for (const [pkg, preset] of Object.entries(packagePresets)) {
|
|
348
|
+
if (preset === null) continue;
|
|
341
349
|
const destPath = join(cwd, pkg, 'tsconfig.json');
|
|
342
350
|
const content = generateTsConfig(preset);
|
|
343
351
|
await writeIfConfirmed(destPath, content, `${pkg}/tsconfig.json`);
|
|
@@ -349,22 +357,26 @@ async function writeTsConfigs(packages) {
|
|
|
349
357
|
await writeIfConfirmed(rootDest, rootContent, 'tsconfig.json (root references)');
|
|
350
358
|
} else {
|
|
351
359
|
// Single package
|
|
352
|
-
const preset
|
|
360
|
+
const preset = await askPreset('this project');
|
|
361
|
+
if (preset === null) return;
|
|
353
362
|
const dest = join(cwd, 'tsconfig.json');
|
|
354
363
|
const content = generateTsConfig(preset);
|
|
355
364
|
await writeIfConfirmed(dest, content, 'tsconfig.json');
|
|
356
365
|
}
|
|
357
366
|
}
|
|
358
367
|
|
|
359
|
-
console.log();
|
|
360
|
-
console.log(dim(' ─────────────────────────────────────────'));
|
|
361
|
-
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')}`);
|
|
362
|
-
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();
|
|
363
372
|
|
|
364
|
-
if (results.failed.length === 0) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
} else {
|
|
368
|
-
|
|
369
|
-
|
|
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
|
+
}
|
|
370
380
|
}
|
|
381
|
+
|
|
382
|
+
main();
|