@tsfpp/agents 1.2.1 → 1.2.3
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 +25 -0
- package/copilot/agents/tsfpp-audit.agent.md +12 -6
- package/init.mjs +59 -30
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,31 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [1.2.3] - 2026-05-16
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added `react` and `data` as supported focus values for `copilot/agents/tsfpp-audit.agent.md`.
|
|
18
|
+
- Added Data profile overlay support via `node_modules/@tsfpp/standard/spec/DATA_CODING_STANDARD.md`.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Updated TSF++ standard reference paths in `copilot/agents/tsfpp-audit.agent.md` to `node_modules/@tsfpp/standard/spec/*.md`.
|
|
23
|
+
- Updated audit startup prompt and argument hints to include React and Data focused targets.
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
- Removed checklist rule `8.x` (prelude ADT/helper reuse enforcement) from the base audit template.
|
|
28
|
+
- Simplified checklist rule `9.x` from broad dependency hygiene checks to a direct `ramda` import guard.
|
|
29
|
+
|
|
30
|
+
## [1.2.2] - 2026-05-16
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Updated `init.mjs` questionnaire flow to avoid overwriting existing `tsconfig` and ESLint config files.
|
|
35
|
+
- Added `N`/skip escape hatch for existing-file prompts.
|
|
36
|
+
- Added `SIGINT` handling by wrapping execution in `main` so Ctrl+C exits cleanly without hanging awaits.
|
|
37
|
+
|
|
13
38
|
## [1.2.1] - 2026-05-16
|
|
14
39
|
|
|
15
40
|
### Fixed
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: TSF++ standards compliance auditor. Produces a structured markdown report in docs/audits/ with per-slice checkboxes.
|
|
3
3
|
name: tsfpp-audit
|
|
4
|
-
argument-hint: "target=<path|package|layer> focus=<all|types|boundary|complexity|loc|annotations|security>"
|
|
4
|
+
argument-hint: "target=<path|package|layer> focus=<all|types|boundary|complexity|loc|annotations|security|react|data>"
|
|
5
5
|
tools:
|
|
6
6
|
- edit/createFile
|
|
7
7
|
- edit/editFiles
|
|
@@ -29,6 +29,7 @@ The canonical standard is at `node_modules/@tsfpp/standard/spec/CODING_STANDARD.
|
|
|
29
29
|
Profile overlays:
|
|
30
30
|
- API: `node_modules/@tsfpp/standard/spec/API_CODING_STANDARD.md`
|
|
31
31
|
- React: `node_modules/@tsfpp/standard/spec/REACT_CODING_STANDARD.md`
|
|
32
|
+
- Data: `node_modules/@tsfpp/standard/spec/DATA_CODING_STANDARD.md`
|
|
32
33
|
- Security: `node_modules/@tsfpp/standard/spec/SECURITY_CODING_STANDARD.md`
|
|
33
34
|
|
|
34
35
|
If any referenced file is missing, stop immediately and report the path. Do not proceed.
|
|
@@ -42,7 +43,7 @@ If any referenced file is missing, stop immediately and report the path. Do not
|
|
|
42
43
|
If the user has not provided both `target` and `focus`, ask exactly this:
|
|
43
44
|
|
|
44
45
|
> **Target** — path, package name, or layer to audit (e.g. `src/domain`, `@tsfpp/prelude`, `api layer`)?
|
|
45
|
-
> **Focus** — `all` · `types` · `boundary` · `complexity` · `loc` · `annotations` · `security` · or comma-separated combination?
|
|
46
|
+
> **Focus** — `all` · `types` · `boundary` · `complexity` · `loc` · `annotations` · `security` · `react` · `data` · or comma-separated combination?
|
|
46
47
|
|
|
47
48
|
Do not proceed until both are confirmed.
|
|
48
49
|
|
|
@@ -132,8 +133,7 @@ Append each completed slice to the report:
|
|
|
132
133
|
- [x] 5.1 — Pipelines via `pipe` from prelude
|
|
133
134
|
- [x] 6.x — No `throw` in core
|
|
134
135
|
- [x] 7.x — JSDoc on all exports
|
|
135
|
-
- [x]
|
|
136
|
-
- [x] 9.x — Dependency hygiene (no deprecated dependencies, no banned imports per policy, no layer-violating imports)
|
|
136
|
+
- [x] 9.x — No direct `ramda` import
|
|
137
137
|
|
|
138
138
|
#### Deviation register
|
|
139
139
|
|
|
@@ -147,10 +147,10 @@ Append each completed slice to the report:
|
|
|
147
147
|
## Focus-specific rule sets
|
|
148
148
|
|
|
149
149
|
### `types`
|
|
150
|
-
1.4 (no bare interface) · 1.5 (no `any`) · 1.6 (no `!` or `as`) · 3.x (readonly) · branded types on domain primitives · smart constructor completeness · exhaustive sum-type dispatch
|
|
150
|
+
1.4 (no bare interface) · 1.5 (no `any`) · 1.6 (no `!` or `as`) · 3.x (readonly) · branded types on domain primitives · smart constructor completeness · exhaustive sum-type dispatch
|
|
151
151
|
|
|
152
152
|
### `boundary`
|
|
153
|
-
API_CODING_STANDARD.md Rules 1–5 · Zod schema completeness · Result/Option at I/O · `extractContext` usage · `apiErrorToResponse` coverage · no raw `throw` across boundaries · `@tsfpp/boundary` response builders used
|
|
153
|
+
API_CODING_STANDARD.md Rules 1–5 · Zod schema completeness · Result/Option at I/O · `extractContext` usage · `apiErrorToResponse` coverage · no raw `throw` across boundaries · `@tsfpp/boundary` response builders used
|
|
154
154
|
|
|
155
155
|
### `complexity`
|
|
156
156
|
Function body ≤ 40 lines · cyclomatic complexity ≤ 10 · nesting ≤ 4 · arity ≤ 3 positional params · pipeline depth ≤ 8 stages
|
|
@@ -164,6 +164,12 @@ JSDoc on every export · `@param` + `@returns` present · `@law` on combinators
|
|
|
164
164
|
### `security`
|
|
165
165
|
SECURITY_CODING_STANDARD.md: input validation at boundaries · no secrets in code · no sensitive data in errors · auth/authz at correct layer · dependency hygiene
|
|
166
166
|
|
|
167
|
+
### `react`
|
|
168
|
+
REACT_CODING_STANDARD.md: component shape and explicit return types · readonly props contracts · state model quality · effect discipline (`useEffect` only for external sync) · server state via TanStack Query · form and routing standards
|
|
169
|
+
|
|
170
|
+
### `data`
|
|
171
|
+
DATA_CODING_STANDARD.md: schema-first design · migration safety and reversibility · repository/query boundaries · transaction discipline · index/key correctness · deterministic data transformation and serialization at boundaries
|
|
172
|
+
|
|
167
173
|
### `all`
|
|
168
174
|
All focus areas above in sequence.
|
|
169
175
|
|
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,17 +253,21 @@ 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
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
|
-
|
|
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
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
} else {
|
|
351
|
-
|
|
352
|
-
|
|
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();
|