@siming-org/cli 0.7.3 → 0.8.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.
Files changed (2) hide show
  1. package/dist/index.js +455 -242
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1263,6 +1263,9 @@ function readStdinLine(stream) {
1263
1263
  async function handleCreateIssue(client, input) {
1264
1264
  return client.postJson("/api/issues", input);
1265
1265
  }
1266
+ async function handleDoneIssue(client, issueId, note) {
1267
+ return client.postJson(`/api/issues/${issueId}/done`, note !== void 0 ? { note } : {});
1268
+ }
1266
1269
  async function handleListIssues(client, filters) {
1267
1270
  const params = new URLSearchParams();
1268
1271
  if (filters.status) params.set("status", filters.status);
@@ -1307,17 +1310,24 @@ async function handleUpdateProject(client, id, patch) {
1307
1310
  return client.putJson(`/api/projects/${encodeURIComponent(id)}`, patch);
1308
1311
  }
1309
1312
  var OBJECT_ID_HEX = /^[a-f0-9]{24}$/;
1310
- async function resolveProjectId(client, input) {
1313
+ function formatCandidateHint(projects, input) {
1314
+ const q = input.toLowerCase();
1315
+ const hits = projects.filter((p) => p.key.toLowerCase().includes(q) || p.name.toLowerCase().includes(q)).slice(0, 5).map((p) => `${p.key} (${p.name})`);
1316
+ if (hits.length === 0) return void 0;
1317
+ return `\u76F8\u8FD1\u9879\u76EE\uFF1A${hits.join("\u3001")}\uFF1B\u68C0\u67E5\u62FC\u5199\uFF0C\u6216 siming project list \u67E5\u770B\u5168\u90E8\u9879\u76EE`;
1318
+ }
1319
+ async function resolveProjectId(client, input, opts) {
1311
1320
  if (OBJECT_ID_HEX.test(input)) return { success: true, data: input };
1312
1321
  const list = await handleListProjects(client);
1313
1322
  if (!list.success) return list;
1314
1323
  const hit = list.data.find((p) => p.key === input);
1315
1324
  if (!hit) {
1325
+ const candidateHint = opts?.withCandidates === true ? formatCandidateHint(list.data, input) : void 0;
1316
1326
  const error = {
1317
1327
  message: `project "${input}" not found`,
1318
1328
  httpStatus: 404,
1319
1329
  bizCode: "PROJECT_NOT_FOUND",
1320
- hint: `check \`siming project list\` for available keys; pass --project <key|id>`
1330
+ hint: candidateHint ?? `check \`siming project list\` for available keys; pass --project <key|id>`
1321
1331
  };
1322
1332
  return fail(error);
1323
1333
  }
@@ -1331,6 +1341,74 @@ async function resolveProjectId(client, input) {
1331
1341
  }
1332
1342
  return { success: true, data: hit.id };
1333
1343
  }
1344
+ async function findProjectDetailByKey(client, key) {
1345
+ const list = await handleListProjects(client);
1346
+ if (!list.success) return list;
1347
+ const hit = list.data.find((p) => p.key === key);
1348
+ if (hit === void 0) {
1349
+ return fail({
1350
+ message: `workspace defaultProject "${key}" not found (removed or renamed)`,
1351
+ httpStatus: 0,
1352
+ bizCode: "DEFAULT_PROJECT_INVALID",
1353
+ hint: DEFAULT_PROJECT_HINT
1354
+ });
1355
+ }
1356
+ if (hit.id === void 0) {
1357
+ return fail({
1358
+ message: `workspace defaultProject "${key}" has no id`,
1359
+ httpStatus: 500,
1360
+ hint: "server returned a project without id; check `siming project list`"
1361
+ });
1362
+ }
1363
+ return ok2({ id: hit.id, status: hit.status });
1364
+ }
1365
+ var MCP_PROJECT_HINT = "MCP \u8C03\u7528\u8BF7\u4F20 projectId \u5B57\u6BB5\uFF08\u9879\u76EE key \u6216 24-hex id \u53CC\u5F62\u6001\uFF0C\u81EA\u52A8\u5F52\u4E00\uFF09\uFF1B\u53EF\u7528 `siming project list` \u67E5\u8BE2\u53EF\u7528\u9879\u76EE";
1366
+ var DEFAULT_PROJECT_HINT = "siming config unset defaultProject \u540E\u91CD\u8BD5\uFF0C\u6216 siming config set defaultProject <key|id> \u91CD\u8BBE\uFF1Bsiming project list \u67E5\u770B\u53EF\u7528\u9879\u76EE";
1367
+ var PROJECT_REQUIRED_HINT = "\u663E\u5F0F\u4F20 --project <key|id>\uFF0C\u6216 siming config set defaultProject <key|id> \u914D\u7F6E\u9ED8\u8BA4\u9879\u76EE\uFF1Bsiming project list \u67E5\u770B\u53EF\u7528\u9879\u76EE";
1368
+ async function resolveScopedProject(client, explicit, tail, opts) {
1369
+ const value = explicit !== void 0 && explicit.trim().length > 0 ? explicit.trim() : void 0;
1370
+ if (value !== void 0) {
1371
+ const r = await resolveProjectId(client, value, { withCandidates: true });
1372
+ if (!r.success) {
1373
+ return fail(
1374
+ opts?.channel === "mcp" && r.error.bizCode === "PROJECT_NOT_FOUND" ? { ...r.error, hint: MCP_PROJECT_HINT } : r.error
1375
+ );
1376
+ }
1377
+ return ok2({ projectId: r.data, source: "explicit" });
1378
+ }
1379
+ if (tail === "explicit-only") {
1380
+ return ok2({ source: "explicit" });
1381
+ }
1382
+ const wsDefault = readWorkspaceConfig()?.defaultProject;
1383
+ if (wsDefault !== void 0) {
1384
+ const detail = await findProjectDetailByKey(client, wsDefault);
1385
+ if (!detail.success) return detail;
1386
+ if (detail.data.status !== "active") {
1387
+ return fail({
1388
+ message: `workspace defaultProject "${wsDefault}" is archived`,
1389
+ httpStatus: 0,
1390
+ bizCode: "DEFAULT_PROJECT_INVALID",
1391
+ hint: DEFAULT_PROJECT_HINT
1392
+ });
1393
+ }
1394
+ return ok2({ projectId: detail.data.id, source: "workspace-default" });
1395
+ }
1396
+ if (tail === "ws-default") {
1397
+ return ok2({ source: "delegated" });
1398
+ }
1399
+ const list = await handleListProjects(client);
1400
+ if (!list.success) return list;
1401
+ const instanceDefault = list.data.find((p) => p.key === "default");
1402
+ if (instanceDefault === void 0 || instanceDefault.id === void 0) {
1403
+ return fail({
1404
+ message: `${opts?.commandLabel ?? "this command"} needs a project: no --project given, no workspace defaultProject, and no key=default project on server`,
1405
+ httpStatus: 0,
1406
+ bizCode: "PROJECT_REQUIRED",
1407
+ hint: PROJECT_REQUIRED_HINT
1408
+ });
1409
+ }
1410
+ return ok2({ projectId: instanceDefault.id, source: "instance-default" });
1411
+ }
1334
1412
  async function resolveProjectKey(client, input) {
1335
1413
  const list = await handleListProjects(client);
1336
1414
  if (!list.success) return list;
@@ -1463,7 +1541,9 @@ var issueArgs = {
1463
1541
  // create
1464
1542
  title: z3.string().optional(),
1465
1543
  description: z3.string().optional(),
1466
- // 公共:create 缺省按工作区 defaultProject,无配置时按 key=default / query 过滤(24-hex)
1544
+ /** create 必填(T202609200001):人类审批原话逐字(1-500 字)——per-action 必填以 ISSUE_FIELD_SPECS.create.required 为权威 */
1545
+ approved: z3.string().optional(),
1546
+ // 公共:create 缺省按工作区 defaultProject(key|id 双形态通道层归一,失效报错)/ query 过滤(空/未传不过滤)
1467
1547
  projectId: z3.string().optional(),
1468
1548
  // query:view 缺省 open(有效未跟进完);与 REST 词汇统一(status/taskId 指定时切对应过滤)
1469
1549
  view: hostNeutralEnum(z3.enum(["open", "all"])).optional(),
@@ -1600,7 +1680,7 @@ var TASK_FIELD_SPECS = {
1600
1680
  "archnote-add": { required: ["taskId"], optional: ["text", "textItems"] }
1601
1681
  };
1602
1682
  var ISSUE_FIELD_SPECS = {
1603
- create: { required: ["title"], optional: ["description", "projectId"] },
1683
+ create: { required: ["title", "approved"], optional: ["description", "projectId"] },
1604
1684
  query: { required: [], optional: ["view", "status", "taskId", "projectId", "page", "limit"] }
1605
1685
  };
1606
1686
  function createMcpServer() {
@@ -1608,7 +1688,7 @@ function createMcpServer() {
1608
1688
  const client = createClient(void 0, "mcp");
1609
1689
  server.tool(
1610
1690
  "siming_task",
1611
- "task \u8D44\u6E90\u57DF\u805A\u5408\u5DE5\u5177\uFF08\u4EFB\u52A1\u5168\u64CD\u4F5C\u7ECF\u672C\u5DE5\u5177 + action \u53C2\u6570\uFF09\u3002action \u5FC5\u586B\uFF1B\u8C03\u7528\u65B9\u5E94\u5728 fieldMask \u4E2D\u5217\u51FA\u672C\u6B21\u771F\u5B9E\u63D0\u4F9B\u7684\u53EF\u9009\u5B57\u6BB5\uFF0Cmask \u5916\u7531\u5BBF\u4E3B\u81EA\u52A8\u586B\u5145\u7684\u7A7A\u4E32\u3001\u7A7A\u6570\u7EC4\u3001false\u30010 \u5747\u5FFD\u7565\uFF0Cmask \u5185\u7A7A\u6570\u7EC4/false \u8868\u793A\u663E\u5F0F\u6E05\u7A7A\u6216\u5173\u95ED\uFF1B\u5404 action \u56FA\u5B9A\u5FC5\u586B\u5B57\u6BB5\u65E0\u9700\u5199\u5165 mask\u3002\u5176\u4F59\u53C2\u6570\u5168\u53EF\u9009\u3001\u6309 action \u8FD0\u884C\u65F6\u6821\u9A8C\u5FC5\u586B\uFF08\u7F3A\u5931\u8FD4\u56DE\u5931\u8D25\u4E09\u8981\u7D20\uFF1A\u5BF9\u8C61+\u6839\u56E0+\u4FEE\u590D\u6307\u5F15\uFF09\u3002\u5199\u64CD\u4F5C\uFF08create/doc-*/record-*/archnote-add/pause/resume/cancel\uFF09\u54CD\u5E94\u4E3A\u8F7B\u91CF\u56DE\u6267\uFF1A{taskId, updated(\u89E6\u53CA\u5B57\u6BB5\u8DEF\u5F84), entry(\u65B0\u6761\u76EE id+kind\uFF0C\u4F9B\u540E\u7EED patch \u5B9A\u4F4D), echo(\u5199\u5165\u5185\u5BB9\u56DE\u663E), updatedAt, task(\u4EFB\u52A1\u8FDB\u5EA6\u6458\u8981 progress: {completed,total})}\u2014\u2014\u4E0D\u8FD4\u56DE\u5B8C\u6574\u4EFB\u52A1\u5B9E\u4F53\u3002action \u6E05\u5355\uFF08\u62EC\u53F7\u5185\u4E3A\u8BE5 action \u5FC5\u586B\u53C2\u6570\uFF09\uFF1Acreate(templateId,taskTitle,track\uFF1B\u53EF\u9009 type=feature|bugfix|ui-tweak|research\u3001skipNodes\u3001projectId\uFF1BtemplateId \u652F\u6301 code|id \u53CC\u5F62\u6001\u2014\u2014code \u4E3A\u6A21\u677F\u4E1A\u52A1\u6807\u8BC6\uFF08\u9879\u76EE\u5185\u552F\u4E00\u3001kebab-case \u5FC5\u542B\u8FDE\u5B57\u7B26\uFF0C\u5982 full-workflow\uFF09\uFF0C\u63A8\u8350\u5F15\u7528\u5F62\u6001\uFF0C\u5BFB\u5740\u987B\u914D projectId \u6307\u5B9A\u9879\u76EE\u57DF\uFF1B24-hex \u6570\u636E\u5E93 id \u76F4\u67E5\u517C\u5BB9\uFF1Bcode \u5F62\u6001\u4E0D\u5B58\u5728\u65F6\u9519\u8BEF\u542B candidates \u76F8\u8FD1\u5019\u9009\u6E05\u5355\uFF08id/code/name \u22645 \u6761\uFF09\uFF1B\u54CD\u5E94 {task, firstNode}\u2014\u2014firstNode \u5373\u9996\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u514D\u8C03 context)\uFF1Bquery(taskId \u8FD4\u56DE\u5355\u4EFB\u52A1\u8BE6\u60C5\uFF0C\u7F3A\u7701\u8FD4\u56DE\u5217\u8868\uFF0C\u53EF\u9009 status/track/projectId/page/limit)\uFF1Bcontext(taskId\uFF1B\u53EF\u9009 view=basic|full\u2014\u2014basic \u7F3A\u7701\u4E0D\u643A\u5E26 artifact \u5168\u6587\u5FEB\u7167\uFF0Cfull \u65AD\u70B9\u7EED\u8DD1\u9700\u5386\u53F2\u4EA7\u7269\u5168\u6587\u65F6\u7528\u3002\u26A0 \u514D\u91CD\u8BFB\uFF1Aadvance/approve \u6D41\u8F6C\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u5185\u6D41\u8F6C\u540E\u76F4\u63A5\u5F00\u5DE5\u3001\u65E0\u9700\u518D\u8C03 context\uFF1B\u4EC5\u65B0\u4F1A\u8BDD/\u65AD\u70B9\u7EED\u8DD1\u8C03\u7528)\uFF1Bnode(taskId,view=prompt|skills\uFF1BnodeId \u7F3A\u7701\u7528\u5F53\u524D\u8282\u70B9\uFF1B\u26A0 view \u503C\u968F action \u4E0D\u540C\uFF1Anode\u2192prompt|skills\uFF0Ccontext\u2192basic|full\uFF0C\u9519\u914D\u62A5\u9519)\uFF1Badvance(taskId\uFF1Bnote \u5199 history\u3001summary \u5199 nodeRecords\uFF0C\u4E24\u4F4D\u72EC\u7ACB\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bapprove(taskId,decision=approved|rejected\uFF1B\u53EF\u9009 comment\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bpause(taskId\uFF1B\u53EF\u9009 reason)\uFF1Bresume(taskId\uFF1B\u53EF\u9009 resumeNote)\uFF1Bcancel(taskId\uFF1B\u53EF\u9009 reason 1-500 \u5B57\u7B26\uFF1B\u26A0 \u7EC8\u6001\u4E0D\u53EF\u9006\u2014\u2014active/paused \u53EF\u53D6\u6D88\uFF0Ccompleted/cancelled \u62D2\u7EDD\u5E76\u8FD4\u56DE TASK_TERMINATED\uFF1B\u53D6\u6D88\u540E\u7ECF\u672C\u5DE5\u5177\u7684\u6D41\u8F6C\u4E0E\u8BB0\u5F55\u5199\u64CD\u4F5C\u88AB\u62D2)\uFF1Bhistory(taskId)\uFF1Binstance(taskId\u2014\u2014\u67E5\u770B\u4EFB\u52A1 DAG \u5B9E\u4F8B\u5FEB\u7167\uFF1A\u521B\u5EFA\u4EFB\u52A1\u65F6\u51BB\u7ED3\u7684\u6A21\u677F\u5B9E\u4F8B\uFF0C\u6A21\u677F\u540E\u7EED\u53D8\u66F4\u4E0D\u56DE\u6EAF)\u3002\u8BB0\u5F55\u5199\u5165\uFF1Arecord-set(taskId,node,summary)\uFF1Brecord-check-add(taskId,node,item\uFF1B\u53EF\u9009 passed \u7F3A\u7701 true\uFF1B\u56DE\u6267 entry.id \u4F9B record-check-patch \u5B9A\u4F4D)\uFF1Brecord-check-patch(taskId,node,checkId\uFF1B\u53EF\u9009 passed)\uFF1Brecord-artifact-add(taskId,node,artifactType=prd|tech|code|test|doc|other\uFF1Bpath \u7EAF\u5F15\u7528 / file \u8BFB\u672C\u673A\u6587\u4EF6 / content \u76F4\u4F20\u5168\u6587\u2014\u2014file \u4E0E content\u3001file \u4E0E path \u4E92\u65A5\uFF0Ccontent \u53EF\u4E0E path \u6210\u5BF9\u6216\u5355\u72EC\uFF0C\u5355\u72EC\u65F6\u767B\u8BB0\u503C\u5408\u6210 mcp-inline/<type>\uFF1B\u53EF\u9009 note)\uFF1Brecord-confirm(taskId,node,quote\u2014\u2014\u6682\u505C\u70B9\u7528\u6237\u786E\u8BA4\u539F\u6587\u9010\u5B57)\uFF1Brecord-decision-add(taskId,node,topic,decisionText)\uFF1Brecord-review-set(taskId,node,verdict=pass|fail,rounds,critical)\u3002\u9700\u6C42\u6587\u6863\uFF1Adoc-set(taskId\uFF1Bwhat/why/trackNote \u81F3\u5C11\u4F20\u4E00)\uFF1Bdoc-acceptance-add(taskId,text)\uFF1Bdoc-non-goal-add(taskId,text)\u3002\u7D20\u6750\uFF1Aarchnote-add(taskId,text)\u3002\u6279\u91CF\u5F62\u6001\uFF08\u63A8\u8350\uFF0C\u516D\u7C7B\u540C\u6784\uFF09\uFF1Arecord-check-add \u4F20 checkItems=[{item,passed?}]\u3001record-artifact-add \u4F20 artifactEntries=[{artifactType,path?/file?/content?,note?}]\u3001record-decision-add \u4F20 decisionEntries=[{topic,decisionText}]\u3001doc-acceptance-add/doc-non-goal-add/archnote-add \u4F20 textItems=[string]\u2014\u2014\u6279\u91CF\u53C2\u6570\u4E0E\u5355\u6761\u53C2\u6570\u4E8C\u9009\u4E00\uFF0C\u4E00\u6B21\u8C03\u7528\u5199\u591A\u6761\uFF08\u4E0A\u9650 50 \u6761/\u6279\uFF0Cartifact \u5168\u6587\u5FEB\u7167\u603B\u91CF \u22642M \u5B57\u7B26/\u6279\uFF09\uFF1B\u90E8\u5206\u5931\u8D25\u4FDD\u7559\u6210\u529F\u6761\u76EE\u5E76\u9010\u6761\u62A5\u544A\uFF08failures[].index=\u539F\u59CB\u63D0\u4EA4\u5E8F\uFF0C\u53EA\u91CD\u8BD5\u5931\u8D25\u9879\uFF09\uFF0Capplied=0 \u6574\u4F53\u62A5\u5931\u8D25\u3002",
1691
+ "task \u8D44\u6E90\u57DF\u805A\u5408\u5DE5\u5177\uFF08\u4EFB\u52A1\u5168\u64CD\u4F5C\u7ECF\u672C\u5DE5\u5177 + action \u53C2\u6570\uFF09\u3002action \u5FC5\u586B\uFF1B\u8C03\u7528\u65B9\u5E94\u5728 fieldMask \u4E2D\u5217\u51FA\u672C\u6B21\u771F\u5B9E\u63D0\u4F9B\u7684\u53EF\u9009\u5B57\u6BB5\uFF0Cmask \u5916\u7531\u5BBF\u4E3B\u81EA\u52A8\u586B\u5145\u7684\u7A7A\u4E32\u3001\u7A7A\u6570\u7EC4\u3001false\u30010 \u5747\u5FFD\u7565\uFF0Cmask \u5185\u7A7A\u6570\u7EC4/false \u8868\u793A\u663E\u5F0F\u6E05\u7A7A\u6216\u5173\u95ED\uFF1B\u5404 action \u56FA\u5B9A\u5FC5\u586B\u5B57\u6BB5\u65E0\u9700\u5199\u5165 mask\u3002\u5176\u4F59\u53C2\u6570\u5168\u53EF\u9009\u3001\u6309 action \u8FD0\u884C\u65F6\u6821\u9A8C\u5FC5\u586B\uFF08\u7F3A\u5931\u8FD4\u56DE\u5931\u8D25\u4E09\u8981\u7D20\uFF1A\u5BF9\u8C61+\u6839\u56E0+\u4FEE\u590D\u6307\u5F15\uFF09\u3002projectId \u4E3A key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\u4E3A 24-hex id\uFF1B\u663E\u5F0F\u6307\u5B9A\u89E3\u6790\u5931\u8D25\u62A5\u9519\u4E0D\u515C\u5E95\uFF1B\u7F3A\u7701\u8BED\u4E49\u6309 action\uFF1Acreate \u843D\u9ED8\u8BA4\u9879\u76EE\u94FE\uFF08\u5DE5\u4F5C\u533A defaultProject \u5931\u6548\u62A5\u9519\uFF0C\u65E0\u914D\u7F6E\u4E0D\u4F20\u7531 server \u6309\u6A21\u677F\u5F52\u5C5E\u515C\u5E95\uFF09\uFF0Cquery \u4E0D\u8FC7\u6EE4\uFF08\u5168\u5C40\u67E5\u8BE2\uFF09\u3002\u5199\u64CD\u4F5C\uFF08create/doc-*/record-*/archnote-add/pause/resume/cancel\uFF09\u54CD\u5E94\u4E3A\u8F7B\u91CF\u56DE\u6267\uFF1A{taskId, updated(\u89E6\u53CA\u5B57\u6BB5\u8DEF\u5F84), entry(\u65B0\u6761\u76EE id+kind\uFF0C\u4F9B\u540E\u7EED patch \u5B9A\u4F4D), echo(\u5199\u5165\u5185\u5BB9\u56DE\u663E), updatedAt, task(\u4EFB\u52A1\u8FDB\u5EA6\u6458\u8981 progress: {completed,total})}\u2014\u2014\u4E0D\u8FD4\u56DE\u5B8C\u6574\u4EFB\u52A1\u5B9E\u4F53\u3002action \u6E05\u5355\uFF08\u62EC\u53F7\u5185\u4E3A\u8BE5 action \u5FC5\u586B\u53C2\u6570\uFF09\uFF1Acreate(templateId,taskTitle,track\uFF1B\u53EF\u9009 type=feature|bugfix|ui-tweak|research\u3001skipNodes\u3001projectId\uFF1BtemplateId \u652F\u6301 code|id \u53CC\u5F62\u6001\u2014\u2014code \u4E3A\u6A21\u677F\u4E1A\u52A1\u6807\u8BC6\uFF08\u9879\u76EE\u5185\u552F\u4E00\u3001kebab-case \u5FC5\u542B\u8FDE\u5B57\u7B26\uFF0C\u5982 full-workflow\uFF09\uFF0C\u63A8\u8350\u5F15\u7528\u5F62\u6001\uFF0C\u5BFB\u5740\u987B\u914D projectId \u6307\u5B9A\u9879\u76EE\u57DF\uFF1B24-hex \u6570\u636E\u5E93 id \u76F4\u67E5\u517C\u5BB9\uFF1Bcode \u5F62\u6001\u4E0D\u5B58\u5728\u65F6\u9519\u8BEF\u542B candidates \u76F8\u8FD1\u5019\u9009\u6E05\u5355\uFF08id/code/name \u22645 \u6761\uFF09\uFF1B\u54CD\u5E94 {task, firstNode}\u2014\u2014firstNode \u5373\u9996\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u514D\u8C03 context)\uFF1Bquery(taskId \u8FD4\u56DE\u5355\u4EFB\u52A1\u8BE6\u60C5\uFF0C\u7F3A\u7701\u8FD4\u56DE\u5217\u8868\uFF0C\u53EF\u9009 status/track/projectId/page/limit)\uFF1Bcontext(taskId\uFF1B\u53EF\u9009 view=basic|full\u2014\u2014basic \u7F3A\u7701\u4E0D\u643A\u5E26 artifact \u5168\u6587\u5FEB\u7167\uFF0Cfull \u65AD\u70B9\u7EED\u8DD1\u9700\u5386\u53F2\u4EA7\u7269\u5168\u6587\u65F6\u7528\u3002\u26A0 \u514D\u91CD\u8BFB\uFF1Aadvance/approve \u6D41\u8F6C\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u5185\u6D41\u8F6C\u540E\u76F4\u63A5\u5F00\u5DE5\u3001\u65E0\u9700\u518D\u8C03 context\uFF1B\u4EC5\u65B0\u4F1A\u8BDD/\u65AD\u70B9\u7EED\u8DD1\u8C03\u7528)\uFF1Bnode(taskId,view=prompt|skills\uFF1BnodeId \u7F3A\u7701\u7528\u5F53\u524D\u8282\u70B9\uFF1B\u26A0 view \u503C\u968F action \u4E0D\u540C\uFF1Anode\u2192prompt|skills\uFF0Ccontext\u2192basic|full\uFF0C\u9519\u914D\u62A5\u9519)\uFF1Badvance(taskId\uFF1Bnote \u5199 history\u3001summary \u5199 nodeRecords\uFF0C\u4E24\u4F4D\u72EC\u7ACB\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bapprove(taskId,decision=approved|rejected\uFF1B\u53EF\u9009 comment\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bpause(taskId\uFF1B\u53EF\u9009 reason)\uFF1Bresume(taskId\uFF1B\u53EF\u9009 resumeNote)\uFF1Bcancel(taskId\uFF1B\u53EF\u9009 reason 1-500 \u5B57\u7B26\uFF1B\u26A0 \u7EC8\u6001\u4E0D\u53EF\u9006\u2014\u2014active/paused \u53EF\u53D6\u6D88\uFF0Ccompleted/cancelled \u62D2\u7EDD\u5E76\u8FD4\u56DE TASK_TERMINATED\uFF1B\u53D6\u6D88\u540E\u7ECF\u672C\u5DE5\u5177\u7684\u6D41\u8F6C\u4E0E\u8BB0\u5F55\u5199\u64CD\u4F5C\u88AB\u62D2)\uFF1Bhistory(taskId)\uFF1Binstance(taskId\u2014\u2014\u67E5\u770B\u4EFB\u52A1 DAG \u5B9E\u4F8B\u5FEB\u7167\uFF1A\u521B\u5EFA\u4EFB\u52A1\u65F6\u51BB\u7ED3\u7684\u6A21\u677F\u5B9E\u4F8B\uFF0C\u6A21\u677F\u540E\u7EED\u53D8\u66F4\u4E0D\u56DE\u6EAF)\u3002\u8BB0\u5F55\u5199\u5165\uFF1Arecord-set(taskId,node,summary)\uFF1Brecord-check-add(taskId,node,item\uFF1B\u53EF\u9009 passed \u7F3A\u7701 true\uFF1B\u56DE\u6267 entry.id \u4F9B record-check-patch \u5B9A\u4F4D)\uFF1Brecord-check-patch(taskId,node,checkId\uFF1B\u53EF\u9009 passed)\uFF1Brecord-artifact-add(taskId,node,artifactType=prd|tech|code|test|doc|other\uFF1Bpath \u7EAF\u5F15\u7528 / file \u8BFB\u672C\u673A\u6587\u4EF6 / content \u76F4\u4F20\u5168\u6587\u2014\u2014file \u4E0E content\u3001file \u4E0E path \u4E92\u65A5\uFF0Ccontent \u53EF\u4E0E path \u6210\u5BF9\u6216\u5355\u72EC\uFF0C\u5355\u72EC\u65F6\u767B\u8BB0\u503C\u5408\u6210 mcp-inline/<type>\uFF1B\u53EF\u9009 note)\uFF1Brecord-confirm(taskId,node,quote\u2014\u2014\u6682\u505C\u70B9\u7528\u6237\u786E\u8BA4\u539F\u6587\u9010\u5B57)\uFF1Brecord-decision-add(taskId,node,topic,decisionText)\uFF1Brecord-review-set(taskId,node,verdict=pass|fail,rounds,critical)\u3002\u9700\u6C42\u6587\u6863\uFF1Adoc-set(taskId\uFF1Bwhat/why/trackNote \u81F3\u5C11\u4F20\u4E00)\uFF1Bdoc-acceptance-add(taskId,text)\uFF1Bdoc-non-goal-add(taskId,text)\u3002\u7D20\u6750\uFF1Aarchnote-add(taskId,text)\u3002\u6279\u91CF\u5F62\u6001\uFF08\u63A8\u8350\uFF0C\u516D\u7C7B\u540C\u6784\uFF09\uFF1Arecord-check-add \u4F20 checkItems=[{item,passed?}]\u3001record-artifact-add \u4F20 artifactEntries=[{artifactType,path?/file?/content?,note?}]\u3001record-decision-add \u4F20 decisionEntries=[{topic,decisionText}]\u3001doc-acceptance-add/doc-non-goal-add/archnote-add \u4F20 textItems=[string]\u2014\u2014\u6279\u91CF\u53C2\u6570\u4E0E\u5355\u6761\u53C2\u6570\u4E8C\u9009\u4E00\uFF0C\u4E00\u6B21\u8C03\u7528\u5199\u591A\u6761\uFF08\u4E0A\u9650 50 \u6761/\u6279\uFF0Cartifact \u5168\u6587\u5FEB\u7167\u603B\u91CF \u22642M \u5B57\u7B26/\u6279\uFF09\uFF1B\u90E8\u5206\u5931\u8D25\u4FDD\u7559\u6210\u529F\u6761\u76EE\u5E76\u9010\u6761\u62A5\u544A\uFF08failures[].index=\u539F\u59CB\u63D0\u4EA4\u5E8F\uFF0C\u53EA\u91CD\u8BD5\u5931\u8D25\u9879\uFF09\uFF0Capplied=0 \u6574\u4F53\u62A5\u5931\u8D25\u3002",
1612
1692
  taskArgs,
1613
1693
  async (rawArgs) => {
1614
1694
  const masked = applyFieldMask(rawArgs, TASK_FIELD_SPECS);
@@ -1620,13 +1700,15 @@ function createMcpServer() {
1620
1700
  if (templateId === void 0 || taskTitle === void 0 || track === void 0) {
1621
1701
  return requireError("create", ["templateId", "taskTitle", "track"], args);
1622
1702
  }
1703
+ const projectR = await resolveScopedProject(client, args.projectId, "ws-default", { channel: "mcp" });
1704
+ if (!projectR.success) return mcpContent(projectR);
1623
1705
  const input = {
1624
1706
  title: taskTitle,
1625
1707
  track,
1626
1708
  dagTemplateId: templateId,
1627
1709
  ...args.type !== void 0 ? { type: args.type } : {},
1628
1710
  ...args.skipNodes !== void 0 ? { skipNodes: args.skipNodes } : {},
1629
- ...args.projectId !== void 0 ? { projectId: args.projectId } : {}
1711
+ ...projectR.data.projectId !== void 0 ? { projectId: projectR.data.projectId } : {}
1630
1712
  };
1631
1713
  return mcpContent(await handleCreateTask(client, input));
1632
1714
  }
@@ -1640,11 +1722,17 @@ function createMcpServer() {
1640
1722
  if (args.taskId) {
1641
1723
  return mcpContent(await handleQueryState(client, args.taskId));
1642
1724
  }
1725
+ let queryProjectId;
1726
+ if (args.projectId !== void 0 && args.projectId.trim().length > 0) {
1727
+ const pr = await resolveScopedProject(client, args.projectId, "explicit-only", { channel: "mcp" });
1728
+ if (!pr.success) return mcpContent(pr);
1729
+ queryProjectId = pr.data.projectId;
1730
+ }
1643
1731
  return mcpContent(
1644
1732
  await handleListTasks(client, {
1645
1733
  ...args.status !== void 0 ? { status: args.status } : {},
1646
1734
  ...args.track !== void 0 ? { track: args.track } : {},
1647
- ...args.projectId !== void 0 ? { projectId: args.projectId } : {},
1735
+ ...queryProjectId !== void 0 ? { projectId: queryProjectId } : {},
1648
1736
  ...args.page !== void 0 ? { page: args.page } : {},
1649
1737
  ...args.limit !== void 0 ? { limit: args.limit } : {}
1650
1738
  })
@@ -1942,26 +2030,38 @@ function createMcpServer() {
1942
2030
  );
1943
2031
  server.tool(
1944
2032
  "siming_template",
1945
- "DAG \u6A21\u677F\u53EA\u8BFB\u67E5\u8BE2\uFF08\u4EFB\u52A1\u521B\u5EFA\u524D\u9009\u6A21\u677F/\u6838\u5BF9\u6A21\u677F\u7ED3\u6784\u7528\uFF1B\u672C\u5DE5\u5177\u65E0\u5199\u64CD\u4F5C\uFF09\u3002action\uFF1Alist=\u6A21\u677F\u5217\u8868\uFF08\u6982\u8981\u6295\u5F71\uFF0C\u542B id/code/name/version/isDefault/projectId/nodeCount/nodes \u6982\u8981/pausePoints/description\uFF0C\u4E0D\u542B\u8282\u70B9 prompt \u5168\u6587\uFF1B\u53EF\u9009 projectId \u8FC7\u6EE4\u9879\u76EE\u57DF\uFF1B\u8FD4\u56DE code \u4E3A\u540E\u7EED\u5F15\u7528\u6A21\u677F\u7684\u63A8\u8350\u5F62\u6001\uFF09\uFF1Bshow=\u6A21\u677F\u8BE6\u60C5\uFF08\u542B\u5168\u90E8\u8282\u70B9 prompt\uFF1BtemplateId \u652F\u6301 code|id \u53CC\u5F62\u6001\u2014\u2014code \u4E3A\u4E1A\u52A1\u6807\u8BC6\uFF08\u9879\u76EE\u5185\u552F\u4E00\u3001\u521B\u5EFA\u540E\u4E0D\u53EF\u53D8\u3001kebab-case \u5FC5\u542B\u8FDE\u5B57\u7B26\u4E14\u975E\u7EAF 24-hex \u5F62\u6001\uFF0C\u5982 full-workflow\uFF09\uFF0Ccode \u5F62\u6001\u5BFB\u5740\u987B\u914D projectId \u6307\u5B9A\u9879\u76EE\u57DF\uFF1B24 \u4F4D\u6570\u636E\u5E93 id \u76F4\u67E5\u517C\u5BB9\uFF0C\u65E0\u9700 projectId\uFF09\u3002\u6A21\u677F\u4E0D\u5B58\u5728\u65F6\u9519\u8BEF\u4FE1\u606F\u542B candidates \u5019\u9009\u6E05\u5355\uFF08\u9879\u76EE\u5185\u76F8\u8FD1\u6A21\u677F\u7684 id/code/name\uFF0C\u6309\u76F8\u5173\u5EA6\u6392\u5E8F\u4E0A\u9650 5 \u6761\uFF09\u2014\u2014\u8F6C\u5F55\u9519\u8BEF\u53EF\u636E\u6E05\u5355\u5F53\u573A\u7EA0\u6B63\u3002\u505C\u7528\u6A21\u677F\uFF08Web \u7BA1\u7406\u53F0\u542F\u505C\uFF09\uFF1Alist \u9ED8\u8BA4\u4E0D\u542B\u505C\u7528\u6A21\u677F\uFF1Bshow \u6309 code|id \u4ECD\u53EF\u8BFB\uFF1B\u7528\u505C\u7528\u6A21\u677F\u521B\u5EFA\u4EFB\u52A1\u88AB\u62D2\uFF08409 TEMPLATE_DISABLED\uFF09\u2014\u2014\u9700\u5728 Web \u7BA1\u7406\u53F0\u91CD\u65B0\u542F\u7528\u540E\u518D\u5EFA\u4EFB\u52A1\u3002\u6A21\u677F\u5199\u5165\u4E0E\u6F14\u8FDB\u4E0D\u5728 MCP\uFF1A\u5BFC\u5165\uFF08\u542B\u540C\u540D\u66F4\u65B0/\u9884\u89C8/\u5B8C\u6574\u4F9D\u8D56\u95ED\u5305 bundle\uFF09\u7528 CLI `siming dag template import`\uFF0C\u6A21\u677F\u5347\u7EA7\u9884\u68C0/\u6267\u884C\u7528 CLI `siming dag template upgrade plan/apply`\uFF08bash \u6267\u884C\uFF0C--help \u67E5\u7528\u6CD5\uFF09\u3002",
2033
+ "DAG \u6A21\u677F\u53EA\u8BFB\u67E5\u8BE2\uFF08\u4EFB\u52A1\u521B\u5EFA\u524D\u9009\u6A21\u677F/\u6838\u5BF9\u6A21\u677F\u7ED3\u6784\u7528\uFF1B\u672C\u5DE5\u5177\u65E0\u5199\u64CD\u4F5C\uFF09\u3002action\uFF1Alist=\u6A21\u677F\u5217\u8868\uFF08\u6982\u8981\u6295\u5F71\uFF0C\u542B id/code/name/version/isDefault/projectId/nodeCount/nodes \u6982\u8981/pausePoints/description\uFF0C\u4E0D\u542B\u8282\u70B9 prompt \u5168\u6587\uFF1B\u53EF\u9009 projectId \u8FC7\u6EE4\u9879\u76EE\u57DF\u2014\u2014key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\uFF0C\u7A7A/\u672A\u4F20\u4E0D\u8FC7\u6EE4\uFF1B\u8FD4\u56DE code \u4E3A\u540E\u7EED\u5F15\u7528\u6A21\u677F\u7684\u63A8\u8350\u5F62\u6001\uFF09\uFF1Bshow=\u6A21\u677F\u8BE6\u60C5\uFF08\u542B\u5168\u90E8\u8282\u70B9 prompt\uFF1BtemplateId \u652F\u6301 code|id \u53CC\u5F62\u6001\u2014\u2014code \u4E3A\u4E1A\u52A1\u6807\u8BC6\uFF08\u9879\u76EE\u5185\u552F\u4E00\u3001\u521B\u5EFA\u540E\u4E0D\u53EF\u53D8\u3001kebab-case \u5FC5\u542B\u8FDE\u5B57\u7B26\u4E14\u975E\u7EAF 24-hex \u5F62\u6001\uFF0C\u5982 full-workflow\uFF09\uFF0Ccode \u5F62\u6001\u5BFB\u5740\u987B\u914D projectId \u6307\u5B9A\u9879\u76EE\u57DF\u2014\u2014key|id \u53CC\u5F62\u6001\u81EA\u52A8\u5F52\u4E00\uFF0C\u7F3A\u7701\u6309\u5DE5\u4F5C\u533A defaultProject\uFF08\u5931\u6548\u62A5\u9519\uFF09\uFF0C\u65E0\u914D\u7F6E\u4E0D\u4F20\u7531 server \u63D0\u793A\u8865\u57DF\uFF1B24 \u4F4D\u6570\u636E\u5E93 id \u76F4\u67E5\u517C\u5BB9\uFF0C\u65E0\u9700 projectId\uFF09\u3002\u6A21\u677F\u4E0D\u5B58\u5728\u65F6\u9519\u8BEF\u4FE1\u606F\u542B candidates \u5019\u9009\u6E05\u5355\uFF08\u9879\u76EE\u5185\u76F8\u8FD1\u6A21\u677F\u7684 id/code/name\uFF0C\u6309\u76F8\u5173\u5EA6\u6392\u5E8F\u4E0A\u9650 5 \u6761\uFF09\u2014\u2014\u8F6C\u5F55\u9519\u8BEF\u53EF\u636E\u6E05\u5355\u5F53\u573A\u7EA0\u6B63\u3002\u505C\u7528\u6A21\u677F\uFF08Web \u7BA1\u7406\u53F0\u542F\u505C\uFF09\uFF1Alist \u9ED8\u8BA4\u4E0D\u542B\u505C\u7528\u6A21\u677F\uFF1Bshow \u6309 code|id \u4ECD\u53EF\u8BFB\uFF1B\u7528\u505C\u7528\u6A21\u677F\u521B\u5EFA\u4EFB\u52A1\u88AB\u62D2\uFF08409 TEMPLATE_DISABLED\uFF09\u2014\u2014\u9700\u5728 Web \u7BA1\u7406\u53F0\u91CD\u65B0\u542F\u7528\u540E\u518D\u5EFA\u4EFB\u52A1\u3002\u6A21\u677F\u5199\u5165\u4E0E\u6F14\u8FDB\u4E0D\u5728 MCP\uFF1A\u5BFC\u5165\uFF08\u542B\u540C\u540D\u66F4\u65B0/\u9884\u89C8/\u5B8C\u6574\u4F9D\u8D56\u95ED\u5305 bundle\uFF09\u7528 CLI `siming dag template import`\uFF0C\u6A21\u677F\u5347\u7EA7\u9884\u68C0/\u6267\u884C\u7528 CLI `siming dag template upgrade plan/apply`\uFF08bash \u6267\u884C\uFF0C--help \u67E5\u7528\u6CD5\uFF09\u3002",
1946
2034
  templateArgs,
1947
2035
  async (args) => {
1948
2036
  switch (args.action) {
1949
2037
  case "list": {
1950
- const result = await handleListTemplates(client, args.projectId);
2038
+ let listProjectId;
2039
+ if (args.projectId !== void 0 && args.projectId.trim().length > 0) {
2040
+ const pr = await resolveScopedProject(client, args.projectId, "explicit-only", { channel: "mcp" });
2041
+ if (!pr.success) return mcpContent(pr);
2042
+ listProjectId = pr.data.projectId;
2043
+ }
2044
+ const result = await handleListTemplates(client, listProjectId);
1951
2045
  if (!result.success) return mcpContent(result);
1952
2046
  return mcpContent(ok2(result.data.map(toTemplateSummary)));
1953
2047
  }
1954
2048
  case "show": {
1955
- const { templateId, projectId } = args;
2049
+ const { templateId } = args;
1956
2050
  if (templateId === void 0) return requireError("show", ["templateId"], args);
1957
- return mcpContent(await handleGetTemplate(client, templateId, projectId));
2051
+ let showProjectId;
2052
+ if (!/^[a-f0-9]{24}$/.test(templateId)) {
2053
+ const pr = await resolveScopedProject(client, args.projectId, "ws-default", { channel: "mcp" });
2054
+ if (!pr.success) return mcpContent(pr);
2055
+ showProjectId = pr.data.projectId;
2056
+ }
2057
+ return mcpContent(await handleGetTemplate(client, templateId, showProjectId));
1958
2058
  }
1959
2059
  }
1960
2060
  }
1961
2061
  );
1962
2062
  server.tool(
1963
2063
  "siming_issue",
1964
- `\u95EE\u9898\u6C60\u5DE5\u5177\uFF08T202609150002\uFF09\uFF1A\u5C0F\u95EE\u9898\u4F4E\u6210\u672C\u5165\u6C60\uFF0C\u6C89\u6DC0\u540E\u7EDF\u4E00\u6574\u7406\u8F6C\u4EFB\u52A1\uFF08\u8F6C\u4EFB\u52A1/\u6574\u7406\u91CD\u4EA4\u4E92\u8D70 CLI siming issue convert \u6216 Web\uFF0C\u4E0D\u5728\u672C\u5DE5\u5177\u9762\uFF09\u3002action\uFF1Acreate(title \u5FC5\u586B 1-200 \u5B57\uFF1B\u53EF\u9009 description\u3001projectId\u2014\u2014key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\u4E3A 24-hex id\uFF08hex \u96F6\u7F51\u7EDC\u5F00\u9500\u76F4\u901A\uFF09\uFF1B\u663E\u5F0F\u6307\u5B9A\u89E3\u6790\u5931\u8D25\u62A5\u9519\u4E0D\u515C\u5E95\uFF1B\u7F3A\u7701\u94FE\uFF1A\u672A\u4F20\u6216\u7A7A\u4E32\u65F6\u6309\u5DE5\u4F5C\u533A defaultProject \u89E3\u6790\uFF0C\u65E0\u914D\u7F6E\u6216\u89E3\u6790\u5931\u8D25\u65F6\u6309 key=default\uFF1B\u5F55\u5165\u540E source=mcp)\uFF1Bquery(\u53EF\u9009 view=open|all\u2014\u2014\u7F3A\u7701 open=\u6709\u6548\u672A\u8DDF\u8FDB\u5B8C\uFF0Call=\u5168\u91CF\uFF1Bstatus \u8FC7\u6EE4\u2014\u2014\u663E\u5F0F\u6307\u5B9A\u65F6\u4F18\u5148\u4E8E view\uFF0C\u53EF\u67E5\u5DF2\u53D6\u6D88/\u5DF2\u5B8C\u7ED3\uFF1BtaskId=\u4EFB\u52A1\u53CD\u67E5\uFF1BprojectId/page\u22651/limit 1-500\uFF1B\u54CD\u5E94 items \u542B progress {done,cancelled,total})\u3002\u8C03\u7528\u65B9\u5E94\u5728 fieldMask \u4E2D\u5217\u51FA\u672C\u6B21\u771F\u5B9E\u63D0\u4F9B\u7684\u53EF\u9009\u5B57\u6BB5\uFF0Cmask \u5916\u7531\u5BBF\u4E3B\u81EA\u52A8\u586B\u5145\u7684\u7A7A\u4E32\u3001\u7A7A\u6570\u7EC4\u3001false\u30010 \u5747\u5FFD\u7565\uFF1B\u5404 action \u56FA\u5B9A\u5FC5\u586B\u5B57\u6BB5\u65E0\u9700\u5199\u5165 mask\u3002`,
2064
+ `\u95EE\u9898\u6C60\u5DE5\u5177\uFF08T202609150002\uFF1BT202609200001 \u5F55\u5165\u5BA1\u6279\u95E8\u69DB\uFF09\uFF1A\u5C0F\u95EE\u9898\u4F4E\u6210\u672C\u5165\u6C60\uFF0C\u6C89\u6DC0\u540E\u7EDF\u4E00\u6574\u7406\u8F6C\u4EFB\u52A1\uFF08\u8F6C\u4EFB\u52A1/\u76F4\u63A5\u6807\u8BB0\u5B8C\u6210\u7B49\u6574\u7406\u91CD\u4EA4\u4E92\u8D70 CLI siming issue convert / siming issue done \u6216 Web\uFF0C\u4E0D\u5728\u672C\u5DE5\u5177\u9762\uFF09\u3002action\uFF1Acreate(title \u5FC5\u586B 1-200 \u5B57\uFF1Bapproved \u5FC5\u586B 1-500 \u5B57\u2014\u2014\u4EBA\u7C7B\u5BA1\u6279\u539F\u8BDD\u9010\u5B57\u5F15\u7528\uFF1A\u5F55\u5165\u524D\u5FC5\u987B\u5148\u5411\u7528\u6237\u5B8C\u6574\u5448\u73B0\u95EE\u9898\uFF08\u73B0\u8C61/\u6839\u56E0/\u5F71\u54CD/\u5EFA\u8BAE\uFF09\u5E76\u83B7\u7528\u6237\u660E\u786E\u6279\u51C6\uFF0C\u503C = \u7528\u6237\u6279\u51C6\u65F6\u8F93\u5165\u7684\u539F\u8BDD\uFF08\u5982\u300COK\uFF0C\u8BB0\u5F55\u300D\uFF09\uFF0C\u7981\u6B62\u4EE3\u586B\u3001\u7F16\u9020\u6216\u4EE5\u8BC4\u4F30\u6027\u8BED\u8A00\u4EE3\u66FF\uFF1B\u4F2A\u9020\u5C5E\u8FDD\u89C4\u5E76\u7559\u5BA1\u8BA1\u75D5\u8FF9\uFF1B\u539F\u8BDD\u843D\u5E93\u4F9B\u4E8B\u540E\u5BA1\u8BA1\uFF08query \u4E0E Web \u8BE6\u60C5\u9875\u53EF\u67E5\uFF09\uFF1B\u53EF\u9009 description\u3001projectId\u2014\u2014key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\u4E3A 24-hex id\uFF08hex \u96F6\u7F51\u7EDC\u5F00\u9500\u76F4\u901A\uFF09\uFF1B\u663E\u5F0F\u6307\u5B9A\u89E3\u6790\u5931\u8D25\u62A5\u9519\u4E0D\u515C\u5E95\uFF1B\u7F3A\u7701\u94FE\uFF1A\u672A\u4F20\u6216\u7A7A\u4E32\u65F6\u6309\u5DE5\u4F5C\u533A defaultProject \u89E3\u6790\uFF08\u5931\u6548\u62A5\u9519\uFF09\uFF0C\u65E0\u914D\u7F6E\u65F6\u4E0D\u4F20 projectId \u7531 server \u6309 key=default \u515C\u5E95\uFF1B\u5F55\u5165\u540E source=mcp\uFF0C\u7F3A approved \u670D\u52A1\u7AEF 400 \u5E76\u8FD4\u56DE\u81EA\u89E3\u91CA\u6587\u6848)\uFF1Bquery(\u53EF\u9009 view=open|all\u2014\u2014\u7F3A\u7701 open=\u6709\u6548\u672A\u8DDF\u8FDB\u5B8C\uFF0Call=\u5168\u91CF\uFF1Bstatus \u8FC7\u6EE4\u2014\u2014pending|partial|converted|cancelled|done\uFF0C\u663E\u5F0F\u6307\u5B9A\u65F6\u4F18\u5148\u4E8E view\uFF0C\u53EF\u67E5\u5DF2\u53D6\u6D88/\u5DF2\u5B8C\u6210\uFF1BtaskId=\u4EFB\u52A1\u53CD\u67E5\uFF1BprojectId\u2014\u2014key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\uFF0C\u7A7A/\u672A\u4F20\u4E0D\u8FC7\u6EE4\uFF08\u663E\u5F0F\u89E3\u6790\u5931\u8D25\u62A5\u9519\uFF09\uFF1Bpage\u22651/limit 1-500\uFF1B\u54CD\u5E94 items \u542B progress {done,cancelled,total})\u3002\u8C03\u7528\u65B9\u5E94\u5728 fieldMask \u4E2D\u5217\u51FA\u672C\u6B21\u771F\u5B9E\u63D0\u4F9B\u7684\u53EF\u9009\u5B57\u6BB5\uFF0Cmask \u5916\u7531\u5BBF\u4E3B\u81EA\u52A8\u586B\u5145\u7684\u7A7A\u4E32\u3001\u7A7A\u6570\u7EC4\u3001false\u30010 \u5747\u5FFD\u7565\uFF1B\u5404 action \u56FA\u5B9A\u5FC5\u586B\u5B57\u6BB5\u65E0\u9700\u5199\u5165 mask\u3002`,
1965
2065
  issueArgs,
1966
2066
  async (rawArgs) => {
1967
2067
  const masked = applyFieldMask(rawArgs, ISSUE_FIELD_SPECS);
@@ -1969,36 +2069,19 @@ function createMcpServer() {
1969
2069
  const args = masked.args;
1970
2070
  switch (args.action) {
1971
2071
  case "create": {
1972
- const { title } = args;
1973
- if (title === void 0) {
1974
- return requireError("create", ["title"], args);
1975
- }
1976
- let projectId;
1977
- const explicitProject = args.projectId !== void 0 && args.projectId.trim().length > 0 ? args.projectId : void 0;
1978
- if (explicitProject !== void 0) {
1979
- const resolved = await resolveProjectId(client, explicitProject);
1980
- if (!resolved.success) {
1981
- return mcpContent({
1982
- ...resolved,
1983
- error: {
1984
- ...resolved.error,
1985
- hint: "MCP \u8C03\u7528\u8BF7\u4F20 projectId \u5B57\u6BB5\uFF08\u9879\u76EE key \u6216 24-hex id \u53CC\u5F62\u6001\uFF0C\u81EA\u52A8\u5F52\u4E00\uFF09\uFF1B\u53EF\u7528 `siming project list` \u67E5\u8BE2\u53EF\u7528\u9879\u76EE"
1986
- }
1987
- });
1988
- }
1989
- projectId = resolved.data;
1990
- } else {
1991
- const wsDefaultProject = readWorkspaceConfig()?.defaultProject;
1992
- if (wsDefaultProject) {
1993
- const resolved = await resolveProjectId(client, wsDefaultProject);
1994
- if (resolved.success) projectId = resolved.data;
1995
- }
2072
+ const { title, approved } = args;
2073
+ if (title === void 0 || approved === void 0) {
2074
+ return requireError("create", ["title", "approved"], args);
1996
2075
  }
2076
+ const projectR = await resolveScopedProject(client, args.projectId, "ws-default", { channel: "mcp" });
2077
+ if (!projectR.success) return mcpContent(projectR);
2078
+ const projectId = projectR.data.projectId;
1997
2079
  return mcpContent(
1998
2080
  await handleCreateIssue(client, {
1999
2081
  title,
2000
2082
  ...args.description !== void 0 ? { description: args.description } : {},
2001
- ...projectId !== void 0 ? { projectId } : {}
2083
+ ...projectId !== void 0 ? { projectId } : {},
2084
+ approved
2002
2085
  })
2003
2086
  );
2004
2087
  }
@@ -2009,12 +2092,18 @@ function createMcpServer() {
2009
2092
  if (args.limit !== void 0 && (args.limit < 1 || args.limit > 500)) {
2010
2093
  return localError("action=query \u7684 limit \u5FC5\u987B\u4E3A 1-500 \u7684\u6574\u6570");
2011
2094
  }
2095
+ let issueQueryProjectId;
2096
+ if (args.projectId !== void 0 && args.projectId.trim().length > 0) {
2097
+ const pr = await resolveScopedProject(client, args.projectId, "explicit-only", { channel: "mcp" });
2098
+ if (!pr.success) return mcpContent(pr);
2099
+ issueQueryProjectId = pr.data.projectId;
2100
+ }
2012
2101
  return mcpContent(
2013
2102
  await handleListIssues(client, {
2014
2103
  view: args.view ?? "open",
2015
2104
  ...args.status !== void 0 ? { status: args.status } : {},
2016
2105
  ...args.taskId !== void 0 ? { taskId: args.taskId } : {},
2017
- ...args.projectId !== void 0 ? { projectId: args.projectId } : {},
2106
+ ...issueQueryProjectId !== void 0 ? { projectId: issueQueryProjectId } : {},
2018
2107
  ...args.page !== void 0 ? { page: args.page } : {},
2019
2108
  ...args.limit !== void 0 ? { limit: args.limit } : {}
2020
2109
  })
@@ -2236,18 +2325,19 @@ var PAGE_DEFAULT = 20;
2236
2325
  var PAGE_MAX = 500;
2237
2326
  function createTaskCommand() {
2238
2327
  const task = new Command2("task").description("Task management \u2014 create, advance, approve, pause, resume, cancel, reopen, query");
2239
- withCommonOpts(task.command("list"), "table").option("--status <s>", "filter by status").option("--track <t>", "filter by track").option("--project <key-or-id>", "filter by projectId (key or id)").option("--page <n>", "page number (default 1)", "1").option("--limit <n>", `page size (default ${PAGE_DEFAULT}, max ${PAGE_MAX})`).option("--all", `show all (limit ${PAGE_MAX})`).option("--sort <s>", "sort: progress|createdAt|updatedAt (default progress)").description("List tasks (summary projection: no dagInstance/history payloads)").action(async (opts) => {
2328
+ withCommonOpts(task.command("list"), "table").option("--status <s>", "filter by status").option("--track <t>", "filter by track").option("--project <key|id>", "filter by projectId (key|id)").option("--page <n>", "page number (default 1)", "1").option("--limit <n>", `page size (default ${PAGE_DEFAULT}, max ${PAGE_MAX})`).option("--all", `show all (limit ${PAGE_MAX})`).option("--sort <s>", "sort: progress|createdAt|updatedAt (default progress)").description("List tasks (summary projection: no dagInstance/history payloads)").action(async (opts) => {
2240
2329
  const client = createClient(opts.url);
2241
2330
  const filters = {};
2242
2331
  if (opts.status) filters.status = opts.status;
2243
2332
  if (opts.track) filters.track = opts.track;
2244
- if (opts.project) {
2245
- const r = await resolveProjectId(client, opts.project);
2246
- if (!r.success) {
2247
- renderResult(opts.format, r);
2333
+ if (opts.project !== void 0 && opts.project.trim().length > 0) {
2334
+ const pr = await resolveScopedProject(client, opts.project, "explicit-only");
2335
+ if (!pr.success) {
2336
+ renderResult(opts.format, pr);
2248
2337
  return;
2249
2338
  }
2250
- filters.projectId = r.data;
2339
+ const pid = pr.data.projectId;
2340
+ if (pid !== void 0) filters.projectId = pid;
2251
2341
  }
2252
2342
  if (opts.sort) {
2253
2343
  const r = parseEnum(opts.sort, z5.enum(["progress", "createdAt", "updatedAt"]), "sort");
@@ -2303,7 +2393,7 @@ function createTaskCommand() {
2303
2393
  const result = await handleQueryState(client, taskId);
2304
2394
  renderResult(opts.format, result, { fields: result.success ? taskFields(result.data) : [] });
2305
2395
  });
2306
- task.command("create").description("Create a task from a DAG template").option("--url <url>", "server base URL").requiredOption("--title <t>", "task title").requiredOption("--template <code|name|id>", "DAG template code (preferred), name, or 24-hex id (code/name resolved within project)").requiredOption("--track <t>", "task track (e.g. backend|ui|mixed|research \u2014 research \u4E3A\u8C03\u7814\u7C7B\u578B\u4E13\u7528\uFF0C\u987B\u914D --type research)").option("--type <t>", "task type (feature|bugfix|ui-tweak|research) \u2014 \u7C7B\u578B\u2192\u8DEF\u5F84\u6620\u5C04\u7531 task-create skill \u6301\u6709").option("--skip <ids>", "node ids to prune at creation (comma-separated, repeatable) \u2014 \u53EA\u5141\u8BB8\u6765\u81EA task-create skill \u7684\u6620\u5C04\u77E9\u9635", collectArgs, []).option("--project <key-or-id>", "projectId (key or id; defaults to workspace defaultProject then template projectId)").action(async (opts) => {
2396
+ task.command("create").description("Create a task from a DAG template").option("--url <url>", "server base URL").requiredOption("--title <t>", "task title").requiredOption("--template <code|name|id>", "DAG template code (preferred), name, or 24-hex id (code/name resolved within project)").requiredOption("--track <t>", "task track (e.g. backend|ui|mixed|research \u2014 research \u4E3A\u8C03\u7814\u7C7B\u578B\u4E13\u7528\uFF0C\u987B\u914D --type research)").option("--type <t>", "task type (feature|bugfix|ui-tweak|research) \u2014 \u7C7B\u578B\u2192\u8DEF\u5F84\u6620\u5C04\u7531 task-create skill \u6301\u6709").option("--skip <ids>", "node ids to prune at creation (comma-separated, repeatable) \u2014 \u53EA\u5141\u8BB8\u6765\u81EA task-create skill \u7684\u6620\u5C04\u77E9\u9635", collectArgs, []).option("--project <key|id>", "projectId (key|id; defaults to workspace defaultProject then template projectId)").action(async (opts) => {
2307
2397
  const client = createClient(opts.url);
2308
2398
  let type;
2309
2399
  if (opts.type !== void 0) {
@@ -2318,21 +2408,12 @@ function createTaskCommand() {
2318
2408
  if (opts.skip !== void 0 && opts.skip.length > 0) {
2319
2409
  skipNodes = [...new Set(opts.skip.flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0))];
2320
2410
  }
2321
- let projectId;
2322
- if (opts.project) {
2323
- const r = await resolveProjectId(client, opts.project);
2324
- if (!r.success) {
2325
- renderResult("json", r);
2326
- return;
2327
- }
2328
- projectId = r.data;
2329
- } else {
2330
- const wsDefaultProject = readWorkspaceConfig()?.defaultProject;
2331
- if (wsDefaultProject) {
2332
- const r = await resolveProjectId(client, wsDefaultProject);
2333
- if (r.success) projectId = r.data;
2334
- }
2411
+ const projectR = await resolveScopedProject(client, opts.project, "ws-default", { commandLabel: "siming task create" });
2412
+ if (!projectR.success) {
2413
+ renderResult("json", projectR);
2414
+ return;
2335
2415
  }
2416
+ const projectId = projectR.data.projectId;
2336
2417
  let templateRef = opts.template;
2337
2418
  if (!/^[0-9a-f]{24}$/.test(templateRef)) {
2338
2419
  const listR = await handleListTemplates(client, projectId);
@@ -2351,20 +2432,9 @@ function createTaskCommand() {
2351
2432
  ...skipNodes !== void 0 && skipNodes.length > 0 ? { skipNodes } : {},
2352
2433
  ...projectId ? { projectId } : {}
2353
2434
  });
2354
- if (!result.success && !opts.project) {
2355
- const bizCode = result.error.bizCode;
2356
- if (bizCode === "PROJECT_NOT_FOUND") {
2357
- renderResult("json", { ...result, error: { ...result.error, hint: "workspace defaultProject \u6307\u5411\u7684\u9879\u76EE\u4E0D\u5B58\u5728\u2014\u2014run `siming config unset defaultProject` or `siming config set defaultProject <key|id>`" } });
2358
- return;
2359
- }
2360
- if (bizCode === "PROJECT_ARCHIVED") {
2361
- renderResult("json", { ...result, error: { ...result.error, hint: "workspace defaultProject \u6307\u5411\u7684\u9879\u76EE\u5DF2\u5F52\u6863\u2014\u2014run `siming project list --status active` to pick an active project" } });
2362
- return;
2363
- }
2364
- if (bizCode === "TEMPLATE_PROJECT_MISMATCH") {
2365
- renderResult("json", { ...result, error: { ...result.error, hint: "template belongs to another project\u2014\u2014pass --project <key|id> of the template's project" } });
2366
- return;
2367
- }
2435
+ if (!result.success && !opts.project && result.error.bizCode === "TEMPLATE_PROJECT_MISMATCH") {
2436
+ renderResult("json", { ...result, error: { ...result.error, hint: "template belongs to another project\u2014\u2014pass --project <key|id> of the template's project" } });
2437
+ return;
2368
2438
  }
2369
2439
  renderResult("json", result);
2370
2440
  });
@@ -3214,9 +3284,18 @@ async function handleScaffoldSkill(client, input) {
3214
3284
  // src/commands/skill/command.ts
3215
3285
  function createSkillCommand() {
3216
3286
  const skill = new Command3("skill").description("Skill management \u2014 global/project-scoped skills");
3217
- withCommonOpts(skill.command("list"), "table").option("--project <id>", "filter by projectId").description("List skills").action(async (opts) => {
3287
+ withCommonOpts(skill.command("list"), "table").option("--project <key|id>", "filter by projectId (key|id)").description("List skills").action(async (opts) => {
3218
3288
  const client = createClient(opts.url);
3219
- const result = await handleListSkills(client, opts.project);
3289
+ let projectId;
3290
+ if (opts.project !== void 0 && opts.project.trim().length > 0) {
3291
+ const pr = await resolveScopedProject(client, opts.project, "explicit-only");
3292
+ if (!pr.success) {
3293
+ renderResult(opts.format, pr);
3294
+ return;
3295
+ }
3296
+ projectId = pr.data.projectId;
3297
+ }
3298
+ const result = await handleListSkills(client, projectId);
3220
3299
  renderResult(opts.format, result, {
3221
3300
  columns: [
3222
3301
  { key: "name", header: "Name" },
@@ -3228,7 +3307,7 @@ function createSkillCommand() {
3228
3307
  ]
3229
3308
  });
3230
3309
  });
3231
- withCommonOpts(skill.command("show <name>"), "fancy").description("Show skill details").option("--scope <scope>", "global|project (asset addressing; defaults to global)").option("--project-id <hex>", "projectId (required when scope=project)").action(async (name, opts) => {
3310
+ withCommonOpts(skill.command("show <name>"), "fancy").description("Show skill details").option("--scope <scope>", "global|project (asset addressing; defaults to global)").option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (name, opts) => {
3232
3311
  const client = createClient(opts.url);
3233
3312
  let scope;
3234
3313
  if (opts.scope !== void 0) {
@@ -3239,13 +3318,23 @@ function createSkillCommand() {
3239
3318
  }
3240
3319
  scope = scopeR.data;
3241
3320
  }
3321
+ let projectId;
3322
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
3323
+ if (explicitProject !== void 0 || scope === "project") {
3324
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming skill show" });
3325
+ if (!pr.success) {
3326
+ renderResult(opts.format, pr);
3327
+ return;
3328
+ }
3329
+ projectId = pr.data.projectId;
3330
+ }
3242
3331
  const result = await handleGetSkill(client, name, {
3243
3332
  ...scope !== void 0 ? { scope } : {},
3244
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
3333
+ ...projectId !== void 0 ? { projectId } : {}
3245
3334
  });
3246
3335
  renderResult(opts.format, result, { fields: result.success ? skillFields(result.data) : [] });
3247
3336
  });
3248
- skill.command("create").description("Create a skill (scope required)").option("--url <url>", "server base URL").requiredOption("--name <name>", "skill name (kebab-case)").requiredOption("--description <text>", "skill description").requiredOption("--content <text>", "skill content (prompt body)").requiredOption("--category <cat>", "process|domain|tooling").requiredOption("--scope <scope>", "global|project").option("--ref <path@file>", "reference doc: <relative path>@<local file> (repeatable)", collectArgs, []).option("--project-id <hex>", "projectId (required when scope=project)").action(async (opts) => {
3337
+ skill.command("create").description("Create a skill (scope required)").option("--url <url>", "server base URL").requiredOption("--name <name>", "skill name (kebab-case)").requiredOption("--description <text>", "skill description").requiredOption("--content <text>", "skill content (prompt body)").requiredOption("--category <cat>", "process|domain|tooling").requiredOption("--scope <scope>", "global|project").option("--ref <path@file>", "reference doc: <relative path>@<local file> (repeatable)", collectArgs, []).option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (opts) => {
3249
3338
  const categoryR = parseEnum(opts.category, SkillCategorySchema, "category");
3250
3339
  if (!categoryR.success) {
3251
3340
  renderResult("json", categoryR);
@@ -3262,6 +3351,16 @@ function createSkillCommand() {
3262
3351
  return;
3263
3352
  }
3264
3353
  const client = createClient(opts.url);
3354
+ let projectId;
3355
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
3356
+ if (explicitProject !== void 0 || scopeR.data === "project") {
3357
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming skill create" });
3358
+ if (!pr.success) {
3359
+ renderResult("json", pr);
3360
+ return;
3361
+ }
3362
+ projectId = pr.data.projectId;
3363
+ }
3265
3364
  const input = {
3266
3365
  name: opts.name,
3267
3366
  description: opts.description,
@@ -3270,12 +3369,12 @@ function createSkillCommand() {
3270
3369
  scope: scopeR.data,
3271
3370
  version: "1.0.0",
3272
3371
  ...refsR.data.length > 0 ? { references: refsR.data } : {},
3273
- ...opts.projectId ? { projectId: opts.projectId } : {}
3372
+ ...projectId !== void 0 ? { projectId } : {}
3274
3373
  };
3275
3374
  const result = await handleCreateSkill(client, input);
3276
3375
  renderResult("json", result);
3277
3376
  });
3278
- skill.command("update <name>").description("Update a skill (partial; references untouched unless --ref/--clear-refs given)").option("--url <url>", "server base URL").option("--description <text>", "new description").option("--content <text>", "new content").option("--new-version <v>", "new version (note: --version is reserved for the program version)").option("--scope <scope>", "global|project (asset addressing)").option("--ref <path@file>", "replace all reference docs: <relative path>@<local file> (repeatable)", collectArgs, []).option("--clear-refs", "remove all reference docs (mutually exclusive with --ref)").option("--project-id <hex>", "projectId (required when scope=project)").action(async (name, opts) => {
3377
+ skill.command("update <name>").description("Update a skill (partial; references untouched unless --ref/--clear-refs given)").option("--url <url>", "server base URL").option("--description <text>", "new description").option("--content <text>", "new content").option("--new-version <v>", "new version (note: --version is reserved for the program version)").option("--scope <scope>", "global|project (asset addressing)").option("--ref <path@file>", "replace all reference docs: <relative path>@<local file> (repeatable)", collectArgs, []).option("--clear-refs", "remove all reference docs (mutually exclusive with --ref)").option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (name, opts) => {
3279
3378
  const client = createClient(opts.url);
3280
3379
  let scope;
3281
3380
  if (opts.scope !== void 0) {
@@ -3305,21 +3404,31 @@ function createSkillCommand() {
3305
3404
  }
3306
3405
  references = refsR.data;
3307
3406
  }
3407
+ let projectId;
3408
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
3409
+ if (explicitProject !== void 0 || scope === "project") {
3410
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming skill update" });
3411
+ if (!pr.success) {
3412
+ renderResult("json", pr);
3413
+ return;
3414
+ }
3415
+ projectId = pr.data.projectId;
3416
+ }
3308
3417
  const patch = {
3309
3418
  ...opts.description !== void 0 ? { description: opts.description } : {},
3310
3419
  ...opts.content !== void 0 ? { content: opts.content } : {},
3311
3420
  ...opts.newVersion !== void 0 ? { version: opts.newVersion } : {},
3312
3421
  ...scope !== void 0 ? { scope } : {},
3313
3422
  ...references !== void 0 ? { references } : {},
3314
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
3423
+ ...projectId !== void 0 ? { projectId } : {}
3315
3424
  };
3316
3425
  const result = await handleUpdateSkill(client, name, patch, {
3317
3426
  ...scope !== void 0 ? { scope } : {},
3318
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
3427
+ ...projectId !== void 0 ? { projectId } : {}
3319
3428
  });
3320
3429
  renderResult("json", result);
3321
3430
  });
3322
- skill.command("copy <name>").description("Copy a skill to a new name/scope").option("--url <url>", "server base URL").requiredOption("--new-name <name>", "copy name").requiredOption("--new-scope <scope>", "global|project").option("--target-project-id <hex>", "target projectId (required when newScope=project)").option("--scope <scope>", "source asset addressing: global|project (defaults to global)").option("--source-project-id <hex>", "source projectId (required when scope=project)").action(async (name, opts) => {
3431
+ skill.command("copy <name>").description("Copy a skill to a new name/scope").option("--url <url>", "server base URL").requiredOption("--new-name <name>", "copy name").requiredOption("--new-scope <scope>", "global|project").option("--target-project-id <key|id>", "target projectId (newScope=project; falls back to workspace/instance default project)").option("--scope <scope>", "source asset addressing: global|project (defaults to global)").option("--source-project-id <key|id>", "source projectId (scope=project; falls back to workspace/instance default project)").action(async (name, opts) => {
3323
3432
  const scopeR = parseEnum(opts.newScope, SkillScopeSchema, "new-scope");
3324
3433
  if (!scopeR.success) {
3325
3434
  renderResult("json", scopeR);
@@ -3335,22 +3444,42 @@ function createSkillCommand() {
3335
3444
  srcScope = srcR.data;
3336
3445
  }
3337
3446
  const client = createClient(opts.url);
3447
+ let targetProjectId;
3448
+ const explicitTarget = opts.targetProjectId !== void 0 && opts.targetProjectId.trim().length > 0 ? opts.targetProjectId : void 0;
3449
+ if (explicitTarget !== void 0 || scopeR.data === "project") {
3450
+ const pr = await resolveScopedProject(client, explicitTarget, "instance-default", { commandLabel: "siming skill copy (target)" });
3451
+ if (!pr.success) {
3452
+ renderResult("json", pr);
3453
+ return;
3454
+ }
3455
+ targetProjectId = pr.data.projectId;
3456
+ }
3457
+ let sourceProjectId;
3458
+ const explicitSource = opts.sourceProjectId !== void 0 && opts.sourceProjectId.trim().length > 0 ? opts.sourceProjectId : void 0;
3459
+ if (explicitSource !== void 0 || srcScope === "project") {
3460
+ const pr = await resolveScopedProject(client, explicitSource, "instance-default", { commandLabel: "siming skill copy (source)" });
3461
+ if (!pr.success) {
3462
+ renderResult("json", pr);
3463
+ return;
3464
+ }
3465
+ sourceProjectId = pr.data.projectId;
3466
+ }
3338
3467
  const result = await handleCopySkill(
3339
3468
  client,
3340
3469
  name,
3341
3470
  {
3342
3471
  newName: opts.newName,
3343
3472
  newScope: scopeR.data,
3344
- ...opts.targetProjectId ? { targetProjectId: opts.targetProjectId } : {}
3473
+ ...targetProjectId !== void 0 ? { targetProjectId } : {}
3345
3474
  },
3346
3475
  {
3347
3476
  ...srcScope !== void 0 ? { scope: srcScope } : {},
3348
- ...opts.sourceProjectId !== void 0 ? { projectId: opts.sourceProjectId } : {}
3477
+ ...sourceProjectId !== void 0 ? { projectId: sourceProjectId } : {}
3349
3478
  }
3350
3479
  );
3351
3480
  renderResult("json", result);
3352
3481
  });
3353
- withCommonOpts(skill.command("delete <name>"), "fancy").description("Delete a skill").option("--scope <scope>", "global|project (asset addressing; defaults to global)").option("--project-id <hex>", "projectId (required when scope=project)").action(async (name, opts) => {
3482
+ withCommonOpts(skill.command("delete <name>"), "fancy").description("Delete a skill").option("--scope <scope>", "global|project (asset addressing; defaults to global)").option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (name, opts) => {
3354
3483
  const client = createClient(opts.url);
3355
3484
  let scope;
3356
3485
  if (opts.scope !== void 0) {
@@ -3361,13 +3490,23 @@ function createSkillCommand() {
3361
3490
  }
3362
3491
  scope = scopeR.data;
3363
3492
  }
3493
+ let projectId;
3494
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
3495
+ if (explicitProject !== void 0 || scope === "project") {
3496
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming skill delete" });
3497
+ if (!pr.success) {
3498
+ renderResult("json", pr);
3499
+ return;
3500
+ }
3501
+ projectId = pr.data.projectId;
3502
+ }
3364
3503
  const result = await handleDeleteSkill(client, name, {
3365
3504
  ...scope !== void 0 ? { scope } : {},
3366
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
3505
+ ...projectId !== void 0 ? { projectId } : {}
3367
3506
  });
3368
3507
  renderResult(opts.format, result, { successMessage: `deleted skill '${name}'` });
3369
3508
  });
3370
- skill.command("enabled <name> <on|off>").description("Enable/disable a skill (asset addressing; defaults to global)").option("--url <url>", "server base URL").option("--format <fmt>", "output format: fancy|table|json", "fancy").option("--scope <scope>", "global|project (asset addressing; defaults to global)").option("--project-id <hex>", "projectId (required when scope=project)").action(async (name, onOff, opts) => {
3509
+ skill.command("enabled <name> <on|off>").description("Enable/disable a skill (asset addressing; defaults to global)").option("--url <url>", "server base URL").option("--format <fmt>", "output format: fancy|table|json", "fancy").option("--scope <scope>", "global|project (asset addressing; defaults to global)").option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (name, onOff, opts) => {
3371
3510
  const enabledR = parseOnOff(onOff);
3372
3511
  if (!enabledR.success) {
3373
3512
  renderResult(opts.format, enabledR);
@@ -3383,9 +3522,19 @@ function createSkillCommand() {
3383
3522
  }
3384
3523
  scope = scopeR.data;
3385
3524
  }
3525
+ let projectId;
3526
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
3527
+ if (explicitProject !== void 0 || scope === "project") {
3528
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming skill enabled" });
3529
+ if (!pr.success) {
3530
+ renderResult(opts.format, pr);
3531
+ return;
3532
+ }
3533
+ projectId = pr.data.projectId;
3534
+ }
3386
3535
  const result = await handleSetSkillEnabled(client, name, enabledR.data, {
3387
3536
  ...scope !== void 0 ? { scope } : {},
3388
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
3537
+ ...projectId !== void 0 ? { projectId } : {}
3389
3538
  });
3390
3539
  renderResult(opts.format, result, {
3391
3540
  successMessage: `skill '${name}' ${enabledR.data ? "enabled" : "disabled"}`
@@ -3393,16 +3542,17 @@ function createSkillCommand() {
3393
3542
  });
3394
3543
  skill.command("scaffold <template>").description(
3395
3544
  "Scaffold an official project-scoped skill from the packaged skeleton (task-create entry; same-name conflict refused by default)"
3396
- ).option("--url <url>", "server base URL").requiredOption("--project <key-or-id>", "target project (key or 24-hex id)").option("--overwrite", "explicitly replace an existing same-name project asset (version bumps monotonically; enabled state untouched)").option("--format <fmt>", "output format: fancy|table|json", "fancy").action(async (template, opts) => {
3545
+ ).option("--url <url>", "server base URL").option("--project <key|id>", "target project (key|id; falls back to workspace/instance default project)").option("--overwrite", "explicitly replace an existing same-name project asset (version bumps monotonically; enabled state untouched)").option("--format <fmt>", "output format: fancy|table|json", "fancy").action(async (template, opts) => {
3397
3546
  const client = createClient(opts.url);
3398
- const projectR = await resolveProjectId(client, opts.project);
3547
+ const projectR = await resolveScopedProject(client, opts.project, "instance-default", { commandLabel: "siming skill scaffold" });
3399
3548
  if (!projectR.success) {
3400
3549
  renderResult(opts.format, projectR);
3401
3550
  return;
3402
3551
  }
3403
3552
  const result = await handleScaffoldSkill(client, {
3404
3553
  template,
3405
- projectId: projectR.data,
3554
+ projectId: projectR.data.projectId,
3555
+ // instance-default 成功必含 id
3406
3556
  ...opts.overwrite === true ? { overwrite: true } : {}
3407
3557
  });
3408
3558
  const renderOptions = result.success ? {
@@ -3493,9 +3643,18 @@ async function handleDeleteAgent(client, name, query = {}) {
3493
3643
  // src/commands/agent/command.ts
3494
3644
  function createAgentCommand() {
3495
3645
  const agent = new Command4("agent").description("Agent management \u2014 subagent definitions (global/project scope)");
3496
- withCommonOpts(agent.command("list"), "table").option("--project <id>", "filter by projectId").description("List agents").action(async (opts) => {
3646
+ withCommonOpts(agent.command("list"), "table").option("--project <key|id>", "filter by projectId (key|id)").description("List agents").action(async (opts) => {
3497
3647
  const client = createClient(opts.url);
3498
- const result = await handleListAgents(client, opts.project);
3648
+ let projectId;
3649
+ if (opts.project !== void 0 && opts.project.trim().length > 0) {
3650
+ const pr = await resolveScopedProject(client, opts.project, "explicit-only");
3651
+ if (!pr.success) {
3652
+ renderResult(opts.format, pr);
3653
+ return;
3654
+ }
3655
+ projectId = pr.data.projectId;
3656
+ }
3657
+ const result = await handleListAgents(client, projectId);
3499
3658
  renderResult(opts.format, result, {
3500
3659
  columns: [
3501
3660
  { key: "name", header: "Name" },
@@ -3513,7 +3672,7 @@ function createAgentCommand() {
3513
3672
  const result = await handleGetAgent(client, name);
3514
3673
  renderResult(opts.format, result, { fields: result.success ? agentFields(result.data) : [] });
3515
3674
  });
3516
- agent.command("create").description("Create an agent (scope + system-prompt + model required)").option("--url <url>", "server base URL").requiredOption("--name <name>", "agent name").requiredOption("--description <text>", "agent description").requiredOption("--system-prompt <text>", "system prompt").requiredOption("--model <code>", "model siming-code (kebab-case)").requiredOption("--scope <scope>", "global|project").option("--function <function>", "agent function: reviewer|executor (default executor)").option("--bound-skill <name>", "bound skill name (repeatable)", collectArgs, []).option("--tool <name>", "tool name (repeatable)", collectArgs, []).option("--permission <name>", "permission name (repeatable)", collectArgs, []).option("--ref <path@file>", "reference doc: <relative path>@<local file> (repeatable)", collectArgs, []).option("--project-id <hex>", "projectId (required when scope=project)").action(async (opts) => {
3675
+ agent.command("create").description("Create an agent (scope + system-prompt + model required)").option("--url <url>", "server base URL").requiredOption("--name <name>", "agent name").requiredOption("--description <text>", "agent description").requiredOption("--system-prompt <text>", "system prompt").requiredOption("--model <code>", "model siming-code (kebab-case)").requiredOption("--scope <scope>", "global|project").option("--function <function>", "agent function: reviewer|executor (default executor)").option("--bound-skill <name>", "bound skill name (repeatable)", collectArgs, []).option("--tool <name>", "tool name (repeatable)", collectArgs, []).option("--permission <name>", "permission name (repeatable)", collectArgs, []).option("--ref <path@file>", "reference doc: <relative path>@<local file> (repeatable)", collectArgs, []).option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (opts) => {
3517
3676
  const scopeR = parseEnum(opts.scope, AgentScopeSchema, "scope");
3518
3677
  if (!scopeR.success) {
3519
3678
  renderResult("json", scopeR);
@@ -3534,6 +3693,16 @@ function createAgentCommand() {
3534
3693
  return;
3535
3694
  }
3536
3695
  const client = createClient(opts.url);
3696
+ let projectId;
3697
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
3698
+ if (explicitProject !== void 0 || scopeR.data === "project") {
3699
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming agent create" });
3700
+ if (!pr.success) {
3701
+ renderResult("json", pr);
3702
+ return;
3703
+ }
3704
+ projectId = pr.data.projectId;
3705
+ }
3537
3706
  const input = {
3538
3707
  name: opts.name,
3539
3708
  description: opts.description,
@@ -3547,12 +3716,12 @@ function createAgentCommand() {
3547
3716
  tools: opts.tool,
3548
3717
  permissions: opts.permission,
3549
3718
  ...refsR.data.length > 0 ? { references: refsR.data } : {},
3550
- ...opts.projectId ? { projectId: opts.projectId } : {}
3719
+ ...projectId !== void 0 ? { projectId } : {}
3551
3720
  };
3552
3721
  const result = await handleCreateAgent(client, input);
3553
3722
  renderResult("json", result);
3554
3723
  });
3555
- agent.command("update <name>").description("Update an agent (partial)").option("--url <url>", "server base URL").option("--description <text>", "new description").option("--system-prompt <text>", "new system prompt").option("--model <code>", "new model siming-code").option("--scope <scope>", "global|project (asset addressing)").option("--function <function>", "new agent function: reviewer|executor").option("--bound-skill <name>", "replace bound skills (repeatable)", collectArgs, []).option("--tool <name>", "replace tools (repeatable)", collectArgs, []).option("--permission <name>", "replace permissions (repeatable)", collectArgs, []).option("--ref <path@file>", "replace all reference docs: <relative path>@<local file> (repeatable)", collectArgs, []).option("--clear-refs", "remove all reference docs (mutually exclusive with --ref)").option("--project-id <hex>", "projectId (required when scope=project)").action(async (name, opts) => {
3724
+ agent.command("update <name>").description("Update an agent (partial)").option("--url <url>", "server base URL").option("--description <text>", "new description").option("--system-prompt <text>", "new system prompt").option("--model <code>", "new model siming-code").option("--scope <scope>", "global|project (asset addressing)").option("--function <function>", "new agent function: reviewer|executor").option("--bound-skill <name>", "replace bound skills (repeatable)", collectArgs, []).option("--tool <name>", "replace tools (repeatable)", collectArgs, []).option("--permission <name>", "replace permissions (repeatable)", collectArgs, []).option("--ref <path@file>", "replace all reference docs: <relative path>@<local file> (repeatable)", collectArgs, []).option("--clear-refs", "remove all reference docs (mutually exclusive with --ref)").option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (name, opts) => {
3556
3725
  const client = createClient(opts.url);
3557
3726
  let scope;
3558
3727
  if (opts.scope !== void 0) {
@@ -3591,6 +3760,16 @@ function createAgentCommand() {
3591
3760
  }
3592
3761
  references = refsR.data;
3593
3762
  }
3763
+ let projectId;
3764
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
3765
+ if (explicitProject !== void 0 || scope === "project") {
3766
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming agent update" });
3767
+ if (!pr.success) {
3768
+ renderResult("json", pr);
3769
+ return;
3770
+ }
3771
+ projectId = pr.data.projectId;
3772
+ }
3594
3773
  const patch = {
3595
3774
  ...opts.description !== void 0 ? { description: opts.description } : {},
3596
3775
  ...opts.systemPrompt !== void 0 ? { systemPrompt: opts.systemPrompt } : {},
@@ -3601,15 +3780,15 @@ function createAgentCommand() {
3601
3780
  ...opts.tool.length > 0 ? { tools: opts.tool } : {},
3602
3781
  ...opts.permission.length > 0 ? { permissions: opts.permission } : {},
3603
3782
  ...references !== void 0 ? { references } : {},
3604
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
3783
+ ...projectId !== void 0 ? { projectId } : {}
3605
3784
  };
3606
3785
  const result = await handleUpdateAgent(client, name, patch, {
3607
3786
  ...scope !== void 0 ? { scope } : {},
3608
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
3787
+ ...projectId !== void 0 ? { projectId } : {}
3609
3788
  });
3610
3789
  renderResult("json", result);
3611
3790
  });
3612
- agent.command("copy <name>").description("Copy an agent to a new name/scope").option("--url <url>", "server base URL").requiredOption("--new-name <name>", "copy name").requiredOption("--new-scope <scope>", "global|project").option("--target-project-id <hex>", "target projectId (required when newScope=project)").option("--scope <scope>", "source asset addressing: global|project (defaults to global)").option("--source-project-id <hex>", "source projectId (required when scope=project)").action(async (name, opts) => {
3791
+ agent.command("copy <name>").description("Copy an agent to a new name/scope").option("--url <url>", "server base URL").requiredOption("--new-name <name>", "copy name").requiredOption("--new-scope <scope>", "global|project").option("--target-project-id <key|id>", "target projectId (newScope=project; falls back to workspace/instance default project)").option("--scope <scope>", "source asset addressing: global|project (defaults to global)").option("--source-project-id <key|id>", "source projectId (scope=project; falls back to workspace/instance default project)").action(async (name, opts) => {
3613
3792
  const scopeR = parseEnum(opts.newScope, AgentScopeSchema, "new-scope");
3614
3793
  if (!scopeR.success) {
3615
3794
  renderResult("json", scopeR);
@@ -3625,22 +3804,42 @@ function createAgentCommand() {
3625
3804
  srcScope = srcR.data;
3626
3805
  }
3627
3806
  const client = createClient(opts.url);
3807
+ let targetProjectId;
3808
+ const explicitTarget = opts.targetProjectId !== void 0 && opts.targetProjectId.trim().length > 0 ? opts.targetProjectId : void 0;
3809
+ if (explicitTarget !== void 0 || scopeR.data === "project") {
3810
+ const pr = await resolveScopedProject(client, explicitTarget, "instance-default", { commandLabel: "siming agent copy (target)" });
3811
+ if (!pr.success) {
3812
+ renderResult("json", pr);
3813
+ return;
3814
+ }
3815
+ targetProjectId = pr.data.projectId;
3816
+ }
3817
+ let sourceProjectId;
3818
+ const explicitSource = opts.sourceProjectId !== void 0 && opts.sourceProjectId.trim().length > 0 ? opts.sourceProjectId : void 0;
3819
+ if (explicitSource !== void 0 || srcScope === "project") {
3820
+ const pr = await resolveScopedProject(client, explicitSource, "instance-default", { commandLabel: "siming agent copy (source)" });
3821
+ if (!pr.success) {
3822
+ renderResult("json", pr);
3823
+ return;
3824
+ }
3825
+ sourceProjectId = pr.data.projectId;
3826
+ }
3628
3827
  const result = await handleCopyAgent(
3629
3828
  client,
3630
3829
  name,
3631
3830
  {
3632
3831
  newName: opts.newName,
3633
3832
  newScope: scopeR.data,
3634
- ...opts.targetProjectId ? { targetProjectId: opts.targetProjectId } : {}
3833
+ ...targetProjectId !== void 0 ? { targetProjectId } : {}
3635
3834
  },
3636
3835
  {
3637
3836
  ...srcScope !== void 0 ? { scope: srcScope } : {},
3638
- ...opts.sourceProjectId !== void 0 ? { projectId: opts.sourceProjectId } : {}
3837
+ ...sourceProjectId !== void 0 ? { projectId: sourceProjectId } : {}
3639
3838
  }
3640
3839
  );
3641
3840
  renderResult("json", result);
3642
3841
  });
3643
- withCommonOpts(agent.command("delete <name>"), "fancy").description("Delete an agent").option("--scope <scope>", "global|project (asset addressing; defaults to global)").option("--project-id <hex>", "projectId (required when scope=project)").action(async (name, opts) => {
3842
+ withCommonOpts(agent.command("delete <name>"), "fancy").description("Delete an agent").option("--scope <scope>", "global|project (asset addressing; defaults to global)").option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (name, opts) => {
3644
3843
  const client = createClient(opts.url);
3645
3844
  let scope;
3646
3845
  if (opts.scope !== void 0) {
@@ -3651,9 +3850,19 @@ function createAgentCommand() {
3651
3850
  }
3652
3851
  scope = scopeR.data;
3653
3852
  }
3853
+ let projectId;
3854
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
3855
+ if (explicitProject !== void 0 || scope === "project") {
3856
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming agent delete" });
3857
+ if (!pr.success) {
3858
+ renderResult(opts.format, pr);
3859
+ return;
3860
+ }
3861
+ projectId = pr.data.projectId;
3862
+ }
3654
3863
  const result = await handleDeleteAgent(client, name, {
3655
3864
  ...scope !== void 0 ? { scope } : {},
3656
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
3865
+ ...projectId !== void 0 ? { projectId } : {}
3657
3866
  });
3658
3867
  renderResult(opts.format, result, { successMessage: `deleted agent '${name}'` });
3659
3868
  });
@@ -3684,23 +3893,13 @@ function createDagCommand() {
3684
3893
  const tpl = dag.command("template").description("DAG template management");
3685
3894
  withCommonOpts(tpl.command("list"), "table").option("--project <key|id>", "filter by project (key or id; defaults to workspace defaultProject)").description("List DAG templates (summary projection: node labels only, no prompts \u2014 use `show` for full detail)").action(async (opts) => {
3686
3895
  const client = createClient(opts.url);
3687
- const projectOpt = opts.project ?? readWorkspaceConfig()?.defaultProject;
3688
- let projectId;
3689
- if (projectOpt !== void 0) {
3690
- const r = await resolveProjectId(client, projectOpt);
3691
- if (!r.success) {
3692
- if (opts.project === void 0) {
3693
- renderResult(opts.format, fail({
3694
- ...r.error,
3695
- hint: `workspace defaultProject "${projectOpt}" \u89E3\u6790\u5931\u8D25\u2014\u2014siming config unset defaultProject \u540E\u91CD\u8BD5\uFF0C\u6216\u663E\u5F0F --project <key|id>`
3696
- }));
3697
- } else {
3698
- renderResult(opts.format, r);
3699
- }
3700
- return;
3701
- }
3702
- projectId = r.data;
3896
+ const explicitProject = opts.project !== void 0 && opts.project.trim().length > 0 ? opts.project : void 0;
3897
+ const pr = await resolveScopedProject(client, explicitProject, "ws-default", { commandLabel: "siming dag template list" });
3898
+ if (!pr.success) {
3899
+ renderResult(opts.format, pr);
3900
+ return;
3703
3901
  }
3902
+ const projectId = pr.data.projectId;
3704
3903
  const result = await handleListTemplates(client, projectId);
3705
3904
  if (!result.success) {
3706
3905
  renderResult(opts.format, result);
@@ -3728,15 +3927,13 @@ function createDagCommand() {
3728
3927
  const client = createClient(opts.url);
3729
3928
  let projectId;
3730
3929
  if (!/^[a-f0-9]{24}$/.test(templateRef)) {
3731
- const projectOpt = opts.project ?? readWorkspaceConfig()?.defaultProject;
3732
- if (projectOpt !== void 0) {
3733
- const r = await resolveProjectId(client, projectOpt);
3734
- if (!r.success) {
3735
- renderResult("json", r);
3736
- return;
3737
- }
3738
- projectId = r.data;
3930
+ const explicitProject = opts.project !== void 0 && opts.project.trim().length > 0 ? opts.project : void 0;
3931
+ const pr = await resolveScopedProject(client, explicitProject, "ws-default", { commandLabel: "siming dag template show" });
3932
+ if (!pr.success) {
3933
+ renderResult(opts.format, pr);
3934
+ return;
3739
3935
  }
3936
+ projectId = pr.data.projectId;
3740
3937
  }
3741
3938
  const result = await handleGetTemplate(client, templateRef, projectId);
3742
3939
  renderResult(opts.format, result, { fields: result.success ? templateFields(result.data) : [] });
@@ -3903,13 +4100,15 @@ async function runYamlImport(client, yamlText, opts, jsonParseError2) {
3903
4100
  return;
3904
4101
  }
3905
4102
  let projectId;
3906
- if (opts.project !== void 0) {
3907
- const r = await resolveProjectId(client, opts.project);
4103
+ if (opts.project !== void 0 && opts.project.trim().length > 0) {
4104
+ const r = await resolveScopedProject(client, opts.project, "explicit-only", {
4105
+ commandLabel: "siming dag template import"
4106
+ });
3908
4107
  if (!r.success) {
3909
4108
  renderResult("json", r);
3910
4109
  return;
3911
4110
  }
3912
- projectId = r.data;
4111
+ projectId = r.data.projectId;
3913
4112
  }
3914
4113
  const result = await importTemplateFromYaml(client, yamlText, {
3915
4114
  ...projectId !== void 0 ? { projectId } : {},
@@ -3969,13 +4168,15 @@ async function runBundleImport(client, bundleText, opts) {
3969
4168
  }));
3970
4169
  return;
3971
4170
  }
3972
- const target = await resolveImportTargetProject(client, opts.project);
3973
- if (!target.success) {
3974
- renderResult("json", target);
4171
+ const explicitBundleTarget = opts.project !== void 0 && opts.project.trim().length > 0 ? opts.project : void 0;
4172
+ const pr = await resolveScopedProject(client, explicitBundleTarget, "instance-default", { commandLabel: "siming dag template import" });
4173
+ if (!pr.success) {
4174
+ renderResult("json", pr);
3975
4175
  return;
3976
4176
  }
3977
4177
  const result = await importTemplateBundle(client, bundleText, {
3978
- targetProjectId: target.data,
4178
+ targetProjectId: pr.data.projectId,
4179
+ // instance-default 成功必含 id
3979
4180
  ...opts.overwriteDeps === true ? { overwriteDeps: true } : {},
3980
4181
  ...opts.plan === true ? { planOnly: true } : {}
3981
4182
  });
@@ -3985,31 +4186,6 @@ async function runBundleImport(client, bundleText, opts) {
3985
4186
  }
3986
4187
  renderBundleImportOutcome(result.data, opts.plan === true);
3987
4188
  }
3988
- async function resolveImportTargetProject(client, projectOpt) {
3989
- if (projectOpt !== void 0) return resolveProjectId(client, projectOpt);
3990
- const wsDefault = readWorkspaceConfig()?.defaultProject;
3991
- if (wsDefault !== void 0) {
3992
- const r = await resolveProjectId(client, wsDefault);
3993
- if (!r.success) {
3994
- return fail({
3995
- ...r.error,
3996
- hint: `workspace defaultProject "${wsDefault}" \u89E3\u6790\u5931\u8D25\u2014\u2014siming config unset defaultProject \u540E\u91CD\u8BD5\uFF0C\u6216\u663E\u5F0F --project <key|id>`
3997
- });
3998
- }
3999
- return r;
4000
- }
4001
- const list = await handleListProjects(client);
4002
- if (!list.success) return list;
4003
- const defaultProject = list.data.find((p) => p.key === "default");
4004
- if (defaultProject?.id === void 0) {
4005
- return fail({
4006
- message: "bundle \u5BFC\u5165\u9700\u8981\u76EE\u6807\u9879\u76EE\uFF1A\u672A\u6307\u5B9A --project\uFF0C\u5DE5\u4F5C\u533A\u65E0 defaultProject\uFF0C\u5B9E\u4F8B\u4E2D\u4E5F\u65E0 key=default \u7684\u9879\u76EE",
4007
- httpStatus: 0,
4008
- hint: "pass --project <key|id>\uFF0C\u6216\u5148 siming project create + siming init --project <key>"
4009
- });
4010
- }
4011
- return { success: true, data: defaultProject.id };
4012
- }
4013
4189
  function renderBundleImportOutcome(outcome, planOnly) {
4014
4190
  const t = outcome.plan.template;
4015
4191
  const overwritten = t.willOverwrite && t.currentVersion !== void 0 ? `\u8986\u76D6\u73B0\u6709 v${t.currentVersion} \u2192 ` : "";
@@ -4284,7 +4460,7 @@ ${failures.join("\n")}`,
4284
4460
  // src/commands/node/command.ts
4285
4461
  function createNodeCommand() {
4286
4462
  const node = new Command6("node").description("NodePreset management \u2014 reusable DAG node assets (addressed by unique code)");
4287
- withCommonOpts(node.command("list"), "table").option("--project <id>", "overlay visible set: global + this project").option("--scope <scope>", "exact scope filter: global|project").option("--q <keyword>", "search in code/label").description("List node presets (summary projection: no prompt \u2014 use `show` for full detail)").action(async (opts) => {
4463
+ withCommonOpts(node.command("list"), "table").option("--project <key|id>", "overlay visible set: global + this project (key|id)").option("--scope <scope>", "exact scope filter: global|project").option("--q <keyword>", "search in code/label").description("List node presets (summary projection: no prompt \u2014 use `show` for full detail)").action(async (opts) => {
4288
4464
  let scope;
4289
4465
  if (opts.scope !== void 0) {
4290
4466
  const scopeR = parseEnum(opts.scope, SkillScopeSchema2, "scope");
@@ -4295,8 +4471,17 @@ function createNodeCommand() {
4295
4471
  scope = scopeR.data;
4296
4472
  }
4297
4473
  const client = createClient(opts.url);
4474
+ let projectId;
4475
+ if (opts.project !== void 0 && opts.project.trim().length > 0) {
4476
+ const pr = await resolveScopedProject(client, opts.project, "explicit-only");
4477
+ if (!pr.success) {
4478
+ renderResult(opts.format, pr);
4479
+ return;
4480
+ }
4481
+ projectId = pr.data.projectId;
4482
+ }
4298
4483
  const result = await handleListNodePresets(client, {
4299
- ...opts.project !== void 0 ? { projectId: opts.project } : {},
4484
+ ...projectId !== void 0 ? { projectId } : {},
4300
4485
  ...scope !== void 0 ? { scope } : {},
4301
4486
  ...opts.q !== void 0 ? { q: opts.q } : {}
4302
4487
  });
@@ -4319,7 +4504,7 @@ function createNodeCommand() {
4319
4504
  const result = await handleGetNodePreset(client, code);
4320
4505
  renderResult(opts.format, result, { fields: result.success ? nodeFields2(result.data) : [] });
4321
4506
  });
4322
- node.command("create").description("Create a node preset (code globally unique & immutable; scope required)").option("--url <url>", "server base URL").requiredOption("--code <code>", "unique business code (kebab-case, immutable)").requiredOption("--label <text>", "display label").requiredOption("--node-id <id>", "default node id when assembled into templates ([A-Za-z0-9_-]+)").requiredOption("--phase <phase>", "node phase (e.g. entry|track|test|exit)").requiredOption("--track <track>", "node track (backend|ui|all)").requiredOption("--prompt <text>", "node prompt body").option("--prompt-file <path>", "read prompt from file (overrides --prompt)").option("--skill <name>", "bound skill name (repeatable)", collectArgs, []).option("--agent <name>", "callable agent name (repeatable)", collectArgs, []).option("--description <text>", "asset description").requiredOption("--scope <scope>", "global|project").option("--project-id <hex>", "projectId (required when scope=project)").action(async (opts) => {
4507
+ node.command("create").description("Create a node preset (code globally unique & immutable; scope required)").option("--url <url>", "server base URL").requiredOption("--code <code>", "unique business code (kebab-case, immutable)").requiredOption("--label <text>", "display label").requiredOption("--node-id <id>", "default node id when assembled into templates ([A-Za-z0-9_-]+)").requiredOption("--phase <phase>", "node phase (e.g. entry|track|test|exit)").requiredOption("--track <track>", "node track (backend|ui|all)").requiredOption("--prompt <text>", "node prompt body").option("--prompt-file <path>", "read prompt from file (overrides --prompt)").option("--skill <name>", "bound skill name (repeatable)", collectArgs, []).option("--agent <name>", "callable agent name (repeatable)", collectArgs, []).option("--description <text>", "asset description").requiredOption("--scope <scope>", "global|project").option("--project-id <key|id>", "projectId (scope=project; falls back to workspace/instance default project)").action(async (opts) => {
4323
4508
  const scopeR = parseEnum(opts.scope, SkillScopeSchema2, "scope");
4324
4509
  if (!scopeR.success) {
4325
4510
  renderResult("json", scopeR);
@@ -4342,6 +4527,16 @@ function createNodeCommand() {
4342
4527
  return;
4343
4528
  }
4344
4529
  const client = createClient(opts.url);
4530
+ let projectId;
4531
+ const explicitProject = opts.projectId !== void 0 && opts.projectId.trim().length > 0 ? opts.projectId : void 0;
4532
+ if (explicitProject !== void 0 || scopeR.data === "project") {
4533
+ const pr = await resolveScopedProject(client, explicitProject, "instance-default", { commandLabel: "siming node create" });
4534
+ if (!pr.success) {
4535
+ renderResult("json", pr);
4536
+ return;
4537
+ }
4538
+ projectId = pr.data.projectId;
4539
+ }
4345
4540
  const input = {
4346
4541
  code: opts.code,
4347
4542
  label: opts.label,
@@ -4354,7 +4549,7 @@ function createNodeCommand() {
4354
4549
  scope: scopeR.data,
4355
4550
  version: "1.0.0",
4356
4551
  ...opts.description !== void 0 ? { description: opts.description } : {},
4357
- ...opts.projectId !== void 0 ? { projectId: opts.projectId } : {}
4552
+ ...projectId !== void 0 ? { projectId } : {}
4358
4553
  };
4359
4554
  renderResult("json", await handleCreateNodePreset(client, input));
4360
4555
  });
@@ -4399,17 +4594,27 @@ function createNodeCommand() {
4399
4594
  }
4400
4595
  renderResult("json", await handleUpdateNodePreset(client, code, patch));
4401
4596
  });
4402
- node.command("copy <code>").description("Copy a node preset to a new code/scope (copy version resets 1.0.0)").option("--url <url>", "server base URL").requiredOption("--new-code <code>", "new unique code (kebab-case)").requiredOption("--new-scope <scope>", "global|project").option("--target-project-id <hex>", "target projectId (required when newScope=project)").action(async (code, opts) => {
4597
+ node.command("copy <code>").description("Copy a node preset to a new code/scope (copy version resets 1.0.0)").option("--url <url>", "server base URL").requiredOption("--new-code <code>", "new unique code (kebab-case)").requiredOption("--new-scope <scope>", "global|project").option("--target-project-id <key|id>", "target projectId (newScope=project; falls back to workspace/instance default project)").action(async (code, opts) => {
4403
4598
  const scopeR = parseEnum(opts.newScope, SkillScopeSchema2, "new-scope");
4404
4599
  if (!scopeR.success) {
4405
4600
  renderResult("json", scopeR);
4406
4601
  return;
4407
4602
  }
4408
4603
  const client = createClient(opts.url);
4604
+ let targetProjectId;
4605
+ const explicitTarget = opts.targetProjectId !== void 0 && opts.targetProjectId.trim().length > 0 ? opts.targetProjectId : void 0;
4606
+ if (explicitTarget !== void 0 || scopeR.data === "project") {
4607
+ const pr = await resolveScopedProject(client, explicitTarget, "instance-default", { commandLabel: "siming node copy" });
4608
+ if (!pr.success) {
4609
+ renderResult("json", pr);
4610
+ return;
4611
+ }
4612
+ targetProjectId = pr.data.projectId;
4613
+ }
4409
4614
  renderResult("json", await handleCopyNodePreset(client, code, {
4410
4615
  newCode: opts.newCode,
4411
4616
  newScope: scopeR.data,
4412
- ...opts.targetProjectId !== void 0 ? { targetProjectId: opts.targetProjectId } : {}
4617
+ ...targetProjectId !== void 0 ? { targetProjectId } : {}
4413
4618
  }));
4414
4619
  });
4415
4620
  node.command("enabled <code>").description("Enable/disable a node preset (sole write entry for enabled; disabled presets invisible to list/compose)").option("--url <url>", "server base URL").requiredOption("--value <bool>", "true|false").action(async (code, opts) => {
@@ -4792,26 +4997,22 @@ function createFlowCommand() {
4792
4997
  });
4793
4998
  flow.command("install <pkg-spec>").description(
4794
4999
  "Install a node library package from npm \u2014 validate 1.1 manifest \u2192 plan complete assets \u2192 confirm \u2192 apply dependencies \u2192 nodes \u2192 library"
4795
- ).option("--url <url>", "server base URL").option("--scope <scope>", "install scope: global|project", "global").option("--project <key|id>", "target project (required when scope=project)").option("--registry <url>", "npm registry for pack (e.g. private mirror; default npm config)").option("--yes", "skip interactive confirmation").option("--force", "allow version downgrade; admin may also sync a conflicting model alias display name (realModel is never overwritten)").action(async (pkgSpec, opts) => {
5000
+ ).option("--url <url>", "server base URL").option("--scope <scope>", "install scope: global|project", "global").option("--project <key|id>", "target project (scope=project; falls back to workspace/instance default project)").option("--registry <url>", "npm registry for pack (e.g. private mirror; default npm config)").option("--yes", "skip interactive confirmation").option("--force", "allow version downgrade; admin may also sync a conflicting model alias display name (realModel is never overwritten)").action(async (pkgSpec, opts) => {
4796
5001
  const scopeResult = parseEnum(opts.scope, SkillScopeSchema3, "scope");
4797
5002
  if (!scopeResult.success) {
4798
5003
  renderResult("json", scopeResult);
4799
5004
  return;
4800
5005
  }
4801
5006
  const scope = scopeResult.data;
4802
- if (scope === "project" && opts.project === void 0) {
4803
- renderResult("json", fail({ message: "scope=project \u9700\u8981 --project <key|id>", httpStatus: 0 }));
4804
- return;
4805
- }
4806
5007
  const client = createClient(opts.url);
4807
5008
  let projectId;
4808
5009
  if (scope === "project") {
4809
- const projectResult = await resolveProjectId(client, opts.project);
4810
- if (!projectResult.success) {
4811
- renderResult("json", projectResult);
5010
+ const pr = await resolveScopedProject(client, opts.project, "instance-default", { commandLabel: "siming flow install" });
5011
+ if (!pr.success) {
5012
+ renderResult("json", pr);
4812
5013
  return;
4813
5014
  }
4814
- projectId = projectResult.data;
5015
+ projectId = pr.data.projectId;
4815
5016
  }
4816
5017
  const fetched = await fetchManifest(pkgSpec, opts.registry);
4817
5018
  if (!fetched.success) {
@@ -4855,15 +5056,16 @@ ${conflicts.map((item) => ` ${item.kind}:${item.key} \u2014 ${item.reason ?? "\
4855
5056
  cleanup();
4856
5057
  }
4857
5058
  });
4858
- flow.command("compose <composition>").description("Assemble a DAG template from an installed library composition (value-copy nodes + sourcePreset baseline)").option("--url <url>", "server base URL").requiredOption("--project <key|id>", "target project (template owner)").option("--name <name>", "generated template name (default: composition name)").option("--from <library>", "source library name (required when multiple libraries define the same composition)").action(async (composition, opts) => {
5059
+ flow.command("compose <composition>").description("Assemble a DAG template from an installed library composition (value-copy nodes + sourcePreset baseline)").option("--url <url>", "server base URL").option("--project <key|id>", "target project (template owner; falls back to workspace/instance default project)").option("--name <name>", "generated template name (default: composition name)").option("--from <library>", "source library name (required when multiple libraries define the same composition)").action(async (composition, opts) => {
4859
5060
  const client = createClient(opts.url);
4860
- const projectResult = await resolveProjectId(client, opts.project);
4861
- if (!projectResult.success) {
4862
- renderResult("json", projectResult);
5061
+ const pr = await resolveScopedProject(client, opts.project, "instance-default", { commandLabel: "siming flow compose" });
5062
+ if (!pr.success) {
5063
+ renderResult("json", pr);
4863
5064
  return;
4864
5065
  }
4865
5066
  const result = await composeFlowTemplate(client, composition, {
4866
- projectId: projectResult.data,
5067
+ projectId: pr.data.projectId,
5068
+ // instance-default 成功必含 id
4867
5069
  ...opts.name !== void 0 ? { name: opts.name } : {},
4868
5070
  ...opts.from !== void 0 ? { from: opts.from } : {}
4869
5071
  });
@@ -6933,13 +7135,13 @@ function parseVersion(v) {
6933
7135
  // src/commands/install/command.ts
6934
7136
  function createInstallCommand() {
6935
7137
  const install = new Command12("install").description("Install skills/agents from Siming to local AI platform");
6936
- withCommonOpts(install.command("plan"), "table").option("--scope <s>", "install scope: global|project", "global").option("--project <key-or-id>", "project key or id (required when scope=project)").option("--only <name>", 'filter by asset name (repeatable; use "siming" for the MCP entry)', collectArgs, []).description("Preview install plan (no write)").action(async (opts) => {
7138
+ withCommonOpts(install.command("plan"), "table").option("--scope <s>", "install scope: global|project", "global").option("--project <key|id>", "project key or id (scope=project; falls back to workspace/instance default project)").option("--only <name>", 'filter by asset name (repeatable; use "siming" for the MCP entry)', collectArgs, []).description("Preview install plan (no write)").action(async (opts) => {
6937
7139
  const scope = parseScope(opts.scope, opts.format);
6938
7140
  if (!scope) return;
6939
7141
  const client = createClient(opts.url);
6940
7142
  const projectR = await resolveInstallProject(client, scope, opts.project);
6941
7143
  if (!projectR.ok) {
6942
- renderResult(opts.format, { success: false, error: { message: projectR.error.message, httpStatus: 0, ...projectR.error.hint ? { hint: projectR.error.hint } : {} } });
7144
+ renderResult(opts.format, { success: false, error: { message: projectR.error.message, httpStatus: projectR.error.httpStatus, ...projectR.error.bizCode !== void 0 ? { bizCode: projectR.error.bizCode } : {}, ...projectR.error.hint ? { hint: projectR.error.hint } : {} } });
6943
7145
  return;
6944
7146
  }
6945
7147
  const projectId = projectR.projectId;
@@ -6951,14 +7153,14 @@ function createInstallCommand() {
6951
7153
  const plan = result.data;
6952
7154
  renderInstallPlan2(opts.format, plan, scope, resolveInstallTargets(scope).targets);
6953
7155
  });
6954
- withCommonOpts(install.command("apply"), "json").option("--scope <s>", "install scope: global|project", "global").option("--project <key-or-id>", "project key or id (required when scope=project)").option("--only <name>", 'install only this asset (repeatable; use "siming" for the MCP entry)', collectArgs, []).option("--yes", "skip interactive confirmation").option("--force", "overwrite all selected local assets, including same-version and conflict items").description("Apply install plan (write to local)").action(
7156
+ withCommonOpts(install.command("apply"), "json").option("--scope <s>", "install scope: global|project", "global").option("--project <key|id>", "project key or id (scope=project; falls back to workspace/instance default project)").option("--only <name>", 'install only this asset (repeatable; use "siming" for the MCP entry)', collectArgs, []).option("--yes", "skip interactive confirmation").option("--force", "overwrite all selected local assets, including same-version and conflict items").description("Apply install plan (write to local)").action(
6955
7157
  async (opts) => {
6956
7158
  const scope = parseScope(opts.scope, opts.format);
6957
7159
  if (!scope) return;
6958
7160
  const client = createClient(opts.url);
6959
7161
  const projectR = await resolveInstallProject(client, scope, opts.project);
6960
7162
  if (!projectR.ok) {
6961
- renderResult(opts.format, { success: false, error: { message: projectR.error.message, httpStatus: 0, ...projectR.error.hint ? { hint: projectR.error.hint } : {} } });
7163
+ renderResult(opts.format, { success: false, error: { message: projectR.error.message, httpStatus: projectR.error.httpStatus, ...projectR.error.bizCode !== void 0 ? { bizCode: projectR.error.bizCode } : {}, ...projectR.error.hint ? { hint: projectR.error.hint } : {} } });
6962
7164
  return;
6963
7165
  }
6964
7166
  const projectId = projectR.projectId;
@@ -7034,34 +7236,19 @@ function parseScope(s, format) {
7034
7236
  }
7035
7237
  async function resolveInstallProject(client, scope, projectOpt) {
7036
7238
  if (scope === "global") return { ok: true };
7037
- if (projectOpt) {
7038
- const r = await resolveProjectId(client, projectOpt);
7039
- if (!r.success) {
7040
- return { ok: false, error: { message: r.error.message, ...r.error.hint ? { hint: r.error.hint } : {} } };
7041
- }
7042
- return { ok: true, projectId: r.data };
7043
- }
7044
- const ws = findWorkspaceDir();
7045
- const cfg = ws ? readWorkspaceConfigSafe(ws) : null;
7046
- if (cfg?.defaultProject) {
7047
- const r = await resolveProjectId(client, cfg.defaultProject);
7048
- if (r.success) return { ok: true, projectId: r.data };
7049
- return { ok: false, error: { message: r.error.message, ...r.error.hint ? { hint: r.error.hint } : {} } };
7050
- }
7051
- return {
7052
- ok: false,
7053
- error: {
7054
- message: "scope=project requires --project <key|id> or workspace default project",
7055
- hint: "run `siming config set defaultProject <key|id>` or `siming install --scope project --project <key|id>`"
7056
- }
7057
- };
7058
- }
7059
- function readWorkspaceConfigSafe(ws) {
7060
- try {
7061
- return readWorkspaceConfig(ws);
7062
- } catch {
7063
- return null;
7239
+ const r = await resolveScopedProject(client, projectOpt, "instance-default", { commandLabel: "siming install" });
7240
+ if (!r.success) {
7241
+ return {
7242
+ ok: false,
7243
+ error: {
7244
+ message: r.error.message,
7245
+ httpStatus: r.error.httpStatus,
7246
+ ...r.error.bizCode !== void 0 ? { bizCode: r.error.bizCode } : {},
7247
+ ...r.error.hint ? { hint: r.error.hint } : {}
7248
+ }
7249
+ };
7064
7250
  }
7251
+ return r.data.projectId !== void 0 ? { ok: true, projectId: r.data.projectId } : { ok: true };
7065
7252
  }
7066
7253
  async function buildInstallPlan(client, scope, projectId, only) {
7067
7254
  const listQuery = scope === "global" ? { scope: "global", includeDisabled: true } : { scope: "project", ...projectId ? { projectId } : {}, includeDisabled: true };
@@ -7071,7 +7258,15 @@ async function buildInstallPlan(client, scope, projectId, only) {
7071
7258
  if (!agentsR.success) return agentsR;
7072
7259
  const t = resolveInstallTargets(scope);
7073
7260
  if (t.error || !t.targets) {
7074
- return { success: false, error: { message: t.error ?? "cannot resolve install targets", httpStatus: 0 } };
7261
+ return {
7262
+ success: false,
7263
+ error: {
7264
+ message: t.error ?? "cannot resolve install targets",
7265
+ httpStatus: 0,
7266
+ // T202609190002:错误四要素契约补齐(hint 缺失导致 E2E_CLI_WS_INSTALL_002 失败)
7267
+ hint: "run `siming init --project <key|id>` inside the project workspace (containing .siming/), or retry with --scope global"
7268
+ }
7269
+ };
7075
7270
  }
7076
7271
  const dir = t.targets.dir;
7077
7272
  const items = [];
@@ -7431,7 +7626,7 @@ async function runInit(client, input) {
7431
7626
  };
7432
7627
  }
7433
7628
  function createInitCommand() {
7434
- const init = new Command13("init").description("Initialize workspace config for Siming (idempotent)").option("--url <url>", "server base URL (also used to resolve project key)").option("--server-url <url>", "server base URL to persist into workspace config").option("--project <key-or-id>", "project key or id to set as workspace default project").option("--install", "run install apply after init (non-interactive; writes to .agents universal dir)").option("--install-scope <scope>", "install scope when --install: global|project (default: global; global also registers mcp.json)").option("--format <fmt>", "output format", "fancy").action(async (opts) => {
7629
+ const init = new Command13("init").description("Initialize workspace config for Siming (idempotent)").option("--url <url>", "server base URL (also used to resolve project key)").option("--server-url <url>", "server base URL to persist into workspace config").option("--project <key|id>", "project key or id to set as workspace default project").option("--install", "run install apply after init (non-interactive; writes to .agents universal dir)").option("--install-scope <scope>", "install scope when --install: global|project (default: global; global also registers mcp.json)").option("--format <fmt>", "output format", "fancy").action(async (opts) => {
7435
7630
  const client = createClient(opts.url);
7436
7631
  const result = await runInit(client, {
7437
7632
  ...opts.serverUrl ? { serverUrl: opts.serverUrl } : {},
@@ -7744,7 +7939,7 @@ ${ROADMAP_TEXT}
7744
7939
  \u9879\u76EE\u6807\u8BC6\u3001\u5FEB\u901F\u901A\u9053\u89C4\u5219\uFF09\uFF0C\u6309\u6E05\u5355\u66FF\u6362\u4E3A\u9879\u76EE\u771F\u5B9E\u7EA6\u5B9A\u540E\u65B9\u53EF\u7528\u4E8E\u521B\u5EFA\u4EFB\u52A1\u3002
7745
7940
  \u540C\u540D\u51B2\u7A81\u4FDD\u62A4\uFF1A\u9879\u76EE\u5185\u5DF2\u6709\u540C\u540D\u8D44\u4EA7\u65F6\u9ED8\u8BA4\u62D2\u7EDD\u4E14\u96F6\u5199\u5165\uFF1B
7746
7941
  \u786E\u8BA4\u66FF\u6362\u52A0 --overwrite\uFF08\u7248\u672C\u5355\u8C03\u9012\u589E\uFF0C\u542F\u505C\u72B6\u6001\u4E0D\u53D8\uFF09\u3002
7747
- \u67E5\u770B\u73B0\u5B58\u8D44\u4EA7\uFF1Asiming skill show task-create --scope project --project-id <id>
7942
+ \u67E5\u770B\u73B0\u5B58\u8D44\u4EA7\uFF1Asiming skill show task-create --scope project --project-id <key|id>
7748
7943
 
7749
7944
  2. siming-arch \u2014\u2014 \u67B6\u6784\u6587\u6863\u7D22\u5F15\uFF1A\u6307\u5411\u672C\u9879\u76EE\u4ED3\u5E93\u67B6\u6784\u6587\u6863\u76EE\u5F55\u7684\u9AA8\u67B6
7750
7945
  \uFF08\u6BCF\u9879\u76EE\u4E00\u4EFD\uFF1B\u67B6\u6784\u6587\u6863\u5B9E\u4F53\u5B58\u9879\u76EE\u4ED3\u5E93\uFF0C\u7D22\u5F15\u53EA\u5B58\u4F4D\u7F6E\u4E0D\u5B58\u5185\u5BB9\uFF09\u3002
@@ -8523,7 +8718,7 @@ function parseDeclarationsMap(spec) {
8523
8718
  }
8524
8719
  function createIssueCommand() {
8525
8720
  const issue = new Command18("issue").description("Issue pool: record \u2192 organize \u2192 convert to tasks");
8526
- withCommonOpts(issue.command("add"), "fancy").description("Record an issue into the pool (source=cli)").argument("[title]", "issue title (or use --title)").option("--title <t>", "issue title (1-200 chars)").option("--description <d>", "issue description (optional)").option("--project <key-or-id>", "projectId (key or id; defaults to workspace defaultProject, then server default project)").action(async (titleArg, opts) => {
8721
+ withCommonOpts(issue.command("add"), "fancy").description("Record an issue into the pool (source=cli; requires --approved human approval quote)").argument("[title]", "issue title (or use --title)").option("--title <t>", "issue title (1-200 chars)").option("--description <d>", "issue description (optional)").option("--approved <text>", `human approval quote verbatim (1-500 chars; e.g. user's literal reply "OK, record it")`).option("--project <key|id>", "projectId (key|id; falls back to workspace defaultProject, then server default project)").action(async (titleArg, opts) => {
8527
8722
  const client = createClient(opts.url);
8528
8723
  const title = opts.title ?? titleArg;
8529
8724
  if (title === void 0 || title.length === 0) {
@@ -8533,29 +8728,41 @@ function createIssueCommand() {
8533
8728
  });
8534
8729
  return;
8535
8730
  }
8536
- let projectId;
8537
- if (opts.project) {
8538
- const r = await resolveProjectId(client, opts.project);
8539
- if (!r.success) {
8540
- renderResult("json", r);
8541
- return;
8542
- }
8543
- projectId = r.data;
8544
- } else {
8545
- const wsDefaultProject = readWorkspaceConfig()?.defaultProject;
8546
- if (wsDefaultProject) {
8547
- const r = await resolveProjectId(client, wsDefaultProject);
8548
- if (r.success) projectId = r.data;
8549
- }
8731
+ if (opts.approved === void 0 || opts.approved.trim() === "") {
8732
+ renderResult("json", {
8733
+ success: false,
8734
+ error: {
8735
+ message: 'issue add \u987B\u643A\u5E26 --approved "<\u4EBA\u7C7B\u5BA1\u6279\u539F\u8BDD>"\uFF08\u5982\u7528\u6237\u56DE\u590D\u7684\u300COK\uFF0C\u8BB0\u5F55\u300D\u539F\u6587\u9010\u5B57\uFF09\u2014\u2014AI \u4E0D\u5F97\u672A\u7ECF\u7528\u6237\u6279\u51C6\u5F55\u5165 issue\uFF0C\u4E5F\u4E0D\u5F97\u4EE3\u586B/\u7F16\u9020\u5BA1\u6279\u539F\u8BDD',
8736
+ httpStatus: 0
8737
+ }
8738
+ });
8739
+ return;
8740
+ }
8741
+ if (opts.approved.length > 500) {
8742
+ renderResult("json", {
8743
+ success: false,
8744
+ error: {
8745
+ message: `--approved \u8D85\u51FA 500 \u5B57\u4E0A\u9650\uFF08\u5F53\u524D ${String(opts.approved.length)} \u5B57\uFF09\u2014\u2014\u5BA1\u6279\u539F\u8BDD\u987B\u4E3A\u7528\u6237\u539F\u6587\u4E14 \u2264500 \u5B57\uFF0C\u53EF\u8BF7\u7528\u6237\u7ED9\u51FA\u7B80\u77ED\u660E\u786E\u7684\u6279\u51C6\u8868\u8FF0\u540E\u5F15\u7528\u5176\u539F\u6587`,
8746
+ httpStatus: 0
8747
+ }
8748
+ });
8749
+ return;
8750
+ }
8751
+ const projectR = await resolveScopedProject(client, opts.project, "ws-default", { commandLabel: "siming issue add" });
8752
+ if (!projectR.success) {
8753
+ renderResult(opts.format ?? "fancy", projectR);
8754
+ return;
8550
8755
  }
8756
+ const projectId = projectR.data.projectId;
8551
8757
  const result = await handleCreateIssue(client, {
8552
8758
  title,
8553
8759
  ...opts.description !== void 0 ? { description: opts.description } : {},
8554
- ...projectId !== void 0 ? { projectId } : {}
8760
+ ...projectId !== void 0 ? { projectId } : {},
8761
+ approved: opts.approved
8555
8762
  });
8556
8763
  renderResult(opts.format ?? "fancy", result);
8557
8764
  });
8558
- withCommonOpts(issue.command("list"), "table").option("--status <s>", "filter by issue status (pending|partial|converted|cancelled)").option("--source <s>", "filter by source channel (mcp|cli|web)").option("--project <key-or-id>", "filter by projectId (key or id)").option("--task <taskId>", "filter by linked task").option("--q <keyword>", "search in title/description").option("--view <v>", "view: all|open (default all; open = \u6709\u6548\u672A\u8DDF\u8FDB\u5B8C)").option("--page <n>", "page number (default 1)", "1").option("--limit <n>", `page size (default ${PAGE_DEFAULT2}, max ${PAGE_MAX2})`).option("--all", `show all (limit ${PAGE_MAX2})`).description("List issues (with progress aggregation done/cancelled/total)").action(async (opts) => {
8765
+ withCommonOpts(issue.command("list"), "table").option("--status <s>", "filter by issue status (pending|partial|converted|cancelled|done)").option("--source <s>", "filter by source channel (mcp|cli|web)").option("--project <key|id>", "filter by projectId (key|id)").option("--task <taskId>", "filter by linked task").option("--q <keyword>", "search in title/description").option("--view <v>", "view: all|open (default all; open = \u6709\u6548\u672A\u8DDF\u8FDB\u5B8C)").option("--page <n>", "page number (default 1)", "1").option("--limit <n>", `page size (default ${PAGE_DEFAULT2}, max ${PAGE_MAX2})`).option("--all", `show all (limit ${PAGE_MAX2})`).description("List issues (with progress aggregation done/cancelled/total)").action(async (opts) => {
8559
8766
  const client = createClient(opts.url);
8560
8767
  const filters = {};
8561
8768
  if (opts.status) filters.status = opts.status;
@@ -8570,13 +8777,14 @@ function createIssueCommand() {
8570
8777
  }
8571
8778
  filters.view = r.data;
8572
8779
  }
8573
- if (opts.project) {
8574
- const r = await resolveProjectId(client, opts.project);
8575
- if (!r.success) {
8576
- renderResult(opts.format ?? "table", r);
8780
+ if (opts.project !== void 0 && opts.project.trim().length > 0) {
8781
+ const pr = await resolveScopedProject(client, opts.project, "explicit-only");
8782
+ if (!pr.success) {
8783
+ renderResult(opts.format ?? "table", pr);
8577
8784
  return;
8578
8785
  }
8579
- filters.projectId = r.data;
8786
+ const pid = pr.data.projectId;
8787
+ if (pid !== void 0) filters.projectId = pid;
8580
8788
  }
8581
8789
  if (opts.all) {
8582
8790
  filters.limit = PAGE_MAX2;
@@ -8656,7 +8864,12 @@ function createIssueCommand() {
8656
8864
  const result = await handleCancelIssue(client, issueId, opts.reason);
8657
8865
  renderResult(opts.format ?? "fancy", result);
8658
8866
  });
8659
- withCommonOpts(issue.command("convert"), "fancy").requiredOption("--issue-ids <ids>", "comma-separated issue ids (or repeatable flag)", collectArgs, []).option("--declaration <mode>", "uniform declaration for all issues: all|partial (default all)").option("--declarations <map>", "per-issue declarations: id1:all,id2:partial (overrides --declaration)").requiredOption("--title <t>", "new task title").requiredOption("--template <code|name|id>", "DAG template code (preferred), name, or 24-hex id").requiredOption("--track <t>", "task track (backend|ui|mixed|research)").option("--type <t>", "task type (feature|bugfix|ui-tweak|research)").option("--skip <ids>", "node ids to prune (comma-separated, repeatable)", collectArgs, []).option("--project <key-or-id>", "projectId (key or id; issues and new task must belong to it)").description("Convert issues to a task (single group: N issues \u2192 1 task; split = multiple calls)").action(async (opts) => {
8867
+ withCommonOpts(issue.command("done"), "fancy").argument("<issueId>", "issue id (I-prefixed)").option("--note <n>", "completion note (optional, \u2264500 chars \u2014 e.g. \u5728\u54EA\u4E2A\u4EFB\u52A1/\u54EA\u6B21\u6539\u52A8\u4E2D\u987A\u5E26\u89E3\u51B3)").description("Mark an issue as done directly (terminal state; keep taskLinks)").action(async (issueId, opts) => {
8868
+ const client = createClient(opts.url);
8869
+ const result = await handleDoneIssue(client, issueId, opts.note);
8870
+ renderResult(opts.format ?? "fancy", result);
8871
+ });
8872
+ withCommonOpts(issue.command("convert"), "fancy").requiredOption("--issue-ids <ids>", "comma-separated issue ids (or repeatable flag)", collectArgs, []).option("--declaration <mode>", "uniform declaration for all issues: all|partial (default all)").option("--declarations <map>", "per-issue declarations: id1:all,id2:partial (overrides --declaration)").requiredOption("--title <t>", "new task title").requiredOption("--template <code|name|id>", "DAG template code (preferred), name, or 24-hex id").requiredOption("--track <t>", "task track (backend|ui|mixed|research)").option("--type <t>", "task type (feature|bugfix|ui-tweak|research)").option("--skip <ids>", "node ids to prune (comma-separated, repeatable)", collectArgs, []).option("--project <key|id>", "projectId (key|id; issues and new task must belong to it)").description("Convert issues to a task (single group: N issues \u2192 1 task; split = multiple calls)").action(async (opts) => {
8660
8873
  const client = createClient(opts.url);
8661
8874
  const issueIds = [
8662
8875
  ...new Set((opts.issueIds ?? []).flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0))
@@ -8687,13 +8900,13 @@ function createIssueCommand() {
8687
8900
  declarations = Object.fromEntries(issueIds.map((id) => [id, "all"]));
8688
8901
  }
8689
8902
  let projectId;
8690
- if (opts.project) {
8691
- const r = await resolveProjectId(client, opts.project);
8692
- if (!r.success) {
8693
- renderResult(opts.format ?? "fancy", r);
8903
+ if (opts.project !== void 0 && opts.project.trim().length > 0) {
8904
+ const pr = await resolveScopedProject(client, opts.project, "explicit-only");
8905
+ if (!pr.success) {
8906
+ renderResult(opts.format ?? "fancy", pr);
8694
8907
  return;
8695
8908
  }
8696
- projectId = r.data;
8909
+ projectId = pr.data.projectId;
8697
8910
  } else {
8698
8911
  const first = await handleGetIssue(client, issueIds[0]);
8699
8912
  if (!first.success) {