dsh-gitbash-shell 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/index.js +137 -66
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-gitbash-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "Windows 全模式 Git Bash:用 Git for Windows bash 替换 PowerShell 执行器,并物化 standard/minimal/code/cordis 的 Git Bash 变体 preset。双 era 适配 dsh <= 0.1.1(内置 id 'code')与 >= 0.1.2(改名 'ptc'),启动探测自动选择,preset id 永不变。联动:dsh-ptc-cordis-preset(两者同装时 PTC 创造模式自动物化为 Git Bash 版)+ dsh-better-sidebar(终端 UI 标签与模型 terminal_* 工具自动切到 Git Bash)。",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gitbash-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Windows 上让 agent 的每条 shell 命令改经 Git for Windows bash 执行;物化四个内置预设的 Git Bash 变体,统一 MSYS 盘根路径方言,侧栏终端接管可开关。· Run every agent shell command through Git for Windows bash on Windows; materialize Git Bash variants of the built-in presets, unify the MSYS drive-root path dialect, and adopt the sidebar terminal behind a switch.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/index.js
CHANGED
|
@@ -756,13 +756,17 @@ export function readAdoptSidebar(ctxLike) {
|
|
|
756
756
|
}
|
|
757
757
|
}
|
|
758
758
|
|
|
759
|
-
// Windows absolute path -> MSYS drive-root form, for rewriting
|
|
760
|
-
//
|
|
761
|
-
//
|
|
762
|
-
//
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
|
|
759
|
+
// Windows absolute path -> MSYS drive-root form, for rewriting PROSE in place
|
|
760
|
+
// (v0.10.0). Quoted spaced paths ("C:\Program Files\Git") are matched whole; a bare
|
|
761
|
+
// path stops at a closing punctuation and crosses a space ONLY into a token that
|
|
762
|
+
// itself carries a separator — so "C:\Program Files\Git\bin" comes back whole (v0.23.0)
|
|
763
|
+
// while "see C:\Users\kanna for details" keeps its prose. An unquoted path whose LAST
|
|
764
|
+
// segment holds the space (…\my dir\f.txt") stays ambiguous in prose; the model meets such
|
|
765
|
+
// a path as a whole-value FIELD, which never goes through here (driveToMsys).
|
|
766
|
+
// The lookbehind set rejects URL schemes (https:), file:// forms, and anything
|
|
767
|
+
// already mid-word, so only real drive-letter paths are translated. Separators
|
|
768
|
+
// normalize to single slashes.
|
|
769
|
+
const BARE_WIN_PATH = /(?<![A-Za-z0-9:\\/"'`])([A-Za-z]):(?:\\|\/)([^\s"'`<>|),;:!?]+(?: [^\s"'`<>|),;:!?]*[\\/][^\s"'`<>|),;:!?]*)*)/g
|
|
766
770
|
const QUOTED_WIN_PATH = /(["'`])([A-Za-z]):(?:\\|\/)([^`]*?)\1/g
|
|
767
771
|
|
|
768
772
|
/** Rewrite every Windows absolute path in a text to the MSYS form; pure. */
|
|
@@ -785,7 +789,7 @@ export function windowsToMsys(text) {
|
|
|
785
789
|
function msysEcho(value, env) {
|
|
786
790
|
const out = windowsToMsys(value)
|
|
787
791
|
if (typeof out !== 'string' || out.length === 0) return out
|
|
788
|
-
const tempRoot = env && env.tmpDir ?
|
|
792
|
+
const tempRoot = env && env.tmpDir ? driveToMsys(env.tmpDir) : ''
|
|
789
793
|
if (!tempRoot) return out
|
|
790
794
|
return mountTempRoot(out, tempRoot)
|
|
791
795
|
}
|
|
@@ -829,6 +833,38 @@ function posixSeparators(value) {
|
|
|
829
833
|
: value
|
|
830
834
|
}
|
|
831
835
|
|
|
836
|
+
/**
|
|
837
|
+
* One WHOLE path value -> the MSYS dialect (v0.23.0). windowsToMsys is a PROSE
|
|
838
|
+
* rewriter: its bare pattern stops at whitespace, so a path whose DIRECTORY
|
|
839
|
+
* contains a space came back half-translated — "C:\my dir\f.txt" turned into
|
|
840
|
+
* "/c/my dir\f.txt", and the model met that mixed form in a SUCCESSFUL
|
|
841
|
+
* read/write/edit result (issue #3). A whole-value field IS exactly one path, so
|
|
842
|
+
* the drive prefix is translated directly and every remaining separator
|
|
843
|
+
* normalizes; no prose pattern is involved. Relative values (grep match paths)
|
|
844
|
+
* only normalize. A UNC root becomes the MSYS spelling "//server/share";
|
|
845
|
+
* anything without a drive prefix passes through unchanged. Pure.
|
|
846
|
+
*/
|
|
847
|
+
function driveToMsys(value) {
|
|
848
|
+
if (typeof value !== 'string' || value.length === 0) return value
|
|
849
|
+
const drive = /^([A-Za-z]):(?=[\\/])/.exec(value)
|
|
850
|
+
if (drive) return '/' + drive[1].toLowerCase() + '/' + posixSeparators(value.slice(2)).replace(/^\/+/, '')
|
|
851
|
+
return posixSeparators(value)
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* The MSYS echo of one whole path VALUE — never prose (v0.23.0): the drive root
|
|
856
|
+
* and separators through driveToMsys, then the /tmp mount. Every whole-value
|
|
857
|
+
* face (read/write/edit path, glob paths[], grep matches[].path, present
|
|
858
|
+
* files[].path) goes through here, so none of them can disagree about the
|
|
859
|
+
* dialect, and a spaced directory no longer leaks a backslash.
|
|
860
|
+
*/
|
|
861
|
+
function pathEcho(value, env) {
|
|
862
|
+
const out = driveToMsys(value)
|
|
863
|
+
if (typeof out !== 'string' || out.length === 0) return out
|
|
864
|
+
const tempRoot = env && env.tmpDir ? driveToMsys(env.tmpDir) : ''
|
|
865
|
+
return tempRoot ? mountTempRoot(out, tempRoot) : out
|
|
866
|
+
}
|
|
867
|
+
|
|
832
868
|
/**
|
|
833
869
|
* Rewrite Windows absolute paths inside an ERROR result's text blocks to
|
|
834
870
|
* the MSYS dialect (pure, v0.17.1). Error results cannot carry a
|
|
@@ -918,7 +954,11 @@ export function rewriteResultPaths(name, value, env) {
|
|
|
918
954
|
// v0.17.0: paths under the user TEMP directory echo back in the bash
|
|
919
955
|
// /tmp dialect (that IS what /tmp means in Git Bash), so the model sees
|
|
920
956
|
// the same short root bash itself prints for $TMP.
|
|
921
|
-
|
|
957
|
+
// v0.23.0: every value around here is ONE path, so it goes through
|
|
958
|
+
// pathEcho — the prose rewriter stops at whitespace and left a spaced
|
|
959
|
+
// directory half-translated (issue #3). posixSeparators now lives inside
|
|
960
|
+
// pathEcho, so the three nested branches below need no second pass.
|
|
961
|
+
const toMsys = (p) => pathEcho(p, env)
|
|
922
962
|
if (name === 'read' || name === 'read_image' || name === 'write' || name === 'edit') {
|
|
923
963
|
if (typeof value.path === 'string' && value.path !== '') {
|
|
924
964
|
const out = toMsys(value.path)
|
|
@@ -930,7 +970,7 @@ export function rewriteResultPaths(name, value, env) {
|
|
|
930
970
|
let changed = false
|
|
931
971
|
const files = value.files.map((file) => {
|
|
932
972
|
if (!file || typeof file !== 'object' || typeof file.path !== 'string' || file.path === '') return file
|
|
933
|
-
const out =
|
|
973
|
+
const out = toMsys(file.path)
|
|
934
974
|
if (out === file.path) return file
|
|
935
975
|
changed = true
|
|
936
976
|
return { ...file, path: out }
|
|
@@ -941,7 +981,7 @@ export function rewriteResultPaths(name, value, env) {
|
|
|
941
981
|
let changed = false
|
|
942
982
|
const paths = value.paths.map((p) => {
|
|
943
983
|
if (typeof p !== 'string') return p
|
|
944
|
-
const out =
|
|
984
|
+
const out = toMsys(p)
|
|
945
985
|
if (out === p) return p
|
|
946
986
|
changed = true
|
|
947
987
|
return out
|
|
@@ -952,7 +992,7 @@ export function rewriteResultPaths(name, value, env) {
|
|
|
952
992
|
let changed = false
|
|
953
993
|
const matches = value.matches.map((m) => {
|
|
954
994
|
if (!m || typeof m !== 'object' || typeof m.path !== 'string') return m
|
|
955
|
-
const out =
|
|
995
|
+
const out = toMsys(m.path)
|
|
956
996
|
if (out === m.path) return m
|
|
957
997
|
changed = true
|
|
958
998
|
return { ...m, path: out }
|
|
@@ -1858,90 +1898,121 @@ export async function apply(ctx, config = {}) {
|
|
|
1858
1898
|
// ── MSYS path translation on every tool dispatch (Windows only) ─────────
|
|
1859
1899
|
// Covers every preset and mode: the wrapper sits on the global
|
|
1860
1900
|
// tools/execute waterfall, through which model-direct calls, PTC run_code
|
|
1861
|
-
// sub-dispatches, and dynamic-tool calls all pass.
|
|
1901
|
+
// sub-dispatches, and dynamic-tool calls all pass. It carries BOTH dialect
|
|
1902
|
+
// faces of one call — the path-bearing ARGUMENTS on the way in, and the
|
|
1903
|
+
// path-bearing RESULT metadata on the way out.
|
|
1904
|
+
//
|
|
1905
|
+
// The result face rides HERE, not tools/post-execute (v0.23.0, issue #2).
|
|
1906
|
+
// The registry rejects a post-execute decision that replaces `value` while
|
|
1907
|
+
// any listener on that waterfall replaced `content` —
|
|
1908
|
+
// "tools/post-execute accept decision cannot replace both value and content",
|
|
1909
|
+
// raised after the waterfall settles and outside every listener try/catch,
|
|
1910
|
+
// so the call ended as an isError and the model lost the result. An
|
|
1911
|
+
// around-dispatch wrapper may author the result instead: the registry re-runs
|
|
1912
|
+
// the owning output contract on what we return (normalizeDispatchResult ->
|
|
1913
|
+
// createSuccessResult -> render), so the content the model reads is rendered
|
|
1914
|
+
// FROM the dialect value handed back, and this plugin never projects a
|
|
1915
|
+
// `value` on a decision at all — the collision cannot happen. An unchanged
|
|
1916
|
+
// value returns the ORIGINAL result object, so the registry re-renders
|
|
1917
|
+
// nothing. Only a SUCCESSFUL result is touched; failures keep their value
|
|
1918
|
+
// (and their dialect rides the post-execute listener below).
|
|
1862
1919
|
if (process.platform === 'win32') {
|
|
1863
1920
|
try {
|
|
1864
1921
|
ctx.on('tools/execute', (exec, next) => {
|
|
1922
|
+
let echo = false
|
|
1923
|
+
let echoEnv = null
|
|
1865
1924
|
try {
|
|
1866
1925
|
const dialect = readDialectSettings(ctx)
|
|
1867
|
-
if (
|
|
1926
|
+
if (dialect.posixPaths) {
|
|
1927
|
+
echo = true
|
|
1868
1928
|
const env = dialect.virtualMounts ? buildTranslateEnv(dialect.bashPath) : null
|
|
1869
|
-
|
|
1870
|
-
if (exec.
|
|
1871
|
-
|
|
1872
|
-
if (
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1929
|
+
echoEnv = env
|
|
1930
|
+
if (exec && exec.arguments && typeof exec.arguments === 'object') {
|
|
1931
|
+
let translated = translatePathArguments(exec.arguments, env)
|
|
1932
|
+
if (exec.name === 'glob' && dialect.globSplit) {
|
|
1933
|
+
const globTranslated = translateGlobArguments(translated, env)
|
|
1934
|
+
if (globTranslated !== translated) translated = globTranslated
|
|
1935
|
+
}
|
|
1936
|
+
// v0.20.0: a run_code program is DATA for a native Node process, so
|
|
1937
|
+
// a '/c/...' literal inside it never reached this layer and landed
|
|
1938
|
+
// on the current drive as '<drive>:\\c\\...'. The same mount table
|
|
1939
|
+
// now covers the program path literals (see rewriteCodePaths;
|
|
1940
|
+
// anything unclear leaves the code untouched).
|
|
1941
|
+
if (exec.name === 'run_code' && dialect.codePaths && typeof translated.code === 'string') {
|
|
1942
|
+
const rewritten = rewriteCodePaths(translated.code, env)
|
|
1943
|
+
const nextCode = programPrelude(env) + rewritten
|
|
1944
|
+
if (nextCode !== translated.code) translated = { ...translated, code: nextCode }
|
|
1945
|
+
}
|
|
1946
|
+
if (translated !== exec.arguments) exec.arguments = translated
|
|
1883
1947
|
}
|
|
1884
|
-
if (translated !== exec.arguments) exec.arguments = translated
|
|
1885
1948
|
}
|
|
1886
1949
|
} catch { /* never block a call on translation */ }
|
|
1887
|
-
return next()
|
|
1950
|
+
if (!echo) return next()
|
|
1951
|
+
return next().then((result) => {
|
|
1952
|
+
try {
|
|
1953
|
+
if (!result || typeof result !== 'object' || result.isError !== false) return result
|
|
1954
|
+
if (!result.value || typeof result.value !== 'object') return result
|
|
1955
|
+
const value = rewriteResultPaths(exec && exec.name, result.value, echoEnv)
|
|
1956
|
+
return value === result.value ? result : { ...result, value }
|
|
1957
|
+
} catch { return result }
|
|
1958
|
+
})
|
|
1888
1959
|
})
|
|
1889
|
-
console.log(TAG + ' MSYS
|
|
1960
|
+
console.log(TAG + ' MSYS path translation active on tool dispatch (arguments and result metadata)')
|
|
1890
1961
|
} catch (error) {
|
|
1891
1962
|
console.log(TAG + ' tools/execute wiring failed: ' + (error?.message ?? error))
|
|
1892
1963
|
}
|
|
1893
1964
|
}
|
|
1894
1965
|
|
|
1895
|
-
// ──
|
|
1896
|
-
//
|
|
1897
|
-
//
|
|
1898
|
-
//
|
|
1899
|
-
//
|
|
1900
|
-
//
|
|
1901
|
-
//
|
|
1966
|
+
// ── failure dialect on tools/post-execute (Windows only, gated; v0.17.1) ─
|
|
1967
|
+
// A FAILURE has two faces: `content` (the text the model reads on the failed
|
|
1968
|
+
// call, and what the UI card shows) and `error.message` (what the PTC bridge
|
|
1969
|
+
// hands a running program as its caught ToolCallError, while the durable
|
|
1970
|
+
// record keeps the structured identity). Rewriting only the content left a
|
|
1971
|
+
// program that printed a caught error holding the Windows form while every
|
|
1972
|
+
// other face said MSYS (v0.22.0).
|
|
1973
|
+
//
|
|
1974
|
+
// This listener projects CONTENT ONLY, and that is deliberate (v0.23.0,
|
|
1975
|
+
// issue #2): a decision carrying both `content` and `value` makes the
|
|
1976
|
+
// registry throw, and a replacement value is illegal on a failed result
|
|
1977
|
+
// anyway. The SUCCESS face therefore lives on the tools/execute wrapper
|
|
1978
|
+
// above, and this listener can never collide with any other plugin.
|
|
1979
|
+
//
|
|
1980
|
+
// error.message is rewritten IN PLACE on the result the waterfall was handed:
|
|
1981
|
+
// the registry keeps `result.error` by reference until the final
|
|
1982
|
+
// materialization, so this write reaches the PTC bridge and the record.
|
|
1983
|
+
// A post-execute `block` decision would rebuild the error as a bare message
|
|
1984
|
+
// and drop its structured identity — never do that.
|
|
1902
1985
|
if (process.platform === 'win32') {
|
|
1903
1986
|
try {
|
|
1904
1987
|
ctx.on('tools/post-execute', (exec, result, next) => {
|
|
1905
|
-
let patch
|
|
1906
1988
|
let contentPatch
|
|
1907
1989
|
try {
|
|
1908
1990
|
const dialect = readDialectSettings(ctx)
|
|
1909
|
-
if (dialect.posixPaths && result && typeof result === 'object'
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
const
|
|
1919
|
-
if (
|
|
1920
|
-
|
|
1921
|
-
const content = rewriteErrorContent(result.content, { nulHint: false, env })
|
|
1922
|
-
if (content !== result.content) contentPatch = content
|
|
1923
|
-
rewriteFailureMessage(result, env)
|
|
1924
|
-
} else if (ERROR_CONTENT_TOOLS.has(exec.name)) {
|
|
1925
|
-
const content = rewriteErrorContent(result.content, { env })
|
|
1926
|
-
if (content !== result.content) contentPatch = content
|
|
1927
|
-
rewriteFailureMessage(result, env)
|
|
1928
|
-
}
|
|
1991
|
+
if (dialect.posixPaths && dialect.errorDialect && exec && result && typeof result === 'object'
|
|
1992
|
+
&& result.isError === true && Array.isArray(result.content)) {
|
|
1993
|
+
const env = dialect.virtualMounts ? buildTranslateEnv(dialect.bashPath) : null
|
|
1994
|
+
if (exec.name === 'run_code') {
|
|
1995
|
+
// paths only — the /dev/null hint is for file tools
|
|
1996
|
+
const content = rewriteErrorContent(result.content, { nulHint: false, env })
|
|
1997
|
+
if (content !== result.content) contentPatch = content
|
|
1998
|
+
rewriteFailureMessage(result, env)
|
|
1999
|
+
} else if (ERROR_CONTENT_TOOLS.has(exec.name)) {
|
|
2000
|
+
const content = rewriteErrorContent(result.content, { env })
|
|
2001
|
+
if (content !== result.content) contentPatch = content
|
|
2002
|
+
rewriteFailureMessage(result, env)
|
|
1929
2003
|
}
|
|
1930
2004
|
}
|
|
1931
2005
|
} catch { /* never block a result */ }
|
|
1932
2006
|
const chain = next()
|
|
1933
|
-
if (
|
|
2007
|
+
if (contentPatch === undefined) return chain
|
|
1934
2008
|
return chain.then((decision) => {
|
|
1935
2009
|
try {
|
|
1936
|
-
if (decision && decision.kind === 'accept') {
|
|
1937
|
-
if (patch !== undefined) return { ...decision, value: patch }
|
|
1938
|
-
return { ...decision, content: contentPatch }
|
|
1939
|
-
}
|
|
2010
|
+
if (decision && decision.kind === 'accept') return { ...decision, content: contentPatch }
|
|
1940
2011
|
} catch { /* keep the downstream decision */ }
|
|
1941
2012
|
return decision
|
|
1942
2013
|
})
|
|
1943
2014
|
})
|
|
1944
|
-
console.log(TAG + '
|
|
2015
|
+
console.log(TAG + ' failure dialect active on tools/post-execute (content + error.message)')
|
|
1945
2016
|
} catch (error) {
|
|
1946
2017
|
console.log(TAG + ' tools/post-execute wiring failed: ' + (error?.message ?? error))
|
|
1947
2018
|
}
|
|
@@ -2068,4 +2139,4 @@ export async function apply(ctx, config = {}) {
|
|
|
2068
2139
|
}
|
|
2069
2140
|
|
|
2070
2141
|
// Test surface: pure helpers, no Cordis context required.
|
|
2071
|
-
export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, rewriteCodePaths, scanCodeLiterals, programPrelude, translateGlobArguments, buildTranslateEnv, rewriteErrorContent, rewriteErrorMessage, rewriteFailureMessage, msysEcho, ERROR_CONTENT_TOOLS, readPosixPaths, readAdoptSidebar, readDialectSettings, windowsToMsys, rewriteResultPaths, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition, personaEraForText, detectPersonaEra, injectPresentRow, hostHasToolPresent, detectPresentSupport, injectPluginManagerRow, hostHasPluginManagerTools, rowFormOf, rowFormsOf, alignEngineRow, alignRalphRow, ROW_SOURCE }
|
|
2142
|
+
export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, rewriteCodePaths, scanCodeLiterals, programPrelude, translateGlobArguments, buildTranslateEnv, rewriteErrorContent, rewriteErrorMessage, rewriteFailureMessage, msysEcho, driveToMsys, pathEcho, ERROR_CONTENT_TOOLS, readPosixPaths, readAdoptSidebar, readDialectSettings, windowsToMsys, rewriteResultPaths, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition, personaEraForText, detectPersonaEra, injectPresentRow, hostHasToolPresent, detectPresentSupport, injectPluginManagerRow, hostHasPluginManagerTools, rowFormOf, rowFormsOf, alignEngineRow, alignRalphRow, ROW_SOURCE }
|