@goodandready/dsh-goal 0.2.30 → 0.2.32
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 +502 -5
- package/lib/engine-compaction.js +126 -0
- package/lib/engine-diff.js +147 -0
- package/lib/engine-kanban-sync.js +242 -0
- package/lib/engine-prompt.js +37 -5
- package/lib/engine-subgoals.js +207 -0
- package/lib/goal-engine-constants.js +12 -0
- package/lib/goal-engine.js +38 -47
- package/lib/index.js +10 -7
- package/lib/routes.js +71 -1
- package/lib/tools.js +69 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1808,9 +1808,200 @@ window.__ModuleLoader__.load({
|
|
|
1808
1808
|
background: var(--dsw-alias-bg-layer-2);
|
|
1809
1809
|
font-family: monospace;
|
|
1810
1810
|
}
|
|
1811
|
-
|
|
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
|
+
}
|
|
1933
|
+
/* Sub-goals and Compaction (Issue 167 and Issue 170) */
|
|
1934
|
+
.dsh-goal-badge-subgoal {
|
|
1935
|
+
background: var(--dsw-alias-brand-bg, var(--dsw-alias-bg-hover));
|
|
1936
|
+
color: var(--dsw-alias-brand-primary, var(--dsw-alias-text-primary));
|
|
1937
|
+
border: 1px solid var(--dsw-alias-brand-border, var(--dsw-alias-border));
|
|
1938
|
+
}
|
|
1939
|
+
.dsh-goal-breadcrumbs {
|
|
1940
|
+
display: inline-flex;
|
|
1941
|
+
align-items: center;
|
|
1942
|
+
gap: 4px;
|
|
1943
|
+
font-size: 13px;
|
|
1944
|
+
color: var(--dsw-alias-label-secondary);
|
|
1945
|
+
}
|
|
1946
|
+
.dsh-goal-breadcrumb-item {
|
|
1947
|
+
color: var(--dsw-alias-label-secondary);
|
|
1948
|
+
}
|
|
1949
|
+
.dsh-goal-breadcrumb-item.active {
|
|
1950
|
+
color: var(--dsw-alias-text-primary);
|
|
1951
|
+
font-weight: 600;
|
|
1952
|
+
}
|
|
1953
|
+
.dsh-goal-crumb-sep {
|
|
1954
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1955
|
+
margin: 0 2px;
|
|
1956
|
+
}
|
|
1957
|
+
.dsh-goal-subgoals-box {
|
|
1958
|
+
display: flex;
|
|
1959
|
+
flex-direction: column;
|
|
1960
|
+
gap: 10px;
|
|
1961
|
+
padding: 4px 0;
|
|
1962
|
+
}
|
|
1963
|
+
.dsh-goal-subgoals-list {
|
|
1964
|
+
display: flex;
|
|
1965
|
+
flex-direction: column;
|
|
1966
|
+
gap: 8px;
|
|
1967
|
+
}
|
|
1968
|
+
.dsh-goal-subgoal-card {
|
|
1969
|
+
background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-hover));
|
|
1970
|
+
border: 1px solid var(--dsw-alias-border);
|
|
1971
|
+
border-radius: 6px;
|
|
1972
|
+
padding: 10px 12px;
|
|
1973
|
+
display: flex;
|
|
1974
|
+
flex-direction: column;
|
|
1975
|
+
gap: 4px;
|
|
1976
|
+
}
|
|
1977
|
+
.dsh-goal-subgoal-header {
|
|
1978
|
+
display: flex;
|
|
1979
|
+
align-items: center;
|
|
1980
|
+
justify-content: space-between;
|
|
1981
|
+
gap: 8px;
|
|
1982
|
+
}
|
|
1983
|
+
.dsh-goal-subgoal-title {
|
|
1984
|
+
font-weight: 600;
|
|
1985
|
+
font-size: 13px;
|
|
1986
|
+
color: var(--dsw-alias-text-primary);
|
|
1987
|
+
}
|
|
1988
|
+
.dsh-goal-subgoal-summary {
|
|
1989
|
+
font-size: 12px;
|
|
1990
|
+
color: var(--dsw-alias-label-secondary);
|
|
1991
|
+
}
|
|
1992
|
+
.dsh-goal-compacted-pill {
|
|
1993
|
+
display: inline-flex;
|
|
1994
|
+
align-items: center;
|
|
1995
|
+
gap: 3px;
|
|
1996
|
+
font-size: 10px;
|
|
1997
|
+
padding: 1px 5px;
|
|
1998
|
+
border-radius: 3px;
|
|
1999
|
+
background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-hover));
|
|
2000
|
+
color: var(--dsw-alias-state-success-primary, var(--dsw-alias-text-secondary));
|
|
2001
|
+
border: 1px solid var(--dsw-alias-border);
|
|
2002
|
+
margin-left: 6px;
|
|
2003
|
+
}
|
|
1812
2004
|
`;
|
|
1813
|
-
|
|
1814
2005
|
function injectStylesOnce() {
|
|
1815
2006
|
|
|
1816
2007
|
if (typeof document === 'undefined') return;
|
|
@@ -2103,6 +2294,193 @@ window.__ModuleLoader__.load({
|
|
|
2103
2294
|
const [interveneText, setInterveneText] = useState('');
|
|
2104
2295
|
|
|
2105
2296
|
const [interveneSent, setInterveneSent] = useState(false);
|
|
2297
|
+
const [activeTab, setActiveTab] = useState("overview");
|
|
2298
|
+
const [diffData, setDiffData] = useState(null);
|
|
2299
|
+
const [diffLoading, setDiffLoading] = useState(false);
|
|
2300
|
+
const [diffError, setDiffError] = useState(null);
|
|
2301
|
+
const [selectedFile, setSelectedFile] = useState(null);
|
|
2302
|
+
|
|
2303
|
+
const fetchDiff = useCallback(() => {
|
|
2304
|
+
setDiffLoading(true);
|
|
2305
|
+
setDiffError(null);
|
|
2306
|
+
const sid = state.sessionId || "default";
|
|
2307
|
+
const url = "/dsh-goal/diff?sessionId=" + encodeURIComponent(sid);
|
|
2308
|
+
if (typeof fetch === "function") {
|
|
2309
|
+
fetch(url)
|
|
2310
|
+
.then((res) => {
|
|
2311
|
+
if (!res.ok) throw new Error("HTTP " + res.status);
|
|
2312
|
+
return res.json();
|
|
2313
|
+
})
|
|
2314
|
+
.then((data) => {
|
|
2315
|
+
if (data && data.ok) {
|
|
2316
|
+
setDiffData(data);
|
|
2317
|
+
} else {
|
|
2318
|
+
setDiffError((data && data.error) || "Failed to load diff");
|
|
2319
|
+
}
|
|
2320
|
+
})
|
|
2321
|
+
.catch((err) => {
|
|
2322
|
+
setDiffError(err.message || "Network error");
|
|
2323
|
+
})
|
|
2324
|
+
.finally(() => {
|
|
2325
|
+
setDiffLoading(false);
|
|
2326
|
+
});
|
|
2327
|
+
} else {
|
|
2328
|
+
setDiffLoading(false);
|
|
2329
|
+
}
|
|
2330
|
+
}, [state.sessionId]);
|
|
2331
|
+
|
|
2332
|
+
useEffect(() => {
|
|
2333
|
+
if (activeTab === "diff") {
|
|
2334
|
+
fetchDiff();
|
|
2335
|
+
}
|
|
2336
|
+
}, [activeTab, fetchDiff]);
|
|
2337
|
+
|
|
2338
|
+
const renderDiffBox = () => {
|
|
2339
|
+
return React.createElement(
|
|
2340
|
+
"div",
|
|
2341
|
+
{ className: "dsh-goal-diff-box" },
|
|
2342
|
+
React.createElement(
|
|
2343
|
+
"div",
|
|
2344
|
+
{ className: "dsh-goal-diff-summary" },
|
|
2345
|
+
React.createElement(
|
|
2346
|
+
"div",
|
|
2347
|
+
{ style: { display: "flex", alignItems: "center", gap: 8 } },
|
|
2348
|
+
React.createElement("span", null, (t("diffBaseCommit") || "Base:") + " ", React.createElement("code", null, (diffData && diffData.base ? diffData.base : (state.gitStartCommit || "main")).slice(0, 7))),
|
|
2349
|
+
React.createElement("span", null, "→"),
|
|
2350
|
+
React.createElement("span", null, (t("diffTargetBranch") || "Target:") + " ", React.createElement("code", null, (diffData && diffData.target) || state.gitBranch || state.branchName || "HEAD"))
|
|
2351
|
+
),
|
|
2352
|
+
React.createElement(
|
|
2353
|
+
"div",
|
|
2354
|
+
{ style: { display: "flex", alignItems: "center", gap: 8 } },
|
|
2355
|
+
diffData && diffData.stats ? React.createElement(
|
|
2356
|
+
"span",
|
|
2357
|
+
{ className: "dsh-goal-diff-stat-pill" },
|
|
2358
|
+
React.createElement("span", { className: "dsh-goal-diff-ins" }, "+" + diffData.stats.insertions),
|
|
2359
|
+
React.createElement("span", { className: "dsh-goal-diff-del" }, "-" + diffData.stats.deletions)
|
|
2360
|
+
) : null,
|
|
2361
|
+
React.createElement(
|
|
2362
|
+
"button",
|
|
2363
|
+
{
|
|
2364
|
+
type: "button",
|
|
2365
|
+
className: "dsh-goal-btn icon-only",
|
|
2366
|
+
style: { padding: "2px 8px", fontSize: 11 },
|
|
2367
|
+
onClick: fetchDiff,
|
|
2368
|
+
title: t("diffRefresh") || "Refresh Diff",
|
|
2369
|
+
},
|
|
2370
|
+
"🔄"
|
|
2371
|
+
)
|
|
2372
|
+
)
|
|
2373
|
+
),
|
|
2374
|
+
diffData && diffData.files && diffData.files.length > 0 ? React.createElement(
|
|
2375
|
+
"div",
|
|
2376
|
+
{ className: "dsh-goal-diff-files-list" },
|
|
2377
|
+
React.createElement(
|
|
2378
|
+
"span",
|
|
2379
|
+
{
|
|
2380
|
+
className: `dsh-goal-diff-file-chip${selectedFile === null ? " active" : ""}`,
|
|
2381
|
+
onClick: () => setSelectedFile(null),
|
|
2382
|
+
},
|
|
2383
|
+
(t("allFiles") || "All Files") + ` (${diffData.files.length})`
|
|
2384
|
+
),
|
|
2385
|
+
diffData.files.map((f) => React.createElement(
|
|
2386
|
+
"span",
|
|
2387
|
+
{
|
|
2388
|
+
key: f.path,
|
|
2389
|
+
className: `dsh-goal-diff-file-chip${selectedFile === f.path ? " active" : ""}`,
|
|
2390
|
+
onClick: () => setSelectedFile(selectedFile === f.path ? null : f.path),
|
|
2391
|
+
title: `${f.path} (+${f.insertions} / -${f.deletions})`,
|
|
2392
|
+
},
|
|
2393
|
+
`${f.path.split("/").pop()} `,
|
|
2394
|
+
React.createElement("span", { className: "dsh-goal-diff-ins" }, "+" + f.insertions),
|
|
2395
|
+
"/",
|
|
2396
|
+
React.createElement("span", { className: "dsh-goal-diff-del" }, "-" + f.deletions)
|
|
2397
|
+
))
|
|
2398
|
+
) : null,
|
|
2399
|
+
diffLoading ? React.createElement("div", { className: "dsh-goal-diff-empty" }, t("diffLoading") || "Loading diff...")
|
|
2400
|
+
: diffError ? React.createElement("div", { className: "dsh-goal-diff-empty dsh-goal-gate-error" }, "Error: " + diffError)
|
|
2401
|
+
: !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.")
|
|
2402
|
+
: React.createElement(
|
|
2403
|
+
"div",
|
|
2404
|
+
{ className: "dsh-goal-diff-viewer" },
|
|
2405
|
+
(() => {
|
|
2406
|
+
const allLines = diffData.diff.split("\n");
|
|
2407
|
+
let linesToShow = allLines;
|
|
2408
|
+
if (selectedFile) {
|
|
2409
|
+
const fileChunks = [];
|
|
2410
|
+
let collecting = false;
|
|
2411
|
+
for (const l of allLines) {
|
|
2412
|
+
if (l.startsWith("diff --git ")) {
|
|
2413
|
+
const bPart = l.split(" b/")[1] || "";
|
|
2414
|
+
collecting = bPart === selectedFile;
|
|
2415
|
+
}
|
|
2416
|
+
if (collecting) fileChunks.push(l);
|
|
2417
|
+
}
|
|
2418
|
+
if (fileChunks.length > 0) linesToShow = fileChunks;
|
|
2419
|
+
}
|
|
2420
|
+
return linesToShow.map((line, idx) => {
|
|
2421
|
+
let lineClass = "dsh-goal-diff-line";
|
|
2422
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
2423
|
+
lineClass += " dsh-goal-diff-line-add";
|
|
2424
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
2425
|
+
lineClass += " dsh-goal-diff-line-del";
|
|
2426
|
+
} else if (line.startsWith("@@")) {
|
|
2427
|
+
lineClass += " dsh-goal-diff-line-hunk";
|
|
2428
|
+
} else if (line.startsWith("diff --git") || line.startsWith("index ") || line.startsWith("---") || line.startsWith("+++")) {
|
|
2429
|
+
lineClass += " dsh-goal-diff-line-header";
|
|
2430
|
+
}
|
|
2431
|
+
return React.createElement("span", { key: idx, className: lineClass }, line || " ");
|
|
2432
|
+
});
|
|
2433
|
+
})()
|
|
2434
|
+
)
|
|
2435
|
+
);
|
|
2436
|
+
};
|
|
2437
|
+
|
|
2438
|
+
const renderSubGoalsBox = () => {
|
|
2439
|
+
const subList = Array.isArray(state.subGoals) ? state.subGoals : [];
|
|
2440
|
+
return React.createElement(
|
|
2441
|
+
'div',
|
|
2442
|
+
{ className: 'dsh-goal-subgoals-box' },
|
|
2443
|
+
state.isSubGoal
|
|
2444
|
+
? React.createElement(
|
|
2445
|
+
'div',
|
|
2446
|
+
{ className: 'dsh-goal-subgoal-card', style: { borderStyle: 'dashed' } },
|
|
2447
|
+
React.createElement('div', { style: { fontSize: 11, color: 'var(--dsw-alias-brand-primary)', fontWeight: 600 } }, '🌱 ' + (t('parentGoalBanner') || 'Child Sub-goal of:')),
|
|
2448
|
+
React.createElement('div', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, state.parentGoalId || 'Parent Goal')
|
|
2449
|
+
)
|
|
2450
|
+
: null,
|
|
2451
|
+
subList.length === 0
|
|
2452
|
+
? React.createElement('div', { className: 'dsh-goal-diff-empty' }, t('noSubGoals') || 'No sub-goals spawned yet.')
|
|
2453
|
+
: React.createElement(
|
|
2454
|
+
'div',
|
|
2455
|
+
{ className: 'dsh-goal-subgoals-list' },
|
|
2456
|
+
subList.map((sub, idx) =>
|
|
2457
|
+
React.createElement(
|
|
2458
|
+
'div',
|
|
2459
|
+
{ key: sub.id || sub.subGoalId || idx, className: 'dsh-goal-subgoal-card' },
|
|
2460
|
+
React.createElement(
|
|
2461
|
+
'div',
|
|
2462
|
+
{ className: 'dsh-goal-subgoal-header' },
|
|
2463
|
+
React.createElement('span', { className: 'dsh-goal-subgoal-title' }, sub.title),
|
|
2464
|
+
React.createElement(
|
|
2465
|
+
'span',
|
|
2466
|
+
{
|
|
2467
|
+
className: 'dsh-goal-badge ' + (sub.state === 'COMPLETED' ? 'dsh-goal-badge-ok' : sub.state === 'RUNNING' ? 'dsh-goal-badge-plan' : ''),
|
|
2468
|
+
},
|
|
2469
|
+
sub.state || 'PENDING'
|
|
2470
|
+
)
|
|
2471
|
+
),
|
|
2472
|
+
sub.resultSummary
|
|
2473
|
+
? React.createElement(
|
|
2474
|
+
'div',
|
|
2475
|
+
{ className: 'dsh-goal-subgoal-summary' },
|
|
2476
|
+
sub.resultSummary
|
|
2477
|
+
)
|
|
2478
|
+
: null
|
|
2479
|
+
)
|
|
2480
|
+
)
|
|
2481
|
+
)
|
|
2482
|
+
);
|
|
2483
|
+
};
|
|
2106
2484
|
|
|
2107
2485
|
const totalTokens = state.tokensUsage?.totalTokens || 0;
|
|
2108
2486
|
|
|
@@ -2222,7 +2600,46 @@ window.__ModuleLoader__.load({
|
|
|
2222
2600
|
|
|
2223
2601
|
),
|
|
2224
2602
|
|
|
2225
|
-
|
|
2603
|
+
React.createElement(
|
|
2604
|
+
'div',
|
|
2605
|
+
{ className: 'dsh-goal-tabs' },
|
|
2606
|
+
React.createElement(
|
|
2607
|
+
'button',
|
|
2608
|
+
{
|
|
2609
|
+
type: 'button',
|
|
2610
|
+
className: `dsh-goal-tab-btn${activeTab === "overview" ? " active" : ""}`,
|
|
2611
|
+
onClick: () => setActiveTab("overview"),
|
|
2612
|
+
},
|
|
2613
|
+
"📋 " + (t("tabOverview") || "Plan & Overview")
|
|
2614
|
+
),
|
|
2615
|
+
React.createElement(
|
|
2616
|
+
'button',
|
|
2617
|
+
{
|
|
2618
|
+
type: 'button',
|
|
2619
|
+
className: `dsh-goal-tab-btn${activeTab === "diff" ? " active" : ""}`,
|
|
2620
|
+
onClick: () => setActiveTab("diff"),
|
|
2621
|
+
},
|
|
2622
|
+
"🔍 " + (t("tabDiff") || "Changes / Diff") + (diffData && diffData.stats && diffData.stats.filesChanged ? ` (${diffData.stats.filesChanged})` : "")
|
|
2623
|
+
),
|
|
2624
|
+
React.createElement(
|
|
2625
|
+
'button',
|
|
2626
|
+
{
|
|
2627
|
+
type: 'button',
|
|
2628
|
+
className: `dsh-goal-tab-btn${activeTab === "subgoals" ? " active" : ""}`,
|
|
2629
|
+
onClick: () => setActiveTab("subgoals"),
|
|
2630
|
+
},
|
|
2631
|
+
"🌳 " + (t("tabSubGoals") || "Sub-goals") + (state.subGoals && state.subGoals.length > 0 ? ` (${state.subGoals.length})` : "")
|
|
2632
|
+
)
|
|
2633
|
+
),
|
|
2634
|
+
|
|
2635
|
+
activeTab === 'diff'
|
|
2636
|
+
? renderDiffBox()
|
|
2637
|
+
: activeTab === 'subgoals'
|
|
2638
|
+
? renderSubGoalsBox()
|
|
2639
|
+
: React.createElement(
|
|
2640
|
+
React.Fragment,
|
|
2641
|
+
null,
|
|
2642
|
+
isPlanMode && !isCompleted
|
|
2226
2643
|
? React.createElement(
|
|
2227
2644
|
'div',
|
|
2228
2645
|
{ className: 'dsh-goal-plan-banner' },
|
|
@@ -2547,6 +2964,13 @@ window.__ModuleLoader__.load({
|
|
|
2547
2964
|
},
|
|
2548
2965
|
|
|
2549
2966
|
`${i + 1}. ${m.title}`,
|
|
2967
|
+
m.compactionSummary
|
|
2968
|
+
? React.createElement(
|
|
2969
|
+
'span',
|
|
2970
|
+
{ className: 'dsh-goal-compacted-pill', title: m.compactionSummary },
|
|
2971
|
+
'⚡ ' + (t('compactedBadge') || 'Compacted')
|
|
2972
|
+
)
|
|
2973
|
+
: null,
|
|
2550
2974
|
m.verificationCommand
|
|
2551
2975
|
? React.createElement(
|
|
2552
2976
|
'span',
|
|
@@ -2716,7 +3140,8 @@ window.__ModuleLoader__.load({
|
|
|
2716
3140
|
)
|
|
2717
3141
|
)
|
|
2718
3142
|
)
|
|
2719
|
-
: null
|
|
3143
|
+
: null
|
|
3144
|
+
),
|
|
2720
3145
|
|
|
2721
3146
|
),
|
|
2722
3147
|
|
|
@@ -4068,7 +4493,28 @@ window.__ModuleLoader__.load({
|
|
|
4068
4493
|
|
|
4069
4494
|
),
|
|
4070
4495
|
|
|
4071
|
-
|
|
4496
|
+
state.isSubGoal
|
|
4497
|
+
? React.createElement(
|
|
4498
|
+
'span',
|
|
4499
|
+
{ className: 'dsh-goal-badge dsh-goal-badge-subgoal', title: t('subGoalBadge') || 'Sub-goal' },
|
|
4500
|
+
'🌱 ' + (t('subGoalBadge') || 'Sub-goal')
|
|
4501
|
+
)
|
|
4502
|
+
: null,
|
|
4503
|
+
|
|
4504
|
+
state.breadcrumbs && state.breadcrumbs.length > 1
|
|
4505
|
+
? React.createElement(
|
|
4506
|
+
'span',
|
|
4507
|
+
{ className: 'dsh-goal-breadcrumbs', title: state.title },
|
|
4508
|
+
state.breadcrumbs.map((crumb, idx) =>
|
|
4509
|
+
React.createElement(
|
|
4510
|
+
'span',
|
|
4511
|
+
{ key: crumb.sessionId || idx, className: 'dsh-goal-breadcrumb-item' + (crumb.isCurrent ? ' active' : '') },
|
|
4512
|
+
idx > 0 && React.createElement('span', { className: 'dsh-goal-crumb-sep' }, ' / '),
|
|
4513
|
+
crumb.title
|
|
4514
|
+
)
|
|
4515
|
+
)
|
|
4516
|
+
)
|
|
4517
|
+
: React.createElement('span', { className: 'dsh-goal-title', title: state.title }, state.title),
|
|
4072
4518
|
|
|
4073
4519
|
),
|
|
4074
4520
|
|
|
@@ -4078,6 +4524,19 @@ window.__ModuleLoader__.load({
|
|
|
4078
4524
|
|
|
4079
4525
|
{ className: 'dsh-goal-right' },
|
|
4080
4526
|
|
|
4527
|
+
state.subGoals && state.subGoals.length > 0
|
|
4528
|
+
? React.createElement(
|
|
4529
|
+
'span',
|
|
4530
|
+
{
|
|
4531
|
+
className: 'dsh-goal-stat-pill',
|
|
4532
|
+
style: { cursor: 'pointer' },
|
|
4533
|
+
title: t('subGoalsTitle') || 'Sub-goals',
|
|
4534
|
+
onClick: () => setIsModalOpen(true),
|
|
4535
|
+
},
|
|
4536
|
+
'🌿 ' + state.subGoals.filter((s) => s.state === 'COMPLETED').length + '/' + state.subGoals.length + ' ' + (t('subGoalsPill') || 'sub-goals')
|
|
4537
|
+
)
|
|
4538
|
+
: null,
|
|
4539
|
+
|
|
4081
4540
|
React.createElement(
|
|
4082
4541
|
|
|
4083
4542
|
'span',
|
|
@@ -5533,8 +5992,27 @@ window.__ModuleLoader__.load({
|
|
|
5533
5992
|
planningPhase: 'Planning Phase (Read-Only)',
|
|
5534
5993
|
planPhaseNotice: 'Agent is formulating the plan. File modifications are guarded until operator approval.',
|
|
5535
5994
|
launchModeLabel: 'Launch Mode:',
|
|
5995
|
+
tabOverview: 'Plan & Overview',
|
|
5996
|
+
tabDiff: 'Changes / Diff',
|
|
5997
|
+
diffLoading: 'Loading diff...',
|
|
5998
|
+
diffNoChanges: 'No git changes detected relative to base commit.',
|
|
5999
|
+
diffFilesChanged: 'Files changed:',
|
|
6000
|
+
diffInsertions: 'insertions',
|
|
6001
|
+
diffDeletions: 'deletions',
|
|
6002
|
+
diffBaseCommit: 'Base:',
|
|
6003
|
+
diffTargetBranch: 'Target:',
|
|
6004
|
+
diffRefresh: 'Refresh Diff',
|
|
6005
|
+
allFiles: 'All Files',
|
|
5536
6006
|
planModeDesc: 'Plan first, review milestones, then execute',
|
|
5537
6007
|
actModeDesc: 'Direct execution right away',
|
|
6008
|
+
tabSubGoals: 'Sub-goals',
|
|
6009
|
+
subGoalsTitle: 'Hierarchical Sub-goals',
|
|
6010
|
+
subGoalBadge: 'Sub-goal',
|
|
6011
|
+
subGoalsPill: 'sub-goals',
|
|
6012
|
+
noSubGoals: 'No sub-goals spawned yet. Model can spawn sub-goals via goal_spawn_subgoal tool.',
|
|
6013
|
+
parentGoalBanner: 'Child Sub-goal of:',
|
|
6014
|
+
compactedBadge: 'Compacted',
|
|
6015
|
+
compactionTooltip: 'History compacted to reduce context footprint',
|
|
5538
6016
|
|
|
5539
6017
|
},
|
|
5540
6018
|
|
|
@@ -5684,8 +6162,27 @@ window.__ModuleLoader__.load({
|
|
|
5684
6162
|
planningPhase: '规划阶段(只读)',
|
|
5685
6163
|
planPhaseNotice: '智能体正在制定方案与里程碑。在操作者批准前,禁止修改文件。',
|
|
5686
6164
|
launchModeLabel: '启动模式:',
|
|
6165
|
+
tabOverview: '方案与概览',
|
|
6166
|
+
tabDiff: '代码变更 / Diff',
|
|
6167
|
+
diffLoading: '正在加载差异...',
|
|
6168
|
+
diffNoChanges: '未检测到相对于基线提交的代码变更。',
|
|
6169
|
+
diffFilesChanged: '变更文件数:',
|
|
6170
|
+
diffInsertions: '行新增',
|
|
6171
|
+
diffDeletions: '行删除',
|
|
6172
|
+
diffBaseCommit: '基线:',
|
|
6173
|
+
diffTargetBranch: '目标:',
|
|
6174
|
+
diffRefresh: '刷新 Diff',
|
|
6175
|
+
allFiles: '所有文件',
|
|
5687
6176
|
planModeDesc: '先制定里程碑方案,人工确认后再执行',
|
|
5688
6177
|
actModeDesc: '立即开始编码与任务执行',
|
|
6178
|
+
tabSubGoals: '子目标',
|
|
6179
|
+
subGoalsTitle: '分层子目标管理',
|
|
6180
|
+
subGoalBadge: '子目标',
|
|
6181
|
+
subGoalsPill: '个子目标',
|
|
6182
|
+
noSubGoals: '当前任务暂无衍生子目标。模型可通过 goal_spawn_subgoal 工具动态创建独立子目标。',
|
|
6183
|
+
parentGoalBanner: '衍生自主目标:',
|
|
6184
|
+
compactedBadge: '已压缩',
|
|
6185
|
+
compactionTooltip: '阶段历史已自动精简压缩以优化上下文窗口',
|
|
5689
6186
|
|
|
5690
6187
|
},
|
|
5691
6188
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Milestone Context Compaction for DSH Goal Engine (Issue #167).
|
|
3
|
+
* Compresses verbose raw logs and full checklist traces of completed milestones
|
|
4
|
+
* into high-density summaries, cutting context token footprint by > 40%.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Compact a single milestone upon completion into a structured summary
|
|
9
|
+
* @param {object} milestone
|
|
10
|
+
* @param {object} [options]
|
|
11
|
+
* @returns {object}
|
|
12
|
+
*/
|
|
13
|
+
export function compactMilestone(milestone, options = {}) {
|
|
14
|
+
if (!milestone) return null;
|
|
15
|
+
|
|
16
|
+
const id = milestone.id ? String(milestone.id).toUpperCase() : 'M';
|
|
17
|
+
const title = milestone.title || 'Milestone';
|
|
18
|
+
const notes = milestone.notes ? String(milestone.notes).trim() : '';
|
|
19
|
+
|
|
20
|
+
let checklistPart = '';
|
|
21
|
+
if (Array.isArray(milestone.checklist) && milestone.checklist.length > 0) {
|
|
22
|
+
const doneCount = milestone.checklist.filter((item) => item && item.done).length;
|
|
23
|
+
checklistPart = ` [${doneCount}/${milestone.checklist.length} subtasks]`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let gatePart = '';
|
|
27
|
+
if (milestone.verificationCommand) {
|
|
28
|
+
gatePart = milestone.verificationPassed ? ' [Gate: verified]' : ' [Gate: passed]';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const outcomePart = notes ? ` (Outcome: ${notes})` : '';
|
|
32
|
+
const summaryLine = `[${id} COMPLETED] ${title}${outcomePart}${checklistPart}${gatePart}`;
|
|
33
|
+
|
|
34
|
+
milestone.compactionSummary = summaryLine;
|
|
35
|
+
milestone.compactedAt = milestone.completedAt || Date.now();
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
id: milestone.id,
|
|
39
|
+
title,
|
|
40
|
+
notes,
|
|
41
|
+
summary: summaryLine,
|
|
42
|
+
completedAt: milestone.compactedAt,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Build compacted milestones history block for active prompt injection
|
|
48
|
+
* @param {Array<object>} milestones
|
|
49
|
+
* @param {string} [lang='en']
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
export function buildCompactedMilestonesHistory(milestones = [], lang = 'en') {
|
|
53
|
+
if (!Array.isArray(milestones) || milestones.length === 0) return '';
|
|
54
|
+
|
|
55
|
+
const completed = milestones.filter((m) => m && m.status === 'completed');
|
|
56
|
+
if (completed.length === 0) return '';
|
|
57
|
+
|
|
58
|
+
const header = lang === 'zh'
|
|
59
|
+
? '[已完成里程碑历史简报 / COMPACTED MILESTONES HISTORY]'
|
|
60
|
+
: '[COMPACTED MILESTONES HISTORY]';
|
|
61
|
+
|
|
62
|
+
const lines = completed.map((m) => {
|
|
63
|
+
if (m.compactionSummary) return `- ${m.compactionSummary}`;
|
|
64
|
+
const id = m.id ? String(m.id).toUpperCase() : 'M';
|
|
65
|
+
const outcome = m.notes ? ` (Outcome: ${m.notes})` : '';
|
|
66
|
+
return `- [${id} COMPLETED] ${m.title}${outcome}`;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return `${header}\n${lines.join('\n')}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format milestone into raw uncompacted text (for baseline comparison & active milestones)
|
|
74
|
+
* @param {object} m
|
|
75
|
+
* @param {number} idx
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
export function formatRawMilestoneTrace(m, idx = 0) {
|
|
79
|
+
if (!m) return '';
|
|
80
|
+
const statusStr = (m.status || 'pending').toUpperCase();
|
|
81
|
+
let line = ` ${idx + 1}. [${statusStr}] ${m.title || ''}${m.notes ? ` (${m.notes})` : ''}`;
|
|
82
|
+
if (m.verificationCommand) {
|
|
83
|
+
line += `\n [verification_command: ${m.verificationCommand}]`;
|
|
84
|
+
}
|
|
85
|
+
if (Array.isArray(m.checklist) && m.checklist.length > 0) {
|
|
86
|
+
const checkLines = m.checklist
|
|
87
|
+
.map((item) => ` [${item && item.done ? 'x' : ' '}] ${item && item.text ? item.text : ''}`)
|
|
88
|
+
.join('\n');
|
|
89
|
+
line += `\n${checkLines}`;
|
|
90
|
+
}
|
|
91
|
+
return line;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Estimate context savings achieved by compaction on completed milestones
|
|
96
|
+
* @param {Array<object>} milestones
|
|
97
|
+
* @param {string} [lang='en']
|
|
98
|
+
* @returns {{ completedCount: number, uncompactedChars: number, compactedChars: number, savedChars: number, savingsRatio: number, savingsPercent: string }}
|
|
99
|
+
*/
|
|
100
|
+
export function estimateCompactionSavings(milestones = [], lang = 'en') {
|
|
101
|
+
if (!Array.isArray(milestones) || milestones.length === 0) {
|
|
102
|
+
return { completedCount: 0, uncompactedChars: 0, compactedChars: 0, savedChars: 0, savingsRatio: 0, savingsPercent: '0%' };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const completed = milestones.filter((m) => m && m.status === 'completed');
|
|
106
|
+
if (completed.length === 0) {
|
|
107
|
+
return { completedCount: 0, uncompactedChars: 0, compactedChars: 0, savedChars: 0, savingsRatio: 0, savingsPercent: '0%' };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const uncompacted = completed.map((m, i) => formatRawMilestoneTrace(m, i)).join('\n');
|
|
111
|
+
const compacted = completed.map((m) => m.compactionSummary || `[${m.id ? String(m.id).toUpperCase() : 'M'} COMPLETED] ${m.title}`).join('\n');
|
|
112
|
+
|
|
113
|
+
const uncompactedChars = uncompacted.length;
|
|
114
|
+
const compactedChars = compacted.length;
|
|
115
|
+
const savedChars = Math.max(0, uncompactedChars - compactedChars);
|
|
116
|
+
const savingsRatio = uncompactedChars > 0 ? (savedChars / uncompactedChars) : 0;
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
completedCount: completed.length,
|
|
120
|
+
uncompactedChars,
|
|
121
|
+
compactedChars,
|
|
122
|
+
savedChars,
|
|
123
|
+
savingsRatio,
|
|
124
|
+
savingsPercent: `${Math.round(savingsRatio * 100)}%`,
|
|
125
|
+
};
|
|
126
|
+
}
|