@ghyper9023/pi-dev-workflow 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.version/RELEASE-v0.6.0.md +106 -0
- package/README.md +32 -30
- package/agents/git-agent.md +24 -16
- package/agents/grill/dev-doc-grill-agent.md +5 -4
- package/agents/grill/dev-fix-grill-agent.md +5 -3
- package/agents/grill/dev-grill-agent.md +6 -6
- package/agents/grill/dev-perf-grill-agent.md +5 -3
- package/agents/grill/dev-prd-agent.md +1 -1
- package/agents/grill/dev-refactor-grill-agent.md +5 -2
- package/agents/grill/dev-test-grill-agent.md +5 -2
- package/agents/review-agent.md +30 -22
- package/agents/workflow/docWriter-agent.md +35 -22
- package/agents/workflow/planner-agent.md +97 -59
- package/agents/workflow/reviewer-agent.md +100 -33
- package/agents/workflow/trimmer-agent.md +59 -26
- package/agents/workflow/worker-agent.md +51 -25
- package/extensions/dev-prompts.ts +29 -29
- package/extensions/grill-me-agent.ts +16 -16
- package/extensions/workflow-engine.ts +149 -271
- package/package.json +1 -1
- package/tests/test-workflow-engine-bugs.mjs +252 -10
|
@@ -495,17 +495,20 @@ assert(singleStepFuncStart !== -1, "找到 executeSingleStep 函数");
|
|
|
495
495
|
const singleStepEndSearch = source.indexOf("async function executeLoopGroup", singleStepFuncStart);
|
|
496
496
|
const fullSingleStep = source.slice(singleStepFuncStart, singleStepEndSearch);
|
|
497
497
|
|
|
498
|
-
|
|
499
|
-
|
|
498
|
+
// Verify 工作总结 entries still exist (preserved - from extractFinalOutput)
|
|
499
|
+
const hasWorkSummaryKey = fullSingleStep.includes('工作总结');
|
|
500
|
+
assertTrue(hasWorkSummaryKey, "executeSingleStep 中保留 '工作总结' 条目");
|
|
500
501
|
|
|
501
|
-
const
|
|
502
|
-
assertTrue(
|
|
502
|
+
const hasWorkSummaryUpdate = fullSingleStep.includes('updateChainContext');
|
|
503
|
+
assertTrue(hasWorkSummaryUpdate, "executeSingleStep 中仍有 updateChainContext 调用(工作总结)");
|
|
503
504
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
505
|
+
// Verify the removed summary keys are no longer present in updateChainContext calls
|
|
506
|
+
// (comments may still reference them for historical context — that's fine)
|
|
507
|
+
const chainCtxCalls = [...fullSingleStep.matchAll(/updateChainContext\(`([^`]+)`/g)].map(m => m[1]);
|
|
508
|
+
const plannerInCode = chainCtxCalls.some(k => k.includes('计划制定摘要'));
|
|
509
|
+
assertFalse(plannerInCode, "updateChainContext 中已不使用 '计划制定摘要' key");
|
|
510
|
+
const docWriterInCode = chainCtxCalls.some(k => k.includes('文档更新摘要'));
|
|
511
|
+
assertFalse(docWriterInCode, "updateChainContext 中已不使用 '文档更新摘要' key");
|
|
509
512
|
|
|
510
513
|
|
|
511
514
|
console.log("\n═══ Bug H 测试 — Agent 前置元数据解析 ═══\n");
|
|
@@ -648,11 +651,250 @@ const reviewHasWorkflowId = reviewTaskParams.includes("workflowId");
|
|
|
648
651
|
assertTrue(reviewHasWorkflowId, "buildReviewTask 接收 workflowId 参数");
|
|
649
652
|
|
|
650
653
|
|
|
654
|
+
console.log("\n");
|
|
655
|
+
|
|
656
|
+
console.log("═══ Bug J 测试 — _workflowFileChanges 仅来自 git diff ═══\n");
|
|
657
|
+
|
|
658
|
+
// ── Test J1: _workflowFileChanges.push 出现在 updateToolsFromGit 和 git diff 轮询 ──
|
|
659
|
+
console.log("📋 测试 J1: _workflowFileChanges.push 出现在 updateToolsFromGit 和 git diff 轮询\n");
|
|
660
|
+
|
|
661
|
+
// Count all _workflowFileChanges.push occurrences
|
|
662
|
+
// There should be 2: one in git diff polling (runAgentWithProgress) and one in updateToolsFromGit
|
|
663
|
+
const pushMatches = [...source.matchAll(/_workflowFileChanges\.push\(/g)];
|
|
664
|
+
assertEq(pushMatches.length, 2, "_workflowFileChanges.push 出现 2 次(轮询 + updateToolsFromGit)");
|
|
665
|
+
|
|
666
|
+
// Verify the push is inside updateToolsFromGit
|
|
667
|
+
const updateToolsStart = source.indexOf("function updateToolsFromGit");
|
|
668
|
+
const nextFuncStart = source.indexOf("function saveCheckpoint", updateToolsStart);
|
|
669
|
+
const updateToolsBody = source.slice(updateToolsStart, nextFuncStart);
|
|
670
|
+
assertTrue(updateToolsBody.includes("_workflowFileChanges.push"), "updateToolsFromGit 函数体内包含 _workflowFileChanges.push");
|
|
671
|
+
|
|
672
|
+
// Verify the push is also inside the git diff polling (setInterval) in runAgentWithProgress
|
|
673
|
+
const runAgentStart = source.indexOf("async function runAgentWithProgress");
|
|
674
|
+
const runAgentEnd = source.indexOf("async function executeSingleStep", runAgentStart);
|
|
675
|
+
const runAgentBody = source.slice(runAgentStart, runAgentEnd);
|
|
676
|
+
assertTrue(runAgentBody.includes("_workflowFileChanges.push"), "git diff 轮询(setInterval)中包含 _workflowFileChanges.push");
|
|
677
|
+
|
|
678
|
+
// ── Test J2: addWidgetSubStepTool 不再污染 _workflowFileChanges ──
|
|
679
|
+
console.log("\n📋 测试 J2: addWidgetSubStepTool 不再污染 _workflowFileChanges\n");
|
|
680
|
+
|
|
681
|
+
const addWidgetFunc = source.slice(
|
|
682
|
+
source.indexOf("function addWidgetSubStepTool"),
|
|
683
|
+
source.indexOf("function addWidgetSubStepOutput")
|
|
684
|
+
);
|
|
685
|
+
const hasNoFileChangesRef = !addWidgetFunc.includes("_workflowFileChanges");
|
|
686
|
+
assertTrue(hasNoFileChangesRef, "addWidgetSubStepTool 函数体中不再引用 _workflowFileChanges");
|
|
687
|
+
|
|
688
|
+
// ── Test J3: executeSingleStep 中不再有基于 _workflowFileChanges 的统计 ──
|
|
689
|
+
console.log("\n📋 测试 J3: executeSingleStep 中不再有 '执行摘要' 类 chain context\n");
|
|
690
|
+
|
|
691
|
+
const hasNoExecutorSummary = !fullSingleStep.includes('执行摘要');
|
|
692
|
+
assertTrue(hasNoExecutorSummary, "executeSingleStep 中不再有 '执行摘要' chain context");
|
|
693
|
+
|
|
694
|
+
// ── Test J4: executeLoopGroup 中不再有 "代码实施/精简摘要" chain context ──
|
|
695
|
+
console.log("\n📋 测试 J4: executeLoopGroup 中不再有 '代码实施/精简摘要' chain context\n");
|
|
696
|
+
|
|
697
|
+
const loopGroupStart = source.indexOf("async function executeLoopGroup");
|
|
698
|
+
assert(loopGroupStart !== -1, "找到 executeLoopGroup 函数");
|
|
699
|
+
const loopGroupBody = source.slice(loopGroupStart);
|
|
700
|
+
const hasNoCodeImplSummary = !loopGroupBody.includes('"代码实施摘要"') && !loopGroupBody.includes('"代码精简摘要"');
|
|
701
|
+
assertTrue(hasNoCodeImplSummary, "executeLoopGroup 中不再有 '代码实施摘要' 或 '代码精简摘要' chain context");
|
|
702
|
+
|
|
703
|
+
// Verify the removed review feedback keys are no longer present in updateChainContext calls
|
|
704
|
+
// (comments may still reference them for historical context — that's fine)
|
|
705
|
+
const chainCtxCallsLoop = [...loopGroupBody.matchAll(/updateChainContext\(`([^`]+)`/g)].map(m => m[1]);
|
|
706
|
+
const reviewInCode = chainCtxCallsLoop.some(k => k.includes('代码审查反馈') || k.includes('精简审查反馈'));
|
|
707
|
+
assertFalse(reviewInCode, "updateChainContext 中已不使用 '代码审查反馈' 或 '精简审查反馈' key");
|
|
708
|
+
|
|
709
|
+
// ── Test J5: 工作总结条目在所有 agent 函数中仍保留 ──
|
|
710
|
+
console.log("\n📋 测试 J5: '工作总结' 条目在所有 agent 函数中仍保留\n");
|
|
711
|
+
|
|
712
|
+
const hasWorkSummaryInSingleStep = fullSingleStep.includes('工作总结');
|
|
713
|
+
assertTrue(hasWorkSummaryInSingleStep, "executeSingleStep 保留 '工作总结' 条目");
|
|
714
|
+
|
|
715
|
+
const hasWorkSummaryInLoop = loopGroupBody.includes('工作总结');
|
|
716
|
+
assertTrue(hasWorkSummaryInLoop, "executeLoopGroup 保留 '工作总结' 条目");
|
|
717
|
+
|
|
718
|
+
|
|
651
719
|
console.log("\n=== 增强功能测试汇总 ===\n");
|
|
652
720
|
console.log("📋 附加测试覆盖:");
|
|
653
|
-
console.log(" - G: 链上下文传递 (executeSingleStep)");
|
|
721
|
+
console.log(" - G: 链上下文传递 (executeSingleStep) — 已更新验证工作总结");
|
|
654
722
|
console.log(" - H: Agent 前置元数据解析 + MCP/SKILL 移除");
|
|
655
723
|
console.log(" - I: 工作流 UUID 溯源机制");
|
|
724
|
+
console.log(" - J: _workflowFileChanges 仅来自 git diff");
|
|
725
|
+
console.log("\n═══ Bug K 测试 — git diff 精准识别 + .pi-dev-output 过滤 ═══\n");
|
|
726
|
+
|
|
727
|
+
// ── Test K1: filePatterns 已移除 — 不再用 regex 猜文件路径 ──
|
|
728
|
+
console.log("📋 测试 K1: filePatterns 已移除\n");
|
|
729
|
+
|
|
730
|
+
const hasFilePatterns = source.includes("const filePatterns = [");
|
|
731
|
+
assertFalse(hasFilePatterns, "filePatterns 数组已移除(不再用 regex 嗅探文件路径)");
|
|
732
|
+
|
|
733
|
+
// ── Test K2: seenTools 文本解析 fallback 已移除 ──
|
|
734
|
+
console.log("\n📋 测试 K2: seenTools JSON fallback 已移除\n");
|
|
735
|
+
|
|
736
|
+
const hasJsonFallback = source.includes("if (seenTools.size === 0)");
|
|
737
|
+
assertFalse(hasJsonFallback, "seenTools JSON tool_use fallback 已移除");
|
|
738
|
+
|
|
739
|
+
// ── Test K3: outputPathPatterns 统一 inline 模式已移除 ──
|
|
740
|
+
console.log("\n📋 测试 K3: outputPathPatterns 已简化\n");
|
|
741
|
+
|
|
742
|
+
const hasOutputPathPatterns = source.includes("const outputPathPatterns = [");
|
|
743
|
+
assertFalse(hasOutputPathPatterns, "outputPathPatterns 数组已移除(替换为 inline workflowId 过滤)");
|
|
744
|
+
|
|
745
|
+
// ── Test K4: updateToolsFromGit 有 isWorkflowArtifactPath 过滤 ──
|
|
746
|
+
console.log("\n📋 测试 K4: updateToolsFromGit 使用 isWorkflowArtifactPath 过滤\n");
|
|
747
|
+
|
|
748
|
+
const hasIsWorkflowArtifactPath = source.includes('isWorkflowArtifactPath(change.path)');
|
|
749
|
+
assertTrue(hasIsWorkflowArtifactPath, "updateToolsFromGit 使用 isWorkflowArtifactPath 跳过 .pi-dev-output/ 路径");
|
|
750
|
+
|
|
751
|
+
const hasIsWorkflowArtifactFunc = source.includes('function isWorkflowArtifactPath');
|
|
752
|
+
assertTrue(hasIsWorkflowArtifactFunc, "isWorkflowArtifactPath 辅助函数存在");
|
|
753
|
+
|
|
754
|
+
// ── Test K5: output 路径匹配使用 escapedId + workflowId ──
|
|
755
|
+
console.log("\n📋 测试 K5: output 路径使用 workflowId 过滤\n");
|
|
756
|
+
|
|
757
|
+
const hasEscapedId = source.includes("const escapedId = _workflowId.replace(");
|
|
758
|
+
assertTrue(hasEscapedId, "output 路径匹配使用 escapedId");
|
|
759
|
+
|
|
760
|
+
// ── Test K6: progress handler output match 使用 _workflowId ──
|
|
761
|
+
console.log("\n📋 测试 K6: progress handler 使用 workflowId\n");
|
|
762
|
+
|
|
763
|
+
const hasIncludesWorkflowId = source.includes("pathCandidate.includes(_workflowId)");
|
|
764
|
+
assertTrue(hasIncludesWorkflowId, "progress handler 使用 pathCandidate.includes(_workflowId)");
|
|
765
|
+
|
|
766
|
+
// ── Test K7: addWidgetSubStepTool 只来自 git diff(regex 嗅探已移除)──
|
|
767
|
+
console.log("\n📋 测试 K7: addWidgetSubStepTool 仅来自 git diff\n");
|
|
768
|
+
|
|
769
|
+
// Verify the toolMatch regex sniffing has been removed (source 1)
|
|
770
|
+
const hasToolMatchRegex = source.includes('const toolMatch = progress.match(/(edit|read|write|new|bash|grep|find|ls|delete|remove)');
|
|
771
|
+
assertFalse(hasToolMatchRegex, "progress handler 中已移除 toolMatch 正则 (source 1)");
|
|
772
|
+
|
|
773
|
+
// Verify the updateToolsFromGit call site still exists
|
|
774
|
+
assertTrue(source.includes("addWidgetSubStepTool(stepIndex, agentName, `${change.status} ${change.path}`)"), "updateToolsFromGit 调用 addWidgetSubStepTool");
|
|
775
|
+
|
|
776
|
+
// Verify git diff polling timer exists (replacement for regex sniffing)
|
|
777
|
+
const hasGitPollTimer = source.includes('_gitPollTimer = setInterval');
|
|
778
|
+
assertTrue(hasGitPollTimer, "git diff 轮询定时器已添加");
|
|
779
|
+
|
|
780
|
+
// ── Test K8: addWidgetSubStepOutput 仍存在 (output 路径展示有用) ──
|
|
781
|
+
console.log("\n📋 测试 K8: addWidgetSubStepOutput 仍保留\n");
|
|
782
|
+
|
|
783
|
+
const hasAddWidgetOutput = source.includes("function addWidgetSubStepOutput");
|
|
784
|
+
assertTrue(hasAddWidgetOutput, "addWidgetSubStepOutput 函数声明仍保留");
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
console.log("\n═══ Bug L 测试 — output 路径白名单验证 ═══\n");
|
|
789
|
+
|
|
790
|
+
// ── Test L1: output 路径白名单验证 ──
|
|
791
|
+
console.log("\n📋 测试 L1: output 路径白名单逻辑\n");
|
|
792
|
+
|
|
793
|
+
function isValidOutputPath(path) {
|
|
794
|
+
return /^[\w.\/-]+$/.test(path) &&
|
|
795
|
+
path.length > 15 && path.length < 300 &&
|
|
796
|
+
path.includes("019e57e7");
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// 应接受的合法路径
|
|
800
|
+
assertTrue(isValidOutputPath(".pi-dev-output/pi-plans/20260524-test-019e57e7.md"), "合法路径应被接受");
|
|
801
|
+
assertTrue(isValidOutputPath(".pi-dev-output/pi-review/md/review-20260524-019e57e7.md"), "review 路径应被接受");
|
|
802
|
+
|
|
803
|
+
// 应拒绝的乱码路径
|
|
804
|
+
assertFalse(isValidOutputPath(".pi-dev-output/pi-plans/中grepUUID\"019e57e7\"找到)"), "含中文路径应被拒绝");
|
|
805
|
+
assertFalse(isValidOutputPath(".pi-dev-output/pi-plans/(测试)019e57e7.md"), "含括号路径应被拒绝");
|
|
806
|
+
|
|
807
|
+
// ── Test L2: post-completion output 检测仅搜索 clean text ──
|
|
808
|
+
console.log("\n📋 测试 L2: output 检测仅搜索 clean text(source 2 修复验证)\n");
|
|
809
|
+
|
|
810
|
+
// 验证源代码中 outputPattern.exec 搜索的是 cleanText 而非 searchText
|
|
811
|
+
const hasSearchTextOutput = source.includes('outputPattern.exec(searchText)');
|
|
812
|
+
assertFalse(hasSearchTextOutput, "output 检测不再搜索 searchText (raw JSON)");
|
|
813
|
+
|
|
814
|
+
// 验证替换为 cleanText
|
|
815
|
+
const hasCleanTextOutput = source.includes('outputPattern.exec(cleanText)');
|
|
816
|
+
assertTrue(hasCleanTextOutput, "output 检测搜索 cleanText (extracted text)");
|
|
817
|
+
|
|
818
|
+
// ── Test L3: post-completion output 有白名单验证 ──
|
|
819
|
+
console.log("\n📋 测试 L3: post-completion output 有白名单验证\n");
|
|
820
|
+
|
|
821
|
+
const hasWhitelistCheck = source.includes('/^[\\w.\\/-]+\$/.test(path_)');
|
|
822
|
+
assertTrue(hasWhitelistCheck, "post-completion output 检测包含 /^[\\w.\\/-]+\$/ 白名单验证");
|
|
823
|
+
|
|
824
|
+
// ── Test L4: progress handler output 检测使用 whitelist regex ──
|
|
825
|
+
console.log("\n📋 测试 L4: progress handler output 检测使用 whitelist regex\n");
|
|
826
|
+
|
|
827
|
+
const hasProgressWhitelist = source.includes('.pi-dev-output\\/[a-zA-Z0-9_\\/\\.-]+');
|
|
828
|
+
assertTrue(hasProgressWhitelist, "progress handler 使用 [a-zA-Z0-9_\\/\\.-]+ 白名单正则");
|
|
829
|
+
|
|
830
|
+
// ── Test L5: isWorkflowArtifactPath 在 git diff 轮询中使用 ──
|
|
831
|
+
console.log("\n📋 测试 L5: isWorkflowArtifactPath 在 git diff 轮询中使用\n");
|
|
832
|
+
|
|
833
|
+
const hasPollIsWorkflowArtifact = source.includes('isWorkflowArtifactPath(change.path)');
|
|
834
|
+
assertTrue(hasPollIsWorkflowArtifact, "git diff 轮询使用 isWorkflowArtifactPath 过滤");
|
|
835
|
+
|
|
836
|
+
// ── Test L6: git diff 轮询也向 _workflowFileChanges 推送 ──
|
|
837
|
+
console.log("\n📋 测试 L6: git diff 轮询向 _workflowFileChanges 推送\n");
|
|
838
|
+
|
|
839
|
+
// Verify the polling code pushes to _workflowFileChanges before addWidgetSubStepTool
|
|
840
|
+
const pollTimerBodyStart = source.indexOf('_gitPollTimer = setInterval');
|
|
841
|
+
const pollTimerBodyEnd = source.indexOf('_gitPollTimer.unref()');
|
|
842
|
+
const pollTimerBody = source.slice(pollTimerBodyStart, pollTimerBodyEnd);
|
|
843
|
+
const hasPushInPoll = pollTimerBody.includes('_workflowFileChanges.push');
|
|
844
|
+
assertTrue(hasPushInPoll, "git diff 轮询(setInterval)中包含 _workflowFileChanges.push");
|
|
845
|
+
|
|
846
|
+
// Verify the type mapping (A→new, D→delete, else→edit) exists in polling code
|
|
847
|
+
const hasTypeMappingInPoll = pollTimerBody.includes('change.status === "A" ? "new"');
|
|
848
|
+
assertTrue(hasTypeMappingInPoll, "git diff 轮询中文件变更的类型映射正确");
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
// ── Test L7: _pollSeen 使用 path:stepIndex 而非 status:path ──
|
|
853
|
+
console.log("\n📋 测试 L7: _pollSeen 使用 path:stepIndex 而非 status:path\n");
|
|
854
|
+
|
|
855
|
+
// 验证轮询代码不再使用 status:path 作为键
|
|
856
|
+
const hasStatusPathKeyInPoll = source.includes('const key = `\${change.status}:\${change.path}`');
|
|
857
|
+
assertFalse(hasStatusPathKeyInPoll, "_pollSeen 不使用 status:path 作为键");
|
|
858
|
+
|
|
859
|
+
// 验证轮询代码使用 path:stepIndex 作为键
|
|
860
|
+
const hasPathStepKeyInPoll = source.includes('const key = `\${change.path}:\${stepIndex}`');
|
|
861
|
+
assertTrue(hasPathStepKeyInPoll, "_pollSeen 使用 path:stepIndex 作为键");
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
// ── Test L8: toGitStatus 函数已删除 ──
|
|
865
|
+
console.log("\n📋 测试 L8: toGitStatus 函数已删除(无调用者死代码)\n");
|
|
866
|
+
|
|
867
|
+
const hasToGitStatusFunc = source.includes("function toGitStatus(");
|
|
868
|
+
assertFalse(hasToGitStatusFunc, "toGitStatus 函数已删除");
|
|
869
|
+
|
|
870
|
+
const hasToGitStatusRef = source.includes("toGitStatus");
|
|
871
|
+
assertFalse(hasToGitStatusRef, "toGitStatus 无任何引用残留");
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
// ── Test L9: updateToolsFromGit 不再有 pollSeen 参数 ──
|
|
875
|
+
console.log("\n📋 测试 L9: updateToolsFromGit 不再有 pollSeen 死代码参数\n");
|
|
876
|
+
|
|
877
|
+
const hasPollSeenParam = source.includes("pollSeen?: Set<string>");
|
|
878
|
+
assertFalse(hasPollSeenParam, "updateToolsFromGit 无 pollSeen 参数");
|
|
879
|
+
|
|
880
|
+
const hasPollSeenCheck = source.includes("pollSeen?.has(");
|
|
881
|
+
assertFalse(hasPollSeenCheck, "updateToolsFromGit 无 pollSeen?.has() 检查");
|
|
882
|
+
|
|
883
|
+
const hasPollSeenPass = source.includes("updateToolsFromGit(_workflowCwd, stepIndex, agentName, _pollSeen)");
|
|
884
|
+
assertFalse(hasPollSeenPass, "updateToolsFromGit 调用处无 _pollSeen 参数传递");
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
// ── Test L10: post-completion output 正则使用白名单字符类 ──
|
|
888
|
+
console.log("\n📋 测试 L10: post-completion output 正则使用白名单字符类\n");
|
|
889
|
+
|
|
890
|
+
// 验证 post-completion outputPattern 使用白名单 [a-zA-Z0-9] 而非排除法 [^...]
|
|
891
|
+
const hasPostCompletionWhitelist = source.includes("outputPattern = new RegExp(\`\\\\.pi-dev-output\\\\/[a-zA-Z0-9_\\\\/.-]");
|
|
892
|
+
assertTrue(hasPostCompletionWhitelist, "post-completion output 正则使用 [a-zA-Z0-9_\\\\/.-] 白名单字符类");
|
|
893
|
+
|
|
894
|
+
// 验证不再使用排除字符类 (用反确认方式)
|
|
895
|
+
const hasNegatedClassOutput = source.includes("\\[^\\\\s,;)\\\\]}");
|
|
896
|
+
const hasNegatedClassOutput2 = source.includes("[^\\\\s,;)\\\\]}");
|
|
897
|
+
assertFalse(hasNegatedClassOutput || hasNegatedClassOutput2, "post-completion output 正则不使用排除法 [^\\\\s,;)\\\\]}");
|
|
656
898
|
|
|
657
899
|
|
|
658
900
|
console.log("\n═══════════════════════════════════════════════════════\n");
|