@meet-im/lxcli 0.0.8 → 0.0.9

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.
@@ -240,7 +240,7 @@ metadata:
240
240
  }
241
241
  return lines.join('\n');
242
242
  }
243
- async function generateServiceSkills(skillsDir, serviceName) {
243
+ async function generateServiceSkills(skillsDir, serviceName, force) {
244
244
  // 目前只支持 kb 服务
245
245
  if (serviceName !== 'kb') {
246
246
  logger.info(`Service "${serviceName}" is not supported. Supported services: kb`);
@@ -252,14 +252,16 @@ async function generateServiceSkills(skillsDir, serviceName) {
252
252
  const skillName = `lxcli-${serviceName}-${method}`;
253
253
  const skillDir = path.join(skillsDir, skillName);
254
254
  const skillPath = path.join(skillDir, 'SKILL.md');
255
- // 检查是否已有手写的 SKILL.md
256
- try {
257
- await fs.access(skillPath);
258
- logger.info(`Skipped ${skillName}: SKILL.md already exists`);
259
- continue;
260
- }
261
- catch {
262
- // 不存在,生成新的
255
+ // 检查是否已有 SKILL.md(非 --force 时跳过已有文件)
256
+ if (!force) {
257
+ try {
258
+ await fs.access(skillPath);
259
+ logger.info(`Skipped ${skillName}: SKILL.md already exists (use --force to overwrite)`);
260
+ continue;
261
+ }
262
+ catch {
263
+ // 不存在,生成新的
264
+ }
263
265
  }
264
266
  await fs.mkdir(skillDir, { recursive: true });
265
267
  const content = renderSkillMd(method, version);
@@ -272,7 +274,8 @@ export function registerGenerateSkillsCommand(program) {
272
274
  .command('generate-skills')
273
275
  .description('生成 skills 索引文件')
274
276
  .option('-o, --output <path>', '输出文件路径', DEFAULT_OUTPUT)
275
- .option('-s, --service <name>', '为指定服务生成缺失的 SKILL.md 文件(如 kb, meet)')
277
+ .option('-s, --service <name>', '为指定服务生成 SKILL.md 文件(如 kb, meet)')
278
+ .option('-f, --force', '配合 --service 使用,覆盖已有 SKILL.md')
276
279
  .action(async (options) => {
277
280
  try {
278
281
  const stat = await fs.stat(API_ROOT);
@@ -287,10 +290,10 @@ export function registerGenerateSkillsCommand(program) {
287
290
  const outputPath = path.resolve(options.output);
288
291
  await fs.mkdir(skillsDir, { recursive: true });
289
292
  logger.info(`Scanning skills from ${skillsDir}`);
290
- // 如果指定了 --service,先生成缺失的 SKILL.md
293
+ // 如果指定了 --service,生成 SKILL.md
291
294
  if (options.service) {
292
- logger.info(`Generating missing ${options.service} SKILL.md files...`);
293
- await generateServiceSkills(skillsDir, options.service);
295
+ logger.info(`Generating ${options.service} SKILL.md files...`);
296
+ await generateServiceSkills(skillsDir, options.service, options.force);
294
297
  }
295
298
  const entries = await scanSkills(skillsDir);
296
299
  logger.info(`Found ${entries.length} skills`);
@@ -87,15 +87,30 @@ export function createKbAction(method) {
87
87
  }
88
88
  params = { task_id: taskId };
89
89
  }
90
- else if (method === 'createTask' && options.title && options.project_id && options.owner_id && options.category_id) {
90
+ else if (method === 'createTask' && options.title && options.project_id) {
91
91
  const projectId = parseTaskId(options.project_id);
92
- const ownerId = parseTaskId(options.owner_id);
93
- const categoryId = parseTaskId(options.category_id);
94
- if (projectId === null || ownerId === null || categoryId === null) {
95
- outputCliError('PARAM_INVALID', '--project_id, --owner_id and --category_id must be positive integers');
92
+ if (projectId === null) {
93
+ outputCliError('PARAM_INVALID', '--project_id must be a positive integer');
96
94
  return;
97
95
  }
98
- params = { title: options.title, project_id: projectId, owner_id: ownerId, category_id: categoryId };
96
+ params = { title: options.title, project_id: projectId };
97
+ // 可选快捷参数
98
+ if (options.owner_id) {
99
+ const ownerId = parseTaskId(options.owner_id);
100
+ if (ownerId === null) {
101
+ outputCliError('PARAM_INVALID', '--owner_id must be a positive integer');
102
+ return;
103
+ }
104
+ params.owner_id = ownerId;
105
+ }
106
+ if (options.category_id) {
107
+ const categoryId = parseTaskId(options.category_id);
108
+ if (categoryId === null) {
109
+ outputCliError('PARAM_INVALID', '--category_id must be a positive integer');
110
+ return;
111
+ }
112
+ params.category_id = categoryId;
113
+ }
99
114
  }
100
115
  else if (method === 'searchTasks' && options.project_id && options.query) {
101
116
  const projectId = parseTaskId(options.project_id);
@@ -225,7 +240,7 @@ export function createKbAction(method) {
225
240
  else {
226
241
  const shortcuts = {
227
242
  getTask: '--task_id',
228
- createTask: '--title, --project_id, --owner_id and --category_id',
243
+ createTask: '--title and --project_id',
229
244
  searchTasks: '--project_id and --query',
230
245
  searchSubtasks: '--project_id, --iteration_id, --user_id, --page',
231
246
  getAllActiveIterations: '--project_id',
@@ -38,15 +38,15 @@ const kbService = {
38
38
  .option('--user <user>', 'Specify user')
39
39
  .option('--gateway <url>', 'Override kanboard gateway')
40
40
  .action(createKbAction('getTask'));
41
- // createTask 支持快捷参数 --title / --project_id / --owner_id / --category_id,且 --params 仍然可用
41
+ // createTask 支持快捷参数 --title / --project_id(必填),--owner_id / --category_id(可选),且 --params 仍然可用
42
42
  cmd
43
43
  .command('createTask')
44
44
  .description(METHOD_DESCRIPTIONS.createTask)
45
45
  .option('--params <json>', 'JSON/JSON5 params; CLI fills jsonrpc/method/id')
46
- .option('--title <title>', 'Task title (shortcut)')
47
- .option('--project_id <id>', 'Project ID (shortcut)')
48
- .option('--owner_id <id>', 'Owner user ID (shortcut)')
49
- .option('--category_id <id>', 'Category ID (shortcut)')
46
+ .option('--title <title>', 'Task title (shortcut, required)')
47
+ .option('--project_id <id>', 'Project ID (shortcut, required)')
48
+ .option('--owner_id <id>', 'Owner user ID (shortcut, optional)')
49
+ .option('--category_id <id>', 'Category ID (shortcut, optional)')
50
50
  .option('--user <user>', 'Specify user')
51
51
  .option('--gateway <url>', 'Override kanboard gateway')
52
52
  .action(createKbAction('createTask'));
@@ -36,15 +36,21 @@ export const PARAM_DEFS = {
36
36
  ],
37
37
  // 写入方法
38
38
  createTask: [
39
- { name: 'title', type: 'string', required: true, description: '需求标题' },
40
- { name: 'project_id', type: 'integer', required: true, description: '项目 ID' },
41
- { name: 'owner_id', type: 'integer', required: true, description: '处理人 Kanboard 用户 ID' },
42
- { name: 'is_bug', type: 'integer', required: false, description: '是否为 bug,1 表示 bug,0 表示需求' },
43
- { name: 'description', type: 'string', required: false, description: '需求描述(支持 Markdown)' },
44
- { name: 'category_id', type: 'integer', required: true, description: '分类 ID' },
45
- { name: 'iteration_id', type: 'integer', required: false, description: '迭代 ID' },
46
- { name: 'column_id', type: 'integer', required: false, description: '状态列 ID' },
39
+ { name: 'title', type: 'string', required: true, description: '需求/bug 标题' },
40
+ { name: 'project_id', type: 'integer', required: true, description: '所属项目 ID。接口按项目检查创建权限。' },
41
+ { name: 'description', type: 'string', required: false, description: '描述内容' },
42
+ { name: 'owner_id', type: 'integer', required: false, description: '处理人 Kanboard 用户 ID。处理人必须可分配到该项目。' },
43
+ { name: 'creator_id', type: 'integer', required: false, description: '创建人 Kanboard 用户 ID。登录态存在时创建人会使用当前用户。' },
44
+ { name: 'category_id', type: 'integer', required: false, description: '分类 ID。服务端会根据分类和 bug 类型选择泳道。' },
45
+ { name: 'iteration_id', type: 'integer', required: false, description: '迭代 ID;未传或为 0 时由服务端选择需求池或首个迭代。' },
46
+ { name: 'date_due', type: 'string', required: false, description: '截止日期,建议使用 YYYY-MM-DD' },
47
+ { name: 'date_started', type: 'string', required: false, description: '开始日期,建议使用 YYYY-MM-DD' },
48
+ { name: 'priority', type: 'integer', required: false, description: '优先级' },
49
+ { name: 'score', type: 'integer', required: false, description: '复杂度/分值' },
50
+ { name: 'tags', type: 'array<string>', required: false, description: '标签列表,例如 ["Meet", "Sky", "Co"]' },
47
51
  { name: 'cc_id', type: 'array<string>', required: false, description: '抄送人列表。数组元素格式为 用户ID-用户名称,例如 ["22-Alice", "33-Bob"];未传时不设置抄送人。' },
52
+ { name: 'is_bug', type: 'integer', required: false, description: '是否为 bug,1 表示 bug,0 表示需求' },
53
+ { name: 'platform', type: 'string', required: false, description: 'bug 平台,例如 Server,未传时保存为空字符串' },
48
54
  ],
49
55
  createSubtask: [
50
56
  { name: 'task_id', type: 'integer', required: true, description: '父需求/bug ID' },
@@ -56,23 +62,33 @@ export const PARAM_DEFS = {
56
62
  ],
57
63
  updateTask: [
58
64
  { name: 'id', type: 'integer', required: true, description: '需求/bug ID' },
59
- { name: 'title', type: 'string', required: false, description: '需求标题' },
60
- { name: 'owner_id', type: 'integer', required: false, description: '处理人 Kanboard 用户 ID' },
61
- { name: 'description', type: 'string', required: false, description: '需求描述(支持 Markdown)' },
65
+ { name: 'title', type: 'string', required: false, description: '标题' },
66
+ { name: 'description', type: 'string', required: false, description: '描述,支持 Kanboard 当前配置下的 Markdown/文本内容。' },
67
+ { name: 'owner_id', type: 'integer', required: false, description: '处理人用户 ID。目标用户必须可分配到该项目。' },
62
68
  { name: 'category_id', type: 'integer', required: false, description: '分类 ID' },
63
- { name: 'iteration_id', type: 'integer', required: false, description: '迭代 ID' },
64
- { name: 'column_id', type: 'integer', required: false, description: '状态列 ID' },
69
+ { name: 'iteration_id', type: 'integer', required: false, description: '迭代 ID。传入后修改需求/bug 所属迭代;未传时保持原迭代不变。' },
70
+ { name: 'date_due', type: 'string', required: false, description: '截止日期,建议使用 YYYY-MM-DD' },
71
+ { name: 'date_started', type: 'string', required: false, description: '开始日期,建议使用 YYYY-MM-DD' },
72
+ { name: 'priority', type: 'integer', required: false, description: '优先级' },
73
+ { name: 'score', type: 'integer', required: false, description: '复杂度/分值' },
74
+ { name: 'color_id', type: 'string', required: false, description: '颜色 ID,例如 yellow、blue、green' },
75
+ { name: 'reference', type: 'string', required: false, description: '外部引用编号' },
76
+ { name: 'tags', type: 'array<string>', required: false, description: '标签列表,例如 ["Meet", "Sky", "Co"]' },
65
77
  { name: 'cc_id', type: 'array<string>', required: false, description: '抄送人列表。数组元素格式为 用户ID-用户名称,例如 ["22-Alice", "33-Bob"];传空数组 [] 表示清空抄送人,未传则保持不变。' },
78
+ { name: 'time_spent', type: 'number', required: false, description: '已花费时间' },
79
+ { name: 'time_estimated', type: 'number', required: false, description: '预估时间' },
66
80
  ],
67
81
  updateSubtask: [
68
82
  { name: 'id', type: 'integer', required: true, description: '子任务 ID' },
69
- { name: 'task_id', type: 'integer', required: true, description: '父需求/bug ID' },
83
+ { name: 'task_id', type: 'integer', required: true, description: '父需求/bug ID。接口按该任务检查项目权限。' },
70
84
  { name: 'title', type: 'string', required: false, description: '子任务标题' },
71
- { name: 'user_id', type: 'integer', required: false, description: '子任务处理人,传 0 表示未分配' },
72
- { name: 'time_estimated', type: 'number', required: false, description: '预估时间(小时)' },
73
- { name: 'time_spent', type: 'number', required: false, description: '已花费时间(小时)' },
74
- { name: 'status', type: 'integer', required: false, description: '状态:0 未开始,1 进行中,2 已完成' },
85
+ { name: 'user_id', type: 'integer', required: false, description: '子任务处理人 Kanboard 用户 ID。传 0 表示未分配。' },
86
+ { name: 'time_estimated', type: 'number', required: false, description: '预估时间,最多保留两位小数' },
87
+ { name: 'time_spent', type: 'number', required: false, description: '已花费时间,最多保留两位小数' },
88
+ { name: 'status', type: 'integer', required: false, description: '子任务状态:0 未开始,1 进行中,2 已完成' },
75
89
  { name: 'position', type: 'integer', required: false, description: '子任务排序位置' },
90
+ { name: 'plan_begin_time', type: 'string', required: false, description: '计划开始时间,格式 YYYY-MM-DD' },
91
+ { name: 'plan_end_time', type: 'string', required: false, description: '计划结束时间,格式 YYYY-MM-DD' },
76
92
  ],
77
93
  createComment: [
78
94
  { name: 'task_id', type: 'integer', required: true, description: '需求/bug ID' },
@@ -1,6 +1,6 @@
1
1
  export const METHOD_SHORTCUT_PARAMS = {
2
2
  getTask: ['task_id'],
3
- createTask: ['title', 'project_id', 'owner_id', 'category_id'],
3
+ createTask: ['title', 'project_id'],
4
4
  searchTasks: ['project_id', 'query'],
5
5
  searchSubtasks: ['project_id', 'iteration_id', 'user_id', 'page'],
6
6
  getAllActiveIterations: ['project_id'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meet-im/lxcli",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "连续科技命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",