@xia-sc/dsh-git 0.5.2 → 0.6.0

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/lib/index.js CHANGED
@@ -84,8 +84,15 @@ const LLM_TIMEOUT_MS = 120000;
84
84
  const LLM_DIFF_MAX_CHARS = 12000;
85
85
  /** Diff-stat characters sent to the model. */
86
86
  const LLM_STAT_MAX_CHARS = 2000;
87
- /** Output cap for one generated commit message. */
88
- const LLM_MAX_TOKENS = 256;
87
+ /**
88
+ * Output cap for one generated commit message. A reasoning model spends this
89
+ * budget on its thinking BEFORE any text exists and reports the two together as
90
+ * `completion_tokens`, so a cap sized for the message alone truncates every
91
+ * call that thinks a little. 8192 leaves room for a long think plus a
92
+ * subject+body; the model still stops on its own well before it (an unset cap
93
+ * would fall back to the adapter's own default, which is larger still).
94
+ */
95
+ const LLM_MAX_TOKENS = 8192;
89
96
  /** Accepted `generateMessage` modes. */
90
97
  const GENERATE_MODES = ["staged", "unstaged", "all"];
91
98
  /** Default mode: the only one whose content is what `commit` will actually record. */
@@ -535,7 +542,7 @@ async function gitFetch(rawCwd, rawRemote, signal) {
535
542
  return fail("fetch-failed", cleanMessage(error), {});
536
543
  }
537
544
  const detail = [out.stdout, out.stderr].join("\n").trim();
538
- return ok({ message: detail !== "" ? detail.slice(0, 800) : "fetch complete" });
545
+ return ok({ message: detail.slice(0, 800) });
539
546
  }
540
547
 
541
548
  /**
@@ -555,7 +562,7 @@ async function gitPull(rawCwd, signal) {
555
562
  return fail("pull-failed", cleanMessage(error), {});
556
563
  }
557
564
  const detail = [out.stdout, out.stderr].join("\n").trim();
558
- return ok({ message: detail !== "" ? detail.slice(0, 800) : "pull complete" });
565
+ return ok({ message: detail.slice(0, 800) });
559
566
  }
560
567
 
561
568
  /** Read the repo's configured author (user.name / user.email) or null. */
@@ -624,7 +631,7 @@ async function gitPush(rawCwd, signal) {
624
631
  return fail("push-failed", cleanMessage(error), {});
625
632
  }
626
633
  const detail = [out.stdout, out.stderr].join("\n").trim();
627
- return ok({ message: detail !== "" ? detail.slice(0, 800) : "push complete" });
634
+ return ok({ message: detail.slice(0, 800) });
628
635
  }
629
636
 
630
637
  /**
@@ -759,7 +766,9 @@ async function gitStage(rawCwd, signal) {
759
766
  return fail("stage-failed", cleanMessage(error), {});
760
767
  }
761
768
  const detail = [out.stdout, out.stderr].join("\n").trim();
762
- return ok({ message: detail !== "" ? detail.slice(0, 800) : "all changes staged" });
769
+ // `message` is git's own output and nothing else: an operation git says
770
+ // nothing about reports an empty string, and the panel words its own summary.
771
+ return ok({ message: detail.slice(0, 800) });
763
772
  }
764
773
 
765
774
  /**
@@ -1049,7 +1058,8 @@ function generationMessage(text) {
1049
1058
  * @param route - `{ provider, model }` from {@link resolveLlmRoute}.
1050
1059
  * @param signal - cancellation (browser abort plus the server ceiling).
1051
1060
  * @returns the trimmed message.
1052
- * @throws on a terminal stream failure or empty output.
1061
+ * @throws on a terminal stream failure, an output cap hit before any text
1062
+ * existed (`llm-truncated`), or genuinely empty output (`llm-empty`).
1053
1063
  */
1054
1064
  async function requestCommitMessage(ctx, prompt, route, signal) {
1055
1065
  const options = {
@@ -1076,6 +1086,16 @@ async function requestCommitMessage(ctx, prompt, route, signal) {
1076
1086
  const ordered = (map) => [...map.entries()].sort((left, right) => left[0] - right[0]).map((entry) => entry[1]);
1077
1087
  const message = (blocks.size > 0 ? ordered(blocks) : ordered(deltas)).join("").trim();
1078
1088
  if (message === "") {
1089
+ // "Nothing came back" and "the budget ran out before any text did" are
1090
+ // different failures with different fixes; a reasoning model hits the
1091
+ // second one routinely (its thinking shares `completion_tokens` with the
1092
+ // message), so report the cap instead of a misleading generic empty.
1093
+ if (finish !== undefined && finish.kind === "max-tokens") {
1094
+ throw Object.assign(
1095
+ new Error(`the model hit the ${LLM_MAX_TOKENS}-token output cap before writing any commit message (a reasoning model spends that budget on its thinking first)`),
1096
+ { pluginCode: "llm-truncated" }
1097
+ );
1098
+ }
1079
1099
  throw Object.assign(new Error("the model returned no commit message"), { pluginCode: "llm-empty" });
1080
1100
  }
1081
1101
  return message;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xia-sc/dsh-git",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Complete Git management for the DeepSeek Harness Web GUI: branch switch, new-branch-from-base, fetch, pull, stage-all, commit (with AI-drafted message), push, status, and a click-to-open diff viewer (staged/unstaged, resizable) — as a collapsible floating panel that follows the current session's workspace.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -12,7 +12,8 @@
12
12
  "files": [
13
13
  "lib",
14
14
  "cordis.patch.yml",
15
- "README.en.md"
15
+ "README.en.md",
16
+ "LICENSE"
16
17
  ],
17
18
  "license": "MIT",
18
19
  "author": "xia-sc",