@siming-org/cli 0.6.5 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { Command as Command19 } from "commander";
2
+ import { Command as Command20 } from "commander";
3
3
  import { VERSION as VERSION5 } from "@siming-org/core";
4
4
 
5
5
  // src/output/fancy.ts
@@ -522,10 +522,13 @@ function writeGlobalAuthToken(token, path) {
522
522
 
523
523
  // src/client/http-client.ts
524
524
  var SimingHttpClient = class {
525
- constructor(baseUrl) {
525
+ /** T202609150002:通道 source 标识(x-siming-source header 单一注入口;Web 通道由 web client 固定携带 web) */
526
+ constructor(baseUrl, source = "cli") {
526
527
  this.baseUrl = baseUrl;
528
+ this.source = source;
527
529
  }
528
530
  baseUrl;
531
+ source;
529
532
  /** server base URL(诊断输出用——auth whoami 展示目标地址) */
530
533
  get url() {
531
534
  return this.baseUrl;
@@ -534,7 +537,9 @@ var SimingHttpClient = class {
534
537
  const token = resolveAuthToken();
535
538
  const headers = {
536
539
  ...token !== void 0 ? { Authorization: `Bearer ${token}` } : {},
537
- ...body !== void 0 ? { "Content-Type": "application/json" } : {}
540
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {},
541
+ // T202609150002:issue 域通道注入(服务端按 header 判定 source,缺省 web)
542
+ "x-siming-source": this.source
538
543
  };
539
544
  return fetch(`${this.baseUrl}${path}`, {
540
545
  method,
@@ -628,8 +633,8 @@ function resolveServerUrl(baseUrl) {
628
633
  url = url ?? readWorkspaceConfig()?.serverUrl ?? getLocalServerUrl();
629
634
  return url.replace(/\/$/, "");
630
635
  }
631
- function createClient(baseUrl) {
632
- return new SimingHttpClient(resolveServerUrl(baseUrl));
636
+ function createClient(baseUrl, source) {
637
+ return new SimingHttpClient(resolveServerUrl(baseUrl), source);
633
638
  }
634
639
 
635
640
  // src/commands/healthcheck.ts
@@ -673,10 +678,11 @@ import {
673
678
  async function handleCreateTask(client, input) {
674
679
  return client.postJson("/api/tasks", input);
675
680
  }
676
- async function handleAdvance(client, taskId, note, summary) {
681
+ async function handleAdvance(client, taskId, note, summary, finalize) {
677
682
  return client.postJson(`/api/tasks/${taskId}/advance`, {
678
683
  ...note !== void 0 ? { note } : {},
679
- ...summary !== void 0 ? { summary } : {}
684
+ ...summary !== void 0 ? { summary } : {},
685
+ ...finalize ? { finalize: true } : {}
680
686
  });
681
687
  }
682
688
  async function handleApprove(client, taskId, decision, comment) {
@@ -694,6 +700,9 @@ async function handleResume(client, taskId, decision) {
694
700
  async function handleCancel(client, taskId, reason) {
695
701
  return client.postJson(`/api/tasks/${taskId}/cancel`, reason !== void 0 ? { reason } : {});
696
702
  }
703
+ async function handleReopen(client, taskId, reason) {
704
+ return client.postJson(`/api/tasks/${taskId}/reopen`, { reason });
705
+ }
697
706
  async function handleListTasks(client, filters) {
698
707
  const qs = new URLSearchParams();
699
708
  if (filters?.status) qs.set("status", filters.status);
@@ -1250,6 +1259,94 @@ function readStdinLine(stream) {
1250
1259
  });
1251
1260
  }
1252
1261
 
1262
+ // src/client/handlers/issue.ts
1263
+ async function handleCreateIssue(client, input) {
1264
+ return client.postJson("/api/issues", input);
1265
+ }
1266
+ async function handleListIssues(client, filters) {
1267
+ const params = new URLSearchParams();
1268
+ if (filters.status) params.set("status", filters.status);
1269
+ if (filters.source) params.set("source", filters.source);
1270
+ if (filters.projectId) params.set("projectId", filters.projectId);
1271
+ if (filters.taskId) params.set("taskId", filters.taskId);
1272
+ if (filters.q) params.set("q", filters.q);
1273
+ if (filters.view) params.set("view", filters.view);
1274
+ if (filters.page !== void 0) params.set("page", String(filters.page));
1275
+ if (filters.limit !== void 0) params.set("limit", String(filters.limit));
1276
+ const qs = params.toString();
1277
+ return client.getJson(`/api/issues${qs ? `?${qs}` : ""}`);
1278
+ }
1279
+ async function handleGetIssue(client, issueId) {
1280
+ return client.getJson(`/api/issues/${issueId}`);
1281
+ }
1282
+ async function handleUpdateIssue(client, issueId, patch) {
1283
+ return client.patchJson(`/api/issues/${issueId}`, patch);
1284
+ }
1285
+ async function handleCancelIssue(client, issueId, reason) {
1286
+ return client.postJson(`/api/issues/${issueId}/cancel`, reason !== void 0 ? { reason } : {});
1287
+ }
1288
+ async function handleConvertIssues(client, input) {
1289
+ const { issueIds, task, ...rest } = input;
1290
+ return client.postJson("/api/issues/convert", {
1291
+ ...rest,
1292
+ groups: [{ issueIds, task }]
1293
+ });
1294
+ }
1295
+
1296
+ // src/client/handlers/project.ts
1297
+ async function handleListProjects(client, status) {
1298
+ return client.getJson(`/api/projects${status ? `?status=${encodeURIComponent(status)}` : ""}`);
1299
+ }
1300
+ async function handleGetProject(client, id) {
1301
+ return client.getJson(`/api/projects/${encodeURIComponent(id)}`);
1302
+ }
1303
+ async function handleCreateProject(client, input) {
1304
+ return client.postJson("/api/projects", input);
1305
+ }
1306
+ async function handleUpdateProject(client, id, patch) {
1307
+ return client.putJson(`/api/projects/${encodeURIComponent(id)}`, patch);
1308
+ }
1309
+ var OBJECT_ID_HEX = /^[a-f0-9]{24}$/;
1310
+ async function resolveProjectId(client, input) {
1311
+ if (OBJECT_ID_HEX.test(input)) return { success: true, data: input };
1312
+ const list = await handleListProjects(client);
1313
+ if (!list.success) return list;
1314
+ const hit = list.data.find((p) => p.key === input);
1315
+ if (!hit) {
1316
+ const error = {
1317
+ message: `project "${input}" not found`,
1318
+ httpStatus: 404,
1319
+ bizCode: "PROJECT_NOT_FOUND",
1320
+ hint: `check \`siming project list\` for available keys; pass --project <key|id>`
1321
+ };
1322
+ return fail(error);
1323
+ }
1324
+ if (!hit.id) {
1325
+ const error = {
1326
+ message: `project "${input}" has no id`,
1327
+ httpStatus: 500,
1328
+ hint: `server returned a project without id; check \`siming project list\``
1329
+ };
1330
+ return fail(error);
1331
+ }
1332
+ return { success: true, data: hit.id };
1333
+ }
1334
+ async function resolveProjectKey(client, input) {
1335
+ const list = await handleListProjects(client);
1336
+ if (!list.success) return list;
1337
+ const hit = list.data.find((p) => p.key === input || p.id === input);
1338
+ if (!hit) {
1339
+ const error = {
1340
+ message: `project "${input}" not found`,
1341
+ httpStatus: 404,
1342
+ bizCode: "PROJECT_NOT_FOUND",
1343
+ hint: `check \`siming project list\` for available keys; pass --project <key|id>`
1344
+ };
1345
+ return fail(error);
1346
+ }
1347
+ return { success: true, data: hit.key };
1348
+ }
1349
+
1253
1350
  // src/mcp/tools.ts
1254
1351
  import { z as z3 } from "zod";
1255
1352
  import { TASK_TYPES, ARTIFACT_TYPES as ARTIFACT_TYPES2 } from "@siming-org/core";
@@ -1258,7 +1355,7 @@ function hostNeutralEnum(schema) {
1258
1355
  }
1259
1356
  var taskArgs = {
1260
1357
  action: z3.enum([
1261
- // 任务生命周期(9 机械映射 + query 例外:task list/show 合一)
1358
+ // 任务生命周期(10 机械映射 + query 例外:task list/show 合一)
1262
1359
  "create",
1263
1360
  "query",
1264
1361
  "context",
@@ -1268,6 +1365,7 @@ var taskArgs = {
1268
1365
  "pause",
1269
1366
  "resume",
1270
1367
  "cancel",
1368
+ "reopen",
1271
1369
  "history",
1272
1370
  // 实例(例外:映射自 dag instance)
1273
1371
  "instance",
@@ -1304,9 +1402,11 @@ var taskArgs = {
1304
1402
  // context 只认 basic|full,per-action 校验归 mcp/server.ts;非法组合报失败三要素)
1305
1403
  view: hostNeutralEnum(z3.enum(["prompt", "skills", "basic", "full"])).optional(),
1306
1404
  nodeId: z3.string().optional(),
1307
- // advance / record-set(note 写 history;summary 写 nodeRecords,两位独立)
1405
+ // advance / record-set(note 写 history;summary 写 nodeRecords,两位独立);
1406
+ // finalize 为 advance 收尾确认(终节点终态化必需,中间节点无副作用——T202609130009)
1308
1407
  note: z3.string().optional(),
1309
1408
  summary: z3.string().optional(),
1409
+ finalize: z3.boolean().optional(),
1310
1410
  // approve
1311
1411
  decision: hostNeutralEnum(z3.enum(["approved", "rejected"])).optional(),
1312
1412
  comment: z3.string().optional(),
@@ -1356,6 +1456,23 @@ var templateArgs = {
1356
1456
  var authArgs = {
1357
1457
  action: z3.enum(["whoami"])
1358
1458
  };
1459
+ var issueArgs = {
1460
+ action: z3.enum(["create", "query"]),
1461
+ /** 存在时只解释当前 action 列入 mask 的可选字段(模式对齐 taskArgs/applyFieldMask) */
1462
+ fieldMask: z3.array(z3.string()).optional(),
1463
+ // create
1464
+ title: z3.string().optional(),
1465
+ description: z3.string().optional(),
1466
+ // 公共:create 缺省按工作区 defaultProject,无配置时按 key=default / query 过滤(24-hex)
1467
+ projectId: z3.string().optional(),
1468
+ // query:view 缺省 open(有效未跟进完);与 REST 词汇统一(status/taskId 指定时切对应过滤)
1469
+ view: hostNeutralEnum(z3.enum(["open", "all"])).optional(),
1470
+ status: z3.string().optional(),
1471
+ /** query:任务反查(双向追溯 MCP 侧入口) */
1472
+ taskId: z3.string().optional(),
1473
+ page: z3.number().int().optional(),
1474
+ limit: z3.number().int().optional()
1475
+ };
1359
1476
 
1360
1477
  // src/mcp/server.ts
1361
1478
  function mcpContent(result) {
@@ -1453,11 +1570,12 @@ var TASK_FIELD_SPECS = {
1453
1570
  query: { required: [], optional: ["taskId", "status", "track", "projectId", "page", "limit"] },
1454
1571
  context: { required: ["taskId"], optional: ["view"] },
1455
1572
  node: { required: ["taskId", "view"], optional: ["nodeId"] },
1456
- advance: { required: ["taskId"], optional: ["note", "summary"] },
1573
+ advance: { required: ["taskId"], optional: ["note", "summary", "finalize"] },
1457
1574
  approve: { required: ["taskId", "decision"], optional: ["comment"] },
1458
1575
  pause: { required: ["taskId"], optional: ["reason"] },
1459
1576
  resume: { required: ["taskId"], optional: ["resumeNote"] },
1460
1577
  cancel: { required: ["taskId"], optional: ["reason"] },
1578
+ reopen: { required: ["taskId", "reason"], optional: [] },
1461
1579
  history: { required: ["taskId"], optional: [] },
1462
1580
  instance: { required: ["taskId"], optional: [] },
1463
1581
  "record-set": { required: ["taskId", "node", "summary"], optional: [] },
@@ -1481,9 +1599,13 @@ var TASK_FIELD_SPECS = {
1481
1599
  "doc-non-goal-add": { required: ["taskId"], optional: ["text", "textItems"] },
1482
1600
  "archnote-add": { required: ["taskId"], optional: ["text", "textItems"] }
1483
1601
  };
1602
+ var ISSUE_FIELD_SPECS = {
1603
+ create: { required: ["title"], optional: ["description", "projectId"] },
1604
+ query: { required: [], optional: ["view", "status", "taskId", "projectId", "page", "limit"] }
1605
+ };
1484
1606
  function createMcpServer() {
1485
1607
  const server = new McpServer({ name: "siming", version: VERSION });
1486
- const client = createClient();
1608
+ const client = createClient(void 0, "mcp");
1487
1609
  server.tool(
1488
1610
  "siming_task",
1489
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",
@@ -1566,7 +1688,7 @@ function createMcpServer() {
1566
1688
  case "advance": {
1567
1689
  const { taskId } = args;
1568
1690
  if (taskId === void 0) return requireError("advance", ["taskId"], args);
1569
- return mcpContent(await handleAdvance(client, taskId, args.note, args.summary));
1691
+ return mcpContent(await handleAdvance(client, taskId, args.note, args.summary, args.finalize === true));
1570
1692
  }
1571
1693
  case "approve": {
1572
1694
  const { taskId, decision } = args;
@@ -1590,6 +1712,13 @@ function createMcpServer() {
1590
1712
  if (taskId === void 0) return requireError("cancel", ["taskId"], args);
1591
1713
  return mcpContent(await handleCancel(client, taskId, args.reason));
1592
1714
  }
1715
+ case "reopen": {
1716
+ const { taskId, reason } = args;
1717
+ if (taskId === void 0 || reason === void 0) {
1718
+ return requireError("reopen", ["taskId", "reason"], args);
1719
+ }
1720
+ return mcpContent(await handleReopen(client, taskId, reason));
1721
+ }
1593
1722
  case "history": {
1594
1723
  const { taskId } = args;
1595
1724
  if (taskId === void 0) return requireError("history", ["taskId"], args);
@@ -1830,6 +1959,70 @@ function createMcpServer() {
1830
1959
  }
1831
1960
  }
1832
1961
  );
1962
+ server.tool(
1963
+ "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`,
1965
+ issueArgs,
1966
+ async (rawArgs) => {
1967
+ const masked = applyFieldMask(rawArgs, ISSUE_FIELD_SPECS);
1968
+ if (!masked.success) return masked.response;
1969
+ const args = masked.args;
1970
+ switch (args.action) {
1971
+ 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
+ }
1996
+ }
1997
+ return mcpContent(
1998
+ await handleCreateIssue(client, {
1999
+ title,
2000
+ ...args.description !== void 0 ? { description: args.description } : {},
2001
+ ...projectId !== void 0 ? { projectId } : {}
2002
+ })
2003
+ );
2004
+ }
2005
+ case "query": {
2006
+ if (args.page !== void 0 && args.page < 1) {
2007
+ return localError("action=query \u7684 page \u5FC5\u987B\u4E3A\u5927\u4E8E\u7B49\u4E8E 1 \u7684\u6574\u6570");
2008
+ }
2009
+ if (args.limit !== void 0 && (args.limit < 1 || args.limit > 500)) {
2010
+ return localError("action=query \u7684 limit \u5FC5\u987B\u4E3A 1-500 \u7684\u6574\u6570");
2011
+ }
2012
+ return mcpContent(
2013
+ await handleListIssues(client, {
2014
+ view: args.view ?? "open",
2015
+ ...args.status !== void 0 ? { status: args.status } : {},
2016
+ ...args.taskId !== void 0 ? { taskId: args.taskId } : {},
2017
+ ...args.projectId !== void 0 ? { projectId: args.projectId } : {},
2018
+ ...args.page !== void 0 ? { page: args.page } : {},
2019
+ ...args.limit !== void 0 ? { limit: args.limit } : {}
2020
+ })
2021
+ );
2022
+ }
2023
+ }
2024
+ }
2025
+ );
1833
2026
  server.tool(
1834
2027
  "siming_auth",
1835
2028
  `\u8EAB\u4EFD\u67E5\u8BE2\uFF08\u6743\u9650\u8FB9\u754C\u81EA\u68C0\uFF09\u3002action\uFF1Awhoami=\u8FD4\u56DE\u5F53\u524D\u8EAB\u4EFD\u56DB\u5143\u7EC4\u2014\u2014identity\uFF08admin=\u7BA1\u7406\u5458\u7EA7\u5168\u9879\u76EE\u5168\u5185\u5BB9 / project=\u9879\u76EE\u7EA7\u4EC5\u7ED1\u5B9A\u9879\u76EE\u8BFB\u5199+\u5168\u5C40\u8D44\u4EA7\u53EA\u8BFB / none=\u672A\u914D\u7F6E\u51ED\u8BC1\u6216\u76EE\u6807 server \u9274\u6743\u672A\u5F00\u542F\uFF09+ project\uFF08\u4EC5 project \u8EAB\u4EFD\uFF1A\u7ED1\u5B9A\u9879\u76EE id/key/name\uFF09+ authEnabled\uFF08\u76EE\u6807 server \u9274\u6743\u5F00\u5173\uFF09+ server.url\u3002\u9644\u5E26\u672C\u5730\u51ED\u8BC1\u8BCA\u65AD\uFF1AlocalToken\uFF08\u751F\u6548 Token \u7EA7\u522B\uFF1Aadmin/project/none\uFF09+ localTokenSource\uFF08\u751F\u6548\u6765\u6E90\u5C42\uFF1Aworkspace=\u5DE5\u4F5C\u533A .siming/dev.json / global=\u7528\u6237\u7EA7\u5168\u5C40 ~/.siming/auth.json / env=SIMING_TOKEN \u73AF\u5883\u53D8\u91CF / none=\u672A\u914D\u7F6E\uFF1B\u4F18\u5148\u7EA7 workspace > global > env\uFF09\u3002\u7528\u4E8E AI \u5224\u65AD\u81EA\u8EAB\u6743\u9650\u8FB9\u754C\uFF1Aproject \u8EAB\u4EFD\u4E0D\u5F97\u64CD\u4F5C\u5176\u4ED6\u9879\u76EE\u4E0E\u5168\u5C40\u8D44\u4EA7\u5199\u3001admin \u8EAB\u4EFD\u5168\u6743\u3001none \u4E14 authEnabled=true \u65F6\u4E00\u5207\u6570\u636E\u64CD\u4F5C\u5C06 401\u3002\u672C\u5DE5\u5177\u4E0D\u8FD4\u56DE Token \u672C\u8EAB\uFF1BToken \u7531\u7528\u6237\u914D\u7F6E\uFF08\u4F18\u5148 siming auth token set --global \u672C\u673A\u4E00\u6B21\u5168\u5DE5\u4F5C\u533A/\u5BBF\u4E3B\u5F62\u6001\u590D\u7528\uFF0C\u6216\u5DE5\u4F5C\u533A siming auth token set\uFF09\uFF0C\u5BA2\u6237\u7AEF\u81EA\u52A8\u643A\u5E26\u3002\u4FEE\u590D\u6307\u5F15\uFF1AWeb \u7BA1\u7406\u53F0\u751F\u6210 Token \u540E\u7531\u7528\u6237\u6267\u884C\u4E0A\u8FF0\u547D\u4EE4\u914D\u7F6E\u3002`,
@@ -1864,60 +2057,6 @@ import {
1864
2057
  ContextViewSchema
1865
2058
  } from "@siming-org/core";
1866
2059
 
1867
- // src/client/handlers/project.ts
1868
- async function handleListProjects(client, status) {
1869
- return client.getJson(`/api/projects${status ? `?status=${encodeURIComponent(status)}` : ""}`);
1870
- }
1871
- async function handleGetProject(client, id) {
1872
- return client.getJson(`/api/projects/${encodeURIComponent(id)}`);
1873
- }
1874
- async function handleCreateProject(client, input) {
1875
- return client.postJson("/api/projects", input);
1876
- }
1877
- async function handleUpdateProject(client, id, patch) {
1878
- return client.putJson(`/api/projects/${encodeURIComponent(id)}`, patch);
1879
- }
1880
- var OBJECT_ID_HEX = /^[a-f0-9]{24}$/;
1881
- async function resolveProjectId(client, input) {
1882
- if (OBJECT_ID_HEX.test(input)) return { success: true, data: input };
1883
- const list = await handleListProjects(client);
1884
- if (!list.success) return list;
1885
- const hit = list.data.find((p) => p.key === input);
1886
- if (!hit) {
1887
- const error = {
1888
- message: `project "${input}" not found`,
1889
- httpStatus: 404,
1890
- bizCode: "PROJECT_NOT_FOUND",
1891
- hint: `check \`siming project list\` for available keys; pass --project <key|id>`
1892
- };
1893
- return fail(error);
1894
- }
1895
- if (!hit.id) {
1896
- const error = {
1897
- message: `project "${input}" has no id`,
1898
- httpStatus: 500,
1899
- hint: `server returned a project without id; check \`siming project list\``
1900
- };
1901
- return fail(error);
1902
- }
1903
- return { success: true, data: hit.id };
1904
- }
1905
- async function resolveProjectKey(client, input) {
1906
- const list = await handleListProjects(client);
1907
- if (!list.success) return list;
1908
- const hit = list.data.find((p) => p.key === input || p.id === input);
1909
- if (!hit) {
1910
- const error = {
1911
- message: `project "${input}" not found`,
1912
- httpStatus: 404,
1913
- bizCode: "PROJECT_NOT_FOUND",
1914
- hint: `check \`siming project list\` for available keys; pass --project <key|id>`
1915
- };
1916
- return fail(error);
1917
- }
1918
- return { success: true, data: hit.key };
1919
- }
1920
-
1921
2060
  // src/output/json.ts
1922
2061
  function renderJson(value) {
1923
2062
  console.log(JSON.stringify(value, null, 2));
@@ -2096,7 +2235,7 @@ function parseRefFlags(refs, mainDocName) {
2096
2235
  var PAGE_DEFAULT = 20;
2097
2236
  var PAGE_MAX = 500;
2098
2237
  function createTaskCommand() {
2099
- const task = new Command2("task").description("Task management \u2014 create, advance, approve, pause, resume, cancel, query");
2238
+ const task = new Command2("task").description("Task management \u2014 create, advance, approve, pause, resume, cancel, reopen, query");
2100
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) => {
2101
2240
  const client = createClient(opts.url);
2102
2241
  const filters = {};
@@ -2229,9 +2368,9 @@ function createTaskCommand() {
2229
2368
  }
2230
2369
  renderResult("json", result);
2231
2370
  });
2232
- task.command("advance <taskId>").description("Advance a task to the next node (record contents already written via `task record/doc`; this is the light finish)").option("--url <url>", "server base URL").option("--format <fmt>", "output format", "json").option("--note <text>", "advance note recorded in history").option("--summary <text>", "finishing summary written to nodeRecords.<node>.summary (equivalent to a final `record set --summary`)").action(async (taskId, opts) => {
2371
+ task.command("advance <taskId>").description("Advance a task to the next node (record contents already written via `task record/doc`; this is the light finish). At the final node the advance is rejected unless --finalize is passed (completion confirmation)").option("--url <url>", "server base URL").option("--format <fmt>", "output format", "json").option("--note <text>", "advance note recorded in history").option("--summary <text>", "finishing summary written to nodeRecords.<node>.summary (equivalent to a final `record set --summary`)").option("--finalize", "completion confirmation: finalize the task at its final node (required there; ignored mid-flow)").action(async (taskId, opts) => {
2233
2372
  const client = createClient(opts.url);
2234
- const result = await handleAdvance(client, taskId, opts.note, opts.summary);
2373
+ const result = await handleAdvance(client, taskId, opts.note, opts.summary, opts.finalize === true);
2235
2374
  renderResult(opts.format, result);
2236
2375
  });
2237
2376
  task.command("approve <taskId>").description("Approve or reject a task paused at a human_approval pause point").option("--url <url>", "server base URL").option("--format <fmt>", "output format", "json").requiredOption("--decision <d>", "decision: approved|rejected").option("--comment <text>", "decision comment recorded in history").action(async (taskId, opts) => {
@@ -2339,11 +2478,16 @@ function createTaskCommand() {
2339
2478
  const result = await handleResume(client, taskId, opts.decision);
2340
2479
  renderResult("json", result);
2341
2480
  });
2342
- task.command("cancel <taskId>").description("Cancel a task (active/paused \u2192 cancelled terminal state, irreversible)").option("--url <url>", "server base URL").option("--reason <text>", "cancel reason (1-500 chars)").action(async (taskId, opts) => {
2481
+ task.command("cancel <taskId>").description("Cancel a task (active/paused \u2192 cancelled terminal state; can be reopened)").option("--url <url>", "server base URL").option("--reason <text>", "cancel reason (1-500 chars)").action(async (taskId, opts) => {
2343
2482
  const client = createClient(opts.url);
2344
2483
  const result = await handleCancel(client, taskId, opts.reason);
2345
2484
  renderResult("json", result);
2346
2485
  });
2486
+ task.command("reopen <taskId>").description("Reopen a terminated task (completed/cancelled \u2192 restore pre-termination state; all records preserved). At a pause-point pause the task returns to pending-approval").option("--url <url>", "server base URL").requiredOption("--reason <text>", "reopen reason (1-500 chars, recorded in history)").action(async (taskId, opts) => {
2487
+ const client = createClient(opts.url);
2488
+ const result = await handleReopen(client, taskId, opts.reason);
2489
+ renderResult("json", result);
2490
+ });
2347
2491
  task.command("history <taskId>").description("Show task node history").option("--url <url>", "server base URL").option("--format <fmt>", "output format", "table").action(async (taskId, opts) => {
2348
2492
  const client = createClient(opts.url);
2349
2493
  const result = await handleGetHistory(client, taskId);
@@ -6646,6 +6790,7 @@ function permissionsToMatrix(permissions) {
6646
6790
  var AGENT_INFO_BOUNDARY = "\u4EFB\u52A1\u4F9D\u636E\u9650\u4E8E\u9879\u76EE\u4EE3\u7801\u3001\u8BBE\u8BA1\u6587\u6863/\u6267\u884C\u62A5\u544A\u53CA\u4EFB\u52A1\u4E0A\u4E0B\u6587\uFF08\u542B\u4EFB\u52A1\u4FE1\u606F\u3001\u63D0\u793A\u8BCD\u3001\u59D4\u6D3E\u6750\u6599\uFF09\uFF1B\u7981\u6B62\u81EA\u884C\u8BFB\u53D6\u65E5\u5FD7\u3001\u4E34\u65F6\u6587\u4EF6\u7B49\u8FC7\u7A0B\u4EA7\u7269\uFF0C\u7981\u6B62\u81EA\u884C\u6267\u884C\u6D4B\u8BD5\u3001\u547D\u4EE4\u6216\u5176\u4ED6\u64CD\u4F5C\uFF0C\u59D4\u6D3E\u4EFB\u52A1\u660E\u786E\u8981\u6C42\u65F6\u9664\u5916\u3002";
6647
6791
  function agentToMarkdown(agent, realModel) {
6648
6792
  const fm = {
6793
+ name: agent.name,
6649
6794
  description: agent.description,
6650
6795
  mode: "subagent",
6651
6796
  model: realModel,
@@ -7119,7 +7264,7 @@ function buildInstallGuidance(scope, targets) {
7119
7264
  if (targets.mcpConfigPath) {
7120
7265
  lines.push(` - MCP \u6CE8\u518C: ${targets.mcpConfigPath}\uFF08\u9876\u5C42 mcpServers.siming \u2192 stdio \u547D\u4EE4 "siming mcp-serve"\uFF09`);
7121
7266
  }
7122
- lines.push("[2] \u5F15\u7528\u65B9\u5F0F\uFF1Askill = \u5B50\u76EE\u5F55\u5185 SKILL.md\uFF08frontmatter name/description\uFF09\uFF1Bagent = \u5B50\u76EE\u5F55\u5185 agent.md\uFF08frontmatter description/model/skills\uFF0C\u6B63\u6587\u542B\u7ED1\u5B9A Skill \u52A0\u8F7D\u6307\u4EE4\uFF09\uFF1B\u5E26\u5F15\u7528\u6587\u6863\u7684\u8D44\u4EA7\uFF0C\u4E3B\u6587\u6863\u4E2D\u7684\u76F8\u5BF9\u8DEF\u5F84\u5F15\u7528\u5728\u5B50\u76EE\u5F55\u5185\u76F4\u63A5\u53EF\u8FBE\u3002");
7267
+ lines.push("[2] \u5F15\u7528\u65B9\u5F0F\uFF1Askill = \u5B50\u76EE\u5F55\u5185 SKILL.md\uFF08frontmatter name/description\uFF09\uFF1Bagent = \u5B50\u76EE\u5F55\u5185 agent.md\uFF08frontmatter name/description/model/skills\uFF0C\u6B63\u6587\u542B\u7ED1\u5B9A Skill \u52A0\u8F7D\u6307\u4EE4\uFF09\uFF1B\u5E26\u5F15\u7528\u6587\u6863\u7684\u8D44\u4EA7\uFF0C\u4E3B\u6587\u6863\u4E2D\u7684\u76F8\u5BF9\u8DEF\u5F84\u5F15\u7528\u5728\u5B50\u76EE\u5F55\u5185\u76F4\u63A5\u53EF\u8FBE\u3002");
7123
7268
  const mcpPart = targets.mcpConfigPath ? "\uFF1B\u628A mcpServers.siming \u6761\u76EE\u5408\u5E76\u8FDB\u672C\u5DE5\u5177\u7684 MCP \u914D\u7F6E" : "\uFF08\u9879\u76EE\u7EA7\u5B89\u88C5\u4E0D\u542B MCP \u6CE8\u518C\uFF0C\u9700\u8981\u65F6\u53E6\u884C\u6267\u884C\u7528\u6237\u7EA7 install\uFF09";
7124
7269
  lines.push(`[3] \u6CE8\u518C\u9014\u5F84\uFF08\u5F53\u524D\u7F16\u7A0B\u5DE5\u5177\u4F1A\u8BDD\u4E2D\u7684 AI \u53EF\u76F4\u63A5\u6267\u884C\uFF09\uFF1A\u628A skills \u5B50\u76EE\u5F55\u52A0\u5165\u672C\u5DE5\u5177\u7684 skill \u641C\u7D22\u8DEF\u5F84\uFF1B\u628A agent \u5B50\u76EE\u5F55\u6CE8\u518C\u8FDB\u672C\u5DE5\u5177\u7684 subagent \u914D\u7F6E${mcpPart}\u3002`);
7125
7270
  if (targets.mcpConfigPath) {
@@ -8332,10 +8477,259 @@ function reportPortConflict(port) {
8332
8477
  );
8333
8478
  }
8334
8479
 
8335
- // src/commands/auth/command.ts
8480
+ // src/commands/issue/command.ts
8336
8481
  import { Command as Command18 } from "commander";
8482
+ import { z as z7 } from "zod";
8483
+ import { TASK_TYPES as TASK_TYPES3 } from "@siming-org/core";
8484
+ var PAGE_DEFAULT2 = 20;
8485
+ var PAGE_MAX2 = 500;
8486
+ function renderProgress(row) {
8487
+ if (row.progress.total === 0) return "\u2014";
8488
+ return `${String(row.progress.done)}/${String(row.progress.cancelled)}/${String(row.progress.total)}`;
8489
+ }
8490
+ function parseDeclarationsMap(spec) {
8491
+ const out = {};
8492
+ for (const part of spec.split(",")) {
8493
+ const trimmed = part.trim();
8494
+ if (trimmed.length === 0) continue;
8495
+ const at = trimmed.indexOf(":");
8496
+ if (at <= 0 || at === trimmed.length - 1) {
8497
+ return { error: `--declarations \u6761\u76EE "${trimmed}" \u975E\u6CD5\uFF08\u683C\u5F0F id1:all,id2:partial\uFF09` };
8498
+ }
8499
+ const issueId = trimmed.slice(0, at);
8500
+ const mode = trimmed.slice(at + 1);
8501
+ if (mode !== "all" && mode !== "partial") {
8502
+ return { error: `--declarations \u58F0\u660E\u503C "${mode}" \u975E\u6CD5\uFF08\u5408\u6CD5\u503C\uFF1Aall|partial\uFF09` };
8503
+ }
8504
+ out[issueId] = mode;
8505
+ }
8506
+ return out;
8507
+ }
8508
+ function createIssueCommand() {
8509
+ const issue = new Command18("issue").description("Issue pool: record \u2192 organize \u2192 convert to tasks");
8510
+ 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) => {
8511
+ const client = createClient(opts.url);
8512
+ const title = opts.title ?? titleArg;
8513
+ if (title === void 0 || title.length === 0) {
8514
+ renderResult("json", {
8515
+ success: false,
8516
+ error: { message: "issue add \u9700\u8981\u6807\u9898\uFF08\u4F4D\u7F6E\u53C2\u6570\u6216 --title\uFF09", httpStatus: 0 }
8517
+ });
8518
+ return;
8519
+ }
8520
+ let projectId;
8521
+ if (opts.project) {
8522
+ const r = await resolveProjectId(client, opts.project);
8523
+ if (!r.success) {
8524
+ renderResult("json", r);
8525
+ return;
8526
+ }
8527
+ projectId = r.data;
8528
+ } else {
8529
+ const wsDefaultProject = readWorkspaceConfig()?.defaultProject;
8530
+ if (wsDefaultProject) {
8531
+ const r = await resolveProjectId(client, wsDefaultProject);
8532
+ if (r.success) projectId = r.data;
8533
+ }
8534
+ }
8535
+ const result = await handleCreateIssue(client, {
8536
+ title,
8537
+ ...opts.description !== void 0 ? { description: opts.description } : {},
8538
+ ...projectId !== void 0 ? { projectId } : {}
8539
+ });
8540
+ renderResult(opts.format ?? "fancy", result);
8541
+ });
8542
+ 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) => {
8543
+ const client = createClient(opts.url);
8544
+ const filters = {};
8545
+ if (opts.status) filters.status = opts.status;
8546
+ if (opts.source) filters.source = opts.source;
8547
+ if (opts.task) filters.taskId = opts.task;
8548
+ if (opts.q) filters.q = opts.q;
8549
+ if (opts.view) {
8550
+ const r = parseEnum(opts.view, z7.enum(["all", "open"]), "view");
8551
+ if (!r.success) {
8552
+ renderResult(opts.format ?? "table", r);
8553
+ return;
8554
+ }
8555
+ filters.view = r.data;
8556
+ }
8557
+ if (opts.project) {
8558
+ const r = await resolveProjectId(client, opts.project);
8559
+ if (!r.success) {
8560
+ renderResult(opts.format ?? "table", r);
8561
+ return;
8562
+ }
8563
+ filters.projectId = r.data;
8564
+ }
8565
+ if (opts.all) {
8566
+ filters.limit = PAGE_MAX2;
8567
+ } else {
8568
+ const pageR = parseEnum(opts.page ?? "1", z7.coerce.number().int().min(1), "page");
8569
+ if (!pageR.success) {
8570
+ renderResult(opts.format ?? "table", pageR);
8571
+ return;
8572
+ }
8573
+ filters.page = pageR.data;
8574
+ const limitR = parseEnum(opts.limit ?? String(PAGE_DEFAULT2), z7.coerce.number().int().min(1).max(PAGE_MAX2), "limit");
8575
+ if (!limitR.success) {
8576
+ renderResult(opts.format ?? "table", limitR);
8577
+ return;
8578
+ }
8579
+ filters.limit = limitR.data;
8580
+ }
8581
+ const result = await handleListIssues(client, filters);
8582
+ if (!result.success) {
8583
+ renderResult(opts.format ?? "table", result);
8584
+ return;
8585
+ }
8586
+ if (opts.format === "json") {
8587
+ renderResult("json", result, {});
8588
+ } else {
8589
+ renderResult(opts.format ?? "table", { success: true, data: result.data.items }, {
8590
+ columns: [
8591
+ { key: "issueId", header: "Issue ID" },
8592
+ { key: "title", header: "Title", maxWidth: 32 },
8593
+ { key: "status", header: "Status" },
8594
+ { key: "source", header: "Source" },
8595
+ {
8596
+ key: "taskLinks",
8597
+ header: "Tasks",
8598
+ render: (row) => String(row.taskLinks.length)
8599
+ },
8600
+ {
8601
+ key: "progress",
8602
+ header: "Progress (done/cancelled/total)",
8603
+ render: (row) => renderProgress(row)
8604
+ },
8605
+ {
8606
+ key: "updatedAt",
8607
+ header: "Updated",
8608
+ render: (row) => {
8609
+ const at = row.updatedAt;
8610
+ return at === void 0 ? "\u2014" : new Date(at).toLocaleString();
8611
+ }
8612
+ }
8613
+ ]
8614
+ });
8615
+ console.log(`total: ${String(result.data.total)}\uFF08page ${String(result.data.page)}, limit ${String(result.data.limit)}\uFF09`);
8616
+ }
8617
+ });
8618
+ withCommonOpts(issue.command("show"), "fancy").argument("<issueId>", "issue id (I-prefixed)").description("Show issue detail (progress + linked task snapshots + history)").action(async (issueId, opts) => {
8619
+ const client = createClient(opts.url);
8620
+ const result = await handleGetIssue(client, issueId);
8621
+ renderResult(opts.format ?? "fancy", result);
8622
+ });
8623
+ withCommonOpts(issue.command("update"), "fancy").argument("<issueId>", "issue id").option("--title <t>", "new title").option("--description <d>", "new description").description("Update issue title/description (allowed in any status)").action(async (issueId, opts) => {
8624
+ const client = createClient(opts.url);
8625
+ if (opts.title === void 0 && opts.description === void 0) {
8626
+ renderResult("json", {
8627
+ success: false,
8628
+ error: { message: "issue update \u9700\u8981\u81F3\u5C11\u4E00\u4E2A\u53EF\u66F4\u65B0\u5B57\u6BB5\uFF08--title / --description\uFF09", httpStatus: 0 }
8629
+ });
8630
+ return;
8631
+ }
8632
+ const result = await handleUpdateIssue(client, issueId, {
8633
+ ...opts.title !== void 0 ? { title: opts.title } : {},
8634
+ ...opts.description !== void 0 ? { description: opts.description } : {}
8635
+ });
8636
+ renderResult(opts.format ?? "fancy", result);
8637
+ });
8638
+ withCommonOpts(issue.command("cancel"), "fancy").argument("<issueId>", "issue id").option("--reason <r>", "cancel reason (optional, \u2264500 chars)").description("Cancel an issue (terminal state; active links recorded in history)").action(async (issueId, opts) => {
8639
+ const client = createClient(opts.url);
8640
+ const result = await handleCancelIssue(client, issueId, opts.reason);
8641
+ renderResult(opts.format ?? "fancy", result);
8642
+ });
8643
+ 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) => {
8644
+ const client = createClient(opts.url);
8645
+ const issueIds = [
8646
+ ...new Set((opts.issueIds ?? []).flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0))
8647
+ ];
8648
+ if (issueIds.length === 0) {
8649
+ renderResult("json", {
8650
+ success: false,
8651
+ error: { message: "issue convert \u9700\u8981 --issue-ids\uFF08\u9017\u53F7\u5206\u9694\u7684\u95EE\u9898 id \u6E05\u5355\uFF09", httpStatus: 0 }
8652
+ });
8653
+ return;
8654
+ }
8655
+ let declarations = {};
8656
+ if (opts.declarations) {
8657
+ const parsed = parseDeclarationsMap(opts.declarations);
8658
+ if ("error" in parsed) {
8659
+ renderResult("json", { success: false, error: { message: parsed.error, httpStatus: 0 } });
8660
+ return;
8661
+ }
8662
+ declarations = parsed;
8663
+ } else if (opts.declaration) {
8664
+ const r = parseEnum(opts.declaration, z7.enum(["all", "partial"]), "declaration");
8665
+ if (!r.success) {
8666
+ renderResult(opts.format ?? "fancy", r);
8667
+ return;
8668
+ }
8669
+ declarations = Object.fromEntries(issueIds.map((id) => [id, r.data]));
8670
+ } else {
8671
+ declarations = Object.fromEntries(issueIds.map((id) => [id, "all"]));
8672
+ }
8673
+ let projectId;
8674
+ if (opts.project) {
8675
+ const r = await resolveProjectId(client, opts.project);
8676
+ if (!r.success) {
8677
+ renderResult(opts.format ?? "fancy", r);
8678
+ return;
8679
+ }
8680
+ projectId = r.data;
8681
+ } else {
8682
+ const first = await handleGetIssue(client, issueIds[0]);
8683
+ if (!first.success) {
8684
+ renderResult(opts.format ?? "fancy", first);
8685
+ return;
8686
+ }
8687
+ projectId = first.data.projectId;
8688
+ }
8689
+ let type;
8690
+ if (opts.type !== void 0) {
8691
+ const r = parseEnum(opts.type, z7.enum(TASK_TYPES3), "type");
8692
+ if (!r.success) {
8693
+ renderResult(opts.format ?? "fancy", r);
8694
+ return;
8695
+ }
8696
+ type = r.data;
8697
+ }
8698
+ let skipNodes;
8699
+ if (opts.skip !== void 0 && opts.skip.length > 0) {
8700
+ skipNodes = [...new Set(opts.skip.flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0))];
8701
+ }
8702
+ let templateRef = opts.template;
8703
+ if (!/^[0-9a-f]{24}$/.test(templateRef)) {
8704
+ const listR = await handleListTemplates(client, projectId);
8705
+ if (!listR.success) {
8706
+ renderResult(opts.format ?? "fancy", listR);
8707
+ return;
8708
+ }
8709
+ const hit = listR.data.find((t) => t.code === templateRef) ?? listR.data.find((t) => t.name === templateRef);
8710
+ templateRef = hit?.id ?? templateRef;
8711
+ }
8712
+ const result = await handleConvertIssues(client, {
8713
+ projectId,
8714
+ declarations,
8715
+ issueIds,
8716
+ task: {
8717
+ title: opts.title,
8718
+ track: opts.track,
8719
+ dagTemplateId: templateRef,
8720
+ ...skipNodes !== void 0 && skipNodes.length > 0 ? { skipNodes } : {},
8721
+ ...type !== void 0 ? { type } : {}
8722
+ }
8723
+ });
8724
+ renderResult(opts.format ?? "fancy", result);
8725
+ });
8726
+ return issue;
8727
+ }
8728
+
8729
+ // src/commands/auth/command.ts
8730
+ import { Command as Command19 } from "commander";
8337
8731
  function createAuthCommand() {
8338
- const cmd = new Command18("auth").description("\u8EAB\u4EFD\u67E5\u8BE2\u4E0E\u672C\u5730 Token \u914D\u7F6E\uFF08AI \u51ED\u6B64\u5224\u65AD\u6743\u9650\u8FB9\u754C\uFF1BToken \u503C\u4E0D\u56DE\u663E\uFF09");
8732
+ const cmd = new Command19("auth").description("\u8EAB\u4EFD\u67E5\u8BE2\u4E0E\u672C\u5730 Token \u914D\u7F6E\uFF08AI \u51ED\u6B64\u5224\u65AD\u6743\u9650\u8FB9\u754C\uFF1BToken \u503C\u4E0D\u56DE\u663E\uFF09");
8339
8733
  withCommonOpts(
8340
8734
  cmd.command("whoami").description("\u67E5\u8BE2\u5F53\u524D\u8EAB\u4EFD\uFF08admin / project:<\u9879\u76EE> / \u672A\u914D\u7F6E\uFF09+ \u76EE\u6807 server \u9274\u6743\u5F00\u5173\u72B6\u6001"),
8341
8735
  "fancy"
@@ -8399,7 +8793,7 @@ function tokenSourceLabel(source) {
8399
8793
  }
8400
8794
 
8401
8795
  // src/index.ts
8402
- var program = new Command19();
8796
+ var program = new Command20();
8403
8797
  program.name("siming").description("\u53F8\u547D \xB7 Siming \u2014 AI coding workflow management platform").version(VERSION5);
8404
8798
  program.command("healthcheck").description("Check if the siming server is reachable").option("--url <url>", "server base URL").action(async (opts) => {
8405
8799
  await healthcheckCommand(opts);
@@ -8408,6 +8802,7 @@ program.command("mcp-serve").description("Start MCP server over stdio (for AI ag
8408
8802
  await mcpServeCommand();
8409
8803
  });
8410
8804
  program.addCommand(createTaskCommand());
8805
+ program.addCommand(createIssueCommand());
8411
8806
  program.addCommand(createSkillCommand());
8412
8807
  program.addCommand(createAgentCommand());
8413
8808
  program.addCommand(createDagCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siming-org/cli",
3
- "version": "0.6.5",
3
+ "version": "0.7.1",
4
4
  "license": "MIT",
5
5
  "author": "Wuchao",
6
6
  "engines": {
@@ -27,8 +27,8 @@
27
27
  "picocolors": "^1.1.1",
28
28
  "yaml": "^2.6.0",
29
29
  "zod": "^4.4.3",
30
- "@siming-org/core": "0.6.5",
31
- "@siming-org/server": "0.6.5"
30
+ "@siming-org/core": "0.7.1",
31
+ "@siming-org/server": "0.7.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsup": "^8.3.5",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "exportFormatVersion": "1.0",
3
- "exportedAt": "2026-09-14T09:32:04.255Z",
3
+ "exportedAt": "2026-09-15T08:13:01.500Z",
4
4
  "warnings": [],
5
5
  "template": {
6
6
  "name": "full_workflow",
@@ -138,7 +138,7 @@
138
138
  "label": "视觉验证",
139
139
  "phase": "track",
140
140
  "track": "ui",
141
- "prompt": "# 视觉验证\n\n## 节点职责\n\n运行当前界面并通过浏览器完成验收路径,验证功能结果、采集视觉证据,再根据独立 `visual-reviewer` 的评审结果完成修复和回归。本节点负责运行、操作、采证和流程收敛,不代替 Agent 判断视觉质量。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、验收标准、技术方案、前端实现记录,以及项目和所属模块的提示词。存在交互契约产物时,读取契约并将其自检清单与验收点作为验证清单的底座;无契约时以验收标准为底座。加载 `workflow-discipline`,调用独立 `visual-reviewer` 评审视觉证据。\n\n根据需求整理待验证页面、用户路径、需要呈现的状态和代表性视口。缺少运行方式、访问地址、认证或数据准备信息时,先从项目脚本、配置和现有环境中查找;仍无法运行当前实现时,说明缺失信息并暂停。\n\n## 执行流程\n\n1. 按项目声明启动当前实现及其依赖服务,确认页面对应本次代码。\n2. 使用浏览器自动化能力执行需求涉及的真实用户路径,验证操作后的页面结果、请求结果、反馈、错误处理和持久化后的重新加载结果。\n3. 检查浏览器控制台中的新增错误、失败请求和其他运行异常。\n4. 按验收范围触发适用的加载、空、错误、成功和关键交互状态,并在代表性视口采集截图、视频或等价原始证据;有交互契约时按契约条款逐项触发与核对,证据覆盖契约验收点;不在本节点预判视觉结论。\n5. 将需求、设计规范、场景说明、视口信息和原始证据交给 `visual-reviewer`;交互契约(如有)随需求与设计规范一并作为客观约束移交,不提供实现者结论或预期缺陷。\n6. 修复功能验证失败项以及 `visual-reviewer` 报告的 BLOCKER、HIGH Findings,然后重新执行受影响路径、采集同场景证据并再次评审,直至功能验证通过且视觉评审为 PASS。MEDIUM、LOW 逐项处理或记录保留理由。\n7. 清理本次创建的临时数据和运行资源,不删除非本次创建的数据。\n\n## 产出与检查\n\n推进前确认:\n\n- 需求涉及的用户路径已通过真实浏览器操作验证,并观察到最终结果;\n- 页面结果、请求、反馈、错误处理、持久化和控制台均无未解决的严重问题;\n- 验收所需状态与视口已呈现,原始证据对应当前实现且可追溯;\n- `visual-reviewer` 最终结论为 PASS,所有 Findings 均已处理或说明;\n- 修复后的受影响路径已重新验证并重新采证;\n- 临时数据和运行资源已按项目规则清理。\n\n## 任务差异处理\n\n根据需求和页面能力选择用户路径、状态、视口与证据,不套用固定 CRUD 或截图数量。某项不适用时说明理由,不得静默跳过。完全无法运行当前实现,或修复需要改变已确认需求、设计方向、公开契约或引入新依赖时,说明影响并暂停;验证与修订轮次按 `workflow-discipline` 处理。\n\n## 节点记录与推进\n\n将功能验证结论、已覆盖的用户路径、状态与视口、请求和控制台结果、原始证据引用、缺陷修复、未适用项及理由、`visual-reviewer` 结论与 Findings 处理情况写入当前节点记录。完成上述检查后推进下一节点。",
141
+ "prompt": "# 视觉验证\n\n## 节点职责\n\n运行当前界面并通过浏览器完成验收路径,验证功能结果、采集视觉证据,再根据独立 `visual-reviewer` 的评审结果完成修复和回归。本节点负责运行、操作、采证和流程收敛,不代替 Agent 判断视觉质量。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、验收标准、技术方案、前端实现记录,以及项目和所属模块的提示词。存在交互契约产物时,读取契约并将其自检清单与验收点作为验证清单的底座;无契约时以验收标准为底座。加载 `workflow-discipline`,调用独立 `visual-reviewer` 评审视觉证据。\n\n根据需求整理待验证页面、用户路径、需要呈现的状态和代表性视口。缺少运行方式、访问地址、认证或数据准备信息时,先从项目脚本、配置和现有环境中查找;仍无法运行当前实现时,说明缺失信息并暂停。\n\n## 执行流程\n\n1. 按项目声明启动当前实现及其依赖服务,确认页面对应本次代码。\n2. 使用浏览器自动化能力执行需求涉及的真实用户路径,验证操作后的页面结果、请求结果、反馈、错误处理和持久化后的重新加载结果。\n3. 检查浏览器控制台中的新增错误、失败请求和其他运行异常。\n4. 按验收范围触发适用的加载、空、错误、成功和关键交互状态,并在代表性视口采集截图、视频或等价原始证据;有交互契约时按契约条款逐项触发与核对,证据覆盖契约验收点;不在本节点预判视觉结论。\n5. 将需求、设计规范、场景说明、视口信息和原始证据交给 `visual-reviewer`;交互契约(如有)随需求与设计规范一并作为评审材料移交,不提供实现者结论或预期缺陷。\n6. 修复功能验证失败项以及 `visual-reviewer` 报告的 BLOCKER、HIGH Findings,然后重新执行受影响路径、采集同场景证据并再次评审,直至功能验证通过且视觉评审为 PASS。MEDIUM、LOW 逐项处理或记录保留理由。\n7. 清理本次创建的临时数据和运行资源,不删除非本次创建的数据。\n\n## 产出与检查\n\n推进前确认:\n\n- 需求涉及的用户路径已通过真实浏览器操作验证,并观察到最终结果;\n- 页面结果、请求、反馈、错误处理、持久化和控制台均无未解决的严重问题;\n- 验收所需状态与视口已呈现,原始证据对应当前实现且可追溯;\n- `visual-reviewer` 最终结论为 PASS,所有 Findings 均已处理或说明;\n- 修复后的受影响路径已重新验证并重新采证;\n- 临时数据和运行资源已按项目规则清理。\n\n## 任务差异处理\n\n根据需求和页面能力选择用户路径、状态、视口与证据,不套用固定 CRUD 或截图数量。某项不适用时说明理由,不得静默跳过。完全无法运行当前实现,或修复需要改变已确认需求、设计方向、公开契约或引入新依赖时,说明影响并暂停;验证与修订轮次按 `workflow-discipline` 处理。\n\n## 节点记录与推进\n\n将功能验证结论、已覆盖的用户路径、状态与视口、请求和控制台结果、原始证据引用、缺陷修复、未适用项及理由、`visual-reviewer` 结论与 Findings 处理情况写入当前节点记录。完成上述检查后推进下一节点。",
142
142
  "skills": [
143
143
  "workflow-discipline"
144
144
  ],
@@ -147,8 +147,8 @@
147
147
  ],
148
148
  "sourcePreset": {
149
149
  "code": "verify-visual",
150
- "version": "1.4.0",
151
- "contentHash": "bca69ce7876c0620",
150
+ "version": "1.4.1",
151
+ "contentHash": "a959a6617abf83fb",
152
152
  "agents": [
153
153
  "visual-reviewer"
154
154
  ]
@@ -249,7 +249,7 @@
249
249
  "label": "集成验证",
250
250
  "phase": "test",
251
251
  "track": "all",
252
- "prompt": "# 集成验证\n\n## 节点职责\n\n将远程主线最新状态整合到任务分支,并在最终集成态执行适用回归,形成绑定明确版本的验证结论。版本控制与范围判断由当前执行者负责——范围判断是严肃决策,跑与不跑都必须有证据支撑,全量是须举证的选项而非默认值。测试命令交给独立 `test-executor` 执行;若产生代码修复,再交给独立 `code-reviewer` 评审。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、技术方案、实现与测试记录、`align-branch` 的基础分支与合并记录,以及项目的分支策略、测试体系和所属模块提示词。加载 `workflow-discipline`,调用 `test-executor` 执行回归;发生代码修复时调用 `code-reviewer`。测试失败且根因不明确时,再加载 `bugfix-root-cause-verification`。\n\n优先使用 `align-branch` 已记录的基础分支;记录缺失时,按与该节点相同的规则从任务上下文或项目提示词解析,例如 `main`、`master` 或 `dev`。仍无法解析时询问用户是否需要合并某个分支的远程代码,不根据版本历史猜测基础分支。\n\n## 执行流程\n\n1. 获取远程状态,优先读取 `align-branch` 记录的基础分支;记录缺失时从任务上下文或项目提示词解析,仍无法解析则询问用户。记录当前任务版本、基础分支、远程版本及判断依据。\n2. 用户明确表示无需合并时记录该决定;否则按项目策略整合基础分支的远程最新代码。发生冲突时理解双方意图后逐项融合并验证结果,不盲目选择任一侧;无法可靠解决时说明冲突与缺失决策并暂停。\n3. 汇总任务改动和主线整合改动,读取项目测试目录、配置、脚本与现有用例,列出适用的测试层级和执行入口;项目未定义的层级记录 N/A。\n4. 回归执行决策由「是否执行」与「执行什么」两半构成,每一半都必须由证据支撑,禁止无分析的保守回退:\n - 零差异不重跑:某层级相对既有已验证版本(绑定明确版本标识、全绿证据在案)覆盖面差异为空时,该层级禁止重复执行,记录绑定证据替代重跑;\n - 有差异走判断链:a) 变更清单——汇总任务改动、主线整合改动与集成期修复,落到具体文件和对外可见的符号、契约、配置;b) 依赖分析——逐变更点查明引用方与波及面(导入、调用、共享契约、构建配置),得出受影响模块清单;c) 测试映射——把受影响模块映射到具体用例(既有用例位置、任务新增或修改的用例、所属层级与执行入口);d) 最小充分决策——按定向用例、受影响模块、整层级的顺序逐级扩大,每次扩大都指出上一级不充分的具体证据;\n - 全量须举证:仅当判断链证据表明影响面确实覆盖整个层级(如横切基础设施、共享契约、构建配置变更)才执行全量。宣称「无法判断影响范围」前必须已完成 a)–c) 分析;分析后仍存在的盲区逐条列明(哪个变更点、查了什么、缺什么信息),把盲区对应的补充用例并入执行范围并留痕。未做分析就宣称无法判断、或以「保险起见」跳过判断直接全量,均判定为范围决策失败。\n5. 将明确的命令、顺序、范围和通过标准交给 `test-executor` 执行——委派命令的覆盖范围必须与第 4 条判断链结论一致,判了定向就执行定向入口,不得委派时顺手扩大。依据事实报告核对各层级统计、失败、跳过和日志。\n6. 测试失败时读取完整证据。根因明确则做最小正确修复;根因不明确则按需加载 `bugfix-root-cause-verification`,确认根因后再修改代码、测试或环境。\n7. 每次修复后重新执行原失败范围和受影响回归。不得通过删除有效测试、skip/only、缩小应执行范围、放宽正确断言、吞异常或硬编码结果制造通过。\n8. 检查新增 skip/only、测试删除、未执行的适用层级、范围遗漏和统计对账。发生代码修复时,将客观约束与变更位置交给 `code-reviewer`;修复 BLOCKER、HIGH Findings 后重跑受影响测试并复审,直至测试通过且评审为 PASS。\n9. 记录最终同步状态、实际回归范围、测试结果和已验证的提交或等价版本标识,确保后续能够判断证据是否仍有效。\n\n## 产出与检查\n\n推进前确认:\n\n- 基础分支沿用 `align-branch` 记录,或已按相同规则重新解析并留痕;\n- 基础分支的远程状态已获取并完成整合,或用户已明确表示无需合并;\n- 所有冲突均已理解并正确融合;\n- 项目适用测试层级与执行入口已识别,N/A 有具体依据;\n- 回归范围由完整判断链支撑(变更清单 → 依赖分析 → 测试映射 → 逐级扩大决策),每个执行范围都能指认其覆盖的变更点;零差异层级以绑定证据替代重跑;全量执行附有影响面覆盖整个层级的具体证据;不存在无分析的全量兜底;\n- `test-executor` 报告所有适用回归通过,失败为 0,未授权跳过为 0;\n- 所有失败均在根因明确后修复,没有制造假绿;\n- 发生代码修复时,`code-reviewer` 最终结论为 PASS;\n- 集成结论绑定明确版本,日志和报告可追溯。\n\n## 任务差异处理\n\n基础分支没有新提交时无需制造合并提交;回归是否执行、执行什么,一律按第 4 条的零差异判定与判断链决定——集成态与已验证版本一致且证据在案时以证据绑定替代重跑,集成态有新增内容时按判断链定范围。用户明确表示无需合并时保留决定,验证决策同样按第 4 条执行。根据项目实际体系选择测试层级和命令,不擅自引入未定义的测试类型。远程状态不可获取、冲突无法可靠解决,或修复涉及破坏性契约、新依赖和真实范围扩张时,说明证据与影响并暂停;同一问题多轮不收敛时按 `workflow-discipline` 处理。\n\n## 节点记录与推进\n\n将基础分支来源、同步策略与结果、任务和远程版本、冲突处理、适用测试层级、回归范围判断链(变更清单、依赖分析、测试映射、逐级扩大证据、零差异证据绑定)及结论、实际命令与统计、失败根因和修复、完整性检查、代码评审结果、日志与报告引用、最终已验证版本写入当前节点记录。完成上述检查后推进下一节点。\n",
252
+ "prompt": "# 集成验证\n\n## 节点职责\n\n将远程主线最新状态整合到任务分支,并在最终集成态执行适用回归,形成绑定明确版本的验证结论。版本控制与范围判断由当前执行者负责——范围判断是严肃决策,跑与不跑都必须有证据支撑,全量是须举证的选项而非默认值。测试命令交给独立 `test-executor` 执行;若产生代码修复,再交给独立 `code-reviewer` 评审。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、技术方案、实现与测试记录、`align-branch` 的基础分支与合并记录,以及项目的分支策略、测试体系和所属模块提示词。加载 `workflow-discipline`,调用 `test-executor` 执行回归;发生代码修复时调用 `code-reviewer`。测试失败且根因不明确时,再加载 `bugfix-root-cause-verification`。\n\n优先使用 `align-branch` 已记录的基础分支;记录缺失时,按与该节点相同的规则从任务上下文或项目提示词解析,例如 `main`、`master` 或 `dev`。仍无法解析时询问用户是否需要合并某个分支的远程代码,不根据版本历史猜测基础分支。\n\n## 执行流程\n\n1. 获取远程状态,优先读取 `align-branch` 记录的基础分支;记录缺失时从任务上下文或项目提示词解析,仍无法解析则询问用户。记录当前任务版本、基础分支、远程版本及判断依据。\n2. 用户明确表示无需合并时记录该决定;否则按项目策略整合基础分支的远程最新代码。发生冲突时理解双方意图后逐项融合并验证结果,不盲目选择任一侧;无法可靠解决时说明冲突与缺失决策并暂停。\n3. 汇总任务改动和主线整合改动,读取项目测试目录、配置、脚本与现有用例,列出适用的测试层级和执行入口;项目未定义的层级记录 N/A。\n4. 回归执行决策由「是否执行」与「执行什么」两半构成,每一半都必须由证据支撑,禁止无分析的保守回退:\n - 零差异不重跑:某层级相对既有已验证版本(绑定明确版本标识、全绿证据在案)覆盖面差异为空时,该层级禁止重复执行,记录绑定证据替代重跑;\n - 有差异走判断链:a) 变更清单——汇总任务改动、主线整合改动与集成期修复,落到具体文件和对外可见的符号、契约、配置;b) 依赖分析——逐变更点查明引用方与波及面(导入、调用、共享契约、构建配置),得出受影响模块清单;c) 测试映射——把受影响模块映射到具体用例(既有用例位置、任务新增或修改的用例、所属层级与执行入口);d) 最小充分决策——按定向用例、受影响模块、整层级的顺序逐级扩大,每次扩大都指出上一级不充分的具体证据;\n - 全量须举证:仅当判断链证据表明影响面确实覆盖整个层级(如横切基础设施、共享契约、构建配置变更)才执行全量。宣称「无法判断影响范围」前必须已完成 a)–c) 分析;分析后仍存在的盲区逐条列明(哪个变更点、查了什么、缺什么信息),把盲区对应的补充用例并入执行范围并留痕。未做分析就宣称无法判断、或以「保险起见」跳过判断直接全量,均判定为范围决策失败。\n5. 将明确的命令、顺序、范围和通过标准交给 `test-executor` 执行——委派命令的覆盖范围必须与第 4 条判断链结论一致,判了定向就执行定向入口,不得委派时顺手扩大。依据事实报告核对各层级统计、失败、跳过和日志。\n6. 测试失败时读取完整证据。根因明确则做最小正确修复;根因不明确则按需加载 `bugfix-root-cause-verification`,确认根因后再修改代码、测试或环境。\n7. 每次修复后重新执行原失败范围和受影响回归。不得通过删除有效测试、skip/only、缩小应执行范围、放宽正确断言、吞异常或硬编码结果制造通过。\n8. 检查新增 skip/only、测试删除、未执行的适用层级、范围遗漏和统计对账。发生代码修复时,将评审目标与材料(变更位置与相关任务信息)交给 `code-reviewer`;修复 BLOCKER、HIGH Findings 后重跑受影响测试并复审,直至测试通过且评审为 PASS。\n9. 记录最终同步状态、实际回归范围、测试结果和已验证的提交或等价版本标识,确保后续能够判断证据是否仍有效。\n\n## 产出与检查\n\n推进前确认:\n\n- 基础分支沿用 `align-branch` 记录,或已按相同规则重新解析并留痕;\n- 基础分支的远程状态已获取并完成整合,或用户已明确表示无需合并;\n- 所有冲突均已理解并正确融合;\n- 项目适用测试层级与执行入口已识别,N/A 有具体依据;\n- 回归范围由完整判断链支撑(变更清单 → 依赖分析 → 测试映射 → 逐级扩大决策),每个执行范围都能指认其覆盖的变更点;零差异层级以绑定证据替代重跑;全量执行附有影响面覆盖整个层级的具体证据;不存在无分析的全量兜底;\n- `test-executor` 报告所有适用回归通过,失败为 0,未授权跳过为 0;\n- 所有失败均在根因明确后修复,没有制造假绿;\n- 发生代码修复时,`code-reviewer` 最终结论为 PASS;\n- 集成结论绑定明确版本,日志和报告可追溯。\n\n## 任务差异处理\n\n基础分支没有新提交时无需制造合并提交;回归是否执行、执行什么,一律按第 4 条的零差异判定与判断链决定——集成态与已验证版本一致且证据在案时以证据绑定替代重跑,集成态有新增内容时按判断链定范围。用户明确表示无需合并时保留决定,验证决策同样按第 4 条执行。根据项目实际体系选择测试层级和命令,不擅自引入未定义的测试类型。远程状态不可获取、冲突无法可靠解决,或修复涉及破坏性契约、新依赖和真实范围扩张时,说明证据与影响并暂停;同一问题多轮不收敛时按 `workflow-discipline` 处理。\n\n## 节点记录与推进\n\n将基础分支来源、同步策略与结果、任务和远程版本、冲突处理、适用测试层级、回归范围判断链(变更清单、依赖分析、测试映射、逐级扩大证据、零差异证据绑定)及结论、实际命令与统计、失败根因和修复、完整性检查、代码评审结果、日志与报告引用、最终已验证版本写入当前节点记录。完成上述检查后推进下一节点。\n",
253
253
  "skills": [
254
254
  "workflow-discipline"
255
255
  ],
@@ -259,8 +259,8 @@
259
259
  ],
260
260
  "sourcePreset": {
261
261
  "code": "test-integrate",
262
- "version": "1.4.0",
263
- "contentHash": "2a6c9a9bbc3452ce",
262
+ "version": "1.4.1",
263
+ "contentHash": "0f25ef16474fb990",
264
264
  "agents": [
265
265
  "test-executor",
266
266
  "code-reviewer"
@@ -457,16 +457,16 @@
457
457
  }
458
458
  },
459
459
  "isDefault": false,
460
- "version": "3.5.1"
460
+ "version": "3.5.2"
461
461
  },
462
462
  "dependencies": {
463
463
  "skills": [
464
464
  {
465
465
  "name": "workflow-discipline",
466
466
  "description": "DAG 节点执行、角色分离、授权、暂停点与任务边界纪律",
467
- "content": "# workflow-discipline\n\n保证所有 DAG 节点以一致方式确定工作边界、分离执行与验证、处理授权与暂停点,并在完成判定满足后安全推进。\n\n## 适用边界\n\n- 所有节点执行者都应用本 Skill;任务简单、用户催促或改动很小,不改变节点步骤、质量门和完成标准。\n- 本 Skill规定跨节点通用纪律,不替代当前节点提示词中的职责、产出、检查项和具体执行方法。\n- 当前节点提示词与本 Skill共同生效;发现冲突时不得自行忽略任一约束,应先按权限和任务边界判断,无法收敛再向用户呈现冲突。\n\n## 节点执行流程\n\n1. **确认当前边界**:读取任务上下文、当前节点职责、所需产出、完成判定、暂停点和项目约束。执行 git 操作前确认当前分支及项目声明的分支模型。\n2. **确定执行角色**:设计文档和编码由主会话完成;测试命令由 `test-executor` 执行;节点要求独立审查时,由不同 session 的只读 Verifier 执行。\n3. **完成节点工作**:只实施当前节点和已确认任务范围内的工作。发现死代码删留、顺手重构、范围扩张或其他范围外动作时,将其作为独立决策呈现给用户,不自行纳入。\n4. **执行独立验证**:Worker 与 Verifier 必须分离。Verifier 只接收评审对象路径、客观约束和参照位置,不接收设计结论、决策理由或“用户已确认”等预设结论。\n5. **处理审查结果**:审查是否通过,以 Verifier 本次输出为准。逐条修复 finding,或记录不修复理由;修复阻塞问题后重新审查。交互式审查最多 3 轮,连续 3 轮仍未通过时,保留在当前节点并请用户决定。\n6. **检查并记录**:逐项核对当前节点的完成判定,记录实际产出、检查结果、评审结论与轮次、关键决定、不适用项及其理由。\n7. **提交节点成果**:完成判定全部满足后、推进节点前,检查工作树和 diff。存在本节点产生的应提交改动时,只纳入当前任务与当前节点范围内的代码、测试、文档等成果并创建 git commit;不得混入无关改动,也不得用空提交代替成果提交。提交失败或本节点应提交改动仍有遗漏时,留在当前节点处理。\n8. **推进节点**:确认完成判定全部满足,且本节点应提交成果已经提交后,才按节点规定推进或进入暂停点。没有应提交改动的纯审查、纯审批节点直接推进,不制造空提交。\n\n## 执行与质量边界\n\n- Track 编码节点只修改生产代码,不修改测试或其他验证代码;测试节点不得以“现有测试已经覆盖”代替逐项验证。\n- 本任务改动导致的测试失败必须完成根因调查并处理,不得标记为“既有问题”后拒绝处理,也不得通过删除测试、放宽断言或添加 skip 制造通过。\n- 预算或时间不足是暂停并上升决策的理由,不是降低质量门的理由。\n- 已知问题必须逐条判断是否影响功能完整性;影响验收的,在任务结束前处理。只有用户明确接受风险并留下原文记录,才可作为例外。\n- 增量测试或增量评审只能缩小经过证据证明的受影响范围,不得降低原有测试、审查和通过标准。\n\n## 审查与委派规则\n\n- Worker 与 Verifier 使用不同 session;Verifier 只读,不修改代码或文档,不接管主会话协调。\n- 增量复审聚焦本轮改动及其影响面,但沿用完整审查标准。\n- 委派 prompt 使用七段结构:`IDENTITY → TASK → EXPECTED → CONTEXT → CONSTRAINTS → MUST DO / MUST NOT DO → VERIFICATION`。\n- `IDENTITY` 必须说明 agent 类型、读写边界、所需 Skill 和禁止再次委派;借用其他 Skill 时说明是执行该 Skill,还是仅借用其输出格式。\n- subagent 完成指定产出后立即结束;审查修订、重新委派和多轮协调由主会话负责。\n\n## 授权与暂停点\n\n- **决策授权**只覆盖具体方案或风险选择,不自动跳过审查、复审、测试或其他流程步骤。\n- **流程授权**只覆盖用户明确指名跳过的步骤,不得扩展到其他质量门。\n- “跳过人工审批”只跳过对应的用户暂停点,不等于跳过自动审查或验证。\n- 暂停点只有用户明确肯定当前待确认内容时才算通过,并记录用户原文。用户的提问、评估、条件句或新诉求不是确认;应先回应并继续等待明确结论。\n\n## 任务完成边界\n\n- 当前节点全部完成判定满足,且本节点产生的应提交成果已经提交后,才能推进到下一节点;单项测试通过、核心代码完成、存在未提交成果或分支合并,都不能代替节点完成。没有应提交改动的节点不制造空提交。\n- 节点完成不等于任务完成。架构信息归档完成,或该步骤经用户明确授权跳过并留下记录后,任务才进入终态。\n- 任务终止前,不推荐、不询问、不切换到下一个任务;终止后再输出完整任务总结。",
467
+ "content": "# workflow-discipline\n\n保证所有 DAG 节点以一致方式确定工作边界、分离执行与验证、处理授权与暂停点,并在完成判定满足后安全推进。\n\n## 铁律\n\n本 Skill 是保障 siming 流程正确执行的基础资产。执行会话加载本 Skill 后,任何上下文压缩操作一律不得压缩本 Skill 正文——它是后续所有节点判定与推进的准绳,必须完整留存在上下文中。\n\n执行会话只专注当前任务本身,不关注执行效率等其他因素;节点提示词、skill、委派 brief 中不得出现任何让 AI 转移任务注意力的描述。\n\n## 适用边界\n\n- 所有节点执行者都应用本 Skill;任务简单、用户催促或改动很小,不改变节点步骤、质量门和完成标准。\n- 本 Skill规定跨节点通用纪律,不替代当前节点提示词中的职责、产出、检查项和具体执行方法。\n- 当前节点提示词与本 Skill共同生效;发现冲突时不得自行忽略任一约束,应先按权限和任务边界判断,无法收敛再向用户呈现冲突。\n\n## 节点执行流程\n\n1. **确认当前边界**:读取任务上下文、当前节点职责、所需产出、完成判定、暂停点和项目约束。执行 git 操作前确认当前分支及项目声明的分支模型。\n2. **确定执行角色**:设计文档和编码由主会话完成;测试命令由 `test-executor` 执行;节点要求独立审查时,由不同 session 的只读 Verifier 执行。\n3. **完成节点工作**:只实施当前节点和已确认任务范围内的工作。发现死代码删留、顺手重构、范围扩张或其他范围外动作时,将其作为独立决策呈现给用户,不自行纳入。\n4. **执行独立验证**:Worker 与 Verifier 必须分离。Verifier 只接收评审目标与材料(评审对象路径、参照位置、已认定非问题的历史事项),不接收设计结论、决策理由或“用户已确认”等预设结论。\n5. **处理审查结果**:审查是否通过,以 Verifier 本次输出为准。逐条修复 finding,或记录不修复理由;修复阻塞问题后重新审查。交互式审查最多 3 轮,连续 3 轮仍未通过时,保留在当前节点并请用户决定。\n6. **检查并记录**:逐项核对当前节点的完成判定,记录实际产出、检查结果、评审结论与轮次、关键决定、不适用项及其理由。\n7. **提交节点成果**:完成判定全部满足后、推进节点前,检查工作树和 diff。存在本节点产生的应提交改动时,只纳入当前任务与当前节点范围内的代码、测试、文档等成果并创建 git commit;不得混入无关改动,也不得用空提交代替成果提交。提交失败或本节点应提交改动仍有遗漏时,留在当前节点处理。\n8. **推进节点**:确认完成判定全部满足,且本节点应提交成果已经提交后,才按节点规定推进或进入暂停点。没有应提交改动的纯审查、纯审批节点直接推进,不制造空提交。\n\n## 执行与质量边界\n\n- Track 编码节点只修改生产代码,不修改测试或其他验证代码;测试节点不得以“现有测试已经覆盖”代替逐项验证。\n- 本任务改动导致的测试失败必须完成根因调查并处理,不得标记为“既有问题”后拒绝处理,也不得通过删除测试、放宽断言或添加 skip 制造通过。\n- 预算或时间不足是暂停并上升决策的理由,不是降低质量门的理由。\n- 已知问题必须逐条判断是否影响功能完整性;影响验收的,在任务结束前处理。只有用户明确接受风险并留下原文记录,才可作为例外。\n- 增量测试或增量评审只能缩小经过证据证明的受影响范围,不得降低原有测试、审查和通过标准。\n\n## 委派纪律\n\n- Worker 与 Verifier 使用不同 session;Verifier 只读,不修改代码或文档,不接管主会话协调。\n- 增量复审聚焦本轮改动及其影响面,但沿用完整审查标准。\n- 委派 prompt 只提供评审目标与原始材料:目标 = 评什么、不评什么、主会话已认定不属于问题的历史事项;材料 = 原始需求、技术方案、代码等任务信息与信息地址(路径 / 命令 / 文档位置),怎么使用由 subagent 自行决策。目标必须精准——该评的项必须列全(不缺),不得把无关或大范围扫描类目标并入本次委派(不扩散);评估范围由目标层锁定,评审不自行扩张到目标之外。\n- 执行策略全部由 subagent 自行决策——如何分析、如何组织报告、按什么步骤工作、何时结束,均不在 brief 中指定;主会话不设预算、轮次、时限,不给检索范围限制,不指定评估方式、不追加取证要求、不扩张评估范围;发现 agent 定义有缺陷时修正定义本身,不以委派限制替代。\n- 需要评估时按角色选择对应 reviewer,不以主会话自评替代独立评估;确无匹配 agent 时上升用户。\n- 主会话与 reviewer 结论出现明显问题或冲突时,上升人类决策;流程本身有问题时,通知人类调整流程。\n- subagent 完成指定产出后立即结束;审查修订、重新委派和多轮协调由主会话负责。\n\n## 授权与暂停点\n\n- **决策授权**只覆盖具体方案或风险选择,不自动跳过审查、复审、测试或其他流程步骤。\n- **流程授权**只覆盖用户明确指名跳过的步骤,不得扩展到其他质量门。\n- “跳过人工审批”只跳过对应的用户暂停点,不等于跳过自动审查或验证。\n- 暂停点只有用户明确肯定当前待确认内容时才算通过,并记录用户原文。用户的提问、评估、条件句或新诉求不是确认;应先回应并继续等待明确结论。\n\n## 任务完成边界\n\n- 当前节点全部完成判定满足,且本节点产生的应提交成果已经提交后,才能推进到下一节点;单项测试通过、核心代码完成、存在未提交成果或分支合并,都不能代替节点完成。没有应提交改动的节点不制造空提交。\n- 节点完成不等于任务完成。架构信息归档完成,或该步骤经用户明确授权跳过并留下记录后,任务才进入终态。\n- 任务终止前,不推荐、不询问、不切换到下一个任务;终止后再输出完整任务总结。",
468
468
  "category": "process",
469
- "version": "1.1.1",
469
+ "version": "1.3.0",
470
470
  "references": [],
471
471
  "scope": "global"
472
472
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "exportFormatVersion": "1.0",
3
- "exportedAt": "2026-09-14T09:32:04.262Z",
3
+ "exportedAt": "2026-09-15T08:13:01.507Z",
4
4
  "warnings": [],
5
5
  "template": {
6
6
  "name": "quick_workflow",
@@ -28,7 +28,7 @@
28
28
  "label": "快速设计",
29
29
  "phase": "entry",
30
30
  "track": "all",
31
- "prompt": "# 快速设计\n\n## 节点职责\n\n将用户描述的小型改动直接整理为最小技术方案——改动点、影响面、验证方式——经一次独立 AI 评审和用户人工确认后进入编码。本流程不设独立需求节点:用户描述与任务上下文就是需求源头。本节点只产出方案,不编写生产代码或测试代码。\n\n## 依赖信息\n\n先检查当前任务上下文:用户需求描述、验收标准、非目标、已有决策,以及项目和所属模块的提示词。加载 `workflow-discipline`;调用独立 `arch-reviewer` 完成方案评审。\n\n直接阅读相关代码和文档,确认真实改动位置、调用关系、依赖方与影响。缺少会改变方案的关键信息时先从仓库现状补齐;仍存在歧义时集中向用户说明并确认,不凭猜测设计。\n\n## 正向执行流程\n\n1. 从用户描述提炼目标行为、保持不变的行为和非目标,整理为可核对的改动点清单。\n2. 逐项定位改动点涉及的模块、调用关系与依赖方,说明对接口、数据、配置、兼容性和用户行为的影响。\n3. 形成最小技术方案,必含:背景与目标、改动点清单(位置+目标行为)、影响面、架构图与核心功能流程图、验证方式、非目标、必要决策;验证方式须能对应验收标准。两张图用可渲染的图形语法表达(项目未约定时用 Mermaid),示意级别即可,不使用文字罗列代替;图中模块、步骤、分支与正文互相有对应说明。\n4. 检查复杂度红线:跨多个边界模块、新增公开接口或数据契约、数据迁移、新依赖、破坏性变更、需要多路线取舍。命中时说明快速流程为何不足,建议改走完整流程;用户明确接受风险坚持快速路径时,记录确认原文。\n5. 将方案全文与客观约束(用户描述、验收标准、参照代码位置)交给 `arch-reviewer` 单轮独立评审,不提供主会话的设计结论。存在 BLOCKER 或 HIGH 时修复后重新发起评审;MEDIUM、LOW 逐项处理或记录保留理由,直至评审 PASS。\n6. 评审通过后先把方案全文落盘为可独立阅读的文档(项目正式文档路径;尚未确定时写临时草稿并注明),再向用户呈现该文档的可读位置、改动点清单、影响面、验证方式、AI 评审结论与红线判断,等待人工确认——确认前用户必须能打开完整方案。\n7. 用户确认后:把已呈现的方案文档按项目文档约定保存为正式文档并登记任务产物(携带全文;超限时路径引用须说明理由),已落盘的草稿直接转为正式文档,不重写内容;按项目分支策略创建任务分支并记录关联。若预判改动可能无可自动验证行为,在方案中显式声明——该声明进入编码节点测试范围判定与本次确认范围。\n\n## 产出与检查\n\n推进前确认:每个改动点有位置、目标行为与影响说明;验证方式对应验收标准;方案含架构图与核心功能流程图,以可渲染的图形呈现且与正文一致(确实不适用时已写明对象与理由);复杂度红线已检查,命中项已升级或取得用户风险确认;`arch-reviewer` 最终 PASS 且 Findings 处理留痕;用户明确确认且原文已记录;方案全文已保存登记;任务分支已就绪;呈现给用户的方案文档可独立打开阅读,且与确认的版本一致。\n\n## 任务差异处理\n\n纯文档、配置类改动可裁剪方案章节,但改动点、影响面、架构图与核心功能流程图、验证方式、非目标不得缺失。信息不足以形成方案时保留在本节点补齐,不推进。\n\n## 节点记录与推进\n\n将方案要点、改动点、影响面、验证方式、红线判断、AI 评审结论与轮次、用户确认原文、方案产物与分支信息写入节点记录,推进编码节点。",
31
+ "prompt": "# 快速设计\n\n## 节点职责\n\n将用户描述的小型改动直接整理为最小技术方案——改动点、影响面、验证方式——经一次独立 AI 评审和用户人工确认后进入编码。本流程不设独立需求节点:用户描述与任务上下文就是需求源头。本节点只产出方案,不编写生产代码或测试代码。\n\n## 依赖信息\n\n先检查当前任务上下文:用户需求描述、验收标准、非目标、已有决策,以及项目和所属模块的提示词。加载 `workflow-discipline`;调用独立 `arch-reviewer` 完成方案评审。\n\n直接阅读相关代码和文档,确认真实改动位置、调用关系、依赖方与影响。缺少会改变方案的关键信息时先从仓库现状补齐;仍存在歧义时集中向用户说明并确认,不凭猜测设计。\n\n## 正向执行流程\n\n1. 从用户描述提炼目标行为、保持不变的行为和非目标,整理为可核对的改动点清单。\n2. 逐项定位改动点涉及的模块、调用关系与依赖方,说明对接口、数据、配置、兼容性和用户行为的影响。\n3. 形成最小技术方案,必含:背景与目标、改动点清单(位置+目标行为)、影响面、架构图与核心功能流程图、验证方式、非目标、必要决策;验证方式须能对应验收标准。两张图用可渲染的图形语法表达(项目未约定时用 Mermaid),示意级别即可,不使用文字罗列代替;图中模块、步骤、分支与正文互相有对应说明。\n4. 检查复杂度红线:跨多个边界模块、新增公开接口或数据契约、数据迁移、新依赖、破坏性变更、需要多路线取舍。命中时说明快速流程为何不足,建议改走完整流程;用户明确接受风险坚持快速路径时,记录确认原文。\n5. 将方案全文与评审目标、材料(用户描述、验收标准、参照代码位置)交给 `arch-reviewer` 单轮独立评审,不提供主会话的设计结论。存在 BLOCKER 或 HIGH 时修复后重新发起评审;MEDIUM、LOW 逐项处理或记录保留理由,直至评审 PASS。\n6. 评审通过后先把方案全文落盘为可独立阅读的文档(项目正式文档路径;尚未确定时写临时草稿并注明),再向用户呈现该文档的可读位置、改动点清单、影响面、验证方式、AI 评审结论与红线判断,等待人工确认——确认前用户必须能打开完整方案。\n7. 用户确认后:把已呈现的方案文档按项目文档约定保存为正式文档并登记任务产物(携带全文;超限时路径引用须说明理由),已落盘的草稿直接转为正式文档,不重写内容;按项目分支策略创建任务分支并记录关联。若预判改动可能无可自动验证行为,在方案中显式声明——该声明进入编码节点测试范围判定与本次确认范围。\n\n## 产出与检查\n\n推进前确认:每个改动点有位置、目标行为与影响说明;验证方式对应验收标准;方案含架构图与核心功能流程图,以可渲染的图形呈现且与正文一致(确实不适用时已写明对象与理由);复杂度红线已检查,命中项已升级或取得用户风险确认;`arch-reviewer` 最终 PASS 且 Findings 处理留痕;用户明确确认且原文已记录;方案全文已保存登记;任务分支已就绪;呈现给用户的方案文档可独立打开阅读,且与确认的版本一致。\n\n## 任务差异处理\n\n纯文档、配置类改动可裁剪方案章节,但改动点、影响面、架构图与核心功能流程图、验证方式、非目标不得缺失。信息不足以形成方案时保留在本节点补齐,不推进。\n\n## 节点记录与推进\n\n将方案要点、改动点、影响面、验证方式、红线判断、AI 评审结论与轮次、用户确认原文、方案产物与分支信息写入节点记录,推进编码节点。",
32
32
  "skills": [
33
33
  "workflow-discipline"
34
34
  ],
@@ -37,8 +37,8 @@
37
37
  ],
38
38
  "sourcePreset": {
39
39
  "code": "quick-design",
40
- "version": "1.1.0",
41
- "contentHash": "98b00192cc580305",
40
+ "version": "1.1.1",
41
+ "contentHash": "aa8eb7b9eefb8cbd",
42
42
  "agents": [
43
43
  "arch-reviewer"
44
44
  ]
@@ -99,7 +99,7 @@
99
99
  "label": "集成验证",
100
100
  "phase": "test",
101
101
  "track": "all",
102
- "prompt": "# 集成验证\n\n## 节点职责\n\n将远程主线最新状态整合到任务分支,并在最终集成态执行适用回归,形成绑定明确版本的验证结论。版本控制与范围判断由当前执行者负责——范围判断是严肃决策,跑与不跑都必须有证据支撑,全量是须举证的选项而非默认值。测试命令交给独立 `test-executor` 执行;若产生代码修复,再交给独立 `code-reviewer` 评审。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、技术方案、实现与测试记录、`align-branch` 的基础分支与合并记录,以及项目的分支策略、测试体系和所属模块提示词。加载 `workflow-discipline`,调用 `test-executor` 执行回归;发生代码修复时调用 `code-reviewer`。测试失败且根因不明确时,再加载 `bugfix-root-cause-verification`。\n\n优先使用 `align-branch` 已记录的基础分支;记录缺失时,按与该节点相同的规则从任务上下文或项目提示词解析,例如 `main`、`master` 或 `dev`。仍无法解析时询问用户是否需要合并某个分支的远程代码,不根据版本历史猜测基础分支。\n\n## 执行流程\n\n1. 获取远程状态,优先读取 `align-branch` 记录的基础分支;记录缺失时从任务上下文或项目提示词解析,仍无法解析则询问用户。记录当前任务版本、基础分支、远程版本及判断依据。\n2. 用户明确表示无需合并时记录该决定;否则按项目策略整合基础分支的远程最新代码。发生冲突时理解双方意图后逐项融合并验证结果,不盲目选择任一侧;无法可靠解决时说明冲突与缺失决策并暂停。\n3. 汇总任务改动和主线整合改动,读取项目测试目录、配置、脚本与现有用例,列出适用的测试层级和执行入口;项目未定义的层级记录 N/A。\n4. 回归执行决策由「是否执行」与「执行什么」两半构成,每一半都必须由证据支撑,禁止无分析的保守回退:\n - 零差异不重跑:某层级相对既有已验证版本(绑定明确版本标识、全绿证据在案)覆盖面差异为空时,该层级禁止重复执行,记录绑定证据替代重跑;\n - 有差异走判断链:a) 变更清单——汇总任务改动、主线整合改动与集成期修复,落到具体文件和对外可见的符号、契约、配置;b) 依赖分析——逐变更点查明引用方与波及面(导入、调用、共享契约、构建配置),得出受影响模块清单;c) 测试映射——把受影响模块映射到具体用例(既有用例位置、任务新增或修改的用例、所属层级与执行入口);d) 最小充分决策——按定向用例、受影响模块、整层级的顺序逐级扩大,每次扩大都指出上一级不充分的具体证据;\n - 全量须举证:仅当判断链证据表明影响面确实覆盖整个层级(如横切基础设施、共享契约、构建配置变更)才执行全量。宣称「无法判断影响范围」前必须已完成 a)–c) 分析;分析后仍存在的盲区逐条列明(哪个变更点、查了什么、缺什么信息),把盲区对应的补充用例并入执行范围并留痕。未做分析就宣称无法判断、或以「保险起见」跳过判断直接全量,均判定为范围决策失败。\n5. 将明确的命令、顺序、范围和通过标准交给 `test-executor` 执行——委派命令的覆盖范围必须与第 4 条判断链结论一致,判了定向就执行定向入口,不得委派时顺手扩大。依据事实报告核对各层级统计、失败、跳过和日志。\n6. 测试失败时读取完整证据。根因明确则做最小正确修复;根因不明确则按需加载 `bugfix-root-cause-verification`,确认根因后再修改代码、测试或环境。\n7. 每次修复后重新执行原失败范围和受影响回归。不得通过删除有效测试、skip/only、缩小应执行范围、放宽正确断言、吞异常或硬编码结果制造通过。\n8. 检查新增 skip/only、测试删除、未执行的适用层级、范围遗漏和统计对账。发生代码修复时,将客观约束与变更位置交给 `code-reviewer`;修复 BLOCKER、HIGH Findings 后重跑受影响测试并复审,直至测试通过且评审为 PASS。\n9. 记录最终同步状态、实际回归范围、测试结果和已验证的提交或等价版本标识,确保后续能够判断证据是否仍有效。\n\n## 产出与检查\n\n推进前确认:\n\n- 基础分支沿用 `align-branch` 记录,或已按相同规则重新解析并留痕;\n- 基础分支的远程状态已获取并完成整合,或用户已明确表示无需合并;\n- 所有冲突均已理解并正确融合;\n- 项目适用测试层级与执行入口已识别,N/A 有具体依据;\n- 回归范围由完整判断链支撑(变更清单 → 依赖分析 → 测试映射 → 逐级扩大决策),每个执行范围都能指认其覆盖的变更点;零差异层级以绑定证据替代重跑;全量执行附有影响面覆盖整个层级的具体证据;不存在无分析的全量兜底;\n- `test-executor` 报告所有适用回归通过,失败为 0,未授权跳过为 0;\n- 所有失败均在根因明确后修复,没有制造假绿;\n- 发生代码修复时,`code-reviewer` 最终结论为 PASS;\n- 集成结论绑定明确版本,日志和报告可追溯。\n\n## 任务差异处理\n\n基础分支没有新提交时无需制造合并提交;回归是否执行、执行什么,一律按第 4 条的零差异判定与判断链决定——集成态与已验证版本一致且证据在案时以证据绑定替代重跑,集成态有新增内容时按判断链定范围。用户明确表示无需合并时保留决定,验证决策同样按第 4 条执行。根据项目实际体系选择测试层级和命令,不擅自引入未定义的测试类型。远程状态不可获取、冲突无法可靠解决,或修复涉及破坏性契约、新依赖和真实范围扩张时,说明证据与影响并暂停;同一问题多轮不收敛时按 `workflow-discipline` 处理。\n\n## 节点记录与推进\n\n将基础分支来源、同步策略与结果、任务和远程版本、冲突处理、适用测试层级、回归范围判断链(变更清单、依赖分析、测试映射、逐级扩大证据、零差异证据绑定)及结论、实际命令与统计、失败根因和修复、完整性检查、代码评审结果、日志与报告引用、最终已验证版本写入当前节点记录。完成上述检查后推进下一节点。\n",
102
+ "prompt": "# 集成验证\n\n## 节点职责\n\n将远程主线最新状态整合到任务分支,并在最终集成态执行适用回归,形成绑定明确版本的验证结论。版本控制与范围判断由当前执行者负责——范围判断是严肃决策,跑与不跑都必须有证据支撑,全量是须举证的选项而非默认值。测试命令交给独立 `test-executor` 执行;若产生代码修复,再交给独立 `code-reviewer` 评审。\n\n## 依赖信息\n\n先检查当前任务上下文,读取需求、技术方案、实现与测试记录、`align-branch` 的基础分支与合并记录,以及项目的分支策略、测试体系和所属模块提示词。加载 `workflow-discipline`,调用 `test-executor` 执行回归;发生代码修复时调用 `code-reviewer`。测试失败且根因不明确时,再加载 `bugfix-root-cause-verification`。\n\n优先使用 `align-branch` 已记录的基础分支;记录缺失时,按与该节点相同的规则从任务上下文或项目提示词解析,例如 `main`、`master` 或 `dev`。仍无法解析时询问用户是否需要合并某个分支的远程代码,不根据版本历史猜测基础分支。\n\n## 执行流程\n\n1. 获取远程状态,优先读取 `align-branch` 记录的基础分支;记录缺失时从任务上下文或项目提示词解析,仍无法解析则询问用户。记录当前任务版本、基础分支、远程版本及判断依据。\n2. 用户明确表示无需合并时记录该决定;否则按项目策略整合基础分支的远程最新代码。发生冲突时理解双方意图后逐项融合并验证结果,不盲目选择任一侧;无法可靠解决时说明冲突与缺失决策并暂停。\n3. 汇总任务改动和主线整合改动,读取项目测试目录、配置、脚本与现有用例,列出适用的测试层级和执行入口;项目未定义的层级记录 N/A。\n4. 回归执行决策由「是否执行」与「执行什么」两半构成,每一半都必须由证据支撑,禁止无分析的保守回退:\n - 零差异不重跑:某层级相对既有已验证版本(绑定明确版本标识、全绿证据在案)覆盖面差异为空时,该层级禁止重复执行,记录绑定证据替代重跑;\n - 有差异走判断链:a) 变更清单——汇总任务改动、主线整合改动与集成期修复,落到具体文件和对外可见的符号、契约、配置;b) 依赖分析——逐变更点查明引用方与波及面(导入、调用、共享契约、构建配置),得出受影响模块清单;c) 测试映射——把受影响模块映射到具体用例(既有用例位置、任务新增或修改的用例、所属层级与执行入口);d) 最小充分决策——按定向用例、受影响模块、整层级的顺序逐级扩大,每次扩大都指出上一级不充分的具体证据;\n - 全量须举证:仅当判断链证据表明影响面确实覆盖整个层级(如横切基础设施、共享契约、构建配置变更)才执行全量。宣称「无法判断影响范围」前必须已完成 a)–c) 分析;分析后仍存在的盲区逐条列明(哪个变更点、查了什么、缺什么信息),把盲区对应的补充用例并入执行范围并留痕。未做分析就宣称无法判断、或以「保险起见」跳过判断直接全量,均判定为范围决策失败。\n5. 将明确的命令、顺序、范围和通过标准交给 `test-executor` 执行——委派命令的覆盖范围必须与第 4 条判断链结论一致,判了定向就执行定向入口,不得委派时顺手扩大。依据事实报告核对各层级统计、失败、跳过和日志。\n6. 测试失败时读取完整证据。根因明确则做最小正确修复;根因不明确则按需加载 `bugfix-root-cause-verification`,确认根因后再修改代码、测试或环境。\n7. 每次修复后重新执行原失败范围和受影响回归。不得通过删除有效测试、skip/only、缩小应执行范围、放宽正确断言、吞异常或硬编码结果制造通过。\n8. 检查新增 skip/only、测试删除、未执行的适用层级、范围遗漏和统计对账。发生代码修复时,将评审目标与材料(变更位置与相关任务信息)交给 `code-reviewer`;修复 BLOCKER、HIGH Findings 后重跑受影响测试并复审,直至测试通过且评审为 PASS。\n9. 记录最终同步状态、实际回归范围、测试结果和已验证的提交或等价版本标识,确保后续能够判断证据是否仍有效。\n\n## 产出与检查\n\n推进前确认:\n\n- 基础分支沿用 `align-branch` 记录,或已按相同规则重新解析并留痕;\n- 基础分支的远程状态已获取并完成整合,或用户已明确表示无需合并;\n- 所有冲突均已理解并正确融合;\n- 项目适用测试层级与执行入口已识别,N/A 有具体依据;\n- 回归范围由完整判断链支撑(变更清单 → 依赖分析 → 测试映射 → 逐级扩大决策),每个执行范围都能指认其覆盖的变更点;零差异层级以绑定证据替代重跑;全量执行附有影响面覆盖整个层级的具体证据;不存在无分析的全量兜底;\n- `test-executor` 报告所有适用回归通过,失败为 0,未授权跳过为 0;\n- 所有失败均在根因明确后修复,没有制造假绿;\n- 发生代码修复时,`code-reviewer` 最终结论为 PASS;\n- 集成结论绑定明确版本,日志和报告可追溯。\n\n## 任务差异处理\n\n基础分支没有新提交时无需制造合并提交;回归是否执行、执行什么,一律按第 4 条的零差异判定与判断链决定——集成态与已验证版本一致且证据在案时以证据绑定替代重跑,集成态有新增内容时按判断链定范围。用户明确表示无需合并时保留决定,验证决策同样按第 4 条执行。根据项目实际体系选择测试层级和命令,不擅自引入未定义的测试类型。远程状态不可获取、冲突无法可靠解决,或修复涉及破坏性契约、新依赖和真实范围扩张时,说明证据与影响并暂停;同一问题多轮不收敛时按 `workflow-discipline` 处理。\n\n## 节点记录与推进\n\n将基础分支来源、同步策略与结果、任务和远程版本、冲突处理、适用测试层级、回归范围判断链(变更清单、依赖分析、测试映射、逐级扩大证据、零差异证据绑定)及结论、实际命令与统计、失败根因和修复、完整性检查、代码评审结果、日志与报告引用、最终已验证版本写入当前节点记录。完成上述检查后推进下一节点。\n",
103
103
  "skills": [
104
104
  "workflow-discipline"
105
105
  ],
@@ -109,8 +109,8 @@
109
109
  ],
110
110
  "sourcePreset": {
111
111
  "code": "test-integrate",
112
- "version": "1.4.0",
113
- "contentHash": "2a6c9a9bbc3452ce",
112
+ "version": "1.4.1",
113
+ "contentHash": "0f25ef16474fb990",
114
114
  "agents": [
115
115
  "test-executor",
116
116
  "code-reviewer"
@@ -176,16 +176,16 @@
176
176
  }
177
177
  ],
178
178
  "isDefault": false,
179
- "version": "2.0.2"
179
+ "version": "2.0.3"
180
180
  },
181
181
  "dependencies": {
182
182
  "skills": [
183
183
  {
184
184
  "name": "workflow-discipline",
185
185
  "description": "DAG 节点执行、角色分离、授权、暂停点与任务边界纪律",
186
- "content": "# workflow-discipline\n\n保证所有 DAG 节点以一致方式确定工作边界、分离执行与验证、处理授权与暂停点,并在完成判定满足后安全推进。\n\n## 适用边界\n\n- 所有节点执行者都应用本 Skill;任务简单、用户催促或改动很小,不改变节点步骤、质量门和完成标准。\n- 本 Skill规定跨节点通用纪律,不替代当前节点提示词中的职责、产出、检查项和具体执行方法。\n- 当前节点提示词与本 Skill共同生效;发现冲突时不得自行忽略任一约束,应先按权限和任务边界判断,无法收敛再向用户呈现冲突。\n\n## 节点执行流程\n\n1. **确认当前边界**:读取任务上下文、当前节点职责、所需产出、完成判定、暂停点和项目约束。执行 git 操作前确认当前分支及项目声明的分支模型。\n2. **确定执行角色**:设计文档和编码由主会话完成;测试命令由 `test-executor` 执行;节点要求独立审查时,由不同 session 的只读 Verifier 执行。\n3. **完成节点工作**:只实施当前节点和已确认任务范围内的工作。发现死代码删留、顺手重构、范围扩张或其他范围外动作时,将其作为独立决策呈现给用户,不自行纳入。\n4. **执行独立验证**:Worker 与 Verifier 必须分离。Verifier 只接收评审对象路径、客观约束和参照位置,不接收设计结论、决策理由或“用户已确认”等预设结论。\n5. **处理审查结果**:审查是否通过,以 Verifier 本次输出为准。逐条修复 finding,或记录不修复理由;修复阻塞问题后重新审查。交互式审查最多 3 轮,连续 3 轮仍未通过时,保留在当前节点并请用户决定。\n6. **检查并记录**:逐项核对当前节点的完成判定,记录实际产出、检查结果、评审结论与轮次、关键决定、不适用项及其理由。\n7. **提交节点成果**:完成判定全部满足后、推进节点前,检查工作树和 diff。存在本节点产生的应提交改动时,只纳入当前任务与当前节点范围内的代码、测试、文档等成果并创建 git commit;不得混入无关改动,也不得用空提交代替成果提交。提交失败或本节点应提交改动仍有遗漏时,留在当前节点处理。\n8. **推进节点**:确认完成判定全部满足,且本节点应提交成果已经提交后,才按节点规定推进或进入暂停点。没有应提交改动的纯审查、纯审批节点直接推进,不制造空提交。\n\n## 执行与质量边界\n\n- Track 编码节点只修改生产代码,不修改测试或其他验证代码;测试节点不得以“现有测试已经覆盖”代替逐项验证。\n- 本任务改动导致的测试失败必须完成根因调查并处理,不得标记为“既有问题”后拒绝处理,也不得通过删除测试、放宽断言或添加 skip 制造通过。\n- 预算或时间不足是暂停并上升决策的理由,不是降低质量门的理由。\n- 已知问题必须逐条判断是否影响功能完整性;影响验收的,在任务结束前处理。只有用户明确接受风险并留下原文记录,才可作为例外。\n- 增量测试或增量评审只能缩小经过证据证明的受影响范围,不得降低原有测试、审查和通过标准。\n\n## 审查与委派规则\n\n- Worker 与 Verifier 使用不同 session;Verifier 只读,不修改代码或文档,不接管主会话协调。\n- 增量复审聚焦本轮改动及其影响面,但沿用完整审查标准。\n- 委派 prompt 使用七段结构:`IDENTITY → TASK → EXPECTED → CONTEXT → CONSTRAINTS → MUST DO / MUST NOT DO → VERIFICATION`。\n- `IDENTITY` 必须说明 agent 类型、读写边界、所需 Skill 和禁止再次委派;借用其他 Skill 时说明是执行该 Skill,还是仅借用其输出格式。\n- subagent 完成指定产出后立即结束;审查修订、重新委派和多轮协调由主会话负责。\n\n## 授权与暂停点\n\n- **决策授权**只覆盖具体方案或风险选择,不自动跳过审查、复审、测试或其他流程步骤。\n- **流程授权**只覆盖用户明确指名跳过的步骤,不得扩展到其他质量门。\n- “跳过人工审批”只跳过对应的用户暂停点,不等于跳过自动审查或验证。\n- 暂停点只有用户明确肯定当前待确认内容时才算通过,并记录用户原文。用户的提问、评估、条件句或新诉求不是确认;应先回应并继续等待明确结论。\n\n## 任务完成边界\n\n- 当前节点全部完成判定满足,且本节点产生的应提交成果已经提交后,才能推进到下一节点;单项测试通过、核心代码完成、存在未提交成果或分支合并,都不能代替节点完成。没有应提交改动的节点不制造空提交。\n- 节点完成不等于任务完成。架构信息归档完成,或该步骤经用户明确授权跳过并留下记录后,任务才进入终态。\n- 任务终止前,不推荐、不询问、不切换到下一个任务;终止后再输出完整任务总结。",
186
+ "content": "# workflow-discipline\n\n保证所有 DAG 节点以一致方式确定工作边界、分离执行与验证、处理授权与暂停点,并在完成判定满足后安全推进。\n\n## 铁律\n\n本 Skill 是保障 siming 流程正确执行的基础资产。执行会话加载本 Skill 后,任何上下文压缩操作一律不得压缩本 Skill 正文——它是后续所有节点判定与推进的准绳,必须完整留存在上下文中。\n\n执行会话只专注当前任务本身,不关注执行效率等其他因素;节点提示词、skill、委派 brief 中不得出现任何让 AI 转移任务注意力的描述。\n\n## 适用边界\n\n- 所有节点执行者都应用本 Skill;任务简单、用户催促或改动很小,不改变节点步骤、质量门和完成标准。\n- 本 Skill规定跨节点通用纪律,不替代当前节点提示词中的职责、产出、检查项和具体执行方法。\n- 当前节点提示词与本 Skill共同生效;发现冲突时不得自行忽略任一约束,应先按权限和任务边界判断,无法收敛再向用户呈现冲突。\n\n## 节点执行流程\n\n1. **确认当前边界**:读取任务上下文、当前节点职责、所需产出、完成判定、暂停点和项目约束。执行 git 操作前确认当前分支及项目声明的分支模型。\n2. **确定执行角色**:设计文档和编码由主会话完成;测试命令由 `test-executor` 执行;节点要求独立审查时,由不同 session 的只读 Verifier 执行。\n3. **完成节点工作**:只实施当前节点和已确认任务范围内的工作。发现死代码删留、顺手重构、范围扩张或其他范围外动作时,将其作为独立决策呈现给用户,不自行纳入。\n4. **执行独立验证**:Worker 与 Verifier 必须分离。Verifier 只接收评审目标与材料(评审对象路径、参照位置、已认定非问题的历史事项),不接收设计结论、决策理由或“用户已确认”等预设结论。\n5. **处理审查结果**:审查是否通过,以 Verifier 本次输出为准。逐条修复 finding,或记录不修复理由;修复阻塞问题后重新审查。交互式审查最多 3 轮,连续 3 轮仍未通过时,保留在当前节点并请用户决定。\n6. **检查并记录**:逐项核对当前节点的完成判定,记录实际产出、检查结果、评审结论与轮次、关键决定、不适用项及其理由。\n7. **提交节点成果**:完成判定全部满足后、推进节点前,检查工作树和 diff。存在本节点产生的应提交改动时,只纳入当前任务与当前节点范围内的代码、测试、文档等成果并创建 git commit;不得混入无关改动,也不得用空提交代替成果提交。提交失败或本节点应提交改动仍有遗漏时,留在当前节点处理。\n8. **推进节点**:确认完成判定全部满足,且本节点应提交成果已经提交后,才按节点规定推进或进入暂停点。没有应提交改动的纯审查、纯审批节点直接推进,不制造空提交。\n\n## 执行与质量边界\n\n- Track 编码节点只修改生产代码,不修改测试或其他验证代码;测试节点不得以“现有测试已经覆盖”代替逐项验证。\n- 本任务改动导致的测试失败必须完成根因调查并处理,不得标记为“既有问题”后拒绝处理,也不得通过删除测试、放宽断言或添加 skip 制造通过。\n- 预算或时间不足是暂停并上升决策的理由,不是降低质量门的理由。\n- 已知问题必须逐条判断是否影响功能完整性;影响验收的,在任务结束前处理。只有用户明确接受风险并留下原文记录,才可作为例外。\n- 增量测试或增量评审只能缩小经过证据证明的受影响范围,不得降低原有测试、审查和通过标准。\n\n## 委派纪律\n\n- Worker 与 Verifier 使用不同 session;Verifier 只读,不修改代码或文档,不接管主会话协调。\n- 增量复审聚焦本轮改动及其影响面,但沿用完整审查标准。\n- 委派 prompt 只提供评审目标与原始材料:目标 = 评什么、不评什么、主会话已认定不属于问题的历史事项;材料 = 原始需求、技术方案、代码等任务信息与信息地址(路径 / 命令 / 文档位置),怎么使用由 subagent 自行决策。目标必须精准——该评的项必须列全(不缺),不得把无关或大范围扫描类目标并入本次委派(不扩散);评估范围由目标层锁定,评审不自行扩张到目标之外。\n- 执行策略全部由 subagent 自行决策——如何分析、如何组织报告、按什么步骤工作、何时结束,均不在 brief 中指定;主会话不设预算、轮次、时限,不给检索范围限制,不指定评估方式、不追加取证要求、不扩张评估范围;发现 agent 定义有缺陷时修正定义本身,不以委派限制替代。\n- 需要评估时按角色选择对应 reviewer,不以主会话自评替代独立评估;确无匹配 agent 时上升用户。\n- 主会话与 reviewer 结论出现明显问题或冲突时,上升人类决策;流程本身有问题时,通知人类调整流程。\n- subagent 完成指定产出后立即结束;审查修订、重新委派和多轮协调由主会话负责。\n\n## 授权与暂停点\n\n- **决策授权**只覆盖具体方案或风险选择,不自动跳过审查、复审、测试或其他流程步骤。\n- **流程授权**只覆盖用户明确指名跳过的步骤,不得扩展到其他质量门。\n- “跳过人工审批”只跳过对应的用户暂停点,不等于跳过自动审查或验证。\n- 暂停点只有用户明确肯定当前待确认内容时才算通过,并记录用户原文。用户的提问、评估、条件句或新诉求不是确认;应先回应并继续等待明确结论。\n\n## 任务完成边界\n\n- 当前节点全部完成判定满足,且本节点产生的应提交成果已经提交后,才能推进到下一节点;单项测试通过、核心代码完成、存在未提交成果或分支合并,都不能代替节点完成。没有应提交改动的节点不制造空提交。\n- 节点完成不等于任务完成。架构信息归档完成,或该步骤经用户明确授权跳过并留下记录后,任务才进入终态。\n- 任务终止前,不推荐、不询问、不切换到下一个任务;终止后再输出完整任务总结。",
187
187
  "category": "process",
188
- "version": "1.1.1",
188
+ "version": "1.3.0",
189
189
  "references": [],
190
190
  "scope": "global"
191
191
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "exportFormatVersion": "1.0",
3
- "exportedAt": "2026-09-14T09:32:04.260Z",
3
+ "exportedAt": "2026-09-15T08:13:01.505Z",
4
4
  "warnings": [],
5
5
  "template": {
6
6
  "name": "research_workflow",
@@ -95,9 +95,9 @@
95
95
  {
96
96
  "name": "workflow-discipline",
97
97
  "description": "DAG 节点执行、角色分离、授权、暂停点与任务边界纪律",
98
- "content": "# workflow-discipline\n\n保证所有 DAG 节点以一致方式确定工作边界、分离执行与验证、处理授权与暂停点,并在完成判定满足后安全推进。\n\n## 适用边界\n\n- 所有节点执行者都应用本 Skill;任务简单、用户催促或改动很小,不改变节点步骤、质量门和完成标准。\n- 本 Skill规定跨节点通用纪律,不替代当前节点提示词中的职责、产出、检查项和具体执行方法。\n- 当前节点提示词与本 Skill共同生效;发现冲突时不得自行忽略任一约束,应先按权限和任务边界判断,无法收敛再向用户呈现冲突。\n\n## 节点执行流程\n\n1. **确认当前边界**:读取任务上下文、当前节点职责、所需产出、完成判定、暂停点和项目约束。执行 git 操作前确认当前分支及项目声明的分支模型。\n2. **确定执行角色**:设计文档和编码由主会话完成;测试命令由 `test-executor` 执行;节点要求独立审查时,由不同 session 的只读 Verifier 执行。\n3. **完成节点工作**:只实施当前节点和已确认任务范围内的工作。发现死代码删留、顺手重构、范围扩张或其他范围外动作时,将其作为独立决策呈现给用户,不自行纳入。\n4. **执行独立验证**:Worker 与 Verifier 必须分离。Verifier 只接收评审对象路径、客观约束和参照位置,不接收设计结论、决策理由或“用户已确认”等预设结论。\n5. **处理审查结果**:审查是否通过,以 Verifier 本次输出为准。逐条修复 finding,或记录不修复理由;修复阻塞问题后重新审查。交互式审查最多 3 轮,连续 3 轮仍未通过时,保留在当前节点并请用户决定。\n6. **检查并记录**:逐项核对当前节点的完成判定,记录实际产出、检查结果、评审结论与轮次、关键决定、不适用项及其理由。\n7. **提交节点成果**:完成判定全部满足后、推进节点前,检查工作树和 diff。存在本节点产生的应提交改动时,只纳入当前任务与当前节点范围内的代码、测试、文档等成果并创建 git commit;不得混入无关改动,也不得用空提交代替成果提交。提交失败或本节点应提交改动仍有遗漏时,留在当前节点处理。\n8. **推进节点**:确认完成判定全部满足,且本节点应提交成果已经提交后,才按节点规定推进或进入暂停点。没有应提交改动的纯审查、纯审批节点直接推进,不制造空提交。\n\n## 执行与质量边界\n\n- Track 编码节点只修改生产代码,不修改测试或其他验证代码;测试节点不得以“现有测试已经覆盖”代替逐项验证。\n- 本任务改动导致的测试失败必须完成根因调查并处理,不得标记为“既有问题”后拒绝处理,也不得通过删除测试、放宽断言或添加 skip 制造通过。\n- 预算或时间不足是暂停并上升决策的理由,不是降低质量门的理由。\n- 已知问题必须逐条判断是否影响功能完整性;影响验收的,在任务结束前处理。只有用户明确接受风险并留下原文记录,才可作为例外。\n- 增量测试或增量评审只能缩小经过证据证明的受影响范围,不得降低原有测试、审查和通过标准。\n\n## 审查与委派规则\n\n- Worker 与 Verifier 使用不同 session;Verifier 只读,不修改代码或文档,不接管主会话协调。\n- 增量复审聚焦本轮改动及其影响面,但沿用完整审查标准。\n- 委派 prompt 使用七段结构:`IDENTITY → TASK → EXPECTED → CONTEXT → CONSTRAINTS → MUST DO / MUST NOT DO → VERIFICATION`。\n- `IDENTITY` 必须说明 agent 类型、读写边界、所需 Skill 和禁止再次委派;借用其他 Skill 时说明是执行该 Skill,还是仅借用其输出格式。\n- subagent 完成指定产出后立即结束;审查修订、重新委派和多轮协调由主会话负责。\n\n## 授权与暂停点\n\n- **决策授权**只覆盖具体方案或风险选择,不自动跳过审查、复审、测试或其他流程步骤。\n- **流程授权**只覆盖用户明确指名跳过的步骤,不得扩展到其他质量门。\n- “跳过人工审批”只跳过对应的用户暂停点,不等于跳过自动审查或验证。\n- 暂停点只有用户明确肯定当前待确认内容时才算通过,并记录用户原文。用户的提问、评估、条件句或新诉求不是确认;应先回应并继续等待明确结论。\n\n## 任务完成边界\n\n- 当前节点全部完成判定满足,且本节点产生的应提交成果已经提交后,才能推进到下一节点;单项测试通过、核心代码完成、存在未提交成果或分支合并,都不能代替节点完成。没有应提交改动的节点不制造空提交。\n- 节点完成不等于任务完成。架构信息归档完成,或该步骤经用户明确授权跳过并留下记录后,任务才进入终态。\n- 任务终止前,不推荐、不询问、不切换到下一个任务;终止后再输出完整任务总结。",
98
+ "content": "# workflow-discipline\n\n保证所有 DAG 节点以一致方式确定工作边界、分离执行与验证、处理授权与暂停点,并在完成判定满足后安全推进。\n\n## 铁律\n\n本 Skill 是保障 siming 流程正确执行的基础资产。执行会话加载本 Skill 后,任何上下文压缩操作一律不得压缩本 Skill 正文——它是后续所有节点判定与推进的准绳,必须完整留存在上下文中。\n\n执行会话只专注当前任务本身,不关注执行效率等其他因素;节点提示词、skill、委派 brief 中不得出现任何让 AI 转移任务注意力的描述。\n\n## 适用边界\n\n- 所有节点执行者都应用本 Skill;任务简单、用户催促或改动很小,不改变节点步骤、质量门和完成标准。\n- 本 Skill规定跨节点通用纪律,不替代当前节点提示词中的职责、产出、检查项和具体执行方法。\n- 当前节点提示词与本 Skill共同生效;发现冲突时不得自行忽略任一约束,应先按权限和任务边界判断,无法收敛再向用户呈现冲突。\n\n## 节点执行流程\n\n1. **确认当前边界**:读取任务上下文、当前节点职责、所需产出、完成判定、暂停点和项目约束。执行 git 操作前确认当前分支及项目声明的分支模型。\n2. **确定执行角色**:设计文档和编码由主会话完成;测试命令由 `test-executor` 执行;节点要求独立审查时,由不同 session 的只读 Verifier 执行。\n3. **完成节点工作**:只实施当前节点和已确认任务范围内的工作。发现死代码删留、顺手重构、范围扩张或其他范围外动作时,将其作为独立决策呈现给用户,不自行纳入。\n4. **执行独立验证**:Worker 与 Verifier 必须分离。Verifier 只接收评审目标与材料(评审对象路径、参照位置、已认定非问题的历史事项),不接收设计结论、决策理由或“用户已确认”等预设结论。\n5. **处理审查结果**:审查是否通过,以 Verifier 本次输出为准。逐条修复 finding,或记录不修复理由;修复阻塞问题后重新审查。交互式审查最多 3 轮,连续 3 轮仍未通过时,保留在当前节点并请用户决定。\n6. **检查并记录**:逐项核对当前节点的完成判定,记录实际产出、检查结果、评审结论与轮次、关键决定、不适用项及其理由。\n7. **提交节点成果**:完成判定全部满足后、推进节点前,检查工作树和 diff。存在本节点产生的应提交改动时,只纳入当前任务与当前节点范围内的代码、测试、文档等成果并创建 git commit;不得混入无关改动,也不得用空提交代替成果提交。提交失败或本节点应提交改动仍有遗漏时,留在当前节点处理。\n8. **推进节点**:确认完成判定全部满足,且本节点应提交成果已经提交后,才按节点规定推进或进入暂停点。没有应提交改动的纯审查、纯审批节点直接推进,不制造空提交。\n\n## 执行与质量边界\n\n- Track 编码节点只修改生产代码,不修改测试或其他验证代码;测试节点不得以“现有测试已经覆盖”代替逐项验证。\n- 本任务改动导致的测试失败必须完成根因调查并处理,不得标记为“既有问题”后拒绝处理,也不得通过删除测试、放宽断言或添加 skip 制造通过。\n- 预算或时间不足是暂停并上升决策的理由,不是降低质量门的理由。\n- 已知问题必须逐条判断是否影响功能完整性;影响验收的,在任务结束前处理。只有用户明确接受风险并留下原文记录,才可作为例外。\n- 增量测试或增量评审只能缩小经过证据证明的受影响范围,不得降低原有测试、审查和通过标准。\n\n## 委派纪律\n\n- Worker 与 Verifier 使用不同 session;Verifier 只读,不修改代码或文档,不接管主会话协调。\n- 增量复审聚焦本轮改动及其影响面,但沿用完整审查标准。\n- 委派 prompt 只提供评审目标与原始材料:目标 = 评什么、不评什么、主会话已认定不属于问题的历史事项;材料 = 原始需求、技术方案、代码等任务信息与信息地址(路径 / 命令 / 文档位置),怎么使用由 subagent 自行决策。目标必须精准——该评的项必须列全(不缺),不得把无关或大范围扫描类目标并入本次委派(不扩散);评估范围由目标层锁定,评审不自行扩张到目标之外。\n- 执行策略全部由 subagent 自行决策——如何分析、如何组织报告、按什么步骤工作、何时结束,均不在 brief 中指定;主会话不设预算、轮次、时限,不给检索范围限制,不指定评估方式、不追加取证要求、不扩张评估范围;发现 agent 定义有缺陷时修正定义本身,不以委派限制替代。\n- 需要评估时按角色选择对应 reviewer,不以主会话自评替代独立评估;确无匹配 agent 时上升用户。\n- 主会话与 reviewer 结论出现明显问题或冲突时,上升人类决策;流程本身有问题时,通知人类调整流程。\n- subagent 完成指定产出后立即结束;审查修订、重新委派和多轮协调由主会话负责。\n\n## 授权与暂停点\n\n- **决策授权**只覆盖具体方案或风险选择,不自动跳过审查、复审、测试或其他流程步骤。\n- **流程授权**只覆盖用户明确指名跳过的步骤,不得扩展到其他质量门。\n- “跳过人工审批”只跳过对应的用户暂停点,不等于跳过自动审查或验证。\n- 暂停点只有用户明确肯定当前待确认内容时才算通过,并记录用户原文。用户的提问、评估、条件句或新诉求不是确认;应先回应并继续等待明确结论。\n\n## 任务完成边界\n\n- 当前节点全部完成判定满足,且本节点产生的应提交成果已经提交后,才能推进到下一节点;单项测试通过、核心代码完成、存在未提交成果或分支合并,都不能代替节点完成。没有应提交改动的节点不制造空提交。\n- 节点完成不等于任务完成。架构信息归档完成,或该步骤经用户明确授权跳过并留下记录后,任务才进入终态。\n- 任务终止前,不推荐、不询问、不切换到下一个任务;终止后再输出完整任务总结。",
99
99
  "category": "process",
100
- "version": "1.1.1",
100
+ "version": "1.3.0",
101
101
  "references": [],
102
102
  "scope": "global"
103
103
  }