@tsfpp/agents 1.0.0

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.
@@ -0,0 +1,141 @@
1
+ ---
2
+ applyTo: "**/*.ts"
3
+ ---
4
+
5
+ # TSF++ prelude API
6
+
7
+ Full reference: `node_modules/@tsfpp/prelude/README.md`
8
+ Recipes: `node_modules/@tsfpp/prelude/RECIPES.md`
9
+
10
+ ## Import
11
+
12
+ ```ts
13
+ import {
14
+ // ADT constructors
15
+ some, none, ok, err,
16
+ // Type guards
17
+ isSome, isNone, isOk, isErr,
18
+ // Option combinators
19
+ mapO, flatMapO, orElse, getOrElse, fromNullable,
20
+ // Result combinators
21
+ map, flatMap, flatMapAsync, mapErr, tap, tapErr,
22
+ // Async adapters
23
+ tryCatch, tryCatchAsync,
24
+ // Traversal
25
+ traverseArray, traverseArrayO, sequenceArrayO,
26
+ // Pipe
27
+ pipe, flow, comp, complement,
28
+ // Utilities
29
+ absurd, unit,
30
+ // Types
31
+ type Option, type Result, type Unit, type Brand,
32
+ } from '@tsfpp/prelude'
33
+ ```
34
+
35
+ Never `import from 'ramda'`.
36
+
37
+ ## Option\<A\>
38
+
39
+ ```ts
40
+ // Construct
41
+ const a: Option<number> = some(42)
42
+ const b: Option<number> = none
43
+
44
+ // Guard before accessing .value
45
+ if (isSome(opt)) opt.value // safe
46
+ if (isNone(opt)) return ... // early exit
47
+
48
+ // Transform
49
+ pipe(opt, mapO(n => n + 1))
50
+ pipe(opt, flatMapO(n => n > 0 ? some(n) : none))
51
+ pipe(opt, getOrElse(() => 0))
52
+ pipe(opt, orElse(() => some(defaultValue)))
53
+
54
+ // Lift from nullable
55
+ const opt = fromNullable(maybeNull) // null | undefined → Option<T>
56
+ ```
57
+
58
+ ## Result\<T, E\>
59
+
60
+ ```ts
61
+ // Construct
62
+ const r: Result<number, string> = ok(42)
63
+ const e: Result<number, string> = err('oops')
64
+
65
+ // Guard before accessing .value / .error
66
+ if (isOk(r)) r.value // T
67
+ if (isErr(r)) r.error // E
68
+
69
+ // Transform
70
+ pipe(r, map(v => v + 1))
71
+ pipe(r, flatMap(v => v > 0 ? ok(v) : err('non-positive')))
72
+ pipe(r, mapErr(e => `Wrapped: ${e}`))
73
+
74
+ // Side effects without breaking the chain
75
+ pipe(r, tap(v => log.debug({ v })))
76
+ pipe(r, tapErr(e => log.warn({ e })))
77
+
78
+ // Async adapter — wraps throwing code
79
+ const result = await tryCatchAsync(
80
+ () => db.findById(id),
81
+ e => mkDbError(e),
82
+ )
83
+
84
+ // Sync adapter
85
+ const result = tryCatch(
86
+ () => JSON.parse(raw),
87
+ e => `parse error: ${e}`,
88
+ )
89
+ ```
90
+
91
+ ## pipe
92
+
93
+ ```ts
94
+ const result = pipe(
95
+ input,
96
+ mapO(transform),
97
+ flatMapO(validate),
98
+ getOrElse(() => fallback),
99
+ )
100
+ ```
101
+
102
+ ## Unit
103
+
104
+ ```ts
105
+ // Success with no meaningful value — use ok(unit), not ok(undefined)
106
+ const save = (): Result<Unit, DbError> => ok(unit)
107
+ ```
108
+
109
+ ## absurd
110
+
111
+ ```ts
112
+ // Exhaustiveness witness — type error if a union variant is unhandled
113
+ default: return absurd(x)
114
+ ```
115
+
116
+ ## Brand
117
+
118
+ ```ts
119
+ type TrackId = Brand<string, 'TrackId'>
120
+
121
+ const mkTrackId = brand<string, 'TrackId'>(
122
+ s => s.length > 0,
123
+ s => `Invalid TrackId: "${s}"`,
124
+ )
125
+ ```
126
+
127
+ ## Traversal
128
+
129
+ ```ts
130
+ // ReadonlyArray<A> → (A → Result<B, E>) → Result<ReadonlyArray<B>, E>
131
+ const results = traverseArray(validate)(items)
132
+
133
+ // ReadonlyArray<A> → (A → Option<B>) → Option<ReadonlyArray<B>>
134
+ const options = traverseArrayO(lookup)(items)
135
+ ```
136
+
137
+ ## Discriminant convention
138
+
139
+ - Prelude ADTs use `_tag` internally — **never access it directly**
140
+ - Use exported guards: `isSome`, `isNone`, `isOk`, `isErr`
141
+ - Domain ADTs use `kind` as discriminant
@@ -0,0 +1,87 @@
1
+ ---
2
+ applyTo: "**/*.tsx"
3
+ ---
4
+
5
+ # TSF++ React rules
6
+
7
+ Full standard: `node_modules/@tsfpp/standard/REACT_CODING_STANDARD.md`
8
+ Extends: tsfpp-base.instructions.md (all base rules apply to `.tsx` too)
9
+
10
+ ## Component shape
11
+
12
+ ```ts
13
+ // Props: readonly record, no optional fields — use Option<T>
14
+ type TrackCardProps = {
15
+ readonly track: Track
16
+ readonly onSelect: Option<(id: TrackId) => void>
17
+ }
18
+
19
+ // Component: pure function, explicit return type
20
+ const TrackCard = ({ track, onSelect }: TrackCardProps): React.ReactElement => { ... }
21
+ ```
22
+
23
+ ## State
24
+
25
+ Model state as a discriminated union — never boolean soup:
26
+
27
+ ```ts
28
+ // Yes
29
+ type LoadState =
30
+ | { readonly kind: 'idle' }
31
+ | { readonly kind: 'loading' }
32
+ | { readonly kind: 'success'; readonly data: ReadonlyArray<Track> }
33
+ | { readonly kind: 'error'; readonly message: string }
34
+
35
+ // No
36
+ const [isLoading, setIsLoading] = useState(false)
37
+ const [hasError, setHasError] = useState(false)
38
+ const [data, setData] = useState(null)
39
+ ```
40
+
41
+ ## Data fetching
42
+
43
+ Use TanStack Query. Never `useEffect` for fetching:
44
+
45
+ ```ts
46
+ // Yes
47
+ const { data, isPending, isError } = useQuery({ queryKey: ['tracks'], queryFn: fetchTracks })
48
+
49
+ // No
50
+ useEffect(() => { fetch('/api/tracks').then(...) }, [])
51
+ ```
52
+
53
+ ## useEffect
54
+
55
+ Allowed only for genuine external synchronisation (DOM events, third-party library lifecycle, WebSocket). Must include a comment explaining why `useEffect` is the only option:
56
+
57
+ ```ts
58
+ useEffect(() => {
59
+ // NOTE(author, date): Syncing to external ResizeObserver — no React equivalent
60
+ const observer = new ResizeObserver(...)
61
+ return () => observer.disconnect()
62
+ }, [ref])
63
+ ```
64
+
65
+ ## Forbidden in React
66
+
67
+ - `useEffect` for data fetching or derived state
68
+ - Prop drilling > 2 levels — lift state or use context/Jotai
69
+ - `any` in prop types or event handlers
70
+ - Mutable refs as state (`useRef` for values that drive rendering)
71
+ - Inline object/array literals in JSX props without `useMemo`
72
+
73
+ ## Event handlers
74
+
75
+ ```ts
76
+ // Yes — named, explicit type
77
+ const handleSelect = (id: TrackId): void => { ... }
78
+
79
+ // No — inline arrow in JSX prop recreated every render
80
+ <Button onClick={() => doSomething(id)} />
81
+ ```
82
+
83
+ ## Memoisation
84
+
85
+ - Wrap expensive computations in `useMemo`
86
+ - Wrap callbacks passed to child components in `useCallback`
87
+ - Do not memoize everything — only when a profiler or render trace shows it matters
@@ -0,0 +1,152 @@
1
+ ---
2
+ description: Scaffolds a new TSF++-compliant module with types, smart constructors, exports, JSDoc, and a test file skeleton.
3
+ name: TSF++ new module
4
+ argument-hint: "module=<name> layer=<core|api|dal|react> description=<one sentence>"
5
+ agent: agent
6
+ tools:
7
+ - edit/createFile
8
+ - edit/editFiles
9
+ - read/readFile
10
+ - search/fileSearch
11
+ - vscode/askQuestions
12
+ ---
13
+
14
+ # TSF++ new module
15
+
16
+ Scaffold a new TSF++-compliant module from scratch.
17
+
18
+ The canonical standard is at `node_modules/@tsfpp/standard/CODING_STANDARD.md`.
19
+ The prelude API is at `node_modules/@tsfpp/prelude/README.md`.
20
+
21
+ ---
22
+
23
+ ## Required inputs
24
+
25
+ If any of the following are missing, ask for them before proceeding:
26
+
27
+ - **Module name** — e.g. `track`, `artist`, `audio-asset`
28
+ - **Layer** — `core` · `api` · `dal` · `react`
29
+ - **Domain description** — one sentence: what does this module represent or do?
30
+
31
+ ---
32
+
33
+ ## What to generate
34
+
35
+ ### 1. Source file — `src/<layer>/<module-name>.ts`
36
+
37
+ ```ts
38
+ /**
39
+ * @module <module-name>
40
+ *
41
+ * <Domain description>.
42
+ *
43
+ * @packageDocumentation
44
+ */
45
+
46
+ import { type Option, type Result, some, none, ok, err } from '@tsfpp/prelude'
47
+
48
+ // ─── Types ────────────────────────────────────────────────────────────────────
49
+
50
+ /**
51
+ * <What this branded type represents in the domain.>
52
+ */
53
+ export type <ModuleName>Id = Brand<string, '<ModuleName>Id'>
54
+
55
+ /**
56
+ * <What this sum type represents. List variants.>
57
+ */
58
+ export type <ModuleName> = {
59
+ readonly id: <ModuleName>Id
60
+ readonly <field>: <Type>
61
+ // … additional fields
62
+ }
63
+
64
+ // ─── Errors ───────────────────────────────────────────────────────────────────
65
+
66
+ /**
67
+ * Errors that can occur when working with <ModuleName> values.
68
+ */
69
+ export type <ModuleName>Error =
70
+ | { readonly kind: 'invalid_id'; readonly raw: string }
71
+ | { readonly kind: 'not_found'; readonly id: <ModuleName>Id }
72
+
73
+ // ─── Smart constructors ───────────────────────────────────────────────────────
74
+
75
+ /**
76
+ * Constructs a validated {@link <ModuleName>Id} from a raw string.
77
+ *
78
+ * @param raw - The raw string to validate.
79
+ * @returns `some` with a branded id when valid; `none` when the format is invalid.
80
+ *
81
+ * @example
82
+ * const id = mk<ModuleName>Id('abc-123')
83
+ * // => some(<ModuleName>Id)
84
+ */
85
+ export const mk<ModuleName>Id = (raw: string): Option<<ModuleName>Id> =>
86
+ raw.length > 0 ? some(raw as <ModuleName>Id) : none
87
+
88
+ /**
89
+ * Constructs a {@link <ModuleName>} from validated inputs.
90
+ *
91
+ * @param params - Validated field values.
92
+ * @returns `ok` with the constructed value; `err` with a typed error on validation failure.
93
+ */
94
+ export const mk<ModuleName> = (params: {
95
+ readonly id: <ModuleName>Id
96
+ readonly <field>: <Type>
97
+ }): Result<<ModuleName>, <ModuleName>Error> => {
98
+ // validate invariants here
99
+ return ok(params)
100
+ }
101
+ ```
102
+
103
+ ### 2. Test file — `src/<layer>/<module-name>.test.ts`
104
+
105
+ ```ts
106
+ import { describe, expect, it } from 'vitest'
107
+ import { isSome, isNone, isOk, isErr } from '@tsfpp/prelude'
108
+ import { mk<ModuleName>Id, mk<ModuleName> } from './<module-name>'
109
+
110
+ describe('mk<ModuleName>Id', () => {
111
+ it('returns some for a valid id', () => {
112
+ expect(isSome(mk<ModuleName>Id('abc-123'))).toBe(true)
113
+ })
114
+
115
+ it('returns none for an empty string', () => {
116
+ expect(isNone(mk<ModuleName>Id(''))).toBe(true)
117
+ })
118
+ })
119
+
120
+ describe('mk<ModuleName>', () => {
121
+ it('returns ok for valid inputs', () => {
122
+ // arrange
123
+ // act
124
+ // assert
125
+ })
126
+
127
+ it('returns err for invalid inputs', () => {
128
+ // arrange
129
+ // act
130
+ // assert
131
+ })
132
+ })
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Rules
138
+
139
+ - Never use placeholder comments like `// TODO: implement` in the source file — either implement it or use a properly formatted `// TODO(unknown, <date>): <reason>` marker.
140
+ - The error union must cover every failure mode the smart constructors can produce.
141
+ - Every exported symbol must have a JSDoc block before the file is considered complete.
142
+ - Test file must have at least one passing case and one failing case per smart constructor.
143
+ - Follow layer-specific constraints from `tsfpp-guarded-coding` for the specified layer.
144
+
145
+ ---
146
+
147
+ ## Completion
148
+
149
+ Report:
150
+ 1. Files created and their paths
151
+ 2. Exported symbols and their types
152
+ 3. Any invariants that still need implementing (listed as `TODO` markers in the source)
@@ -0,0 +1,152 @@
1
+ ---
2
+ description: Scaffolds a new TSF++-compliant module with types, smart constructors, exports, JSDoc, and a test file skeleton.
3
+ name: TSF++ new module
4
+ argument-hint: "module=<name> layer=<core|api|dal|react> description=<one sentence>"
5
+ agent: agent
6
+ tools:
7
+ - edit/createFile
8
+ - edit/editFiles
9
+ - read/readFile
10
+ - search/fileSearch
11
+ - vscode/askQuestions
12
+ ---
13
+
14
+ # TSF++ new module
15
+
16
+ Scaffold a new TSF++-compliant module from scratch.
17
+
18
+ The canonical standard is at `node_modules/@tsfpp/standard/CODING_STANDARD.md`.
19
+ The prelude API is at `node_modules/@tsfpp/prelude/README.md`.
20
+
21
+ ---
22
+
23
+ ## Required inputs
24
+
25
+ If any of the following are missing, ask for them before proceeding:
26
+
27
+ - **Module name** — e.g. `track`, `artist`, `audio-asset`
28
+ - **Layer** — `core` · `api` · `dal` · `react`
29
+ - **Domain description** — one sentence: what does this module represent or do?
30
+
31
+ ---
32
+
33
+ ## What to generate
34
+
35
+ ### 1. Source file — `src/<layer>/<module-name>.ts`
36
+
37
+ ```ts
38
+ /**
39
+ * @module <module-name>
40
+ *
41
+ * <Domain description>.
42
+ *
43
+ * @packageDocumentation
44
+ */
45
+
46
+ import { type Option, type Result, some, none, ok, err } from '@tsfpp/prelude'
47
+
48
+ // ─── Types ────────────────────────────────────────────────────────────────────
49
+
50
+ /**
51
+ * <What this branded type represents in the domain.>
52
+ */
53
+ export type <ModuleName>Id = Brand<string, '<ModuleName>Id'>
54
+
55
+ /**
56
+ * <What this sum type represents. List variants.>
57
+ */
58
+ export type <ModuleName> = {
59
+ readonly id: <ModuleName>Id
60
+ readonly <field>: <Type>
61
+ // … additional fields
62
+ }
63
+
64
+ // ─── Errors ───────────────────────────────────────────────────────────────────
65
+
66
+ /**
67
+ * Errors that can occur when working with <ModuleName> values.
68
+ */
69
+ export type <ModuleName>Error =
70
+ | { readonly kind: 'invalid_id'; readonly raw: string }
71
+ | { readonly kind: 'not_found'; readonly id: <ModuleName>Id }
72
+
73
+ // ─── Smart constructors ───────────────────────────────────────────────────────
74
+
75
+ /**
76
+ * Constructs a validated {@link <ModuleName>Id} from a raw string.
77
+ *
78
+ * @param raw - The raw string to validate.
79
+ * @returns `some` with a branded id when valid; `none` when the format is invalid.
80
+ *
81
+ * @example
82
+ * const id = mk<ModuleName>Id('abc-123')
83
+ * // => some(<ModuleName>Id)
84
+ */
85
+ export const mk<ModuleName>Id = (raw: string): Option<<ModuleName>Id> =>
86
+ raw.length > 0 ? some(raw as <ModuleName>Id) : none
87
+
88
+ /**
89
+ * Constructs a {@link <ModuleName>} from validated inputs.
90
+ *
91
+ * @param params - Validated field values.
92
+ * @returns `ok` with the constructed value; `err` with a typed error on validation failure.
93
+ */
94
+ export const mk<ModuleName> = (params: {
95
+ readonly id: <ModuleName>Id
96
+ readonly <field>: <Type>
97
+ }): Result<<ModuleName>, <ModuleName>Error> => {
98
+ // validate invariants here
99
+ return ok(params)
100
+ }
101
+ ```
102
+
103
+ ### 2. Test file — `src/<layer>/<module-name>.test.ts`
104
+
105
+ ```ts
106
+ import { describe, expect, it } from 'vitest'
107
+ import { isSome, isNone, isOk, isErr } from '@tsfpp/prelude'
108
+ import { mk<ModuleName>Id, mk<ModuleName> } from './<module-name>'
109
+
110
+ describe('mk<ModuleName>Id', () => {
111
+ it('returns some for a valid id', () => {
112
+ expect(isSome(mk<ModuleName>Id('abc-123'))).toBe(true)
113
+ })
114
+
115
+ it('returns none for an empty string', () => {
116
+ expect(isNone(mk<ModuleName>Id(''))).toBe(true)
117
+ })
118
+ })
119
+
120
+ describe('mk<ModuleName>', () => {
121
+ it('returns ok for valid inputs', () => {
122
+ // arrange
123
+ // act
124
+ // assert
125
+ })
126
+
127
+ it('returns err for invalid inputs', () => {
128
+ // arrange
129
+ // act
130
+ // assert
131
+ })
132
+ })
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Rules
138
+
139
+ - Never use placeholder comments like `// TODO: implement` in the source file — either implement it or use a properly formatted `// TODO(unknown, <date>): <reason>` marker.
140
+ - The error union must cover every failure mode the smart constructors can produce.
141
+ - Every exported symbol must have a JSDoc block before the file is considered complete.
142
+ - Test file must have at least one passing case and one failing case per smart constructor.
143
+ - Follow layer-specific constraints from `tsfpp-guarded-coding` for the specified layer.
144
+
145
+ ---
146
+
147
+ ## Completion
148
+
149
+ Report:
150
+ 1. Files created and their paths
151
+ 2. Exported symbols and their types
152
+ 3. Any invariants that still need implementing (listed as `TODO` markers in the source)
package/init.mjs ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @tsfpp/agents init
4
+ *
5
+ * Copies Copilot instructions, chatmodes, prompts, and CLAUDE.md
6
+ * into the correct locations in the consumer's project.
7
+ *
8
+ * Usage:
9
+ * pnpm dlx @tsfpp/agents (one-shot, no install)
10
+ * node node_modules/@tsfpp/agents/init.mjs
11
+ */
12
+
13
+ import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
14
+ import { existsSync } from 'node:fs';
15
+ import { join, dirname } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { createInterface } from 'node:readline';
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const cwd = process.cwd();
21
+
22
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
23
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
24
+ const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
25
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
26
+
27
+ // ─── File map ─────────────────────────────────────────────────────────────────
28
+ // Each entry: [source (relative to this file), destination (relative to cwd)]
29
+
30
+ const FILES = [
31
+ // Always-on workspace instructions
32
+ ['copilot/copilot-instructions.md', '.github/copilot-instructions.md'],
33
+
34
+ // Scoped instruction files
35
+ ['copilot/instructions/tsfpp-base.instructions.md', '.github/instructions/tsfpp-base.instructions.md'],
36
+ ['copilot/instructions/tsfpp-react.instructions.md', '.github/instructions/tsfpp-react.instructions.md'],
37
+ ['copilot/instructions/tsfpp-api.instructions.md', '.github/instructions/tsfpp-api.instructions.md'],
38
+ ['copilot/instructions/tsfpp-prelude.instructions.md', '.github/instructions/tsfpp-prelude.instructions.md'],
39
+
40
+ // Agents
41
+ ['copilot/agents/tsfpp-guarded-coding.agent.md', '.github/agents/tsfpp-guarded-coding.agent.md'],
42
+ ['copilot/agents/tsfpp-audit.agent.md', '.github/agents/tsfpp-audit.agent.md'],
43
+ ['copilot/agents/tsfpp-refactor-engineer.agent.md', '.github/agents/tsfpp-refactor-engineer.agent.md'],
44
+ ['copilot/agents/tsfpp-annotate.agent.md', '.github/agents/tsfpp-annotate.agent.md'],
45
+
46
+ // Reusable prompts
47
+ ['copilot/prompts/tsfpp-new-module.prompt.md', '.github/prompts/tsfpp-new-module.prompt.md'],
48
+ ['copilot/prompts/tsfpp-boundary-review.prompt.md', '.github/prompts/tsfpp-boundary-review.prompt.md'],
49
+
50
+ // Claude Code
51
+ ['claude/CLAUDE.md', 'CLAUDE.md'],
52
+ ];
53
+
54
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
55
+
56
+ async function ensureDir(filePath) {
57
+ await mkdir(dirname(filePath), { recursive: true });
58
+ }
59
+
60
+ async function confirm(question) {
61
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
62
+ return new Promise((resolve) => {
63
+ rl.question(question, (answer) => {
64
+ rl.close();
65
+ resolve(answer.trim().toLowerCase() === 'y');
66
+ });
67
+ });
68
+ }
69
+
70
+ // ─── Main ─────────────────────────────────────────────────────────────────────
71
+
72
+ console.log();
73
+ console.log(bold(' @tsfpp/agents — init'));
74
+ console.log(dim(' Copies Copilot and Claude Code configuration into your project.\n'));
75
+
76
+ const results = { copied: [], skipped: [], failed: [] };
77
+
78
+ for (const [src, dest] of FILES) {
79
+ const srcPath = join(__dirname, src);
80
+ const destPath = join(cwd, dest);
81
+
82
+ if (existsSync(destPath)) {
83
+ const overwrite = await confirm(
84
+ ` ${yellow('!')} ${dest} already exists. Overwrite? ${dim('[y/N]')} `
85
+ );
86
+ if (!overwrite) {
87
+ results.skipped.push(dest);
88
+ console.log(` ${dim('–')} ${dim(dest)} ${dim('(skipped)')}`);
89
+ continue;
90
+ }
91
+ }
92
+
93
+ try {
94
+ await ensureDir(destPath);
95
+ await copyFile(srcPath, destPath);
96
+ results.copied.push(dest);
97
+ console.log(` ${green('✓')} ${dest}`);
98
+ } catch (err) {
99
+ results.failed.push(dest);
100
+ console.log(` \x1b[31m✗\x1b[0m ${dest} ${dim(`(${err.message})`)}`);
101
+ }
102
+ }
103
+
104
+ // ─── Summary ─────────────────────────────────────────────────────────────────
105
+
106
+ console.log();
107
+ console.log(dim(' ─────────────────────────────────────────'));
108
+ 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')}`);
109
+ console.log();
110
+
111
+ if (results.failed.length === 0) {
112
+ console.log(' ' + bold('Done.') + ' Reload VS Code to activate Copilot instructions.');
113
+ console.log(dim(' Commit the generated files — they are workspace configuration.\n'));
114
+ } else {
115
+ console.log(' Some files could not be copied. Check the errors above.\n');
116
+ process.exit(1);
117
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@tsfpp/agents",
3
+ "version": "1.0.0",
4
+ "description": "Workspace AI tooling for TSF++ projects: scoped instructions, coding agents, and reusable prompts",
5
+ "keywords": [
6
+ "tsfpp",
7
+ "copilot",
8
+ "agents",
9
+ "instructions",
10
+ "claude",
11
+ "functional",
12
+ "typescript"
13
+ ],
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "homepage": "https://github.com/tsfpp/agents#readme",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/tsfpp/agents.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/tsfpp/agents/issues"
23
+ },
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ },
27
+ "bin": {
28
+ "tsfpp-agents": "./init.mjs"
29
+ },
30
+ "files": [
31
+ "copilot/",
32
+ "claude/",
33
+ "init.mjs",
34
+ "README.md",
35
+ "CHANGELOG.md",
36
+ "LICENSE"
37
+ ],
38
+ "peerDependencies": {
39
+ "@tsfpp/standard": ">=1.0.0",
40
+ "@tsfpp/prelude": ">=1.0.0",
41
+ "@tsfpp/boundary": ">=1.0.0",
42
+ "@tsfpp/eslint-config":">=1.0.0",
43
+ "@tsfpp/tsconfig": ">=1.0.0"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ }
48
+ }