@aipanel/dsh-plugin 1.2.27 → 1.3.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.
Files changed (2) hide show
  1. package/dist/index.js +158 -94
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -26,6 +26,17 @@ var EXT_MSG = {
26
26
  };
27
27
  var SEVERITY_ERROR = 1;
28
28
  var SEVERITY_WARN = 2;
29
+ var VUE_DEVTOOLS_TIMELINE_INCLUDE = {
30
+ SUMMARY: "summary",
31
+ SLOW: "slow",
32
+ ALL: "all"
33
+ };
34
+ var VUE_DEVTOOLS_TIMELINE_INCLUDES = [
35
+ VUE_DEVTOOLS_TIMELINE_INCLUDE.SUMMARY,
36
+ VUE_DEVTOOLS_TIMELINE_INCLUDE.SLOW,
37
+ VUE_DEVTOOLS_TIMELINE_INCLUDE.ALL
38
+ ];
39
+ var DEFAULT_TIMELINE_INCLUDE = VUE_DEVTOOLS_TIMELINE_INCLUDE.SLOW;
29
40
  var MUTATING_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "apply_patch"]);
30
41
  var OPENCODE_ENV = {
31
42
  CONFIG_DIR: "OPENCODE_CONFIG_DIR",
@@ -628,6 +639,22 @@ function parseTscDiags(rawOutput, filePath, projectDir, source = "tsc") {
628
639
  }
629
640
  return diags;
630
641
  }
