Skip to content

Commit fe1d54d

Browse files
Merge pull request #5 from chrisreddington/main
Add Elicitation, Linting, Convert test infrastructure, Other refinements
2 parents 3c7de64 + 65a7cac commit fe1d54d

File tree

97 files changed

+6020
-5419
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+6020
-5419
lines changed

.github/instructions/mcp-server.instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ Implement consistent difficulty levels across all games:
145145
```typescript
146146
export function calculateTicTacToeMove(
147147
gameState: TicTacToeGameState,
148-
difficulty: 'easy' | 'medium' | 'hard' = 'medium'
148+
difficulty: Difficulty = 'medium'
149149
): TicTacToeMove {
150150
const validMoves = getValidMoves(gameState)
151151

.github/instructions/shared-library.instructions.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,89 @@ export function getGameDisplayName(type: GameType): string {
140140
}
141141
```
142142

143+
## Constants and Common Values
144+
145+
### Centralized Constants and Derived Types
146+
**Types are derived from constants using `as const` assertions - constants are the single source of truth:**
147+
148+
```typescript
149+
// ✅ Constants define the source of truth
150+
export const DIFFICULTIES = ['easy', 'medium', 'hard'] as const
151+
export const GAME_TYPES = ['tic-tac-toe', 'rock-paper-scissors'] as const
152+
export const PLAYER_IDS = { HUMAN: 'player1', PLAYER2: 'player2', AI: 'ai' } as const
153+
154+
// ✅ Types are derived from constants
155+
export type Difficulty = typeof DIFFICULTIES[number]
156+
export type GameType = typeof GAME_TYPES[number]
157+
export type PlayerId = typeof PLAYER_IDS[keyof typeof PLAYER_IDS]
158+
159+
// ✅ Import the derived types
160+
import type { Difficulty, GameType } from '@turn-based-mcp/shared'
161+
162+
// ❌ Don't define duplicate union types
163+
export type Difficulty = 'easy' | 'medium' | 'hard' // This duplicates the constants!
164+
```
165+
166+
### Available Constants
167+
Key constants provided by the shared library:
168+
169+
```typescript
170+
// Constants with derived types
171+
export const GAME_TYPES = ['tic-tac-toe', 'rock-paper-scissors'] as const
172+
export const DIFFICULTIES = ['easy', 'medium', 'hard'] as const
173+
export const PLAYER_IDS = { HUMAN: 'player1', PLAYER2: 'player2', AI: 'ai' } as const
174+
export const GAME_STATUSES = ['waiting', 'playing', 'finished'] as const
175+
176+
// Derived types (auto-generated from constants)
177+
export type GameType = typeof GAME_TYPES[number]
178+
export type Difficulty = typeof DIFFICULTIES[number]
179+
export type PlayerId = typeof PLAYER_IDS[keyof typeof PLAYER_IDS]
180+
export type GameStatus = typeof GAME_STATUSES[number]
181+
182+
// Default values
183+
export const DEFAULT_PLAYER_NAME = 'Player'
184+
export const DEFAULT_AI_DIFFICULTY: Difficulty = 'medium'
185+
186+
// UI display configuration
187+
export const DIFFICULTY_DISPLAY = {
188+
easy: { emoji: '😌', label: 'Easy' },
189+
medium: { emoji: '🎯', label: 'Medium' },
190+
hard: { emoji: '🔥', label: 'Hard' }
191+
} as const
192+
```
193+
194+
### Type Guards and Utilities
195+
Use provided validation functions that work with the constants:
196+
197+
```typescript
198+
// Type guards (check against the constant arrays)
199+
export function isSupportedGameType(gameType: string): gameType is GameType
200+
export function isValidDifficulty(difficulty: string): difficulty is Difficulty
201+
export function isValidPlayerId(playerId: string): playerId is PlayerId
202+
203+
// Display helpers
204+
export function getDifficultyDisplay(difficulty: Difficulty)
205+
```
206+
207+
### Architecture Benefits
208+
This approach ensures:
209+
- **Single source of truth**: Constants define what values are valid
210+
- **Type safety**: TypeScript derives exact types from the constant values
211+
- **Runtime validation**: Type guards check against the same arrays used to derive types
212+
- **Maintainability**: Add a new difficulty by updating one constant array
213+
214+
### Testing Constants
215+
For mocking and test data, use shared testing utilities:
216+
217+
```typescript
218+
// Test data from shared/src/testing/
219+
import {
220+
mockTicTacToeGameState,
221+
mockRPSGameState,
222+
createMockGameSession
223+
} from '@turn-based-mcp/shared/testing'
224+
```
225+
143226
## Testing Infrastructure
144227

