@siming-org/cli 0.8.0 → 0.8.2
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
|
@@ -675,15 +675,764 @@ import { readFileSync as readFileSync5 } from "fs";
|
|
|
675
675
|
import {
|
|
676
676
|
ARTIFACT_TYPES
|
|
677
677
|
} from "@siming-org/core";
|
|
678
|
+
|
|
679
|
+
// src/client/checks/types.ts
|
|
680
|
+
var NA_PHRASES = ["\u4E0D\u9002\u7528", "N/A", "not applicable", "n-a"];
|
|
681
|
+
var MIN_AI_CHECK_ITEM_LENGTH = 15;
|
|
682
|
+
var VAGUE_AI_CHECK_BLACKLIST = ["\u5DF2\u5B8C\u6210", "\u901A\u8FC7", "\u5B8C\u6210", "done", "ok", "pass"];
|
|
683
|
+
|
|
684
|
+
// src/client/checks/git-checks.ts
|
|
685
|
+
function naIfRepoUnavailable(ctx) {
|
|
686
|
+
if (ctx.vcsLoadError !== void 0) {
|
|
687
|
+
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` };
|
|
688
|
+
}
|
|
689
|
+
if (ctx.vcs.enabled === false) {
|
|
690
|
+
return na("\u9879\u76EE vcs.enabled=false\uFF08\u58F0\u660E\u65E0\u5206\u652F\u6A21\u578B\uFF09\uFF0Cgit \u7C7B\u68C0\u67E5\u8C41\u514D");
|
|
691
|
+
}
|
|
692
|
+
if (ctx.repoPath === null) {
|
|
693
|
+
return na(`\u4ED3\u5E93\u4E0D\u53EF\u7528\uFF1A${ctx.repoProbeDetail}`);
|
|
694
|
+
}
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
function na(reason) {
|
|
698
|
+
return { type: "", status: "not_applicable", detail: reason };
|
|
699
|
+
}
|
|
700
|
+
function fail2(ctx, type, rootCause, fix) {
|
|
701
|
+
return { type, status: "fail", detail: `${rootCause}\u3002\u4FEE\u590D\uFF1A${fix}\uFF08\u5F53\u524D nodeId=${ctx.nodeId}\uFF09` };
|
|
702
|
+
}
|
|
703
|
+
async function currentBranch(ctx) {
|
|
704
|
+
const r = await ctx.runGit(["rev-parse", "--abbrev-ref", "HEAD"], ctx.repoPath);
|
|
705
|
+
return r.ok ? r.stdout.trim() : null;
|
|
706
|
+
}
|
|
707
|
+
async function checkBranchExists(ctx) {
|
|
708
|
+
const type = "git-branch-exists";
|
|
709
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
710
|
+
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}` };
|
|
722
|
+
}
|
|
723
|
+
async function baseMergedIntoHead(ctx, base) {
|
|
724
|
+
const f = await ctx.runGit(["fetch", "origin", base], ctx.repoPath);
|
|
725
|
+
if (!f.ok) return null;
|
|
726
|
+
const r = await ctx.runGit(["merge-base", "--is-ancestor", `origin/${base}`, "HEAD"], ctx.repoPath);
|
|
727
|
+
return r.ok;
|
|
728
|
+
}
|
|
729
|
+
async function checkBaseMerged(ctx) {
|
|
730
|
+
const type = "git-base-merged";
|
|
731
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
732
|
+
if (guard) return { ...guard, type };
|
|
733
|
+
const waived = findWaivedDecision(ctx, "ALIGN");
|
|
734
|
+
if (waived !== null) {
|
|
735
|
+
return { type, status: "not_applicable", detail: `ALIGN \u8BB0\u5F55\u542B merge-waived \u8C41\u514D\uFF08decision ${waived}\uFF0C\u7528\u6237\u660E\u793A\u4E0D\u9700\u5408\u5E76\uFF09` };
|
|
736
|
+
}
|
|
737
|
+
if (ctx.baseBranch === null) {
|
|
738
|
+
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
|
+
}
|
|
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` };
|
|
748
|
+
}
|
|
749
|
+
function findWaivedDecision(ctx, nodeId) {
|
|
750
|
+
const record = ctx.nodeRecords[nodeId];
|
|
751
|
+
const hit = record?.decisions.find((d) => d.topic === "merge-waived" && d.decision.trim() !== "");
|
|
752
|
+
return hit ? hit.id : null;
|
|
753
|
+
}
|
|
754
|
+
async function checkWorktreeClean(ctx) {
|
|
755
|
+
const type = "git-worktree-clean";
|
|
756
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
757
|
+
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" };
|
|
768
|
+
}
|
|
769
|
+
async function checkArchiveMerged(ctx) {
|
|
770
|
+
const type = "git-archive-merged";
|
|
771
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
772
|
+
if (guard) return { ...guard, type };
|
|
773
|
+
if (ctx.vcs.archiveToBase === false) {
|
|
774
|
+
return { type, status: "not_applicable", detail: "\u9879\u76EE vcs.archiveToBase=false\uFF08\u58F0\u660E\u65E0\u5F52\u6863\u5408\u5E76\u6D41\u7A0B\uFF09" };
|
|
775
|
+
}
|
|
776
|
+
const hasSuccessor = (ctx.edges ?? []).some((e) => e.from === ctx.nodeId);
|
|
777
|
+
if (hasSuccessor) {
|
|
778
|
+
return {
|
|
779
|
+
type,
|
|
780
|
+
status: "not_applicable",
|
|
781
|
+
detail: "\u975E\u7EC8\u6001\u5F62\u6001\uFF08\u5B58\u5728\u540E\u7EE7\u8282\u70B9\uFF09\uFF1A\u4EE3\u7801\u5F52\u6863\u7531\u5BA1\u6279\u901A\u8FC7\u540E\u7684\u5F52\u6863\u5F85\u529E\u627F\u8F7D\uFF0Cadvance \u65F6\u5F52\u6863\u5C1A\u672A\u53D1\u751F"
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
if (ctx.baseBranch === null) {
|
|
785
|
+
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
|
+
}
|
|
787
|
+
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` };
|
|
811
|
+
}
|
|
812
|
+
async function checkStayOnBase(ctx) {
|
|
813
|
+
const type = "git-stay-on-base";
|
|
814
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
815
|
+
if (guard) return { ...guard, type };
|
|
816
|
+
if (ctx.vcs.worktreeMode === true) {
|
|
817
|
+
return { type, status: "not_applicable", detail: "worktreeMode=true\uFF08worktree \u5E76\u884C\u5F00\u53D1\uFF0C\u4E0D\u8981\u6C42\u505C\u7559\u57FA\u7840\u5206\u652F\uFF09" };
|
|
818
|
+
}
|
|
819
|
+
if (ctx.vcs.archiveToBase === false) {
|
|
820
|
+
return { type, status: "not_applicable", detail: "\u9879\u76EE vcs.archiveToBase=false\uFF08\u65E0\u5F52\u6863\u6D41\u7A0B\uFF0C\u7EC8\u6001\u505C\u7559\u4EFB\u52A1\u5206\u652F\uFF09" };
|
|
821
|
+
}
|
|
822
|
+
if (ctx.baseBranch === null) {
|
|
823
|
+
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
|
+
}
|
|
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}` };
|
|
833
|
+
}
|
|
834
|
+
function firstLine(text) {
|
|
835
|
+
return text.trim().split("\n")[0] ?? "";
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/client/checks/record-checks.ts
|
|
839
|
+
function decisionByTopic(record, topic) {
|
|
840
|
+
const hit = record?.decisions.find((d) => d.topic === topic && d.decision.trim() !== "");
|
|
841
|
+
return hit ?? null;
|
|
842
|
+
}
|
|
843
|
+
async function checkTestReport(ctx) {
|
|
844
|
+
const type = "record-test-report";
|
|
845
|
+
const report = ctx.record?.testReport;
|
|
846
|
+
if (!report) {
|
|
847
|
+
return fail3(type, "\u672C\u8282\u70B9\u8BB0\u5F55\u672A\u5199 testReport\uFF08\u7ED3\u6784\u5316\u5B57\u6BB5\u7F3A\u5931\uFF09", "record-set \u5199 testReport {command,passed,failed,skipped}\uFF08\u771F\u5B9E\u6267\u884C\u7684\u6D4B\u8BD5\u547D\u4EE4\u4E0E\u7ED3\u679C\u8BA1\u6570\uFF09");
|
|
848
|
+
}
|
|
849
|
+
if (report.failed > 0) {
|
|
850
|
+
return fail3(type, `\u6D4B\u8BD5\u5B58\u5728\u5931\u8D25\uFF1Afailed=${report.failed}\uFF08${report.command}\uFF09`, "\u4FEE\u590D\u5931\u8D25\u7528\u4F8B\u6216\u7ECF\u7528\u6237\u6388\u6743\u540E\u8D70\u8C41\u514D\u901A\u9053\uFF0C\u7981\u6B62\u5E26\u5931\u8D25\u63A8\u8FDB");
|
|
851
|
+
}
|
|
852
|
+
if (report.skipped > 0) {
|
|
853
|
+
const waiver = decisionByTopic(ctx.record, "skip-authorized");
|
|
854
|
+
if (waiver === null) {
|
|
855
|
+
return fail3(type, `\u8DF3\u8FC7\u7528\u4F8B ${report.skipped} \u4E2A\u4F46\u65E0\u7528\u6237\u6388\u6743\u7559\u75D5\uFF08${report.command}\uFF09`, "\u7ECF\u7528\u6237\u660E\u793A\u6388\u6743\u540E record-decision-add \u5199 topic=skip-authorized\uFF0C\u6216\u6E05\u96F6 skipped");
|
|
856
|
+
}
|
|
857
|
+
return { type, status: "pass", detail: `\u6D4B\u8BD5\u901A\u8FC7\uFF08skipped=${report.skipped}\uFF0Cskip-authorized \u8C41\u514D decision ${waiver.id}\uFF09` };
|
|
858
|
+
}
|
|
859
|
+
return { type, status: "pass", detail: `\u6D4B\u8BD5\u5168\u7EFF\uFF1Apassed=${report.passed}\uFF08${report.command}\uFF09` };
|
|
860
|
+
}
|
|
861
|
+
async function checkNoFakeGreen(ctx) {
|
|
862
|
+
const type = "record-no-fake-green";
|
|
863
|
+
const waiver = decisionByTopic(ctx.record, "test-adjust-approved");
|
|
864
|
+
if (waiver !== null) {
|
|
865
|
+
return { type, status: "pass", detail: `\u6D4B\u8BD5\u8C03\u6574\u6301\u6279\u51C6\u7559\u75D5\uFF08decision ${waiver.id}\uFF09` };
|
|
866
|
+
}
|
|
867
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
868
|
+
if (guard) return { ...guard, type };
|
|
869
|
+
if (ctx.baseBranch === null) {
|
|
870
|
+
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
|
+
}
|
|
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" };
|
|
884
|
+
}
|
|
885
|
+
function scanTestDiff(patch) {
|
|
886
|
+
const violations = [];
|
|
887
|
+
let currentFile = "";
|
|
888
|
+
let deleting = false;
|
|
889
|
+
for (const line of patch.split("\n")) {
|
|
890
|
+
if (line.startsWith("diff --git ")) {
|
|
891
|
+
currentFile = line;
|
|
892
|
+
deleting = false;
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (line.startsWith("deleted file mode")) {
|
|
896
|
+
const m = currentFile.match(/^diff --git a\/(.+) b\//);
|
|
897
|
+
if (m) violations.push({ reason: `\u6D4B\u8BD5\u6587\u4EF6\u6574\u5220\uFF1A${m[1]}` });
|
|
898
|
+
deleting = true;
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
if (deleting) continue;
|
|
902
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
903
|
+
if (line.includes(".skip(") || line.includes(".only(")) {
|
|
904
|
+
violations.push({ reason: `\u65B0\u589E\u8DF3\u8FC7/\u805A\u7126\u6807\u8BB0\uFF1A${line.slice(1).trim().slice(0, 80)}` });
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return violations.map((v) => v.reason);
|
|
909
|
+
}
|
|
910
|
+
async function checkBaseReMerged(ctx) {
|
|
911
|
+
const type = "record-base-re-merged";
|
|
912
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
913
|
+
if (guard) return { ...guard, type };
|
|
914
|
+
const waived = findWaivedDecision(ctx, "INTEGRATE");
|
|
915
|
+
if (waived !== null) {
|
|
916
|
+
return { type, status: "not_applicable", detail: `INTEGRATE \u8BB0\u5F55\u542B merge-waived \u8C41\u514D\uFF08decision ${waived}\uFF09` };
|
|
917
|
+
}
|
|
918
|
+
if (ctx.baseBranch === null) {
|
|
919
|
+
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
|
+
}
|
|
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` };
|
|
930
|
+
}
|
|
931
|
+
async function checkFixReviewPass(ctx) {
|
|
932
|
+
const type = "record-fix-review-pass";
|
|
933
|
+
const fixes = ctx.record?.fixesApplied ?? 0;
|
|
934
|
+
if (fixes > 0) {
|
|
935
|
+
const verdict = ctx.record?.review?.verdict;
|
|
936
|
+
if (verdict !== "pass") {
|
|
937
|
+
return fail3(type, `\u58F0\u660E\u4FEE\u590D ${fixes} \u5904\u4F46\u8BC4\u5BA1\u7ED3\u8BBA\u975E pass\uFF08verdict=${verdict ?? "\u672A\u5199"}\uFF09`, "\u4FEE\u590D\u540E\u91CD\u4EA4 code-reviewer \u8BC4\u5BA1\u81F3 PASS\uFF08record-review-set\uFF09\uFF0C\u6216\u5C06 fixesApplied \u5982\u5B9E\u5F52\u96F6");
|
|
938
|
+
}
|
|
939
|
+
return { type, status: "pass", detail: `\u4FEE\u590D ${fixes} \u5904\u4E14\u8BC4\u5BA1 PASS` };
|
|
940
|
+
}
|
|
941
|
+
return { type, status: "pass", detail: "\u65E0\u4FEE\u590D\uFF08fixesApplied=0\uFF09" };
|
|
942
|
+
}
|
|
943
|
+
async function checkVerifiedCommit(ctx) {
|
|
944
|
+
const type = "record-verified-commit";
|
|
945
|
+
const sha = ctx.record?.verifiedCommit;
|
|
946
|
+
if (!sha) {
|
|
947
|
+
return fail3(type, "\u672C\u8282\u70B9\u8BB0\u5F55\u672A\u5199 verifiedCommit\uFF08\u96C6\u6210\u9A8C\u8BC1\u7248\u672C\u7F3A\u5931\uFF09", "record-set \u5199 verifiedCommit <sha>\uFF08\u7ECF\u9A8C\u8BC1\u7684 HEAD \u63D0\u4EA4\u53F7\uFF09");
|
|
948
|
+
}
|
|
949
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
950
|
+
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");
|
|
954
|
+
}
|
|
955
|
+
return { type, status: "pass", detail: `\u9A8C\u8BC1\u7248\u672C ${sha} \u5B58\u5728` };
|
|
956
|
+
}
|
|
957
|
+
async function checkPrevNodesDone(ctx) {
|
|
958
|
+
const type = "record-prev-nodes-done";
|
|
959
|
+
const predecessors = collectPredecessors(ctx.edges ?? [], ctx.nodeId);
|
|
960
|
+
const pending = ctx.nodes.filter(
|
|
961
|
+
(n) => predecessors.has(n.nodeId) && n.status !== "skipped" && n.status !== "completed"
|
|
962
|
+
);
|
|
963
|
+
if (pending.length > 0) {
|
|
964
|
+
const summary = pending.map((n) => `${n.nodeId}:${n.status}`).join(", ");
|
|
965
|
+
return fail3(type, `\u5B58\u5728\u672A\u5B8C\u6210\u524D\u5E8F\u8282\u70B9\uFF08${summary}\uFF09`, "\u6309 DAG \u987A\u5E8F\u5B8C\u6210\u524D\u7F6E\u8282\u70B9\uFF08\u6216\u660E\u786E\u8DF3\u8FC7\u8DEF\u5F84\uFF09\u540E\u518D\u63A8\u8FDB\u9A8C\u6536");
|
|
966
|
+
}
|
|
967
|
+
if (predecessors.size === 0) {
|
|
968
|
+
return { type, status: "pass", detail: "\u65E0\u6709\u5411\u8FB9\u53EF\u5224\u5B9A\u524D\u5E8F\uFF08\u95ED\u5305\u4E3A\u7A7A\uFF09\u2014\u2014\u89C6\u4E3A\u901A\u8FC7" };
|
|
969
|
+
}
|
|
970
|
+
return { type, status: "pass", detail: "\u524D\u5E8F\u8282\u70B9\u5168\u90E8\u5B8C\u6210\u6216\u5DF2\u8DF3\u8FC7" };
|
|
971
|
+
}
|
|
972
|
+
function collectPredecessors(edges, nodeId) {
|
|
973
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
974
|
+
for (const e of edges) {
|
|
975
|
+
const list = incoming.get(e.to) ?? [];
|
|
976
|
+
list.push(e.from);
|
|
977
|
+
incoming.set(e.to, list);
|
|
978
|
+
}
|
|
979
|
+
const seen = /* @__PURE__ */ new Set();
|
|
980
|
+
const queue = [...incoming.get(nodeId) ?? []];
|
|
981
|
+
while (queue.length > 0) {
|
|
982
|
+
const cur = queue.pop();
|
|
983
|
+
if (seen.has(cur)) continue;
|
|
984
|
+
seen.add(cur);
|
|
985
|
+
for (const prev of incoming.get(cur) ?? []) queue.push(prev);
|
|
986
|
+
}
|
|
987
|
+
return seen;
|
|
988
|
+
}
|
|
989
|
+
async function checkVersionConsistent(ctx) {
|
|
990
|
+
const type = "record-version-consistent";
|
|
991
|
+
const sha = ctx.nodeRecords["INTEGRATE"]?.verifiedCommit;
|
|
992
|
+
if (!sha) {
|
|
993
|
+
return fail3(type, "INTEGRATE \u8BB0\u5F55\u7F3A verifiedCommit\uFF08\u7248\u672C\u4E00\u81F4\u6027\u65E0\u4ECE\u5224\u5B9A\uFF09", "\u56DE\u8865 INTEGRATE record-set verifiedCommit \u540E\u91CD\u8BD5");
|
|
994
|
+
}
|
|
995
|
+
const guard = naIfRepoUnavailable(ctx);
|
|
996
|
+
if (guard) return { ...guard, type };
|
|
997
|
+
if (ctx.baseBranch === null) {
|
|
998
|
+
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
|
+
}
|
|
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");
|
|
1003
|
+
}
|
|
1004
|
+
const exempt = (path) => ctx.docPaths.some((p) => {
|
|
1005
|
+
if (p.endsWith("/")) return path.startsWith(p);
|
|
1006
|
+
if (p.startsWith("*")) return path.endsWith(p.slice(1));
|
|
1007
|
+
return path === p;
|
|
1008
|
+
});
|
|
1009
|
+
const leaked = r.stdout.split("\n").map((l) => l.trim()).filter((l) => l !== "" && !exempt(l));
|
|
1010
|
+
if (leaked.length > 0) {
|
|
1011
|
+
return fail3(
|
|
1012
|
+
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"
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
return { type, status: "pass", detail: `verifiedCommit ${sha} \u4E0E ${ctx.baseBranch} \u4E00\u81F4\uFF08\u6587\u6863\u8DEF\u5F84\u8C41\u514D ${ctx.docPaths.join(" ")}\uFF09` };
|
|
1018
|
+
}
|
|
1019
|
+
async function checkDeferredIssues(ctx) {
|
|
1020
|
+
const type = "record-deferred-issues";
|
|
1021
|
+
const ids = ctx.record?.deferredIssues ?? [];
|
|
1022
|
+
if (ids.length === 0) {
|
|
1023
|
+
return { type, status: "pass", detail: "\u65E0\u5EF6\u671F\u573A\u666F" };
|
|
1024
|
+
}
|
|
1025
|
+
const loaded = await ctx.loadIssueIds();
|
|
1026
|
+
if (!loaded.ok) {
|
|
1027
|
+
return fail3(type, `\u5173\u8054\u95EE\u9898\u53CD\u67E5\u5931\u8D25\uFF1A${loaded.error}`, "\u68C0\u67E5 server \u53EF\u7528\u6027\u540E\u91CD\u8BD5");
|
|
1028
|
+
}
|
|
1029
|
+
const missing = ids.filter((id) => !loaded.ids.has(id));
|
|
1030
|
+
if (missing.length > 0) {
|
|
1031
|
+
return fail3(type, `\u5EF6\u671F\u95EE\u9898\u672A\u843D\u6C60\uFF1A${missing.join(", ")}`, "siming issue create \u5C06\u5EF6\u671F\u573A\u666F\u5F55\u5165\u95EE\u9898\u6C60\u540E\uFF0Crecord-set \u4FEE\u6B63 deferredIssues");
|
|
1032
|
+
}
|
|
1033
|
+
return { type, status: "pass", detail: `\u5EF6\u671F\u95EE\u9898 ${ids.length} \u4E2A\u5747\u5DF2\u843D\u6C60` };
|
|
1034
|
+
}
|
|
1035
|
+
async function checkEvidenceFiles(ctx) {
|
|
1036
|
+
const type = "record-evidence-files";
|
|
1037
|
+
const files = ctx.record?.evidenceFiles ?? [];
|
|
1038
|
+
if (files.length === 0) {
|
|
1039
|
+
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
|
+
}
|
|
1041
|
+
if (ctx.repoPath === null) {
|
|
1042
|
+
return { type, status: "not_applicable", detail: `\u4ED3\u5E93\u4E0D\u53EF\u7528\uFF1A${ctx.repoProbeDetail}` };
|
|
1043
|
+
}
|
|
1044
|
+
const missing = [];
|
|
1045
|
+
for (const rel of files) {
|
|
1046
|
+
if (!ctx.fileExists(`${ctx.repoPath}/${rel}`)) missing.push(rel);
|
|
1047
|
+
}
|
|
1048
|
+
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");
|
|
1050
|
+
}
|
|
1051
|
+
return { type, status: "pass", detail: `\u8BC1\u636E\u6587\u4EF6 ${files.length} \u4E2A\u5747\u5B58\u5728` };
|
|
1052
|
+
}
|
|
1053
|
+
async function checkFinalConverged(ctx) {
|
|
1054
|
+
const type = "record-final-converged";
|
|
1055
|
+
const record = ctx.nodeRecords["ARCHIVE"] ?? ctx.record;
|
|
1056
|
+
const confirmations = record?.confirmations ?? [];
|
|
1057
|
+
const summary = record?.summary ?? "";
|
|
1058
|
+
if (confirmations.length === 0 || summary.trim() === "") {
|
|
1059
|
+
const missing = [
|
|
1060
|
+
...confirmations.length === 0 ? ["confirmations\uFF08\u7528\u6237\u786E\u8BA4\u7559\u75D5\uFF09"] : [],
|
|
1061
|
+
...summary.trim() === "" ? ["summary\uFF08\u5F52\u6863\u6458\u8981\uFF09"] : []
|
|
1062
|
+
];
|
|
1063
|
+
return fail3(type, `\u5F52\u6863\u8BB0\u5F55\u4E0D\u5B8C\u6574\uFF1A\u7F3A ${missing.join(" \u4E0E ")}`, "record-summary-set \u5199\u6458\u8981 + record-confirm-add \u5199\u7528\u6237\u786E\u8BA4\u539F\u6587\u540E\u518D\u63A8\u8FDB");
|
|
1064
|
+
}
|
|
1065
|
+
return { type, status: "pass", detail: "\u5F52\u6863\u6458\u8981\u4E0E\u7528\u6237\u786E\u8BA4\u9F50\u5907" };
|
|
1066
|
+
}
|
|
1067
|
+
function fail3(type, rootCause, fix) {
|
|
1068
|
+
return { type, status: "fail", detail: `${rootCause}\u3002\u4FEE\u590D\uFF1A${fix}` };
|
|
1069
|
+
}
|
|
1070
|
+
function firstLine2(text) {
|
|
1071
|
+
return text.trim().split("\n")[0] ?? "";
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/client/checks/registry.ts
|
|
1075
|
+
var CHECK_REGISTRY = {
|
|
1076
|
+
"git-branch-exists": { run: checkBranchExists },
|
|
1077
|
+
"git-base-merged": { run: checkBaseMerged },
|
|
1078
|
+
"git-base-recorded": { run: checkBaseRecorded },
|
|
1079
|
+
"git-worktree-clean": { run: checkWorktreeClean },
|
|
1080
|
+
"git-archive-merged": { run: checkArchiveMerged },
|
|
1081
|
+
"git-stay-on-base": { run: checkStayOnBase },
|
|
1082
|
+
"record-test-report": { run: checkTestReport },
|
|
1083
|
+
"record-no-fake-green": { run: checkNoFakeGreen },
|
|
1084
|
+
"record-base-re-merged": { run: checkBaseReMerged },
|
|
1085
|
+
"record-fix-review-pass": { run: checkFixReviewPass },
|
|
1086
|
+
"record-verified-commit": { run: checkVerifiedCommit },
|
|
1087
|
+
"record-prev-nodes-done": { run: checkPrevNodesDone },
|
|
1088
|
+
"record-version-consistent": { run: checkVersionConsistent },
|
|
1089
|
+
"record-deferred-issues": { run: checkDeferredIssues },
|
|
1090
|
+
"record-evidence-files": { run: checkEvidenceFiles },
|
|
1091
|
+
"record-final-converged": { run: checkFinalConverged }
|
|
1092
|
+
};
|
|
1093
|
+
async function checkBaseRecorded(ctx) {
|
|
1094
|
+
const type = "git-base-recorded";
|
|
1095
|
+
const alignRecord = ctx.nodeRecords["ALIGN"];
|
|
1096
|
+
if (alignRecord?.baseBranch !== void 0 && alignRecord.baseBranch !== "") {
|
|
1097
|
+
return { type, status: "pass", detail: `\u57FA\u7840\u5206\u652F\u5DF2\u843D\u76D8\uFF1A${alignRecord.baseBranch}` };
|
|
1098
|
+
}
|
|
1099
|
+
return {
|
|
1100
|
+
type,
|
|
1101
|
+
status: "fail",
|
|
1102
|
+
detail: "ALIGN \u8BB0\u5F55\u672A\u5199 baseBranch\uFF08\u540E\u7EED\u5408\u5E76/\u5F52\u6863\u7C7B\u68C0\u67E5\u7684\u57FA\u7840\uFF09\u3002\u4FEE\u590D\uFF1Asiming task record set --node ALIGN --base-branch <\u5206\u652F\u540D>"
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
function auditAiChecks(aiChecks, record) {
|
|
1106
|
+
const results = [];
|
|
1107
|
+
for (const check of aiChecks) {
|
|
1108
|
+
const confirmed = (record?.checks ?? []).some((c) => c.refId === check.id && c.passed === true);
|
|
1109
|
+
if (confirmed) {
|
|
1110
|
+
results.push({ id: check.id, item: check.item, status: "pass", detail: "\u5B8C\u6210\u5224\u5B9A\u9879\u5DF2\u81EA\u68C0\u901A\u8FC7\uFF08refId \u5173\u8054\uFF09" });
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
const rejected = (record?.checks ?? []).some((c) => c.refId === check.id && c.passed === false);
|
|
1114
|
+
if (rejected) {
|
|
1115
|
+
results.push({
|
|
1116
|
+
id: check.id,
|
|
1117
|
+
item: check.item,
|
|
1118
|
+
status: "fail",
|
|
1119
|
+
detail: `\u7559\u75D5\u7ED3\u8BBA\u672A\u901A\u8FC7\uFF08check ${check.id} passed=false\uFF09\u3002\u4FEE\u590D\uFF1A\u91CD\u505A\u540E\u7FFB\u7EFF\uFF08record-check-patch --check ${check.id} --passed\uFF09\u6216\u5982\u5B9E\u8865\u5199\u4E0D\u9002\u7528`
|
|
1120
|
+
});
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
const lowered = check.item.toLowerCase();
|
|
1124
|
+
if (NA_PHRASES.some((p) => lowered.includes(p.toLowerCase()))) {
|
|
1125
|
+
results.push({ id: check.id, item: check.item, status: "not_applicable", detail: "item \u58F0\u660E\u4E0D\u9002\u7528\uFF08N/A \u8BCD\u8868\u547D\u4E2D\uFF09" });
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
if (check.item.length < MIN_AI_CHECK_ITEM_LENGTH) {
|
|
1129
|
+
results.push({
|
|
1130
|
+
id: check.id,
|
|
1131
|
+
item: check.item,
|
|
1132
|
+
status: "fail",
|
|
1133
|
+
detail: `item \u8FC7\u77ED\uFF08${check.item.length} < ${MIN_AI_CHECK_ITEM_LENGTH} \u5B57\u7B26\uFF0C\u6700\u5C0F\u5F62\u6001\u8981\u6C42\u53EF\u6838\u5BF9\u7684\u5177\u4F53\u4E8B\u5B9E\uFF09`
|
|
1134
|
+
});
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
if (VAGUE_AI_CHECK_BLACKLIST.some((w) => lowered.includes(w))) {
|
|
1138
|
+
results.push({ id: check.id, item: check.item, status: "fail", detail: `item \u4E3A\u6A21\u7CCA\u5B8C\u6210\u8BED\uFF08\u547D\u4E2D\u9ED1\u540D\u5355\u300C${VAGUE_AI_CHECK_BLACKLIST.join("/")}\u300D\uFF09\uFF0C\u987B\u6539\u5199\u4E3A\u53EF\u6838\u5BF9\u7684\u5177\u4F53\u4E8B\u5B9E` });
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
results.push({
|
|
1142
|
+
id: check.id,
|
|
1143
|
+
item: check.item,
|
|
1144
|
+
status: "fail",
|
|
1145
|
+
// T202609230001:source 枚举仅 'self'|'tool'(本版工具不写 source,D4)——审计只认 refId 关联
|
|
1146
|
+
detail: "\u5B8C\u6210\u5224\u5B9A\u9879\u672A\u81EA\u68C0\uFF08checks \u65E0 refId \u5173\u8054\u4E14\u672A\u58F0\u660E\u4E0D\u9002\u7528\uFF09\u3002\u4FEE\u590D\uFF1Arecord-check-add \u5E26 refId=" + check.id + " passed=true \u9010\u9879\u81EA\u68C0"
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
return results;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// src/client/checks/param-resolver.ts
|
|
1153
|
+
import { DEFAULT_VCS_DOC_PATHS } from "@siming-org/core";
|
|
1154
|
+
import { execFile } from "child_process";
|
|
1155
|
+
import { promisify } from "util";
|
|
1156
|
+
var execFileAsync = promisify(execFile);
|
|
1157
|
+
var defaultGitRunner = async (args, cwd) => {
|
|
1158
|
+
try {
|
|
1159
|
+
const { stdout, stderr } = await execFileAsync("git", args, { cwd, timeout: 1e4 });
|
|
1160
|
+
return { ok: true, stdout, stderr };
|
|
1161
|
+
} catch (err2) {
|
|
1162
|
+
const e = err2;
|
|
1163
|
+
return { ok: false, stdout: e.stdout ?? "", stderr: e.stderr ?? e.message ?? String(err2) };
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
1166
|
+
async function resolveCheckContextWithRunner(base, vcs, cwd, runGit) {
|
|
1167
|
+
const probe = vcs.repoPath !== void 0 ? await probeRepo(vcs.repoPath, runGit) : await probeRepo(cwd, runGit);
|
|
1168
|
+
const alignRecord = base.nodeRecords["ALIGN"];
|
|
1169
|
+
const baseBranch = (alignRecord?.baseBranch !== void 0 && alignRecord.baseBranch !== "" ? alignRecord.baseBranch : void 0) ?? vcs.baseBranch ?? null;
|
|
1170
|
+
return {
|
|
1171
|
+
...base,
|
|
1172
|
+
repoPath: probe.repoPath,
|
|
1173
|
+
repoProbeDetail: probe.detail,
|
|
1174
|
+
baseBranch,
|
|
1175
|
+
branchPattern: vcs.branchPattern ?? "feature/",
|
|
1176
|
+
docPaths: vcs.docPaths ?? [...DEFAULT_VCS_DOC_PATHS]
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
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
|
+
|
|
1193
|
+
// src/client/checks/runner.ts
|
|
1194
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1195
|
+
function gateHasFailure(report) {
|
|
1196
|
+
return report.toolOutcomes.some((o) => o.status === "fail") || report.aiOutcomes.some((o) => o.status === "fail");
|
|
1197
|
+
}
|
|
1198
|
+
async function runNodeGate(client, taskId) {
|
|
1199
|
+
const ctxRes = await client.getJson(`/api/tasks/${encodeURIComponent(taskId)}/context?view=basic`);
|
|
1200
|
+
if (!ctxRes.success) return ctxRes;
|
|
1201
|
+
const context = ctxRes.data;
|
|
1202
|
+
const node = context.currentNode;
|
|
1203
|
+
if (node === null) {
|
|
1204
|
+
return ok2({ empty: true, nodeId: context.task.currentNode, toolOutcomes: [], aiOutcomes: [] });
|
|
1205
|
+
}
|
|
1206
|
+
const toolChecks = node.toolChecks ?? [];
|
|
1207
|
+
const aiChecks = node.aiChecks ?? [];
|
|
1208
|
+
if (toolChecks.length === 0 && aiChecks.length === 0) {
|
|
1209
|
+
return ok2({ empty: true, nodeId: node.nodeId, toolOutcomes: [], aiOutcomes: [] });
|
|
1210
|
+
}
|
|
1211
|
+
let vcs = {};
|
|
1212
|
+
let vcsLoadError;
|
|
1213
|
+
const projectRes = await client.getJson(
|
|
1214
|
+
`/api/projects/${encodeURIComponent(context.task.projectId)}`
|
|
1215
|
+
);
|
|
1216
|
+
if (projectRes.success && projectRes.data.vcs !== void 0) {
|
|
1217
|
+
vcs = projectRes.data.vcs;
|
|
1218
|
+
} else if (!projectRes.success) {
|
|
1219
|
+
vcsLoadError = projectRes.error.message;
|
|
1220
|
+
}
|
|
1221
|
+
const nodes = context.nodes.map((n) => ({
|
|
1222
|
+
nodeId: n.nodeId,
|
|
1223
|
+
label: n.label,
|
|
1224
|
+
status: n.status
|
|
1225
|
+
}));
|
|
1226
|
+
const checkCtx = await resolveCheckContextWithRunner(
|
|
1227
|
+
{
|
|
1228
|
+
taskId,
|
|
1229
|
+
nodeId: node.nodeId,
|
|
1230
|
+
record: context.nodeRecords[node.nodeId],
|
|
1231
|
+
nodeRecords: context.nodeRecords,
|
|
1232
|
+
nodes,
|
|
1233
|
+
edges: context.edges ?? [],
|
|
1234
|
+
vcs,
|
|
1235
|
+
...vcsLoadError !== void 0 ? { vcsLoadError } : {},
|
|
1236
|
+
runGit: defaultGitRunner,
|
|
1237
|
+
loadIssueIds: async () => {
|
|
1238
|
+
const r = await client.getJson(
|
|
1239
|
+
`/api/tasks/${encodeURIComponent(taskId)}/issues`
|
|
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)
|
|
1247
|
+
},
|
|
1248
|
+
vcs,
|
|
1249
|
+
process.cwd(),
|
|
1250
|
+
defaultGitRunner
|
|
1251
|
+
);
|
|
1252
|
+
const toolOutcomes = [];
|
|
1253
|
+
for (const toolCheck of toolChecks) {
|
|
1254
|
+
const def = CHECK_REGISTRY[toolCheck.type];
|
|
1255
|
+
if (def === void 0) {
|
|
1256
|
+
toolOutcomes.push({
|
|
1257
|
+
type: toolCheck.type,
|
|
1258
|
+
status: "fail",
|
|
1259
|
+
detail: "\u672A\u77E5\u68C0\u67E5\u7C7B\u578B\uFF08\u5F53\u524D CLI \u7248\u672C\u4E0D\u542B\u8BE5\u68C0\u67E5\u5B9E\u73B0\uFF09\u3002\u4FEE\u590D\uFF1A\u5347\u7EA7 siming CLI \u540E\u91CD\u8BD5"
|
|
1260
|
+
});
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
toolOutcomes.push(await def.run(checkCtx));
|
|
1264
|
+
}
|
|
1265
|
+
const aiOutcomes = auditAiChecks(aiChecks, context.nodeRecords[node.nodeId]);
|
|
1266
|
+
return ok2({ empty: false, nodeId: node.nodeId, toolOutcomes, aiOutcomes });
|
|
1267
|
+
}
|
|
1268
|
+
function buildCheckReportPayload(report, mode, note) {
|
|
1269
|
+
const results = [
|
|
1270
|
+
...report.toolOutcomes.map((o) => ({ type: o.type, status: o.status, detail: o.detail })),
|
|
1271
|
+
...report.aiOutcomes.map((o) => ({ type: `ai:${o.id}`, status: o.status, detail: o.detail }))
|
|
1272
|
+
];
|
|
1273
|
+
return { nodeId: report.nodeId, mode, results, ...note !== void 0 ? { note } : {} };
|
|
1274
|
+
}
|
|
1275
|
+
function buildGateFailMessage(report, reportWritten = true) {
|
|
1276
|
+
const fails = [
|
|
1277
|
+
...report.toolOutcomes.filter((o) => o.status === "fail").map((o) => `[${o.type}] ${o.detail}`),
|
|
1278
|
+
...report.aiOutcomes.filter((o) => o.status === "fail").map((o) => `[ai:${o.id}] ${o.detail}`)
|
|
1279
|
+
];
|
|
1280
|
+
const passed = report.toolOutcomes.filter((o) => o.status !== "fail").length + report.aiOutcomes.filter((o) => o.status !== "fail").length;
|
|
1281
|
+
return {
|
|
1282
|
+
message: `\u8282\u70B9\u95E8\u7981\u672A\u901A\u8FC7\uFF08${fails.length} \u9879 fail\uFF0C${passed} \u9879\u901A\u8FC7/\u4E0D\u9002\u7528\uFF09\uFF0Cadvance \u5DF2\u62E6\u622A\uFF1A
|
|
1283
|
+
${fails.map((f, i) => `${i + 1}. ${f}`).join("\n")}`,
|
|
1284
|
+
hint: reportWritten ? "\u9010\u9879\u5B8C\u6210\u4FEE\u590D\u6216\u7528\u6237\u660E\u793A\u8C41\u514D\uFF08\u7ECF record-decision-add \u7559\u75D5\uFF09\u540E\u91CD\u65B0\u63A8\u8FDB\uFF1B\u95E8\u7981\u7ED3\u679C\u5DF2\u5199\u5165\u4EFB\u52A1 history\uFF08checks_rejected\uFF09" : "\u9010\u9879\u5B8C\u6210\u4FEE\u590D\u6216\u7528\u6237\u660E\u793A\u8C41\u514D\uFF08\u7ECF record-decision-add \u7559\u75D5\uFF09\u540E\u91CD\u65B0\u63A8\u8FDB\u3002\u6CE8\u610F\uFF1A\u672C\u6B21\u95E8\u7981\u7ED3\u679C\u7559\u75D5\u5931\u8D25\uFF0Chistory \u7F3A\u5931\u8BE5\u6B21\u8BB0\u5F55\uFF08\u4FEE\u590D\u540E\u91CD\u63A8\u65F6\u4F1A\u91CD\u65B0\u7559\u75D5\uFF09"
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function naCategory(detail) {
|
|
1288
|
+
return detail.split(/[((::,,;;]/)[0]?.trim().slice(0, 16) ?? "";
|
|
1289
|
+
}
|
|
1290
|
+
function buildPassNote(report) {
|
|
1291
|
+
const mark = (status, key, detail) => {
|
|
1292
|
+
if (status === "pass") return `${key}\u2713`;
|
|
1293
|
+
if (status === "not_applicable") return `${key}=N(${naCategory(detail)})`;
|
|
1294
|
+
return `${key}\u2717`;
|
|
1295
|
+
};
|
|
1296
|
+
const marks = [
|
|
1297
|
+
...report.toolOutcomes.map((o) => mark(o.status, o.type, o.detail)),
|
|
1298
|
+
...report.aiOutcomes.map((o) => mark(o.status, o.id, o.detail))
|
|
1299
|
+
].join(" ");
|
|
1300
|
+
const line = `[checks] ${marks}`;
|
|
1301
|
+
if (line.length <= 600) return line;
|
|
1302
|
+
const toolPass = report.toolOutcomes.filter((o) => o.status === "pass").length;
|
|
1303
|
+
const toolNa = report.toolOutcomes.filter((o) => o.status === "not_applicable").length;
|
|
1304
|
+
const parts = [`\u5DE5\u5177\u68C0\u67E5 ${toolPass} \u9879\u901A\u8FC7${toolNa > 0 ? `\uFF0C${toolNa} \u9879 N/A` : ""}`];
|
|
1305
|
+
if (report.aiOutcomes.length > 0) {
|
|
1306
|
+
const aiPass = report.aiOutcomes.filter((o) => o.status === "pass").length;
|
|
1307
|
+
const aiNa = report.aiOutcomes.filter((o) => o.status === "not_applicable").length;
|
|
1308
|
+
parts.push(`\u5B8C\u6210\u5224\u5B9A\u5BA1\u8BA1 ${aiPass} \u9879\u901A\u8FC7${aiNa > 0 ? `\uFF0C${aiNa} \u9879 N/A` : ""}`);
|
|
1309
|
+
}
|
|
1310
|
+
return `[checks] ${parts.join("\uFF1B")}\uFF08\u9010\u9879\u660E\u7EC6\u89C1 history check-report\uFF09`;
|
|
1311
|
+
}
|
|
1312
|
+
function buildForceComment(forceReason, report, maxLen) {
|
|
1313
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
1314
|
+
const skipped = [
|
|
1315
|
+
...report.toolOutcomes.filter((o) => o.status === "fail").map((o) => `${o.type}:${o.status}:${o.detail}`),
|
|
1316
|
+
...report.aiOutcomes.filter((o) => o.status === "fail").map((o) => `ai:${o.id}:${o.status}:${o.detail}`)
|
|
1317
|
+
];
|
|
1318
|
+
const full = [
|
|
1319
|
+
"[FORCE advance] \u7528\u6237\u6307\u4EE4\u539F\u6587\uFF1A" + forceReason,
|
|
1320
|
+
`\u88AB\u8DF3\u8FC7\u9879\u53CA\u5F53\u65F6\u72B6\u6001\uFF1A${skipped.length > 0 ? skipped.join("\uFF1B") : "\u65E0 fail \u9879"}`,
|
|
1321
|
+
`\u6267\u884C\u65F6\u70B9\uFF1A${ts} \u8282\u70B9 ${report.nodeId}`
|
|
1322
|
+
].join("\n");
|
|
1323
|
+
if (maxLen === void 0 || full.length <= maxLen) return full;
|
|
1324
|
+
const types = [
|
|
1325
|
+
...report.toolOutcomes.filter((o) => o.status === "fail").map((o) => o.type),
|
|
1326
|
+
...report.aiOutcomes.filter((o) => o.status === "fail").map((o) => `ai:${o.id}`)
|
|
1327
|
+
];
|
|
1328
|
+
const head = "[FORCE advance] \u7528\u6237\u6307\u4EE4\u539F\u6587\uFF1A" + forceReason;
|
|
1329
|
+
const time = `\u6267\u884C\u65F6\u70B9\uFF1A${ts} \u8282\u70B9 ${report.nodeId}`;
|
|
1330
|
+
const tail = "\uFF08\u660E\u7EC6\u8D85\u957F\u622A\u65AD\uFF0C\u9010\u9879\u89C1 history check-report\uFF09";
|
|
1331
|
+
let list = types.join(",");
|
|
1332
|
+
let compact = [head, `\u88AB\u8DF3\u8FC7 ${types.length} \u9879\uFF1A${list}${tail}`, time].join("\n");
|
|
1333
|
+
while (compact.length > maxLen && list.length > 0) {
|
|
1334
|
+
list = list.slice(0, Math.max(0, list.length - 40));
|
|
1335
|
+
compact = [head, `\u88AB\u8DF3\u8FC7 ${types.length} \u9879\uFF1A${list}\u2026${tail}`, time].join("\n");
|
|
1336
|
+
}
|
|
1337
|
+
if (compact.length > maxLen) {
|
|
1338
|
+
compact = [head, `\u88AB\u8DF3\u8FC7 ${types.length} \u9879${tail}`, time].join("\n");
|
|
1339
|
+
}
|
|
1340
|
+
if (compact.length > maxLen) {
|
|
1341
|
+
const headCut = Math.max(1, maxLen - time.length - 2);
|
|
1342
|
+
compact = `${head.slice(0, headCut)}\u2026
|
|
1343
|
+
${time}`;
|
|
1344
|
+
}
|
|
1345
|
+
return compact.length <= maxLen ? compact : compact.slice(0, maxLen);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// src/client/handlers/task.ts
|
|
678
1349
|
async function handleCreateTask(client, input) {
|
|
679
1350
|
return client.postJson("/api/tasks", input);
|
|
680
1351
|
}
|
|
681
1352
|
async function handleAdvance(client, taskId, note, summary, finalize) {
|
|
682
|
-
return client
|
|
683
|
-
|
|
1353
|
+
return advanceInner(client, taskId, note, summary, finalize, {});
|
|
1354
|
+
}
|
|
1355
|
+
async function handleAdvanceWithGate(client, taskId, gate, note, summary, finalize) {
|
|
1356
|
+
return advanceInner(client, taskId, note, summary, finalize, gate);
|
|
1357
|
+
}
|
|
1358
|
+
async function advanceInner(client, taskId, note, summary, finalize, gate) {
|
|
1359
|
+
const gateReport = await runNodeGate(client, taskId);
|
|
1360
|
+
if (!gateReport.success) return gateReport;
|
|
1361
|
+
const report = gateReport.data;
|
|
1362
|
+
const useForce = gate.force === true;
|
|
1363
|
+
let effectiveNote = note;
|
|
1364
|
+
if (!report.empty) {
|
|
1365
|
+
if (gateHasFailure(report)) {
|
|
1366
|
+
const rejectPayload = buildCheckReportPayload(
|
|
1367
|
+
report,
|
|
1368
|
+
useForce ? "force" : "reject",
|
|
1369
|
+
// force 留痕必须携带用户指令原文(验收 12)——note 走 force comment 模板(≤500,H3 截断保护)
|
|
1370
|
+
useForce && gate.forceReason !== void 0 ? buildForceComment(gate.forceReason, report, 500) : void 0
|
|
1371
|
+
);
|
|
1372
|
+
const reportRes = await postCheckReport(client, taskId, rejectPayload);
|
|
1373
|
+
const auditNote = reportRes.success ? "" : `
|
|
1374
|
+
\uFF08\u8B66\u793A\uFF1Acheck-report \u7559\u75D5\u5931\u8D25\uFF1A${reportRes.error.message}\uFF09`;
|
|
1375
|
+
if (!useForce) {
|
|
1376
|
+
const { message, hint } = buildGateFailMessage(report, reportRes.success);
|
|
1377
|
+
return fail({ message: `${message}${auditNote}`, hint, httpStatus: 0 });
|
|
1378
|
+
}
|
|
1379
|
+
if (!reportRes.success) {
|
|
1380
|
+
const w = `\uFF08\u8B66\u793A\uFF1Acheck-report \u7559\u75D5\u5931\u8D25\uFF1A${reportRes.error.message}\u2014\u2014\u672C\u6B21\u5F3A\u5236\u63A8\u8FDB\u5728 history \u65E0 checks_rejected \u8BB0\u5F55\uFF09`;
|
|
1381
|
+
effectiveNote = effectiveNote !== void 0 ? `${effectiveNote}
|
|
1382
|
+
${w}` : w;
|
|
1383
|
+
}
|
|
1384
|
+
} else if (useForce) {
|
|
1385
|
+
const reportRes = await postCheckReport(
|
|
1386
|
+
client,
|
|
1387
|
+
taskId,
|
|
1388
|
+
buildCheckReportPayload(report, "force", buildForceComment(gate.forceReason ?? "", report, 500))
|
|
1389
|
+
);
|
|
1390
|
+
if (!reportRes.success) {
|
|
1391
|
+
const w = `\uFF08\u8B66\u793A\uFF1Acheck-report \u7559\u75D5\u5931\u8D25\uFF1A${reportRes.error.message}\u2014\u2014\u672C\u6B21\u5F3A\u5236\u63A8\u8FDB\u5728 history \u65E0 checks_rejected \u8BB0\u5F55\uFF09`;
|
|
1392
|
+
effectiveNote = effectiveNote !== void 0 ? `${effectiveNote}
|
|
1393
|
+
${w}` : w;
|
|
1394
|
+
}
|
|
1395
|
+
} else {
|
|
1396
|
+
const passNote = buildPassNote(report);
|
|
1397
|
+
effectiveNote = note !== void 0 ? `${note}
|
|
1398
|
+
${passNote}` : passNote;
|
|
1399
|
+
}
|
|
1400
|
+
} else if (useForce && gate.forceReason !== void 0) {
|
|
1401
|
+
effectiveNote = note !== void 0 ? `${note}
|
|
1402
|
+
[FORCE advance] ${gate.forceReason}` : `[FORCE advance] ${gate.forceReason}`;
|
|
1403
|
+
}
|
|
1404
|
+
const response = await client.postJson(`/api/tasks/${taskId}/advance`, {
|
|
1405
|
+
...effectiveNote !== void 0 ? { note: effectiveNote } : {},
|
|
684
1406
|
...summary !== void 0 ? { summary } : {},
|
|
685
1407
|
...finalize ? { finalize: true } : {}
|
|
686
1408
|
});
|
|
1409
|
+
if (!response.success) return response;
|
|
1410
|
+
if (useForce && response.data.status === "paused") {
|
|
1411
|
+
const comment = buildForceComment(gate.forceReason ?? "", report, 2e3);
|
|
1412
|
+
const approved = await handleApprove(client, taskId, "approved", comment);
|
|
1413
|
+
if (!approved.success) {
|
|
1414
|
+
return fail({
|
|
1415
|
+
message: `force advance \u5DF2\u5230\u6682\u505C\u70B9\uFF0C\u81EA\u52A8\u5BA1\u6279\u5931\u8D25\uFF1A${approved.error.message}`,
|
|
1416
|
+
hint: "\u4EFB\u52A1\u505C\u7559\u5728\u6682\u505C\u70B9\uFF1B\u8BF7\u4EBA\u5DE5\u5BA1\u6279\uFF08siming task approve\uFF09",
|
|
1417
|
+
httpStatus: 0
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
if (approved.data.status === "rejected") {
|
|
1421
|
+
return fail({
|
|
1422
|
+
message: `force advance \u5BA1\u6279\u88AB\u9A73\u56DE\uFF1A${approved.data.guidance}`,
|
|
1423
|
+
hint: "\u4EFB\u52A1\u4FDD\u6301\u6682\u505C\uFF1B\u68C0\u67E5\u6682\u505C\u70B9\u5BA1\u6279\u7EA6\u675F",
|
|
1424
|
+
httpStatus: 0
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
return ok2(approved.data);
|
|
1428
|
+
}
|
|
1429
|
+
return response;
|
|
1430
|
+
}
|
|
1431
|
+
async function postCheckReport(client, taskId, payload) {
|
|
1432
|
+
return client.postJson(`/api/tasks/${taskId}/check-report`, payload);
|
|
1433
|
+
}
|
|
1434
|
+
async function handleTaskCheck(client, taskId) {
|
|
1435
|
+
return runNodeGate(client, taskId);
|
|
687
1436
|
}
|
|
688
1437
|
async function handleApprove(client, taskId, decision, comment) {
|
|
689
1438
|
return client.postJson(`/api/tasks/${taskId}/approve`, {
|
|
@@ -735,14 +1484,35 @@ function handleDocAcceptanceAdd(client, taskId, text) {
|
|
|
735
1484
|
function handleDocNonGoalAdd(client, taskId, text) {
|
|
736
1485
|
return client.postJson(`/api/tasks/${taskId}/doc/non-goal`, { text });
|
|
737
1486
|
}
|
|
738
|
-
function handleRecordSummarySet(client, taskId, nodeId,
|
|
739
|
-
return client.patchJson(
|
|
1487
|
+
function handleRecordSummarySet(client, taskId, nodeId, fields) {
|
|
1488
|
+
return client.patchJson(
|
|
1489
|
+
`/api/tasks/${taskId}/records/${nodeId}/summary`,
|
|
1490
|
+
{
|
|
1491
|
+
...fields.summary !== void 0 ? { summary: fields.summary } : {},
|
|
1492
|
+
...fields.baseBranch !== void 0 ? { baseBranch: fields.baseBranch } : {},
|
|
1493
|
+
...fields.verifiedCommit !== void 0 ? { verifiedCommit: fields.verifiedCommit } : {},
|
|
1494
|
+
...fields.fixesApplied !== void 0 ? { fixesApplied: fields.fixesApplied } : {},
|
|
1495
|
+
...fields.testReport !== void 0 ? { testReport: fields.testReport } : {},
|
|
1496
|
+
...fields.deferredIssues !== void 0 ? { deferredIssues: fields.deferredIssues } : {},
|
|
1497
|
+
...fields.evidenceFiles !== void 0 ? { evidenceFiles: fields.evidenceFiles } : {}
|
|
1498
|
+
}
|
|
1499
|
+
);
|
|
740
1500
|
}
|
|
741
|
-
function handleRecordCheckAdd(client, taskId, nodeId, item, passed) {
|
|
742
|
-
return client.postJson(`/api/tasks/${taskId}/records/${nodeId}/checks`, {
|
|
1501
|
+
function handleRecordCheckAdd(client, taskId, nodeId, item, passed, options) {
|
|
1502
|
+
return client.postJson(`/api/tasks/${taskId}/records/${nodeId}/checks`, {
|
|
1503
|
+
item,
|
|
1504
|
+
passed,
|
|
1505
|
+
...options?.refId !== void 0 ? { refId: options.refId } : {},
|
|
1506
|
+
...options?.source !== void 0 ? { source: options.source } : {}
|
|
1507
|
+
});
|
|
743
1508
|
}
|
|
744
|
-
function handleRecordCheckPatch(client, taskId, nodeId, checkId,
|
|
745
|
-
return client.patchJson(`/api/tasks/${taskId}/records/${nodeId}/checks/${checkId}`, {
|
|
1509
|
+
function handleRecordCheckPatch(client, taskId, nodeId, checkId, patch) {
|
|
1510
|
+
return client.patchJson(`/api/tasks/${taskId}/records/${nodeId}/checks/${checkId}`, {
|
|
1511
|
+
...patch.passed !== void 0 ? { passed: patch.passed } : {},
|
|
1512
|
+
...patch.item !== void 0 ? { item: patch.item } : {},
|
|
1513
|
+
...patch.refId !== void 0 ? { refId: patch.refId } : {},
|
|
1514
|
+
...patch.source !== void 0 ? { source: patch.source } : {}
|
|
1515
|
+
});
|
|
746
1516
|
}
|
|
747
1517
|
var ARTIFACT_CONTENT_LIMIT = 2e5;
|
|
748
1518
|
function handleRecordArtifactAdd(client, taskId, nodeId, type, path, note, content) {
|
|
@@ -1114,7 +1884,7 @@ async function handleUpgradeApply(client, templateId, decisions) {
|
|
|
1114
1884
|
|
|
1115
1885
|
// src/client/handlers/auth.ts
|
|
1116
1886
|
import {
|
|
1117
|
-
existsSync as
|
|
1887
|
+
existsSync as existsSync6,
|
|
1118
1888
|
readFileSync as readFileSync7,
|
|
1119
1889
|
writeFileSync as writeFileSync4,
|
|
1120
1890
|
renameSync as renameSync3,
|
|
@@ -1206,7 +1976,7 @@ function writeWorkspaceLayer(token, kind, opts) {
|
|
|
1206
1976
|
const devJsonPath = join5(dir, ".siming", "dev.json");
|
|
1207
1977
|
let config = {};
|
|
1208
1978
|
let hadIsolationKeys = false;
|
|
1209
|
-
if (
|
|
1979
|
+
if (existsSync6(devJsonPath)) {
|
|
1210
1980
|
try {
|
|
1211
1981
|
config = JSON.parse(readFileSync7(devJsonPath, "utf-8"));
|
|
1212
1982
|
hadIsolationKeys = "serverPort" in config || "dbPrefix" in config;
|
|
@@ -1439,6 +2209,8 @@ var taskArgs = {
|
|
|
1439
2209
|
"context",
|
|
1440
2210
|
"node",
|
|
1441
2211
|
"advance",
|
|
2212
|
+
// 门禁只读预检(T202609230001:advance 前自查节点门禁,零写入)
|
|
2213
|
+
"check",
|
|
1442
2214
|
"approve",
|
|
1443
2215
|
"pause",
|
|
1444
2216
|
"resume",
|
|
@@ -1481,10 +2253,13 @@ var taskArgs = {
|
|
|
1481
2253
|
view: hostNeutralEnum(z3.enum(["prompt", "skills", "basic", "full"])).optional(),
|
|
1482
2254
|
nodeId: z3.string().optional(),
|
|
1483
2255
|
// advance / record-set(note 写 history;summary 写 nodeRecords,两位独立);
|
|
1484
|
-
// finalize 为 advance 收尾确认(终节点终态化必需,中间节点无副作用——T202609130009
|
|
2256
|
+
// finalize 为 advance 收尾确认(终节点终态化必需,中间节点无副作用——T202609130009);
|
|
2257
|
+
// force/forceReason 成对(T202609230001:门禁强制推进,仅凭用户明示指令,AI 禁自决)
|
|
1485
2258
|
note: z3.string().optional(),
|
|
1486
2259
|
summary: z3.string().optional(),
|
|
1487
2260
|
finalize: z3.boolean().optional(),
|
|
2261
|
+
force: z3.boolean().optional(),
|
|
2262
|
+
forceReason: z3.string().optional(),
|
|
1488
2263
|
// approve
|
|
1489
2264
|
decision: hostNeutralEnum(z3.enum(["approved", "rejected"])).optional(),
|
|
1490
2265
|
comment: z3.string().optional(),
|
|
@@ -1496,6 +2271,21 @@ var taskArgs = {
|
|
|
1496
2271
|
item: z3.string().optional(),
|
|
1497
2272
|
checkId: z3.string().optional(),
|
|
1498
2273
|
passed: z3.boolean().optional(),
|
|
2274
|
+
// T202609230001 F1:结构化记录字段(record-set)+ 检查条目留痕锚(check-add/patch)
|
|
2275
|
+
baseBranch: z3.string().optional(),
|
|
2276
|
+
verifiedCommit: z3.string().optional(),
|
|
2277
|
+
fixesApplied: z3.number().int().nonnegative().optional(),
|
|
2278
|
+
testReport: z3.object({
|
|
2279
|
+
command: z3.string(),
|
|
2280
|
+
passed: z3.number().int().nonnegative(),
|
|
2281
|
+
failed: z3.number().int().nonnegative(),
|
|
2282
|
+
// 与 core TestReportSchema 对齐:skipped 必填(缺省语义由调用方显式补 0,禁 optional 漂移)
|
|
2283
|
+
skipped: z3.number().int().nonnegative()
|
|
2284
|
+
}).optional(),
|
|
2285
|
+
deferredIssues: z3.array(z3.string()).optional(),
|
|
2286
|
+
evidenceFiles: z3.array(z3.string()).optional(),
|
|
2287
|
+
refId: z3.string().optional(),
|
|
2288
|
+
source: hostNeutralEnum(z3.enum(["self", "tool"])).optional(),
|
|
1499
2289
|
artifactType: hostNeutralEnum(z3.enum(ARTIFACT_TYPES2)).optional(),
|
|
1500
2290
|
path: z3.string().optional(),
|
|
1501
2291
|
file: z3.string().optional(),
|
|
@@ -1512,7 +2302,14 @@ var taskArgs = {
|
|
|
1512
2302
|
trackNote: z3.string().optional(),
|
|
1513
2303
|
text: z3.string().optional(),
|
|
1514
2304
|
// 批量形态(T202609010002):与对应单条参数二选一;元素字段级校验归 server 分区步(部分失败保留成功条目)
|
|
1515
|
-
checkItems: z3.array(
|
|
2305
|
+
checkItems: z3.array(
|
|
2306
|
+
z3.object({
|
|
2307
|
+
item: z3.string(),
|
|
2308
|
+
passed: z3.boolean().optional(),
|
|
2309
|
+
refId: z3.string().optional(),
|
|
2310
|
+
source: hostNeutralEnum(z3.enum(["self", "tool"])).optional()
|
|
2311
|
+
})
|
|
2312
|
+
).optional(),
|
|
1516
2313
|
artifactEntries: z3.array(
|
|
1517
2314
|
z3.object({
|
|
1518
2315
|
artifactType: z3.enum(ARTIFACT_TYPES2),
|
|
@@ -1650,7 +2447,8 @@ var TASK_FIELD_SPECS = {
|
|
|
1650
2447
|
query: { required: [], optional: ["taskId", "status", "track", "projectId", "page", "limit"] },
|
|
1651
2448
|
context: { required: ["taskId"], optional: ["view"] },
|
|
1652
2449
|
node: { required: ["taskId", "view"], optional: ["nodeId"] },
|
|
1653
|
-
advance: { required: ["taskId"], optional: ["note", "summary", "finalize"] },
|
|
2450
|
+
advance: { required: ["taskId"], optional: ["note", "summary", "finalize", "force", "forceReason"] },
|
|
2451
|
+
check: { required: ["taskId"], optional: [] },
|
|
1654
2452
|
approve: { required: ["taskId", "decision"], optional: ["comment"] },
|
|
1655
2453
|
pause: { required: ["taskId"], optional: ["reason"] },
|
|
1656
2454
|
resume: { required: ["taskId"], optional: ["resumeNote"] },
|
|
@@ -1658,9 +2456,12 @@ var TASK_FIELD_SPECS = {
|
|
|
1658
2456
|
reopen: { required: ["taskId", "reason"], optional: [] },
|
|
1659
2457
|
history: { required: ["taskId"], optional: [] },
|
|
1660
2458
|
instance: { required: ["taskId"], optional: [] },
|
|
1661
|
-
"record-set": {
|
|
1662
|
-
|
|
1663
|
-
|
|
2459
|
+
"record-set": {
|
|
2460
|
+
required: ["taskId", "node"],
|
|
2461
|
+
optional: ["summary", "baseBranch", "verifiedCommit", "fixesApplied", "testReport", "deferredIssues", "evidenceFiles"]
|
|
2462
|
+
},
|
|
2463
|
+
"record-check-add": { required: ["taskId", "node"], optional: ["item", "passed", "checkItems", "refId", "source"] },
|
|
2464
|
+
"record-check-patch": { required: ["taskId", "node", "checkId"], optional: ["passed", "item", "refId", "source"] },
|
|
1664
2465
|
"record-artifact-add": {
|
|
1665
2466
|
required: ["taskId", "node"],
|
|
1666
2467
|
optional: ["artifactType", "path", "file", "content", "note", "artifactEntries"]
|
|
@@ -1688,7 +2489,7 @@ function createMcpServer() {
|
|
|
1688
2489
|
const client = createClient(void 0, "mcp");
|
|
1689
2490
|
server.tool(
|
|
1690
2491
|
"siming_task",
|
|
1691
|
-
"task \u8D44\u6E90\u57DF\u805A\u5408\u5DE5\u5177\uFF08\u4EFB\u52A1\u5168\u64CD\u4F5C\u7ECF\u672C\u5DE5\u5177 + action \u53C2\u6570\uFF09\u3002action \u5FC5\u586B\uFF1B\u8C03\u7528\u65B9\u5E94\u5728 fieldMask \u4E2D\u5217\u51FA\u672C\u6B21\u771F\u5B9E\u63D0\u4F9B\u7684\u53EF\u9009\u5B57\u6BB5\uFF0Cmask \u5916\u7531\u5BBF\u4E3B\u81EA\u52A8\u586B\u5145\u7684\u7A7A\u4E32\u3001\u7A7A\u6570\u7EC4\u3001false\u30010 \u5747\u5FFD\u7565\uFF0Cmask \u5185\u7A7A\u6570\u7EC4/false \u8868\u793A\u663E\u5F0F\u6E05\u7A7A\u6216\u5173\u95ED\uFF1B\u5404 action \u56FA\u5B9A\u5FC5\u586B\u5B57\u6BB5\u65E0\u9700\u5199\u5165 mask\u3002\u5176\u4F59\u53C2\u6570\u5168\u53EF\u9009\u3001\u6309 action \u8FD0\u884C\u65F6\u6821\u9A8C\u5FC5\u586B\uFF08\u7F3A\u5931\u8FD4\u56DE\u5931\u8D25\u4E09\u8981\u7D20\uFF1A\u5BF9\u8C61+\u6839\u56E0+\u4FEE\u590D\u6307\u5F15\uFF09\u3002projectId \u4E3A key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\u4E3A 24-hex id\uFF1B\u663E\u5F0F\u6307\u5B9A\u89E3\u6790\u5931\u8D25\u62A5\u9519\u4E0D\u515C\u5E95\uFF1B\u7F3A\u7701\u8BED\u4E49\u6309 action\uFF1Acreate \u843D\u9ED8\u8BA4\u9879\u76EE\u94FE\uFF08\u5DE5\u4F5C\u533A defaultProject \u5931\u6548\u62A5\u9519\uFF0C\u65E0\u914D\u7F6E\u4E0D\u4F20\u7531 server \u6309\u6A21\u677F\u5F52\u5C5E\u515C\u5E95\uFF09\uFF0Cquery \u4E0D\u8FC7\u6EE4\uFF08\u5168\u5C40\u67E5\u8BE2\uFF09\u3002\u5199\u64CD\u4F5C\uFF08create/doc-*/record-*/archnote-add/pause/resume/cancel\uFF09\u54CD\u5E94\u4E3A\u8F7B\u91CF\u56DE\u6267\uFF1A{taskId, updated(\u89E6\u53CA\u5B57\u6BB5\u8DEF\u5F84), entry(\u65B0\u6761\u76EE id+kind\uFF0C\u4F9B\u540E\u7EED patch \u5B9A\u4F4D), echo(\u5199\u5165\u5185\u5BB9\u56DE\u663E), updatedAt, task(\u4EFB\u52A1\u8FDB\u5EA6\u6458\u8981 progress: {completed,total})}\u2014\u2014\u4E0D\u8FD4\u56DE\u5B8C\u6574\u4EFB\u52A1\u5B9E\u4F53\u3002action \u6E05\u5355\uFF08\u62EC\u53F7\u5185\u4E3A\u8BE5 action \u5FC5\u586B\u53C2\u6570\uFF09\uFF1Acreate(templateId,taskTitle,track\uFF1B\u53EF\u9009 type=feature|bugfix|ui-tweak|research\u3001skipNodes\u3001projectId\uFF1BtemplateId \u652F\u6301 code|id \u53CC\u5F62\u6001\u2014\u2014code \u4E3A\u6A21\u677F\u4E1A\u52A1\u6807\u8BC6\uFF08\u9879\u76EE\u5185\u552F\u4E00\u3001kebab-case \u5FC5\u542B\u8FDE\u5B57\u7B26\uFF0C\u5982 full-workflow\uFF09\uFF0C\u63A8\u8350\u5F15\u7528\u5F62\u6001\uFF0C\u5BFB\u5740\u987B\u914D projectId \u6307\u5B9A\u9879\u76EE\u57DF\uFF1B24-hex \u6570\u636E\u5E93 id \u76F4\u67E5\u517C\u5BB9\uFF1Bcode \u5F62\u6001\u4E0D\u5B58\u5728\u65F6\u9519\u8BEF\u542B candidates \u76F8\u8FD1\u5019\u9009\u6E05\u5355\uFF08id/code/name \u22645 \u6761\uFF09\uFF1B\u54CD\u5E94 {task, firstNode}\u2014\u2014firstNode \u5373\u9996\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u514D\u8C03 context)\uFF1Bquery(taskId \u8FD4\u56DE\u5355\u4EFB\u52A1\u8BE6\u60C5\uFF0C\u7F3A\u7701\u8FD4\u56DE\u5217\u8868\uFF0C\u53EF\u9009 status/track/projectId/page/limit)\uFF1Bcontext(taskId\uFF1B\u53EF\u9009 view=basic|full\u2014\u2014basic \u7F3A\u7701\u4E0D\u643A\u5E26 artifact \u5168\u6587\u5FEB\u7167\uFF0Cfull \u65AD\u70B9\u7EED\u8DD1\u9700\u5386\u53F2\u4EA7\u7269\u5168\u6587\u65F6\u7528\u3002\u26A0 \u514D\u91CD\u8BFB\uFF1Aadvance/approve \u6D41\u8F6C\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u5185\u6D41\u8F6C\u540E\u76F4\u63A5\u5F00\u5DE5\u3001\u65E0\u9700\u518D\u8C03 context\uFF1B\u4EC5\u65B0\u4F1A\u8BDD/\u65AD\u70B9\u7EED\u8DD1\u8C03\u7528)\uFF1Bnode(taskId,view=prompt|skills\uFF1BnodeId \u7F3A\u7701\u7528\u5F53\u524D\u8282\u70B9\uFF1B\u26A0 view \u503C\u968F action \u4E0D\u540C\uFF1Anode\u2192prompt|skills\uFF0Ccontext\u2192basic|full\uFF0C\u9519\u914D\u62A5\u9519)\uFF1Badvance(taskId\uFF1Bnote \u5199 history\u3001summary \u5199 nodeRecords\uFF0C\u4E24\u4F4D\u72EC\u7ACB\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bapprove(taskId,decision=approved|rejected\uFF1B\u53EF\u9009 comment\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bpause(taskId\uFF1B\u53EF\u9009 reason)\uFF1Bresume(taskId\uFF1B\u53EF\u9009 resumeNote)\uFF1Bcancel(taskId\uFF1B\u53EF\u9009 reason 1-500 \u5B57\u7B26\uFF1B\u26A0 \u7EC8\u6001\u4E0D\u53EF\u9006\u2014\u2014active/paused \u53EF\u53D6\u6D88\uFF0Ccompleted/cancelled \u62D2\u7EDD\u5E76\u8FD4\u56DE TASK_TERMINATED\uFF1B\u53D6\u6D88\u540E\u7ECF\u672C\u5DE5\u5177\u7684\u6D41\u8F6C\u4E0E\u8BB0\u5F55\u5199\u64CD\u4F5C\u88AB\u62D2)\uFF1Bhistory(taskId)\uFF1Binstance(taskId\u2014\u2014\u67E5\u770B\u4EFB\u52A1 DAG \u5B9E\u4F8B\u5FEB\u7167\uFF1A\u521B\u5EFA\u4EFB\u52A1\u65F6\u51BB\u7ED3\u7684\u6A21\u677F\u5B9E\u4F8B\uFF0C\u6A21\u677F\u540E\u7EED\u53D8\u66F4\u4E0D\u56DE\u6EAF)\u3002\u8BB0\u5F55\u5199\u5165\uFF1Arecord-set(taskId,node
|
|
2492
|
+
"task \u8D44\u6E90\u57DF\u805A\u5408\u5DE5\u5177\uFF08\u4EFB\u52A1\u5168\u64CD\u4F5C\u7ECF\u672C\u5DE5\u5177 + action \u53C2\u6570\uFF09\u3002action \u5FC5\u586B\uFF1B\u8C03\u7528\u65B9\u5E94\u5728 fieldMask \u4E2D\u5217\u51FA\u672C\u6B21\u771F\u5B9E\u63D0\u4F9B\u7684\u53EF\u9009\u5B57\u6BB5\uFF0Cmask \u5916\u7531\u5BBF\u4E3B\u81EA\u52A8\u586B\u5145\u7684\u7A7A\u4E32\u3001\u7A7A\u6570\u7EC4\u3001false\u30010 \u5747\u5FFD\u7565\uFF0Cmask \u5185\u7A7A\u6570\u7EC4/false \u8868\u793A\u663E\u5F0F\u6E05\u7A7A\u6216\u5173\u95ED\uFF1B\u5404 action \u56FA\u5B9A\u5FC5\u586B\u5B57\u6BB5\u65E0\u9700\u5199\u5165 mask\u3002\u5176\u4F59\u53C2\u6570\u5168\u53EF\u9009\u3001\u6309 action \u8FD0\u884C\u65F6\u6821\u9A8C\u5FC5\u586B\uFF08\u7F3A\u5931\u8FD4\u56DE\u5931\u8D25\u4E09\u8981\u7D20\uFF1A\u5BF9\u8C61+\u6839\u56E0+\u4FEE\u590D\u6307\u5F15\uFF09\u3002projectId \u4E3A key|id \u53CC\u5F62\u6001\uFF0C\u901A\u9053\u5C42\u81EA\u52A8\u5F52\u4E00\u4E3A 24-hex id\uFF1B\u663E\u5F0F\u6307\u5B9A\u89E3\u6790\u5931\u8D25\u62A5\u9519\u4E0D\u515C\u5E95\uFF1B\u7F3A\u7701\u8BED\u4E49\u6309 action\uFF1Acreate \u843D\u9ED8\u8BA4\u9879\u76EE\u94FE\uFF08\u5DE5\u4F5C\u533A defaultProject \u5931\u6548\u62A5\u9519\uFF0C\u65E0\u914D\u7F6E\u4E0D\u4F20\u7531 server \u6309\u6A21\u677F\u5F52\u5C5E\u515C\u5E95\uFF09\uFF0Cquery \u4E0D\u8FC7\u6EE4\uFF08\u5168\u5C40\u67E5\u8BE2\uFF09\u3002\u5199\u64CD\u4F5C\uFF08create/doc-*/record-*/archnote-add/pause/resume/cancel\uFF09\u54CD\u5E94\u4E3A\u8F7B\u91CF\u56DE\u6267\uFF1A{taskId, updated(\u89E6\u53CA\u5B57\u6BB5\u8DEF\u5F84), entry(\u65B0\u6761\u76EE id+kind\uFF0C\u4F9B\u540E\u7EED patch \u5B9A\u4F4D), echo(\u5199\u5165\u5185\u5BB9\u56DE\u663E), updatedAt, task(\u4EFB\u52A1\u8FDB\u5EA6\u6458\u8981 progress: {completed,total})}\u2014\u2014\u4E0D\u8FD4\u56DE\u5B8C\u6574\u4EFB\u52A1\u5B9E\u4F53\u3002action \u6E05\u5355\uFF08\u62EC\u53F7\u5185\u4E3A\u8BE5 action \u5FC5\u586B\u53C2\u6570\uFF09\uFF1Acreate(templateId,taskTitle,track\uFF1B\u53EF\u9009 type=feature|bugfix|ui-tweak|research\u3001skipNodes\u3001projectId\uFF1BtemplateId \u652F\u6301 code|id \u53CC\u5F62\u6001\u2014\u2014code \u4E3A\u6A21\u677F\u4E1A\u52A1\u6807\u8BC6\uFF08\u9879\u76EE\u5185\u552F\u4E00\u3001kebab-case \u5FC5\u542B\u8FDE\u5B57\u7B26\uFF0C\u5982 full-workflow\uFF09\uFF0C\u63A8\u8350\u5F15\u7528\u5F62\u6001\uFF0C\u5BFB\u5740\u987B\u914D projectId \u6307\u5B9A\u9879\u76EE\u57DF\uFF1B24-hex \u6570\u636E\u5E93 id \u76F4\u67E5\u517C\u5BB9\uFF1Bcode \u5F62\u6001\u4E0D\u5B58\u5728\u65F6\u9519\u8BEF\u542B candidates \u76F8\u8FD1\u5019\u9009\u6E05\u5355\uFF08id/code/name \u22645 \u6761\uFF09\uFF1B\u54CD\u5E94 {task, firstNode}\u2014\u2014firstNode \u5373\u9996\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u514D\u8C03 context)\uFF1Bquery(taskId \u8FD4\u56DE\u5355\u4EFB\u52A1\u8BE6\u60C5\uFF0C\u7F3A\u7701\u8FD4\u56DE\u5217\u8868\uFF0C\u53EF\u9009 status/track/projectId/page/limit)\uFF1Bcontext(taskId\uFF1B\u53EF\u9009 view=basic|full\u2014\u2014basic \u7F3A\u7701\u4E0D\u643A\u5E26 artifact \u5168\u6587\u5FEB\u7167\uFF0Cfull \u65AD\u70B9\u7EED\u8DD1\u9700\u5386\u53F2\u4EA7\u7269\u5168\u6587\u65F6\u7528\u3002\u26A0 \u514D\u91CD\u8BFB\uFF1Aadvance/approve \u6D41\u8F6C\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u5B8C\u6574\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u5185\u6D41\u8F6C\u540E\u76F4\u63A5\u5F00\u5DE5\u3001\u65E0\u9700\u518D\u8C03 context\uFF1B\u4EC5\u65B0\u4F1A\u8BDD/\u65AD\u70B9\u7EED\u8DD1\u8C03\u7528)\uFF1Bnode(taskId,view=prompt|skills\uFF1BnodeId \u7F3A\u7701\u7528\u5F53\u524D\u8282\u70B9\uFF1B\u26A0 view \u503C\u968F action \u4E0D\u540C\uFF1Anode\u2192prompt|skills\uFF0Ccontext\u2192basic|full\uFF0C\u9519\u914D\u62A5\u9519)\uFF1Badvance(taskId\uFF1Bnote \u5199 history\u3001summary \u5199 nodeRecords\uFF0C\u4E24\u4F4D\u72EC\u7ACB\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context\u3002\u26A0 T202609230001 \u8282\u70B9\u95E8\u7981\uFF1A\u914D\u7F6E\u4E86 toolChecks/aiChecks \u7684\u8282\u70B9\u63A8\u8FDB\u524D\u81EA\u52A8\u6267\u884C\u68C0\u67E5\u2014\u2014fail \u4F1A\u88AB\u6253\u56DE\u5E76\u8FD4\u56DE\u9010\u9879\u4FEE\u590D\u6307\u5F15\uFF08\u7ED3\u679C\u5DF2\u5199 history checks_rejected\uFF09\uFF0C\u6309\u6307\u5F15\u4FEE\u590D\u6216\u83B7\u7528\u6237\u8C41\u514D\u7559\u75D5\u540E\u91CD\u63A8\uFF1B\u975E\u4EBA\u5DE5\u5BA1\u6279\u8282\u70B9\u7981\u6B62\u56E0\u95E8\u7981\u4E2D\u65AD\u4EFB\u52A1\uFF08\u6253\u56DE\u4FE1\u606F\u5373\u7EE7\u7EED\u4F9D\u636E\uFF09\u3002force+forceReason \u6210\u5BF9\u63D0\u4F9B\u65F6\u53EF\u8D8A\u8FC7 fail \u5F3A\u5236\u63A8\u8FDB\u2014\u2014\u4EC5\u51ED\u7528\u6237\u660E\u793A\u6307\u4EE4\uFF0CAI \u7981\u6B62\u81EA\u51B3\uFF0C\u7406\u7531\u539F\u6587\u843D history \u5BA1\u8BA1)\uFF1Bcheck(taskId\uFF1B\u95E8\u7981\u53EA\u8BFB\u9884\u68C0\u2014\u2014\u9010\u9879\u62A5\u544A\u4E0D\u5199\u4EFB\u4F55\u72B6\u6001\uFF0Cadvance \u524D\u81EA\u67E5\u7528)\uFF1Bapprove(taskId,decision=approved|rejected\uFF1B\u53EF\u9009 comment\uFF1B\u54CD\u5E94\u5DF2\u542B\u4E0B\u4E00\u8282\u70B9\u8D44\u6599\u5305\uFF0C\u540C\u4F1A\u8BDD\u514D\u91CD\u8BFB context)\uFF1Bpause(taskId\uFF1B\u53EF\u9009 reason)\uFF1Bresume(taskId\uFF1B\u53EF\u9009 resumeNote)\uFF1Bcancel(taskId\uFF1B\u53EF\u9009 reason 1-500 \u5B57\u7B26\uFF1B\u26A0 \u7EC8\u6001\u4E0D\u53EF\u9006\u2014\u2014active/paused \u53EF\u53D6\u6D88\uFF0Ccompleted/cancelled \u62D2\u7EDD\u5E76\u8FD4\u56DE TASK_TERMINATED\uFF1B\u53D6\u6D88\u540E\u7ECF\u672C\u5DE5\u5177\u7684\u6D41\u8F6C\u4E0E\u8BB0\u5F55\u5199\u64CD\u4F5C\u88AB\u62D2)\uFF1Bhistory(taskId)\uFF1Binstance(taskId\u2014\u2014\u67E5\u770B\u4EFB\u52A1 DAG \u5B9E\u4F8B\u5FEB\u7167\uFF1A\u521B\u5EFA\u4EFB\u52A1\u65F6\u51BB\u7ED3\u7684\u6A21\u677F\u5B9E\u4F8B\uFF0C\u6A21\u677F\u540E\u7EED\u53D8\u66F4\u4E0D\u56DE\u6EAF)\u3002\u8BB0\u5F55\u5199\u5165\uFF1Arecord-set(taskId,node\uFF1Bsummary \u4E0E\u516D\u4E2A\u7ED3\u6784\u5316\u5B57\u6BB5\uFF08baseBranch/verifiedCommit/fixesApplied/testReport/deferredIssues/evidenceFiles\uFF09\u81F3\u5C11\u5176\u4E00\u2014\u2014\u7ED3\u6784\u5316\u5B57\u6BB5\u6309\u8282\u70B9 prompt\u300C\u7ED3\u6784\u5316\u5B57\u6BB5\u5199\u5165\u8981\u6C42\u300D\u9010\u5B57\u6BB5\u843D\u5E93\uFF0CPATCH \u8BED\u4E49=\u4EC5\u66F4\u65B0\u63D0\u4F9B\u5B57\u6BB5)\uFF1Brecord-check-add(taskId,node,item\uFF1B\u53EF\u9009 passed \u7F3A\u7701 true\u3001refId=aiChecks \u81EA\u68C0\u7559\u75D5\u951A\uFF08\u5BA1\u8BA1\u8BA4 refId===aiCheck.id \u4E14 passed=true\uFF0C\u7F3A\u7559\u75D5\u7684\u5B8C\u6210\u5224\u5B9A\u5BA1\u8BA1 fail\uFF09\u3001source=self|tool \u672C\u7248\u5DE5\u5177\u7F3A\u7701\u4E0D\u5199\uFF1B\u6279\u91CF checkItems \u5143\u7D20\u540C\u6784\uFF1B\u56DE\u6267 entry.id \u4F9B record-check-patch \u5B9A\u4F4D)\uFF1Brecord-check-patch(taskId,node,checkId\uFF1B\u53EF\u9009 passed/item/refId/source\uFF0C\u4EC5\u66F4\u65B0\u63D0\u4F9B\u5B57\u6BB5)\uFF1Brecord-artifact-add(taskId,node,artifactType=prd|tech|code|test|doc|other\uFF1Bpath \u7EAF\u5F15\u7528 / file \u8BFB\u672C\u673A\u6587\u4EF6 / content \u76F4\u4F20\u5168\u6587\u2014\u2014file \u4E0E content\u3001file \u4E0E path \u4E92\u65A5\uFF0Ccontent \u53EF\u4E0E path \u6210\u5BF9\u6216\u5355\u72EC\uFF0C\u5355\u72EC\u65F6\u767B\u8BB0\u503C\u5408\u6210 mcp-inline/<type>\uFF1B\u53EF\u9009 note)\uFF1Brecord-confirm(taskId,node,quote\u2014\u2014\u6682\u505C\u70B9\u7528\u6237\u786E\u8BA4\u539F\u6587\u9010\u5B57)\uFF1Brecord-decision-add(taskId,node,topic,decisionText)\uFF1Brecord-review-set(taskId,node,verdict=pass|fail,rounds,critical)\u3002\u9700\u6C42\u6587\u6863\uFF1Adoc-set(taskId\uFF1Bwhat/why/trackNote \u81F3\u5C11\u4F20\u4E00)\uFF1Bdoc-acceptance-add(taskId,text)\uFF1Bdoc-non-goal-add(taskId,text)\u3002\u7D20\u6750\uFF1Aarchnote-add(taskId,text)\u3002\u6279\u91CF\u5F62\u6001\uFF08\u63A8\u8350\uFF0C\u516D\u7C7B\u540C\u6784\uFF09\uFF1Arecord-check-add \u4F20 checkItems=[{item,passed?}]\u3001record-artifact-add \u4F20 artifactEntries=[{artifactType,path?/file?/content?,note?}]\u3001record-decision-add \u4F20 decisionEntries=[{topic,decisionText}]\u3001doc-acceptance-add/doc-non-goal-add/archnote-add \u4F20 textItems=[string]\u2014\u2014\u6279\u91CF\u53C2\u6570\u4E0E\u5355\u6761\u53C2\u6570\u4E8C\u9009\u4E00\uFF0C\u4E00\u6B21\u8C03\u7528\u5199\u591A\u6761\uFF08\u4E0A\u9650 50 \u6761/\u6279\uFF0Cartifact \u5168\u6587\u5FEB\u7167\u603B\u91CF \u22642M \u5B57\u7B26/\u6279\uFF09\uFF1B\u90E8\u5206\u5931\u8D25\u4FDD\u7559\u6210\u529F\u6761\u76EE\u5E76\u9010\u6761\u62A5\u544A\uFF08failures[].index=\u539F\u59CB\u63D0\u4EA4\u5E8F\uFF0C\u53EA\u91CD\u8BD5\u5931\u8D25\u9879\uFF09\uFF0Capplied=0 \u6574\u4F53\u62A5\u5931\u8D25\u3002",
|
|
1692
2493
|
taskArgs,
|
|
1693
2494
|
async (rawArgs) => {
|
|
1694
2495
|
const masked = applyFieldMask(rawArgs, TASK_FIELD_SPECS);
|
|
@@ -1776,7 +2577,19 @@ function createMcpServer() {
|
|
|
1776
2577
|
case "advance": {
|
|
1777
2578
|
const { taskId } = args;
|
|
1778
2579
|
if (taskId === void 0) return requireError("advance", ["taskId"], args);
|
|
1779
|
-
|
|
2580
|
+
if (args.force === true !== (args.forceReason !== void 0)) {
|
|
2581
|
+
return localError("force \u4E0E forceReason \u5FC5\u987B\u6210\u5BF9\u63D0\u4F9B", '\u5F3A\u5236\u63A8\u8FDB\u4EC5\u51ED\u7528\u6237\u660E\u793A\u6307\u4EE4\uFF1A{"force":true,"forceReason":"<\u7528\u6237\u539F\u8BDD>"}');
|
|
2582
|
+
}
|
|
2583
|
+
const gate = {
|
|
2584
|
+
...args.force === true ? { force: true } : {},
|
|
2585
|
+
...args.forceReason !== void 0 ? { forceReason: args.forceReason } : {}
|
|
2586
|
+
};
|
|
2587
|
+
return mcpContent(await handleAdvanceWithGate(client, taskId, gate, args.note, args.summary, args.finalize === true));
|
|
2588
|
+
}
|
|
2589
|
+
case "check": {
|
|
2590
|
+
const { taskId } = args;
|
|
2591
|
+
if (taskId === void 0) return requireError("check", ["taskId"], args);
|
|
2592
|
+
return mcpContent(await handleTaskCheck(client, taskId));
|
|
1780
2593
|
}
|
|
1781
2594
|
case "approve": {
|
|
1782
2595
|
const { taskId, decision } = args;
|
|
@@ -1818,11 +2631,26 @@ function createMcpServer() {
|
|
|
1818
2631
|
return mcpContent(await handleGetInstance(client, taskId));
|
|
1819
2632
|
}
|
|
1820
2633
|
case "record-set": {
|
|
1821
|
-
const { taskId, node
|
|
1822
|
-
if (taskId === void 0 || node === void 0
|
|
1823
|
-
return requireError("record-set", ["taskId", "node"
|
|
2634
|
+
const { taskId, node } = args;
|
|
2635
|
+
if (taskId === void 0 || node === void 0) {
|
|
2636
|
+
return requireError("record-set", ["taskId", "node"], args);
|
|
1824
2637
|
}
|
|
1825
|
-
|
|
2638
|
+
const fields = {
|
|
2639
|
+
...args.summary !== void 0 ? { summary: args.summary } : {},
|
|
2640
|
+
...args.baseBranch !== void 0 ? { baseBranch: args.baseBranch } : {},
|
|
2641
|
+
...args.verifiedCommit !== void 0 ? { verifiedCommit: args.verifiedCommit } : {},
|
|
2642
|
+
...args.fixesApplied !== void 0 ? { fixesApplied: args.fixesApplied } : {},
|
|
2643
|
+
...args.testReport !== void 0 ? { testReport: args.testReport } : {},
|
|
2644
|
+
...args.deferredIssues !== void 0 ? { deferredIssues: args.deferredIssues } : {},
|
|
2645
|
+
...args.evidenceFiles !== void 0 ? { evidenceFiles: args.evidenceFiles } : {}
|
|
2646
|
+
};
|
|
2647
|
+
if (Object.keys(fields).length === 0) {
|
|
2648
|
+
return localError(
|
|
2649
|
+
"record-set \u9700\u8981\u81F3\u5C11\u4E00\u4E2A\u5B57\u6BB5\uFF08summary \u6216\u7ED3\u6784\u5316\u5B57\u6BB5\uFF09",
|
|
2650
|
+
"\u7ED3\u6784\u5316\u5B57\u6BB5\u6309\u8282\u70B9 prompt\u300C\u7ED3\u6784\u5316\u5B57\u6BB5\u5199\u5165\u8981\u6C42\u300D\u9010\u5B57\u6BB5\u843D\u5E93\uFF1AbaseBranch/verifiedCommit/fixesApplied/testReport/deferredIssues/evidenceFiles"
|
|
2651
|
+
);
|
|
2652
|
+
}
|
|
2653
|
+
return mcpContent(await handleRecordSummarySet(client, taskId, node, fields));
|
|
1826
2654
|
}
|
|
1827
2655
|
case "record-check-add": {
|
|
1828
2656
|
const { taskId, node } = args;
|
|
@@ -1842,7 +2670,13 @@ function createMcpServer() {
|
|
|
1842
2670
|
if (taskId === void 0 || node === void 0 || item === void 0) {
|
|
1843
2671
|
return requireError("record-check-add", ["taskId", "node", "item"], args);
|
|
1844
2672
|
}
|
|
1845
|
-
return mcpContent(
|
|
2673
|
+
return mcpContent(
|
|
2674
|
+
await handleRecordCheckAdd(client, taskId, node, item, args.passed ?? true, {
|
|
2675
|
+
// refId=aiChecks 自检留痕锚(审计认 refId===check.id 且 passed);source 本版工具缺省不写
|
|
2676
|
+
...args.refId !== void 0 ? { refId: args.refId } : {},
|
|
2677
|
+
...args.source !== void 0 ? { source: args.source } : {}
|
|
2678
|
+
})
|
|
2679
|
+
);
|
|
1846
2680
|
}
|
|
1847
2681
|
case "record-check-patch": {
|
|
1848
2682
|
const { taskId, node, checkId } = args;
|
|
@@ -1850,7 +2684,13 @@ function createMcpServer() {
|
|
|
1850
2684
|
return requireError("record-check-patch", ["taskId", "node", "checkId"], args);
|
|
1851
2685
|
}
|
|
1852
2686
|
return mcpContent(
|
|
1853
|
-
await handleRecordCheckPatch(client, taskId, node, checkId,
|
|
2687
|
+
await handleRecordCheckPatch(client, taskId, node, checkId, {
|
|
2688
|
+
// H2:passed 仅显式透传——缺省不默认 true(防未通过留痕被静默翻绿绕过自检门禁)
|
|
2689
|
+
...args.passed !== void 0 ? { passed: args.passed } : {},
|
|
2690
|
+
...args.item !== void 0 ? { item: args.item } : {},
|
|
2691
|
+
...args.refId !== void 0 ? { refId: args.refId } : {},
|
|
2692
|
+
...args.source !== void 0 ? { source: args.source } : {}
|
|
2693
|
+
})
|
|
1854
2694
|
);
|
|
1855
2695
|
}
|
|
1856
2696
|
case "record-artifact-add": {
|
|
@@ -2438,9 +3278,29 @@ function createTaskCommand() {
|
|
|
2438
3278
|
}
|
|
2439
3279
|
renderResult("json", result);
|
|
2440
3280
|
});
|
|
2441
|
-
task.command("advance <taskId>").description("Advance a task to the next node (record contents already written via `task record/doc`; this is the light finish). At the final node the advance is rejected unless --finalize is passed (completion confirmation)").option("--url <url>", "server base URL").option("--format <fmt>", "output format", "json").option("--note <text>", "advance note recorded in history").option("--summary <text>", "finishing summary written to nodeRecords.<node>.summary (equivalent to a final `record set --summary`)").option("--finalize", "completion confirmation: finalize the task at its final node (required there; ignored mid-flow)").action(async (taskId, opts) => {
|
|
3281
|
+
task.command("advance <taskId>").description("Advance a task to the next node (record contents already written via `task record/doc`; this is the light finish). At the final node the advance is rejected unless --finalize is passed (completion confirmation). Node gate checks run before the request; failures block the advance").option("--url <url>", "server base URL").option("--format <fmt>", "output format", "json").option("--note <text>", "advance note recorded in history").option("--summary <text>", "finishing summary written to nodeRecords.<node>.summary (equivalent to a final `record set --summary`)").option("--finalize", "completion confirmation: finalize the task at its final node (required there; ignored mid-flow)").option("--force", "bypass gate failures (user instruction; requires --force-reason, audit trail written to history)").option("--force-reason <text>", "verbatim user instruction justifying --force (audit trail)").action(async (taskId, opts) => {
|
|
3282
|
+
if (opts.force === true !== (opts.forceReason !== void 0)) {
|
|
3283
|
+
renderResult(opts.format, fail({
|
|
3284
|
+
message: "--force \u4E0E --force-reason \u5FC5\u987B\u6210\u5BF9\u63D0\u4F9B",
|
|
3285
|
+
hint: '\u7528\u6CD5\uFF1Asiming task advance <taskId> --force --force-reason "<\u7528\u6237\u660E\u793A\u539F\u6587>"\uFF1B\u7559\u75D5\u5C06\u5199\u5165\u4EFB\u52A1 history',
|
|
3286
|
+
httpStatus: 0
|
|
3287
|
+
}));
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
2442
3290
|
const client = createClient(opts.url);
|
|
2443
|
-
const result =
|
|
3291
|
+
const result = opts.force === true ? await handleAdvanceWithGate(
|
|
3292
|
+
client,
|
|
3293
|
+
taskId,
|
|
3294
|
+
opts.forceReason === void 0 ? { force: true } : { force: true, forceReason: opts.forceReason },
|
|
3295
|
+
opts.note,
|
|
3296
|
+
opts.summary,
|
|
3297
|
+
opts.finalize === true
|
|
3298
|
+
) : await handleAdvance(client, taskId, opts.note, opts.summary, opts.finalize === true);
|
|
3299
|
+
renderResult(opts.format, result);
|
|
3300
|
+
});
|
|
3301
|
+
task.command("check <taskId>").description("Read-only gate check: run current node tool/ai checks and print the result matrix (no writes, no history, no advance)").option("--url <url>", "server base URL").option("--format <fmt>", "output format", "json").action(async (taskId, opts) => {
|
|
3302
|
+
const client = createClient(opts.url);
|
|
3303
|
+
const result = await handleTaskCheck(client, taskId);
|
|
2444
3304
|
renderResult(opts.format, result);
|
|
2445
3305
|
});
|
|
2446
3306
|
task.command("approve <taskId>").description("Approve or reject a task paused at a human_approval pause point").option("--url <url>", "server base URL").option("--format <fmt>", "output format", "json").requiredOption("--decision <d>", "decision: approved|rejected").option("--comment <text>", "decision comment recorded in history").action(async (taskId, opts) => {
|
|
@@ -2575,17 +3435,60 @@ function createTaskCommand() {
|
|
|
2575
3435
|
renderResult(opts.format, result, { fields: result.success ? nodeFields(result.data) : [] });
|
|
2576
3436
|
});
|
|
2577
3437
|
const record = task.command("record").description("Node execution records \u2014 summary/checks/artifacts/confirmations/decisions/review (draft-writable, small-step append)");
|
|
2578
|
-
withCommonOpts(record.command("set <taskId>"), "fancy").description("Set node summary (
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
3438
|
+
withCommonOpts(record.command("set <taskId>"), "fancy").description("Set node record fields \u2014 --summary and/or structured fields (T202609230001: baseBranch/testReport/... ; at least one required)").requiredOption("--node <nid>", "node id (see `task context`)").option("--summary <text>", "node summary text").option("--base-branch <branch>", "align \u57FA\u7840\u5206\u652F\uFF08test-integrate \u7B49\u8282\u70B9\u7ED3\u6784\u5316\u5199\u5165\uFF09").option("--verified-commit <sha>", "\u9A8C\u8BC1\u901A\u8FC7\u5BF9\u5E94\u7684\u63D0\u4EA4\u53F7\uFF08verifiedCommit\uFF09").option("--fixes-applied <n>", "fixesApplied \u4FEE\u590D\u6B21\u6570\uFF08\u975E\u8D1F\u6574\u6570\uFF1BB4 \u6761\u4EF6\u95E8\u7981\uFF1A>0 \u987B\u8BC4\u5BA1 verdict=pass\uFF09").option("--test-report <json>", `testReport JSON\uFF1A'{"command":"pnpm test","passed":10,"failed":0,"skipped":0}'`).option("--deferred-issues <list>", "\u5EF6\u540E\u95EE\u9898 id \u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF09").option("--evidence-files <list>", "\u8BC1\u636E\u6587\u4EF6\u8DEF\u5F84\u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF09").action(
|
|
3439
|
+
async (taskId, opts) => {
|
|
3440
|
+
const client = createClient(opts.url);
|
|
3441
|
+
let fixesApplied;
|
|
3442
|
+
let testReport;
|
|
3443
|
+
if (opts.fixesApplied !== void 0) {
|
|
3444
|
+
const n = Number(opts.fixesApplied);
|
|
3445
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
3446
|
+
renderResult(opts.format, fail({ message: `--fixes-applied \u987B\u4E3A\u975E\u8D1F\u6574\u6570\uFF08\u6536\u5230 ${opts.fixesApplied}\uFF09`, httpStatus: 0 }));
|
|
3447
|
+
return;
|
|
3448
|
+
}
|
|
3449
|
+
fixesApplied = n;
|
|
3450
|
+
}
|
|
3451
|
+
if (opts.testReport !== void 0) {
|
|
3452
|
+
try {
|
|
3453
|
+
testReport = JSON.parse(opts.testReport);
|
|
3454
|
+
} catch {
|
|
3455
|
+
renderResult(opts.format, fail({ message: `--test-report JSON \u89E3\u6790\u5931\u8D25: ${opts.testReport}`, httpStatus: 0 }));
|
|
3456
|
+
return;
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
const splitList = (raw) => raw === void 0 ? void 0 : raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3460
|
+
const deferredIssues = splitList(opts.deferredIssues);
|
|
3461
|
+
const evidenceFiles = splitList(opts.evidenceFiles);
|
|
3462
|
+
const fields = {
|
|
3463
|
+
...opts.summary !== void 0 ? { summary: opts.summary } : {},
|
|
3464
|
+
...opts.baseBranch !== void 0 ? { baseBranch: opts.baseBranch } : {},
|
|
3465
|
+
...opts.verifiedCommit !== void 0 ? { verifiedCommit: opts.verifiedCommit } : {},
|
|
3466
|
+
...fixesApplied !== void 0 ? { fixesApplied } : {},
|
|
3467
|
+
...testReport !== void 0 ? { testReport } : {},
|
|
3468
|
+
...deferredIssues !== void 0 ? { deferredIssues } : {},
|
|
3469
|
+
...evidenceFiles !== void 0 ? { evidenceFiles } : {}
|
|
3470
|
+
};
|
|
3471
|
+
if (Object.keys(fields).length === 0) {
|
|
3472
|
+
renderResult(
|
|
3473
|
+
opts.format,
|
|
3474
|
+
fail({
|
|
3475
|
+
message: "\u9700\u8981\u81F3\u5C11\u4E00\u4E2A\u5B57\u6BB5\uFF1A--summary \u6216\u7ED3\u6784\u5316\u5B57\u6BB5\uFF08--base-branch/--verified-commit/--fixes-applied/--test-report/--deferred-issues/--evidence-files\uFF09",
|
|
3476
|
+
httpStatus: 0,
|
|
3477
|
+
hint: "\u7ED3\u6784\u5316\u5B57\u6BB5\u6309\u8282\u70B9 prompt\u300C\u7ED3\u6784\u5316\u5B57\u6BB5\u5199\u5165\u8981\u6C42\u300D\u9010\u5B57\u6BB5\u843D\u5E93\uFF08summary PATCH \u8BED\u4E49=\u4EC5\u66F4\u65B0\u63D0\u4F9B\u5B57\u6BB5\uFF09"
|
|
3478
|
+
})
|
|
3479
|
+
);
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3482
|
+
const result = await handleRecordSummarySet(client, taskId, opts.node, fields);
|
|
3483
|
+
if (result.success) {
|
|
3484
|
+
renderResult(opts.format, result, { successMessage: `${taskId} \u8282\u70B9 ${opts.node} record \u5DF2\u5199\u5165\uFF08${Object.keys(fields).join("/")}\uFF09` });
|
|
3485
|
+
} else {
|
|
3486
|
+
renderResult(opts.format, result);
|
|
3487
|
+
}
|
|
2585
3488
|
}
|
|
2586
|
-
|
|
3489
|
+
);
|
|
2587
3490
|
const check = record.command("check").description("Completion-check items (server-generated short ids)");
|
|
2588
|
-
withCommonOpts(check.command("add <taskId>"), "fancy").description("Append completion-check item(s) \u2014 single --item (legacy form) / repeat --item or --entry JSON for batch (T202609010002)").requiredOption("--node <nid>", "node id").option("--item <text>", "check item text; repeatable \xD72+ = batch (single occurrence without --entry = single form; mixed with --entry, --item group comes first in submit order)", collectArgs, []).option("--entry <json>", 'batch element {"item":"...","passed":true} (repeatable; passed per-item
|
|
3491
|
+
withCommonOpts(check.command("add <taskId>"), "fancy").description("Append completion-check item(s) \u2014 single --item (legacy form) / repeat --item or --entry JSON for batch (T202609010002)").requiredOption("--node <nid>", "node id").option("--item <text>", "check item text; repeatable \xD72+ = batch (single occurrence without --entry = single form; mixed with --entry, --item group comes first in submit order)", collectArgs, []).option("--entry <json>", 'batch element {"item":"...","passed":true,"refId":"ai-check-id","source":"tool"} (repeatable; passed per-item default true; refId=aiChecks \u81EA\u68C0\u7559\u75D5\u951A)', collectArgs, []).option("--failed", "record as not passed (single form only; mutually exclusive with batch params)").option("--ref-id <id>", "aiCheck id \u7559\u75D5\u951A\uFF08\u5355\u6761\u5F62\u6001\uFF1B\u5BA1\u8BA1\u8BA4 refId===aiCheck.id \u4E14 passed\uFF09").option("--source <src>", "\u6761\u76EE\u6765\u6E90\u6807\u8BB0\uFF08self|tool\uFF1B\u672C\u7248\u5DE5\u5177\u7F3A\u7701\u4E0D\u5199\uFF0Cself \u4E3A AI \u81EA\u62A5\u7F3A\u7701\uFF09").action(async (taskId, opts) => {
|
|
2589
3492
|
const client = createClient(opts.url);
|
|
2590
3493
|
const items = opts.item ?? [];
|
|
2591
3494
|
const rawEntries = opts.entry ?? [];
|
|
@@ -2607,7 +3510,14 @@ function createTaskCommand() {
|
|
|
2607
3510
|
return;
|
|
2608
3511
|
}
|
|
2609
3512
|
if (!batchMode) {
|
|
2610
|
-
|
|
3513
|
+
if (opts.source !== void 0 && opts.source !== "self" && opts.source !== "tool") {
|
|
3514
|
+
renderResult(opts.format, fail({ message: `--source \u4EC5\u652F\u6301 self|tool\uFF08\u6536\u5230 ${opts.source}\uFF09`, httpStatus: 0 }));
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
const result2 = await handleRecordCheckAdd(client, taskId, opts.node, items[0], !opts.failed, {
|
|
3518
|
+
...opts.refId !== void 0 ? { refId: opts.refId } : {},
|
|
3519
|
+
...opts.source !== void 0 ? { source: opts.source } : {}
|
|
3520
|
+
});
|
|
2611
3521
|
if (result2.success) {
|
|
2612
3522
|
renderResult(opts.format, result2, { successMessage: `${taskId} \u8282\u70B9 ${opts.node} check \u5DF2\u6DFB\u52A0\uFF08\u65B0\u6761\u76EE id ${result2.data.entry?.id ?? "?"}\uFF09` });
|
|
2613
3523
|
} else {
|
|
@@ -2631,12 +3541,25 @@ function createTaskCommand() {
|
|
|
2631
3541
|
localFailures.push({ index: submitIndex, message: "passed \u987B\u4E3A\u5E03\u5C14" });
|
|
2632
3542
|
return;
|
|
2633
3543
|
}
|
|
3544
|
+
if (j.refId !== void 0 && typeof j.refId !== "string") {
|
|
3545
|
+
localFailures.push({ index: submitIndex, message: "refId \u987B\u4E3A\u5B57\u7B26\u4E32\uFF08aiCheck id\uFF09" });
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
if (j.source !== void 0 && j.source !== "self" && j.source !== "tool") {
|
|
3549
|
+
localFailures.push({ index: submitIndex, message: "source \u4EC5\u652F\u6301 self|tool" });
|
|
3550
|
+
return;
|
|
3551
|
+
}
|
|
2634
3552
|
entryPairs.push({
|
|
2635
|
-
entry: {
|
|
3553
|
+
entry: {
|
|
3554
|
+
item: j.item,
|
|
3555
|
+
...typeof j.passed === "boolean" ? { passed: j.passed } : {},
|
|
3556
|
+
...typeof j.refId === "string" ? { refId: j.refId } : {},
|
|
3557
|
+
...j.source === "self" || j.source === "tool" ? { source: j.source } : {}
|
|
3558
|
+
},
|
|
2636
3559
|
rawIndex: i
|
|
2637
3560
|
});
|
|
2638
3561
|
} catch {
|
|
2639
|
-
localFailures.push({ index: submitIndex, message: `--entry JSON \u89E3\u6790\u5931\u8D25: ${raw}`, hint: '\u5143\u7D20\u5F62\u5982 {"item":"...","passed":true}' });
|
|
3562
|
+
localFailures.push({ index: submitIndex, message: `--entry JSON \u89E3\u6790\u5931\u8D25: ${raw}`, hint: '\u5143\u7D20\u5F62\u5982 {"item":"...","passed":true,"refId":"..."}' });
|
|
2640
3563
|
}
|
|
2641
3564
|
});
|
|
2642
3565
|
const submitted = [...itemElements, ...entryPairs.map((p) => p.entry)];
|
|
@@ -2653,7 +3576,7 @@ function createTaskCommand() {
|
|
|
2653
3576
|
});
|
|
2654
3577
|
withCommonOpts(check.command("patch <taskId>"), "fancy").description("Flip one check item pass/fail state by check id").requiredOption("--node <nid>", "node id").requiredOption("--check <cid>", "check entry id (from check add output / task context)").option("--failed", "flip to not passed (default: passed)").action(async (taskId, opts) => {
|
|
2655
3578
|
const client = createClient(opts.url);
|
|
2656
|
-
const result = await handleRecordCheckPatch(client, taskId, opts.node, opts.check, !opts.failed);
|
|
3579
|
+
const result = await handleRecordCheckPatch(client, taskId, opts.node, opts.check, { passed: !opts.failed });
|
|
2657
3580
|
if (result.success) {
|
|
2658
3581
|
renderResult(opts.format, result, { successMessage: `${taskId} \u8282\u70B9 ${opts.node} check ${opts.check} \u2192 ${opts.failed ? "failed" : "passed"}` });
|
|
2659
3582
|
} else {
|
|
@@ -2973,7 +3896,7 @@ import { Command as Command3 } from "commander";
|
|
|
2973
3896
|
import { SKILL_MAIN_DOC, SkillScopeSchema } from "@siming-org/core";
|
|
2974
3897
|
|
|
2975
3898
|
// src/client/scaffold-templates.ts
|
|
2976
|
-
import { existsSync as
|
|
3899
|
+
import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync11 } from "fs";
|
|
2977
3900
|
import { dirname as dirname3, join as join6 } from "path";
|
|
2978
3901
|
import { fileURLToPath } from "url";
|
|
2979
3902
|
var SCAFFOLD_DIR = join6("templates", "project-skills");
|
|
@@ -2982,7 +3905,7 @@ function resolveCliPackageRoot() {
|
|
|
2982
3905
|
let dir = dirname3(fileURLToPath(import.meta.url));
|
|
2983
3906
|
for (let depth = 0; depth < 10; depth += 1) {
|
|
2984
3907
|
const pkgFile = join6(dir, "package.json");
|
|
2985
|
-
if (
|
|
3908
|
+
if (existsSync7(pkgFile)) {
|
|
2986
3909
|
try {
|
|
2987
3910
|
const pkg = JSON.parse(readFileSync11(pkgFile, "utf-8"));
|
|
2988
3911
|
if (pkg.name === "@siming-org/cli") return ok2(dir);
|
|
@@ -4295,6 +5218,7 @@ import { Command as Command6 } from "commander";
|
|
|
4295
5218
|
import { SkillScopeSchema as SkillScopeSchema2 } from "@siming-org/core";
|
|
4296
5219
|
|
|
4297
5220
|
// src/client/handlers/node.ts
|
|
5221
|
+
import { assertUniqueAiCheckIds } from "@siming-org/core";
|
|
4298
5222
|
async function handleListNodePresets(client, query = {}) {
|
|
4299
5223
|
const qs = new URLSearchParams();
|
|
4300
5224
|
if (query.projectId) qs.set("projectId", query.projectId);
|
|
@@ -4426,6 +5350,8 @@ ${failures.join("\n")}`,
|
|
|
4426
5350
|
prompt: preset.prompt,
|
|
4427
5351
|
skills: [...preset.skills],
|
|
4428
5352
|
agents: [...preset.agents ?? []],
|
|
5353
|
+
...preset.toolChecks !== void 0 ? { toolChecks: preset.toolChecks.map((c) => c.params ? { ...c, params: { ...c.params } } : { ...c }) } : {},
|
|
5354
|
+
...preset.aiChecks !== void 0 ? { aiChecks: preset.aiChecks.map((c) => ({ ...c })) } : {},
|
|
4429
5355
|
sourcePreset: {
|
|
4430
5356
|
code: preset.code,
|
|
4431
5357
|
version: preset.version,
|
|
@@ -4433,6 +5359,18 @@ ${failures.join("\n")}`,
|
|
|
4433
5359
|
agents: [...preset.agents ?? []]
|
|
4434
5360
|
}
|
|
4435
5361
|
}));
|
|
5362
|
+
try {
|
|
5363
|
+
assertUniqueAiCheckIds(nodes);
|
|
5364
|
+
} catch (err2) {
|
|
5365
|
+
return {
|
|
5366
|
+
success: false,
|
|
5367
|
+
error: {
|
|
5368
|
+
message: `\u7EC4\u5408 "${compositionName}" aiCheck id \u51B2\u7A81\uFF1A${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
5369
|
+
httpStatus: 0,
|
|
5370
|
+
hint: "\u4FEE\u6B63\u8282\u70B9\u9884\u8BBE\u7684 aiChecks id \u6216\u7EC4\u5408\u5185\u6539 nodeId \u540E\u91CD\u8BD5"
|
|
5371
|
+
}
|
|
5372
|
+
};
|
|
5373
|
+
}
|
|
4436
5374
|
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
4437
5375
|
const danglingEdges = composition.edges.filter((e) => !nodeIds.has(e.from) || !nodeIds.has(e.to)).map((e) => `${e.from}\u2192${e.to}`);
|
|
4438
5376
|
if (danglingEdges.length > 0) {
|
|
@@ -4553,7 +5491,7 @@ function createNodeCommand() {
|
|
|
4553
5491
|
};
|
|
4554
5492
|
renderResult("json", await handleCreateNodePreset(client, input));
|
|
4555
5493
|
});
|
|
4556
|
-
node.command("update <code>").description("Update a node preset (partial; content change requires version bump \u2014 422 NODE_PRESET_VERSION_REQUIRED)").option("--url <url>", "server base URL").option("--label <text>", "new label").option("--node-id <id>", "new default node id").option("--phase <phase>", "new phase").option("--track <track>", "new track").option("--prompt <text>", "new prompt body").option("--prompt-file <path>", "read prompt from file (overrides --prompt)").option("--skill <name>", "replace bound skills list (repeatable; omit = untouched)", collectArgs).option("--clear-skills", "clear bound skills list").option("--agent <name>", "replace callable agents list (repeatable; omit = untouched)", collectArgs).option("--clear-agents", "clear callable agents list").option("--description <text>", "new description").option("--new-version <v>", "new version (required when content fields change)").action(async (code, opts) => {
|
|
5494
|
+
node.command("update <code>").description("Update a node preset (partial; content change requires version bump \u2014 422 NODE_PRESET_VERSION_REQUIRED)").option("--url <url>", "server base URL").option("--label <text>", "new label").option("--node-id <id>", "new default node id").option("--phase <phase>", "new phase").option("--track <track>", "new track").option("--prompt <text>", "new prompt body").option("--prompt-file <path>", "read prompt from file (overrides --prompt)").option("--skill <name>", "replace bound skills list (repeatable; omit = untouched)", collectArgs).option("--clear-skills", "clear bound skills list").option("--agent <name>", "replace callable agents list (repeatable; omit = untouched)", collectArgs).option("--clear-agents", "clear callable agents list").option("--description <text>", "new description").option("--checks-file <path>", 'checks \u76F4\u66F4\u901A\u9053\uFF08T202609230001 \u8BBE\u8BA1 :300\uFF09\uFF1AJSON \u6587\u4EF6 {"toolChecks":[],"aiChecks":[]} \u6574\u4F53\u66FF\u6362 checks \u9762\uFF08\u7F3A\u952E=\u6E05\u7A7A\uFF09\uFF1B\u5185\u5BB9\u53D8\u66F4\u540C\u6279 --new-version').option("--new-version <v>", "new version (required when content fields change)").action(async (code, opts) => {
|
|
4557
5495
|
const client = createClient(opts.url);
|
|
4558
5496
|
let prompt;
|
|
4559
5497
|
if (opts.promptFile !== void 0) {
|
|
@@ -4569,6 +5507,30 @@ function createNodeCommand() {
|
|
|
4569
5507
|
} else if (opts.prompt !== void 0) {
|
|
4570
5508
|
prompt = opts.prompt;
|
|
4571
5509
|
}
|
|
5510
|
+
let checks;
|
|
5511
|
+
if (opts.checksFile !== void 0) {
|
|
5512
|
+
let parsed;
|
|
5513
|
+
try {
|
|
5514
|
+
parsed = JSON.parse(readFileSync13(opts.checksFile, "utf-8"));
|
|
5515
|
+
} catch (e) {
|
|
5516
|
+
renderResult("json", fail({
|
|
5517
|
+
message: `checks \u6587\u4EF6\u8BFB\u53D6/\u89E3\u6790\u5931\u8D25: ${opts.checksFile}\uFF08${e instanceof Error ? e.message : String(e)}\uFF09`,
|
|
5518
|
+
httpStatus: 0
|
|
5519
|
+
}));
|
|
5520
|
+
return;
|
|
5521
|
+
}
|
|
5522
|
+
const obj = parsed;
|
|
5523
|
+
const badTool = obj.toolChecks !== void 0 && (!Array.isArray(obj.toolChecks) || obj.toolChecks.some((t) => typeof t?.type !== "string"));
|
|
5524
|
+
const badAi = obj.aiChecks !== void 0 && (!Array.isArray(obj.aiChecks) || obj.aiChecks.some((a) => typeof a?.id !== "string" || typeof a?.item !== "string"));
|
|
5525
|
+
if (badTool || badAi) {
|
|
5526
|
+
renderResult("json", fail({
|
|
5527
|
+
message: '--checks-file \u987B\u4E3A {"toolChecks":[{"type":"..."}],"aiChecks":[{"id":"...","item":"..."}]}\uFF08\u7F3A\u952E=\u6E05\u7A7A\u8BE5\u6570\u7EC4\uFF09',
|
|
5528
|
+
httpStatus: 0
|
|
5529
|
+
}));
|
|
5530
|
+
return;
|
|
5531
|
+
}
|
|
5532
|
+
checks = { toolChecks: obj.toolChecks ?? [], aiChecks: obj.aiChecks ?? [] };
|
|
5533
|
+
}
|
|
4572
5534
|
if (opts.skill !== void 0 && opts.clearSkills) {
|
|
4573
5535
|
renderResult("json", fail({ message: "--skill \u4E0E --clear-skills \u4E92\u65A5", httpStatus: 0 }));
|
|
4574
5536
|
return;
|
|
@@ -4586,10 +5548,11 @@ function createNodeCommand() {
|
|
|
4586
5548
|
...opts.skill !== void 0 ? { skills: opts.skill } : opts.clearSkills ? { skills: [] } : {},
|
|
4587
5549
|
...opts.agent !== void 0 ? { agents: opts.agent } : opts.clearAgents ? { agents: [] } : {},
|
|
4588
5550
|
...opts.description !== void 0 ? { description: opts.description } : {},
|
|
5551
|
+
...checks !== void 0 ? { toolChecks: checks.toolChecks, aiChecks: checks.aiChecks } : {},
|
|
4589
5552
|
...opts.newVersion !== void 0 ? { version: opts.newVersion } : {}
|
|
4590
5553
|
};
|
|
4591
5554
|
if (Object.keys(patch).length === 0) {
|
|
4592
|
-
renderResult("json", fail({ message: "\u672A\u643A\u5E26\u4EFB\u4F55\u5F85\u66F4\u65B0\u5B57\u6BB5", httpStatus: 0, hint: "\u53EF\u66F4\u65B0\uFF1Alabel/node-id/phase/track/prompt/skill/clear-skills/agent/clear-agents/description/new-version" }));
|
|
5555
|
+
renderResult("json", fail({ message: "\u672A\u643A\u5E26\u4EFB\u4F55\u5F85\u66F4\u65B0\u5B57\u6BB5", httpStatus: 0, hint: "\u53EF\u66F4\u65B0\uFF1Alabel/node-id/phase/track/prompt/skill/clear-skills/agent/clear-agents/description/checks-file/new-version" }));
|
|
4593
5556
|
return;
|
|
4594
5557
|
}
|
|
4595
5558
|
renderResult("json", await handleUpdateNodePreset(client, code, patch));
|
|
@@ -4653,11 +5616,11 @@ function nodeFields2(n) {
|
|
|
4653
5616
|
}
|
|
4654
5617
|
|
|
4655
5618
|
// src/commands/flow/command.ts
|
|
4656
|
-
import { execFile } from "child_process";
|
|
5619
|
+
import { execFile as execFile2 } from "child_process";
|
|
4657
5620
|
import { mkdtempSync, rmSync as rmSync2 } from "fs";
|
|
4658
5621
|
import { tmpdir } from "os";
|
|
4659
5622
|
import { join as join7 } from "path";
|
|
4660
|
-
import { promisify } from "util";
|
|
5623
|
+
import { promisify as promisify2 } from "util";
|
|
4661
5624
|
import { createInterface } from "readline/promises";
|
|
4662
5625
|
import { Command as Command7 } from "commander";
|
|
4663
5626
|
import {
|
|
@@ -4674,7 +5637,7 @@ import {
|
|
|
4674
5637
|
SKILL_MAIN_DOC as SKILL_MAIN_DOC2,
|
|
4675
5638
|
createReferencesSchema as createReferencesSchema2
|
|
4676
5639
|
} from "@siming-org/core";
|
|
4677
|
-
var FLOW_MANIFEST_FORMAT_VERSION = "1.
|
|
5640
|
+
var FLOW_MANIFEST_FORMAT_VERSION = "1.2";
|
|
4678
5641
|
var uniqueStrings = (values, ctx, label) => {
|
|
4679
5642
|
const seen = /* @__PURE__ */ new Set();
|
|
4680
5643
|
for (let index = 0; index < values.length; index += 1) {
|
|
@@ -4693,6 +5656,14 @@ var manifestCapabilityRefSchema = z6.object({
|
|
|
4693
5656
|
var capabilityRefsSchema = z6.array(manifestCapabilityRefSchema).superRefine((refs, ctx) => {
|
|
4694
5657
|
uniqueStrings(refs.map((ref) => ref.name), ctx, "\u80FD\u529B\u540D\u79F0");
|
|
4695
5658
|
});
|
|
5659
|
+
var manifestToolCheckSchema = z6.object({
|
|
5660
|
+
type: z6.string().min(1).max(50).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "kebab-case only"),
|
|
5661
|
+
params: z6.record(z6.string(), z6.union([z6.string(), z6.number(), z6.boolean()])).optional()
|
|
5662
|
+
});
|
|
5663
|
+
var manifestAiCheckSchema = z6.object({
|
|
5664
|
+
id: z6.string().min(2).max(30).regex(/^[a-z0-9-]+$/),
|
|
5665
|
+
item: z6.string().min(1).max(200)
|
|
5666
|
+
});
|
|
4696
5667
|
var nodePresetPayloadBaseSchema = z6.object({
|
|
4697
5668
|
code: z6.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "kebab-case only"),
|
|
4698
5669
|
label: z6.string().min(1),
|
|
@@ -4702,7 +5673,9 @@ var nodePresetPayloadBaseSchema = z6.object({
|
|
|
4702
5673
|
prompt: z6.string().min(1),
|
|
4703
5674
|
skills: capabilityRefsSchema,
|
|
4704
5675
|
description: z6.string(),
|
|
4705
|
-
version: z6.string().min(1)
|
|
5676
|
+
version: z6.string().min(1),
|
|
5677
|
+
toolChecks: z6.array(manifestToolCheckSchema).max(20).optional(),
|
|
5678
|
+
aiChecks: z6.array(manifestAiCheckSchema).max(20).optional()
|
|
4706
5679
|
}).strict();
|
|
4707
5680
|
var nodePresetPayloadSchema = nodePresetPayloadBaseSchema.extend({ agents: capabilityRefsSchema }).strict();
|
|
4708
5681
|
var referenceDocsSchema = (mainDoc) => createReferencesSchema2(mainDoc);
|
|
@@ -4976,7 +5949,7 @@ function compareSemverStrings(a, b) {
|
|
|
4976
5949
|
}
|
|
4977
5950
|
|
|
4978
5951
|
// src/commands/flow/command.ts
|
|
4979
|
-
var
|
|
5952
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
4980
5953
|
function createFlowCommand() {
|
|
4981
5954
|
const flow = new Command7("flow").description("Node library management \u2014 install npm-distributed node packages & compose templates");
|
|
4982
5955
|
withCommonOpts(flow.command("list"), "table").description("List installed node libraries (registry entries written by `flow install`)").action(async (opts) => {
|
|
@@ -5102,7 +6075,7 @@ async function fetchManifest(pkgSpec, registry) {
|
|
|
5102
6075
|
}
|
|
5103
6076
|
};
|
|
5104
6077
|
try {
|
|
5105
|
-
const { stdout } = await
|
|
6078
|
+
const { stdout } = await execFileAsync2(
|
|
5106
6079
|
"npm",
|
|
5107
6080
|
[
|
|
5108
6081
|
"pack",
|
|
@@ -5123,7 +6096,7 @@ async function fetchManifest(pkgSpec, registry) {
|
|
|
5123
6096
|
hint: "\u786E\u8BA4 npm \u7248\u672C\u652F\u6301 --json \u8F93\u51FA"
|
|
5124
6097
|
});
|
|
5125
6098
|
}
|
|
5126
|
-
const { stdout: manifestText } = await
|
|
6099
|
+
const { stdout: manifestText } = await execFileAsync2(
|
|
5127
6100
|
"tar",
|
|
5128
6101
|
["-xOf", join7(dir, filename), "package/siming-nodes.json"],
|
|
5129
6102
|
{ maxBuffer: MANIFEST_LIMITS.maxChars * 2 + 64 * 1024 }
|
|
@@ -5533,6 +6506,8 @@ function diffNode(payload, entity, packageName) {
|
|
|
5533
6506
|
if (entity.version !== payload.version) diffs.push("version");
|
|
5534
6507
|
if ((entity.description ?? "") !== payload.description) diffs.push("description");
|
|
5535
6508
|
if (entity.source?.package !== packageName) diffs.push("source");
|
|
6509
|
+
if (JSON.stringify(entity.toolChecks ?? []) !== JSON.stringify(payload.toolChecks ?? [])) diffs.push("toolChecks");
|
|
6510
|
+
if (JSON.stringify(entity.aiChecks ?? []) !== JSON.stringify(payload.aiChecks ?? [])) diffs.push("aiChecks");
|
|
5536
6511
|
return diffs;
|
|
5537
6512
|
}
|
|
5538
6513
|
function sameOrdered(left, right) {
|
|
@@ -5584,6 +6559,9 @@ function snapshotNode(entity) {
|
|
|
5584
6559
|
prompt: entity.prompt,
|
|
5585
6560
|
skills: [...entity.skills].sort(),
|
|
5586
6561
|
agents: [...entity.agents ?? []].sort(),
|
|
6562
|
+
// T202609230001:门禁声明入快照(plan→apply 窗口期门禁被改应触发 FLOW_INSTALL_STALE 而非静默覆盖)
|
|
6563
|
+
toolChecks: entity.toolChecks ?? [],
|
|
6564
|
+
aiChecks: entity.aiChecks ?? [],
|
|
5587
6565
|
scope: entity.scope,
|
|
5588
6566
|
projectId: entity.projectId ?? null,
|
|
5589
6567
|
description: entity.description ?? null,
|
|
@@ -5813,6 +6791,9 @@ async function writeAsset(client, manifest, plan, item) {
|
|
|
5813
6791
|
description: payload.description,
|
|
5814
6792
|
version: payload.version,
|
|
5815
6793
|
source: { package: manifest.name },
|
|
6794
|
+
// T202609230001:门禁声明随安装写入(缺此两行 = 安装成功但门禁静默丢失)
|
|
6795
|
+
...payload.toolChecks !== void 0 ? { toolChecks: payload.toolChecks } : {},
|
|
6796
|
+
...payload.aiChecks !== void 0 ? { aiChecks: payload.aiChecks } : {},
|
|
5816
6797
|
...item.targetProjectId !== void 0 ? { projectId: item.targetProjectId } : {}
|
|
5817
6798
|
};
|
|
5818
6799
|
return handleCreateNodePreset(client, input);
|
|
@@ -5827,7 +6808,12 @@ async function writeAsset(client, manifest, plan, item) {
|
|
|
5827
6808
|
agents: payload.agents.map((ref) => ref.name),
|
|
5828
6809
|
description: payload.description,
|
|
5829
6810
|
version: payload.version,
|
|
5830
|
-
source: { package: manifest.name }
|
|
6811
|
+
source: { package: manifest.name },
|
|
6812
|
+
// T202609230001:门禁声明随升级写入。显式归一空数组——「包未声明=清除」必须以 [] 落到线上
|
|
6813
|
+
// (裸 undefined 经 JSON.stringify 丢键,server 保留旧值 → 写后回读不一致 → FLOW_INSTALL_STALE;
|
|
6814
|
+
// 与 diffNode/模板升级的「缺省=空」归一语义一致)
|
|
6815
|
+
toolChecks: payload.toolChecks ?? [],
|
|
6816
|
+
aiChecks: payload.aiChecks ?? []
|
|
5831
6817
|
};
|
|
5832
6818
|
return handleUpdateNodePreset(client, payload.code, patch);
|
|
5833
6819
|
}
|
|
@@ -5983,11 +6969,57 @@ function createProjectCommand() {
|
|
|
5983
6969
|
const result = await handleGetProject(client, id);
|
|
5984
6970
|
renderResult(opts.format, result, { fields: result.success ? projectFields(result.data) : [] });
|
|
5985
6971
|
});
|
|
5986
|
-
project.command("update <id>").description("Update a project (name/description)").option("--url <url>", "server base URL").option("--name <name>", "new name").option("--description <text>", "new description").action(async (id, opts) => {
|
|
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) => {
|
|
5987
6973
|
const client = createClient(opts.url);
|
|
6974
|
+
const parseBool = (v, flag) => {
|
|
6975
|
+
if (v === void 0) return void 0;
|
|
6976
|
+
if (v === "true") return true;
|
|
6977
|
+
if (v === "false") return false;
|
|
6978
|
+
renderResult("json", fail({
|
|
6979
|
+
message: `${flag} \u53EA\u63A5\u53D7 true/false\uFF0C\u6536\u5230\uFF1A${v}`,
|
|
6980
|
+
hint: "\u5E03\u5C14\u65D7\u6807\u663E\u5F0F\u4F20\u503C\uFF08--vcs-enabled true\uFF09\uFF0C\u4E0D\u7528\u88F8\u65D7\u6807\u5F62\u6001",
|
|
6981
|
+
httpStatus: 0
|
|
6982
|
+
}));
|
|
6983
|
+
return "invalid";
|
|
6984
|
+
};
|
|
6985
|
+
const vcsFields = {};
|
|
6986
|
+
let vcsProvided = false;
|
|
6987
|
+
const enabled = parseBool(opts.vcsEnabled, "--vcs-enabled");
|
|
6988
|
+
const worktreeMode = parseBool(opts.vcsWorktreeMode, "--vcs-worktree-mode");
|
|
6989
|
+
const archiveToBase = parseBool(opts.vcsArchiveToBase, "--vcs-archive-to-base");
|
|
6990
|
+
if (enabled === "invalid" || worktreeMode === "invalid" || archiveToBase === "invalid") return;
|
|
6991
|
+
if (enabled !== void 0) {
|
|
6992
|
+
vcsFields.enabled = enabled;
|
|
6993
|
+
vcsProvided = true;
|
|
6994
|
+
}
|
|
6995
|
+
if (opts.vcsRepoPath !== void 0) {
|
|
6996
|
+
vcsFields.repoPath = opts.vcsRepoPath;
|
|
6997
|
+
vcsProvided = true;
|
|
6998
|
+
}
|
|
6999
|
+
if (opts.vcsBaseBranch !== void 0) {
|
|
7000
|
+
vcsFields.baseBranch = opts.vcsBaseBranch;
|
|
7001
|
+
vcsProvided = true;
|
|
7002
|
+
}
|
|
7003
|
+
if (opts.vcsBranchPattern !== void 0) {
|
|
7004
|
+
vcsFields.branchPattern = opts.vcsBranchPattern;
|
|
7005
|
+
vcsProvided = true;
|
|
7006
|
+
}
|
|
7007
|
+
if (worktreeMode !== void 0) {
|
|
7008
|
+
vcsFields.worktreeMode = worktreeMode;
|
|
7009
|
+
vcsProvided = true;
|
|
7010
|
+
}
|
|
7011
|
+
if (archiveToBase !== void 0) {
|
|
7012
|
+
vcsFields.archiveToBase = archiveToBase;
|
|
7013
|
+
vcsProvided = true;
|
|
7014
|
+
}
|
|
7015
|
+
if (opts.vcsDocPaths !== void 0) {
|
|
7016
|
+
vcsFields.docPaths = opts.vcsDocPaths.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
7017
|
+
vcsProvided = true;
|
|
7018
|
+
}
|
|
5988
7019
|
const patch = {
|
|
5989
7020
|
...opts.name !== void 0 ? { name: opts.name } : {},
|
|
5990
|
-
...opts.description !== void 0 ? { description: opts.description } : {}
|
|
7021
|
+
...opts.description !== void 0 ? { description: opts.description } : {},
|
|
7022
|
+
...vcsProvided ? { vcs: vcsFields } : {}
|
|
5991
7023
|
};
|
|
5992
7024
|
const result = await handleUpdateProject(client, id, patch);
|
|
5993
7025
|
renderResult("json", result);
|
|
@@ -6005,7 +7037,8 @@ function projectFields(p) {
|
|
|
6005
7037
|
["key", p.key],
|
|
6006
7038
|
["name", p.name],
|
|
6007
7039
|
["status", p.status],
|
|
6008
|
-
["description", p.description]
|
|
7040
|
+
["description", p.description],
|
|
7041
|
+
["vcs", p.vcs]
|
|
6009
7042
|
];
|
|
6010
7043
|
}
|
|
6011
7044
|
|
|
@@ -6927,13 +7960,13 @@ function renderReport(report, format) {
|
|
|
6927
7960
|
|
|
6928
7961
|
// src/commands/install/command.ts
|
|
6929
7962
|
import { Command as Command12 } from "commander";
|
|
6930
|
-
import { existsSync as
|
|
7963
|
+
import { existsSync as existsSync9 } from "fs";
|
|
6931
7964
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6932
7965
|
import { dirname as dirname5, join as join9 } from "path";
|
|
6933
7966
|
import { AGENT_MAIN_DOC as AGENT_MAIN_DOC5, SKILL_MAIN_DOC as SKILL_MAIN_DOC5, VERSION as VERSION3 } from "@siming-org/core";
|
|
6934
7967
|
|
|
6935
7968
|
// src/commands/install/writer.ts
|
|
6936
|
-
import { existsSync as
|
|
7969
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
6937
7970
|
import { dirname as dirname4, join as join8 } from "path";
|
|
6938
7971
|
import { homedir as homedir4 } from "os";
|
|
6939
7972
|
import { parse as parseYaml3, stringify as stringifyYaml } from "yaml";
|
|
@@ -7005,7 +8038,7 @@ function permissionsToMatrix(permissions) {
|
|
|
7005
8038
|
}
|
|
7006
8039
|
return matrix;
|
|
7007
8040
|
}
|
|
7008
|
-
var AGENT_INFO_BOUNDARY = "\u4EFB\u52A1\u4F9D\u636E\u9650\u4E8E\u9879\u76EE\u4EE3\u7801\u3001\u8BBE\u8BA1\u6587\u6863/\u6267\u884C\u62A5\u544A\u53CA\u4EFB\u52A1\u4E0A\u4E0B\u6587\uFF08\u542B\u4EFB\u52A1\u4FE1\u606F\u3001\u63D0\u793A\u8BCD\u3001\u59D4\u6D3E\u6750\u6599\uFF09\uFF1B\
|
|
8041
|
+
var AGENT_INFO_BOUNDARY = "\u4EFB\u52A1\u4F9D\u636E\u9650\u4E8E\u9879\u76EE\u4EE3\u7801\u3001\u8BBE\u8BA1\u6587\u6863/\u6267\u884C\u62A5\u544A\u53CA\u4EFB\u52A1\u4E0A\u4E0B\u6587\uFF08\u542B\u4EFB\u52A1\u4FE1\u606F\u3001\u63D0\u793A\u8BCD\u3001\u59D4\u6D3E\u6750\u6599\uFF09\uFF1B\u4E0D\u4FEE\u6539\u4EE3\u7801\u6216\u6587\u6863\uFF0C\u4E0D\u6267\u884C\u6D4B\u8BD5\u3001\u6784\u5EFA\u6216\u53D1\u5E03\u547D\u4EE4\uFF1B\u81EA\u8EAB\u89D2\u8272\u5B9A\u4E49\u6388\u6743\u7684\u91C7\u8BC1\u64CD\u4F5C\uFF08\u5982\u6D4F\u89C8\u5668\u4EA4\u4E92\u3001\u622A\u56FE\u53D6\u8BC1\u3001\u8BFB\u53D6\u8FD0\u884C\u8BC1\u636E\uFF09\u4E0D\u53D7\u6B64\u9650\uFF0C\u5176\u4F59\u8FC7\u7A0B\u4EA7\u7269\u8BFB\u53D6\u4E0E\u547D\u4EE4\u6267\u884C\u4ECD\u7981\u6B62\u3002";
|
|
7009
8042
|
function agentToMarkdown(agent, realModel) {
|
|
7010
8043
|
const fm = {
|
|
7011
8044
|
name: agent.name,
|
|
@@ -7034,7 +8067,7 @@ function agentTargetPath(dir, agent) {
|
|
|
7034
8067
|
return join8(dir, "agents", agent.name, "agent.md");
|
|
7035
8068
|
}
|
|
7036
8069
|
function readSimingVersion(path) {
|
|
7037
|
-
if (!
|
|
8070
|
+
if (!existsSync8(path)) return null;
|
|
7038
8071
|
let text;
|
|
7039
8072
|
try {
|
|
7040
8073
|
text = readFileSync15(path, "utf-8");
|
|
@@ -7059,7 +8092,7 @@ function isPlainObject(v) {
|
|
|
7059
8092
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
7060
8093
|
}
|
|
7061
8094
|
function readMcpConfig(path) {
|
|
7062
|
-
if (!
|
|
8095
|
+
if (!existsSync8(path)) return { state: "absent" };
|
|
7063
8096
|
let text;
|
|
7064
8097
|
try {
|
|
7065
8098
|
text = readFileSync15(path, "utf-8");
|
|
@@ -7299,7 +8332,7 @@ function compareSkill(s, dir) {
|
|
|
7299
8332
|
if (!isSafeAssetName(s.name)) {
|
|
7300
8333
|
return { ...base, action: "conflict", reason: `unsafe asset name "${s.name}"` };
|
|
7301
8334
|
}
|
|
7302
|
-
if (!
|
|
8335
|
+
if (!existsSync9(path)) return { ...base, action: "install", reason: "not installed locally" };
|
|
7303
8336
|
const marked = readSimingVersion(path);
|
|
7304
8337
|
if (marked === null) {
|
|
7305
8338
|
return { ...base, localVersion: "unknown", action: "conflict", reason: "no siming-version marker (third-party asset; use --force to overwrite)" };
|
|
@@ -7317,7 +8350,7 @@ function compareAgent(a, dir) {
|
|
|
7317
8350
|
if (!isSafeAssetName(a.name)) {
|
|
7318
8351
|
return { ...base, action: "conflict", reason: `unsafe asset name "${a.name}"` };
|
|
7319
8352
|
}
|
|
7320
|
-
if (!
|
|
8353
|
+
if (!existsSync9(path)) return { ...base, action: "install", reason: "not installed locally" };
|
|
7321
8354
|
const marked = readSimingVersion(path);
|
|
7322
8355
|
if (marked === null) {
|
|
7323
8356
|
return { ...base, localVersion: "unknown", action: "conflict", reason: "no siming-version marker (third-party asset; use --force to overwrite)" };
|
|
@@ -7410,7 +8443,7 @@ async function applyInstall(client, scope, projectId, items, opts) {
|
|
|
7410
8443
|
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" });
|
|
7411
8444
|
continue;
|
|
7412
8445
|
}
|
|
7413
|
-
const targetExists =
|
|
8446
|
+
const targetExists = existsSync9(targetPath);
|
|
7414
8447
|
writeAssetDirectory(dirname5(targetPath), [
|
|
7415
8448
|
{ relPath: SKILL_MAIN_DOC5, content: skillToMarkdown(r.data) },
|
|
7416
8449
|
...(r.data.references ?? []).map((ref) => ({ relPath: ref.path, content: ref.content }))
|
|
@@ -7446,7 +8479,7 @@ async function applyInstall(client, scope, projectId, items, opts) {
|
|
|
7446
8479
|
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" });
|
|
7447
8480
|
continue;
|
|
7448
8481
|
}
|
|
7449
|
-
const targetExists =
|
|
8482
|
+
const targetExists = existsSync9(targetPath);
|
|
7450
8483
|
writeAssetDirectory(dirname5(targetPath), [
|
|
7451
8484
|
{ relPath: AGENT_MAIN_DOC5, content: agentToMarkdown(r.data, realModel) },
|
|
7452
8485
|
...(r.data.references ?? []).map((ref) => ({ relPath: ref.path, content: ref.content }))
|
|
@@ -7563,7 +8596,7 @@ var PHASE2_NEXT_STEPS = `\u8D44\u4EA7\u5DF2\u5C31\u7EEA\uFF08\u5168\u5C40\u8D44\
|
|
|
7563
8596
|
\uFF08siming task create --template <\u6A21\u677Fcode> --track <\u8F68\u9053> --project <\u9879\u76EEkey> --title "<\u6807\u9898>"\uFF09
|
|
7564
8597
|
\u5E76\u8BFB\u53D6\u9996\u8282\u70B9\u4E0A\u4E0B\u6587\uFF1Asiming task context <taskId>`;
|
|
7565
8598
|
var MCP_TOOLING_TEXT = `\u63A5\u53E3\u5206\u5DE5\u73B0\u72B6\uFF08\u6743\u5A01\uFF09\uFF1A
|
|
7566
|
-
- MCP \u4EC5\u4E09\u4E2A\u5DE5\u5177\uFF1Asiming_task\uFF08\u4EFB\u52A1\u751F\u547D\u5468\u671F\u8BFB\u5199\
|
|
8599
|
+
- MCP \u4EC5\u4E09\u4E2A\u5DE5\u5177\uFF1Asiming_task\uFF08\u4EFB\u52A1\u751F\u547D\u5468\u671F\u8BFB\u5199\uFF0C24 \u4E2A action\uFF09\u3001siming_template\uFF08\u53EA\u8BFB\u6A21\u677F\u67E5\u8BE2\uFF0Clist/show\uFF09\u3001siming_auth\uFF08\u8EAB\u4EFD\u67E5\u8BE2\uFF0Cwhoami\uFF09\uFF1B
|
|
7567
8600
|
- \u8D44\u4EA7\u3001\u9879\u76EE\u3001\u6A21\u677F\u5199\u5165/\u5BFC\u5165/\u5347\u7EA7\u3001\u521D\u59CB\u5316\u3001\u5B89\u88C5\u3001\u8BCA\u65AD\u7B49\u4E00\u5207\u8D44\u4EA7\u7BA1\u7406\u4E0E\u8FD0\u7EF4\u64CD\u4F5C\u4E00\u5F8B\u8D70 CLI \u547D\u4EE4\u3002
|
|
7568
8601
|
|
|
7569
8602
|
\u5BBF\u4E3B\u517C\u5BB9\u6027\u81EA\u68C0\u4E0E\u9002\u914D\uFF08\u63A5\u5165\u540E\u5FC5\u505A\uFF09\uFF1A
|
|
@@ -8005,7 +9038,7 @@ async function handleUpdateEnums(client, category, body) {
|
|
|
8005
9038
|
// src/commands/settings/command.ts
|
|
8006
9039
|
function createSettingsCommand() {
|
|
8007
9040
|
const settings = new Command16("settings").description("Settings (enum registries)");
|
|
8008
|
-
withCommonOpts(settings.command("enums [category]"), "fancy").description("Show enum registry (all
|
|
9041
|
+
withCommonOpts(settings.command("enums [category]"), "fancy").description("Show enum registry (all categories, or one category)").action(async (category, opts) => {
|
|
8009
9042
|
const client = createClient(opts.url);
|
|
8010
9043
|
const catR = category ? EnumRegistryCategorySchema.safeParse(category) : null;
|
|
8011
9044
|
if (category && !catR?.success) {
|
|
@@ -8059,7 +9092,7 @@ import { VERSION as VERSION4, SIMING_CONFIG_KEYS as SIMING_CONFIG_KEYS2 } from "
|
|
|
8059
9092
|
|
|
8060
9093
|
// src/commands/server/daemon.ts
|
|
8061
9094
|
import { execFileSync } from "child_process";
|
|
8062
|
-
import { closeSync, existsSync as
|
|
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";
|
|
8063
9096
|
import { join as join10, resolve as resolve2 } from "path";
|
|
8064
9097
|
import { spawn } from "child_process";
|
|
8065
9098
|
|
|
@@ -8270,7 +9303,7 @@ function readLogTail(lines = 30) {
|
|
|
8270
9303
|
}
|
|
8271
9304
|
function spawnDaemon(configEnv) {
|
|
8272
9305
|
const entry = resolve2(process.argv[1] ?? "");
|
|
8273
|
-
if (entry.length === 0 || !
|
|
9306
|
+
if (entry.length === 0 || !existsSync10(entry)) {
|
|
8274
9307
|
throw new Error(`\u65E0\u6CD5\u5B9A\u4F4D siming \u5165\u53E3\u811A\u672C\uFF08process.argv[1]=${process.argv[1] ?? "(\u7A7A)"}\uFF09`);
|
|
8275
9308
|
}
|
|
8276
9309
|
if (entry.endsWith(".ts")) {
|