@aipanel/dsh-plugin 1.2.27 → 1.2.28
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 +147 -94
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -628,6 +628,22 @@ function parseTscDiags(rawOutput, filePath, projectDir, source = "tsc") {
|
|
|
628
628
|
}
|
|
629
629
|
return diags;
|
|
630
630
|
}
|
|
631
|
+
function filterTscOutputForFile(rawOutput, filePath, projectDir) {
|
|
632
|
+
const resolved = path.resolve(filePath);
|
|
633
|
+
const errorLinePat = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+TS\d+:/;
|
|
634
|
+
const filtered = [];
|
|
635
|
+
let keep = false;
|
|
636
|
+
for (const line of rawOutput.split("\n")) {
|
|
637
|
+
const m = errorLinePat.exec(line);
|
|
638
|
+
if (m) {
|
|
639
|
+
keep = path.resolve(projectDir, m[1]) === resolved;
|
|
640
|
+
} else if (!/^\s/.test(line)) {
|
|
641
|
+
keep = false;
|
|
642
|
+
}
|
|
643
|
+
if (keep) filtered.push(line);
|
|
644
|
+
}
|
|
645
|
+
return filtered.join("\n");
|
|
646
|
+
}
|
|
631
647
|
async function runTypeCheck(filePath, cwd) {
|
|
632
648
|
const dir = cwd;
|
|
633
649
|
const projectDir = filePath ? findTsconfigDir(filePath) ?? dir : dir;
|
|
@@ -658,21 +674,7 @@ async function runTypeCheck(filePath, cwd) {
|
|
|
658
674
|
}
|
|
659
675
|
const diagnostics = parseTscDiags(rawOutput, filePath, projectDir, engine.source);
|
|
660
676
|
if (filePath) {
|
|
661
|
-
|
|
662
|
-
const errorLinePat = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+TS\d+:/;
|
|
663
|
-
const lines = rawOutput.split("\n");
|
|
664
|
-
const filtered = [];
|
|
665
|
-
let keep = false;
|
|
666
|
-
for (const line of lines) {
|
|
667
|
-
const m = errorLinePat.exec(line);
|
|
668
|
-
if (m) {
|
|
669
|
-
keep = path.resolve(projectDir, m[1]) === resolved;
|
|
670
|
-
} else if (!/^\s/.test(line)) {
|
|
671
|
-
keep = false;
|
|
672
|
-
}
|
|
673
|
-
if (keep) filtered.push(line);
|
|
674
|
-
}
|
|
675
|
-
rawOutput = filtered.join("\n");
|
|
677
|
+
rawOutput = filterTscOutputForFile(rawOutput, filePath, projectDir);
|
|
676
678
|
}
|
|
677
679
|
log2.debug("type-check finished", {
|
|
678
680
|
engine: engine.source,
|
|
@@ -682,6 +684,32 @@ async function runTypeCheck(filePath, cwd) {
|
|
|
682
684
|
});
|
|
683
685
|
return { rawOutput, exitCode, diagnostics, source: engine.source };
|
|
684
686
|
}
|
|
687
|
+
async function runTypeChecksForFiles(files, cwd) {
|
|
688
|
+
const groups = /* @__PURE__ */ new Map();
|
|
689
|
+
for (const file of files) {
|
|
690
|
+
const resolved = path.resolve(file);
|
|
691
|
+
const projectDir = findTsconfigDir(resolved) ?? cwd;
|
|
692
|
+
const group = groups.get(projectDir);
|
|
693
|
+
if (group) group.push(resolved);
|
|
694
|
+
else groups.set(projectDir, [resolved]);
|
|
695
|
+
}
|
|
696
|
+
const results = /* @__PURE__ */ new Map();
|
|
697
|
+
for (const [projectDir, group] of groups) {
|
|
698
|
+
const whole = await runTypeCheck(void 0, projectDir).catch(() => ({
|
|
699
|
+
rawOutput: "",
|
|
700
|
+
exitCode: 0
|
|
701
|
+
}));
|
|
702
|
+
for (const file of group) {
|
|
703
|
+
results.set(file, {
|
|
704
|
+
rawOutput: filterTscOutputForFile(whole.rawOutput, file, projectDir),
|
|
705
|
+
exitCode: whole.exitCode,
|
|
706
|
+
diagnostics: (whole.diagnostics ?? []).filter((d) => d.file === file),
|
|
707
|
+
source: whole.source
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return results;
|
|
712
|
+
}
|
|
685
713
|
async function runAllChecks(pattern, cwd) {
|
|
686
714
|
log2.debug("runAllChecks", { pattern, cwd });
|
|
687
715
|
const [eslintOutput, tscOutput] = await Promise.all([
|
|
@@ -690,6 +718,18 @@ async function runAllChecks(pattern, cwd) {
|
|
|
690
718
|
]);
|
|
691
719
|
return { eslintOutput, tscOutput };
|
|
692
720
|
}
|
|
721
|
+
async function runAllChecksForFiles(files, cwd) {
|
|
722
|
+
const targets = [...new Set(files.map((file) => path.resolve(file)))];
|
|
723
|
+
const tscByFile = await runTypeChecksForFiles(targets, cwd);
|
|
724
|
+
const results = /* @__PURE__ */ new Map();
|
|
725
|
+
for (const file of targets) {
|
|
726
|
+
results.set(file, {
|
|
727
|
+
eslintOutput: await lintFiles(file, cwd),
|
|
728
|
+
tscOutput: tscByFile.get(file) ?? { rawOutput: "", exitCode: 0 }
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
return results;
|
|
732
|
+
}
|
|
693
733
|
async function runProjectDiagnostics(workspace) {
|
|
694
734
|
const tscDirs = fs.existsSync(path.join(workspace, "tsconfig.json")) ? [workspace] : findAllTsconfigDirs(workspace);
|
|
695
735
|
log2.debug("Tsc dirs to check", { count: tscDirs.length, dirs: tscDirs });
|
|
@@ -711,18 +751,36 @@ function tscSectionTitle(tscOutput) {
|
|
|
711
751
|
function lintSectionTitle(lintOutput) {
|
|
712
752
|
return lintOutput.engines?.length ? lintOutput.engines.join(" + ") : "ESLint";
|
|
713
753
|
}
|
|
714
|
-
function
|
|
754
|
+
function omittedFindingsHint(omitted) {
|
|
755
|
+
return `\u2026\u8FD8\u6709 ${omitted} \u6761\uFF0C\u5B8C\u6574\u7ED3\u679C\u8BF7\u8C03\u7528 run_diagnostics`;
|
|
756
|
+
}
|
|
757
|
+
function boundFindings(text, maxFindings) {
|
|
758
|
+
if (!maxFindings || !text) return text;
|
|
759
|
+
const lines = text.split("\n").filter((line) => line.trim() !== "");
|
|
760
|
+
if (lines.length <= maxFindings) return text;
|
|
761
|
+
return [...lines.slice(0, maxFindings), omittedFindingsHint(lines.length - maxFindings)].join(
|
|
762
|
+
"\n"
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
function formatDiagnosticsSections(title, eslintOutput, tscOutput, options = {}) {
|
|
766
|
+
const lintLines = boundFindings(eslintOutput.text, options.maxFindingsPerSection);
|
|
767
|
+
const tscLines = boundFindings(tscOutput.rawOutput.trim(), options.maxFindingsPerSection);
|
|
715
768
|
const parts = [];
|
|
716
|
-
|
|
769
|
+
if (!options.onlyFindings || lintLines) {
|
|
770
|
+
parts.push(`## ${lintSectionTitle(eslintOutput)}
|
|
717
771
|
|
|
718
|
-
` + (
|
|
719
|
-
|
|
720
|
-
|
|
772
|
+
` + (lintLines || "\u6CA1\u6709\u53D1\u73B0\u95EE\u9898"));
|
|
773
|
+
}
|
|
774
|
+
if (!options.onlyFindings || tscLines) {
|
|
775
|
+
parts.push(`## ${tscSectionTitle(tscOutput)}
|
|
721
776
|
|
|
722
777
|
` + (tscLines || "\u6CA1\u6709\u53D1\u73B0\u7C7B\u578B\u9519\u8BEF"));
|
|
723
|
-
|
|
778
|
+
}
|
|
779
|
+
if (parts.length === 0) return "";
|
|
780
|
+
const body = parts.join("\n\n");
|
|
781
|
+
return title ? `${title}
|
|
724
782
|
|
|
725
|
-
`
|
|
783
|
+
${body}` : body;
|
|
726
784
|
}
|
|
727
785
|
|
|
728
786
|
// ../../core/es/common/utils.mjs
|
|
@@ -971,46 +1029,66 @@ function buildNodeContext(e) {
|
|
|
971
1029
|
if (e.previewPageUrl) lines.push(`\u7528\u6237\u9009\u4E2D\u8282\u70B9\u65F6\u7684\u9875\u9762 URL\uFF1A${e.previewPageUrl}`);
|
|
972
1030
|
return lines.join("\n");
|
|
973
1031
|
}
|
|
974
|
-
function buildPluginMessage(text) {
|
|
1032
|
+
function buildPluginMessage(text, notice) {
|
|
975
1033
|
return {
|
|
976
1034
|
role: "user",
|
|
977
1035
|
id: randomUUID(),
|
|
978
1036
|
content: [{ type: "text", text }],
|
|
979
|
-
source: { kind: "plugin", plugin: name }
|
|
1037
|
+
source: { kind: "plugin", plugin: name, ...notice }
|
|
980
1038
|
};
|
|
981
1039
|
}
|
|
982
|
-
|
|
1040
|
+
var DIAGNOSTICS_MESSAGE_MAX_CHARS = 4e3;
|
|
1041
|
+
var MAX_FINDINGS_PER_SECTION = 3;
|
|
1042
|
+
function editTarget(exec) {
|
|
1043
|
+
const rawArgs = exec.arguments;
|
|
1044
|
+
const filePath = typeof rawArgs?.file_path === "string" ? rawArgs.file_path : rawArgs?.filePath;
|
|
1045
|
+
return typeof filePath === "string" && filePath ? filePath : void 0;
|
|
1046
|
+
}
|
|
1047
|
+
function registerPendingEdit(pending, exec, result, decision, cwd) {
|
|
983
1048
|
if (!MUTATING_TOOLS.has(exec.name)) return;
|
|
984
1049
|
if (result.isError) return;
|
|
985
1050
|
if (decision.kind !== "accept") return;
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
if (!
|
|
990
|
-
let files =
|
|
1051
|
+
const filePath = editTarget(exec);
|
|
1052
|
+
if (!filePath || !isJsFile(filePath)) return;
|
|
1053
|
+
const { agent } = exec;
|
|
1054
|
+
if (!agent) return;
|
|
1055
|
+
let files = pending.get(agent);
|
|
991
1056
|
if (!files) {
|
|
992
1057
|
files = /* @__PURE__ */ new Set();
|
|
993
|
-
|
|
1058
|
+
pending.set(agent, files);
|
|
994
1059
|
}
|
|
995
1060
|
files.add(path2.resolve(cwd, filePath));
|
|
996
1061
|
}
|
|
997
|
-
|
|
1062
|
+
function truncateDiagnostics(text) {
|
|
1063
|
+
if (text.length <= DIAGNOSTICS_MESSAGE_MAX_CHARS) return text;
|
|
1064
|
+
return `${text.slice(0, DIAGNOSTICS_MESSAGE_MAX_CHARS)}
|
|
1065
|
+
|
|
1066
|
+
\u2026\uFF08\u8BCA\u65AD\u8F93\u51FA\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF1B\u53EF\u8C03\u7528 run_diagnostics \u67E5\u770B\u5B8C\u6574\u7ED3\u679C\uFF09`;
|
|
1067
|
+
}
|
|
1068
|
+
async function collectPendingDiagnostics(pending, cwd) {
|
|
1069
|
+
const files = [...pending];
|
|
1070
|
+
const checks = await runAllChecksForFiles(files, cwd).catch(
|
|
1071
|
+
() => /* @__PURE__ */ new Map()
|
|
1072
|
+
);
|
|
1073
|
+
const empty = {
|
|
1074
|
+
eslintOutput: {},
|
|
1075
|
+
tscOutput: { rawOutput: "", exitCode: 0 }
|
|
1076
|
+
};
|
|
998
1077
|
const blocks = [];
|
|
999
1078
|
for (const file of files) {
|
|
1000
|
-
const { eslintOutput, tscOutput } =
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
if (!eslintOutput.text && !tscOutput.rawOutput.trim()) continue;
|
|
1007
|
-
blocks.push(
|
|
1008
|
-
formatDiagnosticsSections(`### ${path2.relative(cwd, file)}`, eslintOutput, tscOutput)
|
|
1079
|
+
const { eslintOutput, tscOutput } = checks.get(file) ?? empty;
|
|
1080
|
+
const section = formatDiagnosticsSections(
|
|
1081
|
+
`### ${path2.relative(cwd, file)}`,
|
|
1082
|
+
eslintOutput,
|
|
1083
|
+
tscOutput,
|
|
1084
|
+
{ onlyFindings: true, maxFindingsPerSection: MAX_FINDINGS_PER_SECTION }
|
|
1009
1085
|
);
|
|
1086
|
+
if (!section) continue;
|
|
1087
|
+
blocks.push(section);
|
|
1010
1088
|
}
|
|
1011
|
-
return blocks.length > 0 ? `\u81EA\u52A8\u8BCA\u65AD\
|
|
1089
|
+
return blocks.length > 0 ? truncateDiagnostics(`\u81EA\u52A8\u8BCA\u65AD\uFF08\u7F16\u8F91\u540E\uFF09\uFF1A
|
|
1012
1090
|
|
|
1013
|
-
${blocks.join("\n\n")}` : "";
|
|
1091
|
+
${blocks.join("\n\n")}`) : "";
|
|
1014
1092
|
}
|
|
1015
1093
|
function toDiagnosticEntries(items) {
|
|
1016
1094
|
return items.map((d) => ({
|
|
@@ -1189,64 +1267,39 @@ function apply(ctx, config = {}) {
|
|
|
1189
1267
|
}
|
|
1190
1268
|
};
|
|
1191
1269
|
tools.register(diagnosticsTool);
|
|
1192
|
-
const
|
|
1270
|
+
const pendingEdits = /* @__PURE__ */ new WeakMap();
|
|
1193
1271
|
ctx.on(
|
|
1194
1272
|
"tools/post-execute",
|
|
1195
1273
|
async (exec, result, next) => {
|
|
1196
1274
|
const decision = await next();
|
|
1197
1275
|
if (!autoDiagnose) return decision;
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
buildPluginMessage(ptcDiagText)
|
|
1214
|
-
]
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
if (!MUTATING_TOOLS.has(exec.name)) return decision;
|
|
1218
|
-
if (result.isError) return decision;
|
|
1219
|
-
if (decision.kind !== "accept") return decision;
|
|
1220
|
-
const rawArgs = exec.arguments;
|
|
1221
|
-
const filePath = typeof rawArgs?.file_path === "string" ? rawArgs.file_path : rawArgs?.filePath;
|
|
1222
|
-
if (typeof filePath !== "string" || !filePath) return decision;
|
|
1223
|
-
if (!isJsFile(filePath)) return decision;
|
|
1224
|
-
const { eslintOutput, tscOutput } = await runAllChecks(
|
|
1225
|
-
path2.resolve(cwd, filePath),
|
|
1226
|
-
cwd
|
|
1227
|
-
).catch(() => ({
|
|
1228
|
-
eslintOutput: {},
|
|
1229
|
-
tscOutput: { rawOutput: "", exitCode: 0 }
|
|
1230
|
-
}));
|
|
1231
|
-
const parts = [];
|
|
1232
|
-
if (tscOutput.rawOutput.trim())
|
|
1233
|
-
parts.push(`## ${tscSectionTitle(tscOutput)}
|
|
1234
|
-
|
|
1235
|
-
` + tscOutput.rawOutput.trim());
|
|
1236
|
-
if (eslintOutput.text)
|
|
1237
|
-
parts.push(`## ${lintSectionTitle(eslintOutput)}
|
|
1238
|
-
|
|
1239
|
-
` + eslintOutput.text);
|
|
1240
|
-
const diagText = parts.join("\n\n");
|
|
1241
|
-
if (!diagText) return decision;
|
|
1242
|
-
const existing = decision.kind === "accept" && decision.content || result.content;
|
|
1276
|
+
registerPendingEdit(pendingEdits, exec, result, decision, cwd);
|
|
1277
|
+
return decision;
|
|
1278
|
+
}
|
|
1279
|
+
);
|
|
1280
|
+
ctx.on(
|
|
1281
|
+
"agent/pre-step",
|
|
1282
|
+
async ({ agent }, next) => {
|
|
1283
|
+
const decision = await next();
|
|
1284
|
+
if (!autoDiagnose) return decision;
|
|
1285
|
+
const files = pendingEdits.get(agent);
|
|
1286
|
+
if (!files || files.size === 0) return decision;
|
|
1287
|
+
pendingEdits.delete(agent);
|
|
1288
|
+
if (decision.kind === "reject") return decision;
|
|
1289
|
+
const text = await collectPendingDiagnostics(files, cwd);
|
|
1290
|
+
if (!text) return decision;
|
|
1243
1291
|
return {
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1292
|
+
...decision,
|
|
1293
|
+
messages: [
|
|
1294
|
+
...decision.messages,
|
|
1295
|
+
buildPluginMessage(text, {
|
|
1296
|
+
form: "notice",
|
|
1297
|
+
summary: `\u7F16\u8F91\u540E\u81EA\u52A8\u8BCA\u65AD\uFF1A${files.size} \u4E2A\u6587\u4EF6`
|
|
1298
|
+
})
|
|
1299
|
+
]
|
|
1248
1300
|
};
|
|
1249
|
-
}
|
|
1301
|
+
},
|
|
1302
|
+
{ prepend: true }
|
|
1250
1303
|
);
|
|
1251
1304
|
}
|
|
1252
1305
|
if (vitePort > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aipanel/dsh-plugin",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AIPanel for DeepSeek Harness (dsh):注入审查工具 run_diagnostics、编辑后自动诊断。",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"vue-tsc": "^3.3.9"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@aipanel/core": "1.2.
|
|
19
|
+
"@aipanel/core": "1.2.28",
|
|
20
20
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
21
21
|
"@deepseek-ai/dsh-agent": "^0.1.6-alpha.2",
|
|
22
22
|
"@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
|