@siming-org/cli 0.8.2 → 0.8.4
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 +390 -207
- package/package.json +3 -3
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
|
|
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
|
|
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.
|
|
693
|
-
return na(`\
|
|
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"],
|
|
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
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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],
|
|
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"],
|
|
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
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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
|
|
873
|
-
|
|
874
|
-
ctx.
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
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
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
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
|
|
952
|
-
|
|
953
|
-
|
|
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
|
|
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
|
|
1001
|
-
|
|
1002
|
-
|
|
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
|
|
1010
|
-
|
|
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 ${
|
|
1014
|
-
|
|
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
|
-
|
|
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";
|
|
@@ -1024,11 +1128,15 @@ async function checkDeferredIssues(ctx) {
|
|
|
1024
1128
|
}
|
|
1025
1129
|
const loaded = await ctx.loadIssueIds();
|
|
1026
1130
|
if (!loaded.ok) {
|
|
1027
|
-
return fail3(type, `\
|
|
1131
|
+
return fail3(type, `\u95EE\u9898\u6C60\u53CD\u67E5\u5931\u8D25\uFF1A${loaded.error}`, "\u68C0\u67E5 server \u53EF\u7528\u6027\u540E\u91CD\u8BD5");
|
|
1028
1132
|
}
|
|
1029
1133
|
const missing = ids.filter((id) => !loaded.ids.has(id));
|
|
1030
1134
|
if (missing.length > 0) {
|
|
1031
|
-
return fail3(
|
|
1135
|
+
return fail3(
|
|
1136
|
+
type,
|
|
1137
|
+
`\u5EF6\u671F\u95EE\u9898\u672A\u843D\u6C60\uFF1A${missing.join(", ")}`,
|
|
1138
|
+
"siming issue create \u5C06\u5EF6\u671F\u573A\u666F\u5F55\u5165\u4EFB\u52A1\u6240\u5C5E\u9879\u76EE\u7684\u95EE\u9898\u6C60\uFF08cancelled \u4E0D\u8BA1\u5165\uFF09\u540E\uFF0Crecord-set \u4FEE\u6B63 deferredIssues"
|
|
1139
|
+
);
|
|
1032
1140
|
}
|
|
1033
1141
|
return { type, status: "pass", detail: `\u5EF6\u671F\u95EE\u9898 ${ids.length} \u4E2A\u5747\u5DF2\u843D\u6C60` };
|
|
1034
1142
|
}
|
|
@@ -1038,17 +1146,21 @@ async function checkEvidenceFiles(ctx) {
|
|
|
1038
1146
|
if (files.length === 0) {
|
|
1039
1147
|
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
1148
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
}
|
|
1149
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
1150
|
+
if (guard) return { ...guard, type };
|
|
1044
1151
|
const missing = [];
|
|
1152
|
+
const hits = [];
|
|
1045
1153
|
for (const rel of files) {
|
|
1046
|
-
|
|
1154
|
+
const hit = ctx.repos.find((repo) => ctx.fileExists(`${repo}/${rel}`));
|
|
1155
|
+
if (hit === void 0) missing.push(rel);
|
|
1156
|
+
else hits.push({ rel, repo: hit });
|
|
1047
1157
|
}
|
|
1048
1158
|
if (missing.length > 0) {
|
|
1049
|
-
return fail3(type, `\u8BC1\u636E\u6587\u4EF6\u4E0D\u5B58\u5728\
|
|
1159
|
+
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
|
|
1160
|
+
${ctx.reposScanDetail}`);
|
|
1050
1161
|
}
|
|
1051
|
-
return { type, status: "pass", detail: `\u8BC1\u636E\u6587\u4EF6 ${files.length} \u4E2A\u5747\u5B58\u5728
|
|
1162
|
+
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
|
|
1163
|
+
${ctx.reposScanDetail}` };
|
|
1052
1164
|
}
|
|
1053
1165
|
async function checkFinalConverged(ctx) {
|
|
1054
1166
|
const type = "record-final-converged";
|
|
@@ -1149,10 +1261,95 @@ function auditAiChecks(aiChecks, record) {
|
|
|
1149
1261
|
return results;
|
|
1150
1262
|
}
|
|
1151
1263
|
|
|
1264
|
+
// src/client/checks/issue-pool-loader.ts
|
|
1265
|
+
var POOL_STATUSES = "pending,partial,converted,done";
|
|
1266
|
+
var PAGE_LIMIT = 500;
|
|
1267
|
+
var MAX_PAGES = 20;
|
|
1268
|
+
async function loadProjectIssuePoolIds(client, projectId) {
|
|
1269
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1270
|
+
let collected = 0;
|
|
1271
|
+
for (let page = 1; page <= MAX_PAGES; page++) {
|
|
1272
|
+
const r = await client.getJson(
|
|
1273
|
+
`/api/issues?projectId=${encodeURIComponent(projectId)}&status=${encodeURIComponent(POOL_STATUSES)}&limit=${PAGE_LIMIT}&page=${page}`
|
|
1274
|
+
);
|
|
1275
|
+
if (!r.success) return { ok: false, error: r.error.message };
|
|
1276
|
+
for (const item of r.data.items) {
|
|
1277
|
+
const id = item.issueId ?? item.id ?? "";
|
|
1278
|
+
if (id !== "") ids.add(id);
|
|
1279
|
+
}
|
|
1280
|
+
collected += r.data.items.length;
|
|
1281
|
+
if (collected >= r.data.total || r.data.items.length < r.data.limit) {
|
|
1282
|
+
return { ok: true, ids };
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
return {
|
|
1286
|
+
ok: false,
|
|
1287
|
+
error: `\u95EE\u9898\u6C60\u5206\u9875\u8D85\u4E0A\u9650\uFF08>${MAX_PAGES} \u9875 \xD7 ${PAGE_LIMIT} \u6761\uFF09\u2014\u2014\u68C0\u67E5\u6570\u636E\u5F62\u6001\u540E\u91CD\u8BD5`
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1152
1291
|
// src/client/checks/param-resolver.ts
|
|
1153
1292
|
import { DEFAULT_VCS_DOC_PATHS } from "@siming-org/core";
|
|
1154
1293
|
import { execFile } from "child_process";
|
|
1155
1294
|
import { promisify } from "util";
|
|
1295
|
+
|
|
1296
|
+
// src/config/workspace-repos.ts
|
|
1297
|
+
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync5 } from "fs";
|
|
1298
|
+
import { join as join5 } from "path";
|
|
1299
|
+
var EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set(["temp", "node_modules", "dist", "build"]);
|
|
1300
|
+
var DEFAULT_SCAN_DEPTH = 1;
|
|
1301
|
+
var MAX_SCAN_DEPTH = 3;
|
|
1302
|
+
var WORKSPACE_DIR_NAME3 = ".siming";
|
|
1303
|
+
var DEV_JSON_FILE2 = "dev.json";
|
|
1304
|
+
function normalizeScanDepth(raw) {
|
|
1305
|
+
if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
|
|
1306
|
+
return DEFAULT_SCAN_DEPTH;
|
|
1307
|
+
}
|
|
1308
|
+
if (raw <= 0) return DEFAULT_SCAN_DEPTH;
|
|
1309
|
+
return Math.min(raw, MAX_SCAN_DEPTH);
|
|
1310
|
+
}
|
|
1311
|
+
function readScanDepth(cwd) {
|
|
1312
|
+
const path = join5(cwd, WORKSPACE_DIR_NAME3, DEV_JSON_FILE2);
|
|
1313
|
+
try {
|
|
1314
|
+
if (!existsSync5(path)) return DEFAULT_SCAN_DEPTH;
|
|
1315
|
+
const parsed = JSON.parse(readFileSync5(path, "utf-8"));
|
|
1316
|
+
return normalizeScanDepth(parsed.scanDepth);
|
|
1317
|
+
} catch {
|
|
1318
|
+
return DEFAULT_SCAN_DEPTH;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
function isGitRepo(dir) {
|
|
1322
|
+
return existsSync5(join5(dir, ".git"));
|
|
1323
|
+
}
|
|
1324
|
+
function isExcludedDir(name) {
|
|
1325
|
+
return EXCLUDED_DIR_NAMES.has(name) || name.startsWith(".");
|
|
1326
|
+
}
|
|
1327
|
+
function scanWorkspaceRepos(cwd, scanDepthOverride) {
|
|
1328
|
+
const scanDepth = scanDepthOverride !== void 0 ? normalizeScanDepth(scanDepthOverride) : readScanDepth(cwd);
|
|
1329
|
+
const repos = [];
|
|
1330
|
+
let scannedDirs = 0;
|
|
1331
|
+
const queue = [{ dir: cwd, depth: 0 }];
|
|
1332
|
+
while (queue.length > 0) {
|
|
1333
|
+
const { dir, depth } = queue.shift();
|
|
1334
|
+
if (isGitRepo(dir)) repos.push(dir);
|
|
1335
|
+
if (depth >= scanDepth) continue;
|
|
1336
|
+
let entries;
|
|
1337
|
+
try {
|
|
1338
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
1339
|
+
} catch {
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
for (const entry of entries) {
|
|
1343
|
+
if (!entry.isDirectory()) continue;
|
|
1344
|
+
scannedDirs++;
|
|
1345
|
+
if (isExcludedDir(entry.name)) continue;
|
|
1346
|
+
queue.push({ dir: join5(dir, entry.name), depth: depth + 1 });
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
return { repos, scannedDirs, scanDepth };
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// src/client/checks/param-resolver.ts
|
|
1156
1353
|
var execFileAsync = promisify(execFile);
|
|
1157
1354
|
var defaultGitRunner = async (args, cwd) => {
|
|
1158
1355
|
try {
|
|
@@ -1163,35 +1360,22 @@ var defaultGitRunner = async (args, cwd) => {
|
|
|
1163
1360
|
return { ok: false, stdout: e.stdout ?? "", stderr: e.stderr ?? e.message ?? String(err2) };
|
|
1164
1361
|
}
|
|
1165
1362
|
};
|
|
1166
|
-
async function resolveCheckContextWithRunner(base, vcs, cwd,
|
|
1167
|
-
const
|
|
1363
|
+
async function resolveCheckContextWithRunner(base, vcs, cwd, scan) {
|
|
1364
|
+
const result = scan !== void 0 ? scan(cwd) : scanWorkspaceRepos(cwd);
|
|
1168
1365
|
const alignRecord = base.nodeRecords["ALIGN"];
|
|
1169
1366
|
const baseBranch = (alignRecord?.baseBranch !== void 0 && alignRecord.baseBranch !== "" ? alignRecord.baseBranch : void 0) ?? vcs.baseBranch ?? null;
|
|
1170
1367
|
return {
|
|
1171
1368
|
...base,
|
|
1172
|
-
|
|
1173
|
-
|
|
1369
|
+
repos: result.repos,
|
|
1370
|
+
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
1371
|
baseBranch,
|
|
1175
1372
|
branchPattern: vcs.branchPattern ?? "feature/",
|
|
1176
1373
|
docPaths: vcs.docPaths ?? [...DEFAULT_VCS_DOC_PATHS]
|
|
1177
1374
|
};
|
|
1178
1375
|
}
|
|
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
1376
|
|
|
1193
1377
|
// src/client/checks/runner.ts
|
|
1194
|
-
import { existsSync as
|
|
1378
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1195
1379
|
function gateHasFailure(report) {
|
|
1196
1380
|
return report.toolOutcomes.some((o) => o.status === "fail") || report.aiOutcomes.some((o) => o.status === "fail");
|
|
1197
1381
|
}
|
|
@@ -1234,20 +1418,13 @@ async function runNodeGate(client, taskId) {
|
|
|
1234
1418
|
vcs,
|
|
1235
1419
|
...vcsLoadError !== void 0 ? { vcsLoadError } : {},
|
|
1236
1420
|
runGit: defaultGitRunner,
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
if (!r.success) return { ok: false, error: r.error.message };
|
|
1242
|
-
const ids = new Set(r.data.items.map((i) => i.issueId ?? i.id ?? ""));
|
|
1243
|
-
ids.delete("");
|
|
1244
|
-
return { ok: true, ids };
|
|
1245
|
-
},
|
|
1246
|
-
fileExists: (p) => existsSync5(p)
|
|
1421
|
+
// B8 判定通道(T202610100001):同项目问题池存在性——旧任务绑定视图(GET
|
|
1422
|
+
// /api/tasks/:taskId/issues)对普通入池 issue 恒空集,详见 issue-pool-loader.ts 头注
|
|
1423
|
+
loadIssueIds: () => loadProjectIssuePoolIds(client, context.task.projectId),
|
|
1424
|
+
fileExists: (p) => existsSync6(p)
|
|
1247
1425
|
},
|
|
1248
1426
|
vcs,
|
|
1249
|
-
process.cwd()
|
|
1250
|
-
defaultGitRunner
|
|
1427
|
+
process.cwd()
|
|
1251
1428
|
);
|
|
1252
1429
|
const toolOutcomes = [];
|
|
1253
1430
|
for (const toolCheck of toolChecks) {
|
|
@@ -1576,7 +1753,7 @@ async function resolveArtifactBatchEntries(entries) {
|
|
|
1576
1753
|
let content = inlineContent;
|
|
1577
1754
|
if (filePath !== void 0) {
|
|
1578
1755
|
try {
|
|
1579
|
-
content =
|
|
1756
|
+
content = readFileSync6(filePath, "utf-8");
|
|
1580
1757
|
} catch (e) {
|
|
1581
1758
|
localFailures.push({ index, message: `\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${filePath}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09` });
|
|
1582
1759
|
continue;
|
|
@@ -1662,7 +1839,7 @@ async function handleArchNoteAddBatch(client, taskId, texts) {
|
|
|
1662
1839
|
}
|
|
1663
1840
|
|
|
1664
1841
|
// src/client/handlers/dag.ts
|
|
1665
|
-
import { readFileSync as
|
|
1842
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
1666
1843
|
import { parse as parseYaml } from "yaml";
|
|
1667
1844
|
import {
|
|
1668
1845
|
BIZ_CODE_MESSAGES,
|
|
@@ -1884,13 +2061,13 @@ async function handleUpgradeApply(client, templateId, decisions) {
|
|
|
1884
2061
|
|
|
1885
2062
|
// src/client/handlers/auth.ts
|
|
1886
2063
|
import {
|
|
1887
|
-
existsSync as
|
|
1888
|
-
readFileSync as
|
|
2064
|
+
existsSync as existsSync7,
|
|
2065
|
+
readFileSync as readFileSync8,
|
|
1889
2066
|
writeFileSync as writeFileSync4,
|
|
1890
2067
|
renameSync as renameSync3,
|
|
1891
2068
|
mkdirSync as mkdirSync4
|
|
1892
2069
|
} from "fs";
|
|
1893
|
-
import { join as
|
|
2070
|
+
import { join as join6 } from "path";
|
|
1894
2071
|
import { isAbsolute as isAbsolute2 } from "path";
|
|
1895
2072
|
async function handleWhoami(client) {
|
|
1896
2073
|
const res = await client.getJson("/api/auth/whoami");
|
|
@@ -1973,12 +2150,12 @@ function writeWorkspaceLayer(token, kind, opts) {
|
|
|
1973
2150
|
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
2151
|
});
|
|
1975
2152
|
}
|
|
1976
|
-
const devJsonPath =
|
|
2153
|
+
const devJsonPath = join6(dir, ".siming", "dev.json");
|
|
1977
2154
|
let config = {};
|
|
1978
2155
|
let hadIsolationKeys = false;
|
|
1979
|
-
if (
|
|
2156
|
+
if (existsSync7(devJsonPath)) {
|
|
1980
2157
|
try {
|
|
1981
|
-
config = JSON.parse(
|
|
2158
|
+
config = JSON.parse(readFileSync8(devJsonPath, "utf-8"));
|
|
1982
2159
|
hadIsolationKeys = "serverPort" in config || "dbPrefix" in config;
|
|
1983
2160
|
} catch {
|
|
1984
2161
|
return fail({
|
|
@@ -1997,7 +2174,7 @@ function writeWorkspaceLayer(token, kind, opts) {
|
|
|
1997
2174
|
});
|
|
1998
2175
|
}
|
|
1999
2176
|
config.authToken = token;
|
|
2000
|
-
mkdirSync4(
|
|
2177
|
+
mkdirSync4(join6(dir, ".siming"), { recursive: true });
|
|
2001
2178
|
const tmp = `${devJsonPath}.tmp`;
|
|
2002
2179
|
writeFileSync4(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2003
2180
|
renameSync3(tmp, devJsonPath);
|
|
@@ -2739,7 +2916,7 @@ function createMcpServer() {
|
|
|
2739
2916
|
let fullText = content;
|
|
2740
2917
|
if (file) {
|
|
2741
2918
|
try {
|
|
2742
|
-
fullText =
|
|
2919
|
+
fullText = readFileSync9(file, "utf-8");
|
|
2743
2920
|
} catch (e) {
|
|
2744
2921
|
return localError(`\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${file}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`);
|
|
2745
2922
|
}
|
|
@@ -2978,7 +3155,7 @@ async function mcpServeCommand() {
|
|
|
2978
3155
|
|
|
2979
3156
|
// src/commands/task/command.ts
|
|
2980
3157
|
import { Command as Command2 } from "commander";
|
|
2981
|
-
import { readFileSync as
|
|
3158
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
2982
3159
|
import { z as z5 } from "zod";
|
|
2983
3160
|
import {
|
|
2984
3161
|
ARTIFACT_TYPES as ARTIFACT_TYPES3,
|
|
@@ -3092,7 +3269,7 @@ function renderResult(format, result, opts) {
|
|
|
3092
3269
|
}
|
|
3093
3270
|
|
|
3094
3271
|
// src/commands/opts.ts
|
|
3095
|
-
import { readFileSync as
|
|
3272
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
3096
3273
|
import "commander";
|
|
3097
3274
|
import { z as z4 } from "zod";
|
|
3098
3275
|
import { canonicalizeReferencePath, createReferencesSchema } from "@siming-org/core";
|
|
@@ -3147,7 +3324,7 @@ function parseRefFlags(refs, mainDocName) {
|
|
|
3147
3324
|
seen.add(canonicalizeReferencePath(relPath));
|
|
3148
3325
|
let content;
|
|
3149
3326
|
try {
|
|
3150
|
-
content =
|
|
3327
|
+
content = readFileSync10(localPath, "utf-8");
|
|
3151
3328
|
} catch (e) {
|
|
3152
3329
|
return fail({
|
|
3153
3330
|
message: `--ref \u672C\u5730\u6587\u4EF6\u4E0D\u53EF\u8BFB\uFF1A"${localPath}"`,
|
|
@@ -3641,7 +3818,7 @@ function createTaskCommand() {
|
|
|
3641
3818
|
let content;
|
|
3642
3819
|
if (opts.file) {
|
|
3643
3820
|
try {
|
|
3644
|
-
content =
|
|
3821
|
+
content = readFileSync11(opts.file, "utf-8");
|
|
3645
3822
|
} catch (e) {
|
|
3646
3823
|
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
3824
|
return;
|
|
@@ -3896,18 +4073,18 @@ import { Command as Command3 } from "commander";
|
|
|
3896
4073
|
import { SKILL_MAIN_DOC, SkillScopeSchema } from "@siming-org/core";
|
|
3897
4074
|
|
|
3898
4075
|
// src/client/scaffold-templates.ts
|
|
3899
|
-
import { existsSync as
|
|
3900
|
-
import { dirname as dirname3, join as
|
|
4076
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync12 } from "fs";
|
|
4077
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
3901
4078
|
import { fileURLToPath } from "url";
|
|
3902
|
-
var SCAFFOLD_DIR =
|
|
4079
|
+
var SCAFFOLD_DIR = join7("templates", "project-skills");
|
|
3903
4080
|
var KEBAB_CASE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
3904
4081
|
function resolveCliPackageRoot() {
|
|
3905
4082
|
let dir = dirname3(fileURLToPath(import.meta.url));
|
|
3906
4083
|
for (let depth = 0; depth < 10; depth += 1) {
|
|
3907
|
-
const pkgFile =
|
|
3908
|
-
if (
|
|
4084
|
+
const pkgFile = join7(dir, "package.json");
|
|
4085
|
+
if (existsSync8(pkgFile)) {
|
|
3909
4086
|
try {
|
|
3910
|
-
const pkg = JSON.parse(
|
|
4087
|
+
const pkg = JSON.parse(readFileSync12(pkgFile, "utf-8"));
|
|
3911
4088
|
if (pkg.name === "@siming-org/cli") return ok2(dir);
|
|
3912
4089
|
} catch {
|
|
3913
4090
|
}
|
|
@@ -3925,10 +4102,10 @@ function resolveCliPackageRoot() {
|
|
|
3925
4102
|
function listScaffoldTemplates() {
|
|
3926
4103
|
const root = resolveCliPackageRoot();
|
|
3927
4104
|
if (!root.success) return root;
|
|
3928
|
-
const dir =
|
|
4105
|
+
const dir = join7(root.data, SCAFFOLD_DIR);
|
|
3929
4106
|
let entries;
|
|
3930
4107
|
try {
|
|
3931
|
-
entries =
|
|
4108
|
+
entries = readdirSync2(dir);
|
|
3932
4109
|
} catch {
|
|
3933
4110
|
return fail({
|
|
3934
4111
|
message: `\u9AA8\u67B6\u6A21\u677F\u76EE\u5F55\u7F3A\u5931\uFF1A${SCAFFOLD_DIR}`,
|
|
@@ -3987,7 +4164,7 @@ function loadScaffoldTemplate(name) {
|
|
|
3987
4164
|
if (!root.success) return root;
|
|
3988
4165
|
let text;
|
|
3989
4166
|
try {
|
|
3990
|
-
text =
|
|
4167
|
+
text = readFileSync12(join7(root.data, SCAFFOLD_DIR, `${name}.md`), "utf-8");
|
|
3991
4168
|
} catch (e) {
|
|
3992
4169
|
return fail({
|
|
3993
4170
|
message: `\u9AA8\u67B6\u6587\u4EF6\u4E0D\u53EF\u8BFB\uFF1A${SCAFFOLD_DIR}/${name}.md`,
|
|
@@ -4809,7 +4986,7 @@ function agentFields(a) {
|
|
|
4809
4986
|
}
|
|
4810
4987
|
|
|
4811
4988
|
// src/commands/dag/command.ts
|
|
4812
|
-
import { readFileSync as
|
|
4989
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
4813
4990
|
import { Command as Command5 } from "commander";
|
|
4814
4991
|
function createDagCommand() {
|
|
4815
4992
|
const dag = new Command5("dag").description("DAG template & instance management");
|
|
@@ -4867,7 +5044,7 @@ function createDagCommand() {
|
|
|
4867
5044
|
const client = createClient(opts.url);
|
|
4868
5045
|
let text;
|
|
4869
5046
|
try {
|
|
4870
|
-
text =
|
|
5047
|
+
text = readFileSync13(file, "utf-8");
|
|
4871
5048
|
} catch (e) {
|
|
4872
5049
|
renderResult("json", fail({
|
|
4873
5050
|
message: `\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${file}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
|
|
@@ -5213,7 +5390,7 @@ function diffSummary(item) {
|
|
|
5213
5390
|
}
|
|
5214
5391
|
|
|
5215
5392
|
// src/commands/node/command.ts
|
|
5216
|
-
import { readFileSync as
|
|
5393
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
5217
5394
|
import { Command as Command6 } from "commander";
|
|
5218
5395
|
import { SkillScopeSchema as SkillScopeSchema2 } from "@siming-org/core";
|
|
5219
5396
|
|
|
@@ -5451,7 +5628,7 @@ function createNodeCommand() {
|
|
|
5451
5628
|
let prompt = opts.prompt ?? "";
|
|
5452
5629
|
if (opts.promptFile !== void 0) {
|
|
5453
5630
|
try {
|
|
5454
|
-
prompt =
|
|
5631
|
+
prompt = readFileSync14(opts.promptFile, "utf-8");
|
|
5455
5632
|
} catch (e) {
|
|
5456
5633
|
renderResult("json", fail({
|
|
5457
5634
|
message: `prompt \u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${opts.promptFile}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
|
|
@@ -5496,7 +5673,7 @@ function createNodeCommand() {
|
|
|
5496
5673
|
let prompt;
|
|
5497
5674
|
if (opts.promptFile !== void 0) {
|
|
5498
5675
|
try {
|
|
5499
|
-
prompt =
|
|
5676
|
+
prompt = readFileSync14(opts.promptFile, "utf-8");
|
|
5500
5677
|
} catch (e) {
|
|
5501
5678
|
renderResult("json", fail({
|
|
5502
5679
|
message: `prompt \u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${opts.promptFile}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
|
|
@@ -5511,7 +5688,7 @@ function createNodeCommand() {
|
|
|
5511
5688
|
if (opts.checksFile !== void 0) {
|
|
5512
5689
|
let parsed;
|
|
5513
5690
|
try {
|
|
5514
|
-
parsed = JSON.parse(
|
|
5691
|
+
parsed = JSON.parse(readFileSync14(opts.checksFile, "utf-8"));
|
|
5515
5692
|
} catch (e) {
|
|
5516
5693
|
renderResult("json", fail({
|
|
5517
5694
|
message: `checks \u6587\u4EF6\u8BFB\u53D6/\u89E3\u6790\u5931\u8D25: ${opts.checksFile}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
|
|
@@ -5619,7 +5796,7 @@ function nodeFields2(n) {
|
|
|
5619
5796
|
import { execFile as execFile2 } from "child_process";
|
|
5620
5797
|
import { mkdtempSync, rmSync as rmSync2 } from "fs";
|
|
5621
5798
|
import { tmpdir } from "os";
|
|
5622
|
-
import { join as
|
|
5799
|
+
import { join as join8 } from "path";
|
|
5623
5800
|
import { promisify as promisify2 } from "util";
|
|
5624
5801
|
import { createInterface } from "readline/promises";
|
|
5625
5802
|
import { Command as Command7 } from "commander";
|
|
@@ -6061,7 +6238,7 @@ ${conflicts.map((item) => ` ${item.kind}:${item.key} \u2014 ${item.reason ?? "\
|
|
|
6061
6238
|
async function fetchManifest(pkgSpec, registry) {
|
|
6062
6239
|
let dir;
|
|
6063
6240
|
try {
|
|
6064
|
-
dir = mkdtempSync(
|
|
6241
|
+
dir = mkdtempSync(join8(tmpdir(), "siming-flow-"));
|
|
6065
6242
|
} catch (error) {
|
|
6066
6243
|
return fail({
|
|
6067
6244
|
message: `\u4E34\u65F6\u76EE\u5F55\u521B\u5EFA\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -6098,7 +6275,7 @@ async function fetchManifest(pkgSpec, registry) {
|
|
|
6098
6275
|
}
|
|
6099
6276
|
const { stdout: manifestText } = await execFileAsync2(
|
|
6100
6277
|
"tar",
|
|
6101
|
-
["-xOf",
|
|
6278
|
+
["-xOf", join8(dir, filename), "package/siming-nodes.json"],
|
|
6102
6279
|
{ maxBuffer: MANIFEST_LIMITS.maxChars * 2 + 64 * 1024 }
|
|
6103
6280
|
);
|
|
6104
6281
|
const parsed = parseFlowManifest(manifestText);
|
|
@@ -6969,7 +7146,7 @@ function createProjectCommand() {
|
|
|
6969
7146
|
const result = await handleGetProject(client, id);
|
|
6970
7147
|
renderResult(opts.format, result, { fields: result.success ? projectFields(result.data) : [] });
|
|
6971
7148
|
});
|
|
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>", "
|
|
7149
|
+
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
7150
|
const client = createClient(opts.url);
|
|
6974
7151
|
const parseBool = (v, flag) => {
|
|
6975
7152
|
if (v === void 0) return void 0;
|
|
@@ -6993,6 +7170,7 @@ function createProjectCommand() {
|
|
|
6993
7170
|
vcsProvided = true;
|
|
6994
7171
|
}
|
|
6995
7172
|
if (opts.vcsRepoPath !== void 0) {
|
|
7173
|
+
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
7174
|
vcsFields.repoPath = opts.vcsRepoPath;
|
|
6997
7175
|
vcsProvided = true;
|
|
6998
7176
|
}
|
|
@@ -7201,7 +7379,7 @@ function createConfigCommand() {
|
|
|
7201
7379
|
|
|
7202
7380
|
// src/commands/export/command.ts
|
|
7203
7381
|
import { Command as Command11 } from "commander";
|
|
7204
|
-
import { readFileSync as
|
|
7382
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
7205
7383
|
import { parse as parseYaml2 } from "yaml";
|
|
7206
7384
|
|
|
7207
7385
|
// src/client/handlers/export.ts
|
|
@@ -7872,7 +8050,7 @@ function createExportCommand() {
|
|
|
7872
8050
|
async function runExportPlan(client, filePath, typesArg) {
|
|
7873
8051
|
let raw;
|
|
7874
8052
|
try {
|
|
7875
|
-
const text =
|
|
8053
|
+
const text = readFileSync15(filePath, "utf-8");
|
|
7876
8054
|
raw = parseYaml2(text);
|
|
7877
8055
|
} catch (e) {
|
|
7878
8056
|
return fail({
|
|
@@ -7897,7 +8075,7 @@ async function runExportPlan(client, filePath, typesArg) {
|
|
|
7897
8075
|
}
|
|
7898
8076
|
function loadPlanFile(path) {
|
|
7899
8077
|
try {
|
|
7900
|
-
const text =
|
|
8078
|
+
const text = readFileSync15(path, "utf-8");
|
|
7901
8079
|
const parsed = JSON.parse(text);
|
|
7902
8080
|
if (!Array.isArray(parsed.items)) {
|
|
7903
8081
|
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 +8138,14 @@ function renderReport(report, format) {
|
|
|
7960
8138
|
|
|
7961
8139
|
// src/commands/install/command.ts
|
|
7962
8140
|
import { Command as Command12 } from "commander";
|
|
7963
|
-
import { existsSync as
|
|
8141
|
+
import { existsSync as existsSync10 } from "fs";
|
|
7964
8142
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
7965
|
-
import { dirname as dirname5, join as
|
|
8143
|
+
import { dirname as dirname5, join as join10 } from "path";
|
|
7966
8144
|
import { AGENT_MAIN_DOC as AGENT_MAIN_DOC5, SKILL_MAIN_DOC as SKILL_MAIN_DOC5, VERSION as VERSION3 } from "@siming-org/core";
|
|
7967
8145
|
|
|
7968
8146
|
// src/commands/install/writer.ts
|
|
7969
|
-
import { existsSync as
|
|
7970
|
-
import { dirname as dirname4, join as
|
|
8147
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync16, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
8148
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
7971
8149
|
import { homedir as homedir4 } from "os";
|
|
7972
8150
|
import { parse as parseYaml3, stringify as stringifyYaml } from "yaml";
|
|
7973
8151
|
import {
|
|
@@ -7978,12 +8156,12 @@ import {
|
|
|
7978
8156
|
} from "@siming-org/core";
|
|
7979
8157
|
function agentsHomeDir() {
|
|
7980
8158
|
const env = process.env.SIMING_AGENTS_HOME;
|
|
7981
|
-
return env && env.length > 0 ? env :
|
|
8159
|
+
return env && env.length > 0 ? env : join9(homedir4(), ".agents");
|
|
7982
8160
|
}
|
|
7983
8161
|
function resolveInstallTargets(scope, workspaceDir) {
|
|
7984
8162
|
if (scope === "global") {
|
|
7985
8163
|
const dir = agentsHomeDir();
|
|
7986
|
-
return { targets: { dir, mcpConfigPath:
|
|
8164
|
+
return { targets: { dir, mcpConfigPath: join9(dir, "mcp.json") } };
|
|
7987
8165
|
}
|
|
7988
8166
|
const wd = workspaceDir ?? findWorkspaceDir();
|
|
7989
8167
|
if (!wd) {
|
|
@@ -7991,7 +8169,7 @@ function resolveInstallTargets(scope, workspaceDir) {
|
|
|
7991
8169
|
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
8170
|
};
|
|
7993
8171
|
}
|
|
7994
|
-
return { targets: { dir:
|
|
8172
|
+
return { targets: { dir: join9(wd, ".agents"), mcpConfigPath: null } };
|
|
7995
8173
|
}
|
|
7996
8174
|
function isSafeAssetName(name) {
|
|
7997
8175
|
if (name.length === 0 || name === "." || name === "..") return false;
|
|
@@ -8011,7 +8189,7 @@ function validateReferencesLocally(mainDocName, references) {
|
|
|
8011
8189
|
function writeAssetDirectory(assetDir, files) {
|
|
8012
8190
|
rmSync3(assetDir, { recursive: true, force: true });
|
|
8013
8191
|
for (const file of files) {
|
|
8014
|
-
const target =
|
|
8192
|
+
const target = join9(assetDir, ...file.relPath.split("/"));
|
|
8015
8193
|
mkdirSync5(dirname4(target), { recursive: true });
|
|
8016
8194
|
writeFileSync5(target, file.content, "utf-8");
|
|
8017
8195
|
}
|
|
@@ -8028,7 +8206,7 @@ ${skill.content}
|
|
|
8028
8206
|
`;
|
|
8029
8207
|
}
|
|
8030
8208
|
function skillTargetPath(dir, skill) {
|
|
8031
|
-
return
|
|
8209
|
+
return join9(dir, "skills", skill.name, "SKILL.md");
|
|
8032
8210
|
}
|
|
8033
8211
|
function permissionsToMatrix(permissions) {
|
|
8034
8212
|
const matrix = {};
|
|
@@ -8064,13 +8242,13 @@ ${agent.systemPrompt}
|
|
|
8064
8242
|
`;
|
|
8065
8243
|
}
|
|
8066
8244
|
function agentTargetPath(dir, agent) {
|
|
8067
|
-
return
|
|
8245
|
+
return join9(dir, "agents", agent.name, "agent.md");
|
|
8068
8246
|
}
|
|
8069
8247
|
function readSimingVersion(path) {
|
|
8070
|
-
if (!
|
|
8248
|
+
if (!existsSync9(path)) return null;
|
|
8071
8249
|
let text;
|
|
8072
8250
|
try {
|
|
8073
|
-
text =
|
|
8251
|
+
text = readFileSync16(path, "utf-8");
|
|
8074
8252
|
} catch {
|
|
8075
8253
|
return null;
|
|
8076
8254
|
}
|
|
@@ -8092,10 +8270,10 @@ function isPlainObject(v) {
|
|
|
8092
8270
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
8093
8271
|
}
|
|
8094
8272
|
function readMcpConfig(path) {
|
|
8095
|
-
if (!
|
|
8273
|
+
if (!existsSync9(path)) return { state: "absent" };
|
|
8096
8274
|
let text;
|
|
8097
8275
|
try {
|
|
8098
|
-
text =
|
|
8276
|
+
text = readFileSync16(path, "utf-8");
|
|
8099
8277
|
} catch (e) {
|
|
8100
8278
|
return { state: "corrupt", error: `\u8BFB\u53D6\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}` };
|
|
8101
8279
|
}
|
|
@@ -8332,7 +8510,7 @@ function compareSkill(s, dir) {
|
|
|
8332
8510
|
if (!isSafeAssetName(s.name)) {
|
|
8333
8511
|
return { ...base, action: "conflict", reason: `unsafe asset name "${s.name}"` };
|
|
8334
8512
|
}
|
|
8335
|
-
if (!
|
|
8513
|
+
if (!existsSync10(path)) return { ...base, action: "install", reason: "not installed locally" };
|
|
8336
8514
|
const marked = readSimingVersion(path);
|
|
8337
8515
|
if (marked === null) {
|
|
8338
8516
|
return { ...base, localVersion: "unknown", action: "conflict", reason: "no siming-version marker (third-party asset; use --force to overwrite)" };
|
|
@@ -8350,7 +8528,7 @@ function compareAgent(a, dir) {
|
|
|
8350
8528
|
if (!isSafeAssetName(a.name)) {
|
|
8351
8529
|
return { ...base, action: "conflict", reason: `unsafe asset name "${a.name}"` };
|
|
8352
8530
|
}
|
|
8353
|
-
if (!
|
|
8531
|
+
if (!existsSync10(path)) return { ...base, action: "install", reason: "not installed locally" };
|
|
8354
8532
|
const marked = readSimingVersion(path);
|
|
8355
8533
|
if (marked === null) {
|
|
8356
8534
|
return { ...base, localVersion: "unknown", action: "conflict", reason: "no siming-version marker (third-party asset; use --force to overwrite)" };
|
|
@@ -8443,7 +8621,7 @@ async function applyInstall(client, scope, projectId, items, opts) {
|
|
|
8443
8621
|
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
8622
|
continue;
|
|
8445
8623
|
}
|
|
8446
|
-
const targetExists =
|
|
8624
|
+
const targetExists = existsSync10(targetPath);
|
|
8447
8625
|
writeAssetDirectory(dirname5(targetPath), [
|
|
8448
8626
|
{ relPath: SKILL_MAIN_DOC5, content: skillToMarkdown(r.data) },
|
|
8449
8627
|
...(r.data.references ?? []).map((ref) => ({ relPath: ref.path, content: ref.content }))
|
|
@@ -8479,7 +8657,7 @@ async function applyInstall(client, scope, projectId, items, opts) {
|
|
|
8479
8657
|
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
8658
|
continue;
|
|
8481
8659
|
}
|
|
8482
|
-
const targetExists =
|
|
8660
|
+
const targetExists = existsSync10(targetPath);
|
|
8483
8661
|
writeAssetDirectory(dirname5(targetPath), [
|
|
8484
8662
|
{ relPath: AGENT_MAIN_DOC5, content: agentToMarkdown(r.data, realModel) },
|
|
8485
8663
|
...(r.data.references ?? []).map((ref) => ({ relPath: ref.path, content: ref.content }))
|
|
@@ -8503,8 +8681,8 @@ function buildInstallGuidance(scope, targets) {
|
|
|
8503
8681
|
const lines = [];
|
|
8504
8682
|
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
8683
|
lines.push(`[1] \u8D44\u4EA7\u4F4D\u7F6E\uFF08${scope === "global" ? "\u7528\u6237\u7EA7" : "\u9879\u76EE\u7EA7"} ${targets.dir}\uFF09\uFF1A`);
|
|
8506
|
-
lines.push(` - skills: ${
|
|
8507
|
-
lines.push(` - agents: ${
|
|
8684
|
+
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`);
|
|
8685
|
+
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
8686
|
if (targets.mcpConfigPath) {
|
|
8509
8687
|
lines.push(` - MCP \u6CE8\u518C: ${targets.mcpConfigPath}\uFF08\u9876\u5C42 mcpServers.siming \u2192 stdio \u547D\u4EE4 "siming mcp-serve"\uFF09`);
|
|
8510
8688
|
}
|
|
@@ -8890,7 +9068,8 @@ ${ROADMAP_TEXT}
|
|
|
8890
9068
|
|
|
8891
9069
|
\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
9070
|
\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
|
|
9071
|
+
\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
|
|
9072
|
+
\u7B2C 9 \u9879\u4E3A\u884C\u4E3A\u89C4\u5219\u91CD\u63D0\u793A\uFF08\u89C1\u8868\u683C\u540E\u8BF4\u660E\uFF09\uFF1A
|
|
8894
9073
|
|
|
8895
9074
|
| # | \u58F0\u660E\u9879 | \u58F0\u660E\u5185\u5BB9 | \u7F3A\u5931\u540E\u679C |
|
|
8896
9075
|
|---|--------|----------|----------|
|
|
@@ -8902,6 +9081,10 @@ ${ROADMAP_TEXT}
|
|
|
8902
9081
|
| 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
9082
|
| 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
9083
|
| 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 |
|
|
9084
|
+
| 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 |
|
|
9085
|
+
|
|
9086
|
+
\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
|
|
9087
|
+
\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
9088
|
|
|
8906
9089
|
----------------------------------------------------------------
|
|
8907
9090
|
\u4E8C\u3001\u6D41\u7A0B\u6A21\u677F\u83B7\u53D6\u6307\u5F15
|
|
@@ -9092,8 +9275,8 @@ import { VERSION as VERSION4, SIMING_CONFIG_KEYS as SIMING_CONFIG_KEYS2 } from "
|
|
|
9092
9275
|
|
|
9093
9276
|
// src/commands/server/daemon.ts
|
|
9094
9277
|
import { execFileSync } from "child_process";
|
|
9095
|
-
import { closeSync, existsSync as
|
|
9096
|
-
import { join as
|
|
9278
|
+
import { closeSync, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync17, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
9279
|
+
import { join as join11, resolve as resolve2 } from "path";
|
|
9097
9280
|
import { spawn } from "child_process";
|
|
9098
9281
|
|
|
9099
9282
|
// src/commands/server/probe.ts
|
|
@@ -9110,10 +9293,10 @@ function runtimeDir() {
|
|
|
9110
9293
|
return process.env.SIMING_RUNTIME_DIR ?? "/tmp/siming";
|
|
9111
9294
|
}
|
|
9112
9295
|
function pidfilePath() {
|
|
9113
|
-
return
|
|
9296
|
+
return join11(runtimeDir(), "server.pid");
|
|
9114
9297
|
}
|
|
9115
9298
|
function logfilePath() {
|
|
9116
|
-
return
|
|
9299
|
+
return join11(runtimeDir(), "server.log");
|
|
9117
9300
|
}
|
|
9118
9301
|
var READY_TIMEOUT_MS = 2e4;
|
|
9119
9302
|
var STOP_GRACE_MS = 1e4;
|
|
@@ -9146,7 +9329,7 @@ function readPidFile() {
|
|
|
9146
9329
|
try {
|
|
9147
9330
|
const st = lstatSync(pidfilePath(), { throwIfNoEntry: false });
|
|
9148
9331
|
if (st === void 0 || st.isSymbolicLink()) return null;
|
|
9149
|
-
const parsed = JSON.parse(
|
|
9332
|
+
const parsed = JSON.parse(readFileSync17(pidfilePath(), "utf-8").trim());
|
|
9150
9333
|
if (typeof parsed.pid !== "number" || typeof parsed.startedAt !== "number" || typeof parsed.port !== "number") {
|
|
9151
9334
|
return null;
|
|
9152
9335
|
}
|
|
@@ -9187,7 +9370,7 @@ function removePidFile(expectedPid) {
|
|
|
9187
9370
|
}
|
|
9188
9371
|
function readCmdline(pid) {
|
|
9189
9372
|
try {
|
|
9190
|
-
const raw =
|
|
9373
|
+
const raw = readFileSync17(`/proc/${pid}/cmdline`);
|
|
9191
9374
|
const joined = raw.toString().split("\0").filter((s) => s.length > 0).join(" ");
|
|
9192
9375
|
if (joined.length > 0) return joined;
|
|
9193
9376
|
} catch {
|
|
@@ -9216,7 +9399,7 @@ function isSimingDaemonCmdline(cmdline, expectedEntry) {
|
|
|
9216
9399
|
}
|
|
9217
9400
|
function readProcessStartMs(pid) {
|
|
9218
9401
|
try {
|
|
9219
|
-
const stat =
|
|
9402
|
+
const stat = readFileSync17(`/proc/${pid}/stat`, "utf-8");
|
|
9220
9403
|
const rest = stat.slice(stat.lastIndexOf(")") + 2);
|
|
9221
9404
|
const fields = rest.split(" ");
|
|
9222
9405
|
const starttimeJiffies = Number(fields[19]);
|
|
@@ -9245,7 +9428,7 @@ function readProcessStartMs(pid) {
|
|
|
9245
9428
|
}
|
|
9246
9429
|
function readBtimeFromProcStat() {
|
|
9247
9430
|
try {
|
|
9248
|
-
const stat =
|
|
9431
|
+
const stat = readFileSync17("/proc/stat", "utf-8");
|
|
9249
9432
|
for (const line of stat.split("\n")) {
|
|
9250
9433
|
if (line.startsWith("btime ")) {
|
|
9251
9434
|
const v = Number(line.slice(6).trim());
|
|
@@ -9296,14 +9479,14 @@ function sleep(ms) {
|
|
|
9296
9479
|
function readLogTail(lines = 30) {
|
|
9297
9480
|
const st = lstatSync(logfilePath(), { throwIfNoEntry: false });
|
|
9298
9481
|
if (st === void 0 || st.isSymbolicLink()) return null;
|
|
9299
|
-
const content =
|
|
9482
|
+
const content = readFileSync17(logfilePath(), "utf-8");
|
|
9300
9483
|
const all = content.split("\n");
|
|
9301
9484
|
if (all.at(-1) === "") all.pop();
|
|
9302
9485
|
return all.slice(-lines);
|
|
9303
9486
|
}
|
|
9304
9487
|
function spawnDaemon(configEnv) {
|
|
9305
9488
|
const entry = resolve2(process.argv[1] ?? "");
|
|
9306
|
-
if (entry.length === 0 || !
|
|
9489
|
+
if (entry.length === 0 || !existsSync11(entry)) {
|
|
9307
9490
|
throw new Error(`\u65E0\u6CD5\u5B9A\u4F4D siming \u5165\u53E3\u811A\u672C\uFF08process.argv[1]=${process.argv[1] ?? "(\u7A7A)"}\uFF09`);
|
|
9308
9491
|
}
|
|
9309
9492
|
if (entry.endsWith(".ts")) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siming-org/cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
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/
|
|
31
|
-
"@siming-org/
|
|
30
|
+
"@siming-org/server": "0.8.4",
|
|
31
|
+
"@siming-org/core": "0.8.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"tsup": "^8.3.5",
|