@deepwhale/coding-agent 1.0.10 → 1.0.11

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 yysf1949
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yysf1949
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ export * from './modes/index.js';
22
22
  export * from './verify/index.js';
23
23
  export * from './policy/index.js';
24
24
  export * from './util/index.js';
25
+ export { runVerify, detectContext, type RunVerifyOptions, type VerificationReport } from './verify/index.js';
25
26
  export type { ChatMessage, LLMClient } from '@deepwhale/llm';
26
27
  export { SessionReader, SessionWriter } from '@deepwhale/core';
27
28
  export { startRepl, runOneTurn, formatUsageStatus, createReplConfirm } from './repl.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAEhC,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACxF,YAAY,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrG,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7C,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAGhC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE7G,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACxF,YAAY,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrG,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7C,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -22,6 +22,9 @@ export * from './modes/index.js';
22
22
  export * from './verify/index.js'; // Sprint 1c-revive-2-D-11-4 (2026-06-04): verify module
23
23
  export * from './policy/index.js'; // Sprint 1c-revive-2-D-24.2 (2026-06-06): tui-ink needs ToolPolicy/staticToolPolicy
24
24
  export * from './util/index.js'; // Sprint 1c-revive-2-D-25 B4 (2026-06-06): tui-ink + tui.ts 共享 util (tui-history)
25
+ // Sprint 1c-revive-2-D-26 C3 (2026-06-07): tui-ink /verify slash command 调 runVerify
26
+ // runVerify 在 verify barrel 里已 export, 这里再 re-export 出顶层供 tui-ink 用
27
+ export { runVerify, detectContext } from './verify/index.js';
25
28
  export { SessionReader, SessionWriter } from '@deepwhale/core';
26
29
  export { startRepl, runOneTurn, formatUsageStatus, createReplConfirm } from './repl.js';
27
30
  // Sprint 1c-revive-2-D-25 B2 (2026-06-06): tui-ink App needs LLMClient factory + ToolRegistry
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC,CAAC,wDAAwD;AAC3F,cAAc,mBAAmB,CAAC,CAAC,oFAAoF;AACvH,cAAc,iBAAiB,CAAC,CAAC,kFAAkF;AAGnH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAGxF,8FAA8F;AAC9F,kEAAkE;AAClE,OAAO,EAAE,mBAAmB,EAA2C,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC,CAAC,wDAAwD;AAC3F,cAAc,mBAAmB,CAAC,CAAC,oFAAoF;AACvH,cAAc,iBAAiB,CAAC,CAAC,kFAAkF;AACnH,qFAAqF;AACrF,sEAAsE;AACtE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAkD,MAAM,mBAAmB,CAAC;AAG7G,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAGxF,8FAA8F;AAC9F,kEAAkE;AAClE,OAAO,EAAE,mBAAmB,EAA2C,MAAM,kBAAkB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // @deepwhale/tui-ink — D-24 full Ink TUI container. Self-contained ESM bundle.
3
- // Built 2026-06-07T09:12:54.644Z
3
+ // Built 2026-06-07T09:33:55.188Z
4
4
  import { createRequire as __cr } from 'node:module';
5
5
  import { fileURLToPath as __fpath } from 'node:url';
6
6
  const require = __cr(__fpath(import.meta.url));
@@ -37641,7 +37641,7 @@ var import_react32 = __toESM(require_react(), 1);
37641
37641
  var import_react33 = __toESM(require_react(), 1);
37642
37642
 
37643
37643
  // src/app.tsx
37644
- var import_react43 = __toESM(require_react(), 1);
37644
+ var import_react46 = __toESM(require_react(), 1);
37645
37645
 
37646
37646
  // src/theme/index.ts
37647
37647
  import { stderr } from "node:process";
@@ -37875,6 +37875,16 @@ function sealLastAssistant() {
37875
37875
  $transcript.set([...entries.slice(0, -1), { ...last, streaming: false }]);
37876
37876
  }
37877
37877
  }
37878
+ function appendReasoningChunk(delta) {
37879
+ const entries = $transcript.get();
37880
+ const last = entries[entries.length - 1];
37881
+ if (last && last.kind === "assistant") {
37882
+ const newReasoning = (last.reasoning ?? "") + delta;
37883
+ $transcript.set([...entries.slice(0, -1), { ...last, reasoning: newReasoning }]);
37884
+ } else {
37885
+ $transcript.set([...entries, { kind: "assistant", text: "", reasoning: delta }]);
37886
+ }
37887
+ }
37878
37888
 
37879
37889
  // src/components/StatusBar.tsx
37880
37890
  import { formatUsageStatus } from "@deepwhale/coding-agent";
@@ -37905,38 +37915,297 @@ function StatusBar({ theme = THEMES.default, usage: usageOverride }) {
37905
37915
  ] });
37906
37916
  }
37907
37917
 
37908
- // src/components/Transcript.tsx
37918
+ // src/components/Markdown.tsx
37919
+ var import_react37 = __toESM(require_react(), 1);
37920
+
37921
+ // src/markdown/render.tsx
37922
+ var import_react36 = __toESM(require_react(), 1);
37909
37923
  var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
