familiar-vtt 0.1.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.
Files changed (105) hide show
  1. package/.github/workflows/publish.yml +23 -0
  2. package/.mcp.json +11 -0
  3. package/ARCHITECTURE.md +320 -0
  4. package/BUSINESS.md +547 -0
  5. package/CLAUDE.md +118 -0
  6. package/COMMUNITY_RESEARCH.md +225 -0
  7. package/LICENSE +21 -0
  8. package/TOOLS_REFERENCE.md +224 -0
  9. package/dist/module/main.js +1016 -0
  10. package/dist/module/main.js.map +7 -0
  11. package/dist/server/server/config.d.ts +22 -0
  12. package/dist/server/server/config.js +33 -0
  13. package/dist/server/server/config.js.map +1 -0
  14. package/dist/server/server/connector.d.ts +26 -0
  15. package/dist/server/server/connector.js +179 -0
  16. package/dist/server/server/connector.js.map +1 -0
  17. package/dist/server/server/http-transport.d.ts +10 -0
  18. package/dist/server/server/http-transport.js +43 -0
  19. package/dist/server/server/http-transport.js.map +1 -0
  20. package/dist/server/server/index.d.ts +2 -0
  21. package/dist/server/server/index.js +59 -0
  22. package/dist/server/server/index.js.map +1 -0
  23. package/dist/server/server/logger.d.ts +6 -0
  24. package/dist/server/server/logger.js +14 -0
  25. package/dist/server/server/logger.js.map +1 -0
  26. package/dist/server/server/tools/bundles.d.ts +56 -0
  27. package/dist/server/server/tools/bundles.js +187 -0
  28. package/dist/server/server/tools/bundles.js.map +1 -0
  29. package/dist/server/server/tools/canvas-environment.d.ts +3 -0
  30. package/dist/server/server/tools/canvas-environment.js +151 -0
  31. package/dist/server/server/tools/canvas-environment.js.map +1 -0
  32. package/dist/server/server/tools/canvas.d.ts +3 -0
  33. package/dist/server/server/tools/canvas.js +134 -0
  34. package/dist/server/server/tools/canvas.js.map +1 -0
  35. package/dist/server/server/tools/cards.d.ts +3 -0
  36. package/dist/server/server/tools/cards.js +35 -0
  37. package/dist/server/server/tools/cards.js.map +1 -0
  38. package/dist/server/server/tools/characters.d.ts +3 -0
  39. package/dist/server/server/tools/characters.js +115 -0
  40. package/dist/server/server/tools/characters.js.map +1 -0
  41. package/dist/server/server/tools/chat.d.ts +3 -0
  42. package/dist/server/server/tools/chat.js +23 -0
  43. package/dist/server/server/tools/chat.js.map +1 -0
  44. package/dist/server/server/tools/combat.d.ts +3 -0
  45. package/dist/server/server/tools/combat.js +38 -0
  46. package/dist/server/server/tools/combat.js.map +1 -0
  47. package/dist/server/server/tools/compendium.d.ts +3 -0
  48. package/dist/server/server/tools/compendium.js +33 -0
  49. package/dist/server/server/tools/compendium.js.map +1 -0
  50. package/dist/server/server/tools/dice.d.ts +3 -0
  51. package/dist/server/server/tools/dice.js +12 -0
  52. package/dist/server/server/tools/dice.js.map +1 -0
  53. package/dist/server/server/tools/effects.d.ts +3 -0
  54. package/dist/server/server/tools/effects.js +54 -0
  55. package/dist/server/server/tools/effects.js.map +1 -0
  56. package/dist/server/server/tools/ember-events.d.ts +3 -0
  57. package/dist/server/server/tools/ember-events.js +34 -0
  58. package/dist/server/server/tools/ember-events.js.map +1 -0
  59. package/dist/server/server/tools/folders.d.ts +3 -0
  60. package/dist/server/server/tools/folders.js +30 -0
  61. package/dist/server/server/tools/folders.js.map +1 -0
  62. package/dist/server/server/tools/helpers.d.ts +10 -0
  63. package/dist/server/server/tools/helpers.js +13 -0
  64. package/dist/server/server/tools/helpers.js.map +1 -0
  65. package/dist/server/server/tools/items.d.ts +3 -0
  66. package/dist/server/server/tools/items.js +23 -0
  67. package/dist/server/server/tools/items.js.map +1 -0
  68. package/dist/server/server/tools/journals.d.ts +3 -0
  69. package/dist/server/server/tools/journals.js +71 -0
  70. package/dist/server/server/tools/journals.js.map +1 -0
  71. package/dist/server/server/tools/macros.d.ts +3 -0
  72. package/dist/server/server/tools/macros.js +32 -0
  73. package/dist/server/server/tools/macros.js.map +1 -0
  74. package/dist/server/server/tools/playlists.d.ts +3 -0
  75. package/dist/server/server/tools/playlists.js +39 -0
  76. package/dist/server/server/tools/playlists.js.map +1 -0
  77. package/dist/server/server/tools/regions.d.ts +3 -0
  78. package/dist/server/server/tools/regions.js +65 -0
  79. package/dist/server/server/tools/regions.js.map +1 -0
  80. package/dist/server/server/tools/registry.d.ts +11 -0
  81. package/dist/server/server/tools/registry.js +73 -0
  82. package/dist/server/server/tools/registry.js.map +1 -0
  83. package/dist/server/server/tools/scenes.d.ts +3 -0
  84. package/dist/server/server/tools/scenes.js +142 -0
  85. package/dist/server/server/tools/scenes.js.map +1 -0
  86. package/dist/server/server/tools/tables.d.ts +3 -0
  87. package/dist/server/server/tools/tables.js +22 -0
  88. package/dist/server/server/tools/tables.js.map +1 -0
  89. package/dist/server/server/tools/world.d.ts +3 -0
  90. package/dist/server/server/tools/world.js +26 -0
  91. package/dist/server/server/tools/world.js.map +1 -0
  92. package/dist/server/shared/constants.d.ts +16 -0
  93. package/dist/server/shared/constants.js +17 -0
  94. package/dist/server/shared/constants.js.map +1 -0
  95. package/dist/server/shared/html.d.ts +4 -0
  96. package/dist/server/shared/html.js +9 -0
  97. package/dist/server/shared/html.js.map +1 -0
  98. package/dist/server/shared/protocol.d.ts +184 -0
  99. package/dist/server/shared/protocol.js +58 -0
  100. package/dist/server/shared/protocol.js.map +1 -0
  101. package/dist/server/shared/types.d.ts +21 -0
  102. package/dist/server/shared/types.js +2 -0
  103. package/dist/server/shared/types.js.map +1 -0
  104. package/module.json +21 -0
  105. package/package.json +81 -0
