dsh-gitbash-shell 0.17.0 → 0.18.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 +101 -6
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.18.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.18.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
|
@@ -724,6 +724,50 @@ function posixSeparators(value) {
|
|
|
724
724
|
: value
|
|
725
725
|
}
|
|
726
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Rewrite Windows absolute paths inside an ERROR result's text blocks to
|
|
729
|
+
* the MSYS dialect (pure, v0.17.1). Error results cannot carry a
|
|
730
|
+
* replacement `value` (the registry rejects that), but replacing `content`
|
|
731
|
+
* is the official post-execute channel, so the message the model reads
|
|
732
|
+
* stays in one dialect — it must not relearn Windows forms from failure
|
|
733
|
+
* text. Non-drive-letter diagnostics (device paths, URLs) never match
|
|
734
|
+
* windowsToMsys and are left verbatim.
|
|
735
|
+
*/
|
|
736
|
+
/**
|
|
737
|
+
* The only tools whose ERROR content is dialect-translated (v0.17.1).
|
|
738
|
+
* These messages are harness-generated path diagnostics (cannot read
|
|
739
|
+
* "C:\..."), so rewriting them is safe. Every other tool — bash above all,
|
|
740
|
+
* whose failed runs carry raw command stdout/stderr — keeps its error
|
|
741
|
+
* content VERBATIM: output is data, never rewritten (the same red line as
|
|
742
|
+
* file content in successful results).
|
|
743
|
+
*/
|
|
744
|
+
const ERROR_CONTENT_TOOLS = new Set(['read', 'read_image', 'write', 'edit', 'glob', 'grep', 'present'])
|
|
745
|
+
|
|
746
|
+
export function rewriteErrorContent(content) {
|
|
747
|
+
if (!Array.isArray(content)) return content
|
|
748
|
+
let changed = false
|
|
749
|
+
const next = content.map((block) => {
|
|
750
|
+
if (!block || typeof block !== 'object' || typeof block.text !== 'string') return block
|
|
751
|
+
const out = windowsToMsys(block.text)
|
|
752
|
+
if (out === block.text) return block
|
|
753
|
+
changed = true
|
|
754
|
+
return { ...block, text: out }
|
|
755
|
+
})
|
|
756
|
+
// /dev/null x file tools: the harness realpath step rejects device
|
|
757
|
+
// paths, so the failure is EXPECTED — append one guidance line (v0.18.0)
|
|
758
|
+
// instead of leaving the model to puzzle over EINVAL. The original
|
|
759
|
+
// diagnostic stays verbatim; only a hint block is appended.
|
|
760
|
+
const nulHit = content.some((block) => {
|
|
761
|
+
const t = block && typeof block === 'object' && typeof block.text === 'string' ? block.text : ''
|
|
762
|
+
return t.includes('EINVAL') && t.includes('NUL')
|
|
763
|
+
})
|
|
764
|
+
if (nulHit) {
|
|
765
|
+
next.push({ type: 'text', text: 'hint: /dev/null is the NUL device — file tools cannot target device paths (their realpath step rejects them); discard output through bash instead (echo ... > /dev/null)' })
|
|
766
|
+
changed = true
|
|
767
|
+
}
|
|
768
|
+
return changed ? next : content
|
|
769
|
+
}
|
|
770
|
+
|
|
727
771
|
export function rewriteResultPaths(name, value, env) {
|
|
728
772
|
if (!value || typeof value !== 'object') return value
|
|
729
773
|
// v0.17.0: paths under the user TEMP directory echo back in the bash
|
|
@@ -879,11 +923,53 @@ export function buildTranslateEnv() {
|
|
|
879
923
|
* resolve after the drive roots; any other shape returns the input
|
|
880
924
|
* unchanged. Pure: the env is a plain fact bag, nothing is probed here.
|
|
881
925
|
*/
|
|
926
|
+
/**
|
|
927
|
+
* Expand a leading dollar-sign NAME / braced NAME path variable the way
|
|
928
|
+
* Git Bash would (pure, string surgery, v0.18.0): HOME is the user home,
|
|
929
|
+
* TMPDIR/TMP/TEMP all mean the /tmp mount. Unknown names and NAMEX-style
|
|
930
|
+
* false positives return the input verbatim (written without any dollar
|
|
931
|
+
* literal — a code-transport layer mangles that byte inside quotes).
|
|
932
|
+
*/
|
|
933
|
+
function expandLeadingVar(value, env) {
|
|
934
|
+
if (!env || typeof value !== 'string' || value.length < 2) return value
|
|
935
|
+
if (value.charCodeAt(0) !== 36) return value
|
|
936
|
+
let name = ''
|
|
937
|
+
let rest = ''
|
|
938
|
+
if (value.charCodeAt(1) === 123) {
|
|
939
|
+
const close = value.indexOf('}', 2)
|
|
940
|
+
if (close < 0) return value
|
|
941
|
+
name = value.slice(2, close)
|
|
942
|
+
rest = value.slice(close + 1)
|
|
943
|
+
} else {
|
|
944
|
+
const head = value.slice(1)
|
|
945
|
+
let i = 0
|
|
946
|
+
while (i < head.length) {
|
|
947
|
+
const c = head.charCodeAt(i)
|
|
948
|
+
const letter = (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95
|
|
949
|
+
const digit = c >= 48 && c <= 57
|
|
950
|
+
if (i === 0 ? !letter : !(letter || digit)) break
|
|
951
|
+
i++
|
|
952
|
+
}
|
|
953
|
+
if (i === 0) return value
|
|
954
|
+
name = head.slice(0, i)
|
|
955
|
+
rest = head.slice(i)
|
|
956
|
+
}
|
|
957
|
+
if (rest !== '' && rest.charCodeAt(0) !== 47) return value
|
|
958
|
+
let base
|
|
959
|
+
if (name === 'HOME') base = env.home
|
|
960
|
+
else if (name === 'TMPDIR' || name === 'TMP' || name === 'TEMP') base = env.tmpDir
|
|
961
|
+
else return value
|
|
962
|
+
return base ? (rest !== '' ? base + rest : base) : value
|
|
963
|
+
}
|
|
882
964
|
export function translateMsysPath(value, env) {
|
|
883
965
|
if (typeof value !== 'string') return value
|
|
884
966
|
if (env && env.home && (value === '~' || value.startsWith('~/'))) {
|
|
885
967
|
return value === '~' ? env.home : env.home + '/' + value.slice(2)
|
|
886
968
|
}
|
|
969
|
+
if (env) {
|
|
970
|
+
const expanded = expandLeadingVar(value, env)
|
|
971
|
+
if (expanded !== value) return expanded
|
|
972
|
+
}
|
|
887
973
|
const match = MSYS_DRIVE_ROOT.exec(value)
|
|
888
974
|
if (match) return match[1].toUpperCase() + ':/' + match[2]
|
|
889
975
|
const bare = MSYS_BARE_DRIVE.exec(value)
|
|
@@ -1369,17 +1455,26 @@ export async function apply(ctx, config = {}) {
|
|
|
1369
1455
|
try {
|
|
1370
1456
|
ctx.on('tools/post-execute', (exec, result, next) => {
|
|
1371
1457
|
let patch
|
|
1458
|
+
let contentPatch
|
|
1372
1459
|
try {
|
|
1373
|
-
if (readPosixPaths(ctx) && result &&
|
|
1374
|
-
|
|
1375
|
-
|
|
1460
|
+
if (readPosixPaths(ctx) && result && typeof result === 'object') {
|
|
1461
|
+
if (result.isError === false && result.value && typeof result.value === 'object') {
|
|
1462
|
+
const value = rewriteResultPaths(exec && exec.name, result.value, buildTranslateEnv())
|
|
1463
|
+
if (value !== result.value) patch = value
|
|
1464
|
+
} else if (result.isError === true && exec && ERROR_CONTENT_TOOLS.has(exec.name) && Array.isArray(result.content)) {
|
|
1465
|
+
const content = rewriteErrorContent(result.content)
|
|
1466
|
+
if (content !== result.content) contentPatch = content
|
|
1467
|
+
}
|
|
1376
1468
|
}
|
|
1377
1469
|
} catch { /* never block a result */ }
|
|
1378
1470
|
const chain = next()
|
|
1379
|
-
if (patch === undefined) return chain
|
|
1471
|
+
if (patch === undefined && contentPatch === undefined) return chain
|
|
1380
1472
|
return chain.then((decision) => {
|
|
1381
1473
|
try {
|
|
1382
|
-
if (decision && decision.kind === 'accept')
|
|
1474
|
+
if (decision && decision.kind === 'accept') {
|
|
1475
|
+
if (patch !== undefined) return { ...decision, value: patch }
|
|
1476
|
+
return { ...decision, content: contentPatch }
|
|
1477
|
+
}
|
|
1383
1478
|
} catch { /* keep the downstream decision */ }
|
|
1384
1479
|
return decision
|
|
1385
1480
|
})
|
|
@@ -1510,4 +1605,4 @@ export async function apply(ctx, config = {}) {
|
|
|
1510
1605
|
}
|
|
1511
1606
|
|
|
1512
1607
|
// Test surface: pure helpers, no Cordis context required.
|
|
1513
|
-
export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, translateGlobArguments, buildTranslateEnv, readPosixPaths, readAdoptSidebar, 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 }
|
|
1608
|
+
export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, translateGlobArguments, buildTranslateEnv, rewriteErrorContent, ERROR_CONTENT_TOOLS, readPosixPaths, readAdoptSidebar, 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 }
|