weavatrix 0.3.7 → 0.3.9
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/README.md +129 -942
- package/docs/releases/v0.3.8.md +21 -0
- package/docs/releases/v0.3.9.md +36 -0
- package/package.json +3 -1
- package/scripts/run-agent-task-benchmark.mjs +0 -1
- package/src/analysis/change-classification.js +9 -19
- package/src/analysis/dead-code-review/candidates.js +194 -0
- package/src/analysis/dead-code-review.js +4 -188
- package/src/analysis/git-history/collector.js +1 -43
- package/src/analysis/git-history.js +2 -1
- package/src/analysis/git-ref-graph.js +4 -10
- package/src/analysis/internal-audit/repo-files.js +2 -6
- package/src/git-exec.js +67 -0
- package/src/graph/freshness-probe.js +2 -5
- package/src/graph/internal-builder.langs.js +11 -24
- package/src/mcp/git-output.mjs +2 -5
- package/src/mcp/graph/context-state.mjs +4 -5
- package/src/mcp/health/audit-format.mjs +2 -5
- package/src/mcp/tools-impact-change.mjs +2 -3
- package/src/mcp-rg.mjs +2 -4
- package/src/mcp-source-tools.mjs +4 -5
- package/src/process.js +18 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {readFileSync, readdirSync} from 'node:fs'
|
|
2
2
|
import {join} from 'node:path'
|
|
3
|
-
import {
|
|
4
|
-
import {childProcessEnv} from '../../child-env.js'
|
|
3
|
+
import {runGit} from '../../git-exec.js'
|
|
5
4
|
import {filterWeavatrixIgnored} from '../../path-ignore.js'
|
|
6
5
|
|
|
7
6
|
export const readText = (path) => {
|
|
@@ -29,10 +28,7 @@ const SKIP_DIRS = new Set([
|
|
|
29
28
|
|
|
30
29
|
export function listRepoFiles(repoRoot) {
|
|
31
30
|
try {
|
|
32
|
-
const result =
|
|
33
|
-
encoding: 'utf8', windowsHide: true, timeout: 15_000, maxBuffer: 32 * 1024 * 1024,
|
|
34
|
-
env: childProcessEnv(),
|
|
35
|
-
})
|
|
31
|
+
const result = runGit(repoRoot, ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], {timeout: 15_000, maxBuffer: 32 * 1024 * 1024})
|
|
36
32
|
if (result.status === 0) {
|
|
37
33
|
const files = String(result.stdout || '').split('\0').filter(Boolean).map((file) => file.replace(/\\/g, '/'))
|
|
38
34
|
return filterWeavatrixIgnored(repoRoot, files)
|
package/src/git-exec.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// The single chokepoint for launching git. Every git subprocess in the package routes through here,
|
|
2
|
+
// so there is one place that applies `-C <repoRoot>` containment, a credential-stripped child
|
|
3
|
+
// environment, a hidden Windows window and a bounded timeout — instead of a dozen near-identical
|
|
4
|
+
// spawnSync wrappers scattered across the analysis, graph and MCP layers.
|
|
5
|
+
import { spawnSync, spawn } from "node:child_process";
|
|
6
|
+
import { childProcessEnv } from "./child-env.js";
|
|
7
|
+
|
|
8
|
+
// Synchronous git. Returns the raw spawnSync result ({status, stdout, stderr, error}) so callers keep
|
|
9
|
+
// their existing status/stdout/error handling. encoding defaults to "utf8" (pass "buffer" for binary
|
|
10
|
+
// stdout); maxBuffer and stdio are forwarded only when provided so Node's defaults are preserved.
|
|
11
|
+
export function runGit(repoRoot, args, options = {}) {
|
|
12
|
+
const spawnOptions = {
|
|
13
|
+
encoding: options.encoding ?? "utf8",
|
|
14
|
+
windowsHide: true,
|
|
15
|
+
timeout: options.timeout ?? 8000,
|
|
16
|
+
env: childProcessEnv(options.env),
|
|
17
|
+
};
|
|
18
|
+
if (options.maxBuffer != null) spawnOptions.maxBuffer = options.maxBuffer;
|
|
19
|
+
if (options.stdio != null) spawnOptions.stdio = options.stdio;
|
|
20
|
+
return spawnSync("git", ["-C", repoRoot, ...args], spawnOptions);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Streaming git for the history collector: byte-bounded stdout, a hard timeout that SIGKILLs the child,
|
|
24
|
+
// and a resolved {stdout, stderr, exitCode, truncated}. The caller owns cwd/env so reads can be scoped
|
|
25
|
+
// to a specific worktree.
|
|
26
|
+
export function boundedGitCommand(command, args, options) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const child = spawn(command, args, {
|
|
29
|
+
cwd: options.cwd, env: options.env, shell: false, windowsHide: true,
|
|
30
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
31
|
+
});
|
|
32
|
+
const stdout = [], stderr = [];
|
|
33
|
+
let stdoutBytes = 0, stderrBytes = 0, truncated = false, timedOut = false, settled = false;
|
|
34
|
+
const finish = (callback) => {
|
|
35
|
+
if (settled) return;
|
|
36
|
+
settled = true;
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
callback();
|
|
39
|
+
};
|
|
40
|
+
const stop = () => { try { child.kill("SIGKILL"); } catch { /* process already exited */ } };
|
|
41
|
+
const timer = setTimeout(() => { timedOut = true; stop(); }, options.timeoutMs);
|
|
42
|
+
child.stdout?.on("data", (chunk) => {
|
|
43
|
+
if (truncated) return;
|
|
44
|
+
const remaining = options.maxOutputBytes - stdoutBytes;
|
|
45
|
+
if (remaining <= 0) { truncated = true; stop(); return; }
|
|
46
|
+
const kept = chunk.length <= remaining ? chunk : chunk.subarray(0, remaining);
|
|
47
|
+
stdout.push(kept); stdoutBytes += kept.length;
|
|
48
|
+
if (kept.length !== chunk.length) { truncated = true; stop(); }
|
|
49
|
+
});
|
|
50
|
+
child.stderr?.on("data", (chunk) => {
|
|
51
|
+
const remaining = 64 * 1024 - stderrBytes;
|
|
52
|
+
if (remaining <= 0) return;
|
|
53
|
+
const kept = chunk.length <= remaining ? chunk : chunk.subarray(0, remaining);
|
|
54
|
+
stderr.push(kept); stderrBytes += kept.length;
|
|
55
|
+
});
|
|
56
|
+
child.on("error", (error) => finish(() => reject(error)));
|
|
57
|
+
child.on("close", (exitCode) => finish(() => {
|
|
58
|
+
if (timedOut) return reject(new Error("git history collection timed out"));
|
|
59
|
+
resolve({
|
|
60
|
+
stdout: Buffer.concat(stdout),
|
|
61
|
+
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
62
|
+
exitCode: Number(exitCode ?? 1),
|
|
63
|
+
truncated,
|
|
64
|
+
});
|
|
65
|
+
}));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
// authority when something changed; this exact Git/worktree token is safe to cache in-process and in
|
|
3
3
|
// versioned graph metadata when HEAD + dirty/untracked/control-file content remain unchanged.
|
|
4
4
|
import {createHash} from 'node:crypto'
|
|
5
|
-
import {spawnSync} from 'node:child_process'
|
|
6
5
|
import {readFileSync, statSync} from 'node:fs'
|
|
7
6
|
import {createRequire} from 'node:module'
|
|
8
|
-
import {
|
|
7
|
+
import {runGit} from '../git-exec.js'
|
|
9
8
|
import {createRepoBoundary} from '../repo-path.js'
|
|
10
9
|
|
|
11
10
|
const REPOSITORY_FRESHNESS_PROBE_V = 1
|
|
@@ -34,9 +33,7 @@ const CONTROL_FILES = ['.gitignore', '.weavatrixignore', '.weavatrix.json']
|
|
|
34
33
|
const MAX_CONTROL_BYTES = 1_000_000
|
|
35
34
|
|
|
36
35
|
function git(repoRoot, args) {
|
|
37
|
-
const result =
|
|
38
|
-
encoding: 'buffer', timeout: 8000, windowsHide: true, env: childProcessEnv(), maxBuffer: 16 * 1024 * 1024,
|
|
39
|
-
})
|
|
36
|
+
const result = runGit(repoRoot, args, {encoding: 'buffer', maxBuffer: 16 * 1024 * 1024})
|
|
40
37
|
return result.status === 0 ? Buffer.from(result.stdout || '') : null
|
|
41
38
|
}
|
|
42
39
|
|
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
// build works — and Electron main runs Node, so this needs no external runtime.
|
|
6
6
|
import { readdirSync, statSync, realpathSync } from "node:fs";
|
|
7
7
|
import { join, extname, dirname } from "node:path";
|
|
8
|
-
import { execFileSync } from "node:child_process";
|
|
9
8
|
import { createRequire } from "node:module";
|
|
10
9
|
import { isPathInside } from "../repo-path.js";
|
|
11
|
-
import {
|
|
10
|
+
import { runGit } from "../git-exec.js";
|
|
12
11
|
import { filterWeavatrixIgnored } from "../path-ignore.js";
|
|
13
12
|
import {trustedGrammarWasm, trustedRuntimeWasm} from './parser-artifact-boundary.js'
|
|
14
13
|
import LANG_JS from "./builder/lang-js.js";
|
|
@@ -112,17 +111,11 @@ function walkFallback(dir, acc = [], seen = new Set(), depth = 0, rootReal = nul
|
|
|
112
111
|
// A repository may be opened without Git (or Git may be unavailable), so failure is deliberately a signal to
|
|
113
112
|
// use the boundary-safe walker above rather than a build failure.
|
|
114
113
|
function gitFileUniverse(dir) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
121
|
-
timeout: 15_000,
|
|
122
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
123
|
-
env: childProcessEnv(),
|
|
124
|
-
});
|
|
125
|
-
} catch { return null; }
|
|
114
|
+
const lsFiles = runGit(dir, ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], {
|
|
115
|
+
stdio: ["ignore", "pipe", "ignore"], timeout: 15_000, maxBuffer: 64 * 1024 * 1024,
|
|
116
|
+
});
|
|
117
|
+
if (lsFiles.status !== 0 || lsFiles.error) return null;
|
|
118
|
+
const raw = lsFiles.stdout;
|
|
126
119
|
|
|
127
120
|
// `git -C <dir>` also succeeds when <dir> is merely nested under some parent repository. If that
|
|
128
121
|
// nested directory is ignored by the parent, `ls-files` is empty even though <dir> can be a valid
|
|
@@ -130,17 +123,11 @@ function gitFileUniverse(dir) {
|
|
|
130
123
|
// that one ambiguous case, fall back to the boundary-safe walker. Preserve an empty universe for
|
|
131
124
|
// an actual Git root so ignored files do not leak back into its graph.
|
|
132
125
|
if (!raw) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
timeout: 15_000,
|
|
139
|
-
maxBuffer: 1024 * 1024,
|
|
140
|
-
env: childProcessEnv(),
|
|
141
|
-
}).trim();
|
|
142
|
-
if (realpathSync.native(top) !== realpathSync.native(dir)) return null;
|
|
143
|
-
} catch { return null; }
|
|
126
|
+
const top = runGit(dir, ["rev-parse", "--show-toplevel"], {
|
|
127
|
+
stdio: ["ignore", "pipe", "ignore"], timeout: 15_000, maxBuffer: 1024 * 1024,
|
|
128
|
+
});
|
|
129
|
+
if (top.status !== 0 || top.error) return null;
|
|
130
|
+
if (realpathSync.native(String(top.stdout).trim()) !== realpathSync.native(dir)) return null;
|
|
144
131
|
}
|
|
145
132
|
|
|
146
133
|
let rootReal;
|
package/src/mcp/git-output.mjs
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {childProcessEnv} from '../child-env.js'
|
|
1
|
+
import {runGit} from '../git-exec.js'
|
|
3
2
|
|
|
4
3
|
export function gitLines(repoRoot, args) {
|
|
5
|
-
const result =
|
|
6
|
-
encoding: 'utf8', timeout: 8000, env: childProcessEnv(), windowsHide: true,
|
|
7
|
-
})
|
|
4
|
+
const result = runGit(repoRoot, args)
|
|
8
5
|
if (result.status !== 0) return null
|
|
9
6
|
return String(result.stdout || '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
|
|
10
7
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {readFileSync, statSync} from 'node:fs'
|
|
2
2
|
import {join} from 'node:path'
|
|
3
|
-
import {
|
|
4
|
-
import {childProcessEnv} from '../../child-env.js'
|
|
3
|
+
import {runGit} from '../../git-exec.js'
|
|
5
4
|
import {resolveRepoPath} from '../../repo-path.js'
|
|
6
5
|
import {mergePrecisionOverlay, precisionSemanticInputsMatch, readPrecisionOverlay} from '../../precision/lsp-overlay.js'
|
|
7
6
|
|
|
@@ -14,19 +13,19 @@ export function graphStaleness(ctx) {
|
|
|
14
13
|
try { info.builtAt = statSync(ctx.graphPath).mtime } catch { /* no graph file */ }
|
|
15
14
|
if (ctx.repoRoot && info.builtAt) {
|
|
16
15
|
try {
|
|
17
|
-
const head =
|
|
16
|
+
const head = runGit(ctx.repoRoot, ['log', '-1', '--format=%cI'], {timeout: 4000})
|
|
18
17
|
const iso = (head.stdout || '').trim()
|
|
19
18
|
if (head.status === 0 && iso) {
|
|
20
19
|
info.headAt = new Date(iso)
|
|
21
20
|
if (info.headAt > info.builtAt) {
|
|
22
21
|
info.stale = true
|
|
23
|
-
const count =
|
|
22
|
+
const count = runGit(ctx.repoRoot, ['rev-list', '--count', `--since=${info.builtAt.toISOString()}`, 'HEAD'], {timeout: 4000})
|
|
24
23
|
if (count.status === 0) info.behind = Number(count.stdout.trim()) || null
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
} catch { /* git unavailable */ }
|
|
28
27
|
try {
|
|
29
|
-
const status =
|
|
28
|
+
const status = runGit(ctx.repoRoot, ['status', '--porcelain'], {timeout: 4000})
|
|
30
29
|
if (status.status === 0) {
|
|
31
30
|
let newer = 0
|
|
32
31
|
for (const line of String(status.stdout || '').split(/\r?\n/).filter(Boolean).slice(0, 200)) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {spawnSync} from 'node:child_process'
|
|
2
1
|
import {summarizeFindings} from '../../analysis/findings.js'
|
|
3
|
-
import {
|
|
2
|
+
import {runGit} from '../../git-exec.js'
|
|
4
3
|
import {createPathClassifier, hasPathClass} from '../../path-classification.js'
|
|
5
4
|
|
|
6
5
|
const SEVERITY_RANK = {critical: 0, high: 1, medium: 2, low: 3, info: 4}
|
|
@@ -179,9 +178,7 @@ export const formatOrdinaryAudit = (audit, args, findings = audit.findings, head
|
|
|
179
178
|
}
|
|
180
179
|
|
|
181
180
|
export const gitUntracked = (repoRoot) => {
|
|
182
|
-
const result =
|
|
183
|
-
encoding: 'utf8', timeout: 8000, maxBuffer: 2 * 1024 * 1024, env: childProcessEnv(), windowsHide: true,
|
|
184
|
-
})
|
|
181
|
+
const result = runGit(repoRoot, ['ls-files', '--others', '--exclude-standard'], {maxBuffer: 2 * 1024 * 1024})
|
|
185
182
|
if (result.status !== 0) return {
|
|
186
183
|
ok: false,
|
|
187
184
|
files: [],
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Symbol-aware change_impact implementation, isolated from the other hot impact tools so its
|
|
2
2
|
// classifier/evidence contract can be tested without perturbing get_dependents or graph_diff.
|
|
3
|
-
import {
|
|
4
|
-
import {childProcessEnv} from '../child-env.js'
|
|
3
|
+
import {runGit} from '../git-exec.js'
|
|
5
4
|
import {fileOfId} from '../graph/node-id.js'
|
|
6
5
|
import {classifyChangeImpact} from '../analysis/change-classification.js'
|
|
7
6
|
import {readCoverageForRepo} from '../analysis/coverage-reports.js'
|
|
@@ -12,7 +11,7 @@ import {gitLines} from './git-output.mjs'
|
|
|
12
11
|
import {toolResult} from './tool-result.mjs'
|
|
13
12
|
|
|
14
13
|
function gitValue(repoRoot, args) {
|
|
15
|
-
const result =
|
|
14
|
+
const result = runGit(repoRoot, args)
|
|
16
15
|
if (result.status !== 0) return null
|
|
17
16
|
return String(result.stdout || '').trim() || null
|
|
18
17
|
}
|
package/src/mcp-rg.mjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { readdirSync, existsSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import { spawnSync } from 'node:child_process'
|
|
4
|
-
import { childProcessEnv } from './child-env.js'
|
|
5
3
|
import { createRequire } from 'node:module'
|
|
6
|
-
import
|
|
4
|
+
import { runCommandSync } from './process.js'
|
|
7
5
|
|
|
8
6
|
const rgInInstall = (base) => [
|
|
9
7
|
join(base, 'resources', 'app', 'node_modules', '@vscode', 'ripgrep', 'bin', 'rg.exe'),
|
|
@@ -43,7 +41,7 @@ export function createRgResolver(selfDir) {
|
|
|
43
41
|
} catch { /* optional packaged ripgrep path */ }
|
|
44
42
|
for (const c of editorRgCandidates()) if (existsSync(c)) return (rgPath = c)
|
|
45
43
|
try {
|
|
46
|
-
const probe =
|
|
44
|
+
const probe = runCommandSync(process.platform === 'win32' ? 'where' : 'which', ['rg'], {timeout: 3000})
|
|
47
45
|
const p = probe.status === 0 ? probe.stdout.split(/\r?\n/)[0].trim() : ''
|
|
48
46
|
if (p && existsSync(p)) return (rgPath = p)
|
|
49
47
|
} catch { /* optional PATH probe */ }
|
package/src/mcp-source-tools.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
|
|
2
|
-
import { spawnSync } from 'node:child_process'
|
|
3
2
|
import { extname, isAbsolute, join, relative, resolve } from 'node:path'
|
|
4
3
|
import { resolveRepoPath } from './repo-path.js'
|
|
5
|
-
import {
|
|
4
|
+
import { runCommandSync } from './process.js'
|
|
6
5
|
import { toolResult } from './mcp/tool-result.mjs'
|
|
7
6
|
|
|
8
7
|
const SEARCH_SKIP = new Set(['.git', 'node_modules', 'dist', 'build', 'out', '.next', 'coverage', 'vendor', '.venv', 'venv', 'env', 'target', '__pycache__', '.idea', '.vscode', '.cache', 'bin', 'obj', 'weavatrix-graphs'])
|
|
@@ -11,7 +10,7 @@ const MAX_SEARCH_FILE_BYTES = 1024 * 1024
|
|
|
11
10
|
const MAX_SOURCE_FILE_BYTES = 2 * 1024 * 1024
|
|
12
11
|
const MAX_SOURCE_CONTEXT_LINES = 1000
|
|
13
12
|
|
|
14
|
-
function rgSearch(repoRoot, resolveRg, query, { isRegex, glob, maxResults }, spawnRg =
|
|
13
|
+
function rgSearch(repoRoot, resolveRg, query, { isRegex, glob, maxResults }, spawnRg = runCommandSync) {
|
|
15
14
|
const rg = resolveRg()
|
|
16
15
|
if (!rg) return null
|
|
17
16
|
// Exclude node_modules explicitly: --hidden + the default gitignore stack is the only thing that
|
|
@@ -24,7 +23,7 @@ function rgSearch(repoRoot, resolveRg, query, { isRegex, glob, maxResults }, spa
|
|
|
24
23
|
// against repository-relative paths on Windows too. Passing an absolute search root makes rg
|
|
25
24
|
// compare the glob with a drive-prefixed path and silently return no matches.
|
|
26
25
|
args.push('--', query, '.')
|
|
27
|
-
const res = spawnRg(rg, args, { cwd: repoRoot, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024, timeout: 15000
|
|
26
|
+
const res = spawnRg(rg, args, { cwd: repoRoot, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024, timeout: 15000 })
|
|
28
27
|
if (res.status !== 0 && res.status !== 1) return null
|
|
29
28
|
const out = []
|
|
30
29
|
for (const line of (res.stdout || '').split(/\r?\n/)) {
|
|
@@ -93,7 +92,7 @@ function nodeGrep(repoRoot, query, { isRegex, glob, maxResults }) {
|
|
|
93
92
|
return out
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
export function searchCode({ repoRoot, resolveRg, spawnRg =
|
|
95
|
+
export function searchCode({ repoRoot, resolveRg, spawnRg = runCommandSync }, { query, is_regex = false, max_results = 40, glob } = {}) {
|
|
97
96
|
if (!query) return 'Provide a "query" string.'
|
|
98
97
|
if (!repoRoot || !existsSync(repoRoot)) return 'Source search unavailable: repo root not provided to this MCP server.'
|
|
99
98
|
const max = Math.max(1, Math.min(200, Number(max_results) || 40))
|
package/src/process.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Subprocess runner shared by the search engines and security sweeps.
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
3
|
import { childProcessEnv } from "./child-env.js";
|
|
4
4
|
|
|
5
5
|
// Windows: .cmd/.ps1 shims (npx, etc.) can't be spawned directly by Node — they need a
|
|
@@ -76,3 +76,20 @@ export function runCommand(command, args = [], options = {}) {
|
|
|
76
76
|
child.on("close", (code) => finish(() => (timedOut ? reject(new Error("Command timed out")) : resolve({ stdout, stderr, exitCode: code }))));
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
// Synchronous run-and-collect variant for callers that resolve a real binary and read its output in
|
|
81
|
+
// one shot (ripgrep resolution/search, tar extraction). These launch a resolved .exe or bare binary
|
|
82
|
+
// directly, so no shell quoting is needed; the child env is stripped of connector secrets and a
|
|
83
|
+
// bounded timeout/buffer applies. Returns the raw spawnSync result ({status, stdout, stderr, error}).
|
|
84
|
+
export function runCommandSync(command, args = [], options = {}) {
|
|
85
|
+
const spawnOptions = {
|
|
86
|
+
cwd: options.cwd || undefined,
|
|
87
|
+
encoding: options.encoding ?? "utf8",
|
|
88
|
+
env: childProcessEnv(options.env),
|
|
89
|
+
windowsHide: true,
|
|
90
|
+
};
|
|
91
|
+
if (options.timeout != null) spawnOptions.timeout = options.timeout;
|
|
92
|
+
if (options.maxBuffer != null) spawnOptions.maxBuffer = options.maxBuffer;
|
|
93
|
+
if (options.stdio != null) spawnOptions.stdio = options.stdio;
|
|
94
|
+
return spawnSync(command, args, spawnOptions);
|
|
95
|
+
}
|