145228
### Test Database Utilities

.github/instructions/testing.instructions.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,33 @@ Follow these testing patterns for the turn-based games platform:
3131
})
3232
```
3333

34+
## Shared Testing Utilities
35+
36+
**Use centralized mock data and test utilities from the shared package:**
37+
38+
```typescript
39+
// ✅ Import shared testing utilities
40+
import {
41+
mockTicTacToeGameState,
42+
mockRPSGameState,
43+
createMockGameSession,
44+
setupTestDatabase,
45+
clearTestDatabase
46+
} from '@turn-based-mcp/shared/testing'
47+
48+
// ✅ Use shared constants in tests
49+
import { DIFFICULTIES, GAME_TYPES } from '@turn-based-mcp/shared'
50+
51+
// ❌ Don't recreate mock data locally
52+
const localMockGameState = { /* duplicated data */ } // Use shared mocks instead!
53+
```
54+
55+
**Available shared testing utilities:**
56+
- Mock game states: `mockTicTacToeGameState`, `mockRPSGameState`
57+
- Factory functions: `createMockGameSession`, `createMockPlayer`
58+
- Database utilities: `setupTestDatabase`, `clearTestDatabase`, `teardownTestDatabase`
59+
- Type assertions and validation helpers
60+
3461
## Component Testing
3562

3663
- Always render components with realistic props

.github/instructions/typescript.instructions.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,37 @@ Follow these TypeScript patterns for consistent, type-safe code:
1515
- Use barrel exports (`index.ts`) for clean imports
1616
- Re-export shared types from `@turn-based-mcp/shared`
1717

18+
### Shared Types and Constants
19+
**Always import types derived from shared constants - don't duplicate union types:**
20+
21+
```typescript
22+
// ✅ Import types derived from constants
23+
import type { Difficulty, GameType, PlayerId } from '@turn-based-mcp/shared'
24+
import { DIFFICULTIES, DEFAULT_AI_DIFFICULTY, GAME_TYPES, PLAYER_IDS } from '@turn-based-mcp/shared'
25+
26+
// ✅ Use the imported types
27+
const [aiDifficulty, setAiDifficulty] = useState<Difficulty>('medium')
28+
const playerIds: PlayerId[] = Object.values(PLAYER_IDS)
29+
30+
// ❌ Don't define duplicate union types
31+
type Difficulty = 'easy' | 'medium' | 'hard' // This duplicates shared constants!
32+
type PlayerId = 'player1' | 'player2' | 'ai' // Use the derived type instead!
33+
```
34+
35+
**Key principle: Types are derived from constants using `as const` assertions:**
36+
```typescript
37+
// In shared/src/constants/game-constants.ts
38+
export const DIFFICULTIES = ['easy', 'medium', 'hard'] as const
39+
export type Difficulty = typeof DIFFICULTIES[number] // 'easy' | 'medium' | 'hard'
40+
```
41+
42+
**Common types available from shared package:**
43+
- `Difficulty` - AI difficulty levels (derived from `DIFFICULTIES`)
44+
- `GameType` - Supported game types (derived from `GAME_TYPES`)
45+
- `PlayerId` - Player identifiers (derived from `PLAYER_IDS`)
46+
- `GameStatus` - Game state values (derived from `GAME_STATUSES`)
47+
- Game-specific interfaces: `TicTacToeGameState`, `RPSGameState`, etc.
48+
1849
### Interface Design
1950
- Use interfaces for object shapes and component props
2051
- Include JSDoc comments for complex properties

.github/workflows/ci.yml

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,30 @@ jobs:
3131
run: npm ci
3232

3333
- name: Build shared package
34-
run: npm run build --workspace=shared
34+
run: npm run build --workspace=@turn-based-mcp/shared
35+
36+
- name: Verify shared build artifacts
37+
run: |
38+
test -f shared/dist/index.js || (echo 'shared/dist/index.js missing' && exit 1)
39+
test -f shared/dist/testing/index.js || (echo 'shared/dist/testing/index.js missing' && exit 1)
3540
3641
- name: Type check all workspaces
3742
run: npm run type-check
3843

3944
- name: Build web package
40-
run: npm run build --workspace=web
45+
run: npm run build --workspace=@turn-based-mcp/web
4146

4247
- name: Build mcp-server package
43-
run: npm run build --workspace=mcp-server
44-
45-
- name: Lint web package
46-
run: npm run lint --workspace=web
47-
48-
# Future: Add test steps when tests are implemented
49-
# - name: Run tests - shared
50-
# run: npm run test --workspace=shared
51-
#
52-
# - name: Run tests - web
53-
# run: npm run test --workspace=web
54-
#
55-
# - name: Run tests - mcp-server
56-
# run: npm run test --workspace=mcp-server
48+
run: npm run build --workspace=@turn-based-mcp/mcp-server
49+
50+
- name: Run tests - shared
51+
run: npm run test --workspace=@turn-based-mcp/shared
52+
53+
- name: Run tests - web
54+
run: npm run test --workspace=@turn-based-mcp/web
55+
56+
- name: Run tests - mcp-server
57+
run: npm run test --workspace=@turn-based-mcp/mcp-server
58+
59+
- name: Lint all packages
60+
run: npm run lint:all

.vscode/mcp.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"servers": {
33
"turn-based-games": {
44
"command": "node",
5-
"args": ["dist/index.js"],
5+
"args": ["dist/server.js"],
66
"cwd": "./mcp-server"
77
},
88
"playwright": {

eslint.config.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// @ts-check
2+
3+
import eslint from '@eslint/js';
4+
import tseslint from 'typescript-eslint';
5+
6+
export default tseslint.config(
7+
eslint.configs.recommended,
8+
...tseslint.configs.recommended,
9+
{
10+
files: ['**/*.{js,mjs,cjs,ts}'],
11+
languageOptions: {
12+
parserOptions: {
13+
ecmaVersion: 'latest',
14+
sourceType: 'module',
15+
},
16+
},
17+
rules: {
18+
// Add any global rules here
19+
},
20+
},
21+
{
22+
ignores: [
23+
'**/node_modules/**',
24+
'**/dist/**',
25+
'**/.next/**',
26+
'**/coverage/**',
27+
'**/*.config.js',
28+
'**/*.config.ts',
29+
],
30+
}
31+
);

mcp-server/eslint.config.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// @ts-check
2+
3+
import eslint from '@eslint/js';
4+
import tseslint from 'typescript-eslint';
5+
6+
export default tseslint.config(
7+
eslint.configs.recommended,
8+
...tseslint.configs.recommended,
9+
{
10+
files: ['**/*.{js,mjs,cjs,ts}'],
11+
languageOptions: {
12+
parserOptions: {
13+
ecmaVersion: 'latest',
14+
sourceType: 'module',
15+
},
16+
},
17+
rules: {
18+
// MCP server specific rules
19+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
20+
'@typescript-eslint/explicit-function-return-type': 'warn',
21+
'@typescript-eslint/no-explicit-any': 'warn',
22+
'no-console': 'off', // Console logging is often needed for MCP servers
23+
},
24+
},
25+
{
26+
files: ['**/*.test.ts', 'vitest.*.ts'],
27+
rules: {
28+
'@typescript-eslint/explicit-function-return-type': 'off',
29+
'@typescript-eslint/no-explicit-any': 'off',
30+
},
31+
},
32+
{
33+
ignores: [
34+
'node_modules/**',
35+
'dist/**',
36+
'coverage/**',
37+
'*.config.js',
38+
'*.config.ts',
39+
],
40+
}
41+
);

mcp-server/jest.config.cjs

Lines changed: 0 additions & 39 deletions
This file was deleted.

mcp-server/jest.setup.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)