Skip to content

Commit 7d9b292

Browse files
fix(windows): fix MotW block, swallowed config errors, and taskkill quoting
- README: document Unblock-File step for Mark-of-the-Web restriction that prevents install.ps1 from running after Invoke-WebRequest - install.ps1: check $LASTEXITCODE after `install -y` and exit 1 with a visible error when zero agents are configured, instead of silently printing "Done!" - compat_fs.c: replace _spawnvp with CreateProcess in cbm_exec_no_shell on Windows; add cbm_build_cmdline that properly quotes argv arguments containing spaces (MSVC convention), fixing the taskkill /FI filter that was splitting "IMAGENAME eq codebase-memory-mcp.exe" into three bare tokens and printing an invalid-argument error Fixes #697 Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
1 parent b9b7905 commit 7d9b292

3 files changed

Lines changed: 132 additions & 7 deletions

File tree

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,16 @@ Invoke-WebRequest -Uri https://raw.githubusercontent.com/DeusData/codebase-memor
5959
# 2. (Optional but recommended) Inspect the script
6060
notepad install.ps1
6161
62-
# 3. Run it
62+
# 3. Unblock the downloaded file (removes Mark-of-the-Web restriction added by browsers/Invoke-WebRequest)
63+
Unblock-File .\install.ps1
64+
65+
# 4. Run it
6366
.\install.ps1
6467
6568
```
6669

70+
> **Note:** If you see a script execution policy error, run `Set-ExecutionPolicy -Scope Process Bypass` first, or invoke with `PowerShell -ExecutionPolicy Bypass -File .\install.ps1`.
71+
6772
Options: `--ui` (graph visualization), `--skip-config` (binary only, no agent setup), `--dir=<path>` (custom location).
6873

6974
Restart your coding agent. Say **"Index this project"** — done.
@@ -86,6 +91,7 @@ Restart your coding agent. Say **"Index this project"** — done.
8691
Windows (PowerShell):
8792
```powershell
8893
Expand-Archive codebase-memory-mcp-windows-amd64.zip -DestinationPath .
94+
Unblock-File .\install.ps1
8995
.\install.ps1
9096
```
9197

install.ps1

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,13 @@ if ($SkipConfig) {
131131
} else {
132132
Write-Host ""
133133
Write-Host "Configuring coding agents..."
134-
try {
135-
& $Dest install -y 2>&1 | Write-Host
136-
} catch {
137-
Write-Host "Agent configuration failed (non-fatal)."
138-
Write-Host "Run manually: codebase-memory-mcp install"
134+
& $Dest install -y 2>&1 | Write-Host
135+
if ($LASTEXITCODE -ne 0) {
136+
Write-Host ""
137+
Write-Host "error: agent configuration failed (exit code $LASTEXITCODE)" -ForegroundColor Red
138+
Write-Host "The binary was installed, but no coding agents were configured."
139+
Write-Host "Run manually to configure: `"$Dest`" install"
140+
exit 1
139141
}
140142
}
141143

src/foundation/compat_fs.c

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,128 @@ int cbm_rmdir(const char *path) {
197197
return ret;
198198
}
199199

