@siming-org/cli 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -666,12 +666,12 @@ async function healthcheckCommand(opts) {
666
666
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
667
667
 
668
668
  // src/mcp/server.ts
669
- import { readFileSync as readFileSync8 } from "fs";
669
+ import { readFileSync as readFileSync9 } from "fs";
670
670
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
671
671
  import { VERSION } from "@siming-org/core";
672
672
 
673
673
  // src/client/handlers/task.ts
674
- import { readFileSync as readFileSync5 } from "fs";
674
+ import { readFileSync as readFileSync6 } from "fs";
675
675
  import {
676
676
  ARTIFACT_TYPES
677
677
  } from "@siming-org/core";
@@ -682,6 +682,44 @@ var MIN_AI_CHECK_ITEM_LENGTH = 15;
682
682
  var VAGUE_AI_CHECK_BLACKLIST = ["\u5DF2\u5B8C\u6210", "\u901A\u8FC7", "\u5B8C\u6210", "done", "ok", "pass"];
683
683
 
684
684
  // src/client/checks/git-checks.ts
685
+ async function perRepoCheck(ctx, type, judge) {
686
+ const results = [];
687
+ for (const repo of ctx.repos) {
688
+ results.push({ repo, r: await judge(repo) });
689
+ }
690
+ const failed = results.filter((x) => x.r.status === "fail");
691
+ if (failed.length > 0) {
692
+ const failLines = failed.slice(0, 4).map((x) => `${x.repo}: ${x.r.detail}`);
693
+ const rest = results.filter((x) => x.r.status !== "fail").map((x) => `${x.repo}: ${x.r.status === "pass" ? "pass" : "N/A"}`);
694
+ const suffix = failed.length > 4 ? `\uFF08\u53E6\u6709 ${failed.length - 4} \u4ED3 fail \u89C1\u626B\u63CF\u8DEF\u5F84\u9010\u4ED3\u91CD\u67E5\uFF09` : "";
695
+ const okPart = rest.length > 0 ? `\uFF1B\u5176\u4F59\u4ED3\uFF1A${rest.join("\u3001")}` : "";
696
+ return {
697
+ type,
698
+ status: "fail",
699
+ // 尾附扫描详情:fail 时用户需核对 R 完整性(子目录启动漏仓诊断——scanned from 可观测启动位置)
700
+ detail: `${failed.length}/${results.length} \u4ED3\u672A\u8FC7\u2014\u2014${failLines.join("\uFF1B")}${suffix}${okPart}\u3002\u4FEE\u590D\uFF1A\u9010\u4ED3\u5904\u7406\u540E\u91CD\u8BD5\uFF08\u591A\u4ED3\u987B\u5168\u90E8\u9002\u914D\u76F8\u540C\u5206\u652F\u7BA1\u7406\uFF09
701
+ ${ctx.reposScanDetail}`
702
+ };
703
+ }
704
+ const allNa = results.every((x) => x.r.status === "not_applicable");
705
+ if (allNa) {
706
+ return {
707
+ type,
708
+ status: "not_applicable",
709
+ detail: `\u5168\u90E8 ${results.length} \u4ED3\u514D\u68C0\uFF1A${results.map((x) => `${x.repo}\uFF08${x.r.detail}\uFF09`).join("\uFF1B")}
710
+ ${ctx.reposScanDetail}`
711
+ };
712
+ }
713
+ const passLines = results.filter((x) => x.r.status === "pass").map((x) => `${x.repo}: ${x.r.detail}`);
714
+ const naPart = results.filter((x) => x.r.status === "not_applicable").map((x) => `${x.repo}\uFF08${x.r.detail}\uFF09`);
715
+ return {
716
+ type,
717
+ status: "pass",
718
+ // 尾附扫描详情:pass 同样需可观测 R 完整性(子目录启动扫到部分仓的静默假 pass 诊断)
719
+ detail: `\u5168\u90E8 ${results.length} \u4ED3\u901A\u8FC7\u2014\u2014${passLines.join("\uFF1B")}${naPart.length > 0 ? `\uFF1B\u514D\u68C0\u4ED3\uFF1A${naPart.join("\uFF1B")}` : ""}
720
+ ${ctx.reposScanDetail}`
721
+ };
722
+ }
685
723
  function naIfRepoUnavailable(ctx) {
686
724
  if (ctx.vcsLoadError !== void 0) {
687
725
  return { type: "", status: "fail", detail: `\u9879\u76EE vcs \u914D\u7F6E\u8BFB\u53D6\u5931\u8D25\uFF1A${ctx.vcsLoadError}\u2014\u2014\u65E0\u6CD5\u786E\u8BA4\u95E8\u7981\u542F\u7528\u72B6\u6001\uFF08\u62D2\u9759\u9ED8\u964D\u7EA7\uFF09\u3002\u4FEE\u590D\uFF1A\u786E\u8BA4 server \u53EF\u8FBE\u4E0E\u9879\u76EE\u914D\u7F6E\u540E\u91CD\u8DD1 task check` };
@@ -689,8 +727,8 @@ function naIfRepoUnavailable(ctx) {
689
727
  if (ctx.vcs.enabled === false) {
690
728
  return na("\u9879\u76EE vcs.enabled=false\uFF08\u58F0\u660E\u65E0\u5206\u652F\u6A21\u578B\uFF09\uFF0Cgit \u7C7B\u68C0\u67E5\u8C41\u514D");
691
729
  }
692
- if (ctx.repoPath === null) {
693
- return na(`\u4ED3\u5E93\u4E0D\u53EF\u7528\uFF1A${ctx.repoProbeDetail}`);
730
+ if (ctx.repos.length === 0) {
731
+ return na(`\u5DE5\u4F5C\u76EE\u5F55\u65E0 git \u4ED3\uFF08${ctx.reposScanDetail}\uFF09`);
694
732
  }
695
733
  return null;
696
734
  }
@@ -700,30 +738,32 @@ function na(reason) {
700
738
  function fail2(ctx, type, rootCause, fix) {
701
739
  return { type, status: "fail", detail: `${rootCause}\u3002\u4FEE\u590D\uFF1A${fix}\uFF08\u5F53\u524D nodeId=${ctx.nodeId}\uFF09` };
702
740
  }
703
- async function currentBranch(ctx) {
704
- const r = await ctx.runGit(["rev-parse", "--abbrev-ref", "HEAD"], ctx.repoPath);
741
+ async function currentBranch(ctx, repo) {
742
+ const r = await ctx.runGit(["rev-parse", "--abbrev-ref", "HEAD"], repo);
705
743
  return r.ok ? r.stdout.trim() : null;
706
744
  }
707
745
  async function checkBranchExists(ctx) {
708
746
  const type = "git-branch-exists";
709
747
  const guard = naIfRepoUnavailable(ctx);
710
748
  if (guard) return { ...guard, type };
711
- const branch = await currentBranch(ctx);
712
- if (branch === null) {
713
- return fail2(ctx, type, "\u65E0\u6CD5\u8BFB\u53D6\u5F53\u524D\u5206\u652F\uFF08git rev-parse --abbrev-ref HEAD \u5931\u8D25\uFF09", "\u786E\u8BA4\u5728\u4EFB\u52A1\u5206\u652F\u4E0A\u6267\u884C\uFF0C\u6216\u68C0\u67E5\u4ED3\u5E93\u72B6\u6001");
714
- }
715
- if (branch === "HEAD") {
716
- return fail2(ctx, type, "\u5904\u4E8E detached HEAD \u72B6\u6001\uFF08\u672A\u68C0\u51FA\u4EFB\u4F55\u5206\u652F\uFF09", `git checkout \u4EFB\u52A1\u5206\u652F\uFF08\u987B\u5339\u914D ${ctx.branchPattern} \u524D\u7F00\uFF09`);
717
- }
718
- if (!branch.startsWith(ctx.branchPattern)) {
719
- return fail2(ctx, type, `\u5F53\u524D\u5206\u652F\u300C${branch}\u300D\u4E0D\u5339\u914D\u4EFB\u52A1\u5206\u652F\u89C4\u5219\u300C${ctx.branchPattern} \u524D\u7F00\u300D`, `\u5207\u5230\u4EFB\u52A1\u5206\u652F\uFF08feature/Txxxxxxxx...\uFF09\u540E\u518D\u63A8\u8FDB`);
720
- }
721
- return { type, status: "pass", detail: `\u5F53\u524D\u5206\u652F ${branch}` };
749
+ return perRepoCheck(ctx, type, async (repo) => {
750
+ const branch = await currentBranch(ctx, repo);
751
+ if (branch === null) {
752
+ return { status: "fail", detail: "\u65E0\u6CD5\u8BFB\u53D6\u5F53\u524D\u5206\u652F\uFF08git rev-parse --abbrev-ref HEAD \u5931\u8D25\u2014\u2014\u7591\u4F3C\u635F\u574F\u4ED3\uFF09" };
753
+ }
754
+ if (branch === "HEAD") {
755
+ return { status: "fail", detail: `\u5904\u4E8E detached HEAD \u72B6\u6001\uFF08\u672A\u68C0\u51FA\u4EFB\u4F55\u5206\u652F\uFF09\u2014\u2014git checkout \u4EFB\u52A1\u5206\u652F\uFF08\u987B\u5339\u914D ${ctx.branchPattern} \u524D\u7F00\uFF09` };
756
+ }
757
+ if (!branch.startsWith(ctx.branchPattern)) {
758
+ return { status: "fail", detail: `\u5F53\u524D\u5206\u652F\u300C${branch}\u300D\u4E0D\u5339\u914D\u4EFB\u52A1\u5206\u652F\u89C4\u5219\u300C${ctx.branchPattern} \u524D\u7F00\u300D\u2014\u2014\u5207\u5230\u4EFB\u52A1\u5206\u652F\u540E\u518D\u63A8\u8FDB` };
759
+ }
760
+ return { status: "pass", detail: `\u5F53\u524D\u5206\u652F ${branch}` };
761
+ });
722
762
  }
723
- async function baseMergedIntoHead(ctx, base) {
724
- const f = await ctx.runGit(["fetch", "origin", base], ctx.repoPath);
763
+ async function baseMergedIntoHead(ctx, base, repo) {
764
+ const f = await ctx.runGit(["fetch", "origin", base], repo);
725
765
  if (!f.ok) return null;
726
- const r = await ctx.runGit(["merge-base", "--is-ancestor", `origin/${base}`, "HEAD"], ctx.repoPath);
766
+ const r = await ctx.runGit(["merge-base", "--is-ancestor", `origin/${base}`, "HEAD"], repo);
727
767
  return r.ok;
728
768
  }
729
769
  async function checkBaseMerged(ctx) {
@@ -737,14 +777,17 @@ async function checkBaseMerged(ctx) {
737
777
  if (ctx.baseBranch === null) {
738
778
  return fail2(ctx, type, "\u57FA\u7840\u5206\u652F\u672A\u77E5\uFF08ALIGN \u8BB0\u5F55\u672A\u5199 baseBranch \u4E14\u9879\u76EE\u672A\u914D\u7F6E vcs.baseBranch\uFF09", "record-set \u5199 ALIGN.baseBranch\uFF0C\u6216 siming project update --vcs-base-branch <\u5206\u652F>");
739
779
  }
740
- const merged = await baseMergedIntoHead(ctx, ctx.baseBranch);
741
- if (merged === null) {
742
- return fail2(ctx, type, `git fetch origin ${ctx.baseBranch} \u5931\u8D25\u2014\u2014\u65E0\u6CD5\u4EE5\u8FDC\u7A0B\u6700\u65B0\u5F15\u7528\u5224\u5B9A\uFF08\u62D2\u6309\u9648\u65E7 ref \u8BEF\u5224\uFF09`, "\u68C0\u67E5\u7F51\u7EDC/\u8FDC\u7A0B\u53EF\u8FBE\u540E\u91CD\u8BD5\uFF1B\u786E\u8BA4\u5206\u652F\u5B58\u5728\u4E8E origin");
743
- }
744
- if (!merged) {
745
- return fail2(ctx, type, `origin/${ctx.baseBranch} \u5C1A\u672A\u5408\u5165\u5F53\u524D\u5206\u652F`, `\u6309 ALIGN \u8282\u70B9\u8981\u6C42\u5408\u5E76\u57FA\u7840\u5206\u652F\u8FDC\u7A0B\u6700\u65B0\u4EE3\u7801\uFF08merge ${ctx.baseBranch} \u2192 \u5F53\u524D\u5206\u652F\uFF09\u540E\u91CD\u8BD5`);
746
- }
747
- return { type, status: "pass", detail: `origin/${ctx.baseBranch} \u5DF2\u5408\u5165 HEAD` };
780
+ const base = ctx.baseBranch;
781
+ return perRepoCheck(ctx, type, async (repo) => {
782
+ const merged = await baseMergedIntoHead(ctx, base, repo);
783
+ if (merged === null) {
784
+ return { status: "fail", detail: `git fetch origin ${base} \u5931\u8D25\u2014\u2014\u65E0\u6CD5\u4EE5\u8FDC\u7A0B\u6700\u65B0\u5F15\u7528\u5224\u5B9A\uFF08\u62D2\u6309\u9648\u65E7 ref \u8BEF\u5224\uFF09\uFF1B\u68C0\u67E5\u7F51\u7EDC/\u8FDC\u7A0B\u53EF\u8FBE\u540E\u91CD\u8BD5\uFF0C\u786E\u8BA4\u5206\u652F\u5B58\u5728\u4E8E origin` };
785
+ }
786
+ if (!merged) {
787
+ return { status: "fail", detail: `origin/${base} \u5C1A\u672A\u5408\u5165\u5F53\u524D\u5206\u652F\u2014\u2014\u6309 ALIGN \u8282\u70B9\u8981\u6C42\u5408\u5E76\u57FA\u7840\u5206\u652F\u8FDC\u7A0B\u6700\u65B0\u4EE3\u7801\uFF08merge ${base} \u2192 \u5F53\u524D\u5206\u652F\uFF09\u540E\u91CD\u8BD5` };
788
+ }
789
+ return { status: "pass", detail: `origin/${base} \u5DF2\u5408\u5165 HEAD` };
790
+ });
748
791
  }
749
792
  function findWaivedDecision(ctx, nodeId) {
750
793
  const record = ctx.nodeRecords[nodeId];
@@ -755,16 +798,18 @@ async function checkWorktreeClean(ctx) {
755
798
  const type = "git-worktree-clean";
756
799
  const guard = naIfRepoUnavailable(ctx);
757
800
  if (guard) return { ...guard, type };
758
- const r = await ctx.runGit(["status", "--porcelain"], ctx.repoPath);
759
- if (!r.ok) {
760
- return fail2(ctx, type, `git status \u5931\u8D25\uFF1A${firstLine(r.stderr)}`, "\u68C0\u67E5\u4ED3\u5E93\u53EF\u7528\u6027");
761
- }
762
- if (r.stdout.trim() !== "") {
763
- const count = r.stdout.trim().split("\n").length;
764
- const sample = r.stdout.trim().split("\n").slice(0, 3).join("; ");
765
- return fail2(ctx, type, `\u5DE5\u4F5C\u6811\u4E0D\u5E72\u51C0\uFF08${count} \u5904\u672A\u63D0\u4EA4\u53D8\u66F4\uFF1A${sample}\uFF09`, "\u5B8C\u6210\u672C\u8282\u70B9\u6210\u679C\u63D0\u4EA4\uFF08\u6216\u5199 .gitignore / \u8C41\u514D\u7559\u75D5\uFF09\u540E\u518D\u63A8\u8FDB");
766
- }
767
- return { type, status: "pass", detail: "\u5DE5\u4F5C\u6811\u5E72\u51C0" };
801
+ return perRepoCheck(ctx, type, async (repo) => {
802
+ const r = await ctx.runGit(["status", "--porcelain"], repo);
803
+ if (!r.ok) {
804
+ return { status: "fail", detail: `git status \u5931\u8D25\uFF1A${firstLine(r.stderr)}\u2014\u2014\u68C0\u67E5\u4ED3\u5E93\u53EF\u7528\u6027` };
805
+ }
806
+ if (r.stdout.trim() !== "") {
807
+ const count = r.stdout.trim().split("\n").length;
808
+ const sample = r.stdout.trim().split("\n").slice(0, 3).join("; ");
809
+ return { status: "fail", detail: `\u5DE5\u4F5C\u6811\u4E0D\u5E72\u51C0\uFF08${count} \u5904\u672A\u63D0\u4EA4\u53D8\u66F4\uFF1A${sample}\uFF09\u2014\u2014\u5B8C\u6210\u672C\u8282\u70B9\u6210\u679C\u63D0\u4EA4\uFF08\u6216\u5199 .gitignore / \u8C41\u514D\u7559\u75D5\uFF09\u540E\u518D\u63A8\u8FDB` };
810
+ }
811
+ return { status: "pass", detail: "\u5DE5\u4F5C\u6811\u5E72\u51C0" };
812
+ });
768
813
  }
769
814
  async function checkArchiveMerged(ctx) {
770
815
  const type = "git-archive-merged";
@@ -785,29 +830,31 @@ async function checkArchiveMerged(ctx) {
785
830
  return fail2(ctx, type, "\u57FA\u7840\u5206\u652F\u672A\u77E5\uFF08ACCEPT \u524D\u7F6E\uFF1AALIGN \u5E94\u5DF2\u843D\u76D8 baseBranch\uFF09", "record-set \u5199 ALIGN.baseBranch\uFF0C\u6216 siming project update --vcs-base-branch <\u5206\u652F>");
786
831
  }
787
832
  const base = ctx.baseBranch;
788
- const count = await ctx.runGit(["rev-list", "--count", `${base}..HEAD`], ctx.repoPath);
789
- if (count.ok && count.stdout.trim() === "0") {
790
- return { type, status: "not_applicable", detail: `\u4EFB\u52A1\u5206\u652F\u76F8\u5BF9 ${base} \u96F6\u63D0\u4EA4\uFF08\u65E0\u4EE3\u7801\u6539\u52A8\uFF0C\u65E0\u9700\u5408\u5E76\uFF09` };
791
- }
792
- const fetch2 = await ctx.runGit(["fetch", "origin", base], ctx.repoPath);
793
- if (!fetch2.ok) {
794
- return fail2(ctx, type, `git fetch origin ${base} \u5931\u8D25\u2014\u2014\u672C\u5730\u4E0E\u8FDC\u7A0B\u4E00\u81F4\u6027\u65E0\u4ECE\u5224\u5B9A\uFF08\u62D2\u6309\u9648\u65E7 ref \u8BEF\u5224\uFF09`, "\u68C0\u67E5\u7F51\u7EDC/\u8FDC\u7A0B\u53EF\u8FBE\u540E\u91CD\u8BD5");
795
- }
796
- const ancestor = await ctx.runGit(["merge-base", "--is-ancestor", "HEAD", base], ctx.repoPath);
797
- if (!ancestor.ok) {
798
- return fail2(ctx, type, `\u57FA\u7840\u5206\u652F ${base} \u5C1A\u672A\u5305\u542B\u4EFB\u52A1\u5206\u652F HEAD\uFF08\u672A\u5408\u5E76\uFF09`, `git checkout ${base} && git merge --no-ff <\u4EFB\u52A1\u5206\u652F> && git push`);
799
- }
800
- const merges = await ctx.runGit(["log", "--merges", "-1", "--format=%P", base], ctx.repoPath);
801
- const parents = merges.ok ? merges.stdout.trim().split(/\s+/) : [];
802
- if (parents.length !== 2) {
803
- return fail2(ctx, type, `\u672A\u68C0\u6D4B\u5230 --no-ff \u5408\u5E76\u63D0\u4EA4\uFF08\u6700\u8FD1\u4E00\u6B21\u5408\u5E76 parents=${parents.length}\uFF09`, "\u5408\u5E76\u5FC5\u987B\u4F7F\u7528 --no-ff\uFF08\u4FDD\u7559\u5408\u5E76\u8BB0\u5F55\uFF09\uFF1Agit merge --no-ff <\u4EFB\u52A1\u5206\u652F>");
804
- }
805
- const localBase = await ctx.runGit(["rev-parse", base], ctx.repoPath);
806
- const remoteBase = await ctx.runGit(["rev-parse", `origin/${base}`], ctx.repoPath);
807
- if (!localBase.ok || !remoteBase.ok || localBase.stdout.trim() !== remoteBase.stdout.trim()) {
808
- return fail2(ctx, type, `\u672C\u5730 ${base} \u4E0E origin/${base} \u4E0D\u4E00\u81F4\uFF08\u5408\u5E76\u7ED3\u679C\u672A\u63A8\u9001\u6216\u672A\u62C9\u53D6\uFF09`, `git push origin ${base}\uFF08\u6216\u5148 pull \u5BF9\u9F50\uFF09`);
809
- }
810
- return { type, status: "pass", detail: `\u4EFB\u52A1\u5206\u652F\u5DF2 --no-ff \u5408\u5165 ${base} \u4E14\u5DF2\u4E0E\u8FDC\u7A0B\u4E00\u81F4` };
833
+ return perRepoCheck(ctx, type, async (repo) => {
834
+ const count = await ctx.runGit(["rev-list", "--count", `${base}..HEAD`], repo);
835
+ if (count.ok && count.stdout.trim() === "0") {
836
+ return { status: "not_applicable", detail: `\u4EFB\u52A1\u5206\u652F\u76F8\u5BF9 ${base} \u96F6\u63D0\u4EA4\uFF08\u65E0\u4EE3\u7801\u6539\u52A8\uFF0C\u65E0\u9700\u5408\u5E76\uFF09` };
837
+ }
838
+ const fetch2 = await ctx.runGit(["fetch", "origin", base], repo);
839
+ if (!fetch2.ok) {
840
+ return { status: "fail", detail: `git fetch origin ${base} \u5931\u8D25\u2014\u2014\u672C\u5730\u4E0E\u8FDC\u7A0B\u4E00\u81F4\u6027\u65E0\u4ECE\u5224\u5B9A\uFF08\u62D2\u6309\u9648\u65E7 ref \u8BEF\u5224\uFF09\uFF1B\u68C0\u67E5\u7F51\u7EDC/\u8FDC\u7A0B\u53EF\u8FBE\u540E\u91CD\u8BD5` };
841
+ }
842
+ const ancestor = await ctx.runGit(["merge-base", "--is-ancestor", "HEAD", base], repo);
843
+ if (!ancestor.ok) {
844
+ return { status: "fail", detail: `\u57FA\u7840\u5206\u652F ${base} \u5C1A\u672A\u5305\u542B\u4EFB\u52A1\u5206\u652F HEAD\uFF08\u672A\u5408\u5E76\uFF09\u2014\u2014git checkout ${base} && git merge --no-ff <\u4EFB\u52A1\u5206\u652F> && git push` };
845
+ }
846
+ const merges = await ctx.runGit(["log", "--merges", "-1", "--format=%P", base], repo);
847
+ const parents = merges.ok ? merges.stdout.trim().split(/\s+/) : [];
848
+ if (parents.length !== 2) {
849
+ return { status: "fail", detail: `\u672A\u68C0\u6D4B\u5230 --no-ff \u5408\u5E76\u63D0\u4EA4\uFF08\u6700\u8FD1\u4E00\u6B21\u5408\u5E76 parents=${parents.length}\uFF09\u2014\u2014\u5408\u5E76\u5FC5\u987B\u4F7F\u7528 --no-ff\uFF08\u4FDD\u7559\u5408\u5E76\u8BB0\u5F55\uFF09` };
850
+ }
851
+ const localBase = await ctx.runGit(["rev-parse", base], repo);
852
+ const remoteBase = await ctx.runGit(["rev-parse", `origin/${base}`], repo);
853
+ if (!localBase.ok || !remoteBase.ok || localBase.stdout.trim() !== remoteBase.stdout.trim()) {
854
+ return { status: "fail", detail: `\u672C\u5730 ${base} \u4E0E origin/${base} \u4E0D\u4E00\u81F4\uFF08\u5408\u5E76\u7ED3\u679C\u672A\u63A8\u9001\u6216\u672A\u62C9\u53D6\uFF09\u2014\u2014git push origin ${base}\uFF08\u6216\u5148 pull \u5BF9\u9F50\uFF09` };
855
+ }
856
+ return { status: "pass", detail: `\u5DF2 --no-ff \u5408\u5165 ${base} \u4E14\u4E0E\u8FDC\u7A0B\u4E00\u81F4` };
857
+ });
811
858
  }
812
859
  async function checkStayOnBase(ctx) {
813
860
  const type = "git-stay-on-base";
@@ -822,14 +869,17 @@ async function checkStayOnBase(ctx) {
822
869
  if (ctx.baseBranch === null) {
823
870
  return fail2(ctx, type, "\u57FA\u7840\u5206\u652F\u672A\u77E5\uFF08ARCHIVE \u524D\u7F6E\uFF1AALIGN \u5E94\u5DF2\u843D\u76D8 baseBranch\uFF09", "record-set \u5199 ALIGN.baseBranch\uFF0C\u6216 siming project update --vcs-base-branch <\u5206\u652F>");
824
871
  }
825
- const branch = await currentBranch(ctx);
826
- if (branch === null) {
827
- return fail2(ctx, type, "\u65E0\u6CD5\u8BFB\u53D6\u5F53\u524D\u5206\u652F", "\u786E\u8BA4\u4ED3\u5E93\u72B6\u6001");
828
- }
829
- if (branch !== ctx.baseBranch) {
830
- return fail2(ctx, type, `\u5F52\u6863\u540E\u5E94\u505C\u7559\u57FA\u7840\u5206\u652F\uFF1A\u5F53\u524D\u5728\u300C${branch}\u300D\uFF0C\u671F\u671B\u300C${ctx.baseBranch}\u300D`, `git checkout ${ctx.baseBranch}\uFF08\u7981\u6B62\u7ACB\u5373\u5F00\u59CB\u4E0B\u4E00\u4EFB\u52A1\u5206\u652F\uFF09`);
831
- }
832
- return { type, status: "pass", detail: `\u5F53\u524D\u505C\u7559\u5728\u57FA\u7840\u5206\u652F ${branch}` };
872
+ const base = ctx.baseBranch;
873
+ return perRepoCheck(ctx, type, async (repo) => {
874
+ const branch = await currentBranch(ctx, repo);
875
+ if (branch === null) {
876
+ return { status: "fail", detail: "\u65E0\u6CD5\u8BFB\u53D6\u5F53\u524D\u5206\u652F\u2014\u2014\u786E\u8BA4\u4ED3\u5E93\u72B6\u6001" };
877
+ }
878
+ if (branch !== base) {
879
+ return { status: "fail", detail: `\u5F52\u6863\u540E\u5E94\u505C\u7559\u57FA\u7840\u5206\u652F\uFF1A\u5F53\u524D\u5728\u300C${branch}\u300D\uFF0C\u671F\u671B\u300C${base}\u300D\u2014\u2014git checkout ${base}\uFF08\u7981\u6B62\u7ACB\u5373\u5F00\u59CB\u4E0B\u4E00\u4EFB\u52A1\u5206\u652F\uFF09` };
880
+ }
881
+ return { status: "pass", detail: `\u505C\u7559\u5728\u57FA\u7840\u5206\u652F ${branch}` };
882
+ });
833
883
  }
834
884
  function firstLine(text) {
835
885
  return text.trim().split("\n")[0] ?? "";
@@ -869,18 +919,21 @@ async function checkNoFakeGreen(ctx) {
869
919
  if (ctx.baseBranch === null) {
870
920
  return fail3(type, "\u57FA\u7840\u5206\u652F\u672A\u77E5\uFF08ALIGN.baseBranch \u672A\u843D\u76D8\u4E14\u9879\u76EE\u672A\u914D\u7F6E\uFF09", "record-set \u5199 ALIGN.baseBranch\uFF0C\u6216 siming project update --vcs-base-branch <\u5206\u652F>");
871
921
  }
872
- const diff = await ctx.runGit(
873
- ["diff", `${ctx.baseBranch}...HEAD`, "--", "**/*.test.ts", "**/*.spec.ts", "**/tests/**"],
874
- ctx.repoPath
875
- );
876
- if (!diff.ok) {
877
- return fail3(type, `\u6D4B\u8BD5\u6587\u4EF6 diff \u5931\u8D25\uFF1A${firstLine2(diff.stderr)}`, "\u68C0\u67E5\u4ED3\u5E93\u4E0E\u57FA\u7840\u5206\u652F\u53EF\u7528\u6027");
878
- }
879
- const violations = scanTestDiff(diff.stdout);
880
- if (violations.length > 0) {
881
- return fail3(type, `\u6D4B\u8BD5\u5B8C\u6574\u6027\u8FDD\u89C4\uFF1A${violations.join("\uFF1B")}`, "\u8FD8\u539F\u6D4B\u8BD5\u6539\u52A8\uFF1B\u786E\u5C5E\u5408\u6CD5\u8C03\u6574\u987B\u7ECF\u7528\u6237\u6279\u51C6\u540E record-decision-add \u5199 topic=test-adjust-approved");
882
- }
883
- return { type, status: "pass", detail: "\u6D4B\u8BD5\u6587\u4EF6\u65E0 skip/only \u65B0\u589E\u4E0E\u6574\u5220\uFF08\u653E\u5BBD\u65AD\u8A00\u5F62\u6001\u4E0D\u5728\u673A\u5668\u68C0\u6D4B\u9762\uFF0C\u4F9D\u8D56 test-adjust-approved \u7559\u75D5\u628A\u5173\uFF09" };
922
+ const base = ctx.baseBranch;
923
+ return perRepoCheck(ctx, type, async (repo) => {
924
+ const diff = await ctx.runGit(
925
+ ["diff", `${base}...HEAD`, "--", "**/*.test.ts", "**/*.spec.ts", "**/tests/**"],
926
+ repo
927
+ );
928
+ if (!diff.ok) {
929
+ return { status: "fail", detail: `\u6D4B\u8BD5\u6587\u4EF6 diff \u5931\u8D25\uFF1A${firstLine2(diff.stderr)}\u2014\u2014\u68C0\u67E5\u4ED3\u5E93\u4E0E\u57FA\u7840\u5206\u652F\u53EF\u7528\u6027` };
930
+ }
931
+ const violations = scanTestDiff(diff.stdout);
932
+ if (violations.length > 0) {
933
+ return { status: "fail", detail: `\u6D4B\u8BD5\u5B8C\u6574\u6027\u8FDD\u89C4\uFF1A${violations.join("\uFF1B")}\u2014\u2014\u8FD8\u539F\u6D4B\u8BD5\u6539\u52A8\uFF1B\u786E\u5C5E\u5408\u6CD5\u8C03\u6574\u987B\u7ECF\u7528\u6237\u6279\u51C6\u540E record-decision-add \u5199 topic=test-adjust-approved` };
934
+ }
935
+ return { status: "pass", detail: "\u6D4B\u8BD5\u6587\u4EF6\u65E0 skip/only \u65B0\u589E\u4E0E\u6574\u5220\uFF08\u653E\u5BBD\u65AD\u8A00\u5F62\u6001\u4E0D\u5728\u673A\u5668\u68C0\u6D4B\u9762\uFF0C\u4F9D\u8D56 test-adjust-approved \u7559\u75D5\u628A\u5173\uFF09" };
936
+ });
884
937
  }
885
938
  function scanTestDiff(patch) {
886
939
  const violations = [];
@@ -918,15 +971,18 @@ async function checkBaseReMerged(ctx) {
918
971
  if (ctx.baseBranch === null) {
919
972
  return fail3(type, "\u57FA\u7840\u5206\u652F\u672A\u77E5\uFF08ALIGN.baseBranch \u672A\u843D\u76D8\u4E14\u9879\u76EE\u672A\u914D\u7F6E\uFF09", "record-set \u5199 ALIGN.baseBranch\uFF0C\u6216 siming project update --vcs-base-branch <\u5206\u652F>");
920
973
  }
921
- const fetch2 = await ctx.runGit(["fetch", "origin", ctx.baseBranch], ctx.repoPath);
922
- if (!fetch2.ok) {
923
- return fail3(type, `git fetch origin ${ctx.baseBranch} \u5931\u8D25\u2014\u2014\u65E0\u6CD5\u4EE5\u8FDC\u7A0B\u6700\u65B0\u5F15\u7528\u5224\u5B9A\uFF08\u62D2\u6309\u9648\u65E7 ref \u8BEF\u5224\uFF09`, "\u68C0\u67E5\u7F51\u7EDC/\u8FDC\u7A0B\u53EF\u8FBE\u540E\u91CD\u8BD5");
924
- }
925
- const r = await ctx.runGit(["merge-base", "--is-ancestor", `origin/${ctx.baseBranch}`, "HEAD"], ctx.repoPath);
926
- if (!r.ok) {
927
- return fail3(type, `origin/${ctx.baseBranch} \u589E\u91CF\u672A\u518D\u5408\u5165\u5F53\u524D\u5206\u652F\uFF08\u96C6\u6210\u524D\u57FA\u7840\u5206\u652F\u6709\u65B0\u63D0\u4EA4\uFF09`, `merge ${ctx.baseBranch} \u2192 \u5F53\u524D\u5206\u652F\uFF08\u6D88\u89E3\u6F02\u79FB\uFF09\u540E\u518D\u63A8\u8FDB`);
928
- }
929
- return { type, status: "pass", detail: `origin/${ctx.baseBranch} \u5DF2\u518D\u5408\u5165 HEAD` };
974
+ const base = ctx.baseBranch;
975
+ return perRepoCheck(ctx, type, async (repo) => {
976
+ const fetch2 = await ctx.runGit(["fetch", "origin", base], repo);
977
+ if (!fetch2.ok) {
978
+ return { status: "fail", detail: `git fetch origin ${base} \u5931\u8D25\u2014\u2014\u65E0\u6CD5\u4EE5\u8FDC\u7A0B\u6700\u65B0\u5F15\u7528\u5224\u5B9A\uFF08\u62D2\u6309\u9648\u65E7 ref \u8BEF\u5224\uFF09\uFF1B\u68C0\u67E5\u7F51\u7EDC/\u8FDC\u7A0B\u53EF\u8FBE\u540E\u91CD\u8BD5` };
979
+ }
980
+ const r = await ctx.runGit(["merge-base", "--is-ancestor", `origin/${base}`, "HEAD"], repo);
981
+ if (!r.ok) {
982
+ return { status: "fail", detail: `origin/${base} \u589E\u91CF\u672A\u518D\u5408\u5165\u5F53\u524D\u5206\u652F\uFF08\u96C6\u6210\u524D\u57FA\u7840\u5206\u652F\u6709\u65B0\u63D0\u4EA4\uFF09\u2014\u2014merge ${base} \u2192 \u5F53\u524D\u5206\u652F\uFF08\u6D88\u89E3\u6F02\u79FB\uFF09\u540E\u518D\u63A8\u8FDB` };
983
+ }
984
+ return { status: "pass", detail: `origin/${base} \u5DF2\u518D\u5408\u5165 HEAD` };
985
+ });
930
986
  }
931
987
  async function checkFixReviewPass(ctx) {
932
988
  const type = "record-fix-review-pass";
@@ -948,11 +1004,25 @@ async function checkVerifiedCommit(ctx) {
948
1004
  }
949
1005
  const guard = naIfRepoUnavailable(ctx);
950
1006
  if (guard) return { ...guard, type };
951
- const r = await ctx.runGit(["cat-file", "-e", sha], ctx.repoPath);
952
- if (!r.ok) {
953
- return fail3(type, `verifiedCommit\u300C${sha}\u300D\u5728\u4ED3\u5E93\u4E2D\u4E0D\u5B58\u5728\uFF08cat-file \u5931\u8D25\uFF09`, "record-set \u4FEE\u6B63 verifiedCommit \u4E3A\u771F\u5B9E\u5B58\u5728\u7684\u63D0\u4EA4\u53F7");
1007
+ const hitRepos = [];
1008
+ for (const repo of ctx.repos) {
1009
+ const r = await ctx.runGit(["cat-file", "-e", sha], repo);
1010
+ if (r.ok) hitRepos.push(repo);
1011
+ }
1012
+ if (hitRepos.length > 0) {
1013
+ return {
1014
+ type,
1015
+ status: "pass",
1016
+ detail: `\u9A8C\u8BC1\u7248\u672C ${sha} \u5B58\u5728\uFF08\u547D\u4E2D\u4ED3 ${hitRepos.join("\u3001")}\uFF09
1017
+ ${ctx.reposScanDetail}`
1018
+ };
954
1019
  }
955
- return { type, status: "pass", detail: `\u9A8C\u8BC1\u7248\u672C ${sha} \u5B58\u5728` };
1020
+ return fail3(
1021
+ type,
1022
+ `verifiedCommit\u300C${sha}\u300D\u5728 R \u5168\u90E8\u4ED3\u4E2D\u4E0D\u5B58\u5728\uFF08${ctx.repos.join("\u3001")}\uFF09`,
1023
+ `record-set \u4FEE\u6B63 verifiedCommit \u4E3A\u771F\u5B9E\u5B58\u5728\u7684\u63D0\u4EA4\u53F7
1024
+ ${ctx.reposScanDetail}`
1025
+ );
956
1026
  }
957
1027
  async function checkPrevNodesDone(ctx) {
958
1028
  const type = "record-prev-nodes-done";
@@ -997,24 +1067,58 @@ async function checkVersionConsistent(ctx) {
997
1067
  if (ctx.baseBranch === null) {
998
1068
  return fail3(type, "\u57FA\u7840\u5206\u652F\u672A\u77E5\uFF08ALIGN.baseBranch \u672A\u843D\u76D8\u4E14\u9879\u76EE\u672A\u914D\u7F6E\uFF09", "record-set \u5199 ALIGN.baseBranch\uFF0C\u6216 siming project update --vcs-base-branch <\u5206\u652F>");
999
1069
  }
1000
- const r = await ctx.runGit(["diff", "--name-only", `${sha}..${ctx.baseBranch}`], ctx.repoPath);
1001
- if (!r.ok) {
1002
- return fail3(type, `\u7248\u672C diff \u5931\u8D25\uFF1A${firstLine2(r.stderr)}`, "\u68C0\u67E5 verifiedCommit \u4E0E\u57FA\u7840\u5206\u652F\u53EF\u7528\u6027");
1070
+ const base = ctx.baseBranch;
1071
+ const hitRepos = [];
1072
+ for (const repo of ctx.repos) {
1073
+ const probe = await ctx.runGit(["cat-file", "-e", sha], repo);
1074
+ if (probe.ok) hitRepos.push(repo);
1075
+ }
1076
+ if (hitRepos.length === 0) {
1077
+ return fail3(
1078
+ type,
1079
+ `verifiedCommit\u300C${sha}\u300D\u5728 R \u5168\u90E8\u4ED3\u4E2D\u4E0D\u5B58\u5728\uFF08${ctx.repos.join("\u3001")}\uFF09`,
1080
+ `\u56DE\u8865 INTEGRATE record-set verifiedCommit \u4E3A\u4EFB\u52A1\u4ED3\u771F\u5B9E\u63D0\u4EA4\u53F7\u540E\u91CD\u8BD5
1081
+ ${ctx.reposScanDetail}`
1082
+ );
1003
1083
  }
1004
1084
  const exempt = (path) => ctx.docPaths.some((p) => {
1005
1085
  if (p.endsWith("/")) return path.startsWith(p);
1006
1086
  if (p.startsWith("*")) return path.endsWith(p.slice(1));
1007
1087
  return path === p;
1008
1088
  });
1009
- const leaked = r.stdout.split("\n").map((l) => l.trim()).filter((l) => l !== "" && !exempt(l));
1010
- if (leaked.length > 0) {
1089
+ const leakByRepo = [];
1090
+ const errRepos = [];
1091
+ for (const repo of hitRepos) {
1092
+ const r = await ctx.runGit(["diff", "--name-only", `${sha}..${base}`], repo);
1093
+ if (!r.ok) {
1094
+ errRepos.push(repo);
1095
+ continue;
1096
+ }
1097
+ const leaked = r.stdout.split("\n").map((l) => l.trim()).filter((l) => l !== "" && !exempt(l));
1098
+ if (leaked.length > 0) leakByRepo.push({ repo, leaked });
1099
+ }
1100
+ if (errRepos.length > 0) {
1101
+ return fail3(type, `\u7248\u672C diff \u5931\u8D25\uFF08\u4ED3 ${errRepos.join("\u3001")}\uFF09`, `\u68C0\u67E5 verifiedCommit \u4E0E\u57FA\u7840\u5206\u652F\u53EF\u7528\u6027
1102
+ ${ctx.reposScanDetail}`);
1103
+ }
1104
+ if (leakByRepo.length > 0) {
1105
+ const lines = leakByRepo.slice(0, 3).map((x) => `${x.repo}: ${x.leaked.slice(0, 5).join(", ")}${x.leaked.length > 5 ? ` \u7B49 ${x.leaked.length} \u4E2A` : ""}`);
1106
+ const suffix = leakByRepo.length > 3 ? `\uFF08\u53E6\u6709 ${leakByRepo.length - 3} \u4ED3\u6CC4\u6F0F\u89C1\u9010\u4ED3\u91CD\u67E5\uFF09` : "";
1011
1107
  return fail3(
1012
1108
  type,
1013
- `verifiedCommit \u4E0E ${ctx.baseBranch} \u5B58\u5728\u975E\u6587\u6863\u5DEE\u5F02\uFF08${leaked.slice(0, 5).join(", ")}${leaked.length > 5 ? ` \u7B49 ${leaked.length} \u4E2A` : ""}\uFF09`,
1014
- "\u4EE3\u7801\u6539\u52A8\u987B\u5408\u5E76\u8FDB\u57FA\u7840\u5206\u652F\uFF08\u542B\u5E76\u884C\u4EFB\u52A1 base \u524D\u8FDB\u573A\u666F\uFF1A\u91CD\u8D70\u5408\u5E76\u6216\u7ECF\u7528\u6237\u660E\u793A\u540E force \u63A8\u8FDB\u5E76\u7559\u75D5\uFF09"
1109
+ `verifiedCommit \u4E0E ${base} \u5B58\u5728\u975E\u6587\u6863\u5DEE\u5F02\u2014\u2014${lines.join("\uFF1B")}${suffix}`,
1110
+ `\u4EE3\u7801\u6539\u52A8\u987B\u5408\u5E76\u8FDB\u57FA\u7840\u5206\u652F\uFF08\u542B\u5E76\u884C\u4EFB\u52A1 base \u524D\u8FDB\u573A\u666F\uFF1A\u91CD\u8D70\u5408\u5E76\u6216\u7ECF\u7528\u6237\u660E\u793A\u540E force \u63A8\u8FDB\u5E76\u7559\u75D5\uFF09
1111
+ ${ctx.reposScanDetail}`
1015
1112
  );
1016
1113
  }
1017
- return { type, status: "pass", detail: `verifiedCommit ${sha} \u4E0E ${ctx.baseBranch} \u4E00\u81F4\uFF08\u6587\u6863\u8DEF\u5F84\u8C41\u514D ${ctx.docPaths.join(" ")}\uFF09` };
1114
+ const naRepos = ctx.repos.filter((r) => !hitRepos.includes(r));
1115
+ const naPart = naRepos.length > 0 ? `\uFF1B\u65E0\u951A\u70B9\u4ED3\uFF08\u4E0D\u542B\u8BE5 sha\uFF0C\u514D\u5224\uFF09\uFF1A${naRepos.join("\u3001")}` : "";
1116
+ return {
1117
+ type,
1118
+ status: "pass",
1119
+ detail: `verifiedCommit ${sha} \u4E0E ${base} \u4E00\u81F4\uFF08\u547D\u4E2D\u4ED3 ${hitRepos.join("\u3001")}\uFF1B\u6587\u6863\u8DEF\u5F84\u8C41\u514D ${ctx.docPaths.join(" ")}${naPart}\uFF09
1120
+ ${ctx.reposScanDetail}`
1121
+ };
1018
1122
  }
1019
1123
  async function checkDeferredIssues(ctx) {
1020
1124
  const type = "record-deferred-issues";
@@ -1038,17 +1142,21 @@ async function checkEvidenceFiles(ctx) {
1038
1142
  if (files.length === 0) {
1039
1143
  return fail3(type, "\u672C\u8282\u70B9\u8BB0\u5F55\u672A\u5199 evidenceFiles\uFF08\u89C6\u89C9\u9A8C\u6536\u8BC1\u636E\u7F3A\u5931\uFF09", "record-set \u5199 evidenceFiles\uFF08\u622A\u56FE/\u5F55\u5C4F\u76F8\u5BF9\u8DEF\u5F84\u6E05\u5355\uFF09");
1040
1144
  }
1041
- if (ctx.repoPath === null) {
1042
- return { type, status: "not_applicable", detail: `\u4ED3\u5E93\u4E0D\u53EF\u7528\uFF1A${ctx.repoProbeDetail}` };
1043
- }
1145
+ const guard = naIfRepoUnavailable(ctx);
1146
+ if (guard) return { ...guard, type };
1044
1147
  const missing = [];
1148
+ const hits = [];
1045
1149
  for (const rel of files) {
1046
- if (!ctx.fileExists(`${ctx.repoPath}/${rel}`)) missing.push(rel);
1150
+ const hit = ctx.repos.find((repo) => ctx.fileExists(`${repo}/${rel}`));
1151
+ if (hit === void 0) missing.push(rel);
1152
+ else hits.push({ rel, repo: hit });
1047
1153
  }
1048
1154
  if (missing.length > 0) {
1049
- return fail3(type, `\u8BC1\u636E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${missing.join(", ")}`, "\u8865\u9F50\u8BC1\u636E\u6587\u4EF6\u6216 record-set \u4FEE\u6B63 evidenceFiles \u8DEF\u5F84");
1155
+ return fail3(type, `\u8BC1\u636E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF08${missing.join(", ")}\u2014\u2014R \u5168\u90E8\u4ED3\u67E5\u627E\u65E0\u679C\uFF09`, `\u8865\u9F50\u8BC1\u636E\u6587\u4EF6\u6216 record-set \u4FEE\u6B63 evidenceFiles \u8DEF\u5F84
1156
+ ${ctx.reposScanDetail}`);
1050
1157
  }
1051
- return { type, status: "pass", detail: `\u8BC1\u636E\u6587\u4EF6 ${files.length} \u4E2A\u5747\u5B58\u5728` };
1158
+ return { type, status: "pass", detail: `\u8BC1\u636E\u6587\u4EF6 ${files.length} \u4E2A\u5747\u5B58\u5728\uFF08${hits.map((h) => `${h.rel} @ ${h.repo}`).join("\uFF1B")}\uFF09
1159
+ ${ctx.reposScanDetail}` };
1052
1160
  }
1053
1161
  async function checkFinalConverged(ctx) {
1054
1162
  const type = "record-final-converged";
@@ -1153,6 +1261,64 @@ function auditAiChecks(aiChecks, record) {
1153
1261
  import { DEFAULT_VCS_DOC_PATHS } from "@siming-org/core";
1154
1262
  import { execFile } from "child_process";
1155
1263
  import { promisify } from "util";
1264
+
1265
+ // src/config/workspace-repos.ts
1266
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync5 } from "fs";
1267
+ import { join as join5 } from "path";
1268
+ var EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set(["temp", "node_modules", "dist", "build"]);
1269
+ var DEFAULT_SCAN_DEPTH = 1;
1270
+ var MAX_SCAN_DEPTH = 3;
1271
+ var WORKSPACE_DIR_NAME3 = ".siming";
1272
+ var DEV_JSON_FILE2 = "dev.json";
1273
+ function normalizeScanDepth(raw) {
1274
+ if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
1275
+ return DEFAULT_SCAN_DEPTH;
1276
+ }
1277
+ if (raw <= 0) return DEFAULT_SCAN_DEPTH;
1278
+ return Math.min(raw, MAX_SCAN_DEPTH);
1279
+ }
1280
+ function readScanDepth(cwd) {
1281
+ const path = join5(cwd, WORKSPACE_DIR_NAME3, DEV_JSON_FILE2);
1282
+ try {
1283
+ if (!existsSync5(path)) return DEFAULT_SCAN_DEPTH;
1284
+ const parsed = JSON.parse(readFileSync5(path, "utf-8"));
1285
+ return normalizeScanDepth(parsed.scanDepth);
1286
+ } catch {
1287
+ return DEFAULT_SCAN_DEPTH;
1288
+ }
1289
+ }
1290
+ function isGitRepo(dir) {
1291
+ return existsSync5(join5(dir, ".git"));
1292
+ }
1293
+ function isExcludedDir(name) {
1294
+ return EXCLUDED_DIR_NAMES.has(name) || name.startsWith(".");
1295
+ }
1296
+ function scanWorkspaceRepos(cwd, scanDepthOverride) {
1297
+ const scanDepth = scanDepthOverride !== void 0 ? normalizeScanDepth(scanDepthOverride) : readScanDepth(cwd);
1298
+ const repos = [];
1299
+ let scannedDirs = 0;
1300
+ const queue = [{ dir: cwd, depth: 0 }];
1301
+ while (queue.length > 0) {
1302
+ const { dir, depth } = queue.shift();
1303
+ if (isGitRepo(dir)) repos.push(dir);
1304
+ if (depth >= scanDepth) continue;
1305
+ let entries;
1306
+ try {
1307
+ entries = readdirSync(dir, { withFileTypes: true });
1308
+ } catch {
1309
+ continue;
1310
+ }
1311
+ for (const entry of entries) {
1312
+ if (!entry.isDirectory()) continue;
1313
+ scannedDirs++;
1314
+ if (isExcludedDir(entry.name)) continue;
1315
+ queue.push({ dir: join5(dir, entry.name), depth: depth + 1 });
1316
+ }
1317
+ }
1318
+ return { repos, scannedDirs, scanDepth };
1319
+ }
1320
+
1321
+ // src/client/checks/param-resolver.ts
1156
1322
  var execFileAsync = promisify(execFile);
1157
1323
  var defaultGitRunner = async (args, cwd) => {
1158
1324
  try {
@@ -1163,35 +1329,22 @@ var defaultGitRunner = async (args, cwd) => {
1163
1329
  return { ok: false, stdout: e.stdout ?? "", stderr: e.stderr ?? e.message ?? String(err2) };
1164
1330
  }
1165
1331
  };
1166
- async function resolveCheckContextWithRunner(base, vcs, cwd, runGit) {
1167
- const probe = vcs.repoPath !== void 0 ? await probeRepo(vcs.repoPath, runGit) : await probeRepo(cwd, runGit);
1332
+ async function resolveCheckContextWithRunner(base, vcs, cwd, scan) {
1333
+ const result = scan !== void 0 ? scan(cwd) : scanWorkspaceRepos(cwd);
1168
1334
  const alignRecord = base.nodeRecords["ALIGN"];
1169
1335
  const baseBranch = (alignRecord?.baseBranch !== void 0 && alignRecord.baseBranch !== "" ? alignRecord.baseBranch : void 0) ?? vcs.baseBranch ?? null;
1170
1336
  return {
1171
1337
  ...base,
1172
- repoPath: probe.repoPath,
1173
- repoProbeDetail: probe.detail,
1338
+ repos: result.repos,
1339
+ reposScanDetail: `scanned from: ${cwd}\uFF08\u5BBF\u4E3B\u542F\u52A8 cwd\u2014\u2014\u5B50\u76EE\u5F55\u542F\u52A8\u4F1A\u9759\u9ED8\u6F0F\u4ED3\uFF09\uFF1Brepos(${result.repos.length}): ${result.repos.join(", ") || "\u65E0"}\uFF1BscanDepth=${result.scanDepth}\uFF1BscannedDirs=${result.scannedDirs}`,
1174
1340
  baseBranch,
1175
1341
  branchPattern: vcs.branchPattern ?? "feature/",
1176
1342
  docPaths: vcs.docPaths ?? [...DEFAULT_VCS_DOC_PATHS]
1177
1343
  };
1178
1344
  }
1179
- async function probeRepo(candidate, runGit) {
1180
- const r = await runGit(["rev-parse", "--show-toplevel"], candidate);
1181
- if (!r.ok) {
1182
- return {
1183
- repoPath: null,
1184
- detail: `\u4ED3\u5E93\u63A2\u6D4B\u5931\u8D25\uFF1ArepoPath \u5019\u9009\u300C${candidate}\u300D\uFF08git rev-parse --show-toplevel \u5931\u8D25\uFF1A${firstLine3(r.stderr)}\uFF09`
1185
- };
1186
- }
1187
- return { repoPath: firstLine3(r.stdout), detail: "" };
1188
- }
1189
- function firstLine3(text) {
1190
- return text.trim().split("\n")[0] ?? "";
1191
- }
1192
1345
 
1193
1346
  // src/client/checks/runner.ts
1194
- import { existsSync as existsSync5 } from "fs";
1347
+ import { existsSync as existsSync6 } from "fs";
1195
1348
  function gateHasFailure(report) {
1196
1349
  return report.toolOutcomes.some((o) => o.status === "fail") || report.aiOutcomes.some((o) => o.status === "fail");
1197
1350
  }
@@ -1243,11 +1396,10 @@ async function runNodeGate(client, taskId) {
1243
1396
  ids.delete("");
1244
1397
  return { ok: true, ids };
1245
1398
  },
1246
- fileExists: (p) => existsSync5(p)
1399
+ fileExists: (p) => existsSync6(p)
1247
1400
  },
1248
1401
  vcs,
1249
- process.cwd(),
1250
- defaultGitRunner
1402
+ process.cwd()
1251
1403
  );
1252
1404
  const toolOutcomes = [];
1253
1405
  for (const toolCheck of toolChecks) {
@@ -1576,7 +1728,7 @@ async function resolveArtifactBatchEntries(entries) {
1576
1728
  let content = inlineContent;
1577
1729
  if (filePath !== void 0) {
1578
1730
  try {
1579
- content = readFileSync5(filePath, "utf-8");
1731
+ content = readFileSync6(filePath, "utf-8");
1580
1732
  } catch (e) {
1581
1733
  localFailures.push({ index, message: `\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${filePath}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09` });
1582
1734
  continue;
@@ -1662,7 +1814,7 @@ async function handleArchNoteAddBatch(client, taskId, texts) {
1662
1814
  }
1663
1815
 
1664
1816
  // src/client/handlers/dag.ts
1665
- import { readFileSync as readFileSync6 } from "fs";
1817
+ import { readFileSync as readFileSync7 } from "fs";
1666
1818
  import { parse as parseYaml } from "yaml";
1667
1819
  import {
1668
1820
  BIZ_CODE_MESSAGES,
@@ -1884,13 +2036,13 @@ async function handleUpgradeApply(client, templateId, decisions) {
1884
2036
 
1885
2037
  // src/client/handlers/auth.ts
1886
2038
  import {
1887
- existsSync as existsSync6,
1888
- readFileSync as readFileSync7,
2039
+ existsSync as existsSync7,
2040
+ readFileSync as readFileSync8,
1889
2041
  writeFileSync as writeFileSync4,
1890
2042
  renameSync as renameSync3,
1891
2043
  mkdirSync as mkdirSync4
1892
2044
  } from "fs";
1893
- import { join as join5 } from "path";
2045
+ import { join as join6 } from "path";
1894
2046
  import { isAbsolute as isAbsolute2 } from "path";
1895
2047
  async function handleWhoami(client) {
1896
2048
  const res = await client.getJson("/api/auth/whoami");
@@ -1973,12 +2125,12 @@ function writeWorkspaceLayer(token, kind, opts) {
1973
2125
  hint: "\u5728\u9879\u76EE\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\uFF08\u542B .siming/\uFF09\u6267\u884C\uFF0C\u6216\u5148\u521B\u5EFA .siming/ \u76EE\u5F55\uFF1B\u7528\u6237\u7EA7\u5168\u5C40\u914D\u7F6E\u53EF\u52A0 --global\uFF08\u4EFB\u610F\u76EE\u5F55\u53EF\u5199\uFF09"
1974
2126
  });
1975
2127
  }
1976
- const devJsonPath = join5(dir, ".siming", "dev.json");
2128
+ const devJsonPath = join6(dir, ".siming", "dev.json");
1977
2129
  let config = {};
1978
2130
  let hadIsolationKeys = false;
1979
- if (existsSync6(devJsonPath)) {
2131
+ if (existsSync7(devJsonPath)) {
1980
2132
  try {
1981
- config = JSON.parse(readFileSync7(devJsonPath, "utf-8"));
2133
+ config = JSON.parse(readFileSync8(devJsonPath, "utf-8"));
1982
2134
  hadIsolationKeys = "serverPort" in config || "dbPrefix" in config;
1983
2135
  } catch {
1984
2136
  return fail({
@@ -1997,7 +2149,7 @@ function writeWorkspaceLayer(token, kind, opts) {
1997
2149
  });
1998
2150
  }
1999
2151
  config.authToken = token;
2000
- mkdirSync4(join5(dir, ".siming"), { recursive: true });
2152
+ mkdirSync4(join6(dir, ".siming"), { recursive: true });
2001
2153
  const tmp = `${devJsonPath}.tmp`;
2002
2154
  writeFileSync4(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
2003
2155
  renameSync3(tmp, devJsonPath);
@@ -2739,7 +2891,7 @@ function createMcpServer() {
2739
2891
  let fullText = content;
2740
2892
  if (file) {
2741
2893
  try {
2742
- fullText = readFileSync8(file, "utf-8");
2894
+ fullText = readFileSync9(file, "utf-8");
2743
2895
  } catch (e) {
2744
2896
  return localError(`\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${file}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`);
2745
2897
  }
@@ -2978,7 +3130,7 @@ async function mcpServeCommand() {
2978
3130
 
2979
3131
  // src/commands/task/command.ts
2980
3132
  import { Command as Command2 } from "commander";
2981
- import { readFileSync as readFileSync10 } from "fs";
3133
+ import { readFileSync as readFileSync11 } from "fs";
2982
3134
  import { z as z5 } from "zod";
2983
3135
  import {
2984
3136
  ARTIFACT_TYPES as ARTIFACT_TYPES3,
@@ -3092,7 +3244,7 @@ function renderResult(format, result, opts) {
3092
3244
  }
3093
3245
 
3094
3246
  // src/commands/opts.ts
3095
- import { readFileSync as readFileSync9 } from "fs";
3247
+ import { readFileSync as readFileSync10 } from "fs";
3096
3248
  import "commander";
3097
3249
  import { z as z4 } from "zod";
3098
3250
  import { canonicalizeReferencePath, createReferencesSchema } from "@siming-org/core";
@@ -3147,7 +3299,7 @@ function parseRefFlags(refs, mainDocName) {
3147
3299
  seen.add(canonicalizeReferencePath(relPath));
3148
3300
  let content;
3149
3301
  try {
3150
- content = readFileSync9(localPath, "utf-8");
3302
+ content = readFileSync10(localPath, "utf-8");
3151
3303
  } catch (e) {
3152
3304
  return fail({
3153
3305
  message: `--ref \u672C\u5730\u6587\u4EF6\u4E0D\u53EF\u8BFB\uFF1A"${localPath}"`,
@@ -3641,7 +3793,7 @@ function createTaskCommand() {
3641
3793
  let content;
3642
3794
  if (opts.file) {
3643
3795
  try {
3644
- content = readFileSync10(opts.file, "utf-8");
3796
+ content = readFileSync11(opts.file, "utf-8");
3645
3797
  } catch (e) {
3646
3798
  renderResult(opts.format, fail({ message: `\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${opts.file}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`, httpStatus: 0 }));
3647
3799
  return;
@@ -3896,18 +4048,18 @@ import { Command as Command3 } from "commander";
3896
4048
  import { SKILL_MAIN_DOC, SkillScopeSchema } from "@siming-org/core";
3897
4049
 
3898
4050
  // src/client/scaffold-templates.ts
3899
- import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync11 } from "fs";
3900
- import { dirname as dirname3, join as join6 } from "path";
4051
+ import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync12 } from "fs";
4052
+ import { dirname as dirname3, join as join7 } from "path";
3901
4053
  import { fileURLToPath } from "url";
3902
- var SCAFFOLD_DIR = join6("templates", "project-skills");
4054
+ var SCAFFOLD_DIR = join7("templates", "project-skills");
3903
4055
  var KEBAB_CASE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
3904
4056
  function resolveCliPackageRoot() {
3905
4057
  let dir = dirname3(fileURLToPath(import.meta.url));
3906
4058
  for (let depth = 0; depth < 10; depth += 1) {
3907
- const pkgFile = join6(dir, "package.json");
3908
- if (existsSync7(pkgFile)) {
4059
+ const pkgFile = join7(dir, "package.json");
4060
+ if (existsSync8(pkgFile)) {
3909
4061
  try {
3910
- const pkg = JSON.parse(readFileSync11(pkgFile, "utf-8"));
4062
+ const pkg = JSON.parse(readFileSync12(pkgFile, "utf-8"));
3911
4063
  if (pkg.name === "@siming-org/cli") return ok2(dir);
3912
4064
  } catch {
3913
4065
  }
@@ -3925,10 +4077,10 @@ function resolveCliPackageRoot() {
3925
4077
  function listScaffoldTemplates() {
3926
4078
  const root = resolveCliPackageRoot();
3927
4079
  if (!root.success) return root;
3928
- const dir = join6(root.data, SCAFFOLD_DIR);
4080
+ const dir = join7(root.data, SCAFFOLD_DIR);
3929
4081
  let entries;
3930
4082
  try {
3931
- entries = readdirSync(dir);
4083
+ entries = readdirSync2(dir);
3932
4084
  } catch {
3933
4085
  return fail({
3934
4086
  message: `\u9AA8\u67B6\u6A21\u677F\u76EE\u5F55\u7F3A\u5931\uFF1A${SCAFFOLD_DIR}`,
@@ -3987,7 +4139,7 @@ function loadScaffoldTemplate(name) {
3987
4139
  if (!root.success) return root;
3988
4140
  let text;
3989
4141
  try {
3990
- text = readFileSync11(join6(root.data, SCAFFOLD_DIR, `${name}.md`), "utf-8");
4142
+ text = readFileSync12(join7(root.data, SCAFFOLD_DIR, `${name}.md`), "utf-8");
3991
4143
  } catch (e) {
3992
4144
  return fail({
3993
4145
  message: `\u9AA8\u67B6\u6587\u4EF6\u4E0D\u53EF\u8BFB\uFF1A${SCAFFOLD_DIR}/${name}.md`,
@@ -4809,7 +4961,7 @@ function agentFields(a) {
4809
4961
  }
4810
4962
 
4811
4963
  // src/commands/dag/command.ts
4812
- import { readFileSync as readFileSync12 } from "fs";
4964
+ import { readFileSync as readFileSync13 } from "fs";
4813
4965
  import { Command as Command5 } from "commander";
4814
4966
  function createDagCommand() {
4815
4967
  const dag = new Command5("dag").description("DAG template & instance management");
@@ -4867,7 +5019,7 @@ function createDagCommand() {
4867
5019
  const client = createClient(opts.url);
4868
5020
  let text;
4869
5021
  try {
4870
- text = readFileSync12(file, "utf-8");
5022
+ text = readFileSync13(file, "utf-8");
4871
5023
  } catch (e) {
4872
5024
  renderResult("json", fail({
4873
5025
  message: `\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${file}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
@@ -5213,7 +5365,7 @@ function diffSummary(item) {
5213
5365
  }
5214
5366
 
5215
5367
  // src/commands/node/command.ts
5216
- import { readFileSync as readFileSync13 } from "fs";
5368
+ import { readFileSync as readFileSync14 } from "fs";
5217
5369
  import { Command as Command6 } from "commander";
5218
5370
  import { SkillScopeSchema as SkillScopeSchema2 } from "@siming-org/core";
5219
5371
 
@@ -5451,7 +5603,7 @@ function createNodeCommand() {
5451
5603
  let prompt = opts.prompt ?? "";
5452
5604
  if (opts.promptFile !== void 0) {
5453
5605
  try {
5454
- prompt = readFileSync13(opts.promptFile, "utf-8");
5606
+ prompt = readFileSync14(opts.promptFile, "utf-8");
5455
5607
  } catch (e) {
5456
5608
  renderResult("json", fail({
5457
5609
  message: `prompt \u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${opts.promptFile}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
@@ -5496,7 +5648,7 @@ function createNodeCommand() {
5496
5648
  let prompt;
5497
5649
  if (opts.promptFile !== void 0) {
5498
5650
  try {
5499
- prompt = readFileSync13(opts.promptFile, "utf-8");
5651
+ prompt = readFileSync14(opts.promptFile, "utf-8");
5500
5652
  } catch (e) {
5501
5653
  renderResult("json", fail({
5502
5654
  message: `prompt \u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${opts.promptFile}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
@@ -5511,7 +5663,7 @@ function createNodeCommand() {
5511
5663
  if (opts.checksFile !== void 0) {
5512
5664
  let parsed;
5513
5665
  try {
5514
- parsed = JSON.parse(readFileSync13(opts.checksFile, "utf-8"));
5666
+ parsed = JSON.parse(readFileSync14(opts.checksFile, "utf-8"));
5515
5667
  } catch (e) {
5516
5668
  renderResult("json", fail({
5517
5669
  message: `checks \u6587\u4EF6\u8BFB\u53D6/\u89E3\u6790\u5931\u8D25: ${opts.checksFile}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
@@ -5619,7 +5771,7 @@ function nodeFields2(n) {
5619
5771
  import { execFile as execFile2 } from "child_process";
5620
5772
  import { mkdtempSync, rmSync as rmSync2 } from "fs";
5621
5773
  import { tmpdir } from "os";
5622
- import { join as join7 } from "path";
5774
+ import { join as join8 } from "path";
5623
5775
  import { promisify as promisify2 } from "util";
5624
5776
  import { createInterface } from "readline/promises";
5625
5777
  import { Command as Command7 } from "commander";
@@ -6061,7 +6213,7 @@ ${conflicts.map((item) => ` ${item.kind}:${item.key} \u2014 ${item.reason ?? "\
6061
6213
  async function fetchManifest(pkgSpec, registry) {
6062
6214
  let dir;
6063
6215
  try {
6064
- dir = mkdtempSync(join7(tmpdir(), "siming-flow-"));
6216
+ dir = mkdtempSync(join8(tmpdir(), "siming-flow-"));
6065
6217
  } catch (error) {
6066
6218
  return fail({
6067
6219
  message: `\u4E34\u65F6\u76EE\u5F55\u521B\u5EFA\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`,
@@ -6098,7 +6250,7 @@ async function fetchManifest(pkgSpec, registry) {
6098
6250
  }
6099
6251
  const { stdout: manifestText } = await execFileAsync2(
6100
6252
  "tar",
6101
- ["-xOf", join7(dir, filename), "package/siming-nodes.json"],
6253
+ ["-xOf", join8(dir, filename), "package/siming-nodes.json"],
6102
6254
  { maxBuffer: MANIFEST_LIMITS.maxChars * 2 + 64 * 1024 }
6103
6255
  );
6104
6256
  const parsed = parseFlowManifest(manifestText);
@@ -6969,7 +7121,7 @@ function createProjectCommand() {
6969
7121
  const result = await handleGetProject(client, id);
6970
7122
  renderResult(opts.format, result, { fields: result.success ? projectFields(result.data) : [] });
6971
7123
  });
6972
- project.command("update <id>").description("Update a project (name/description/status/vcs gate config). vcs is replaced as a whole: any --vcs-* flag replaces the entire vcs object with only the provided fields").option("--url <url>", "server base URL").option("--name <name>", "new name").option("--description <text>", "new description").option("--vcs-enabled <bool>", "gate VCS checks for this project (true/false; false=all git checks N/A)").option("--vcs-repo-path <path>", "repository absolute path (default: MCP host cwd probing)").option("--vcs-base-branch <branch>", "base branch (2nd level of the base-branch resolution chain)").option("--vcs-branch-pattern <pattern>", "task branch prefix pattern for A1").option("--vcs-worktree-mode <bool>", "worktree parallel mode (true=A6 stay-on-base N/A)").option("--vcs-archive-to-base <bool>", "project declares merge-back-to-base archive flow (false=A5 N/A)").option("--vcs-doc-paths <paths>", "B7 exemption paths, comma separated (dirs need trailing /, glob like *.md)").action(async (id, opts) => {
7124
+ project.command("update <id>").description("Update a project (name/description/status/vcs gate config). vcs is replaced as a whole: any --vcs-* flag replaces the entire vcs object with only the provided fields").option("--url <url>", "server base URL").option("--name <name>", "new name").option("--description <text>", "new description").option("--vcs-enabled <bool>", "gate VCS checks for this project (true/false; false=all git checks N/A)").option("--vcs-repo-path <path>", "[deprecated] no longer read by gate resolution \u2014 task repos are discovered by scanning the working directory (T202610080003); flag kept writable for DB compatibility only").option("--vcs-base-branch <branch>", "base branch (2nd level of the base-branch resolution chain)").option("--vcs-branch-pattern <pattern>", "task branch prefix pattern for A1").option("--vcs-worktree-mode <bool>", "worktree parallel mode (true=A6 stay-on-base N/A)").option("--vcs-archive-to-base <bool>", "project declares merge-back-to-base archive flow (false=A5 N/A)").option("--vcs-doc-paths <paths>", "B7 exemption paths, comma separated (dirs need trailing /, glob like *.md)").action(async (id, opts) => {
6973
7125
  const client = createClient(opts.url);
6974
7126
  const parseBool = (v, flag) => {
6975
7127
  if (v === void 0) return void 0;
@@ -6993,6 +7145,7 @@ function createProjectCommand() {
6993
7145
  vcsProvided = true;
6994
7146
  }
6995
7147
  if (opts.vcsRepoPath !== void 0) {
7148
+ console.error(`[siming] deprecated: --vcs-repo-path \u5DF2\u9000\u5F79\u2014\u2014\u95E8\u7981\u4EFB\u52A1\u4ED3\u96C6\u5408 = \u5DE5\u4F5C\u76EE\u5F55\u626B\u63CF\u5168\u91CF\uFF08cwd + \u2264scanDepth \u5B50\u76EE\u5F55\uFF09\uFF0C\u8BE5\u5B57\u6BB5\u4E0D\u518D\u5F71\u54CD\u5224\u5B9A\uFF1BscanDepth \u53EF\u7ECF <\u5DE5\u4F5C\u76EE\u5F55>/.siming/dev.json \u914D\u7F6E\uFF08\u9ED8\u8BA4 1\uFF0C\u94B3\u5236 \u22643\uFF09`);
6996
7149
  vcsFields.repoPath = opts.vcsRepoPath;
6997
7150
  vcsProvided = true;
6998
7151
  }
@@ -7201,7 +7354,7 @@ function createConfigCommand() {
7201
7354
 
7202
7355
  // src/commands/export/command.ts
7203
7356
  import { Command as Command11 } from "commander";
7204
- import { readFileSync as readFileSync14 } from "fs";
7357
+ import { readFileSync as readFileSync15 } from "fs";
7205
7358
  import { parse as parseYaml2 } from "yaml";
7206
7359
 
7207
7360
  // src/client/handlers/export.ts
@@ -7872,7 +8025,7 @@ function createExportCommand() {
7872
8025
  async function runExportPlan(client, filePath, typesArg) {
7873
8026
  let raw;
7874
8027
  try {
7875
- const text = readFileSync14(filePath, "utf-8");
8028
+ const text = readFileSync15(filePath, "utf-8");
7876
8029
  raw = parseYaml2(text);
7877
8030
  } catch (e) {
7878
8031
  return fail({
@@ -7897,7 +8050,7 @@ async function runExportPlan(client, filePath, typesArg) {
7897
8050
  }
7898
8051
  function loadPlanFile(path) {
7899
8052
  try {
7900
- const text = readFileSync14(path, "utf-8");
8053
+ const text = readFileSync15(path, "utf-8");
7901
8054
  const parsed = JSON.parse(text);
7902
8055
  if (!Array.isArray(parsed.items)) {
7903
8056
  return fail({ message: `\u8BA1\u5212\u6587\u4EF6\u7ED3\u6784\u65E0\u6548\uFF1A\u7F3A\u5C11 items \u6570\u7EC4`, httpStatus: 0, hint: "\u8BA1\u5212\u6587\u4EF6\u7531 `siming export plan --format json > plan.json` \u751F\u6210" });
@@ -7960,14 +8113,14 @@ function renderReport(report, format) {
7960
8113
 
7961
8114
  // src/commands/install/command.ts
7962
8115
  import { Command as Command12 } from "commander";
7963
- import { existsSync as existsSync9 } from "fs";
8116
+ import { existsSync as existsSync10 } from "fs";
7964
8117
  import { createInterface as createInterface2 } from "readline/promises";
7965
- import { dirname as dirname5, join as join9 } from "path";
8118
+ import { dirname as dirname5, join as join10 } from "path";
7966
8119
  import { AGENT_MAIN_DOC as AGENT_MAIN_DOC5, SKILL_MAIN_DOC as SKILL_MAIN_DOC5, VERSION as VERSION3 } from "@siming-org/core";
7967
8120
 
7968
8121
  // src/commands/install/writer.ts
7969
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
7970
- import { dirname as dirname4, join as join8 } from "path";
8122
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync16, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
8123
+ import { dirname as dirname4, join as join9 } from "path";
7971
8124
  import { homedir as homedir4 } from "os";
7972
8125
  import { parse as parseYaml3, stringify as stringifyYaml } from "yaml";
7973
8126
  import {
@@ -7978,12 +8131,12 @@ import {
7978
8131
  } from "@siming-org/core";
7979
8132
  function agentsHomeDir() {
7980
8133
  const env = process.env.SIMING_AGENTS_HOME;
7981
- return env && env.length > 0 ? env : join8(homedir4(), ".agents");
8134
+ return env && env.length > 0 ? env : join9(homedir4(), ".agents");
7982
8135
  }
7983
8136
  function resolveInstallTargets(scope, workspaceDir) {
7984
8137
  if (scope === "global") {
7985
8138
  const dir = agentsHomeDir();
7986
- return { targets: { dir, mcpConfigPath: join8(dir, "mcp.json") } };
8139
+ return { targets: { dir, mcpConfigPath: join9(dir, "mcp.json") } };
7987
8140
  }
7988
8141
  const wd = workspaceDir ?? findWorkspaceDir();
7989
8142
  if (!wd) {
@@ -7991,7 +8144,7 @@ function resolveInstallTargets(scope, workspaceDir) {
7991
8144
  error: "scope=project \u9700\u8981\u5DE5\u4F5C\u533A\uFF08.siming/ \u76EE\u5F55\uFF09\u2014\u2014\u5148\u8FD0\u884C `siming init --project <key|id>` \u7ED1\u5B9A\u9879\u76EE\uFF0C\u6216\u5728\u9879\u76EE\u76EE\u5F55\u5185\u6267\u884C"
7992
8145
  };
7993
8146
  }
7994
- return { targets: { dir: join8(wd, ".agents"), mcpConfigPath: null } };
8147
+ return { targets: { dir: join9(wd, ".agents"), mcpConfigPath: null } };
7995
8148
  }
7996
8149
  function isSafeAssetName(name) {
7997
8150
  if (name.length === 0 || name === "." || name === "..") return false;
@@ -8011,7 +8164,7 @@ function validateReferencesLocally(mainDocName, references) {
8011
8164
  function writeAssetDirectory(assetDir, files) {
8012
8165
  rmSync3(assetDir, { recursive: true, force: true });
8013
8166
  for (const file of files) {
8014
- const target = join8(assetDir, ...file.relPath.split("/"));
8167
+ const target = join9(assetDir, ...file.relPath.split("/"));
8015
8168
  mkdirSync5(dirname4(target), { recursive: true });
8016
8169
  writeFileSync5(target, file.content, "utf-8");
8017
8170
  }
@@ -8028,7 +8181,7 @@ ${skill.content}
8028
8181
  `;
8029
8182
  }
8030
8183
  function skillTargetPath(dir, skill) {
8031
- return join8(dir, "skills", skill.name, "SKILL.md");
8184
+ return join9(dir, "skills", skill.name, "SKILL.md");
8032
8185
  }
8033
8186
  function permissionsToMatrix(permissions) {
8034
8187
  const matrix = {};
@@ -8064,13 +8217,13 @@ ${agent.systemPrompt}
8064
8217
  `;
8065
8218
  }
8066
8219
  function agentTargetPath(dir, agent) {
8067
- return join8(dir, "agents", agent.name, "agent.md");
8220
+ return join9(dir, "agents", agent.name, "agent.md");
8068
8221
  }
8069
8222
  function readSimingVersion(path) {
8070
- if (!existsSync8(path)) return null;
8223
+ if (!existsSync9(path)) return null;
8071
8224
  let text;
8072
8225
  try {
8073
- text = readFileSync15(path, "utf-8");
8226
+ text = readFileSync16(path, "utf-8");
8074
8227
  } catch {
8075
8228
  return null;
8076
8229
  }
@@ -8092,10 +8245,10 @@ function isPlainObject(v) {
8092
8245
  return typeof v === "object" && v !== null && !Array.isArray(v);
8093
8246
  }
8094
8247
  function readMcpConfig(path) {
8095
- if (!existsSync8(path)) return { state: "absent" };
8248
+ if (!existsSync9(path)) return { state: "absent" };
8096
8249
  let text;
8097
8250
  try {
8098
- text = readFileSync15(path, "utf-8");
8251
+ text = readFileSync16(path, "utf-8");
8099
8252
  } catch (e) {
8100
8253
  return { state: "corrupt", error: `\u8BFB\u53D6\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}` };
8101
8254
  }
@@ -8332,7 +8485,7 @@ function compareSkill(s, dir) {
8332
8485
  if (!isSafeAssetName(s.name)) {
8333
8486
  return { ...base, action: "conflict", reason: `unsafe asset name "${s.name}"` };
8334
8487
  }
8335
- if (!existsSync9(path)) return { ...base, action: "install", reason: "not installed locally" };
8488
+ if (!existsSync10(path)) return { ...base, action: "install", reason: "not installed locally" };
8336
8489
  const marked = readSimingVersion(path);
8337
8490
  if (marked === null) {
8338
8491
  return { ...base, localVersion: "unknown", action: "conflict", reason: "no siming-version marker (third-party asset; use --force to overwrite)" };
@@ -8350,7 +8503,7 @@ function compareAgent(a, dir) {
8350
8503
  if (!isSafeAssetName(a.name)) {
8351
8504
  return { ...base, action: "conflict", reason: `unsafe asset name "${a.name}"` };
8352
8505
  }
8353
- if (!existsSync9(path)) return { ...base, action: "install", reason: "not installed locally" };
8506
+ if (!existsSync10(path)) return { ...base, action: "install", reason: "not installed locally" };
8354
8507
  const marked = readSimingVersion(path);
8355
8508
  if (marked === null) {
8356
8509
  return { ...base, localVersion: "unknown", action: "conflict", reason: "no siming-version marker (third-party asset; use --force to overwrite)" };
@@ -8443,7 +8596,7 @@ async function applyInstall(client, scope, projectId, items, opts) {
8443
8596
  results.push({ name: item.name, type: "skill", action: "failed", reason: `invalid reference doc from server: ${invalidRef}`, hint: "\u5F15\u7528\u6587\u6863\u975E\u6CD5\uFF08\u6570\u636E\u53EF\u80FD\u88AB\u7ED5\u8FC7\u6821\u9A8C\u5199\u5165\uFF09\uFF1B\u4FEE\u6B63 server \u6570\u636E\u540E\u91CD\u8BD5\uFF0C\u672C\u6761\u672A\u5199\u5165" });
8444
8597
  continue;
8445
8598
  }
8446
- const targetExists = existsSync9(targetPath);
8599
+ const targetExists = existsSync10(targetPath);
8447
8600
  writeAssetDirectory(dirname5(targetPath), [
8448
8601
  { relPath: SKILL_MAIN_DOC5, content: skillToMarkdown(r.data) },
8449
8602
  ...(r.data.references ?? []).map((ref) => ({ relPath: ref.path, content: ref.content }))
@@ -8479,7 +8632,7 @@ async function applyInstall(client, scope, projectId, items, opts) {
8479
8632
  results.push({ name: item.name, type: "agent", action: "failed", reason: `invalid reference doc from server: ${invalidRef}`, hint: "\u5F15\u7528\u6587\u6863\u975E\u6CD5\uFF08\u6570\u636E\u53EF\u80FD\u88AB\u7ED5\u8FC7\u6821\u9A8C\u5199\u5165\uFF09\uFF1B\u4FEE\u6B63 server \u6570\u636E\u540E\u91CD\u8BD5\uFF0C\u672C\u6761\u672A\u5199\u5165" });
8480
8633
  continue;
8481
8634
  }
8482
- const targetExists = existsSync9(targetPath);
8635
+ const targetExists = existsSync10(targetPath);
8483
8636
  writeAssetDirectory(dirname5(targetPath), [
8484
8637
  { relPath: AGENT_MAIN_DOC5, content: agentToMarkdown(r.data, realModel) },
8485
8638
  ...(r.data.references ?? []).map((ref) => ({ relPath: ref.path, content: ref.content }))
@@ -8503,8 +8656,8 @@ function buildInstallGuidance(scope, targets) {
8503
8656
  const lines = [];
8504
8657
  lines.push("\u2500\u2500 siming \u901A\u7528\u8D44\u4EA7\u8FC1\u79FB\u6307\u5F15\uFF08.agents \u5F00\u653E\u534F\u8BAE\u843D\u70B9\uFF09\u2500\u2500");
8505
8658
  lines.push(`[1] \u8D44\u4EA7\u4F4D\u7F6E\uFF08${scope === "global" ? "\u7528\u6237\u7EA7" : "\u9879\u76EE\u7EA7"} ${targets.dir}\uFF09\uFF1A`);
8506
- lines.push(` - skills: ${join9(targets.dir, "skills")}\uFF08\u6BCF\u4E2A skill \u4E00\u4E2A\u5B50\u76EE\u5F55\uFF0C\u542B SKILL.md \u4E0E\u5176\u5F15\u7528\u6587\u6863\uFF09`);
8507
- lines.push(` - agents: ${join9(targets.dir, "agents")}\uFF08\u6BCF\u4E2A agent \u4E00\u4E2A\u5B50\u76EE\u5F55\uFF0C\u542B agent.md \u4E0E\u5176\u5F15\u7528\u6587\u6863\uFF09`);
8659
+ lines.push(` - skills: ${join10(targets.dir, "skills")}\uFF08\u6BCF\u4E2A skill \u4E00\u4E2A\u5B50\u76EE\u5F55\uFF0C\u542B SKILL.md \u4E0E\u5176\u5F15\u7528\u6587\u6863\uFF09`);
8660
+ lines.push(` - agents: ${join10(targets.dir, "agents")}\uFF08\u6BCF\u4E2A agent \u4E00\u4E2A\u5B50\u76EE\u5F55\uFF0C\u542B agent.md \u4E0E\u5176\u5F15\u7528\u6587\u6863\uFF09`);
8508
8661
  if (targets.mcpConfigPath) {
8509
8662
  lines.push(` - MCP \u6CE8\u518C: ${targets.mcpConfigPath}\uFF08\u9876\u5C42 mcpServers.siming \u2192 stdio \u547D\u4EE4 "siming mcp-serve"\uFF09`);
8510
8663
  }
@@ -8890,7 +9043,8 @@ ${ROADMAP_TEXT}
8890
9043
 
8891
9044
  \u6D41\u7A0B\u6A21\u677F\u7684\u8282\u70B9\u63D0\u793A\u8BCD\u4E2D\uFF0C\u73AF\u5883\u76F8\u5173\u4FE1\u606F\u5168\u90E8\u53C2\u6570\u5316\u4E3A\u300C\u6309\u9879\u76EE\u58F0\u660E\u300D\uFF0C
8892
9045
  \u58F0\u660E\u503C\u843D\u70B9 = \u9879\u76EE\u4ED3\u5E93\u7684 AI \u5165\u53E3\u6587\u4EF6\uFF08\u9879\u76EE AGENTS.md\uFF09\u3002
8893
- \u4EE5\u4E0B 8 \u9879\u58F0\u660E\u4E0D\u9F50\u65F6\uFF0C\u6D41\u7A0B\u4F1A\u5728\u5BF9\u5E94\u73AF\u8282\u6682\u505C\u8BE2\u95EE\u6216\u964D\u7EA7\uFF1A
9046
+ \u4EE5\u4E0B 9 \u9879\u4E2D 1-8 \u4E3A\u73AF\u5883\u58F0\u660E\uFF0C\u4E0D\u9F50\u65F6\u6D41\u7A0B\u4F1A\u5728\u5BF9\u5E94\u73AF\u8282\u6682\u505C\u8BE2\u95EE\u6216\u964D\u7EA7\uFF1B
9047
+ \u7B2C 9 \u9879\u4E3A\u884C\u4E3A\u89C4\u5219\u91CD\u63D0\u793A\uFF08\u89C1\u8868\u683C\u540E\u8BF4\u660E\uFF09\uFF1A
8894
9048
 
8895
9049
  | # | \u58F0\u660E\u9879 | \u58F0\u660E\u5185\u5BB9 | \u7F3A\u5931\u540E\u679C |
8896
9050
  |---|--------|----------|----------|
@@ -8902,6 +9056,10 @@ ${ROADMAP_TEXT}
8902
9056
  | 6 | E2E \u552F\u4E00\u6267\u884C\u5165\u53E3 | E2E \u6D4B\u8BD5\u552F\u4E00\u5408\u6CD5\u7684\u6267\u884C\u547D\u4EE4/\u811A\u672C | E2E \u5F00\u53D1\u73AF\u8282\u65E0\u6CD5\u786E\u5B9A\u6267\u884C\u65B9\u5F0F\uFF0C\u6682\u505C\u8BE2\u95EE |
8903
9057
  | 7 | \u6D4B\u8BD5\u5B8C\u6574\u6027\u68C0\u67E5 | \u9632\u5220\u6D4B/\u7981 skip \u7684\u9759\u6001\u68C0\u67E5\u547D\u4EE4 | \u6D4B\u8BD5\u6536\u5C3E\u7F3A\u5C11\u95E8\u7981\u4F9D\u636E\uFF0C\u6682\u505C\u8BE2\u95EE |
8904
9058
  | 8 | \u547D\u4EE4\u65E5\u5FD7\u843D\u76D8 | \u6784\u5EFA/\u6D4B\u8BD5\u547D\u4EE4\u7684\u65E5\u5FD7\u5F52\u6863\u7EA6\u5B9A | \u547D\u4EE4\u6267\u884C\u65E0\u6CD5\u56DE\u6EAF\uFF0C\u5F52\u6863\u8282\u70B9\u8BC1\u636E\u4E0D\u8DB3 |
9059
+ | 9 | \u590D\u5BA1\u59D4\u6D3E\u89C4\u5219 | \u591A\u8F6E\u590D\u5BA1\u4F18\u5148\u540C\u4E00 reviewer \u4F1A\u8BDD\u7EED\u804A\uFF1B\u6362\u65B0\u4F1A\u8BDD\u65F6\u968F\u9644\u4E0A\u8F6E Findings \u4E0E\u5904\u7F6E\u72B6\u6001\uFF08\u8868\u8FF0\u5BF9\u9F50 workflow-discipline\u300C\u59D4\u6D3E\u7EAA\u5F8B\u300D\uFF09 | \u591A\u8F6E\u8BC4\u5BA1\u9000\u5316\u4E3A\u6BCF\u8F6E\u5168\u65B0\u5168\u91CF\u8BC4\u5BA1\uFF0C\u91CD\u590D\u6D88\u8017\u65F6\u95F4\u4E0E token |
9060
+
9061
+ \u7B2C 9 \u9879\u4E3A\u884C\u4E3A\u89C4\u5219\u91CD\u63D0\u793A\uFF1A\u6743\u5A01\u6E90\u662F workflow-discipline \u59D4\u6D3E\u7EAA\u5F8B\uFF08\u8282\u70B9\u52A0\u8F7D\uFF09\uFF0C
9062
+ \u5728\u9879\u76EE AGENTS.md \u91CD\u590D\u5199\u5165\u53EF\u63D0\u9AD8\u8DE8\u5BBF\u4E3B\u9075\u5FAA\u7387\uFF08\u5E38\u9A7B\u5165\u53E3\u5148\u4E8E\u8282\u70B9\u52A0\u8F7D\u751F\u6548\uFF09\u3002
8905
9063
 
8906
9064
  ----------------------------------------------------------------
8907
9065
  \u4E8C\u3001\u6D41\u7A0B\u6A21\u677F\u83B7\u53D6\u6307\u5F15
@@ -9038,7 +9196,7 @@ async function handleUpdateEnums(client, category, body) {
9038
9196
  // src/commands/settings/command.ts
9039
9197
  function createSettingsCommand() {
9040
9198
  const settings = new Command16("settings").description("Settings (enum registries)");
9041
- withCommonOpts(settings.command("enums [category]"), "fancy").description("Show enum registry (all 7 categories, or one category)").action(async (category, opts) => {
9199
+ withCommonOpts(settings.command("enums [category]"), "fancy").description("Show enum registry (all categories, or one category)").action(async (category, opts) => {
9042
9200
  const client = createClient(opts.url);
9043
9201
  const catR = category ? EnumRegistryCategorySchema.safeParse(category) : null;
9044
9202
  if (category && !catR?.success) {
@@ -9092,8 +9250,8 @@ import { VERSION as VERSION4, SIMING_CONFIG_KEYS as SIMING_CONFIG_KEYS2 } from "
9092
9250
 
9093
9251
  // src/commands/server/daemon.ts
9094
9252
  import { execFileSync } from "child_process";
9095
- import { closeSync, existsSync as existsSync10, lstatSync, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync16, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
9096
- import { join as join10, resolve as resolve2 } from "path";
9253
+ import { closeSync, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync17, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
9254
+ import { join as join11, resolve as resolve2 } from "path";
9097
9255
  import { spawn } from "child_process";
9098
9256
 
9099
9257
  // src/commands/server/probe.ts
@@ -9110,10 +9268,10 @@ function runtimeDir() {
9110
9268
  return process.env.SIMING_RUNTIME_DIR ?? "/tmp/siming";
9111
9269
  }
9112
9270
  function pidfilePath() {
9113
- return join10(runtimeDir(), "server.pid");
9271
+ return join11(runtimeDir(), "server.pid");
9114
9272
  }
9115
9273
  function logfilePath() {
9116
- return join10(runtimeDir(), "server.log");
9274
+ return join11(runtimeDir(), "server.log");
9117
9275
  }
9118
9276
  var READY_TIMEOUT_MS = 2e4;
9119
9277
  var STOP_GRACE_MS = 1e4;
@@ -9146,7 +9304,7 @@ function readPidFile() {
9146
9304
  try {
9147
9305
  const st = lstatSync(pidfilePath(), { throwIfNoEntry: false });
9148
9306
  if (st === void 0 || st.isSymbolicLink()) return null;
9149
- const parsed = JSON.parse(readFileSync16(pidfilePath(), "utf-8").trim());
9307
+ const parsed = JSON.parse(readFileSync17(pidfilePath(), "utf-8").trim());
9150
9308
  if (typeof parsed.pid !== "number" || typeof parsed.startedAt !== "number" || typeof parsed.port !== "number") {
9151
9309
  return null;
9152
9310
  }
@@ -9187,7 +9345,7 @@ function removePidFile(expectedPid) {
9187
9345
  }
9188
9346
  function readCmdline(pid) {
9189
9347
  try {
9190
- const raw = readFileSync16(`/proc/${pid}/cmdline`);
9348
+ const raw = readFileSync17(`/proc/${pid}/cmdline`);
9191
9349
  const joined = raw.toString().split("\0").filter((s) => s.length > 0).join(" ");
9192
9350
  if (joined.length > 0) return joined;
9193
9351
  } catch {
@@ -9216,7 +9374,7 @@ function isSimingDaemonCmdline(cmdline, expectedEntry) {
9216
9374
  }
9217
9375
  function readProcessStartMs(pid) {
9218
9376
  try {
9219
- const stat = readFileSync16(`/proc/${pid}/stat`, "utf-8");
9377
+ const stat = readFileSync17(`/proc/${pid}/stat`, "utf-8");
9220
9378
  const rest = stat.slice(stat.lastIndexOf(")") + 2);
9221
9379
  const fields = rest.split(" ");
9222
9380
  const starttimeJiffies = Number(fields[19]);
@@ -9245,7 +9403,7 @@ function readProcessStartMs(pid) {
9245
9403
  }
9246
9404
  function readBtimeFromProcStat() {
9247
9405
  try {
9248
- const stat = readFileSync16("/proc/stat", "utf-8");
9406
+ const stat = readFileSync17("/proc/stat", "utf-8");
9249
9407
  for (const line of stat.split("\n")) {
9250
9408
  if (line.startsWith("btime ")) {
9251
9409
  const v = Number(line.slice(6).trim());
@@ -9296,14 +9454,14 @@ function sleep(ms) {
9296
9454
  function readLogTail(lines = 30) {
9297
9455
  const st = lstatSync(logfilePath(), { throwIfNoEntry: false });
9298
9456
  if (st === void 0 || st.isSymbolicLink()) return null;
9299
- const content = readFileSync16(logfilePath(), "utf-8");
9457
+ const content = readFileSync17(logfilePath(), "utf-8");
9300
9458
  const all = content.split("\n");
9301
9459
  if (all.at(-1) === "") all.pop();
9302
9460
  return all.slice(-lines);
9303
9461
  }
9304
9462
  function spawnDaemon(configEnv) {
9305
9463
  const entry = resolve2(process.argv[1] ?? "");
9306
- if (entry.length === 0 || !existsSync10(entry)) {
9464
+ if (entry.length === 0 || !existsSync11(entry)) {
9307
9465
  throw new Error(`\u65E0\u6CD5\u5B9A\u4F4D siming \u5165\u53E3\u811A\u672C\uFF08process.argv[1]=${process.argv[1] ?? "(\u7A7A)"}\uFF09`);
9308
9466
  }
9309
9467
  if (entry.endsWith(".ts")) {