@leeoohoo/ui-apps-devkit 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +61 -62
  2. package/bin/chatos-uiapp.js +3 -4
  3. package/package.json +23 -23
  4. package/src/cli.js +53 -53
  5. package/src/commands/dev.js +14 -14
  6. package/src/commands/init.js +129 -129
  7. package/src/commands/install.js +45 -45
  8. package/src/commands/pack.js +72 -72
  9. package/src/commands/validate.js +90 -138
  10. package/src/lib/args.js +49 -49
  11. package/src/lib/config.js +29 -29
  12. package/src/lib/fs.js +78 -78
  13. package/src/lib/path-boundary.js +16 -16
  14. package/src/lib/plugin.js +45 -45
  15. package/src/lib/template.js +172 -172
  16. package/src/sandbox/server.js +1204 -1028
  17. package/templates/basic/README.md +63 -65
  18. package/templates/basic/chatos.config.json +5 -5
  19. package/templates/basic/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +209 -211
  20. package/templates/basic/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +73 -73
  21. package/templates/basic/docs/CHATOS_UI_APPS_HOST_API.md +136 -136
  22. package/templates/basic/docs/CHATOS_UI_APPS_OVERVIEW.md +106 -106
  23. package/templates/basic/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +239 -239
  24. package/templates/basic/docs/CHATOS_UI_APPS_STYLE_GUIDE.md +95 -95
  25. package/templates/basic/docs/CHATOS_UI_APPS_TROUBLESHOOTING.md +40 -40
  26. package/templates/basic/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -392
  27. package/templates/basic/plugin/apps/app/compact.mjs +41 -41
  28. package/templates/basic/plugin/apps/app/index.mjs +287 -287
  29. package/templates/basic/plugin/apps/app/mcp-prompt.en.md +7 -7
  30. package/templates/basic/plugin/apps/app/mcp-prompt.zh.md +7 -7
  31. package/templates/basic/plugin/apps/app/mcp-server.mjs +15 -15
  32. package/templates/basic/plugin/backend/index.mjs +37 -37
  33. package/templates/basic/template.json +7 -7
  34. package/templates/notepad/README.md +38 -44
  35. package/templates/notepad/chatos.config.json +4 -4
  36. package/templates/notepad/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +209 -211
  37. package/templates/notepad/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +73 -73
  38. package/templates/notepad/docs/CHATOS_UI_APPS_HOST_API.md +136 -136
  39. package/templates/notepad/docs/CHATOS_UI_APPS_OVERVIEW.md +106 -106
  40. package/templates/notepad/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +239 -239
  41. package/templates/notepad/docs/CHATOS_UI_APPS_STYLE_GUIDE.md +95 -95
  42. package/templates/notepad/docs/CHATOS_UI_APPS_TROUBLESHOOTING.md +40 -40
  43. package/templates/notepad/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -392
  44. package/templates/notepad/plugin/apps/app/api.mjs +30 -30
  45. package/templates/notepad/plugin/apps/app/compact.mjs +41 -41
  46. package/templates/notepad/plugin/apps/app/dom.mjs +14 -14
  47. package/templates/notepad/plugin/apps/app/ds-tree.mjs +35 -35
  48. package/templates/notepad/plugin/apps/app/index.mjs +1056 -1056
  49. package/templates/notepad/plugin/apps/app/layers.mjs +338 -338
  50. package/templates/notepad/plugin/apps/app/markdown.mjs +120 -120
  51. package/templates/notepad/plugin/apps/app/mcp-prompt.en.md +22 -22
  52. package/templates/notepad/plugin/apps/app/mcp-prompt.zh.md +22 -22
  53. package/templates/notepad/plugin/apps/app/mcp-server.mjs +199 -199
  54. package/templates/notepad/plugin/apps/app/styles.mjs +355 -355
  55. package/templates/notepad/plugin/apps/app/tags.mjs +21 -21
  56. package/templates/notepad/plugin/apps/app/ui.mjs +280 -280
  57. package/templates/notepad/plugin/backend/index.mjs +99 -99
  58. package/templates/notepad/plugin/plugin.json +23 -23
  59. package/templates/notepad/plugin/shared/notepad-paths.mjs +39 -39
  60. package/templates/notepad/plugin/shared/notepad-store.mjs +765 -765
  61. package/templates/notepad/template.json +8 -8
