patchwork-os 0.2.0-beta.3 → 0.2.0-beta.5
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 +95 -25
- package/dist/activityLog.js +2 -1
- package/dist/activityLog.js.map +1 -1
- package/dist/approvalHttp.js +25 -8
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalQueue.d.ts +44 -1
- package/dist/approvalQueue.js +117 -0
- package/dist/approvalQueue.js.map +1 -1
- package/dist/automation.d.ts +3 -3
- package/dist/automation.js +12 -5
- package/dist/automation.js.map +1 -1
- package/dist/bridge.js +29 -1
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeLockDiscovery.js +2 -1
- package/dist/bridgeLockDiscovery.js.map +1 -1
- package/dist/claudeOrchestrator.js +27 -10
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/dashboard.js +8 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/install.js +3 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/patchworkInit.js +4 -1
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commitIssueLinkLog.d.ts +16 -0
- package/dist/commitIssueLinkLog.js +87 -4
- package/dist/commitIssueLinkLog.js.map +1 -1
- package/dist/config.d.ts +20 -1
- package/dist/config.js +42 -4
- package/dist/config.js.map +1 -1
- package/dist/connectorRoutes.js +1 -1
- package/dist/connectorRoutes.js.map +1 -1
- package/dist/connectors/asana.js +4 -3
- package/dist/connectors/asana.js.map +1 -1
- package/dist/connectors/confluence.js +35 -0
- package/dist/connectors/confluence.js.map +1 -1
- package/dist/connectors/datadog.js +33 -4
- package/dist/connectors/datadog.js.map +1 -1
- package/dist/connectors/discord.js +5 -4
- package/dist/connectors/discord.js.map +1 -1
- package/dist/connectors/gitlab.js +7 -1
- package/dist/connectors/gitlab.js.map +1 -1
- package/dist/connectors/mcpOAuth.js +71 -6
- package/dist/connectors/mcpOAuth.js.map +1 -1
- package/dist/connectors/slack.d.ts +1 -1
- package/dist/connectors/slack.js +56 -4
- package/dist/connectors/slack.js.map +1 -1
- package/dist/connectors/tokenStorage.js +10 -4
- package/dist/connectors/tokenStorage.js.map +1 -1
- package/dist/decisionTraceLog.d.ts +28 -0
- package/dist/decisionTraceLog.js +115 -7
- package/dist/decisionTraceLog.js.map +1 -1
- package/dist/drivers/claude/subprocess.js +22 -3
- package/dist/drivers/claude/subprocess.js.map +1 -1
- package/dist/drivers/gemini/index.js +19 -3
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/extensionClient.d.ts +29 -4
- package/dist/extensionClient.js +26 -11
- package/dist/extensionClient.js.map +1 -1
- package/dist/featureFlags.js +18 -32
- package/dist/featureFlags.js.map +1 -1
- package/dist/fileLockSync.d.ts +67 -0
- package/dist/fileLockSync.js +126 -0
- package/dist/fileLockSync.js.map +1 -0
- package/dist/fp/automationInterpreter.d.ts +6 -0
- package/dist/fp/automationInterpreter.js +15 -2
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationState.d.ts +1 -1
- package/dist/fp/automationState.js +10 -0
- package/dist/fp/automationState.js.map +1 -1
- package/dist/fp/commandDescription.js +7 -1
- package/dist/fp/commandDescription.js.map +1 -1
- package/dist/fsWatchWithFallback.d.ts +36 -0
- package/dist/fsWatchWithFallback.js +127 -0
- package/dist/fsWatchWithFallback.js.map +1 -0
- package/dist/index.js +108 -48
- package/dist/index.js.map +1 -1
- package/dist/installGuard.js +6 -2
- package/dist/installGuard.js.map +1 -1
- package/dist/lockfile.js +27 -3
- package/dist/lockfile.js.map +1 -1
- package/dist/patchworkConfig.js +8 -3
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/pluginLoader.js +10 -1
- package/dist/pluginLoader.js.map +1 -1
- package/dist/pluginWatcher.js +6 -13
- package/dist/pluginWatcher.js.map +1 -1
- package/dist/preToolUseHook.js +3 -2
- package/dist/preToolUseHook.js.map +1 -1
- package/dist/processTree.d.ts +34 -0
- package/dist/processTree.js +105 -0
- package/dist/processTree.js.map +1 -0
- package/dist/prompts.js +3 -3
- package/dist/prompts.js.map +1 -1
- package/dist/recipeOrchestration.js +58 -8
- package/dist/recipeOrchestration.js.map +1 -1
- package/dist/recipeRoutes.d.ts +1 -0
- package/dist/recipeRoutes.js +100 -15
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/recipes/connectorPreflight.js +64 -0
- package/dist/recipes/connectorPreflight.js.map +1 -1
- package/dist/recipes/idempotencyKey.js +3 -4
- package/dist/recipes/idempotencyKey.js.map +1 -1
- package/dist/recipes/installer.js +48 -2
- package/dist/recipes/installer.js.map +1 -1
- package/dist/recipes/parser.js +82 -4
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/scheduler.d.ts +17 -0
- package/dist/recipes/scheduler.js +33 -1
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/yamlRunner.d.ts +4 -1
- package/dist/recipes/yamlRunner.js +18 -6
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/resources.js +21 -13
- package/dist/resources.js.map +1 -1
- package/dist/runLog.js +14 -3
- package/dist/runLog.js.map +1 -1
- package/dist/sanitizeParsedJson.d.ts +39 -0
- package/dist/sanitizeParsedJson.js +55 -0
- package/dist/sanitizeParsedJson.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.js +105 -33
- package/dist/server.js.map +1 -1
- package/dist/sessionCheckpoint.d.ts +8 -0
- package/dist/sessionCheckpoint.js +18 -2
- package/dist/sessionCheckpoint.js.map +1 -1
- package/dist/tools/detectUnusedCode.js +9 -7
- package/dist/tools/detectUnusedCode.js.map +1 -1
- package/dist/tools/editText.js +2 -1
- package/dist/tools/editText.js.map +1 -1
- package/dist/tools/fileOperations.js +2 -1
- package/dist/tools/fileOperations.js.map +1 -1
- package/dist/tools/fileWatcher.js +8 -2
- package/dist/tools/fileWatcher.js.map +1 -1
- package/dist/tools/fixAllLintErrors.js +10 -5
- package/dist/tools/fixAllLintErrors.js.map +1 -1
- package/dist/tools/formatDocument.js +10 -5
- package/dist/tools/formatDocument.js.map +1 -1
- package/dist/tools/handoffNote.js +2 -1
- package/dist/tools/handoffNote.js.map +1 -1
- package/dist/tools/headless/lspClient.js +3 -0
- package/dist/tools/headless/lspClient.js.map +1 -1
- package/dist/tools/index.js +0 -6
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/lsp.js +17 -0
- package/dist/tools/lsp.js.map +1 -1
- package/dist/tools/openDiff.js +4 -1
- package/dist/tools/openDiff.js.map +1 -1
- package/dist/tools/openFile.js +4 -1
- package/dist/tools/openFile.js.map +1 -1
- package/dist/tools/organizeImports.js +5 -3
- package/dist/tools/organizeImports.js.map +1 -1
- package/dist/tools/previewEdit.js +7 -2
- package/dist/tools/previewEdit.js.map +1 -1
- package/dist/tools/refactorExtractFunction.js +4 -1
- package/dist/tools/refactorExtractFunction.js.map +1 -1
- package/dist/tools/refactorPreview.js +10 -2
- package/dist/tools/refactorPreview.js.map +1 -1
- package/dist/tools/replaceBlock.js +2 -1
- package/dist/tools/replaceBlock.js.map +1 -1
- package/dist/tools/searchAndReplace.js +2 -1
- package/dist/tools/searchAndReplace.js.map +1 -1
- package/dist/tools/spawnWorkspace.js +15 -7
- package/dist/tools/spawnWorkspace.js.map +1 -1
- package/dist/tools/transaction.js +4 -1
- package/dist/tools/transaction.js.map +1 -1
- package/dist/tools/utils.js +62 -5
- package/dist/tools/utils.js.map +1 -1
- package/dist/transport.d.ts +1 -1
- package/dist/transport.js +18 -4
- package/dist/transport.js.map +1 -1
- package/dist/winShim.d.ts +34 -0
- package/dist/winShim.js +94 -0
- package/dist/winShim.js.map +1 -0
- package/dist/writeFileAtomic.d.ts +23 -0
- package/dist/writeFileAtomic.js +94 -0
- package/dist/writeFileAtomic.js.map +1 -0
- package/package.json +1 -1
- package/scripts/postinstall.mjs +18 -5
- package/scripts/smoke/run-all.mjs +55 -4
- package/scripts/start-all.mjs +60 -1
- package/scripts/start-all.ps1 +209 -209
- package/scripts/start-orchestrator.ps1 +158 -158
- package/dist/tools/ccRoutines.d.ts +0 -221
- package/dist/tools/ccRoutines.js +0 -264
- package/dist/tools/ccRoutines.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -41,7 +41,10 @@ import { Bridge } from "./bridge.js";
|
|
|
41
41
|
import { isBridgeToolsFileValid, repairBridgeToolsRulesIfStale, } from "./bridgeToolsRules.js";
|
|
42
42
|
import { findEditor, parseConfig } from "./config.js";
|
|
43
43
|
import { detectWorkspaceSymlinkInstall, PATCHWORK_PACKAGE_NAME, SYMLINK_INSTALL_FIX, } from "./installGuard.js";
|
|
44
|
+
import { treeKill } from "./processTree.js";
|
|
44
45
|
import { PACKAGE_VERSION, semverGt } from "./version.js";
|
|
46
|
+
import { ensureCmdShim } from "./winShim.js";
|
|
47
|
+
import { writeFileAtomicSync } from "./writeFileAtomic.js";
|
|
45
48
|
const __dirnameTop = path.dirname(fileURLToPath(import.meta.url));
|
|
46
49
|
// Warn when a symlinked global install is detected (`npm install -g .`).
|
|
47
50
|
// launchctl / sandbox environments can fail through that link with EPERM.
|
|
@@ -190,8 +193,12 @@ if (process.argv[2] === "--help" ||
|
|
|
190
193
|
` recipe --help Full recipe subcommand index\n\n` +
|
|
191
194
|
`Diagnose\n` +
|
|
192
195
|
` halts [--window 1h|24h|overnight|7d] Morning summary of recent recipe halts\n` +
|
|
196
|
+
` judgments [--window ...] [--recipe N] Recent judge-step verdicts across runs\n` +
|
|
193
197
|
` traces export Bundle approval / recipe / decision traces\n` +
|
|
194
198
|
` print-token [--port N] Print the active bridge auth token\n\n` +
|
|
199
|
+
`Safety\n` +
|
|
200
|
+
` kill-switch <engage|release|status> Block / resume write-tier tools across bridges\n` +
|
|
201
|
+
` panic [--reason "..."] Shorthand for kill-switch engage\n\n` +
|
|
195
202
|
`Daemon (no subcommand)\n` +
|
|
196
203
|
` --workspace <dir> Start the bridge in foreground\n` +
|
|
197
204
|
` --watch Auto-restart supervisor\n` +
|
|
@@ -1760,11 +1767,22 @@ if (process.argv[2] === "kill-switch") {
|
|
|
1760
1767
|
// form is `kill-switch engage`; this alias matches it so shell history six
|
|
1761
1768
|
// months later still makes sense. Does not accept sub-verbs — just runs engage.
|
|
1762
1769
|
if (process.argv[2] === "panic") {
|
|
1770
|
+
const extra = process.argv.slice(3); // e.g. --reason "..." --force-local
|
|
1771
|
+
// Guard against `panic --help` engaging the kill switch — a real
|
|
1772
|
+
// footgun if you tab-completed the verb to confirm syntax before
|
|
1773
|
+
// committing to the action. `panic` is an alias, so we honor --help
|
|
1774
|
+
// here ourselves rather than forwarding to kill-switch engage.
|
|
1775
|
+
if (extra.includes("--help") || extra.includes("-h")) {
|
|
1776
|
+
console.log('Usage: patchwork panic [--reason "..."] [--force-local]\n\n' +
|
|
1777
|
+
" Alias for `patchwork kill-switch engage` — blocks all write-tier\n" +
|
|
1778
|
+
" tool calls across every running bridge. Use --reason to leave a\n" +
|
|
1779
|
+
" note in the audit trail. Release with `patchwork kill-switch release`.\n");
|
|
1780
|
+
process.exit(0);
|
|
1781
|
+
}
|
|
1763
1782
|
// Spawn self with kill-switch engage to reuse the full handler without
|
|
1764
1783
|
// duplicating 200+ LOC. Passes through any flags (--reason, --force-local).
|
|
1765
1784
|
import("node:child_process").then(({ spawnSync }) => {
|
|
1766
1785
|
const self = process.argv[1] ?? process.execPath;
|
|
1767
|
-
const extra = process.argv.slice(3); // e.g. --reason "..." --force-local
|
|
1768
1786
|
const result = spawnSync(process.execPath, [self, "kill-switch", "engage", ...extra], { stdio: "inherit" });
|
|
1769
1787
|
process.exit(result.status ?? 1);
|
|
1770
1788
|
});
|
|
@@ -1831,14 +1849,6 @@ if (process.argv[2] === "halts") {
|
|
|
1831
1849
|
process.stderr.write("No running bridge found. Start one with `patchwork start` (or `--driver subprocess`).\n");
|
|
1832
1850
|
process.exit(2);
|
|
1833
1851
|
}
|
|
1834
|
-
// Single-bridge default: query the first. Multi-bridge users will
|
|
1835
|
-
// typically have one orchestrator anyway; expanding to fan-out is a
|
|
1836
|
-
// follow-up if needed.
|
|
1837
|
-
const lock = liveLocks[0];
|
|
1838
|
-
if (!lock) {
|
|
1839
|
-
process.stderr.write("No running bridge found.\n");
|
|
1840
|
-
process.exit(2);
|
|
1841
|
-
}
|
|
1842
1852
|
const sinceMs = windowSinceMs(window);
|
|
1843
1853
|
const params = [];
|
|
1844
1854
|
if (sinceMs != null)
|
|
@@ -1846,20 +1856,35 @@ if (process.argv[2] === "halts") {
|
|
|
1846
1856
|
if (recipeFilter)
|
|
1847
1857
|
params.push(`recipe=${encodeURIComponent(recipeFilter)}`);
|
|
1848
1858
|
const qs = params.length > 0 ? `?${params.join("&")}` : "";
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1859
|
+
// Walk live bridges in order; first responsive one wins. See the
|
|
1860
|
+
// matching block in the `judgments` handler — findAllLiveBridges
|
|
1861
|
+
// can include stale entries when a recycled PID still answers
|
|
1862
|
+
// `kill(pid, 0)` but the lock points at a dead bridge.
|
|
1863
|
+
let res = null;
|
|
1864
|
+
let lastStatus = 0;
|
|
1865
|
+
for (const lock of liveLocks) {
|
|
1866
|
+
const controller = new AbortController();
|
|
1867
|
+
const timer = setTimeout(() => controller.abort(), 10_000);
|
|
1868
|
+
try {
|
|
1869
|
+
const candidate = await fetch(`http://127.0.0.1:${lock.port}/runs/halt-summary${qs}`, {
|
|
1870
|
+
headers: { Authorization: `Bearer ${lock.authToken}` },
|
|
1871
|
+
signal: controller.signal,
|
|
1872
|
+
});
|
|
1873
|
+
if (candidate.ok) {
|
|
1874
|
+
res = candidate;
|
|
1875
|
+
break;
|
|
1876
|
+
}
|
|
1877
|
+
lastStatus = candidate.status;
|
|
1878
|
+
}
|
|
1879
|
+
catch {
|
|
1880
|
+
/* unreachable lock — try next */
|
|
1881
|
+
}
|
|
1882
|
+
finally {
|
|
1883
|
+
clearTimeout(timer);
|
|
1884
|
+
}
|
|
1860
1885
|
}
|
|
1861
|
-
if (!res
|
|
1862
|
-
process.stderr.write(`
|
|
1886
|
+
if (!res) {
|
|
1887
|
+
process.stderr.write(`No live bridge served /runs/halt-summary (last status: ${lastStatus || "unreachable"}).\n`);
|
|
1863
1888
|
process.exit(1);
|
|
1864
1889
|
}
|
|
1865
1890
|
const summary = (await res.json());
|
|
@@ -1971,11 +1996,6 @@ if (process.argv[2] === "judgments") {
|
|
|
1971
1996
|
process.stderr.write("No running bridge found. Start one with `patchwork start` (or `--driver subprocess`).\n");
|
|
1972
1997
|
process.exit(2);
|
|
1973
1998
|
}
|
|
1974
|
-
const lock = liveLocks[0];
|
|
1975
|
-
if (!lock) {
|
|
1976
|
-
process.stderr.write("No running bridge found.\n");
|
|
1977
|
-
process.exit(2);
|
|
1978
|
-
}
|
|
1979
1999
|
const sinceMs = windowSinceMs(window);
|
|
1980
2000
|
const params = [];
|
|
1981
2001
|
if (sinceMs != null)
|
|
@@ -1983,20 +2003,37 @@ if (process.argv[2] === "judgments") {
|
|
|
1983
2003
|
if (recipeFilter)
|
|
1984
2004
|
params.push(`recipe=${encodeURIComponent(recipeFilter)}`);
|
|
1985
2005
|
const qs = params.length > 0 ? `?${params.join("&")}` : "";
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
2006
|
+
// Walk live bridges in order; the first responsive one wins.
|
|
2007
|
+
// findAllLiveBridges uses `kill(pid, 0)` for liveness, which
|
|
2008
|
+
// returns true for any recycled PID — so liveLocks can contain
|
|
2009
|
+
// stale entries from dead bridges. Previously we picked [0]
|
|
2010
|
+
// unconditionally and surfaced a confusing 404; now we try each
|
|
2011
|
+
// and only fall through to the error path when *all* fail.
|
|
2012
|
+
let res = null;
|
|
2013
|
+
let lastStatus = 0;
|
|
2014
|
+
for (const lock of liveLocks) {
|
|
2015
|
+
const controller = new AbortController();
|
|
2016
|
+
const timer = setTimeout(() => controller.abort(), 10_000);
|
|
2017
|
+
try {
|
|
2018
|
+
const candidate = await fetch(`http://127.0.0.1:${lock.port}/runs/judge-summary${qs}`, {
|
|
2019
|
+
headers: { Authorization: `Bearer ${lock.authToken}` },
|
|
2020
|
+
signal: controller.signal,
|
|
2021
|
+
});
|
|
2022
|
+
if (candidate.ok) {
|
|
2023
|
+
res = candidate;
|
|
2024
|
+
break;
|
|
2025
|
+
}
|
|
2026
|
+
lastStatus = candidate.status;
|
|
2027
|
+
}
|
|
2028
|
+
catch {
|
|
2029
|
+
/* unreachable lock — try next */
|
|
2030
|
+
}
|
|
2031
|
+
finally {
|
|
2032
|
+
clearTimeout(timer);
|
|
2033
|
+
}
|
|
1997
2034
|
}
|
|
1998
|
-
if (!res
|
|
1999
|
-
process.stderr.write(`
|
|
2035
|
+
if (!res) {
|
|
2036
|
+
process.stderr.write(`No live bridge served /runs/judge-summary (last status: ${lastStatus || "unreachable"}).\n`);
|
|
2000
2037
|
process.exit(1);
|
|
2001
2038
|
}
|
|
2002
2039
|
const summary = (await res.json());
|
|
@@ -2630,9 +2667,12 @@ Steps performed:
|
|
|
2630
2667
|
}
|
|
2631
2668
|
if (extensionArg2) {
|
|
2632
2669
|
try {
|
|
2670
|
+
// Windows: editor binaries (code/cursor/windsurf) are `.cmd` shims that
|
|
2671
|
+
// Node's execFileSync can't launch without a shell. See bridgeProcess.ts.
|
|
2633
2672
|
execFileSync(editor, ["--install-extension", extensionArg2], {
|
|
2634
2673
|
stdio: "pipe",
|
|
2635
2674
|
timeout: 30000,
|
|
2675
|
+
shell: process.platform === "win32",
|
|
2636
2676
|
});
|
|
2637
2677
|
process.stderr.write(` ✓ Extension installed via ${editor}\n\n`);
|
|
2638
2678
|
}
|
|
@@ -2761,13 +2801,18 @@ Steps performed:
|
|
|
2761
2801
|
process.stderr.write(` ✓ MCP shim — already registered in ${claudeJsonAbs}\n\n`);
|
|
2762
2802
|
}
|
|
2763
2803
|
else {
|
|
2804
|
+
// claude -p spawns the stdio command via Node's child_process, which
|
|
2805
|
+
// can't resolve a bare `.cmd` shim on Windows. Record the `.cmd` form
|
|
2806
|
+
// on win32 so the bridge binary is findable by the spawned process.
|
|
2764
2807
|
mcpServers["claude-ide-bridge"] = {
|
|
2765
|
-
command: "claude-ide-bridge",
|
|
2808
|
+
command: ensureCmdShim("claude-ide-bridge"),
|
|
2766
2809
|
args: ["shim"],
|
|
2767
2810
|
type: "stdio",
|
|
2768
2811
|
};
|
|
2769
2812
|
claudeJson.mcpServers = mcpServers;
|
|
2770
|
-
|
|
2813
|
+
// Atomic — `~/.claude.json` holds every MCP server registration on
|
|
2814
|
+
// the machine. A crash mid-write would brick Claude Code globally.
|
|
2815
|
+
writeFileAtomicSync(claudeJsonAbs, `${JSON.stringify(claudeJson, null, 2)}\n`);
|
|
2771
2816
|
process.stderr.write(` ✓ MCP shim — registered in ${claudeJsonAbs}\n Note: bridge tools are wired via ~/.claude.json (global), not .mcp.json.\n This is intentional — when VS Code/Windsurf/Cursor launches Claude Code it\n injects --mcp-config which overrides any project .mcp.json. Only ~/.claude.json\n is always loaded. You do not need to add anything to .mcp.json.\n\n`);
|
|
2772
2817
|
}
|
|
2773
2818
|
}
|
|
@@ -2818,7 +2863,9 @@ Steps performed:
|
|
|
2818
2863
|
}
|
|
2819
2864
|
if (added.length > 0 || migrated.length > 0) {
|
|
2820
2865
|
ccSettings.hooks = ccHooks;
|
|
2821
|
-
|
|
2866
|
+
// Atomic — `~/.claude/settings.json` holds every CC hook entry; a
|
|
2867
|
+
// crash mid-write loses the user's full hook configuration.
|
|
2868
|
+
writeFileAtomicSync(ccSettingsPath, `${JSON.stringify(ccSettings, null, 2)}\n`);
|
|
2822
2869
|
const addMsg = added.length > 0
|
|
2823
2870
|
? ` ✓ CC hooks — wired ${added.length} automation hook(s) in ${ccSettingsPath}\n Added: ${added.join(", ")}\n`
|
|
2824
2871
|
: "";
|
|
@@ -2859,6 +2906,9 @@ Steps performed:
|
|
|
2859
2906
|
execFileSync("claude-ide-bridge", ["--version"], {
|
|
2860
2907
|
stdio: "pipe",
|
|
2861
2908
|
timeout: 5000,
|
|
2909
|
+
// Windows: global npm bin is a `.cmd` shim that Node's execFileSync
|
|
2910
|
+
// can't launch without a shell. See bridgeProcess.ts for context.
|
|
2911
|
+
shell: process.platform === "win32",
|
|
2862
2912
|
});
|
|
2863
2913
|
shimOnPath = true;
|
|
2864
2914
|
}
|
|
@@ -2937,8 +2987,11 @@ Steps performed:
|
|
|
2937
2987
|
? fallbackDocs
|
|
2938
2988
|
: null;
|
|
2939
2989
|
if (target) {
|
|
2940
|
-
|
|
2941
|
-
|
|
2990
|
+
// Use execFile with argv (no shell) — exec(`code "${target}"`) was
|
|
2991
|
+
// shell-evaluated and could be injected via `--workspace '"; ...'`
|
|
2992
|
+
// since path.resolve preserves shell metachars. Audit 2026-05-17.
|
|
2993
|
+
const { execFile } = await import("node:child_process");
|
|
2994
|
+
execFile("code", [target], { timeout: 3000 }, () => { });
|
|
2942
2995
|
}
|
|
2943
2996
|
}
|
|
2944
2997
|
// Analytics opt-in prompt — only ask once; skip if preference already set
|
|
@@ -3055,9 +3108,11 @@ else if (process.argv[2] === "install-extension") {
|
|
|
3055
3108
|
}
|
|
3056
3109
|
try {
|
|
3057
3110
|
process.stderr.write(`Installing extension via ${editor}...\n`);
|
|
3111
|
+
// Windows: editor binaries are `.cmd` shims; need shell for resolution.
|
|
3058
3112
|
execFileSync(editor, ["--install-extension", extensionArg], {
|
|
3059
3113
|
stdio: "inherit",
|
|
3060
3114
|
timeout: 30000,
|
|
3115
|
+
shell: process.platform === "win32",
|
|
3061
3116
|
});
|
|
3062
3117
|
process.stderr.write("Extension installed successfully.\n");
|
|
3063
3118
|
}
|
|
@@ -3401,7 +3456,12 @@ else {
|
|
|
3401
3456
|
for (const sig of ["SIGTERM", "SIGINT"]) {
|
|
3402
3457
|
process.once(sig, () => {
|
|
3403
3458
|
stopping = true;
|
|
3404
|
-
|
|
3459
|
+
// Use treeKill so grandchildren (recipe runners, claude
|
|
3460
|
+
// subprocesses, extension watchers) are reaped on Windows.
|
|
3461
|
+
// Bare `child.kill(sig)` maps to TerminateProcess on win32
|
|
3462
|
+
// and skips descendants → orphaned processes survive a
|
|
3463
|
+
// supervisor SIGTERM. Audit 2026-05-17.
|
|
3464
|
+
treeKill(child, sig);
|
|
3405
3465
|
});
|
|
3406
3466
|
}
|
|
3407
3467
|
child.on("exit", (code, signal) => {
|
|
@@ -3439,12 +3499,12 @@ else {
|
|
|
3439
3499
|
if (!isSourceBuild) {
|
|
3440
3500
|
import("node:child_process")
|
|
3441
3501
|
.then(({ exec }) => {
|
|
3442
|
-
exec("npm view
|
|
3502
|
+
exec("npm view patchwork-os version", { timeout: 5000 }, (err, stdout) => {
|
|
3443
3503
|
if (err || !stdout)
|
|
3444
3504
|
return;
|
|
3445
3505
|
const latest = stdout.trim();
|
|
3446
3506
|
if (latest && semverGt(latest, PACKAGE_VERSION)) {
|
|
3447
|
-
console.log(`\n
|
|
3507
|
+
console.log(`\n Patchwork OS v${latest} available — run: npm update -g patchwork-os\n`);
|
|
3448
3508
|
}
|
|
3449
3509
|
});
|
|
3450
3510
|
})
|