@@ -0,0 +1,23 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ workflow_dispatch: # Manual trigger from GitHub UI
5
+
6
+ jobs:
7
+ publish:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v4
11
+
12
+ - uses: actions/setup-node@v4
13
+ with:
14
+ node-version: 20
15
+ registry-url: https://registry.npmjs.org
16
+
17
+ - run: npm ci
18
+
19
+ - run: npm run build
20
+
21
+ - run: npm publish --access public
22
+ env:
23
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/.mcp.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "mcpServers": {
3
+ "familiar": {
4
+ "command": "node",
5
+ "args": ["C:\\Users\\ryan_\\Projects\\familiar\\dist\\server\\server\\index.js"],
6
+ "env": {
7
+ "FAMILIAR_WS_PORT": "3005"
8
+ }
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,320 @@
1
+ # Familiar Architecture
2
+
3
+ Technical architecture documentation for the Familiar project.
4
+
5
+ ## Design Principles
6
+
7
+ - **Single process** — one Node.js process, stdio transport for MCP, WebSocket to Foundry. No wrapper/backend split.
8
+ - **System-agnostic core** — most tools work with any Foundry game system. Character formatting is D&D 5e for now, pluggable later.
9
+ - **Integrate, don't rebuild** — use existing modules (Sequencer, Token Magic FX, etc.) via their APIs instead of reimplementing visual features.
10
+ - **Full AI control** — every action a human DM can do in Foundry, the AI should be able to do via tools.
11
+ - **Build → test → iterate** — implement one tool, test it live, fix issues, move on.
12
+ - **Type-safe routing** — discriminated unions and exhaustive switches, not string-based dispatch.
13
+ - **Modular tools** — each tool domain in its own file with a clean registration pattern.
14
+ - **Minimal dependencies** — `@modelcontextprotocol/sdk`, `ws`, `zod`, `pino`. That's it.
15
+
16
+ ## Architecture Overview
17
+
18
+ ### Current Architecture (MCP)
19
+
20
+ ```
21
+ Claude (or any MCP client)
22
+ ↓ stdio (MCP protocol)
23
+ MCP Server (single Node.js process)
24
+ ↓ WebSocket (localhost)
25
+ Foundry VTT Module (browser-side bridge)
26
+ ↓ Foundry API
27
+ Game World (actors, scenes, items, journals)
28
+ ```
29
+
30
+ ### Future Architecture (Built-in, Plug & Play)
31
+
32
+ ```
33
+ Foundry Module (browser)
34
+ ├── Built-in LLM client (calls AI provider API directly)
35
+ ├── Tool execution (same 124 tools, runs in browser)
36
+ ├── Chat UI in Foundry sidebar
37
+ └── Foundry API
38
+ Game World
39
+ ```
40
+
41
+ The future architecture eliminates the MCP server entirely. The Foundry module talks directly to the AI provider API. No Node.js, no separate process, no MCP config. Everything lives inside the Foundry module.
42
+
43
+ **Both architectures will be supported** — MCP for power users who want Claude Code/Cursor integration, built-in for everyone else.
44
+
45
+ ## Two Components
46
+
47
+ ### 1. MCP Server (`src/server/`)
48
+
49
+ Node.js process that:
50
+ - Exposes MCP tools via stdio transport
51
+ - Runs a WebSocket server (port 3005) waiting for the Foundry module to connect
52
+ - Translates MCP tool calls → WebSocket queries → Foundry responses → MCP results
53
+
54
+ ### 2. Foundry Module (`src/module/`)
55
+
56
+ Browser-side Foundry VTT module that:
57
+ - Connects to the MCP server's WebSocket on world load
58
+ - Receives queries and executes them against the Foundry API (`game.actors`, `game.scenes`, etc.)
59
+ - Returns structured results back over WebSocket
60
+ - GM-only access (non-GM users are silently ignored)
61
+
62
+ ## Shared Code (`src/shared/`)
63
+
64
+ - `protocol.ts` — Zod-validated WebSocket message schemas (query, response, ping/pong)
65
+ - `types.ts` — Shared TypeScript interfaces
66
+ - `constants.ts` — Module ID, ports, timeouts
67
+
68
+ ## Project Structure
69
+
70
+ ```
71
+ familiar/
72
+ ├── src/
73
+ │ ├── server/ # MCP server (Node.js)
74
+ │ │ ├── index.ts # Entry point: stdio transport + WebSocket server
75
+ │ │ ├── config.ts # Zod-validated env config
76
+ │ │ ├── connector.ts # WebSocket server ↔ Foundry module
77
+ │ │ ├── logger.ts # Pino logger (stderr)
78
+ │ │ └── tools/ # MCP tool implementations
79
+ │ │ ├── registry.ts # Tool registration & routing
80
+ │ │ ├── characters.ts # Character read/write tools (14 tools)
81
+ │ │ ├── combat.ts # Initiative, conditions, HP tracking (8 tools)
82
+ │ │ ├── scenes.ts # Scene, token, weather, screen tools (17 tools)
83
+ │ │ ├── canvas.ts # Templates, tiles, drawings (12 tools)
84
+ │ │ ├── canvas-environment.ts # Walls, lights, sounds, notes, doors (18 tools)
85
+ │ │ ├── compendium.ts # Compendium search & lookup (4 tools)
86
+ │ │ ├── dice.ts # Dice rolling (1 tool)
87
+ │ │ ├── journals.ts # Journal & quest tools (9 tools)
88
+ │ │ ├── chat.ts # Chat messages (2 tools)
89
+ │ │ ├── playlists.ts # Music & playlist control (6 tools)
90
+ │ │ ├── tables.ts # Rollable tables (3 tools)
91
+ │ │ ├── cards.ts # Card stacks & decks (5 tools)
92
+ │ │ ├── macros.ts # Macro CRUD & execution (4 tools)
93
+ │ │ ├── effects.ts # Active effect CRUD (4 tools)
94
+ │ │ ├── items.ts # Standalone item CRUD (2 tools)
95
+ │ │ ├── folders.ts # Folder management (3 tools)
96
+ │ │ ├── regions.ts # Region/trigger zones (3 tools)
97
+ │ │ ├── ember-events.ts # Ember campaign event tools (5 tools)
98
+ │ │ ├── helpers.ts # Shared tool helper utilities
99
+ │ │ └── world.ts # World info, pause & time (4 tools)
100
+ │ ├── module/ # Foundry VTT module (browser)
101
+ │ │ ├── main.ts # Module entry, lifecycle hooks, settings registration
102
+ │ │ ├── connection.ts # WebSocket client with reconnection
103
+ │ │ ├── query-handler.ts # Query routing → data access
104
+ │ │ ├── query-handlers-sidebar.ts # Sidebar query handlers (chat, playlists, tables, cards, macros)
105
+ │ │ ├── settings-app.ts # AI Provider Settings UI (ApplicationV2)
106
+ │ │ ├── providers.ts # AI provider definitions (16 chat, 4 voice, 5 image)
107
+ │ │ ├── ai/ # Built-in LLM client (browser-side)
108
+ │ │ │ ├── llm-client.ts # Provider-agnostic LLM client with tool calling loop
109
+ │ │ │ ├── tool-definitions.ts # All 124 tools in OpenAI function-calling format
110
+ │ │ │ ├── streaming.ts # SSE parser for ReadableStream
111
+ │ │ │ ├── anthropic-adapter.ts # Anthropic Messages API ↔ OpenAI format adapter
112
+ │ │ │ └── types.ts # Chat message, tool call, streaming types
113
+ │ │ ├── types/ # Foundry VTT type declarations
114
+ │ │ │ ├── foundry.d.ts # Core Foundry API types
115
+ │ │ │ ├── foundry-appv2.d.ts # ApplicationV2 type declarations
116
+ │ │ │ ├── foundry-canvas.d.ts # Canvas layer types
117
+ │ │ │ ├── foundry-combat.d.ts # Combat system types
118
+ │ │ │ └── foundry-sidebar.d.ts # Sidebar & UI types
119
+ │ │ └── data/ # Foundry API data access
120
+ │ │ ├── characters.ts # Actor queries & mutations
121
+ │ │ ├── scenes.ts # Scene, token & weather queries
122
+ │ │ ├── canvas.ts # Templates, tiles, drawings
123
+ │ │ ├── canvas-environment.ts # Walls, lights, sounds, notes, doors
124
+ │ │ ├── compendium.ts # Pack search & lookup
125
+ │ │ ├── dice.ts # Roll execution
126
+ │ │ ├── journals.ts # Journal CRUD
127
+ │ │ ├── chat.ts # Chat message access
128
+ │ │ ├── playlists.ts # Playlist & track control
129
+ │ │ ├── tables.ts # Rollable table queries
130
+ │ │ ├── cards.ts # Card stack operations
131
+ │ │ ├── macros.ts # Macro CRUD & execution
132
+ │ │ ├── effects.ts # Active effect CRUD
133
+ │ │ ├── items.ts # Standalone item CRUD
134
+ │ │ ├── folders.ts # Folder management
135
+ │ │ ├── regions.ts # Region/trigger zones
136
+ │ │ ├── ember-events.ts # Ember campaign event handlers
137
+ │ │ ├── world.ts # World info, time & pause
138
+ │ │ └── screen.ts # Screenshot capture
139
+ │ └── shared/ # Shared types & protocol
140
+ │ ├── protocol.ts # WebSocket message schemas (Zod)
141
+ │ ├── types.ts # Shared interfaces
142
+ │ └── constants.ts # IDs, ports, timeouts
143
+ ├── tests/ # Vitest tests
144
+ ├── dist/ # Build output
145
+ │ ├── server/ # Compiled MCP server
146
+ │ └── module/ # Bundled Foundry module (esbuild)
147
+ ├── package.json
148
+ ├── tsconfig.json
149
+ ├── tsconfig.server.json # Server TypeScript config (Node.js)
150
+ ├── tsconfig.module.json # Module TypeScript config (browser)
151
+ ├── eslint.config.ts
152
+ ├── vitest.config.ts
153
+ └── CLAUDE.md # Project instructions
154
+ ```
155
+
156
+ ## Query Methods
157
+
158
+ The WebSocket protocol uses method-based routing. Each method maps to a Foundry API operation. There are 124 methods total — the module-side handlers are organized by domain in `src/module/data/`. Key examples:
159
+
160
+ | Domain | Methods | Module Handler |
161
+ |--------|---------|---------------|
162
+ | World & System | `get-world-info`, `toggle-pause`, `get-time`, `advance-time` | `data/world.ts` |
163
+ | Characters | `get-character`, `list-characters`, `update-character`, etc. | `data/characters.ts` |
164
+ | Scenes & Tokens | `get-current-scene`, `move-token`, `create-tokens`, `set-weather`, `pan-canvas`, etc. | `data/scenes.ts` |
165
+ | Combat | `start-combat`, `roll-initiative`, `toggle-condition`, etc. | `data/combat.ts` |
166
+ | Canvas | `create-template`, `create-tile`, `create-drawing`, etc. | `data/canvas.ts` |
167
+ | Environment | `create-walls`, `create-light`, `toggle-door`, etc. | `data/canvas-environment.ts` |
168
+ | Compendium | `search-compendium`, `get-compendium-entry`, etc. | `data/compendium.ts` |
169
+ | Journals | `list-journals`, `search-journals`, `get-journal`, etc. | `data/journals.ts` |
170
+ | Chat | `send-chat-message`, `get-recent-messages` | `data/chat.ts` |
171
+ | Playlists | `play-playlist`, `play-track`, `stop-all-playlists`, etc. | `data/playlists.ts` |
172
+ | Tables | `list-rollable-tables`, `roll-table`, etc. | `data/tables.ts` |
173
+ | Cards | `list-card-stacks`, `draw-cards`, `shuffle-deck`, etc. | `data/cards.ts` |
174
+ | Macros | `list-macros`, `create-macro`, `execute-macro`, `delete-macro` | `data/macros.ts` |
175
+ | Active Effects | `list-effects`, `create-effect`, `update-effect`, `delete-effect` | `data/effects.ts` |
176
+ | Items | `create-item`, `delete-item` | `data/items.ts` |
177
+ | Folders | `list-folders`, `create-folder`, `delete-folder` | `data/folders.ts` |
178
+ | Regions | `list-regions`, `create-region`, `delete-regions` | `data/regions.ts` |
179
+ | Ember Events | `list-events`, `get-event`, `begin-event`, `complete-event`, `set-event-outcome` | `data/ember-events.ts` |
180
+ | Dice | `roll-dice` | `data/dice.ts` |
181
+ | Screen | `capture-screen` | `data/screen.ts` |
182
+
183
+ ## Tool Registration Pattern
184
+
185
+ Tools use a typed registry pattern, not string-based dispatch:
186
+
187
+ ```typescript
188
+ // src/server/tools/registry.ts
189
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
190
+ import { z } from 'zod';
191
+ import type { FoundryConnector } from '../connector.js';
192
+
193
+ export type ToolRegistrar = (server: McpServer, connector: FoundryConnector) => void;
194
+
195
+ // Each tool file exports a registrar
196
+ // src/server/tools/characters.ts
197
+ export const registerCharacterTools: ToolRegistrar = (server, connector) => {
198
+ server.tool(
199
+ 'get-character',
200
+ 'Get a character sheet by name or Foundry ID',
201
+ { identifier: z.string().describe('Character name or Foundry actor ID') },
202
+ async ({ identifier }) => {
203
+ const response = await connector.query('get-character', { identifier });
204
+ return {
205
+ content: [{ type: 'text', text: JSON.stringify(response.result, null, 2) }],
206
+ };
207
+ }
208
+ );
209
+ // ... more character tools
210
+ };
211
+ ```
212
+
213
+ ## Configuration
214
+
215
+ ### Environment Variables (MCP Server)
216
+
217
+ ```bash
218
+ FAMILIAR_WS_PORT=3005 # WebSocket port (default: 3005)
219
+ FAMILIAR_LOG_LEVEL=info # Log level: fatal|error|warn|info|debug|trace
220
+ ```
221
+
222
+ ### Claude Code MCP Config (`.mcp.json` or Claude Desktop config)
223
+
224
+ ```json
225
+ {
226
+ "mcpServers": {
227
+ "familiar": {
228
+ "command": "node",
229
+ "args": ["dist/server/index.js"],
230
+ "env": {
231
+ "FAMILIAR_WS_PORT": "3005"
232
+ }
233
+ }
234
+ }
235
+ }
236
+ ```
237
+
238
+ ### Foundry Module Installation
239
+
240
+ Copy `dist/module/` to Foundry's `Data/modules/familiar/` directory along with `module.json`.
241
+
242
+ ## Foundry Module Details
243
+
244
+ ### Module Manifest (`module.json`)
245
+
246
+ ```json
247
+ {
248
+ "id": "familiar",
249
+ "title": "Familiar",
250
+ "description": "Your AI familiar for Foundry VTT — AI DM assistant",
251
+ "version": "0.1.0",
252
+ "compatibility": {
253
+ "minimum": "12",
254
+ "verified": "13"
255
+ },
256
+ "authors": [{ "name": "Ryan Jansen" }],
257
+ "esmodules": ["main.js"],
258
+ "socket": true
259
+ }
260
+ ```
261
+
262
+ ### Module Lifecycle
263
+
264
+ 1. `Hooks.once('init')` — Register module settings:
265
+ - MCP connection settings (port, auto-connect) — `config: true` (shown in default settings)
266
+ - AI provider settings (provider, API key, model, temperature, etc.) — `config: false` (custom UI via `registerMenu`)
267
+ 2. `Hooks.once('ready')` — If GM + auto-connect enabled → connect WebSocket to MCP server
268
+ 3. On WebSocket message → parse with Zod schema → route to handler → execute Foundry API → send response
269
+ 4. On disconnect → exponential backoff reconnect (up to 5 attempts)
270
+
271
+ ### Security
272
+
273
+ - **GM-only**: All queries require `game.user.isGM` — non-GM users are silently ignored
274
+ - **Write protection**: Destructive operations (delete, update) check permissions before execution
275
+ - **No remote access**: WebSocket listens on localhost only by default
276
+
277
+ ## D&D 5e (2024) Specifics
278
+
279
+ ### Character Data Mapping
280
+
281
+ The module translates Foundry's D&D 5e system data (`system.abilities`, `system.attributes`, etc.) into a clean, readable format for AI consumption:
282
+
283
+ - **Abilities**: STR/DEX/CON/INT/WIS/CHA with scores, modifiers, saves
284
+ - **HP**: Current, max, temp
285
+ - **AC**: Total with breakdown
286
+ - **Skills**: All skills with modifiers, proficiency, expertise
287
+ - **Spells**: By level, with slots remaining, prepared status
288
+ - **Items**: Inventory with attunement, equipped status, quantity
289
+ - **Features**: Class features, racial traits, feats
290
+
291
+ ### Compendium Packs
292
+
293
+ D&D 5e provides compendium packs for:
294
+ - `dnd5e.monsters` — Creature stat blocks
295
+ - `dnd5e.items` — Equipment, weapons, armor
296
+ - `dnd5e.spells` — All spells
297
+ - `dnd5e.classfeatures` — Class features
298
+ - `dnd5e.races` — Ancestries/races
299
+
300
+ The `search-compendium` tool searches across packs by name. The `get-compendium-entry` tool returns the full entry with all fields.
301
+
302
+ ## Dependencies
303
+
304
+ ### Runtime
305
+
306
+ - `@modelcontextprotocol/sdk` — MCP protocol implementation
307
+ - `ws` — WebSocket server (lightweight, no socket.io overhead)
308
+ - `zod` — Schema validation for config & protocol
309
+ - `pino` — Fast structured logging
310
+
311
+ ### Dev
312
+
313
+ - `typescript`, `tsx` — TypeScript toolchain
314
+ - `esbuild` — Fast bundler for Foundry module
315
+ - `eslint` + plugins — Linting (security, sonarjs, unicorn, etc.)
316
+ - `prettier` — Formatting
317
+ - `vitest` — Testing
318
+ - `husky` + `lint-staged` — Pre-commit hooks
319
+ - `commitlint` — Conventional commit enforcement
320
+ - `knip` — Dead code detection