200+
/* Build a properly-quoted Windows command line from an argv array.
201+
* Returns a heap-allocated wide string, or NULL on allocation failure.
202+
* Quoting follows the MSVC CRT convention: arguments containing spaces,
203+
* tabs, or double-quotes are wrapped in double-quotes, with backslashes
204+
* before a closing quote doubled and the quote itself escaped. */
205+
static wchar_t *cbm_build_cmdline(const char *const *argv) {
206+
/* First pass: compute required buffer size. */
207+
size_t total = 1; /* NUL terminator */
208+
for (int i = 0; argv[i]; i++) {
209+
const char *arg = argv[i];
210+
bool needs_quote = (arg[0] == '\0');
211+
for (const char *p = arg; *p; p++) {
212+
if (*p == ' ' || *p == '\t' || *p == '"') {
213+
needs_quote = true;
214+
}
215+
}
216+
if (i > 0) {
217+
total++; /* space separator */
218+
}
219+
if (needs_quote) {
220+
total += 2; /* opening and closing quote */
221+
size_t backslashes = 0;
222+
for (const char *p = arg; *p; p++) {
223+
if (*p == '\\') {
224+
backslashes++;
225+
} else if (*p == '"') {
226+
total += backslashes + 1; /* double backslashes + escape backslash */
227+
backslashes = 0;
228+
} else {
229+
backslashes = 0;
230+
}
231+
total++;
232+
}
233+
/* Trailing backslashes before closing quote must be doubled. */
234+
total += backslashes;
235+
} else {
236+
total += strlen(arg);
237+
}
238+
}
239+
240+
wchar_t *out = (wchar_t *)malloc(total * sizeof(wchar_t));
241+
if (!out) {
242+
return NULL;
243+
}
244+
245+
/* Second pass: write the command line. */
246+
wchar_t *w = out;
247+
for (int i = 0; argv[i]; i++) {
248+
const char *arg = argv[i];
249+
bool needs_quote = (arg[0] == '\0');
250+
for (const char *p = arg; *p; p++) {
251+
if (*p == ' ' || *p == '\t' || *p == '"') {
252+
needs_quote = true;
253+
break;
254+
}
255+
}
256+
if (i > 0) {
257+
*w++ = L' ';
258+
}
259+
if (needs_quote) {
260+
*w++ = L'"';
261+
size_t backslashes = 0;
262+
for (const char *p = arg; *p; p++) {
263+
if (*p == '\\') {
264+
backslashes++;
265+
*w++ = L'\\';
266+
} else if (*p == '"') {
267+
/* Double the preceding backslashes, then escape the quote. */
268+
for (size_t b = 0; b < backslashes; b++) {
269+
*w++ = L'\\';
270+
}
271+
*w++ = L'\\';
272+
*w++ = L'"';
273+
backslashes = 0;
274+
} else {
275+
backslashes = 0;
276+
*w++ = (wchar_t)(unsigned char)*p;
277+
}
278+
}
279+
/* Double trailing backslashes before the closing quote. */
280+
for (size_t b = 0; b < backslashes; b++) {
281+
*w++ = L'\\';
282+
}
283+
*w++ = L'"';
284+
} else {
285+
for (const char *p = arg; *p; p++) {
286+
*w++ = (wchar_t)(unsigned char)*p;
287+
}
288+
}
289+
}
290+
*w = L'\0';
291+
return out;
292+
}
293+
200294
int cbm_exec_no_shell(const char *const *argv) {
201295
if (!argv || !argv[0]) {
202296
return CBM_NOT_FOUND;
203297
}
204-
return (int)_spawnvp(_P_WAIT, argv[0], argv);
298+
299+
wchar_t *cmdline = cbm_build_cmdline(argv);
300+
if (!cmdline) {
301+
return CBM_NOT_FOUND;
302+
}
303+
304+
STARTUPINFOW si;
305+
PROCESS_INFORMATION pi;
306+
memset(&si, 0, sizeof(si));
307+
memset(&pi, 0, sizeof(pi));
308+
si.cb = sizeof(si);
309+
310+
if (!CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
311+
free(cmdline);
312+
return CBM_NOT_FOUND;
313+
}
314+
free(cmdline);
315+
316+
WaitForSingleObject(pi.hProcess, INFINITE);
317+
DWORD exit_code = (DWORD)CBM_NOT_FOUND;
318+
GetExitCodeProcess(pi.hProcess, &exit_code);
319+
CloseHandle(pi.hProcess);
320+
CloseHandle(pi.hThread);
321+
return (int)exit_code;
205322
}
206323

207324
#else /* POSIX */

0 commit comments

Comments
 (0)