@gui-chat-plugin/tictactoe 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.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # GUIChatPluginTicTacToe
2
+
3
+ Tic-Tac-Toe game plugin for GUI Chat.
4
+
5
+ ## Development
6
+
7
+ ### 1. Plugin単体でデバッグ
8
+
9
+ ```bash
10
+ yarn install
11
+ yarn run dev
12
+ ```
13
+
14
+ ブラウザで http://localhost:5173/ を開く
15
+
16
+ ### 2. MulmoChatでデバッグ
17
+
18
+ **初回セットアップ(MulmoChat側):**
19
+
20
+ 1. `MulmoChat/package.json` に依存関係を追加:
21
+ ```json
22
+ "@gui-chat-plugin/tictactoe": "file:../GUIChatPluginTicTacToe",
23
+ ```
24
+
25
+ 2. `MulmoChat/src/main.ts` にCSS importを追加:
26
+ ```typescript
27
+ import "@gui-chat-plugin/tictactoe/style.css";
28
+ ```
29
+
30
+ 3. `MulmoChat/src/tools/index.ts` にプラグインを登録:
31
+ ```typescript
32
+ import TicTacToePlugin from "@gui-chat-plugin/tictactoe/vue";
33
+ // pluginList に TicTacToePlugin を追加
34
+ ```
35
+
36
+ **デバッグ実行:**
37
+ ```bash
38
+ # プラグインをビルドしてMulmoChatに反映
39
+ ./refresh-in-mulmochat.sh
40
+
41
+ # MulmoChatを起動
42
+ cd ../MulmoChat
43
+ yarn run dev
44
+ ```
45
+
46
+ ### 3. チェックスクリプト
47
+
48
+ ```bash
49
+ # プラグインファイル構成チェック
50
+ ./check-plugin-structure.sh
51
+
52
+ # MulmoChat統合チェック(CSS, 依存関係, 登録)
53
+ ./check-mulmochat-integration.sh
54
+ ```
55
+
56
+ ## Required Files (24 files)
57
+
58
+ ```
59
+ 【ルート設定ファイル (7)】
60
+ .gitignore
61
+ package.json
62
+ tsconfig.json
63
+ tsconfig.build.json
64
+ vite.config.ts
65
+ eslint.config.js
66
+ index.html
67
+
68
+ 【ソースエントリ (2)】
69
+ src/index.ts
70
+ src/style.css
71
+
72
+ 【Coreモジュール (6)】
73
+ src/core/index.ts
74
+ src/core/types.ts
75
+ src/core/definition.ts
76
+ src/core/logic.ts
77
+ src/core/plugin.ts
78
+ src/core/samples.ts
79
+
80
+ 【Vueモジュール (3)】
81
+ src/vue/index.ts
82
+ src/vue/View.vue
83
+ src/vue/Preview.vue
84
+
85
+ 【デモファイル (2)】
86
+ demo/main.ts
87
+ demo/App.vue
88
+ ```
89
+
90
+ ## Scripts
91
+
92
+ | Command | Description |
93
+ |---------|-------------|
94
+ | `yarn dev` | 開発サーバー起動 |
95
+ | `yarn build` | プロダクションビルド |
96
+ | `yarn typecheck` | TypeScriptチェック |
97
+ | `yarn lint` | ESLintチェック |
98
+ | `./check-plugin-structure.sh` | ファイル構成チェック |
99
+ | `./check-mulmochat-integration.sh` | MulmoChat統合チェック |
100
+ | `./refresh-in-mulmochat.sh` | MulmoChatに反映 |
@@ -0,0 +1,7 @@
1
+ /**
2
+ * TicTacToe Plugin - Tool Definition
3
+ */
4
+ import type { ToolDefinition } from "gui-chat-protocol";
5
+ export declare const TOOL_NAME = "playTicTacToe";
6
+ export declare const TOOL_DEFINITION: ToolDefinition;
7
+ export declare const SYSTEM_PROMPT = "You can play Tic-Tac-Toe with users using the playTicTacToe tool.\n\nIMPORTANT: When the user specifies a move (e.g., \"I want to play at top-left, which is row=0, col=0\"), you MUST call the playTicTacToe tool with action=\"move\", NOT respond with text.\n\nGame rules:\n1. Start a new game with action=\"new_game\"\n2. For moves, use action=\"move\" with row (0-2) and col (0-2), plus the current board state\n3. X always goes first. Win by getting 3 in a row (horizontal, vertical, or diagonal)\n4. Positions: row=0 is top, row=2 is bottom; col=0 is left, col=2 is right";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * TicTacToe Plugin - Core (Framework-agnostic)
3
+ */
4
+ export type { Cell, Side, TicTacToeBoard, PlayerType, TicTacToeArgs, NewGameCommand, MoveCommand, Command, TicTacToeState, TicTacToeClickData, } from "./types";
5
+ export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, playTicTacToe, executeTicTacToe, pluginCore, } from "./plugin";
6
+ export { samples } from "./samples";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * TicTacToe Plugin - Game Logic
3
+ */
4
+ import type { Command, TicTacToeState } from "./types";
5
+ export declare function playTicTacToe(cmd: Command): TicTacToeState;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * TicTacToe Plugin Core
3
+ */
4
+ import type { ToolPluginCore, ToolContext, ToolResult } from "gui-chat-protocol";
5
+ import type { TicTacToeArgs, TicTacToeState } from "./types";
6
+ export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT } from "./definition";
7
+ export { playTicTacToe } from "./logic";
8
+ export declare const executeTicTacToe: (_context: ToolContext, args: TicTacToeArgs) => Promise<ToolResult<never, TicTacToeState>>;
9
+ export declare const pluginCore: ToolPluginCore<never, TicTacToeState, TicTacToeArgs>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * TicTacToe Plugin - Sample Data
3
+ */
4
+ import type { ToolSample } from "gui-chat-protocol";
5
+ export declare const samples: ToolSample[];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * TicTacToe Plugin - Type Definitions
3
+ */
4
+ export type Cell = "." | "X" | "O";
5
+ export type Side = "X" | "O";
6
+ export type TicTacToeBoard = Cell[][];
7
+ export type PlayerType = "user" | "computer";
8
+ export interface TicTacToeArgs {
9
+ action: "new_game" | "move";
10
+ col?: number;
11
+ row?: number;
12
+ board?: Cell[][];
13
+ currentSide?: Side;
14
+ playerNames?: {
15
+ X: PlayerType;
16
+ O: PlayerType;
17
+ };
18
+ firstPlayer?: PlayerType;
19
+ }
20
+ export type NewGameCommand = {
21
+ action: "new_game";
22
+ playerNames: {
23
+ X: string;
24
+ O: string;
25
+ };
26
+ };
27
+ export type MoveCommand = {
28
+ action: "move";
29
+ row: number;
30
+ col: number;
31
+ board: TicTacToeBoard;
32
+ currentSide: Side;
33
+ playerNames: {
34
+ X: string;
35
+ O: string;
36
+ };
37
+ };
38
+ export type Command = NewGameCommand | MoveCommand;
39
+ export interface TicTacToeState {
40
+ board: TicTacToeBoard;
41
+ currentSide: Side;
42
+ playerNames: {
43
+ X: string;
44
+ O: string;
45
+ };
46
+ legalMoves: {
47
+ row: number;
48
+ col: number;
49
+ }[];
50
+ counts: {
51
+ X: number;
52
+ O: number;
53
+ empty: number;
54
+ };
55
+ isTerminal: boolean;
56
+ winner: Side | "draw" | null;
57
+ winningLine: {
58
+ row: number;
59
+ col: number;
60
+ }[] | null;
61
+ lastAction: {
62
+ type: "new_game";
63
+ } | {
64
+ type: "move";
65
+ row: number;
66
+ col: number;
67
+ };
68
+ error?: string;
69
+ }
70
+ /**
71
+ * Data passed from handleCellClick for demo testing
72
+ */
73
+ export interface TicTacToeClickData {
74
+ row: number;
75
+ col: number;
76
+ currentState: TicTacToeState;
77
+ }
package/dist/core.cjs ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y="playTicTacToe",T={type:"function",name:y,description:"Play Tic-Tac-Toe (Noughts and Crosses) game with the user. You can start a new game or make moves on the 3x3 board.",parameters:{type:"object",properties:{action:{type:"string",enum:["new_game","move"],description:"The action to perform: start a new game or make a move"},col:{type:"number",description:"Column position for the move (0-2, required for 'move' action). Left=0, Center=1, Right=2",minimum:0,maximum:2},row:{type:"number",description:"Row position for the move (0-2, required for 'move' action). Top=0, Middle=1, Bottom=2",minimum:0,maximum:2},board:{type:"array",description:"Current 3x3 board state BEFORE the move (required for 'move' action). Pass the current board state as-is.",items:{type:"array",items:{type:"string",enum:[".","X","O"]}}},currentSide:{type:"string",enum:["X","O"],description:"Current player's side (required for 'move' action). X always goes first."},playerNames:{type:"object",description:"Player assignments (required for 'move' action)",properties:{X:{type:"string",enum:["user","computer"]},O:{type:"string",enum:["user","computer"]}},required:["X","O"]},firstPlayer:{type:"string",enum:["user","computer"],description:"Optional: Which player should play as X (goes first) for 'new_game' action. If not specified, will be chosen randomly."}},required:["action"],additionalProperties:!1}},f=`You can play Tic-Tac-Toe with users using the playTicTacToe tool.
2
+
3
+ IMPORTANT: When the user specifies a move (e.g., "I want to play at top-left, which is row=0, col=0"), you MUST call the playTicTacToe tool with action="move", NOT respond with text.
4
+
5
+ Game rules:
6
+ 1. Start a new game with action="new_game"
7
+ 2. For moves, use action="move" with row (0-2) and col (0-2), plus the current board state
8
+ 3. X always goes first. Win by getting 3 in a row (horizontal, vertical, or diagonal)
9
+ 4. Positions: row=0 is top, row=2 is bottom; col=0 is left, col=2 is right`,M=[[{row:0,col:0},{row:0,col:1},{row:0,col:2}],[{row:1,col:0},{row:1,col:1},{row:1,col:2}],[{row:2,col:0},{row:2,col:1},{row:2,col:2}],[{row:0,col:0},{row:1,col:0},{row:2,col:0}],[{row:0,col:1},{row:1,col:1},{row:2,col:1}],[{row:0,col:2},{row:1,col:2},{row:2,col:2}],[{row:0,col:0},{row:1,col:1},{row:2,col:2}],[{row:0,col:2},{row:1,col:1},{row:2,col:0}]];function P(){const r=[];for(let o=0;o<3;o++)r[o]=new Array(3).fill(".");return r}function S(r){return r.map(o=>[...o])}function I(r){return r==="X"?"O":"X"}function p(r){const o=[];for(let t=0;t<3;t++)for(let e=0;e<3;e++)r[t][e]==="."&&o.push({row:t,col:e});return o}function L(r,o,t,e){const n=S(r);return n[o][t]=e,n}function u(r){let o=0,t=0,e=0;for(let n=0;n<3;n++)for(let i=0;i<3;i++){const s=r[n][i];s==="X"?o++:s==="O"?t++:e++}return{X:o,O:t,empty:e}}function _(r){for(const o of M){const t=o.map(e=>r[e.row][e.col]);if(t[0]!=="."&&t[0]===t[1]&&t[1]===t[2])return{winner:t[0],winningLine:o}}return{winner:null,winningLine:null}}function X(r){const{winner:o,winningLine:t}=_(r);return o?{isTerminal:!0,winner:o,winningLine:t}:u(r).empty===0?{isTerminal:!0,winner:"draw",winningLine:null}:{isTerminal:!1,winner:null,winningLine:null}}function g(r){if(r.action==="new_game"){const{playerNames:m}=r,c=P(),N=p(c),O=u(c);return{board:c,currentSide:"X",playerNames:m,legalMoves:N,counts:O,isTerminal:!1,winner:null,winningLine:null,lastAction:{type:"new_game"}}}const{row:o,col:t,board:e,currentSide:n,playerNames:i}=r;if(o<0||o>2||t<0||t>2){const m=p(e),c=u(e);return{board:e,currentSide:n,playerNames:i,legalMoves:m,counts:c,isTerminal:!1,winner:null,winningLine:null,lastAction:{type:"move",row:o,col:t},error:`Invalid move: (${o}, ${t}) is out of bounds. Row and column must be 0-2.`}}if(e[o][t]!=="."){const m=p(e),c=u(e);return{board:e,currentSide:n,playerNames:i,legalMoves:m,counts:c,isTerminal:!1,winner:null,winningLine:null,lastAction:{type:"move",row:o,col:t},error:`Invalid move: (${o}, ${t}) is already occupied by ${e[o][t]}.`}}const s=L(e,o,t,n),a=I(n),l=p(s),d=u(s),{isTerminal:w,winner:h,winningLine:b}=X(s);return{board:s,currentSide:a,playerNames:i,legalMoves:l,counts:d,isTerminal:w,winner:h,winningLine:b,lastAction:{type:"move",row:o,col:t}}}const v=async(r,o)=>{try{let t;if(o.action==="new_game"){let a;o.firstPlayer?a=o.firstPlayer:a=Math.random()<.5?"computer":"user",t={action:"new_game",playerNames:{X:a,O:a==="user"?"computer":"user"}}}else if(o.action==="move"){if(typeof o.row!="number"||typeof o.col!="number"||!o.board||!o.currentSide||!o.playerNames)throw new Error("Move action requires row, col, board, currentSide, and playerNames parameters");t={action:"move",row:o.row,col:o.col,board:o.board,currentSide:o.currentSide,playerNames:o.playerNames}}else throw new Error(`Unknown action: ${o.action}`);const e=g(t);if(e.error){const a=e.playerNames[e.currentSide]==="computer",l=e.legalMoves.map(w=>`(row=${w.row}, col=${w.col})`).join(", "),d=a?`Invalid move attempted. You must make a valid move. Legal moves are: ${l}. Choose one of these moves.`:`Invalid move attempted. Tell the user they must make a valid move. Legal moves are: ${l}.`;return{message:e.error,jsonData:e,instructions:d,updating:!0}}let n="";if(e.lastAction.type==="new_game")n="Started a new Tic-Tac-Toe game! X goes first.";else if(e.lastAction.type==="move"){const a=$(e.lastAction.row,e.lastAction.col);n=`Played ${e.board[e.lastAction.row][e.lastAction.col]==="X"?"O":"X"} at ${a} (row=${e.lastAction.row}, col=${e.lastAction.col}).`}e.isTerminal&&(e.winner==="draw"?n+=" Game over - it's a draw!":e.winner&&(n+=` Game over - ${e.winner} wins!`));const i=e.playerNames[e.currentSide]==="computer",s=e.isTerminal?"The game is over. Briefly announce the result. Do NOT draw the board - the user can see it in the GUI.":i?"It is your turn. You MUST call playTicTacToe with action='move' immediately. Do NOT describe the board or explain - just make your move.":"It is the user's turn. Do NOT draw or describe the board - the user can see it in the GUI. Just say it's their turn briefly. When they specify a move, call playTicTacToe with action='move'.";return{message:n,jsonData:e,instructions:s,instructionsRequired:e.isTerminal||i,updating:o.action!=="new_game"}}catch(t){return console.error("ERR: TicTacToe game error",t),{message:`TicTacToe game error: ${t instanceof Error?t.message:"Unknown error"}`,instructions:"Acknowledge that there was an error with the TicTacToe game and suggest trying again."}}};function $(r,o){const t=["Top","Middle","Bottom"],e=["Left","Center","Right"];return`${t[r]}-${e[o]}`}const A={toolDefinition:T,execute:v,generatingMessage:"Processing TicTacToe move...",isEnabled:()=>!0,systemPrompt:f},E=[{name:"New Game (User plays X)",args:{action:"new_game",firstPlayer:"user"}},{name:"New Game (Computer plays X)",args:{action:"new_game",firstPlayer:"computer"}}];exports.SYSTEM_PROMPT=f;exports.TOOL_DEFINITION=T;exports.TOOL_NAME=y;exports.executeTicTacToe=v;exports.playTicTacToe=g;exports.pluginCore=A;exports.samples=E;
package/dist/core.js ADDED
@@ -0,0 +1,281 @@
1
+ const h = "playTicTacToe", v = {
2
+ type: "function",
3
+ name: h,
4
+ description: "Play Tic-Tac-Toe (Noughts and Crosses) game with the user. You can start a new game or make moves on the 3x3 board.",
5
+ parameters: {
6
+ type: "object",
7
+ properties: {
8
+ action: {
9
+ type: "string",
10
+ enum: ["new_game", "move"],
11
+ description: "The action to perform: start a new game or make a move"
12
+ },
13
+ col: {
14
+ type: "number",
15
+ description: "Column position for the move (0-2, required for 'move' action). Left=0, Center=1, Right=2",
16
+ minimum: 0,
17
+ maximum: 2
18
+ },
19
+ row: {
20
+ type: "number",
21
+ description: "Row position for the move (0-2, required for 'move' action). Top=0, Middle=1, Bottom=2",
22
+ minimum: 0,
23
+ maximum: 2
24
+ },
25
+ board: {
26
+ type: "array",
27
+ description: "Current 3x3 board state BEFORE the move (required for 'move' action). Pass the current board state as-is.",
28
+ items: {
29
+ type: "array",
30
+ items: {
31
+ type: "string",
32
+ enum: [".", "X", "O"]
33
+ }
34
+ }
35
+ },
36
+ currentSide: {
37
+ type: "string",
38
+ enum: ["X", "O"],
39
+ description: "Current player's side (required for 'move' action). X always goes first."
40
+ },
41
+ playerNames: {
42
+ type: "object",
43
+ description: "Player assignments (required for 'move' action)",
44
+ properties: {
45
+ X: {
46
+ type: "string",
47
+ enum: ["user", "computer"]
48
+ },
49
+ O: {
50
+ type: "string",
51
+ enum: ["user", "computer"]
52
+ }
53
+ },
54
+ required: ["X", "O"]
55
+ },
56
+ firstPlayer: {
57
+ type: "string",
58
+ enum: ["user", "computer"],
59
+ description: "Optional: Which player should play as X (goes first) for 'new_game' action. If not specified, will be chosen randomly."
60
+ }
61
+ },
62
+ required: ["action"],
63
+ additionalProperties: !1
64
+ }
65
+ }, b = `You can play Tic-Tac-Toe with users using the playTicTacToe tool.
66
+
67
+ IMPORTANT: When the user specifies a move (e.g., "I want to play at top-left, which is row=0, col=0"), you MUST call the playTicTacToe tool with action="move", NOT respond with text.
68
+
69
+ Game rules:
70
+ 1. Start a new game with action="new_game"
71
+ 2. For moves, use action="move" with row (0-2) and col (0-2), plus the current board state
72
+ 3. X always goes first. Win by getting 3 in a row (horizontal, vertical, or diagonal)
73
+ 4. Positions: row=0 is top, row=2 is bottom; col=0 is left, col=2 is right`, N = [
74
+ // Rows
75
+ [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 0, col: 2 }],
76
+ [{ row: 1, col: 0 }, { row: 1, col: 1 }, { row: 1, col: 2 }],
77
+ [{ row: 2, col: 0 }, { row: 2, col: 1 }, { row: 2, col: 2 }],
78
+ // Columns
79
+ [{ row: 0, col: 0 }, { row: 1, col: 0 }, { row: 2, col: 0 }],
80
+ [{ row: 0, col: 1 }, { row: 1, col: 1 }, { row: 2, col: 1 }],
81
+ [{ row: 0, col: 2 }, { row: 1, col: 2 }, { row: 2, col: 2 }],
82
+ // Diagonals
83
+ [{ row: 0, col: 0 }, { row: 1, col: 1 }, { row: 2, col: 2 }],
84
+ [{ row: 0, col: 2 }, { row: 1, col: 1 }, { row: 2, col: 0 }]
85
+ ];
86
+ function O() {
87
+ const n = [];
88
+ for (let o = 0; o < 3; o++)
89
+ n[o] = new Array(3).fill(".");
90
+ return n;
91
+ }
92
+ function M(n) {
93
+ return n.map((o) => [...o]);
94
+ }
95
+ function P(n) {
96
+ return n === "X" ? "O" : "X";
97
+ }
98
+ function p(n) {
99
+ const o = [];
100
+ for (let t = 0; t < 3; t++)
101
+ for (let e = 0; e < 3; e++)
102
+ n[t][e] === "." && o.push({ row: t, col: e });
103
+ return o;
104
+ }
105
+ function S(n, o, t, e) {
106
+ const r = M(n);
107
+ return r[o][t] = e, r;
108
+ }
109
+ function u(n) {
110
+ let o = 0, t = 0, e = 0;
111
+ for (let r = 0; r < 3; r++)
112
+ for (let i = 0; i < 3; i++) {
113
+ const s = n[r][i];
114
+ s === "X" ? o++ : s === "O" ? t++ : e++;
115
+ }
116
+ return { X: o, O: t, empty: e };
117
+ }
118
+ function X(n) {
119
+ for (const o of N) {
120
+ const t = o.map((e) => n[e.row][e.col]);
121
+ if (t[0] !== "." && t[0] === t[1] && t[1] === t[2])
122
+ return { winner: t[0], winningLine: o };
123
+ }
124
+ return { winner: null, winningLine: null };
125
+ }
126
+ function $(n) {
127
+ const { winner: o, winningLine: t } = X(n);
128
+ return o ? { isTerminal: !0, winner: o, winningLine: t } : u(n).empty === 0 ? { isTerminal: !0, winner: "draw", winningLine: null } : { isTerminal: !1, winner: null, winningLine: null };
129
+ }
130
+ function I(n) {
131
+ if (n.action === "new_game") {
132
+ const { playerNames: m } = n, c = O(), T = p(c), g = u(c);
133
+ return {
134
+ board: c,
135
+ currentSide: "X",
136
+ // X always goes first
137
+ playerNames: m,
138
+ legalMoves: T,
139
+ counts: g,
140
+ isTerminal: !1,
141
+ winner: null,
142
+ winningLine: null,
143
+ lastAction: { type: "new_game" }
144
+ };
145
+ }
146
+ const { row: o, col: t, board: e, currentSide: r, playerNames: i } = n;
147
+ if (o < 0 || o > 2 || t < 0 || t > 2) {
148
+ const m = p(e), c = u(e);
149
+ return {
150
+ board: e,
151
+ currentSide: r,
152
+ playerNames: i,
153
+ legalMoves: m,
154
+ counts: c,
155
+ isTerminal: !1,
156
+ winner: null,
157
+ winningLine: null,
158
+ lastAction: { type: "move", row: o, col: t },
159
+ error: `Invalid move: (${o}, ${t}) is out of bounds. Row and column must be 0-2.`
160
+ };
161
+ }
162
+ if (e[o][t] !== ".") {
163
+ const m = p(e), c = u(e);
164
+ return {
165
+ board: e,
166
+ currentSide: r,
167
+ playerNames: i,
168
+ legalMoves: m,
169
+ counts: c,
170
+ isTerminal: !1,
171
+ winner: null,
172
+ winningLine: null,
173
+ lastAction: { type: "move", row: o, col: t },
174
+ error: `Invalid move: (${o}, ${t}) is already occupied by ${e[o][t]}.`
175
+ };
176
+ }
177
+ const s = S(e, o, t, r), a = P(r), l = p(s), d = u(s), { isTerminal: w, winner: y, winningLine: f } = $(s);
178
+ return {
179
+ board: s,
180
+ currentSide: a,
181
+ playerNames: i,
182
+ legalMoves: l,
183
+ counts: d,
184
+ isTerminal: w,
185
+ winner: y,
186
+ winningLine: f,
187
+ lastAction: { type: "move", row: o, col: t }
188
+ };
189
+ }
190
+ const L = async (n, o) => {
191
+ try {
192
+ let t;
193
+ if (o.action === "new_game") {
194
+ let a;
195
+ o.firstPlayer ? a = o.firstPlayer : a = Math.random() < 0.5 ? "computer" : "user", t = {
196
+ action: "new_game",
197
+ playerNames: { X: a, O: a === "user" ? "computer" : "user" }
198
+ };
199
+ } else if (o.action === "move") {
200
+ if (typeof o.row != "number" || typeof o.col != "number" || !o.board || !o.currentSide || !o.playerNames)
201
+ throw new Error(
202
+ "Move action requires row, col, board, currentSide, and playerNames parameters"
203
+ );
204
+ t = {
205
+ action: "move",
206
+ row: o.row,
207
+ col: o.col,
208
+ board: o.board,
209
+ currentSide: o.currentSide,
210
+ playerNames: o.playerNames
211
+ };
212
+ } else
213
+ throw new Error(`Unknown action: ${o.action}`);
214
+ const e = I(t);
215
+ if (e.error) {
216
+ const a = e.playerNames[e.currentSide] === "computer", l = e.legalMoves.map((w) => `(row=${w.row}, col=${w.col})`).join(", "), d = a ? `Invalid move attempted. You must make a valid move. Legal moves are: ${l}. Choose one of these moves.` : `Invalid move attempted. Tell the user they must make a valid move. Legal moves are: ${l}.`;
217
+ return {
218
+ message: e.error,
219
+ jsonData: e,
220
+ instructions: d,
221
+ updating: !0
222
+ };
223
+ }
224
+ let r = "";
225
+ if (e.lastAction.type === "new_game")
226
+ r = "Started a new Tic-Tac-Toe game! X goes first.";
227
+ else if (e.lastAction.type === "move") {
228
+ const a = A(e.lastAction.row, e.lastAction.col);
229
+ r = `Played ${e.board[e.lastAction.row][e.lastAction.col] === "X" ? "O" : "X"} at ${a} (row=${e.lastAction.row}, col=${e.lastAction.col}).`;
230
+ }
231
+ e.isTerminal && (e.winner === "draw" ? r += " Game over - it's a draw!" : e.winner && (r += ` Game over - ${e.winner} wins!`));
232
+ const i = e.playerNames[e.currentSide] === "computer", s = e.isTerminal ? "The game is over. Briefly announce the result. Do NOT draw the board - the user can see it in the GUI." : i ? "It is your turn. You MUST call playTicTacToe with action='move' immediately. Do NOT describe the board or explain - just make your move." : "It is the user's turn. Do NOT draw or describe the board - the user can see it in the GUI. Just say it's their turn briefly. When they specify a move, call playTicTacToe with action='move'.";
233
+ return {
234
+ message: r,
235
+ jsonData: e,
236
+ instructions: s,
237
+ instructionsRequired: e.isTerminal || i,
238
+ updating: o.action !== "new_game"
239
+ };
240
+ } catch (t) {
241
+ return console.error("ERR: TicTacToe game error", t), {
242
+ message: `TicTacToe game error: ${t instanceof Error ? t.message : "Unknown error"}`,
243
+ instructions: "Acknowledge that there was an error with the TicTacToe game and suggest trying again."
244
+ };
245
+ }
246
+ };
247
+ function A(n, o) {
248
+ const t = ["Top", "Middle", "Bottom"], e = ["Left", "Center", "Right"];
249
+ return `${t[n]}-${e[o]}`;
250
+ }
251
+ const _ = {
252
+ toolDefinition: v,
253
+ execute: L,
254
+ generatingMessage: "Processing TicTacToe move...",
255
+ isEnabled: () => !0,
256
+ systemPrompt: b
257
+ }, E = [
258
+ {
259
+ name: "New Game (User plays X)",
260
+ args: {
261
+ action: "new_game",
262
+ firstPlayer: "user"
263
+ }
264
+ },
265
+ {
266
+ name: "New Game (Computer plays X)",
267
+ args: {
268
+ action: "new_game",
269
+ firstPlayer: "computer"
270
+ }
271
+ }
272
+ ];
273
+ export {
274
+ b as SYSTEM_PROMPT,
275
+ v as TOOL_DEFINITION,
276
+ h as TOOL_NAME,
277
+ L as executeTicTacToe,
278
+ I as playTicTacToe,
279
+ _ as pluginCore,
280
+ E as samples
281
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./core.cjs");exports.SYSTEM_PROMPT=e.SYSTEM_PROMPT;exports.TOOL_DEFINITION=e.TOOL_DEFINITION;exports.TOOL_NAME=e.TOOL_NAME;exports.executeTicTacToe=e.executeTicTacToe;exports.playTicTacToe=e.playTicTacToe;exports.pluginCore=e.pluginCore;exports.samples=e.samples;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TicTacToe Plugin - Main Entry Point
3
+ */
4
+ export * from "./core";
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ import { SYSTEM_PROMPT as O, TOOL_DEFINITION as c, TOOL_NAME as o, executeTicTacToe as a, playTicTacToe as p, pluginCore as i, samples as l } from "./core.js";
2
+ export {
3
+ O as SYSTEM_PROMPT,
4
+ c as TOOL_DEFINITION,
5
+ o as TOOL_NAME,
6
+ a as executeTicTacToe,
7
+ p as playTicTacToe,
8
+ i as pluginCore,
9
+ l as samples
10
+ };
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-600:oklch(57.7% .245 27.325);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-400:oklch(85.2% .199 91.936);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-600:oklch(54.6% .245 262.881);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-200:oklch(87% .065 274.039);--color-indigo-700:oklch(45.7% .24 277.023);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-white:#fff;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.mx-auto{margin-inline:auto}.mt-4{margin-top:calc(var(--spacing)*4)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-5{margin-bottom:calc(var(--spacing)*5)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.flex{display:flex}.grid{display:grid}.inline-block{display:inline-block}.h-4{height:calc(var(--spacing)*4)}.h-6{height:calc(var(--spacing)*6)}.h-16{height:calc(var(--spacing)*16)}.h-24{height:calc(var(--spacing)*24)}.h-\[500px\]{height:500px}.h-full{height:100%}.w-4{width:calc(var(--spacing)*4)}.w-6{width:calc(var(--spacing)*6)}.w-16{width:calc(var(--spacing)*16)}.w-24{width:calc(var(--spacing)*24)}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[200px\]{max-width:200px}.cursor-pointer{cursor:pointer}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-8{gap:calc(var(--spacing)*8)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.border-gray-600{border-color:var(--color-gray-600)}.border-indigo-200{border-color:var(--color-indigo-200)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-600{background-color:var(--color-gray-600)}.bg-gray-700{background-color:var(--color-gray-700)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-indigo-100{background-color:var(--color-indigo-100)}.bg-white{background-color:var(--color-white)}.bg-yellow-200{background-color:var(--color-yellow-200)}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-8{padding:calc(var(--spacing)*8)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-2{padding-block:calc(var(--spacing)*2)}.text-center{text-align:center}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-blue-400{color:var(--color-blue-400)}.text-blue-600{color:var(--color-blue-600)}.text-gray-200{color:var(--color-gray-200)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-indigo-700{color:var(--color-indigo-700)}.text-red-400{color:var(--color-red-400)}.text-red-600{color:var(--color-red-600)}.opacity-30{opacity:.3}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-yellow-400{--tw-ring-color:var(--color-yellow-400)}@media(hover:hover){.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-indigo-200:hover{background-color:var(--color-indigo-200)}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
@@ -0,0 +1,8 @@
1
+ import type { ToolResult } from "gui-chat-protocol/vue";
2
+ import type { TicTacToeState } from "../core/types";
3
+ type __VLS_Props = {
4
+ result: ToolResult<never, TicTacToeState>;
5
+ };
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import type { ToolResult, SendTextMessageOptions } from "gui-chat-protocol/vue";
2
+ import type { TicTacToeState } from "../core/types";
3
+ type __VLS_Props = {
4
+ selectedResult: ToolResult<never, TicTacToeState> | null;
5
+ sendTextMessage?: (text: string, options?: SendTextMessageOptions) => void;
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * TicTacToe Plugin - Vue Implementation
3
+ */
4
+ import "../style.css";
5
+ import type { ToolPlugin } from "gui-chat-protocol/vue";
6
+ import type { TicTacToeArgs, TicTacToeState } from "../core/types";
7
+ import View from "./View.vue";
8
+ import Preview from "./Preview.vue";
9
+ export declare const plugin: ToolPlugin<never, TicTacToeState, TicTacToeArgs>;
10
+ export type { Cell, Side, TicTacToeBoard, PlayerType, TicTacToeArgs, NewGameCommand, MoveCommand, Command, TicTacToeState, TicTacToeClickData, } from "../core/types";
11
+ export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, playTicTacToe, executeTicTacToe, pluginCore, } from "../core/plugin";
12
+ export { samples } from "../core/samples";
13
+ export { View, Preview };
14
+ declare const _default: {
15
+ plugin: ToolPlugin<never, TicTacToeState, TicTacToeArgs, import("gui-chat-protocol/vue").InputHandler, Record<string, unknown>>;
16
+ };
17
+ export default _default;
package/dist/vue.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const i=require("./core.cjs"),e=require("vue"),N={class:"w-full h-full flex flex-col items-center justify-center p-4 bg-gray-100"},T={key:0,class:"flex flex-col items-center"},C={key:0},h={key:1},V={class:"grid grid-cols-3 gap-1 p-2 bg-gray-700 rounded-lg shadow-lg"},b=["onClick","onMouseenter","onMouseleave"],_={key:0,class:"w-16 h-16 text-blue-600",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"3","stroke-linecap":"round"},D={key:1,class:"w-16 h-16 text-red-600",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"3"},S={key:1,cx:"12",cy:"12",r:"8"},O={class:"mt-4 flex gap-8 text-lg"},$={class:"flex items-center gap-2"},M={class:"text-gray-500 text-sm"},j={class:"flex items-center gap-2"},L={class:"text-gray-500 text-sm"},w=e.defineComponent({__name:"View",props:{selectedResult:{},sendTextMessage:{type:Function}},setup(a){const u=a,t=e.ref(null),v=e.ref(null);e.watch(()=>u.selectedResult,n=>{n?.toolName==="playTicTacToe"&&n.jsonData&&(t.value=n.jsonData)},{immediate:!0});const f=e.computed(()=>{if(!t.value?.playerNames)return"";const n=t.value.playerNames[t.value.currentSide];return n.charAt(0).toUpperCase()+n.slice(1)}),d=e.computed(()=>t.value?.playerNames&&t.value.playerNames[t.value.currentSide]==="computer"),s=e.computed(()=>t.value?t.value.isTerminal?t.value.winner==="draw"?"text-gray-600":t.value.winner==="X"?"text-blue-600":"text-red-600":t.value.currentSide==="X"?"text-blue-600":"text-red-600":""),c=e.computed(()=>{if(!t.value?.board)return[];const n=[];for(let l=0;l<3;l++)for(let o=0;o<3;o++){const r=t.value.board[l][o],p=t.value.legalMoves?.some(y=>y.row===l&&y.col===o),x=t.value.winningLine?.some(y=>y.row===l&&y.col===o);n.push({row:l,col:o,piece:r!=="."?r:null,isLegalMove:p??!1,isWinningCell:x??!1})}return n});function m(n,l){const o="w-24 h-24 flex items-center justify-center bg-white rounded";let r="";return n.isWinningCell?r="bg-yellow-200 ring-2 ring-yellow-400":n.isLegalMove&&!d.value&&!t.value?.isTerminal&&(r=v.value===l?"bg-gray-200 cursor-pointer":"hover:bg-gray-100 cursor-pointer"),`${o} ${r}`}function g(n){if(!t.value||t.value.isTerminal||d.value)return;const l=c.value[n];if(!l.isLegalMove)return;const o=["top","middle","bottom"],r=["left","center","right"],p=`${o[l.row]}-${r[l.col]}`,x={row:l.row,col:l.col,currentState:t.value};u.sendTextMessage?.(`I want to play at ${p}, which is row=${l.row}, col=${l.col}`,{data:x})}function k(n,l){!t.value||t.value.isTerminal||d.value||!c.value[n].isLegalMove||(v.value=l?n:null)}return(n,l)=>(e.openBlock(),e.createElementBlock("div",N,[t.value?(e.openBlock(),e.createElementBlock("div",T,[l[6]||(l[6]=e.createElementVNode("h1",{class:"text-2xl font-bold text-gray-800 mb-2"},"Tic-Tac-Toe",-1)),e.createElementVNode("div",{class:e.normalizeClass(["text-lg font-semibold mb-4 text-center",s.value])},[t.value.isTerminal?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[t.value.winner==="draw"?(e.openBlock(),e.createElementBlock("span",C,"It's a Draw!")):(e.openBlock(),e.createElementBlock("span",h,e.toDisplayString(t.value.winner)+" Wins!",1))],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createTextVNode(e.toDisplayString(f.value)+"'s Turn ("+e.toDisplayString(t.value.currentSide)+") ",1)],64))],2),e.createElementVNode("div",V,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(c.value,(o,r)=>(e.openBlock(),e.createElementBlock("div",{key:r,class:e.normalizeClass(m(o,r)),onClick:p=>g(r),onMouseenter:p=>k(r,!0),onMouseleave:p=>k(r,!1)},[o.piece==="X"?(e.openBlock(),e.createElementBlock("svg",_,[...l[0]||(l[0]=[e.createElementVNode("line",{x1:"4",y1:"4",x2:"20",y2:"20"},null,-1),e.createElementVNode("line",{x1:"20",y1:"4",x2:"4",y2:"20"},null,-1)])])):o.piece==="O"?(e.openBlock(),e.createElementBlock("svg",D,[...l[1]||(l[1]=[e.createElementVNode("circle",{cx:"12",cy:"12",r:"8"},null,-1)])])):o.isLegalMove&&!d.value&&v.value===r?(e.openBlock(),e.createElementBlock("svg",{key:2,class:e.normalizeClass(["w-16 h-16 opacity-30",t.value.currentSide==="X"?"text-blue-400":"text-red-400"]),viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"3","stroke-linecap":"round"},[t.value.currentSide==="X"?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[l[2]||(l[2]=e.createElementVNode("line",{x1:"4",y1:"4",x2:"20",y2:"20"},null,-1)),l[3]||(l[3]=e.createElementVNode("line",{x1:"20",y1:"4",x2:"4",y2:"20"},null,-1))],64)):(e.openBlock(),e.createElementBlock("circle",S))],2)):e.createCommentVNode("",!0)],42,b))),128))]),e.createElementVNode("div",O,[e.createElementVNode("div",$,[l[4]||(l[4]=e.createElementVNode("span",{class:"text-blue-600 font-bold"},"X:",-1)),e.createElementVNode("span",null,e.toDisplayString(t.value.counts.X),1),e.createElementVNode("span",M,"("+e.toDisplayString(t.value.playerNames.X)+")",1)]),e.createElementVNode("div",j,[l[5]||(l[5]=e.createElementVNode("span",{class:"text-red-600 font-bold"},"O:",-1)),e.createElementVNode("span",null,e.toDisplayString(t.value.counts.O),1),e.createElementVNode("span",L,"("+e.toDisplayString(t.value.playerNames.O)+")",1)])])])):e.createCommentVNode("",!0)]))}}),X={class:"p-3 bg-gray-50 rounded"},F={key:0,class:"space-y-2"},P={class:"flex justify-center"},z={class:"inline-block bg-gray-600 p-0.5 rounded"},W={class:"grid grid-cols-3 gap-0.5"},I={key:0,class:"w-4 h-4 text-blue-600",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"3","stroke-linecap":"round"},A={key:1,class:"w-4 h-4 text-red-600",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"3"},R={class:"text-xs text-center space-y-1"},q={key:0,class:"text-gray-600"},E=e.defineComponent({__name:"Preview",props:{result:{}},setup(a){const u=a;function t(s,c){return u.result.jsonData?.winningLine?.some(m=>m.row===s&&m.col===c)??!1}function v(s){return s.isTerminal?s.winner==="draw"?"Draw!":s.winner==="X"?"X Wins!":s.winner==="O"?"O Wins!":"Game Over":""}function f(){return u.result.jsonData?.isTerminal?u.result.jsonData.winner==="draw"?"text-gray-600":u.result.jsonData.winner==="X"?"text-blue-600":u.result.jsonData.winner==="O"?"text-red-600":"":""}function d(s){return s.charAt(0).toUpperCase()+s.slice(1)}return(s,c)=>(e.openBlock(),e.createElementBlock("div",X,[a.result.jsonData?(e.openBlock(),e.createElementBlock("div",F,[e.createElementVNode("div",P,[e.createElementVNode("div",z,[e.createElementVNode("div",W,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(a.result.jsonData.board,(m,g)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:g},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(m,(k,n)=>(e.openBlock(),e.createElementBlock("div",{key:`${g}-${n}`,class:e.normalizeClass(["w-6 h-6 flex items-center justify-center bg-white rounded-sm",t(g,n)?"bg-yellow-200":""])},[k==="X"?(e.openBlock(),e.createElementBlock("svg",I,[...c[0]||(c[0]=[e.createElementVNode("line",{x1:"4",y1:"4",x2:"20",y2:"20"},null,-1),e.createElementVNode("line",{x1:"20",y1:"4",x2:"4",y2:"20"},null,-1)])])):k==="O"?(e.openBlock(),e.createElementBlock("svg",A,[...c[1]||(c[1]=[e.createElementVNode("circle",{cx:"12",cy:"12",r:"8"},null,-1)])])):e.createCommentVNode("",!0)],2))),128))],64))),128))])])]),e.createElementVNode("div",R,[a.result.jsonData.isTerminal?(e.openBlock(),e.createElementBlock("div",{key:1,class:e.normalizeClass(["font-medium",f()])},e.toDisplayString(v(a.result.jsonData)),3)):(e.openBlock(),e.createElementBlock("div",q,[e.createElementVNode("span",{class:e.normalizeClass(a.result.jsonData.currentSide==="X"?"text-blue-600":"text-red-600")},e.toDisplayString(a.result.jsonData.currentSide),3),e.createTextVNode(" "+e.toDisplayString(d(a.result.jsonData.playerNames[a.result.jsonData.currentSide]))+" to play ",1)]))])])):e.createCommentVNode("",!0)]))}}),B={...i.pluginCore,viewComponent:w,previewComponent:E,samples:i.samples},G={plugin:B};exports.SYSTEM_PROMPT=i.SYSTEM_PROMPT;exports.TOOL_DEFINITION=i.TOOL_DEFINITION;exports.TOOL_NAME=i.TOOL_NAME;exports.executeTicTacToe=i.executeTicTacToe;exports.playTicTacToe=i.playTicTacToe;exports.pluginCore=i.pluginCore;exports.samples=i.samples;exports.Preview=E;exports.View=w;exports.default=G;exports.plugin=B;
package/dist/vue.js ADDED
@@ -0,0 +1,278 @@
1
+ import { samples as M, pluginCore as O } from "./core.js";
2
+ import { SYSTEM_PROMPT as ce, TOOL_DEFINITION as de, TOOL_NAME as ve, executeTicTacToe as ye, playTicTacToe as fe } from "./core.js";
3
+ import { defineComponent as j, ref as D, watch as X, computed as b, createElementBlock as n, openBlock as l, createCommentVNode as C, createElementVNode as s, normalizeClass as x, Fragment as y, toDisplayString as d, createTextVNode as N, renderList as $ } from "vue";
4
+ const L = { class: "w-full h-full flex flex-col items-center justify-center p-4 bg-gray-100" }, S = {
5
+ key: 0,
6
+ class: "flex flex-col items-center"
7
+ }, B = { key: 0 }, W = { key: 1 }, V = { class: "grid grid-cols-3 gap-1 p-2 bg-gray-700 rounded-lg shadow-lg" }, E = ["onClick", "onMouseenter", "onMouseleave"], P = {
8
+ key: 0,
9
+ class: "w-16 h-16 text-blue-600",
10
+ viewBox: "0 0 24 24",
11
+ fill: "none",
12
+ stroke: "currentColor",
13
+ "stroke-width": "3",
14
+ "stroke-linecap": "round"
15
+ }, F = {
16
+ key: 1,
17
+ class: "w-16 h-16 text-red-600",
18
+ viewBox: "0 0 24 24",
19
+ fill: "none",
20
+ stroke: "currentColor",
21
+ "stroke-width": "3"
22
+ }, A = {
23
+ key: 1,
24
+ cx: "12",
25
+ cy: "12",
26
+ r: "8"
27
+ }, I = { class: "mt-4 flex gap-8 text-lg" }, R = { class: "flex items-center gap-2" }, z = { class: "text-gray-500 text-sm" }, G = { class: "flex items-center gap-2" }, U = { class: "text-gray-500 text-sm" }, H = /* @__PURE__ */ j({
28
+ __name: "View",
29
+ props: {
30
+ selectedResult: {},
31
+ sendTextMessage: { type: Function }
32
+ },
33
+ setup(u) {
34
+ const v = u, e = D(null), p = D(null);
35
+ X(
36
+ () => v.selectedResult,
37
+ (r) => {
38
+ r?.toolName === "playTicTacToe" && r.jsonData && (e.value = r.jsonData);
39
+ },
40
+ { immediate: !0 }
41
+ );
42
+ const T = b(() => {
43
+ if (!e.value?.playerNames) return "";
44
+ const r = e.value.playerNames[e.value.currentSide];
45
+ return r.charAt(0).toUpperCase() + r.slice(1);
46
+ }), f = b(() => e.value?.playerNames && e.value.playerNames[e.value.currentSide] === "computer"), i = b(() => e.value ? e.value.isTerminal ? e.value.winner === "draw" ? "text-gray-600" : e.value.winner === "X" ? "text-blue-600" : "text-red-600" : e.value.currentSide === "X" ? "text-blue-600" : "text-red-600" : ""), c = b(() => {
47
+ if (!e.value?.board) return [];
48
+ const r = [];
49
+ for (let t = 0; t < 3; t++)
50
+ for (let o = 0; o < 3; o++) {
51
+ const a = e.value.board[t][o], m = e.value.legalMoves?.some(
52
+ (h) => h.row === t && h.col === o
53
+ ), _ = e.value.winningLine?.some(
54
+ (h) => h.row === t && h.col === o
55
+ );
56
+ r.push({
57
+ row: t,
58
+ col: o,
59
+ piece: a !== "." ? a : null,
60
+ isLegalMove: m ?? !1,
61
+ isWinningCell: _ ?? !1
62
+ });
63
+ }
64
+ return r;
65
+ });
66
+ function g(r, t) {
67
+ const o = "w-24 h-24 flex items-center justify-center bg-white rounded";
68
+ let a = "";
69
+ return r.isWinningCell ? a = "bg-yellow-200 ring-2 ring-yellow-400" : r.isLegalMove && !f.value && !e.value?.isTerminal && (a = p.value === t ? "bg-gray-200 cursor-pointer" : "hover:bg-gray-100 cursor-pointer"), `${o} ${a}`;
70
+ }
71
+ function w(r) {
72
+ if (!e.value || e.value.isTerminal || f.value)
73
+ return;
74
+ const t = c.value[r];
75
+ if (!t.isLegalMove) return;
76
+ const o = ["top", "middle", "bottom"], a = ["left", "center", "right"], m = `${o[t.row]}-${a[t.col]}`, _ = {
77
+ row: t.row,
78
+ col: t.col,
79
+ currentState: e.value
80
+ };
81
+ v.sendTextMessage?.(
82
+ `I want to play at ${m}, which is row=${t.row}, col=${t.col}`,
83
+ { data: _ }
84
+ );
85
+ }
86
+ function k(r, t) {
87
+ !e.value || e.value.isTerminal || f.value || !c.value[r].isLegalMove || (p.value = t ? r : null);
88
+ }
89
+ return (r, t) => (l(), n("div", L, [
90
+ e.value ? (l(), n("div", S, [
91
+ t[6] || (t[6] = s("h1", { class: "text-2xl font-bold text-gray-800 mb-2" }, "Tic-Tac-Toe", -1)),
92
+ s("div", {
93
+ class: x(["text-lg font-semibold mb-4 text-center", i.value])
94
+ }, [
95
+ e.value.isTerminal ? (l(), n(y, { key: 0 }, [
96
+ e.value.winner === "draw" ? (l(), n("span", B, "It's a Draw!")) : (l(), n("span", W, d(e.value.winner) + " Wins!", 1))
97
+ ], 64)) : (l(), n(y, { key: 1 }, [
98
+ N(d(T.value) + "'s Turn (" + d(e.value.currentSide) + ") ", 1)
99
+ ], 64))
100
+ ], 2),
101
+ s("div", V, [
102
+ (l(!0), n(y, null, $(c.value, (o, a) => (l(), n("div", {
103
+ key: a,
104
+ class: x(g(o, a)),
105
+ onClick: (m) => w(a),
106
+ onMouseenter: (m) => k(a, !0),
107
+ onMouseleave: (m) => k(a, !1)
108
+ }, [
109
+ o.piece === "X" ? (l(), n("svg", P, [...t[0] || (t[0] = [
110
+ s("line", {
111
+ x1: "4",
112
+ y1: "4",
113
+ x2: "20",
114
+ y2: "20"
115
+ }, null, -1),
116
+ s("line", {
117
+ x1: "20",
118
+ y1: "4",
119
+ x2: "4",
120
+ y2: "20"
121
+ }, null, -1)
122
+ ])])) : o.piece === "O" ? (l(), n("svg", F, [...t[1] || (t[1] = [
123
+ s("circle", {
124
+ cx: "12",
125
+ cy: "12",
126
+ r: "8"
127
+ }, null, -1)
128
+ ])])) : o.isLegalMove && !f.value && p.value === a ? (l(), n("svg", {
129
+ key: 2,
130
+ class: x(["w-16 h-16 opacity-30", e.value.currentSide === "X" ? "text-blue-400" : "text-red-400"]),
131
+ viewBox: "0 0 24 24",
132
+ fill: "none",
133
+ stroke: "currentColor",
134
+ "stroke-width": "3",
135
+ "stroke-linecap": "round"
136
+ }, [
137
+ e.value.currentSide === "X" ? (l(), n(y, { key: 0 }, [
138
+ t[2] || (t[2] = s("line", {
139
+ x1: "4",
140
+ y1: "4",
141
+ x2: "20",
142
+ y2: "20"
143
+ }, null, -1)),
144
+ t[3] || (t[3] = s("line", {
145
+ x1: "20",
146
+ y1: "4",
147
+ x2: "4",
148
+ y2: "20"
149
+ }, null, -1))
150
+ ], 64)) : (l(), n("circle", A))
151
+ ], 2)) : C("", !0)
152
+ ], 42, E))), 128))
153
+ ]),
154
+ s("div", I, [
155
+ s("div", R, [
156
+ t[4] || (t[4] = s("span", { class: "text-blue-600 font-bold" }, "X:", -1)),
157
+ s("span", null, d(e.value.counts.X), 1),
158
+ s("span", z, "(" + d(e.value.playerNames.X) + ")", 1)
159
+ ]),
160
+ s("div", G, [
161
+ t[5] || (t[5] = s("span", { class: "text-red-600 font-bold" }, "O:", -1)),
162
+ s("span", null, d(e.value.counts.O), 1),
163
+ s("span", U, "(" + d(e.value.playerNames.O) + ")", 1)
164
+ ])
165
+ ])
166
+ ])) : C("", !0)
167
+ ]));
168
+ }
169
+ }), Y = { class: "p-3 bg-gray-50 rounded" }, q = {
170
+ key: 0,
171
+ class: "space-y-2"
172
+ }, J = { class: "flex justify-center" }, K = { class: "inline-block bg-gray-600 p-0.5 rounded" }, Q = { class: "grid grid-cols-3 gap-0.5" }, Z = {
173
+ key: 0,
174
+ class: "w-4 h-4 text-blue-600",
175
+ viewBox: "0 0 24 24",
176
+ fill: "none",
177
+ stroke: "currentColor",
178
+ "stroke-width": "3",
179
+ "stroke-linecap": "round"
180
+ }, ee = {
181
+ key: 1,
182
+ class: "w-4 h-4 text-red-600",
183
+ viewBox: "0 0 24 24",
184
+ fill: "none",
185
+ stroke: "currentColor",
186
+ "stroke-width": "3"
187
+ }, te = { class: "text-xs text-center space-y-1" }, se = {
188
+ key: 0,
189
+ class: "text-gray-600"
190
+ }, ne = /* @__PURE__ */ j({
191
+ __name: "Preview",
192
+ props: {
193
+ result: {}
194
+ },
195
+ setup(u) {
196
+ const v = u;
197
+ function e(i, c) {
198
+ return v.result.jsonData?.winningLine?.some(
199
+ (g) => g.row === i && g.col === c
200
+ ) ?? !1;
201
+ }
202
+ function p(i) {
203
+ return i.isTerminal ? i.winner === "draw" ? "Draw!" : i.winner === "X" ? "X Wins!" : i.winner === "O" ? "O Wins!" : "Game Over" : "";
204
+ }
205
+ function T() {
206
+ return v.result.jsonData?.isTerminal ? v.result.jsonData.winner === "draw" ? "text-gray-600" : v.result.jsonData.winner === "X" ? "text-blue-600" : v.result.jsonData.winner === "O" ? "text-red-600" : "" : "";
207
+ }
208
+ function f(i) {
209
+ return i.charAt(0).toUpperCase() + i.slice(1);
210
+ }
211
+ return (i, c) => (l(), n("div", Y, [
212
+ u.result.jsonData ? (l(), n("div", q, [
213
+ s("div", J, [
214
+ s("div", K, [
215
+ s("div", Q, [
216
+ (l(!0), n(y, null, $(u.result.jsonData.board, (g, w) => (l(), n(y, { key: w }, [
217
+ (l(!0), n(y, null, $(g, (k, r) => (l(), n("div", {
218
+ key: `${w}-${r}`,
219
+ class: x(["w-6 h-6 flex items-center justify-center bg-white rounded-sm", e(w, r) ? "bg-yellow-200" : ""])
220
+ }, [
221
+ k === "X" ? (l(), n("svg", Z, [...c[0] || (c[0] = [
222
+ s("line", {
223
+ x1: "4",
224
+ y1: "4",
225
+ x2: "20",
226
+ y2: "20"
227
+ }, null, -1),
228
+ s("line", {
229
+ x1: "20",
230
+ y1: "4",
231
+ x2: "4",
232
+ y2: "20"
233
+ }, null, -1)
234
+ ])])) : k === "O" ? (l(), n("svg", ee, [...c[1] || (c[1] = [
235
+ s("circle", {
236
+ cx: "12",
237
+ cy: "12",
238
+ r: "8"
239
+ }, null, -1)
240
+ ])])) : C("", !0)
241
+ ], 2))), 128))
242
+ ], 64))), 128))
243
+ ])
244
+ ])
245
+ ]),
246
+ s("div", te, [
247
+ u.result.jsonData.isTerminal ? (l(), n("div", {
248
+ key: 1,
249
+ class: x(["font-medium", T()])
250
+ }, d(p(u.result.jsonData)), 3)) : (l(), n("div", se, [
251
+ s("span", {
252
+ class: x(u.result.jsonData.currentSide === "X" ? "text-blue-600" : "text-red-600")
253
+ }, d(u.result.jsonData.currentSide), 3),
254
+ N(" " + d(f(u.result.jsonData.playerNames[u.result.jsonData.currentSide])) + " to play ", 1)
255
+ ]))
256
+ ])
257
+ ])) : C("", !0)
258
+ ]));
259
+ }
260
+ }), le = {
261
+ ...O,
262
+ viewComponent: H,
263
+ previewComponent: ne,
264
+ samples: M
265
+ }, ae = { plugin: le };
266
+ export {
267
+ ne as Preview,
268
+ ce as SYSTEM_PROMPT,
269
+ de as TOOL_DEFINITION,
270
+ ve as TOOL_NAME,
271
+ H as View,
272
+ ae as default,
273
+ ye as executeTicTacToe,
274
+ fe as playTicTacToe,
275
+ le as plugin,
276
+ O as pluginCore,
277
+ M as samples
278
+ };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@gui-chat-plugin/tictactoe",
3
+ "version": "0.1.0",
4
+ "description": "Tic-Tac-Toe game plugin for GUI Chat",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./core": {
16
+ "types": "./dist/core/index.d.ts",
17
+ "import": "./dist/core.js",
18
+ "require": "./dist/core.cjs"
19
+ },
20
+ "./vue": {
21
+ "types": "./dist/vue/index.d.ts",
22
+ "import": "./dist/vue.js",
23
+ "require": "./dist/vue.cjs"
24
+ },
25
+ "./style.css": "./dist/style.css"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "dev": "vite",
32
+ "build": "vite build && vue-tsc -p tsconfig.build.json --emitDeclarationOnly",
33
+ "typecheck": "vue-tsc --noEmit",
34
+ "lint": "eslint src demo"
35
+ },
36
+ "peerDependencies": {
37
+ "gui-chat-protocol": "^0.0.1",
38
+ "vue": "^3.5.0"
39
+ },
40
+ "devDependencies": {
41
+ "@tailwindcss/vite": "^4.1.18",
42
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
43
+ "@typescript-eslint/parser": "^8.53.0",
44
+ "@vitejs/plugin-vue": "^6.0.3",
45
+ "eslint": "^9.39.2",
46
+ "eslint-plugin-vue": "^10.6.2",
47
+ "globals": "^17.0.0",
48
+ "gui-chat-protocol": "^0.0.2",
49
+ "tailwindcss": "^4.1.18",
50
+ "typescript": "~5.9.3",
51
+ "vite": "^7.3.1",
52
+ "vue": "^3.5.27",
53
+ "vue-eslint-parser": "^10.2.0",
54
+ "vue-tsc": "^3.2.2"
55
+ },
56
+ "keywords": [
57
+ "guichat",
58
+ "plugin",
59
+ "tictactoe",
60
+ "game"
61
+ ],
62
+ "license": "MIT"
63
+ }