37910
- function Transcript({ theme = THEMES.default }) {
37924
+ var INLINE_RE = /(?:\[([^\]]+)\]\(([^)\s]+)\)|`([^`]+)`|\*\*([^*]+)\*\*|\*([^*]+)\*|~~([^~]+)~~)/g;
37925
+ var MEDIA_LINE_RE = /^\s*[`"']?MEDIA:\s*(\S+?)[`"']?\s*$/;
37926
+ var AUDIO_DIRECTIVE_RE = /^\s*\[\[audio_as_voice\]\]\s*$/;
37927
+ var FENCE_RE = /^(\s*)(`{3,}|~{3,})(.*)$/;
37928
+ var HEADING_RE = /^(#{1,6})\s+(.*)$/;
37929
+ var LIST_RE = /^(\s*)([-*+])\s+(.*)$/;
37930
+ var OLIST_RE = /^(\s*)(\d+)\.\s+(.*)$/;
37931
+ var BLOCKQUOTE_RE = /^>\s+(.*)$/;
37932
+ var HR_RE = /^[-*_]{3,}$/;
37933
+ function renderInline(line, theme, keyBase) {
37934
+ const out = [];
37935
+ let lastIdx = 0;
37936
+ let m;
37937
+ let counter = 0;
37938
+ INLINE_RE.lastIndex = 0;
37939
+ while ((m = INLINE_RE.exec(line)) !== null) {
37940
+ if (m.index > lastIdx) {
37941
+ out.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react36.Fragment, { children: line.slice(lastIdx, m.index) }, `${keyBase}-t-${counter++}`));
37942
+ }
37943
+ if (m[1] !== void 0 && m[2] !== void 0) {
37944
+ out.push(
37945
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: theme.toolName, underline: true, children: [
37946
+ m[1],
37947
+ " (",
37948
+ m[2],
37949
+ ")"
37950
+ ] }, `${keyBase}-l-${counter++}`)
37951
+ );
37952
+ } else if (m[3] !== void 0) {
37953
+ out.push(
37954
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.model, children: `\`${m[3]}\`` }, `${keyBase}-c-${counter++}`)
37955
+ );
37956
+ } else if (m[4] !== void 0) {
37957
+ out.push(
37958
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: true, children: m[4] }, `${keyBase}-b-${counter++}`)
37959
+ );
37960
+ } else if (m[5] !== void 0) {
37961
+ out.push(
37962
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { italic: true, children: m[5] }, `${keyBase}-i-${counter++}`)
37963
+ );
37964
+ } else if (m[6] !== void 0) {
37965
+ out.push(
37966
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { strikethrough: true, children: m[6] }, `${keyBase}-s-${counter++}`)
37967
+ );
37968
+ }
37969
+ lastIdx = m.index + m[0].length;
37970
+ }
37971
+ if (lastIdx < line.length) {
37972
+ out.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react36.Fragment, { children: line.slice(lastIdx) }, `${keyBase}-t-${counter++}`));
37973
+ }
37974
+ return out.length > 0 ? out : [/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react36.Fragment, { children: line }, `${keyBase}-raw`)];
37975
+ }
37976
+ function renderFence(content, lang, keyBase, theme) {
37977
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", borderStyle: "single", borderColor: theme.divider, paddingX: 1, marginY: 0, children: [
37978
+ lang && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.model, dimColor: true, children: lang }, `${keyBase}-lang`),
37979
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: content }, `${keyBase}-body`)
37980
+ ] }, keyBase);
37981
+ }
37982
+ function tryParseFence(lines, startLine) {
37983
+ const firstLine = lines[startLine];
37984
+ const m = firstLine.match(FENCE_RE);
37985
+ if (!m) return null;
37986
+ const fenceChar = m[2][0];
37987
+ const fenceLen = m[2].length;
37988
+ const lang = m[3].trim();
37989
+ const bodyLines = [];
37990
+ let i = startLine + 1;
37991
+ while (i < lines.length) {
37992
+ const line = lines[i];
37993
+ const closeMatch = line.match(new RegExp(`^\\s*\\${fenceChar}{${fenceLen},}\\s*$`));
37994
+ if (closeMatch) {
37995
+ return {
37996
+ type: "fence",
37997
+ lang,
37998
+ body: bodyLines.join("\n"),
37999
+ endLine: i
38000
+ };
38001
+ }
38002
+ bodyLines.push(line.replace(/^ {0,3}/, ""));
38003
+ i++;
38004
+ }
38005
+ return null;
38006
+ }
38007
+ function tryParseTable(lines, startLine) {
38008
+ const line0 = lines[startLine];
38009
+ const line1 = lines[startLine + 1];
38010
+ if (!line1) return null;
38011
+ if (!line0.includes("|") || !line1.includes("|")) return null;
38012
+ const cells1 = line1.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
38013
+ if (cells1.length < 2) return null;
38014
+ if (!cells1.every((c) => /^:?-+:?\s*$/.test(c))) return null;
38015
+ const header = line0.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
38016
+ if (header.length !== cells1.length) return null;
38017
+ const rows = [];
38018
+ let i = startLine + 2;
38019
+ while (i < lines.length) {
38020
+ const line = lines[i];
38021
+ if (!line.includes("|")) break;
38022
+ const row = line.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
38023
+ rows.push(row);
38024
+ i++;
38025
+ }
38026
+ return { type: "table", header, rows, endLine: i - 1 };
38027
+ }
38028
+ function renderMarkdown(text, theme) {
38029
+ const lines = text.split("\n");
38030
+ const out = [];
38031
+ let i = 0;
38032
+ let blockCounter = 0;
38033
+ while (i < lines.length) {
38034
+ const line = lines[i];
38035
+ const blockKey = `md-${blockCounter++}`;
38036
+ if (MEDIA_LINE_RE.test(line)) {
38037
+ const path = line.match(MEDIA_LINE_RE)[1];
38038
+ out.push(
38039
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.toolName, children: `[image: ${path}]` }, blockKey)
38040
+ );
38041
+ i++;
38042
+ continue;
38043
+ }
38044
+ if (AUDIO_DIRECTIVE_RE.test(line)) {
38045
+ out.push(
38046
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.model, children: "\u{1F50A} audio: (TTS pending \u2014 D-28+ \u5347\u7EA7 mmx-cli TTS)" }, blockKey)
38047
+ );
38048
+ i++;
38049
+ continue;
38050
+ }
38051
+ const fence = tryParseFence(lines, i);
38052
+ if (fence) {
38053
+ out.push(renderFence(fence.body, fence.lang, blockKey, theme));
38054
+ i = fence.endLine + 1;
38055
+ continue;
38056
+ }
38057
+ const table = tryParseTable(lines, i);
38058
+ if (table) {
38059
+ out.push(
38060
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", marginY: 0, children: [
38061
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { children: table.header.map((cell, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: true, color: theme.header, children: ` ${cell.padEnd(15)} ` }, `${blockKey}-h-${idx}`)) }, `${blockKey}-hr`),
38062
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.divider, children: "\u2500".repeat(15 * table.header.length + 3) }, `${blockKey}-hr-sep`),
38063
+ table.rows.map((row, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { children: row.map((cell, cellIdx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: ` ${cell.padEnd(15)} ` }, `${blockKey}-r-${rowIdx}-c-${cellIdx}`)) }, `${blockKey}-r-${rowIdx}`))
38064
+ ] }, blockKey)
38065
+ );
38066
+ i = table.endLine + 1;
38067
+ continue;
38068
+ }
38069
+ const headingMatch = line.match(HEADING_RE);
38070
+ if (headingMatch) {
38071
+ const level = headingMatch[1].length;
38072
+ const text2 = headingMatch[2];
38073
+ out.push(
38074
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: true, color: theme.header, children: "#".repeat(level) + " " + text2 }, blockKey)
38075
+ );
38076
+ i++;
38077
+ continue;
38078
+ }
38079
+ if (HR_RE.test(line)) {
38080
+ out.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.divider, children: "\u2500".repeat(40) }, blockKey));
38081
+ i++;
38082
+ continue;
38083
+ }
38084
+ const listMatch = line.match(LIST_RE);
38085
+ if (listMatch) {
38086
+ out.push(
38087
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: ` ${listMatch[1]}\u2022 ${listMatch[3]}` }, blockKey)
38088
+ );
38089
+ i++;
38090
+ continue;
38091
+ }
38092
+ const olistMatch = line.match(OLIST_RE);
38093
+ if (olistMatch) {
38094
+ out.push(
38095
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: ` ${olistMatch[1]}${olistMatch[2]}. ${olistMatch[3]}` }, blockKey)
38096
+ );
38097
+ i++;
38098
+ continue;
38099
+ }
38100
+ const bqMatch = line.match(BLOCKQUOTE_RE);
38101
+ if (bqMatch) {
38102
+ out.push(
38103
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.divider, children: `\u2502 ${bqMatch[1]}` }, blockKey)
38104
+ );
38105
+ i++;
38106
+ continue;
38107
+ }
38108
+ if (line.trim().length > 0) {
38109
+ out.push(
38110
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: renderInline(line, theme, blockKey) }, blockKey)
38111
+ );
38112
+ } else {
38113
+ out.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }, blockKey));
38114
+ }
38115
+ i++;
38116
+ }
38117
+ return out;
38118
+ }
38119
+
38120
+ // src/components/Markdown.tsx
38121
+ var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
38122
+ function Markdown({ text, theme, inline = false }) {
38123
+ const nodes = renderMarkdown(text, theme);
38124
+ if (inline) {
38125
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { children: nodes.map((node, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react37.Fragment, { children: node }, `md-inline-${i}`)) });
38126
+ }
38127
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { flexDirection: "column", children: nodes });
38128
+ }
38129
+
38130
+ // src/components/Thinking.tsx
38131
+ var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
38132
+ function Thinking({
38133
+ reasoning,
38134
+ theme,
38135
+ initialState = "collapsed"
38136
+ }) {
38137
+ if (!reasoning || reasoning.length === 0) return null;
38138
+ if (initialState === "hidden") return null;
38139
+ if (initialState === "collapsed") {
38140
+ const preview = reasoning.slice(0, 60).replace(/\n/g, " ");
38141
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { children: [
38142
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: colorize2("\u{1F4AD} ", "model", theme) }),
38143
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { dimColor: true, children: [
38144
+ preview,
38145
+ reasoning.length > 60 ? "..." : ""
38146
+ ] }),
38147
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: colorize2(" (press to expand)", "divider", theme) })
38148
+ ] });
38149
+ }
38150
+ const lines = reasoning.split("\n");
38151
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: theme.divider, paddingX: 1, marginY: 0, children: [
38152
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { children: [
38153
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: colorize2("\u{1F4AD} thinking", "model", theme) }),
38154
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: colorize2(" (press to collapse)", "divider", theme) })
38155
+ ] }),
38156
+ lines.map((line, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: line }, `think-${idx}`))
38157
+ ] });
38158
+ }
38159
+
38160
+ // src/components/Transcript.tsx
38161
+ var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
38162
+ function Transcript({ theme = THEMES.default, markdown = false, thinking = true }) {
37911
38163
  const entries = useStore($transcript);
37912
38164
  const lastStreaming = entries[entries.length - 1];
37913
38165
  const isLastStreaming = lastStreaming?.kind === "assistant" && lastStreaming.streaming === true;
37914
38166
  const sealedEntries = isLastStreaming ? entries.slice(0, -1) : entries;
37915
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", children: [
37916
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Static, { items: sealedEntries, children: (entry, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TranscriptRow, { entry, theme }, `entry-${index}`) }),
37917
- isLastStreaming && lastStreaming ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TranscriptRow, { entry: lastStreaming, theme }) : null
38167
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
38168
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Static, { items: sealedEntries, children: (entry, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TranscriptRow, { entry, theme, markdown, thinking }, `entry-${index}`) }),
38169
+ isLastStreaming && lastStreaming ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TranscriptRow, { entry: lastStreaming, theme, markdown, thinking }) : null
37918
38170
  ] });
