@ericrisco/rsc 3.0.5 → 3.0.6

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/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.0.5",
2
+ "version": "3.0.6",
3
3
  "counts": {
4
4
  "skills": 270,
5
5
  "agents": 33,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericrisco/rsc",
3
- "version": "3.0.5",
3
+ "version": "3.0.6",
4
4
  "description": "A guided harness builder that turns your goal into a minimal, verified AI-agent setup using strict best practices.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,7 @@
11
11
  // `node .rsc/auto-update.mjs` on its first turn, and to relay what it prints.
12
12
  import { readFileSync, existsSync, writeFileSync, openSync } from 'node:fs';
13
13
  import { spawn } from 'node:child_process';
14
- import { join, resolve } from 'node:path';
14
+ import { dirname, join, resolve } from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
16
 
17
17
  // Update check: compare the installed version (.rsc/.version, written at install)
@@ -86,12 +86,21 @@ function launchUpdate(root, version, env) {
86
86
  const targets = declaredTargets(root);
87
87
  const args = [...base.slice(1), 'sync', ...(targets.length ? ['--target', targets.join(',')] : [])];
88
88
  const log = openSync(logFile(root), 'w');
89
- const child = spawn(base[0], args, {
89
+ // #293 — on Windows npx is npx.cmd, which Node only starts through a shell, and a shell is one more
90
+ // console that can flash. So npx runs as what it is, a script of the npm that ships with Node,
91
+ // started by node itself. Only when that script cannot be found (a runtime that is not Node, e.g.
92
+ // OpenCode's Bun) does it fall back to the shell — still hidden.
93
+ const npxCli = !env.RSC_AUTO_UPDATE_CMD && process.platform === 'win32' && !process.versions.bun
94
+ ? [join(dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npx-cli.js')].find((p) => existsSync(p))
95
+ : null;
96
+ const [command, argv] = npxCli ? [process.execPath, [npxCli, ...args]] : [base[0], args];
97
+ const viaShell = !npxCli && process.platform === 'win32';
98
+ const child = spawn(command, argv, {
90
99
  cwd: root,
91
100
  detached: true,
92
101
  stdio: ['ignore', log, log],
93
102
  windowsHide: true,
94
- shell: process.platform === 'win32', // npx is npx.cmd there; every argument was validated above
103
+ shell: viaShell, // fallback only (see above); every argument was validated above
95
104
  env: { ...env, RSC_NO_UPDATE_CHECK: '1', RSC_AUTO_UPDATE_TO: version },
96
105
  });
97
106
  child.on('error', () => {});
@@ -45,7 +45,7 @@ export function effectiveDir(command, base) {
45
45
  }
46
46
 
47
47
  const gitAt = (dir) => (...args) => {
48
- const r = spawnSync('git', ['-C', dir, ...args], { encoding: 'utf8' });
48
+ const r = spawnSync('git', ['-C', dir, ...args], { windowsHide: true, encoding: 'utf8' });
49
49
  return r.status === 0 ? (r.stdout || '').trim() : null;
50
50
  };
51
51
 
@@ -41,7 +41,7 @@ const FILES = Object.freeze({
41
41
  });
42
42
 
43
43
  const git = (cwd, args) => {
44
- try { return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
44
+ try { return execFileSync('git', args, { windowsHide: true, cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
45
45
  };
46
46
  const rel = (cwd, path) => relative(cwd, path).split(sep).join('/');
47
47
 
@@ -65,6 +65,7 @@ export const isKnowledge = (path) => KNOWLEDGE.some((k) => path.startsWith(k)) &
65
65
  function run(root, args, { timeout = LOCAL_TIMEOUT_MS, input, env } = {}) {
66
66
  try {
67
67
  const out = execFileSync('git', args, {
68
+ windowsHide: true,
68
69
  cwd: root, encoding: 'utf8', timeout, input, maxBuffer: 16 * 1024 * 1024,
69
70
  stdio: ['pipe', 'pipe', 'pipe'],
70
71
  env: { ...process.env, GIT_TERMINAL_PROMPT: '0', LC_ALL: 'C', ...env },
@@ -33,7 +33,7 @@ const EVENTS = Object.freeze({
33
33
  export const KNOWLEDGE_TARGETS = Object.freeze(Object.keys(CONFIG));
34
34
 
35
35
  const git = (cwd, args) => {
36
- try { return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
36
+ try { return execFileSync('git', args, { windowsHide: true, cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
37
37
  };
38
38
  const rel = (cwd, path) => relative(cwd, path).split(sep).join('/');
39
39
  const tracked = (cwd, path) => Boolean(git(cwd, ['ls-files', '--', rel(cwd, path)]));
package/targets/memory.js CHANGED
@@ -35,7 +35,7 @@ export function memoryEnabledForProject(cwd = process.cwd()) {
35
35
  }
36
36
 
37
37
  function git(cwd, args) {
38
- try { return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
38
+ try { return execFileSync('git', args, { windowsHide: true, cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
39
39
  }
40
40
 
41
41
  function rel(cwd, path) {
package/targets/sello.mjs CHANGED
@@ -103,7 +103,7 @@ export function isEnabled(root) {
103
103
  // The CLI can run from any subdirectory; the guard always runs from the project
104
104
  // root. Both must compute the same candidate, so both resolve to the git toplevel.
105
105
  export function resolveRoot(root) {
106
- const r = spawnSync('git', ['-C', root, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' });
106
+ const r = spawnSync('git', ['-C', root, 'rev-parse', '--show-toplevel'], { windowsHide: true, encoding: 'utf8' });
107
107
  return r.status === 0 ? (r.stdout || '').trim() || root : root;
108
108
  }
109
109
 
@@ -211,7 +211,7 @@ export function lensesRequired(tier, cfg = null) {
211
211
  // name matches no file on disk, so the file would hash as deleted and any later
212
212
  // mutation of it would sail through the gate. -z gives raw NUL-separated paths.
213
213
  function git(root, ...args) {
214
- const r = spawnSync('git', ['-C', root, '-c', 'core.quotePath=false', ...args], {
214
+ const r = spawnSync('git', ['-C', root, '-c', 'core.quotePath=false', ...args], { windowsHide: true,
215
215
  encoding: 'utf8', maxBuffer: 64 * 1024 * 1024,
216
216
  });
217
217
  return r.status === 0 ? (r.stdout || '') : null;
@@ -48,7 +48,7 @@ function cleanId(value, prefix = 'session') {
48
48
 
49
49
  function git(cwd, args) {
50
50
  try {
51
- return { ok: true, out: execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 2 * 1024 * 1024 }).trim() };
51
+ return { ok: true, out: execFileSync('git', args, { windowsHide: true, cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 2 * 1024 * 1024 }).trim() };
52
52
  } catch {
53
53
  return { ok: false, out: '' };
54
54
  }
@@ -67,7 +67,7 @@ const MERGE = /\bgit\s+merge\b/;
67
67
  if (!TRUNK.test(command) && !MERGE.test(command)) allow();
68
68
 
69
69
  const git = (...args) => {
70
- const r = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
70
+ const r = spawnSync('git', ['-C', root, ...args], { windowsHide: true, encoding: 'utf8' });
71
71
  return r.status === 0 ? (r.stdout || '').trim() : null;
72
72
  };
73
73
 
@@ -16,7 +16,7 @@ import { existsSync, mkdirSync, realpathSync, writeFileSync } from 'node:fs';
16
16
  import { basename, join, sep } from 'node:path';
17
17
 
18
18
  const git = (root, args) => {
19
- try { return execFileSync('git', args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 8000 }).trim(); }
19
+ try { return execFileSync('git', args, { windowsHide: true, cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 8000 }).trim(); }
20
20
  catch { return null; }
21
21
  };
22
22
  const ok = (root, args) => git(root, args) !== null;
@@ -28,7 +28,7 @@ const TEAM_COMMITS = 50;
28
28
 
29
29
  function git(root, args) {
30
30
  try {
31
- return execFileSync('git', args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 4000 }).trim();
31
+ return execFileSync('git', args, { windowsHide: true, cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 4000 }).trim();
32
32
  } catch { return null; }
33
33
  }
34
34
 
@@ -30,7 +30,7 @@ const EVENT = Object.freeze({ codex: 'SessionStart', gemini: 'SessionStart', cur
30
30
  export const UPDATE_HOOK_TARGETS = Object.freeze(['claude', ...Object.keys(CONFIG)].sort());
31
31
 
32
32
  const git = (cwd, args) => {
33
- try { return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
33
+ try { return execFileSync('git', args, { windowsHide: true, cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
34
34
  };
35
35
  const rel = (cwd, path) => relative(cwd, path).split(sep).join('/');
36
36
  const tracked = (cwd, path) => Boolean(git(cwd, ['ls-files', '--', rel(cwd, path)]));
@@ -72,7 +72,7 @@ function git(cwd, args) {
72
72
  // 64 MiB, because node's 1 MiB default kills git mid-write on any worktree with a few thousand
73
73
  // stray files and hands back a truncated-but-successful-looking result. Measured: in the 1.0-1.15 MB
74
74
  // band the same unchanged worktree flipped between verdicts across runs.
75
- const r = spawnSync('git', ['-C', cwd, ...args], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
75
+ const r = spawnSync('git', ['-C', cwd, ...args], { windowsHide: true, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
76
76
  // `raw` matters for porcelain output: its leading space is a status code, not padding, and
77
77
  // trimming it shifted every path by one character — so the message naming the file about to be
78
78
  // lost named a file that does not exist. A refusal has to be true to be actionable (P6).