@@ -6,202 +6,202 @@
6
6
  * a single output (or vendor deps into the plugin directory).
7
7
  */
8
8
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
- import { z } from 'zod';
11
- import { createNotepadStore } from '../../shared/notepad-store.mjs';
12
- import { resolveUiAppDataDir } from '../../shared/notepad-paths.mjs';
13
-
14
- const PLUGIN_ID = '__PLUGIN_ID__';
15
- const SERVER_NAME = '__PLUGIN_ID__.__APP_ID__';
16
-
17
- const store = createNotepadStore({ dataDir: resolveUiAppDataDir({ pluginId: PLUGIN_ID }) });
18
-
19
- const server = new McpServer({
20
- name: SERVER_NAME,
21
- version: '0.1.0',
22
- });
23
-
24
- const toText = (payload) => ({
25
- content: [
26
- {
27
- type: 'text',
28
- text: JSON.stringify(payload, null, 2),
29
- },
30
- ],
31
- });
32
-
33
- server.registerTool(
34
- 'init',
35
- {
36
- title: 'Init Notepad Storage',
37
- description: 'Initialize the Notepad storage (data directory, index, notes root).',
38
- inputSchema: z.object({}).optional(),
39
- },
40
- async () => toText(await store.init())
41
- );
42
-
43
- server.registerTool(
44
- 'list_folders',
45
- {
46
- title: 'List Folders',
47
- description: 'List all folders (categories) under Notepad notes root.',
48
- inputSchema: z.object({}).optional(),
49
- },
50
- async () => toText(await store.listFolders())
51
- );
52
-
53
- server.registerTool(
54
- 'create_folder',
55
- {
56
- title: 'Create Folder',
57
- description: 'Create a folder (category). Supports nested paths like "work/ideas".',
58
- inputSchema: z.object({
59
- folder: z.string().min(1).describe('Folder path, relative to notes root (e.g., "work/ideas")'),
60
- }),
61
- },
62
- async ({ folder }) => toText(await store.createFolder({ folder }))
63
- );
64
-
65
- server.registerTool(
66
- 'rename_folder',
67
- {
68
- title: 'Rename Folder',
69
- description: 'Rename/move a folder (and updates indexed notes folder paths).',
70
- inputSchema: z.object({
71
- from: z.string().min(1).describe('Source folder path'),
72
- to: z.string().min(1).describe('Target folder path'),
73
- }),
74
- },
75
- async ({ from, to }) => toText(await store.renameFolder({ from, to }))
76
- );
77
-
78
- server.registerTool(
79
- 'delete_folder',
80
- {
81
- title: 'Delete Folder',
82
- description: 'Delete a folder. If recursive=true, deletes all notes under it and removes them from index.',
83
- inputSchema: z.object({
84
- folder: z.string().min(1).describe('Folder path to delete'),
85
- recursive: z.boolean().optional().describe('Delete folder recursively'),
86
- }),
87
- },
88
- async ({ folder, recursive } = {}) => toText(await store.deleteFolder({ folder, recursive: recursive === true }))
89
- );
90
-
91
- server.registerTool(
92
- 'list_notes',
93
- {
94
- title: 'List Notes',
95
- description: 'List notes with optional folder/tags/title filtering.',
96
- inputSchema: z.object({
97
- folder: z.string().optional().describe('Folder path; empty means all'),
98
- recursive: z.boolean().optional().describe('Include notes in subfolders (default true)'),
99
- tags: z.array(z.string()).optional().describe('Filter notes by tags'),
100
- match: z.enum(['all', 'any']).optional().describe('Tag match mode'),
101
- query: z.string().optional().describe('Substring filter for title/folder'),
102
- limit: z.number().int().min(1).max(500).optional().describe('Max notes to return'),
103
- }),
104
- },
105
- async ({ folder, recursive, tags, match, query, limit } = {}) =>
106
- toText(await store.listNotes({ folder, recursive, tags, match, query, limit }))
107
- );
108
-
109
- server.registerTool(
110
- 'create_note',
111
- {
112
- title: 'Create Note',
113
- description: 'Create a new markdown note under a folder with optional title/content/tags.',
114
- inputSchema: z.object({
115
- folder: z.string().optional().describe('Folder path to create note in'),
116
- title: z.string().optional().describe('Note title'),
117
- content: z.string().optional().describe('Markdown content (optional)'),
118
- tags: z.array(z.string()).optional().describe('Tags (optional)'),
119
- }),
120
- },
121
- async ({ folder, title, content, tags } = {}) => toText(await store.createNote({ folder, title, content, tags }))
122
- );
123
-
124
- server.registerTool(
125
- 'read_note',
126
- {
127
- title: 'Read Note',
128
- description: 'Read a note by id (returns metadata and markdown content).',
129
- inputSchema: z.object({
130
- id: z.string().min(1).describe('Note id'),
131
- }),
132
- },
133
- async ({ id }) => toText(await store.getNote({ id }))
134
- );
135
-
136
- server.registerTool(
137
- 'update_note',
138
- {
139
- title: 'Update Note',
140
- description: 'Update note metadata/content by id. You can also move it by changing folder.',
141
- inputSchema: z.object({
142
- id: z.string().min(1).describe('Note id'),
143
- title: z.string().optional().describe('New title'),
144
- content: z.string().optional().describe('New markdown content'),
145
- folder: z.string().optional().describe('New folder path'),
146
- tags: z.array(z.string()).optional().describe('Replace tags'),
147
- }),
148
- },
149
- async ({ id, title, content, folder, tags } = {}) => toText(await store.updateNote({ id, title, content, folder, tags }))
150
- );
151
-
152
- server.registerTool(
153
- 'delete_note',
154
- {
155
- title: 'Delete Note',
156
- description: 'Delete a note by id (removes file and index entry).',
157
- inputSchema: z.object({
158
- id: z.string().min(1).describe('Note id'),
159
- }),
160
- },
161
- async ({ id }) => toText(await store.deleteNote({ id }))
162
- );
163
-
164
- server.registerTool(
165
- 'list_tags',
166
- {
167
- title: 'List Tags',
168
- description: 'List all tags with usage counts.',
169
- inputSchema: z.object({}).optional(),
170
- },
171
- async () => toText(await store.listTags())
172
- );
173
-
174
- server.registerTool(
175
- 'search_notes',
176
- {
177
- title: 'Search Notes',
178
- description: 'Search notes by query, optionally filtered by folder/tags; can include content search.',
179
- inputSchema: z.object({
180
- query: z.string().min(1).describe('Search keyword'),
181
- folder: z.string().optional().describe('Folder path filter'),
182
- recursive: z.boolean().optional().describe('Include notes in subfolders (default true)'),
183
- tags: z.array(z.string()).optional().describe('Tag filter'),
184
- match: z.enum(['all', 'any']).optional().describe('Tag match mode'),
185
- includeContent: z.boolean().optional().describe('Search in content (default true)'),
186
- limit: z.number().int().min(1).max(200).optional().describe('Max results'),
187
- }),
188
- },
189
- async ({ query, folder, recursive, tags, match, includeContent, limit } = {}) =>
190
- toText(await store.searchNotes({ query, folder, recursive, tags, match, includeContent: includeContent !== false, limit }))
191
- );
192
-
193
- async function main() {
194
- const initRes = await store.init();
195
- if (!initRes?.ok) {
196
- // eslint-disable-next-line no-console
197
- console.error('Notepad MCP init failed:', initRes?.message || initRes);
198
- }
199
- const transport = new StdioServerTransport();
200
- await server.connect(transport);
201
- }
202
-
203
- main().catch((error) => {
204
- // eslint-disable-next-line no-console
205
- console.error('MCP server error:', error);
206
- process.exit(1);
207
- });
9
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
+ import { z } from 'zod';
11
+ import { createNotepadStore } from '../../shared/notepad-store.mjs';
12
+ import { resolveUiAppDataDir } from '../../shared/notepad-paths.mjs';
13
+
14
+ const PLUGIN_ID = '__PLUGIN_ID__';
15
+ const SERVER_NAME = '__PLUGIN_ID__.__APP_ID__';
16
+
17
+ const store = createNotepadStore({ dataDir: resolveUiAppDataDir({ pluginId: PLUGIN_ID }) });
18
+
19
+ const server = new McpServer({
20
+ name: SERVER_NAME,
21
+ version: '0.1.0',
22
+ });
23
+
24
+ const toText = (payload) => ({
25
+ content: [
26
+ {
27
+ type: 'text',
28
+ text: JSON.stringify(payload, null, 2),
29
+ },
30
+ ],
31
+ });
32
+
33
+ server.registerTool(
34
+ 'init',
35
+ {
36
+ title: 'Init Notepad Storage',
37
+ description: 'Initialize the Notepad storage (data directory, index, notes root).',
38
+ inputSchema: z.object({}).optional(),
39
+ },
40
+ async () => toText(await store.init())
41
+ );
42
+
43
+ server.registerTool(
44
+ 'list_folders',
45
+ {
46
+ title: 'List Folders',
47
+ description: 'List all folders (categories) under Notepad notes root.',
48
+ inputSchema: z.object({}).optional(),
49
+ },
50
+ async () => toText(await store.listFolders())
51
+ );
52
+
53
+ server.registerTool(
54
+ 'create_folder',
55
+ {
56
+ title: 'Create Folder',
57
+ description: 'Create a folder (category). Supports nested paths like "work/ideas".',
58
+ inputSchema: z.object({
59
+ folder: z.string().min(1).describe('Folder path, relative to notes root (e.g., "work/ideas")'),
60
+ }),
61
+ },
62
+ async ({ folder }) => toText(await store.createFolder({ folder }))
63
+ );
64
+
65
+ server.registerTool(
66
+ 'rename_folder',
67
+ {
68
+ title: 'Rename Folder',
69
+ description: 'Rename/move a folder (and updates indexed notes folder paths).',
70
+ inputSchema: z.object({
71
+ from: z.string().min(1).describe('Source folder path'),
72
+ to: z.string().min(1).describe('Target folder path'),
73
+ }),
74
+ },
75
+ async ({ from, to }) => toText(await store.renameFolder({ from, to }))
76
+ );
77
+
78
+ server.registerTool(
79
+ 'delete_folder',
80
+ {
81
+ title: 'Delete Folder',
82
+ description: 'Delete a folder. If recursive=true, deletes all notes under it and removes them from index.',
83
+ inputSchema: z.object({
84
+ folder: z.string().min(1).describe('Folder path to delete'),
85
+ recursive: z.boolean().optional().describe('Delete folder recursively'),
86
+ }),
87
+ },
88
+ async ({ folder, recursive } = {}) => toText(await store.deleteFolder({ folder, recursive: recursive === true }))
89
+ );
90
+
91
+ server.registerTool(
92
+ 'list_notes',
93
+ {
94
+ title: 'List Notes',
95
+ description: 'List notes with optional folder/tags/title filtering.',
96
+ inputSchema: z.object({
97
+ folder: z.string().optional().describe('Folder path; empty means all'),
98
+ recursive: z.boolean().optional().describe('Include notes in subfolders (default true)'),
99
+ tags: z.array(z.string()).optional().describe('Filter notes by tags'),
100
+ match: z.enum(['all', 'any']).optional().describe('Tag match mode'),
101
+ query: z.string().optional().describe('Substring filter for title/folder'),
102
+ limit: z.number().int().min(1).max(500).optional().describe('Max notes to return'),
103
+ }),
104
+ },
105
+ async ({ folder, recursive, tags, match, query, limit } = {}) =>
106
+ toText(await store.listNotes({ folder, recursive, tags, match, query, limit }))
107
+ );
108
+
109
+ server.registerTool(
110
+ 'create_note',
111
+ {
112
+ title: 'Create Note',
113
+ description: 'Create a new markdown note under a folder with optional title/content/tags.',
114
+ inputSchema: z.object({
115
+ folder: z.string().optional().describe('Folder path to create note in'),
116
+ title: z.string().optional().describe('Note title'),
117
+ content: z.string().optional().describe('Markdown content (optional)'),
118
+ tags: z.array(z.string()).optional().describe('Tags (optional)'),
119
+ }),
120
+ },
121
+ async ({ folder, title, content, tags } = {}) => toText(await store.createNote({ folder, title, content, tags }))
122
+ );
123
+
124
+ server.registerTool(
125
+ 'read_note',
126
+ {
127
+ title: 'Read Note',
128
+ description: 'Read a note by id (returns metadata and markdown content).',
129
+ inputSchema: z.object({
130
+ id: z.string().min(1).describe('Note id'),
131
+ }),
132
+ },
133
+ async ({ id }) => toText(await store.getNote({ id }))
134
+ );
135
+
136
+ server.registerTool(
137
+ 'update_note',
138
+ {
139
+ title: 'Update Note',
140
+ description: 'Update note metadata/content by id. You can also move it by changing folder.',
141
+ inputSchema: z.object({
142
+ id: z.string().min(1).describe('Note id'),
143
+ title: z.string().optional().describe('New title'),
144
+ content: z.string().optional().describe('New markdown content'),
145
+ folder: z.string().optional().describe('New folder path'),
146
+ tags: z.array(z.string()).optional().describe('Replace tags'),
147
+ }),
148
+ },
149
+ async ({ id, title, content, folder, tags } = {}) => toText(await store.updateNote({ id, title, content, folder, tags }))
150
+ );
151
+
152
+ server.registerTool(
153
+ 'delete_note',
154
+ {
155
+ title: 'Delete Note',
156
+ description: 'Delete a note by id (removes file and index entry).',
157
+ inputSchema: z.object({
158
+ id: z.string().min(1).describe('Note id'),
159
+ }),
160
+ },
161
+ async ({ id }) => toText(await store.deleteNote({ id }))
162
+ );
163
+
164
+ server.registerTool(
165
+ 'list_tags',
166
+ {
167
+ title: 'List Tags',
168
+ description: 'List all tags with usage counts.',
169
+ inputSchema: z.object({}).optional(),
170
+ },
171
+ async () => toText(await store.listTags())
172
+ );
173
+
174
+ server.registerTool(
175
+ 'search_notes',
176
+ {
177
+ title: 'Search Notes',
178
+ description: 'Search notes by query, optionally filtered by folder/tags; can include content search.',
179
+ inputSchema: z.object({
180
+ query: z.string().min(1).describe('Search keyword'),
181
+ folder: z.string().optional().describe('Folder path filter'),
182
+ recursive: z.boolean().optional().describe('Include notes in subfolders (default true)'),
183
+ tags: z.array(z.string()).optional().describe('Tag filter'),
184
+ match: z.enum(['all', 'any']).optional().describe('Tag match mode'),
185
+ includeContent: z.boolean().optional().describe('Search in content (default true)'),
186
+ limit: z.number().int().min(1).max(200).optional().describe('Max results'),
187
+ }),
188
+ },
189
+ async ({ query, folder, recursive, tags, match, includeContent, limit } = {}) =>
190
+ toText(await store.searchNotes({ query, folder, recursive, tags, match, includeContent: includeContent !== false, limit }))
191
+ );
192
+
193
+ async function main() {
194
+ const initRes = await store.init();
195
+ if (!initRes?.ok) {
196
+ // eslint-disable-next-line no-console
197
+ console.error('Notepad MCP init failed:', initRes?.message || initRes);
198
+ }
199
+ const transport = new StdioServerTransport();
200
+ await server.connect(transport);
201
+ }
202
+
203
+ main().catch((error) => {
204
+ // eslint-disable-next-line no-console
205
+ console.error('MCP server error:', error);
206
+ process.exit(1);
207
+ });