37919
38171
  }
37920
- function TranscriptRow({ entry, theme }) {
38172
+ function TranscriptRow({
38173
+ entry,
38174
+ theme,
38175
+ markdown,
38176
+ thinking
38177
+ }) {
37921
38178
  switch (entry.kind) {
37922
38179
  case "user":
37923
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
38180
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
37924
38181
  colorize2("\u203A ", "prompt", theme),
37925
38182
  entry.text
37926
38183
  ] });
37927
38184
  case "assistant": {
37928
38185
  const prefix = colorize2(" ", "model", theme);
37929
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
37930
- prefix,
37931
- entry.text,
37932
- entry.streaming ? colorize2("\u258C", "prompt", theme) : ""
38186
+ if (markdown) {
38187
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
38188
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
38189
+ prefix,
38190
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Markdown, { text: entry.text, theme, inline: true })
38191
+ ] }),
38192
+ entry.streaming ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { children: colorize2("\u258C", "prompt", theme) }) : null
38193
+ ] });
38194
+ }
38195
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", children: [
38196
+ thinking && entry.reasoning ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Thinking, { reasoning: entry.reasoning, theme }) : null,
38197
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
38198
+ prefix,
38199
+ entry.text,
38200
+ entry.streaming ? colorize2("\u258C", "prompt", theme) : ""
38201
+ ] })
37933
38202
  ] });
