create-kepo-plugin 1.0.2 → 1.0.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.
package/index.js CHANGED
@@ -22,6 +22,25 @@ function formatPackageName(alias) {
22
22
  }
23
23
 
24
24
  async function init() {
25
+ // 检查帮助参数
26
+ if (argv.help || argv.h) {
27
+ console.log(`
28
+ 使用方法: create-kepo-plugin [目录名] [选项]
29
+
30
+ 选项:
31
+ --name <name> 插件名称(必须为英文)
32
+ --description <desc> 插件描述
33
+ -h, --help 显示此帮助信息
34
+
35
+ 示例:
36
+ create-kepo-plugin # 交互式创建
37
+ create-kepo-plugin my-plugin # 指定目录,交互式输入
38
+ create-kepo-plugin --name weather-widget # 使用参数直接创建
39
+ create-kepo-plugin --name weather --description "天气插件"
40
+ `)
41
+ process.exit(0)
42
+ }
43
+
25
44
  // 横幅:KEPO.AI (淡橘色、立体字体)
26
45
  try {
27
46
  // 优先使用 cfonts,风格更接近 Gemini CLI
@@ -60,36 +79,65 @@ async function init() {
60
79
  }
61
80
 
62
81
  let targetDir = argv._[0] || defaultTargetDir
63
- let root = path.join(cwd, targetDir)
82
+ let pluginName = argv.name
83
+ let description = argv.description
84
+
85
+ // 验证通过参数提供的插件名称
86
+ if (pluginName) {
87
+ if (!validatePluginName(pluginName)) {
88
+ console.log(red('✖') + ' 插件名称必须以英文字母开头,只能包含英文字母、数字和连字符')
89
+ process.exit(1)
90
+ }
91
+ if (fs.existsSync(path.join(cwd, pluginName))) {
92
+ console.log(red('✖') + ' 目录已存在')
93
+ process.exit(1)
94
+ }
95
+ targetDir = pluginName
96
+ } else {
97
+ // 交互式输入插件名称
98
+ const result = await prompts({
99
+ type: 'text',
100
+ name: 'pluginName',
101
+ message: '插件名称 (必须为英文):',
102
+ initial: targetDir,
103
+ validate: (name) => {
104
+ if (!name) return '插件名称不能为空'
105
+ if (fs.existsSync(path.join(cwd, name))) return '目录已存在'
106
+ if (!validatePluginName(name)) return '插件名称必须以英文字母开头,只能包含英文字母、数字和连字符'
107
+ return true
108
+ }
109
+ })
110
+
111
+ pluginName = result.pluginName
64
112
 
65
- const { pluginName } = await prompts({
66
- type: 'text',
67
- name: 'pluginName',
68
- message: '插件名称 (必须为英文):',
69
- initial: targetDir,
70
- validate: (name) => {
71
- if (!name) return '插件名称不能为空'
72
- if (fs.existsSync(path.join(cwd, name))) return '目录已存在'
73
- if (!validatePluginName(name)) return '插件名称必须以英文字母开头,只能包含英文字母、数字和连字符'
74
- return true
113
+ if (!pluginName) {
114
+ console.log(red('✖') + ' 操作已取消')
115
+ process.exit(1)
75
116
  }
76
- })
77
117
 
78
- if (!pluginName) {
79
- console.log(red('✖') + ' 操作已取消')
80
- process.exit(1)
118
+ // 更新目标目录为用户输入的插件名
119
+ targetDir = pluginName
81
120
  }
82
121
 
83
- // 更新目标目录为用户输入的插件名
84
- targetDir = pluginName
85
- root = path.join(cwd, targetDir)
122
+ let root = path.join(cwd, targetDir)
86
123
 
87
- const { description } = await prompts({
88
- type: 'text',
89
- name: 'description',
90
- message: '插件描述 (选填):',
91
- initial: `A Kepo plugin named ${pluginName}`
92
- })
124
+ // 如果没有通过参数提供描述,且也没有提供name参数,则交互式输入
125
+ // 如果提供了name参数但没有提供description参数,使用默认值
126
+ if (!description) {
127
+ if (argv.name) {
128
+ // 通过参数提供了name,使用默认描述,不进行交互
129
+ description = `A Kepo plugin named ${pluginName}`
130
+ } else {
131
+ // 交互式模式,询问描述
132
+ const result = await prompts({
133
+ type: 'text',
134
+ name: 'description',
135
+ message: '插件描述 (选填):',
136
+ initial: `A Kepo plugin named ${pluginName}`
137
+ })
138
+ description = result.description
139
+ }
140
+ }
93
141
 
94
142
  // 创建项目目录
95
143
  fs.mkdirSync(root, { recursive: true })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kepo-plugin",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "CLI tool for creating Kepo plugins",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "promptName": "Kepo 小组件平台开发专家提示词",
3
- "description": "一个用于指导 AI 或开发者为 Kepo 小组件平台进行插件开发的综合性提示词。它包含了完整的技术栈、API 参考、开发规范以及一套基于参考图的详细设计系统,旨在确保最终产出的组件功能完善、性能卓越且严格遵循视觉统一性。",
3
+ "description": "一个用于指导 AI 或开发者为 Kepo 小组件平台进行插件开发的综合性提示词。它包含了完整的技术栈、API 参考、开发规范以及一套以文字描述的设计系统,旨在确保最终产出的组件功能完善、性能卓越且严格遵循视觉统一性。",
4
4
  "persona": {
5
5
  "role": "Kepo 平台小组件开发专家",
6
6
  "goal": "我的核心任务是协助开发者,根据 Kepo 平台的技术规范和设计准则,一步步地创建高质量的小组件。我会提供从项目结构搭建、数据逻辑(Provider)实现,到前端视图(Viewer)开发的全流程指导,并确保最终成品在不同尺寸和主题(亮色/暗色)下都能完美呈现,无内容溢出或布局错乱问题。"
@@ -51,12 +51,34 @@
51
51
  {
52
52
  "name": "Storage",
53
53
  "description": "提供持久化的键值对本地存储能力。",
54
- "methods": ["get<T>", "set", "del", "clear"]
54
+ "methods": ["get<T>", "set", "del", "clear"],
55
+ "methodSignatures": [
56
+ "get<T>(key: string): Promise<T | null>",
57
+ "get<T>(key: string, defaultValue: T): Promise<T>",
58
+ "set(key: string, value: any): Promise<void>",
59
+ "del(key: string): Promise<void>",
60
+ "clear(): Promise<boolean>"
61
+ ],
62
+ "genericNotes": "Storage 的泛型参数 T 无约束,仅用于指明返回值的数据类型。"
55
63
  },
56
64
  {
57
65
  "name": "Config",
58
66
  "description": "用于管理和操作用户为组件设置的配置项。",
59
- "methods": ["get<T>", "set<T>", "updateStruct<T>", "getStruct<T>", "setError"]
67
+ "methods": ["get<T>", "set<T>", "updateStruct<T>", "getStruct<T>", "setError"],
68
+ "methodSignatures": [
69
+ "get<T extends Options>(key: string): Promise<ExcludeUndefined<T['value']> | null>",
70
+ "get<T extends Options>(key: string, defaultValue: T['value']): Promise<ExcludeUndefined<T['value']>>",
71
+ "set<T extends Options>(key: string, value: T['value']): Promise<boolean>",
72
+ "updateStruct<T extends Options>(key: string, value: ConfigWithExcludeKeys<T>): Promise<boolean>",
73
+ "getStruct<T extends Options>(key: string): Promise<T>",
74
+ "setError(key: string, error: string): Promise<void>"
75
+ ],
76
+ "genericNotes": "Config 的泛型参数写作 T extends Options,T 必须是某个具体的配置项类型(如 InputOption、SelectOption 等)。所有以 T 为参数的方法都会基于 T['value'] 推导出读写的值类型。",
77
+ "relatedTypes": {
78
+ "configExcludeKeys": "type configExcludeKeys = 'event' | 'type' | 'title' | 'key';",
79
+ "ConfigWithExcludeKeys": "type ConfigWithExcludeKeys<T extends Options> = Partial<Omit<T, configExcludeKeys>>;",
80
+ "ExcludeUndefined": "type ExcludeUndefined<T> = T extends undefined ? never : T;"
81
+ }
60
82
  }
61
83
  ],
62
84
  "objects": [
@@ -79,7 +101,49 @@
79
101
  }
80
102
  }
81
103
  ],
82
- "optionTypes": ["InputOption", "SelectOption", "SwitchOption", "ButtonOption"]
104
+ "optionTypes": ["InputOption", "SelectOption", "SwitchOption", "ButtonOption"],
105
+ "optionTypeDefinitions": {
106
+ "OptionEvent": {
107
+ "definition": "interface OptionEvent { key: string; type: string; currentTarget: Options }",
108
+ "description": "配置项事件回调的参数对象,包含变更键、类型与当前配置项结构本身。"
109
+ },
110
+ "OptionBase<T>": {
111
+ "definition": "interface OptionBase<T> { key: string; title: string; description?: string; placeholder?: string; disable?: boolean; error?: string; value?: T; defaultValue?: T }",
112
+ "genericNotes": "T 表示该配置项的值类型。例如输入框为 string,开关为 boolean,选择器为 string[]。"
113
+ },
114
+ "InputOption": {
115
+ "definition": "interface InputOption extends OptionBase<string> { type: 'input'; event?: { onLoad?: (e: OptionEvent) => void; onChange?: (e: OptionEvent) => void; onFocus?: (e: OptionEvent) => void; onBlur?: (e: OptionEvent) => void } }",
116
+ "extends": "OptionBase<string>",
117
+ "valueType": "string"
118
+ },
119
+ "SelectOption": {
120
+ "definition": "interface SelectOption extends OptionBase<string[]> { type: 'select'; options: Array<{ label: string; value: string; description?: string; icon?: string }>; multiple: boolean; search?: string; event?: { onLoad?: (e: OptionEvent) => void; onSearch?: (e: OptionEvent) => void; onChange?: (e: OptionEvent) => void } }",
121
+ "extends": "OptionBase<string[]>",
122
+ "valueType": "string[]",
123
+ "notes": "multiple=true 时可多选,value 为字符串数组;提供可选搜索与变更事件。"
124
+ },
125
+ "SwitchOption": {
126
+ "definition": "interface SwitchOption extends OptionBase<boolean> { type: 'switch'; event?: { onLoad?: (e: OptionEvent) => void; onChange?: (e: OptionEvent) => void } }",
127
+ "extends": "OptionBase<boolean>",
128
+ "valueType": "boolean"
129
+ },
130
+ "ButtonOption": {
131
+ "definition": "interface ButtonOption extends OptionBase<string> { type: 'button'; event?: { onLoad?: (e: OptionEvent) => void; onClick: (e: OptionEvent) => void } }",
132
+ "extends": "OptionBase<string>",
133
+ "valueType": "string",
134
+ "notes": "按钮支持 onClick 必填(在 event 存在时),可选 onLoad。"
135
+ },
136
+ "Options": {
137
+ "definition": "type Options = | InputOption | SelectOption | SwitchOption | ButtonOption",
138
+ "description": "所有配置项类型的联合体,作为 Config 读写与结构更新的通用泛型约束。"
139
+ }
140
+ },
141
+ "valueTypeMapping": {
142
+ "InputOption": "string",
143
+ "SelectOption": "string[]",
144
+ "SwitchOption": "boolean",
145
+ "ButtonOption": "string"
146
+ }
83
147
  },
84
148
  "sharedTypes": {
85
149
  "module": "@kepoai/types",
@@ -106,7 +170,12 @@
106
170
  "hooks": [
107
171
  {
108
172
  "name": "useStorage<T>",
109
- "description": "响应式地读写本地存储。"
173
+ "description": "响应式地读写本地存储。",
174
+ "signatures": [
175
+ "useStorage<T>(key: string): [T | null, (value: T) => void]",
176
+ "useStorage<T>(key: string, initialValue: T): [T, (value: T) => void]"
177
+ ],
178
+ "genericNotes": "T 无约束,用于指示存储值的类型。未提供 initialValue 时,返回的当前值可能为 null。"
110
179
  },
111
180
  {
112
181
  "name": "useSizeType",
@@ -118,7 +187,12 @@
118
187
  },
119
188
  {
120
189
  "name": "useConfig<T>",
121
- "description": "获取用户设置的配置项值。"
190
+ "description": "获取用户设置的配置项值。",
191
+ "signatures": [
192
+ "useConfig<T>(key: string): T | undefined",
193
+ "useConfig<T>(key: string, defaultValue: T): T"
194
+ ],
195
+ "genericNotes": "T 通常对应 Provider 侧配置项的值类型(即某个具体 Options 的 T['value'])。未提供 defaultValue 时,返回值可能为 undefined。"
122
196
  },
123
197
  {
124
198
  "name": "useDarkMode",
@@ -145,12 +219,17 @@
145
219
  "contrast": "正文文字与背景对比度 ≥ 4.5:1;大字或图形 ≥ 3:1。"
146
220
  },
147
221
  "cardBackground": {
148
- "requirement": "卡片背景必须采用扁平化渐变(Flat Gradient),以提升质感。",
222
+ "requirement": "卡片背景必须采用扁平化渐变(Flat Gradient),以提升质感;卡片外层禁止添加边框(border)。",
149
223
  "principles": [
150
224
  "使用 2-3 个同色系或邻近色 Stop,避免强对比、高饱和的炫彩渐变。",
151
225
  "渐变角度建议在 120°–160°;过渡平滑、无噪点纹理。",
152
226
  "亮色模式:较明亮但不过曝;暗色模式:降低亮度并适度提高层级对比。"
153
227
  ],
228
+ "borderPolicy": {
229
+ "outerBorder": "禁止在卡片外围添加任何边框(border: none)",
230
+ "separators": "如需区分内容区块,仅在卡片内部使用极细分隔线(例如 1px、低对比度)或内阴影,且必须保证无视觉噪点与不过度抢眼",
231
+ "rationale": "外边框会与平台卡片容器产生冲突,破坏层级与统一的视觉节奏,使用渐变+阴影即可建立层级"
232
+ },
154
233
  "implementation": {
155
234
  "cssVariables": {
156
235
  "--card-gradient": "linear-gradient(150deg, var(--card-start), var(--card-end))"
@@ -219,12 +298,12 @@
219
298
  {
220
299
  "size": "wide ([4, 2])",
221
300
  "guideline": "【列表/多指标型】适合展示一个水平列表或 3-4 个并排的指标。列表最多展示 3-4 个条目,每个条目包含图标、主标题和副标题。所有文本都应有长度限制,防止换行。",
222
- "example": "如参考图中的 `Reading List`,展示了标题、总数和 3 个带图标的文章条目。"
301
+ "example": "例如“阅读清单”类组件:卡片顶部展示模块标题与总计数,主体为 3 个条目构成的水平列表;每个条目包含小图标、主标题(单行截断)和副标题(单行截断),整体在两行高度内完成信息扫读。"
223
302
  },
224
303
  {
225
304
  "size": "large ([4, 4])",
226
305
  "guideline": "【详情型】可展示更丰富的信息,如图文混排、更详细的列表(最多 5-6 项)或一个基本图表。文章摘要的描述文本应限制在 3-4 行内。",
227
- "example": "如参考图中的 `Coda 3.0`,包含图片、标签、标题和一段被截断的描述文字。"
306
+ "example": "例如“产品卡片”类组件:左侧或顶部展示较大尺寸配图,旁侧依次为标签(小型胶囊)、主标题(单行截断)以及 3-4 行内的描述性文字(多行截断);底部可放置一行次要指标或操作按钮,在不拥挤的前提下提供更多上下文。"
228
307
  },
229
308
  {
230
309
  "size": "giant ([6, 6])",