642
+ function filterTscOutputForFile(rawOutput, filePath, projectDir) {
643
+ const resolved = path.resolve(filePath);
644
+ const errorLinePat = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+TS\d+:/;
645
+ const filtered = [];
646
+ let keep = false;
647
+ for (const line of rawOutput.split("\n")) {
648
+ const m = errorLinePat.exec(line);
649
+ if (m) {
650
+ keep = path.resolve(projectDir, m[1]) === resolved;
651
+ } else if (!/^\s/.test(line)) {
652
+ keep = false;
653
+ }
654
+ if (keep) filtered.push(line);
655
+ }
656
+ return filtered.join("\n");
657
+ }
631
658
  async function runTypeCheck(filePath, cwd) {
632
659
  const dir = cwd;
633
660
  const projectDir = filePath ? findTsconfigDir(filePath) ?? dir : dir;
@@ -658,21 +685,7 @@ async function runTypeCheck(filePath, cwd) {
658
685
  }
659
686
  const diagnostics = parseTscDiags(rawOutput, filePath, projectDir, engine.source);
660
687
  if (filePath) {
661
- const resolved = path.resolve(filePath);
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");
688
+ rawOutput = filterTscOutputForFile(rawOutput, filePath, projectDir);
676
689
  }
677
690
  log2.debug("type-check finished", {
678
691
  engine: engine.source,
@@ -682,6 +695,32 @@ async function runTypeCheck(filePath, cwd) {
682
695
  });
683
696
  return { rawOutput, exitCode, diagnostics, source: engine.source };
684
697
  }
698
+ async function runTypeChecksForFiles(files, cwd) {
699
+ const groups = /* @__PURE__ */ new Map();
700
+ for (const file of files) {
701
+ const resolved = path.resolve(file);
702
+ const projectDir = findTsconfigDir(resolved) ?? cwd;
703
+ const group = groups.get(projectDir);
704
+ if (group) group.push(resolved);
705
+ else groups.set(projectDir, [resolved]);
706
+ }
707
+ const results = /* @__PURE__ */ new Map();
708
+ for (const [projectDir, group] of groups) {
709
+ const whole = await runTypeCheck(void 0, projectDir).catch(() => ({
710
+ rawOutput: "",
711
+ exitCode: 0
712
+ }));
713
+ for (const file of group) {
714
+ results.set(file, {
715
+ rawOutput: filterTscOutputForFile(whole.rawOutput, file, projectDir),
716
+ exitCode: whole.exitCode,
717
+ diagnostics: (whole.diagnostics ?? []).filter((d) => d.file === file),
718
+ source: whole.source
719
+ });
720
+ }
721
+ }
722
+ return results;
723
+ }
685
724
  async function runAllChecks(pattern, cwd) {
686
725
  log2.debug("runAllChecks", { pattern, cwd });
687
726
  const [eslintOutput, tscOutput] = await Promise.all([
@@ -690,6 +729,18 @@ async function runAllChecks(pattern, cwd) {
690
729
  ]);
691
730
  return { eslintOutput, tscOutput };
692
731
  }
732
+ async function runAllChecksForFiles(files, cwd) {
733
+ const targets = [...new Set(files.map((file) => path.resolve(file)))];
734
+ const tscByFile = await runTypeChecksForFiles(targets, cwd);
735
+ const results = /* @__PURE__ */ new Map();
736
+ for (const file of targets) {
737
+ results.set(file, {
738
+ eslintOutput: await lintFiles(file, cwd),
739
+ tscOutput: tscByFile.get(file) ?? { rawOutput: "", exitCode: 0 }
740
+ });
741
+ }
742
+ return results;
743
+ }
693
744
  async function runProjectDiagnostics(workspace) {
694
745
  const tscDirs = fs.existsSync(path.join(workspace, "tsconfig.json")) ? [workspace] : findAllTsconfigDirs(workspace);
695
746
  log2.debug("Tsc dirs to check", { count: tscDirs.length, dirs: tscDirs });
@@ -711,18 +762,36 @@ function tscSectionTitle(tscOutput) {
711
762
  function lintSectionTitle(lintOutput) {
712
763
  return lintOutput.engines?.length ? lintOutput.engines.join(" + ") : "ESLint";
713
764
  }
714
- function formatDiagnosticsSections(title, eslintOutput, tscOutput) {
765
+ function omittedFindingsHint(omitted) {
766
+ return `\u2026\u8FD8\u6709 ${omitted} \u6761\uFF0C\u5B8C\u6574\u7ED3\u679C\u8BF7\u8C03\u7528 run_diagnostics`;
767
+ }
768
+ function boundFindings(text, maxFindings) {
769
+ if (!maxFindings || !text) return text;
770
+ const lines = text.split("\n").filter((line) => line.trim() !== "");
771
+ if (lines.length <= maxFindings) return text;
772
+ return [...lines.slice(0, maxFindings), omittedFindingsHint(lines.length - maxFindings)].join(
773
+ "\n"
774
+ );
775
+ }
776
+ function formatDiagnosticsSections(title, eslintOutput, tscOutput, options = {}) {
777
+ const lintLines = boundFindings(eslintOutput.text, options.maxFindingsPerSection);
778
+ const tscLines = boundFindings(tscOutput.rawOutput.trim(), options.maxFindingsPerSection);
715
779
  const parts = [];
716
- parts.push(`## ${lintSectionTitle(eslintOutput)}
780
+ if (!options.onlyFindings || lintLines) {
781
+ parts.push(`## ${lintSectionTitle(eslintOutput)}
717
782
 
718
- ` + (eslintOutput.text || "\u6CA1\u6709\u53D1\u73B0\u95EE\u9898"));
719
- const tscLines = tscOutput.rawOutput.trim();
720
- parts.push(`## ${tscSectionTitle(tscOutput)}
783
+ ` + (lintLines || "\u6CA1\u6709\u53D1\u73B0\u95EE\u9898"));
784
+ }
785
+ if (!options.onlyFindings || tscLines) {
786
+ parts.push(`## ${tscSectionTitle(tscOutput)}
721
787
 
722
788
  ` + (tscLines || "\u6CA1\u6709\u53D1\u73B0\u7C7B\u578B\u9519\u8BEF"));
723
- return `${title}
789
+ }
790
+ if (parts.length === 0) return "";
791
+ const body = parts.join("\n\n");
792
+ return title ? `${title}
724
793
 
725
- ` + parts.join("\n\n");
794
+ ${body}` : body;
726
795
  }
727
796
 
728
797
  // ../../core/es/common/utils.mjs
@@ -971,46 +1040,66 @@ function buildNodeContext(e) {
971
1040
  if (e.previewPageUrl) lines.push(`\u7528\u6237\u9009\u4E2D\u8282\u70B9\u65F6\u7684\u9875\u9762 URL\uFF1A${e.previewPageUrl}`);
972
1041
  return lines.join("\n");
973
1042
  }
974
- function buildPluginMessage(text) {
1043
+ function buildPluginMessage(text, notice) {
975
1044
  return {
976
1045
  role: "user",
977
1046
  id: randomUUID(),
978
1047
  content: [{ type: "text", text }],
979
- source: { kind: "plugin", plugin: name }
1048
+ source: { kind: "plugin", plugin: name, ...notice }
980
1049
  };
981
1050
  }
982
- function registerPtcEdit(targets, rootKey, exec, result, decision, cwd) {
1051
+ var DIAGNOSTICS_MESSAGE_MAX_CHARS = 4e3;
1052
+ var MAX_FINDINGS_PER_SECTION = 3;
1053
+ function editTarget(exec) {
1054
+ const rawArgs = exec.arguments;
1055
+ const filePath = typeof rawArgs?.file_path === "string" ? rawArgs.file_path : rawArgs?.filePath;
1056
+ return typeof filePath === "string" && filePath ? filePath : void 0;
1057
+ }
1058
+ function registerPendingEdit(pending, exec, result, decision, cwd) {
983
1059
  if (!MUTATING_TOOLS.has(exec.name)) return;
984
1060
  if (result.isError) return;
985
1061
  if (decision.kind !== "accept") return;
986
- const rawArgs = exec.arguments;
987
- const filePath = typeof rawArgs?.file_path === "string" ? rawArgs.file_path : rawArgs?.filePath;
988
- if (typeof filePath !== "string" || !filePath) return;
989
- if (!isJsFile(filePath)) return;
990
- let files = targets.get(rootKey);
1062
+ const filePath = editTarget(exec);
1063
+ if (!filePath || !isJsFile(filePath)) return;
1064
+ const { agent } = exec;
1065
+ if (!agent) return;
1066
+ let files = pending.get(agent);
991
1067
  if (!files) {
992
1068
  files = /* @__PURE__ */ new Set();
993
- targets.set(rootKey, files);
1069
+ pending.set(agent, files);
994
1070
  }
995
1071
  files.add(path2.resolve(cwd, filePath));
996
1072
  }
997
- async function collectPtcDiagnostics(files, cwd) {
1073
+ function truncateDiagnostics(text) {
1074
+ if (text.length <= DIAGNOSTICS_MESSAGE_MAX_CHARS) return text;
1075
+ return `${text.slice(0, DIAGNOSTICS_MESSAGE_MAX_CHARS)}
1076
+
1077
+ \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`;
1078
+ }
1079
+ async function collectPendingDiagnostics(pending, cwd) {
1080
+ const files = [...pending];
1081
+ const checks = await runAllChecksForFiles(files, cwd).catch(
1082
+ () => /* @__PURE__ */ new Map()
1083
+ );
1084
+ const empty = {
1085
+ eslintOutput: {},
1086
+ tscOutput: { rawOutput: "", exitCode: 0 }
1087
+ };
998
1088
  const blocks = [];
999
1089
  for (const file of files) {
1000
- const { eslintOutput, tscOutput } = await runAllChecks(file, cwd).catch(
1001
- () => ({
1002
- eslintOutput: {},
1003
- tscOutput: { rawOutput: "", exitCode: 0 }
1004
- })
1005
- );
1006
- if (!eslintOutput.text && !tscOutput.rawOutput.trim()) continue;
1007
- blocks.push(
1008
- formatDiagnosticsSections(`### ${path2.relative(cwd, file)}`, eslintOutput, tscOutput)
1090
+ const { eslintOutput, tscOutput } = checks.get(file) ?? empty;
1091
+ const section = formatDiagnosticsSections(
1092
+ `### ${path2.relative(cwd, file)}`,
1093
+ eslintOutput,
1094
+ tscOutput,
1095
+ { onlyFindings: true, maxFindingsPerSection: MAX_FINDINGS_PER_SECTION }
1009
1096
  );
1097
+ if (!section) continue;
1098
+ blocks.push(section);
1010
1099
  }
1011
- return blocks.length > 0 ? `\u81EA\u52A8\u8BCA\u65AD\uFF08PTC \u6279\u91CF\u7F16\u8F91\u540E\uFF09\uFF1A
1100
+ return blocks.length > 0 ? truncateDiagnostics(`\u81EA\u52A8\u8BCA\u65AD\uFF08\u7F16\u8F91\u540E\uFF09\uFF1A
1012
1101
 
1013
- ${blocks.join("\n\n")}` : "";
1102
+ ${blocks.join("\n\n")}`) : "";
1014
1103
  }
1015
1104
  function toDiagnosticEntries(items) {
1016
1105
  return items.map((d) => ({
@@ -1189,64 +1278,39 @@ function apply(ctx, config = {}) {
1189
1278
  }
1190
1279
  };
1191
1280
  tools.register(diagnosticsTool);
1192
- const pendingPtcEdits = /* @__PURE__ */ new Map();
1281
+ const pendingEdits = /* @__PURE__ */ new WeakMap();
1193
1282
  ctx.on(
1194
1283
  "tools/post-execute",
1195
1284
  async (exec, result, next) => {
1196
1285
  const decision = await next();
1197
1286
  if (!autoDiagnose) return decision;
1198
- if (exec.parent !== void 0) {
1199
- registerPtcEdit(pendingPtcEdits, String(exec.rootCallId), exec, result, decision, cwd);
1200
- return decision;
1201
- }
1202
- const rootKey = String(exec.rootCallId);
1203
- const ptcTargets = pendingPtcEdits.get(rootKey);
1204
- if (ptcTargets !== void 0) {
1205
- pendingPtcEdits.delete(rootKey);
1206
- if (decision.kind !== "accept") return decision;
1207
- const ptcDiagText = await collectPtcDiagnostics(ptcTargets, cwd);
1208
- if (!ptcDiagText) return decision;
1209
- return {
1210
- ...decision,
1211
- additionalContexts: [
1212
- ...decision.additionalContexts ?? [],
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;
1287
+ registerPendingEdit(pendingEdits, exec, result, decision, cwd);
1288
+ return decision;
1289
+ }
1290
+ );
1291
+ ctx.on(
1292
+ "agent/pre-step",
1293
+ async ({ agent }, next) => {
1294
+ const decision = await next();
1295
+ if (!autoDiagnose) return decision;
1296
+ const files = pendingEdits.get(agent);
1297
+ if (!files || files.size === 0) return decision;
1298
+ pendingEdits.delete(agent);
1299
+ if (decision.kind === "reject") return decision;
1300
+ const text = await collectPendingDiagnostics(files, cwd);
1301
+ if (!text) return decision;
1243
1302
  return {
1244
- kind: "accept",
1245
- content: [...existing, { type: "text", text: `
1246
-
1247
- ${diagText}` }]
1303
+ ...decision,
1304
+ messages: [
1305
+ ...decision.messages,
1306
+ buildPluginMessage(text, {
1307
+ form: "notice",
1308
+ summary: `\u7F16\u8F91\u540E\u81EA\u52A8\u8BCA\u65AD\uFF1A${files.size} \u4E2A\u6587\u4EF6`
1309
+ })
1310
+ ]
1248
1311
  };
1249
- }
1312
+ },
1313
+ { prepend: true }
1250
1314
  );
1251
1315
  }
1252
1316
  if (vitePort > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/dsh-plugin",
3
- "version": "1.2.27",
3
+ "version": "1.3.1",
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.27",
19
+ "@aipanel/core": "1.3.1",
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",