37934
38203
  }
37935
38204
  case "tool": {
37936
38205
  const statusGlyph = entry.status === "success" ? "\u2713" : "\u2717";
37937
38206
  const statusColor = entry.status === "success" ? "success" : "error";
37938
38207
  const nameColor = colorize2(entry.toolName ?? "tool", "toolName", theme);
37939
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
38208
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Text, { children: [
37940
38209
  " ",
37941
38210
  colorize2(statusGlyph, statusColor, theme),
37942
38211
  " ",
@@ -37950,17 +38219,17 @@ function TranscriptRow({ entry, theme }) {
37950
38219
  }
37951
38220
 
37952
38221
  // src/components/Prompt.tsx
37953
- var import_react38 = __toESM(require_react(), 1);
38222
+ var import_react40 = __toESM(require_react(), 1);
37954
38223
 
37955
38224
  // ../../node_modules/.pnpm/ink-text-input@6.0.0_ink@7._2a39186227fe1d31183ab83244021d5b/node_modules/ink-text-input/build/index.js
37956
- var import_react37 = __toESM(require_react(), 1);
38225
+ var import_react39 = __toESM(require_react(), 1);
37957
38226
  function TextInput({ value: originalValue, placeholder = "", focus = true, mask, highlightPastedText = false, showCursor = true, onChange, onSubmit }) {
37958
- const [state, setState] = (0, import_react37.useState)({
38227
+ const [state, setState] = (0, import_react39.useState)({
37959
38228
  cursorOffset: (originalValue || "").length,
37960
38229
  cursorWidth: 0
37961
38230
  });
37962
38231
  const { cursorOffset, cursorWidth } = state;
37963
- (0, import_react37.useEffect)(() => {
38232
+ (0, import_react39.useEffect)(() => {
37964
38233
  setState((previousState) => {
37965
38234
  if (!focus || !showCursor) {
37966
38235
  return previousState;
@@ -38038,12 +38307,12 @@ function TextInput({ value: originalValue, placeholder = "", focus = true, mask,
38038
38307
  onChange(nextValue);
38039
38308
  }
38040
38309
  }, { isActive: focus });
38041
- return import_react37.default.createElement(Text, null, placeholder ? value.length > 0 ? renderedValue : renderedPlaceholder : renderedValue);
38310
+ return import_react39.default.createElement(Text, null, placeholder ? value.length > 0 ? renderedValue : renderedPlaceholder : renderedValue);
38042
38311
  }
38043
38312
  var build_default = TextInput;
38044
38313
 
38045
38314
  // src/components/Prompt.tsx
38046
- var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
38315
+ var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
38047
38316
  function Prompt({
38048
38317
  theme = THEMES.default,
38049
38318
  history,
@@ -38051,8 +38320,8 @@ function Prompt({
38051
38320
  placeholder = "\u203A message (\\ to continue, empty \\ to cancel)",
38052
38321
  disabled = false
38053
38322
  }) {
38054
- const [cont, setCont] = (0, import_react38.useState)(null);
38055
- const [value, setValue] = (0, import_react38.useState)("");
38323
+ const [cont, setCont] = (0, import_react40.useState)(null);
38324
+ const [value, setValue] = (0, import_react40.useState)("");
38056
38325
  const handleSubmit = (line) => {
38057
38326
  if (line.endsWith("\\\\")) {
38058
38327
  const unescaped = line.slice(0, -1) + "\\\\";
@@ -38080,12 +38349,12 @@ ${line}` : line;
38080
38349
  setValue("");
38081
38350
  };
38082
38351
  if (disabled) {
38083
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: theme.divider, children: "(turn in flight, Ctrl+C to abort)" }) });
38352
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.divider, children: "(turn in flight, Ctrl+C to abort)" }) });
38084
38353
  }
38085
38354
  const prefix = cont ? colorize2(`... (${cont.lineNo + 1}) > `, "prompt", theme) : colorize2("\u203A ", "prompt", theme);
38086
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { children: [
38087
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { children: prefix }),
38088
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
38355
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { children: [
38356
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: prefix }),
38357
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
38089
38358
  build_default,
38090
38359
  {
38091
38360
  value,
@@ -38099,16 +38368,16 @@ ${line}` : line;
38099
38368
  }
38100
38369
 
38101
38370
  // src/components/Confirm.tsx
38102
- var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
38371
+ var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
38103
38372
  function Confirm({ theme = THEMES.default, controller }) {
38104
38373
  const ui = useStore($uiState);
38105
38374
  if (!ui.pendingConfirm) return null;
38106
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
38107
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
38375
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", children: [
38376
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { children: [
38108
38377
  colorize2(" ? ", "prompt", theme),
38109
38378
  colorize2(ui.pendingConfirm.prompt, "prompt", theme)
38110
38379
  ] }),
38111
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: theme.divider, children: [
38380
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.divider, children: [
38112
38381
  " ",
38113
38382
  "y/N: ",
38114
38383
  controller.hasPending() ? "(waiting for input)" : ""
@@ -38117,7 +38386,7 @@ function Confirm({ theme = THEMES.default, controller }) {
38117
38386
  }
38118
38387
 
38119
38388
  // src/hooks/useHistory.ts
38120
- var import_react40 = __toESM(require_react(), 1);
38389
+ var import_react42 = __toESM(require_react(), 1);
38121
38390
 
38122
38391
  // src/history/index.ts
38123
38392
  import {
@@ -38130,8 +38399,8 @@ import {
38130
38399
 
38131
38400
  // src/hooks/useHistory.ts
38132
38401
  function useHistory() {
38133
- const [history, setHistory] = (0, import_react40.useState)(() => tuiHistoryLoad());
38134
- const append = (0, import_react40.useCallback)((line) => {
38402
+ const [history, setHistory] = (0, import_react42.useState)(() => tuiHistoryLoad());
38403
+ const append = (0, import_react42.useCallback)((line) => {
38135
38404
  if (!line || !line.trim()) return;
38136
38405
  tuiHistoryAppend(line);
38137
38406
  setHistory((prev) => {
@@ -38144,15 +38413,15 @@ function useHistory() {
38144
38413
  }
38145
38414
 
38146
38415
  // src/hooks/useAbortController.ts
38147
- var import_react41 = __toESM(require_react(), 1);
38416
+ var import_react43 = __toESM(require_react(), 1);
38148
38417
  function useAbortController(onSigint) {
38149
- const ref = (0, import_react41.useRef)(new AbortController());
38150
- const abort = (0, import_react41.useCallback)(() => {
38418
+ const ref = (0, import_react43.useRef)(new AbortController());
38419
+ const abort = (0, import_react43.useCallback)(() => {
38151
38420
  if (!ref.current.signal.aborted) {
38152
38421
  ref.current.abort();
38153
38422
  }
38154
38423
  }, []);
38155
- const reset = (0, import_react41.useCallback)(() => {
38424
+ const reset = (0, import_react43.useCallback)(() => {
38156
38425
  if (!ref.current.signal.aborted) {
38157
38426
  console.warn("[tui-ink] useAbortController.reset called without prior abort()");
38158
38427
  }
@@ -38164,7 +38433,7 @@ function useAbortController(onSigint) {
38164
38433
  onSigint();
38165
38434
  }
38166
38435
  });
38167
- (0, import_react41.useEffect)(() => {
38436
+ (0, import_react43.useEffect)(() => {
38168
38437
  const handler = () => {
38169
38438
  abort();
38170
38439
  };
@@ -38177,7 +38446,7 @@ function useAbortController(onSigint) {
38177
38446
  }
38178
38447
 
38179
38448
  // src/hooks/useRunToolLoop.ts
38180
- var import_react42 = __toESM(require_react(), 1);
38449
+ var import_react44 = __toESM(require_react(), 1);
38181
38450
  import {
38182
38451
  runToolLoop,
38183
38452
  persistToolLoopSteps,
@@ -38224,7 +38493,7 @@ function collectRanges(text, re, role, out) {
38224
38493
 
38225
38494
  // src/hooks/useRunToolLoop.ts
38226
38495
  function useRunToolLoop(args) {
38227
- const runTurn = (0, import_react42.useCallback)(
38496
+ const runTurn = (0, import_react44.useCallback)(
38228
38497
  async (userPrompt) => {
38229
38498
  const { options, theme, signal, writer, client, registry, workingMessages } = args;
38230
38499
  const modelName = options.model ?? client.model ?? "model";
@@ -38245,6 +38514,9 @@ function useRunToolLoop(args) {
38245
38514
  const colored = highlightChunk(chunk.content, theme, true);
38246
38515
  appendToLastAssistant(colored);
38247
38516
  }
38517
+ if (chunk.reasoning_content) {
38518
+ appendReasoningChunk(chunk.reasoning_content);
38519
+ }
38248
38520
  },
38249
38521
  maxSteps: options.maxSteps ?? 5,
38250
38522
  policy: resolvedPolicy,
@@ -38294,8 +38566,235 @@ function useRunToolLoop(args) {
38294
38566
  return { runTurn };
38295
38567
  }
38296
38568
 
38569
+ // src/hooks/useSubmission.ts
38570
+ var import_react45 = __toESM(require_react(), 1);
38571
+
38572
+ // src/commands/core.ts
38573
+ var HELP_LINES = [
38574
+ ["/help", "list 9 commands"],
38575
+ ["/exit (q/quit)", "exit TUI"],
38576
+ ["/clear", "clear transcript (no session close)"],
38577
+ ["/verify", "run verify (build/lint/typecheck/test)"],
38578
+ ["/status", "show model + session path + usage"],
38579
+ ["/model <name>", "switch model"],
38580
+ ["/resume", "list session paths (D-28 picker)"],
38581
+ ["/personality <name>", "switch system prompt personality"],
38582
+ ["/heapdump (mem)", "V8 heap snapshot + memory diagnostics"]
38583
+ ];
38584
+ var coreCommands = [
38585
+ {
38586
+ name: "help",
38587
+ help: "list 9 commands + hotkeys",
38588
+ category: "core",
38589
+ run: (_arg, ctx) => {
38590
+ const lines = HELP_LINES.map(([cmd, desc]) => ` ${cmd.padEnd(24)} ${desc}`);
38591
+ const helpText = [
38592
+ " /help \u2014 9 commands",
38593
+ " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
38594
+ ...lines,
38595
+ " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
38596
+ " (slash registry \u4E2D\u592E\u5316, \u8DDF Hermes ui-tui 1:1)"
38597
+ ].join("\n");
38598
+ ctx.pushEntry({ kind: "assistant", text: `
38599
+ ${helpText}
38600
+ ` });
38601
+ }
38602
+ },
38603
+ {
38604
+ name: "exit",
38605
+ aliases: ["q", "quit"],
38606
+ help: "exit TUI (writer.close \u8D70 D-19.5 finish \u8DEF\u5F84)",
38607
+ category: "core",
38608
+ run: (_arg, ctx) => {
38609
+ ctx.exit({ exitCode: 0, reason: "user-exit" });
38610
+ }
38611
+ },
38612
+ {
38613
+ name: "clear",
38614
+ help: "clear transcript (0 \u5173 session, 0 \u5199 session event)",
38615
+ category: "core",
38616
+ run: (_arg, ctx) => {
38617
+ ctx.clearTranscript();
38618
+ ctx.pushEntry({ kind: "assistant", text: "\n transcript cleared\n" });
38619
+ }
38620
+ },
38621
+ {
38622
+ name: "verify",
38623
+ help: "run verify (build/lint/typecheck/test)",
38624
+ category: "core",
38625
+ run: async (_arg, ctx) => {
38626
+ const { runVerify } = await import("@deepwhale/coding-agent");
38627
+ ctx.pushEntry({ kind: "assistant", text: "\n /verify running...\n" });
38628
+ try {
38629
+ const report = await runVerify();
38630
+ const status = report.overallStatus;
38631
+ const summary = `
38632
+ /verify done: ${status} (${report.checks.length} checks)
38633
+ `;
38634
+ ctx.pushEntry({ kind: "assistant", text: summary });
38635
+ } catch (e) {
38636
+ const err = e instanceof Error ? e.message : String(e);
38637
+ ctx.pushEntry({ kind: "assistant", text: `
38638
+ /verify error: ${err}
38639
+ ` });
38640
+ }
38641
+ }
38642
+ },
38643
+ {
38644
+ name: "status",
38645
+ help: "show model + session path + usage",
38646
+ category: "core",
38647
+ run: (_arg, ctx) => {
38648
+ const usage = ctx.ui.usage;
38649
+ const usageStr = usage ? `${usage.prompt_tokens ?? 0} prompt / ${usage.completion_tokens ?? 0} completion / ${usage.total_tokens ?? 0} total` : "(no usage yet)";
38650
+ const statusText = [
38651
+ " /status",
38652
+ " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
38653
+ ` model: ${ctx.model}`,
38654
+ ` mode: ${ctx.ui.mode}`,
38655
+ ` session: ${ctx.sessionPath ?? "(no session file)"}`,
38656
+ ` usage: ${usageStr}`,
38657
+ ` transcript: ${ctx.transcript.length} entries`,
38658
+ " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
38659
+ ].join("\n");
38660
+ ctx.pushEntry({ kind: "assistant", text: `
38661
+ ${statusText}
38662
+ ` });
38663
+ }
38664
+ }
38665
+ ];
38666
+
38667
+ // src/commands/session.ts
38668
+ var sessionCommands = [
38669
+ {
38670
+ name: "model",
38671
+ help: "switch model (e.g. /model deepseek-v4-flash)",
38672
+ category: "session",
38673
+ run: (arg, ctx) => {
38674
+ const model = arg.trim();
38675
+ if (!model) {
38676
+ ctx.pushEntry({ kind: "assistant", text: "\n /model <name> requires a model name (e.g. /model deepseek-v4-flash)\n" });
38677
+ return;
38678
+ }
38679
+ const provider = model.startsWith("claude") ? "anthropic" : "deepseek";
38680
+ ctx.setModel(model, provider);
38681
+ ctx.pushEntry({ kind: "assistant", text: `
38682
+ /model set to ${model} (provider: ${provider})
38683
+ (note: D-26 \u62CD, \u5B9E\u9645 LLMClient \u91CD build \u7559 D-28+)
38684
+ ` });
38685
+ }
38686
+ },
38687
+ {
38688
+ name: "resume",
38689
+ help: "list session paths (D-28 picker \u5347\u7EA7)",
38690
+ category: "session",
38691
+ run: (_arg, ctx) => {
38692
+ ctx.pushEntry({
38693
+ kind: "assistant",
38694
+ text: "\n /resume: D-28 picker \u5347\u7EA7 (\u8DDF Hermes sessionPicker 1:1)\n \u6682\u4E0D\u652F\u6301\u591A session \u5207\u6362, \u8DDF tui.ts \u5355 session 1:1\n"
38695
+ });
38696
+ }
38697
+ },
38698
+ {
38699
+ name: "personality",
38700
+ help: "switch system prompt personality (D-27 markdown \u6E32\u67D3\u63A5)",
38701
+ category: "session",
38702
+ run: (_arg, ctx) => {
38703
+ const name = _arg.trim();
38704
+ if (!name) {
38705
+ ctx.pushEntry({ kind: "assistant", text: "\n /personality <name> requires a name (D-27 \u5347\u7EA7)\n" });
38706
+ return;
38707
+ }
38708
+ ctx.pushEntry({
38709
+ kind: "assistant",
38710
+ text: `
38711
+ /personality set to ${name} (D-26 \u62CD placeholder, D-27 \u5347\u7EA7\u63A5)
38712
+ `
38713
+ });
38714
+ }
38715
+ }
38716
+ ];
38717
+
38718
+ // src/commands/debug.ts
38719
+ function formatBytes(bytes) {
38720
+ const mb = bytes / 1024 / 1024;
38721
+ return mb < 1 ? `${(bytes / 1024).toFixed(1)}KB` : `${mb.toFixed(1)}MB`;
38722
+ }
38723
+ var debugCommands = [
38724
+ {
38725
+ name: "heapdump",
38726
+ aliases: ["mem"],
38727
+ help: "V8 heap snapshot + memory diagnostics (D-27 \u5347\u7EA7 full memory.ts)",
38728
+ category: "debug",
38729
+ run: (_arg, ctx) => {
38730
+ const mu = process.memoryUsage();
38731
+ const text = [
38732
+ " /heapdump \u2014 process.memoryUsage()",
38733
+ " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
38734
+ ` rss: ${formatBytes(mu.rss)}`,
38735
+ ` heapTotal: ${formatBytes(mu.heapTotal)}`,
38736
+ ` heapUsed: ${formatBytes(mu.heapUsed)}`,
38737
+ ` external: ${formatBytes(mu.external)}`,
38738
+ ` arrayBuffers: ${formatBytes(mu.arrayBuffers)}`,
38739
+ " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
38740
+ " (D-27 \u5347\u7EA7: \u5199 v8.writeHeapSnapshot, D-29 \u5347\u7EA7: auto OOM \u9632\u62A4)"
38741
+ ].join("\n");
38742
+ ctx.pushEntry({ kind: "assistant", text: `
38743
+ ${text}
38744
+ ` });
38745
+ }
38746
+ }
38747
+ ];
38748
+
38749
+ // src/commands/registry.ts
38750
+ var SLASH_COMMANDS = [
38751
+ ...coreCommands,
38752
+ ...sessionCommands,
38753
+ ...debugCommands
38754
+ ];
38755
+ var byName = new Map(
38756
+ SLASH_COMMANDS.flatMap(
38757
+ (cmd) => [cmd.name, ...cmd.aliases ?? []].map((name) => [name.toLowerCase(), cmd])
38758
+ )
38759
+ );
38760
+ var findSlashCommand = (name) => byName.get(name.toLowerCase());
38761
+ var isSlashCommand = (input) => input.startsWith("/");
38762
+
38763
+ // src/hooks/useSubmission.ts
38764
+ function useSubmission(options) {
38765
+ const { slashContext, onChat } = options;
38766
+ const submit = (0, import_react45.useCallback)(
38767
+ (text) => {
38768
+ const trimmed = text.trim();
38769
+ if (!trimmed) return "empty";
38770
+ if (isSlashCommand(trimmed)) {
38771
+ const spaceIdx = trimmed.indexOf(" ");
38772
+ const cmdName = spaceIdx === -1 ? trimmed.slice(1) : trimmed.slice(1, spaceIdx);
38773
+ const arg = spaceIdx === -1 ? "" : trimmed.slice(spaceIdx + 1).trim();
38774
+ const cmd = findSlashCommand(cmdName);
38775
+ if (cmd) {
38776
+ cmd.run(arg, slashContext);
38777
+ return "slash";
38778
+ }
38779
+ slashContext.pushEntry({
38780
+ kind: "assistant",
38781
+ text: `
38782
+ unknown command: /${cmdName}
38783
+ (run /help for the 9 commands list)
38784
+ `
38785
+ });
38786
+ return "slash";
38787
+ }
38788
+ void onChat(trimmed);
38789
+ return "chat";
38790
+ },
38791
+ [slashContext, onChat]
38792
+ );
38793
+ return { submit };
38794
+ }
38795
+
38297
38796
  // src/app.tsx
38298
- var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
38797
+ var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
38299
38798
  import {
38300
38799
  createReplConfirm,
38301
38800
  staticToolPolicy as staticToolPolicy2,
@@ -38308,7 +38807,7 @@ import {
38308
38807
  import { stdout as stdout2 } from "node:process";
38309
38808
  function App2({ options, onExit }) {
38310
38809
  const { exit } = use_app_default();
38311
- const theme = (0, import_react43.useMemo)(
38810
+ const theme = (0, import_react46.useMemo)(
38312
38811
  () => THEMES[resolveTuiTheme(options.theme)],
38313
38812
  [options.theme]
38314
38813
  );
@@ -38316,16 +38815,16 @@ function App2({ options, onExit }) {
38316
38815
  const { history, append: appendHistory } = useHistory();
38317
38816
  const { controller: turnAbortController } = useAbortController(() => {
38318
38817
  });
38319
- const [workingMessages, setWorkingMessages] = (0, import_react43.useState)([]);
38818
+ const [workingMessages, setWorkingMessages] = (0, import_react46.useState)([]);
38320
38819
  const sessionPath = options.sessionPath;
38321
- const writerRef = (0, import_react43.useRef)(null);
38322
- const readerRef = (0, import_react43.useRef)(null);
38820
+ const writerRef = (0, import_react46.useRef)(null);
38821
+ const readerRef = (0, import_react46.useRef)(null);
38323
38822
  if (writerRef.current === null && sessionPath) {
38324
38823
  writerRef.current = new SessionWriter(sessionPath);
38325
38824
  readerRef.current = new SessionReader(sessionPath);
38326
38825
  }
38327
- const [sessionLoaded, setSessionLoaded] = (0, import_react43.useState)(false);
38328
- (0, import_react43.useEffect)(() => {
38826
+ const [sessionLoaded, setSessionLoaded] = (0, import_react46.useState)(false);
38827
+ (0, import_react46.useEffect)(() => {
38329
38828
  if (sessionLoaded) return;
38330
38829
  const writer = writerRef.current;
38331
38830
  const reader = readerRef.current;
@@ -38352,13 +38851,13 @@ function App2({ options, onExit }) {
38352
38851
  setSessionLoaded(true);
38353
38852
  }
38354
38853
  }, [sessionLoaded]);
38355
- const confirmControllerRef = (0, import_react43.useRef)(null);
38854
+ const confirmControllerRef = (0, import_react46.useRef)(null);
38356
38855
  if (confirmControllerRef.current === null) {
38357
38856
  confirmControllerRef.current = createReplConfirm({ output: stdout2 });
38358
38857
  }
38359
38858
  const confirmController = confirmControllerRef.current;
38360
- const [turnInFlight, setTurnInFlight] = (0, import_react43.useState)(false);
38361
- const clientRef = (0, import_react43.useRef)(null);
38859
+ const [turnInFlight, setTurnInFlight] = (0, import_react46.useState)(false);
38860
+ const clientRef = (0, import_react46.useRef)(null);
38362
38861
  if (clientRef.current === null) {
38363
38862
  const providerNarrow = options.provider === "deepseek" || options.provider === "anthropic" ? options.provider : void 0;
38364
38863
  clientRef.current = createDefaultClient({
@@ -38367,11 +38866,14 @@ function App2({ options, onExit }) {
38367
38866
  });
38368
38867
  }
38369
38868
  const client = clientRef.current;
38370
- const registryRef = (0, import_react43.useRef)(null);
38869
+ const registryRef = (0, import_react46.useRef)(null);
38371
38870
  if (registryRef.current === null) {
38372
38871
  registryRef.current = createDefaultRegistry();
38373
38872
  }
38374
38873
  const registry = registryRef.current;
38874
+ const [modelName, setModelName] = (0, import_react46.useState)(
38875
+ options.model ?? client.model ?? "model"
38876
+ );
38375
38877
  const { runTurn } = useRunToolLoop({
38376
38878
  options,
38377
38879
  theme,
@@ -38382,7 +38884,7 @@ function App2({ options, onExit }) {
38382
38884
  policy: staticToolPolicy2,
38383
38885
  workingMessages
38384
38886
  });
38385
- (0, import_react43.useEffect)(() => {
38887
+ (0, import_react46.useEffect)(() => {
38386
38888
  if (ui.mode === "idle" && turnInFlight) {
38387
38889
  setTurnInFlight(false);
38388
38890
  const entries = $transcript.get();
@@ -38398,38 +38900,53 @@ function App2({ options, onExit }) {
38398
38900
  }
38399
38901
  }
38400
38902
  }, [ui.mode, turnInFlight]);
38401
- const handlePromptSubmit = (assembled) => {
38402
- const trimmed = assembled.trim();
38403
- if (!trimmed) return;
38404
- if (trimmed === "/exit" || trimmed === "q" || trimmed === "quit") {
38903
+ const slashContext = (0, import_react46.useMemo)(() => ({
38904
+ theme,
38905
+ ui: $uiState.get(),
38906
+ // snapshot (每次 submit 拿最新, D-29+ 优化)
38907
+ transcript: $transcript.get(),
38908
+ model: modelName,
38909
+ sessionPath,
38910
+ pushEntry,
38911
+ clearTranscript: () => {
38912
+ $transcript.set([]);
38913
+ },
38914
+ setModel: (model) => {
38915
+ setModelName(model);
38916
+ },
38917
+ exit: (result) => {
38405
38918
  void writerRef.current?.close();
38406
- onExit({ exitCode: 0, reason: "user-exit" });
38919
+ onExit(result ?? { exitCode: 0, reason: "user-exit" });
38407
38920
  exit();
38408
- return;
38409
38921
  }
38410
- if (confirmController.hasPending()) {
38411
- confirmController.offerLine(trimmed);
38412
- return;
38922
+ }), [theme, modelName, sessionPath, onExit, exit]);
38923
+ const { submit } = useSubmission({
38924
+ slashContext,
38925
+ onChat: (prompt) => {
38926
+ if (confirmController.hasPending()) {
38927
+ confirmController.offerLine(prompt);
38928
+ return;
38929
+ }
38930
+ if (turnInFlight) return;
38931
+ appendHistory(prompt);
38932
+ setTurnInFlight(true);
38933
+ void (async () => {
38934
+ await runTurn(prompt);
38935
+ })();
38413
38936
  }
38414
- if (turnInFlight) return;
38415
- appendHistory(trimmed);
38416
- setTurnInFlight(true);
38417
- void (async () => {
38418
- await runTurn(trimmed);
38419
- })();
38420
- };
38421
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
38422
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: theme.header, children: "\u232C deepwhale tui-ink v1.0.10" }),
38423
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Divider, { theme }),
38424
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatusBar, { theme }),
38425
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Transcript, { theme }),
38426
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Confirm, { theme, controller: confirmController }),
38427
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
38937
+ });
38938
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
38939
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.header, children: "\u232C deepwhale tui-ink v1.0.11" }),
38940
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Divider, { theme }),
38941
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatusBar, { theme }),
38942
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Transcript, { theme }),
38943
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Confirm, { theme, controller: confirmController }),
38944
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
38428
38945
  Prompt,
38429
38946
  {
38430
38947
  theme,
38431
38948
  history,
38432
- onSubmit: handlePromptSubmit,
38949
+ onSubmit: submit,
38433
38950
  disabled: turnInFlight
38434
38951
  }
38435
38952
  )
@@ -38437,14 +38954,14 @@ function App2({ options, onExit }) {
38437
38954
  }
38438
38955
 
38439
38956
  // src/index.tsx
38440
- var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
38957
+ var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
38441
38958
  async function runTuiInkMode(options = {}) {
38442
38959
  if (!process.stdout.isTTY) {
38443
38960
  return { exitCode: 0, reason: "not-tty" };
38444
38961
  }
38445
38962
  return new Promise((resolve) => {
38446
38963
  const { waitUntilExit, unmount } = render_default(
38447
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
38964
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
38448
38965
  App2,
38449
38966
  {
38450
38967
  options,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepwhale/coding-agent",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "DeepWhale CLI: DeepSeek-first AI coding agent. Linear sessions, 6 tools, Docker sandbox.",
5
5
  "license": "MIT",
6
6
  "type": "module",