@goodandready/dsh-goal 0.2.29 → 0.2.31

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/client.js CHANGED
@@ -32,28 +32,152 @@ window.__ModuleLoader__.load({
32
32
 
33
33
  // --- 10 QUICK LAUNCH ENGINEERING TEMPLATES ---
34
34
 
35
- const QUICK_LAUNCH_TEMPLATES = [
36
-
37
- { id: 'fix', icon: '🐛', label: { en: 'Fix Bug', zh: '修复缺陷' }, prefix: { en: 'Investigate bug, pinpoint root cause, apply fix, and verify with tests: ', zh: '调查并定位缺陷根源,实施修复并通过测试验证:' } },
38
-
39
- { id: 'refactor', icon: '⚡', label: { en: 'Refactor (YAGNI)', zh: '重构精简' }, prefix: { en: 'Refactor module applying YAGNI principle, delete redundant abstractions, and verify: ', zh: '遵循 YAGNI 原则重构模块,删除冗余抽象并验证:' } },
40
-
41
- { id: 'tests', icon: '📝', label: { en: 'Tests & Coverage', zh: '补充测试' }, prefix: { en: 'Write comprehensive unit tests covering edge cases for: ', zh: '为以下模块编写覆盖边界情况的完整单元测试:' } },
42
-
43
- { id: 'review', icon: '🔍', label: { en: 'Code Review', zh: '代码审计' }, prefix: { en: 'Review codebase for subtle bugs, memory leaks, and over-engineering in: ', zh: '对代码库进行深入审查,排查潜在缺陷与过度设计:' } },
44
-
45
- { id: 'feature', icon: '🚀', label: { en: 'New Feature', zh: '新功能开发' }, prefix: { en: 'Implement new feature according to specification and add tests for: ', zh: '根据规格说明实现新功能并补充测试:' } },
46
-
47
- { id: 'security', icon: '🛡️', label: { en: 'Security Audit', zh: '安全加固' }, prefix: { en: 'Audit security boundaries, sanitize inputs, and prevent injection in: ', zh: '审计安全边界,增加输入过滤与防注入保护:' } },
48
-
49
- { id: 'docs', icon: '📚', label: { en: 'Docs & Contract', zh: '文档规范' }, prefix: { en: 'Update technical documentation, README, and DESIGN.md conforming to standard for: ', zh: '按标准更新技术文档、README 与 DESIGN.md:' } },
50
-
51
- { id: 'upgrade', icon: '📦', label: { en: 'Upgrade Deps', zh: '依赖升级' }, prefix: { en: 'Upgrade dependencies, resolve breaking API changes, and verify build for: ', zh: '安全升级依赖库,解决接口兼容性并确保构建通过:' } },
52
-
53
- { id: 'cleanup', icon: '🧹', label: { en: 'Dead Code', zh: '清理死代码' }, prefix: { en: 'Find and safely remove dead code, unused exports, and stale fixtures in: ', zh: '查找并安全移除死代码、未使用的导出与失效测试用例:' } },
54
-
55
- { id: 'perf', icon: '⚙️', label: { en: 'Performance', zh: '性能优化' }, prefix: { en: 'Profile performance bottlenecks, eliminate unnecessary blocking calls in: ', zh: '分析性能瓶颈,消除阻塞调用并优化响应速度:' } },
35
+ function formatQuickLaunchPrompt(tmpl, vars, lang) {
36
+ if (!tmpl) return "";
37
+ const chosenLang = lang === "zh" ? "zh" : "en";
38
+ const tStr = tmpl.template ? (tmpl.template[chosenLang] || tmpl.template.en) : null;
39
+ if (!tStr) return tmpl.prefix ? (tmpl.prefix[chosenLang] || tmpl.prefix.en) : "";
40
+ return tStr.replace(/\{\{([a-zA-Z0-9_]+)\}\}/g, (_, key) => {
41
+ return (vars && vars[key] != null && vars[key] !== "") ? String(vars[key]) : "";
42
+ }).replace(/\s+/g, " ").trim();
43
+ }
56
44
 
45
+ const QUICK_LAUNCH_TEMPLATES = [
46
+ {
47
+ id: 'fix',
48
+ icon: "🐛",
49
+ label: { en: "Fix Bug", zh: "修复缺陷" },
50
+ prefix: { en: "Investigate bug, pinpoint root cause, apply fix, and verify with tests: ", zh: "调查并定位缺陷根源,实施修复并通过测试验证:" },
51
+ template: {
52
+ en: "Investigate bug {{issueNumber}} in {{targetModule}}, pinpoint root cause, apply targeted fix, and verify with tests: {{description}}",
53
+ zh: "调查缺陷 {{issueNumber}}(位于 {{targetModule}}),定位根因,实施修复并通过测试验证:{{description}}"
54
+ },
55
+ variables: [
56
+ { id: "issueNumber", label: { en: "Issue #", zh: "Issue 编号" }, placeholder: '#issue-123', required: true },
57
+ { id: "targetModule", label: { en: "Target Module", zh: "目标模块" }, placeholder: "lib/tools.js" },
58
+ { id: 'description', label: { en: "Description / Symptom", zh: "症状或描述" }, placeholder: "Describe symptom..." },
59
+ ],
60
+ },
61
+ {
62
+ id: 'refactor',
63
+ icon: "⚡",
64
+ label: { en: "Refactor (YAGNI)", zh: "重构精简" },
65
+ prefix: { en: "Refactor module applying YAGNI principle, delete redundant abstractions, and verify: ", zh: "遵循 YAGNI 原则重构模块,删除冗余抽象并验证:" },
66
+ template: {
67
+ en: "Refactor module {{targetFile}} applying YAGNI principle to < {{maxLines}} lines, delete redundant abstractions, and verify tests.",
68
+ zh: "遵循 YAGNI 原则重构模块 {{targetFile}} 至 < {{maxLines}} 行,删除冗余代码并验证测试。"
69
+ },
70
+ variables: [
71
+ { id: "targetFile", label: { en: "Target File", zh: "目标文件" }, placeholder: "lib/goal-engine.js", required: true },
72
+ { id: "maxLines", label: { en: "Max Lines Threshold", zh: "行数上限" }, placeholder: "500" },
73
+ ],
74
+ },
75
+ {
76
+ id: 'tests',
77
+ icon: "📝",
78
+ label: { en: "Tests & Coverage", zh: "补充测试" },
79
+ prefix: { en: "Write comprehensive unit tests covering edge cases for: ", zh: "为以下模块编写覆盖边界情况的完整单元测试:" },
80
+ template: {
81
+ en: "Write comprehensive unit tests in {{testFile}} covering edge cases for {{targetFile}}.",
82
+ zh: "在 {{testFile}} 中为 {{targetFile}} 编写覆盖边界情况的完整单元测试。"
83
+ },
84
+ variables: [
85
+ { id: "targetFile", label: { en: "Module to Test", zh: "被测模块" }, placeholder: "lib/engine-verification.js", required: true },
86
+ { id: "testFile", label: { en: "Test File Path", zh: "测试文件路径" }, placeholder: "test/acceptance-gate.test.mjs" },
87
+ ],
88
+ },
89
+ {
90
+ id: 'review',
91
+ icon: "🔍",
92
+ label: { en: "Code Review", zh: "代码审计" },
93
+ prefix: { en: "Review codebase for subtle bugs, memory leaks, and over-engineering in: ", zh: "对代码库进行深入审查,排查潜在缺陷与过度设计:" },
94
+ template: {
95
+ en: "Review codebase for subtle bugs, memory leaks, and over-engineering in {{targetScope}}.",
96
+ zh: "对 {{targetScope}} 进行深入审查,排查潜在缺陷与过度设计。"
97
+ },
98
+ variables: [
99
+ { id: "targetScope", label: { en: "Target Scope", zh: "审查范围" }, placeholder: "lib/ and src/client/", required: true },
100
+ ],
101
+ },
102
+ {
103
+ id: 'feature',
104
+ icon: "🚀",
105
+ label: { en: "New Feature", zh: "新功能开发" },
106
+ prefix: { en: "Implement new feature according to specification and add tests for: ", zh: "根据规格说明实现新功能并补充测试:" },
107
+ template: {
108
+ en: "Implement feature {{featureName}} according to specification {{specDetails}} and add comprehensive tests.",
109
+ zh: "根据规格 {{specDetails}} 实现新功能 {{featureName}} 并补充完整测试。"
110
+ },
111
+ variables: [
112
+ { id: "featureName", label: { en: "Feature Name", zh: "功能名称" }, placeholder: "Bi-directional kanban sync", required: true },
113
+ { id: "specDetails", label: { en: "Specification / Issue", zh: "规格或Issue" }, placeholder: '#issue-164' },
114
+ ],
115
+ },
116
+ {
117
+ id: 'security',
118
+ icon: "🛡️",
119
+ label: { en: "Security Audit", zh: "安全加固" },
120
+ prefix: { en: "Audit security boundaries, sanitize inputs, and prevent injection in: ", zh: "审计安全边界,增加输入过滤与防注入保护:" },
121
+ template: {
122
+ en: "Audit security boundaries, sanitize inputs, and prevent injection in {{targetRoute}}.",
123
+ zh: "审计 {{targetRoute}} 的安全边界,增加输入过滤与防注入保护。"
124
+ },
125
+ variables: [
126
+ { id: "targetRoute", label: { en: "Routes / Subsystem", zh: "路由或子系统" }, placeholder: "lib/routes.js", required: true },
127
+ ],
128
+ },
129
+ {
130
+ id: 'docs',
131
+ icon: "📚",
132
+ label: { en: "Docs & Contract", zh: "文档规范" },
133
+ prefix: { en: "Update technical documentation, README, and DESIGN.md conforming to standard for: ", zh: "按标准更新技术文档、README 与 DESIGN.md:" },
134
+ template: {
135
+ en: "Update technical documentation, README, and {{docTarget}} conforming to engineering standards.",
136
+ zh: "按标准更新技术文档、README 与 {{docTarget}}。"
137
+ },
138
+ variables: [
139
+ { id: "docTarget", label: { en: "Doc Scope / Module", zh: "文档范围或模块" }, placeholder: "docs/design/DESIGN.md", required: true },
140
+ ],
141
+ },
142
+ {
143
+ id: 'upgrade',
144
+ icon: "📦",
145
+ label: { en: "Upgrade Deps", zh: "依赖升级" },
146
+ prefix: { en: "Upgrade dependencies, resolve breaking API changes, and verify build for: ", zh: "安全升级依赖库,解决接口兼容性并确保构建通过:" },
147
+ template: {
148
+ en: "Upgrade dependency {{depName}}, resolve breaking API changes, and verify build.",
149
+ zh: "升级依赖库 {{depName}},解决接口兼容性并确保构建通过。"
150
+ },
151
+ variables: [
152
+ { id: "depName", label: { en: "Dependency Name", zh: "依赖包名" }, placeholder: "cordis@^4.0.0", required: true },
153
+ ],
154
+ },
155
+ {
156
+ id: 'cleanup',
157
+ icon: "🧹",
158
+ label: { en: "Dead Code", zh: "清理死代码" },
159
+ prefix: { en: "Find and safely remove dead code, unused exports, and stale fixtures in: ", zh: "查找并安全移除死代码、未使用的导出与失效测试用例:" },
160
+ template: {
161
+ en: "Find and safely remove dead code, unused exports, and stale fixtures in {{cleanupTarget}}.",
162
+ zh: "查找并安全移除 {{cleanupTarget}} 中的死代码、未使用的导出与失效测试用例。"
163
+ },
164
+ variables: [
165
+ { id: "cleanupTarget", label: { en: "Target Scope", zh: "清理目标" }, placeholder: "src/client/ or unused fixtures", required: true },
166
+ ],
167
+ },
168
+ {
169
+ id: 'perf',
170
+ icon: "⚙️",
171
+ label: { en: "Performance", zh: "性能优化" },
172
+ prefix: { en: "Profile performance bottlenecks, eliminate unnecessary blocking calls in: ", zh: "分析性能瓶颈,消除阻塞调用并优化响应速度:" },
173
+ template: {
174
+ en: "Profile performance bottlenecks, eliminate unnecessary blocking calls in {{perfTarget}}.",
175
+ zh: "分析性能瓶颈,消除 {{perfTarget}} 中的阻塞调用并优化响应速度。"
176
+ },
177
+ variables: [
178
+ { id: "perfTarget", label: { en: "Subsystem / Function", zh: "优化目标" }, placeholder: "buildClient or state flush", required: true },
179
+ ],
180
+ },
57
181
  ];
58
182
 
59
183
  // --- SVG ICONS ---
@@ -1653,6 +1777,28 @@ window.__ModuleLoader__.load({
1653
1777
  gap: 3px;
1654
1778
  }
1655
1779
 
1780
+ .dsh-goal-template-chip.active {
1781
+ border-color: var(--dsw-alias-brand-primary);
1782
+ font-weight: 600;
1783
+ background: var(--dsw-alias-bg-layer-3);
1784
+ }
1785
+
1786
+ .dsh-goal-operator-card {
1787
+ background: var(--dsw-alias-bg-sunken);
1788
+ border: 1px solid var(--dsw-alias-state-warning-primary);
1789
+ border-radius: 6px;
1790
+ padding: 10px 12px;
1791
+ margin-top: 8px;
1792
+ }
1793
+
1794
+ .dsh-goal-tpl-vars-box {
1795
+ background: var(--dsw-alias-bg-sunken);
1796
+ border: 1px solid var(--dsw-alias-border-l1);
1797
+ border-radius: 6px;
1798
+ padding: 8px 10px;
1799
+ margin-bottom: 10px;
1800
+ }
1801
+
1656
1802
  .dsh-goal-gate-error {
1657
1803
  font-size: 11px;
1658
1804
  color: var(--dsw-alias-state-danger-primary);
@@ -1662,9 +1808,129 @@ window.__ModuleLoader__.load({
1662
1808
  background: var(--dsw-alias-bg-layer-2);
1663
1809
  font-family: monospace;
1664
1810
  }
1665
-
1811
+ .dsh-goal-tabs {
1812
+ display: flex;
1813
+ gap: 8px;
1814
+ border-bottom: 1px solid var(--dsw-alias-border-l1);
1815
+ margin: 10px 0 14px 0;
1816
+ padding-bottom: 6px;
1817
+ }
1818
+ .dsh-goal-tab-btn {
1819
+ background: transparent;
1820
+ border: none;
1821
+ font-size: 13px;
1822
+ font-weight: 500;
1823
+ color: var(--dsw-alias-label-secondary);
1824
+ cursor: pointer;
1825
+ padding: 6px 12px;
1826
+ border-radius: 6px;
1827
+ display: inline-flex;
1828
+ align-items: center;
1829
+ gap: 6px;
1830
+ transition: all 0.15s ease;
1831
+ }
1832
+ .dsh-goal-tab-btn:hover {
1833
+ color: var(--dsw-alias-label-primary);
1834
+ background: var(--dsw-alias-bg-layer-2);
1835
+ }
1836
+ .dsh-goal-tab-btn.active {
1837
+ color: var(--dsw-alias-brand-primary);
1838
+ background: var(--dsw-alias-bg-layer-2);
1839
+ font-weight: 600;
1840
+ }
1841
+ .dsh-goal-diff-box {
1842
+ display: flex;
1843
+ flex-direction: column;
1844
+ gap: 10px;
1845
+ }
1846
+ .dsh-goal-diff-summary {
1847
+ display: flex;
1848
+ align-items: center;
1849
+ justify-content: space-between;
1850
+ background: var(--dsw-alias-bg-sunken);
1851
+ border: 1px solid var(--dsw-alias-border-l1);
1852
+ border-radius: 6px;
1853
+ padding: 8px 12px;
1854
+ font-size: 12px;
1855
+ }
1856
+ .dsh-goal-diff-stat-pill {
1857
+ display: inline-flex;
1858
+ align-items: center;
1859
+ gap: 8px;
1860
+ font-weight: 600;
1861
+ }
1862
+ .dsh-goal-diff-ins {
1863
+ color: var(--dsw-alias-state-success-primary);
1864
+ }
1865
+ .dsh-goal-diff-del {
1866
+ color: var(--dsw-alias-state-danger-primary);
1867
+ }
1868
+ .dsh-goal-diff-files-list {
1869
+ display: flex;
1870
+ flex-wrap: wrap;
1871
+ gap: 6px;
1872
+ max-height: 100px;
1873
+ overflow-y: auto;
1874
+ }
1875
+ .dsh-goal-diff-file-chip {
1876
+ display: inline-flex;
1877
+ align-items: center;
1878
+ gap: 6px;
1879
+ background: var(--dsw-alias-bg-layer-2);
1880
+ border: 1px solid var(--dsw-alias-border-l1);
1881
+ border-radius: 4px;
1882
+ padding: 3px 8px;
1883
+ font-size: 11px;
1884
+ font-family: monospace;
1885
+ cursor: pointer;
1886
+ color: var(--dsw-alias-label-secondary);
1887
+ }
1888
+ .dsh-goal-diff-file-chip:hover,
1889
+ .dsh-goal-diff-file-chip.active {
1890
+ border-color: var(--dsw-alias-brand-primary);
1891
+ color: var(--dsw-alias-label-primary);
1892
+ }
1893
+ .dsh-goal-diff-viewer {
1894
+ background: var(--dsw-alias-bg-sunken);
1895
+ border: 1px solid var(--dsw-alias-border-l1);
1896
+ border-radius: 6px;
1897
+ padding: 10px;
1898
+ font-family: monospace;
1899
+ font-size: 12px;
1900
+ line-height: 1.45;
1901
+ max-height: 380px;
1902
+ overflow: auto;
1903
+ white-space: pre-wrap;
1904
+ word-break: break-all;
1905
+ }
1906
+ .dsh-goal-diff-line {
1907
+ display: block;
1908
+ padding: 1px 4px;
1909
+ border-radius: 2px;
1910
+ }
1911
+ .dsh-goal-diff-line-add {
1912
+ background: var(--dsw-alias-state-success-bg, var(--dsw-alias-bg-hover));
1913
+ color: var(--dsw-alias-state-success-primary);
1914
+ }
1915
+ .dsh-goal-diff-line-del {
1916
+ background: var(--dsw-alias-state-error-bg, var(--dsw-alias-bg-hover));
1917
+ color: var(--dsw-alias-state-danger-primary);
1918
+ }
1919
+ .dsh-goal-diff-line-hunk {
1920
+ background: var(--dsw-alias-bg-layer-2);
1921
+ color: var(--dsw-alias-state-info-primary);
1922
+ }
1923
+ .dsh-goal-diff-line-header {
1924
+ color: var(--dsw-alias-label-tertiary);
1925
+ font-weight: 600;
1926
+ }
1927
+ .dsh-goal-diff-empty {
1928
+ padding: 24px;
1929
+ text-align: center;
1930
+ color: var(--dsw-alias-label-secondary);
1931
+ font-size: 13px;
1932
+ }
1666
1933
  `;
1667
-
1668
1934
  function injectStylesOnce() {
1669
1935
 
1670
1936
  if (typeof document === 'undefined') return;
@@ -1957,6 +2223,146 @@ window.__ModuleLoader__.load({
1957
2223
  const [interveneText, setInterveneText] = useState('');
1958
2224
 
1959
2225
  const [interveneSent, setInterveneSent] = useState(false);
2226
+ const [activeTab, setActiveTab] = useState("overview");
2227
+ const [diffData, setDiffData] = useState(null);
2228
+ const [diffLoading, setDiffLoading] = useState(false);
2229
+ const [diffError, setDiffError] = useState(null);
2230
+ const [selectedFile, setSelectedFile] = useState(null);
2231
+
2232
+ const fetchDiff = useCallback(() => {
2233
+ setDiffLoading(true);
2234
+ setDiffError(null);
2235
+ const sid = state.sessionId || "default";
2236
+ const url = "/dsh-goal/diff?sessionId=" + encodeURIComponent(sid);
2237
+ if (typeof fetch === "function") {
2238
+ fetch(url)
2239
+ .then((res) => {
2240
+ if (!res.ok) throw new Error("HTTP " + res.status);
2241
+ return res.json();
2242
+ })
2243
+ .then((data) => {
2244
+ if (data && data.ok) {
2245
+ setDiffData(data);
2246
+ } else {
2247
+ setDiffError((data && data.error) || "Failed to load diff");
2248
+ }
2249
+ })
2250
+ .catch((err) => {
2251
+ setDiffError(err.message || "Network error");
2252
+ })
2253
+ .finally(() => {
2254
+ setDiffLoading(false);
2255
+ });
2256
+ } else {
2257
+ setDiffLoading(false);
2258
+ }
2259
+ }, [state.sessionId]);
2260
+
2261
+ useEffect(() => {
2262
+ if (activeTab === "diff") {
2263
+ fetchDiff();
2264
+ }
2265
+ }, [activeTab, fetchDiff]);
2266
+
2267
+ const renderDiffBox = () => {
2268
+ return React.createElement(
2269
+ "div",
2270
+ { className: "dsh-goal-diff-box" },
2271
+ React.createElement(
2272
+ "div",
2273
+ { className: "dsh-goal-diff-summary" },
2274
+ React.createElement(
2275
+ "div",
2276
+ { style: { display: "flex", alignItems: "center", gap: 8 } },
2277
+ React.createElement("span", null, (t("diffBaseCommit") || "Base:") + " ", React.createElement("code", null, (diffData && diffData.base ? diffData.base : (state.gitStartCommit || "main")).slice(0, 7))),
2278
+ React.createElement("span", null, "→"),
2279
+ React.createElement("span", null, (t("diffTargetBranch") || "Target:") + " ", React.createElement("code", null, (diffData && diffData.target) || state.gitBranch || state.branchName || "HEAD"))
2280
+ ),
2281
+ React.createElement(
2282
+ "div",
2283
+ { style: { display: "flex", alignItems: "center", gap: 8 } },
2284
+ diffData && diffData.stats ? React.createElement(
2285
+ "span",
2286
+ { className: "dsh-goal-diff-stat-pill" },
2287
+ React.createElement("span", { className: "dsh-goal-diff-ins" }, "+" + diffData.stats.insertions),
2288
+ React.createElement("span", { className: "dsh-goal-diff-del" }, "-" + diffData.stats.deletions)
2289
+ ) : null,
2290
+ React.createElement(
2291
+ "button",
2292
+ {
2293
+ type: "button",
2294
+ className: "dsh-goal-btn icon-only",
2295
+ style: { padding: "2px 8px", fontSize: 11 },
2296
+ onClick: fetchDiff,
2297
+ title: t("diffRefresh") || "Refresh Diff",
2298
+ },
2299
+ "🔄"
2300
+ )
2301
+ )
2302
+ ),
2303
+ diffData && diffData.files && diffData.files.length > 0 ? React.createElement(
2304
+ "div",
2305
+ { className: "dsh-goal-diff-files-list" },
2306
+ React.createElement(
2307
+ "span",
2308
+ {
2309
+ className: `dsh-goal-diff-file-chip${selectedFile === null ? " active" : ""}`,
2310
+ onClick: () => setSelectedFile(null),
2311
+ },
2312
+ (t("allFiles") || "All Files") + ` (${diffData.files.length})`
2313
+ ),
2314
+ diffData.files.map((f) => React.createElement(
2315
+ "span",
2316
+ {
2317
+ key: f.path,
2318
+ className: `dsh-goal-diff-file-chip${selectedFile === f.path ? " active" : ""}`,
2319
+ onClick: () => setSelectedFile(selectedFile === f.path ? null : f.path),
2320
+ title: `${f.path} (+${f.insertions} / -${f.deletions})`,
2321
+ },
2322
+ `${f.path.split("/").pop()} `,
2323
+ React.createElement("span", { className: "dsh-goal-diff-ins" }, "+" + f.insertions),
2324
+ "/",
2325
+ React.createElement("span", { className: "dsh-goal-diff-del" }, "-" + f.deletions)
2326
+ ))
2327
+ ) : null,
2328
+ diffLoading ? React.createElement("div", { className: "dsh-goal-diff-empty" }, t("diffLoading") || "Loading diff...")
2329
+ : diffError ? React.createElement("div", { className: "dsh-goal-diff-empty dsh-goal-gate-error" }, "Error: " + diffError)
2330
+ : !diffData || !diffData.diff || diffData.diff.trim().length === 0 ? React.createElement("div", { className: "dsh-goal-diff-empty" }, t("diffNoChanges") || "No git changes detected relative to base commit.")
2331
+ : React.createElement(
2332
+ "div",
2333
+ { className: "dsh-goal-diff-viewer" },
2334
+ (() => {
2335
+ const allLines = diffData.diff.split("\n");
2336
+ let linesToShow = allLines;
2337
+ if (selectedFile) {
2338
+ const fileChunks = [];
2339
+ let collecting = false;
2340
+ for (const l of allLines) {
2341
+ if (l.startsWith("diff --git ")) {
2342
+ const bPart = l.split(" b/")[1] || "";
2343
+ collecting = bPart === selectedFile;
2344
+ }
2345
+ if (collecting) fileChunks.push(l);
2346
+ }
2347
+ if (fileChunks.length > 0) linesToShow = fileChunks;
2348
+ }
2349
+ return linesToShow.map((line, idx) => {
2350
+ let lineClass = "dsh-goal-diff-line";
2351
+ if (line.startsWith("+") && !line.startsWith("+++")) {
2352
+ lineClass += " dsh-goal-diff-line-add";
2353
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
2354
+ lineClass += " dsh-goal-diff-line-del";
2355
+ } else if (line.startsWith("@@")) {
2356
+ lineClass += " dsh-goal-diff-line-hunk";
2357
+ } else if (line.startsWith("diff --git") || line.startsWith("index ") || line.startsWith("---") || line.startsWith("+++")) {
2358
+ lineClass += " dsh-goal-diff-line-header";
2359
+ }
2360
+ return React.createElement("span", { key: idx, className: lineClass }, line || " ");
2361
+ });
2362
+ })()
2363
+ )
2364
+ );
2365
+ };
1960
2366
 
1961
2367
  const totalTokens = state.tokensUsage?.totalTokens || 0;
1962
2368
 
@@ -2076,7 +2482,35 @@ window.__ModuleLoader__.load({
2076
2482
 
2077
2483
  ),
2078
2484
 
2079
- isPlanMode && !isCompleted
2485
+ React.createElement(
2486
+ 'div',
2487
+ { className: 'dsh-goal-tabs' },
2488
+ React.createElement(
2489
+ 'button',
2490
+ {
2491
+ type: 'button',
2492
+ className: `dsh-goal-tab-btn${activeTab === "overview" ? " active" : ""}`,
2493
+ onClick: () => setActiveTab("overview"),
2494
+ },
2495
+ "📋 " + (t("tabOverview") || "Plan & Overview")
2496
+ ),
2497
+ React.createElement(
2498
+ 'button',
2499
+ {
2500
+ type: 'button',
2501
+ className: `dsh-goal-tab-btn${activeTab === "diff" ? " active" : ""}`,
2502
+ onClick: () => setActiveTab("diff"),
2503
+ },
2504
+ "🔍 " + (t("tabDiff") || "Changes / Diff") + (diffData && diffData.stats && diffData.stats.filesChanged ? ` (${diffData.stats.filesChanged})` : "")
2505
+ )
2506
+ ),
2507
+
2508
+ activeTab === 'diff'
2509
+ ? renderDiffBox()
2510
+ : React.createElement(
2511
+ React.Fragment,
2512
+ null,
2513
+ isPlanMode && !isCompleted
2080
2514
  ? React.createElement(
2081
2515
  'div',
2082
2516
  { className: 'dsh-goal-plan-banner' },
@@ -2570,7 +3004,8 @@ window.__ModuleLoader__.load({
2570
3004
  )
2571
3005
  )
2572
3006
  )
2573
- : null,
3007
+ : null
3008
+ ),
2574
3009
 
2575
3010
  ),
2576
3011
 
@@ -2736,6 +3171,8 @@ window.__ModuleLoader__.load({
2736
3171
 
2737
3172
  const [title, setTitle] = useState('');
2738
3173
  const [launchMode, setLaunchMode] = useState('act');
3174
+ const [activeQuickTpl, setActiveQuickTpl] = useState(null);
3175
+ const [quickTplVars, setQuickTplVars] = useState({});
2739
3176
  const [showTemplates, setShowTemplates] = useState(false);
2740
3177
  const [backendTemplates, setBackendTemplates] = useState([]);
2741
3178
  const [selectedTpl, setSelectedTpl] = useState(null);
@@ -2925,16 +3362,27 @@ window.__ModuleLoader__.load({
2925
3362
 
2926
3363
  type: 'button',
2927
3364
 
2928
- className: 'dsh-goal-template-chip',
3365
+ className: `dsh-goal-template-chip ${activeQuickTpl?.id === tmpl.id ? 'active' : ''}`,
3366
+ style: activeQuickTpl?.id === tmpl.id ? { borderColor: 'var(--dsw-alias-brand-primary)', fontWeight: 600 } : {},
2929
3367
 
2930
3368
  title: prefix,
2931
3369
 
2932
3370
  onClick: () => {
2933
-
2934
- setTitle(prefix);
2935
-
3371
+ const isSelected = activeQuickTpl?.id === tmpl.id;
3372
+ if (isSelected) {
3373
+ setActiveQuickTpl(null);
3374
+ setQuickTplVars({});
3375
+ } else {
3376
+ setActiveQuickTpl(tmpl);
3377
+ const initial = {};
3378
+ (tmpl.variables || []).forEach((v) => { initial[v.id] = ""; });
3379
+ setQuickTplVars(initial);
3380
+ const generated = typeof formatQuickLaunchPrompt === "function"
3381
+ ? formatQuickLaunchPrompt(tmpl, initial, lang)
3382
+ : prefix;
3383
+ setTitle(generated || prefix);
3384
+ }
2936
3385
  if (inputRef.current) inputRef.current.focus();
2937
-
2938
3386
  },
2939
3387
 
2940
3388
  },
@@ -2947,6 +3395,75 @@ window.__ModuleLoader__.load({
2947
3395
 
2948
3396
  ),
2949
3397
 
3398
+ activeQuickTpl?.variables && activeQuickTpl.variables.length > 0
3399
+ ? React.createElement(
3400
+ 'div',
3401
+ {
3402
+ className: 'dsh-goal-tpl-vars-box',
3403
+ style: {
3404
+ background: 'var(--dsw-alias-bg-sunken)',
3405
+ border: '1px solid var(--dsw-alias-border-l1)',
3406
+ borderRadius: 6,
3407
+ padding: '8px 10px',
3408
+ marginBottom: 10,
3409
+ },
3410
+ },
3411
+ React.createElement(
3412
+ 'div',
3413
+ {
3414
+ style: {
3415
+ fontSize: 11,
3416
+ fontWeight: 600,
3417
+ color: 'var(--dsw-alias-label-secondary)',
3418
+ marginBottom: 6,
3419
+ display: 'flex',
3420
+ alignItems: 'center',
3421
+ justifyContent: 'space-between',
3422
+ },
3423
+ },
3424
+ React.createElement('span', null, `${activeQuickTpl.icon} ${activeQuickTpl.label[lang] || activeQuickTpl.label.en} ${t('quickTplVariablesTitle') || 'Parameters:'}`),
3425
+ React.createElement(
3426
+ 'button',
3427
+ {
3428
+ type: 'button',
3429
+ className: 'dsh-goal-btn icon-only',
3430
+ style: { fontSize: 10, padding: '2px 4px', height: 'auto' },
3431
+ onClick: () => { setActiveQuickTpl(null); setQuickTplVars({}); },
3432
+ },
3433
+ '✕'
3434
+ )
3435
+ ),
3436
+ activeQuickTpl.variables.map((v) => {
3437
+ const vLabel = v.label ? (v.label[lang] || v.label.en) : v.id;
3438
+ return React.createElement(
3439
+ 'div',
3440
+ { key: v.id, style: { marginBottom: 6 } },
3441
+ React.createElement(
3442
+ 'label',
3443
+ { style: { fontSize: 11, display: 'block', color: 'var(--dsw-alias-label-primary)', marginBottom: 2 } },
3444
+ vLabel,
3445
+ v.required ? React.createElement('span', { style: { color: 'var(--dsw-alias-state-danger-primary)', marginLeft: 3 } }, '*') : null
3446
+ ),
3447
+ React.createElement('input', {
3448
+ type: 'text',
3449
+ className: 'dsh-goal-card-input',
3450
+ style: { width: '100%', boxSizing: 'border-box', fontSize: 12, padding: '4px 8px' },
3451
+ placeholder: v.placeholder || '',
3452
+ value: quickTplVars[v.id] || '',
3453
+ onChange: (e) => {
3454
+ const updated = { ...quickTplVars, [v.id]: e.target.value };
3455
+ setQuickTplVars(updated);
3456
+ const generated = typeof formatQuickLaunchPrompt === 'function'
3457
+ ? formatQuickLaunchPrompt(activeQuickTpl, updated, lang)
3458
+ : activeQuickTpl.prefix[lang] || activeQuickTpl.prefix.en;
3459
+ setTitle(generated);
3460
+ },
3461
+ })
3462
+ );
3463
+ })
3464
+ )
3465
+ : null,
3466
+
2950
3467
  React.createElement('input', {
2951
3468
 
2952
3469
  ref: inputRef,
@@ -3177,6 +3694,7 @@ window.__ModuleLoader__.load({
3177
3694
  const [isModalOpen, setIsModalOpen] = useState(false);
3178
3695
 
3179
3696
  const [isQuickLaunchOpen, setIsQuickLaunchOpen] = useState(false);
3697
+ const [operatorAnswerInput, setOperatorAnswerInput] = useState('');
3180
3698
 
3181
3699
  const [isDockCollapsed, setIsDockCollapsed] = useState(() => {
3182
3700
  try {
@@ -3701,6 +4219,7 @@ window.__ModuleLoader__.load({
3701
4219
  const isCompleted = state.state === 'COMPLETED';
3702
4220
 
3703
4221
  const isPlanMode = state.mode === 'plan' || state.state === 'PLANNING';
4222
+ const isWaitingOperator = Boolean(state.waitingForOperator && state.operatorQuery);
3704
4223
 
3705
4224
  const formatElapsed = (totalSeconds) => {
3706
4225
  const s = Math.max(0, Math.floor(Number(totalSeconds) || 0));
@@ -3756,8 +4275,8 @@ window.__ModuleLoader__.load({
3756
4275
  ),
3757
4276
  React.createElement(
3758
4277
  'span',
3759
- { className: `dsh-goal-badge ${isCompleted ? 'dsh-goal-badge-ok' : isPlanMode ? 'dsh-goal-badge-plan' : isPaused ? 'dsh-goal-badge-warn' : ''}` },
3760
- isCompleted ? (t('goalCompleted') || 'Done') : isPlanMode ? (t('planMode') || 'Plan Mode') : isPaused ? (t('paused') || 'Paused') : (t('goalLabel') || 'Goal')
4278
+ { className: `dsh-goal-badge ${isCompleted ? 'dsh-goal-badge-ok' : isWaitingOperator ? 'dsh-goal-badge-warn' : isPlanMode ? 'dsh-goal-badge-plan' : isPaused ? 'dsh-goal-badge-warn' : ''}` },
4279
+ isCompleted ? (t('goalCompleted') || 'Done') : isWaitingOperator ? ('❓ ' + (t('operatorInputRequired') || 'Input Needed')) : isPlanMode ? (t('planMode') || 'Plan Mode') : isPaused ? (t('paused') || 'Paused') : (t('goalLabel') || 'Goal')
3761
4280
  ),
3762
4281
  React.createElement('span', { className: 'dsh-goal-title', title: state.title }, state.title),
3763
4282
  React.createElement('span', { className: 'dsh-goal-time' }, `⏱ ${liveElapsedStr}`),
@@ -3965,6 +4484,112 @@ window.__ModuleLoader__.load({
3965
4484
 
3966
4485
  ),
3967
4486
 
4487
+ isWaitingOperator
4488
+ ? React.createElement(
4489
+ 'div',
4490
+ {
4491
+ className: 'dsh-goal-operator-card',
4492
+ style: {
4493
+ background: 'var(--dsw-alias-bg-sunken)',
4494
+ border: '1px solid var(--dsw-alias-state-warning-primary)',
4495
+ borderRadius: 6,
4496
+ padding: '10px 12px',
4497
+ marginTop: 8,
4498
+ },
4499
+ },
4500
+ React.createElement(
4501
+ 'div',
4502
+ {
4503
+ style: {
4504
+ display: 'flex',
4505
+ alignItems: 'center',
4506
+ gap: 6,
4507
+ fontSize: 12,
4508
+ fontWeight: 600,
4509
+ color: 'var(--dsw-alias-state-warning-primary)',
4510
+ marginBottom: 4,
4511
+ },
4512
+ },
4513
+ React.createElement('span', null, '❓'),
4514
+ React.createElement('span', null, t('operatorInputRequired') || 'Operator Input Required')
4515
+ ),
4516
+ React.createElement(
4517
+ 'div',
4518
+ {
4519
+ style: {
4520
+ fontSize: 13,
4521
+ fontWeight: 500,
4522
+ color: 'var(--dsw-alias-label-primary)',
4523
+ marginBottom: 8,
4524
+ wordBreak: 'break-word',
4525
+ },
4526
+ },
4527
+ state.operatorQuery.question
4528
+ ),
4529
+ Array.isArray(state.operatorQuery.options) && state.operatorQuery.options.length > 0
4530
+ ? React.createElement(
4531
+ 'div',
4532
+ {
4533
+ className: 'dsh-goal-operator-options',
4534
+ style: { display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 8 },
4535
+ },
4536
+ state.operatorQuery.options.map((opt, idx) =>
4537
+ React.createElement(
4538
+ 'button',
4539
+ {
4540
+ key: idx,
4541
+ type: 'button',
4542
+ className: 'dsh-goal-btn',
4543
+ style: {
4544
+ fontSize: 12,
4545
+ padding: '4px 10px',
4546
+ borderColor: 'var(--dsw-alias-brand-primary)',
4547
+ color: 'var(--dsw-alias-brand-primary)',
4548
+ },
4549
+ onClick: () => handleAction('answer_operator', { answer: opt, optionIndex: idx }),
4550
+ },
4551
+ opt
4552
+ )
4553
+ )
4554
+ )
4555
+ : null,
4556
+ React.createElement(
4557
+ 'div',
4558
+ { style: { display: 'flex', gap: 6, alignItems: 'center' } },
4559
+ React.createElement('input', {
4560
+ type: 'text',
4561
+ className: 'dsh-goal-card-input',
4562
+ style: { flex: 1, fontSize: 12, padding: '4px 8px' },
4563
+ placeholder: t('answerPlaceholder') || 'Type your answer or select an option above...',
4564
+ value: operatorAnswerInput,
4565
+ onChange: (e) => setOperatorAnswerInput(e.target.value),
4566
+ onKeyDown: (e) => {
4567
+ if (e.key === 'Enter' && operatorAnswerInput.trim()) {
4568
+ e.preventDefault();
4569
+ handleAction('answer_operator', { answer: operatorAnswerInput.trim() });
4570
+ setOperatorAnswerInput('');
4571
+ }
4572
+ },
4573
+ }),
4574
+ React.createElement(
4575
+ 'button',
4576
+ {
4577
+ type: 'button',
4578
+ className: 'dsh-goal-btn dsh-goal-btn-primary',
4579
+ style: { fontSize: 12, padding: '4px 12px', whiteSpace: 'nowrap' },
4580
+ disabled: !operatorAnswerInput.trim(),
4581
+ onClick: () => {
4582
+ if (operatorAnswerInput.trim()) {
4583
+ handleAction('answer_operator', { answer: operatorAnswerInput.trim() });
4584
+ setOperatorAnswerInput('');
4585
+ }
4586
+ },
4587
+ },
4588
+ t('submitAnswer') || 'Submit'
4589
+ )
4590
+ )
4591
+ )
4592
+ : null
3968
4593
  ),
3969
4594
 
3970
4595
  ),
@@ -5081,6 +5706,12 @@ window.__ModuleLoader__.load({
5081
5706
  mergeBranch: 'Merge Branch',
5082
5707
  discardBranch: 'Discard Branch',
5083
5708
  retrospectiveTitle: 'Goal Retrospective',
5709
+ operatorInputRequired: 'Operator Input Required',
5710
+ operatorQuestion: 'Question from agent:',
5711
+ submitAnswer: 'Submit',
5712
+ answerPlaceholder: 'Type your answer or select an option above...',
5713
+ quickTplVariablesTitle: 'Parameters:',
5714
+ requiredField: 'Required',
5084
5715
  verificationGate: 'Acceptance gate',
5085
5716
  verificationPassed: 'Verified',
5086
5717
  verificationFailed: 'Gate failed',
@@ -5191,6 +5822,17 @@ window.__ModuleLoader__.load({
5191
5822
  planningPhase: 'Planning Phase (Read-Only)',
5192
5823
  planPhaseNotice: 'Agent is formulating the plan. File modifications are guarded until operator approval.',
5193
5824
  launchModeLabel: 'Launch Mode:',
5825
+ tabOverview: 'Plan & Overview',
5826
+ tabDiff: 'Changes / Diff',
5827
+ diffLoading: 'Loading diff...',
5828
+ diffNoChanges: 'No git changes detected relative to base commit.',
5829
+ diffFilesChanged: 'Files changed:',
5830
+ diffInsertions: 'insertions',
5831
+ diffDeletions: 'deletions',
5832
+ diffBaseCommit: 'Base:',
5833
+ diffTargetBranch: 'Target:',
5834
+ diffRefresh: 'Refresh Diff',
5835
+ allFiles: 'All Files',
5194
5836
  planModeDesc: 'Plan first, review milestones, then execute',
5195
5837
  actModeDesc: 'Direct execution right away',
5196
5838
 
@@ -5342,6 +5984,17 @@ window.__ModuleLoader__.load({
5342
5984
  planningPhase: '规划阶段(只读)',
5343
5985
  planPhaseNotice: '智能体正在制定方案与里程碑。在操作者批准前,禁止修改文件。',
5344
5986
  launchModeLabel: '启动模式:',
5987
+ tabOverview: '方案与概览',
5988
+ tabDiff: '代码变更 / Diff',
5989
+ diffLoading: '正在加载差异...',
5990
+ diffNoChanges: '未检测到相对于基线提交的代码变更。',
5991
+ diffFilesChanged: '变更文件数:',
5992
+ diffInsertions: '行新增',
5993
+ diffDeletions: '行删除',
5994
+ diffBaseCommit: '基线:',
5995
+ diffTargetBranch: '目标:',
5996
+ diffRefresh: '刷新 Diff',
5997
+ allFiles: '所有文件',
5345
5998
  planModeDesc: '先制定里程碑方案,人工确认后再执行',
5346
5999
  actModeDesc: '立即开始编码与任务执行',
5347
6000