super-backlog 1.0.0 → 1.0.3

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.
@@ -1,6 +1,4 @@
1
- // src/commands/backlog-alias.ts
2
- import { spawn } from 'node:child_process';
3
- import process from 'node:process';
1
+ import spawn from 'cross-spawn';
4
2
  import { resolveBacklogBin } from '../lib/run.js';
5
3
  /** Run a backlog.md subcommand by delegating to the resolved backlog binary. */
6
4
  export function runBacklogSubcommand(cwd, subcommand, args = []) {
@@ -13,7 +11,6 @@ export function runBacklogSubcommand(cwd, subcommand, args = []) {
13
11
  const child = spawn(bin, [subcommand, ...args], {
14
12
  cwd,
15
13
  stdio: 'inherit',
16
- shell: process.platform === 'win32',
17
14
  });
18
15
  child.on('error', () => resolve(1));
19
16
  child.on('exit', (code) => resolve(code ?? 1));
@@ -1,8 +1,6 @@
1
- // src/commands/dashboard.ts
2
- import { spawn } from 'node:child_process';
3
1
  import { tmpdir } from 'node:os';
4
2
  import { join } from 'node:path';
5
- import process from 'node:process';
3
+ import spawn from 'cross-spawn';
6
4
  import { collectDashboardData } from '../dashboard/data.js';
7
5
  import { renderDashboard } from '../dashboard/render.js';
8
6
  import { DASHBOARD_PORT, startServeServer } from '../dashboard/server.js';
@@ -24,7 +22,6 @@ function spawnBacklogBrowser(cwd) {
24
22
  cwd,
25
23
  detached: true,
26
24
  stdio: 'ignore',
27
- shell: process.platform === 'win32',
28
25
  });
29
26
  child.on('error', () => { });
30
27
  child.unref();
@@ -1,8 +1,6 @@
1
- // src/commands/uninstall.ts
2
- import { spawnSync } from 'node:child_process';
1
+ import spawn from 'cross-spawn';
3
2
  import { existsSync, readFileSync, rmSync } from 'node:fs';
4
3
  import { join } from 'node:path';
5
- import process from 'node:process';
6
4
  import { findGitDir, POINTER_HEADING_RE } from '../init/execute.js';
7
5
  import { atomicWrite } from '../lib/atomic.js';
8
6
  import { GUARD_RE, REFRESH_RE, removeGuardHook, removeRefreshHook } from '../lib/hooks.js';
@@ -195,11 +193,10 @@ export function verifyRemnants(cwd) {
195
193
  return remnants;
196
194
  }
197
195
  function defaultRemoveGlobal() {
198
- const cmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
199
- const r = spawnSync(cmd, ['uninstall', '-g', 'super-backlog'], {
196
+ const cmd = 'npm';
197
+ const r = spawn.sync(cmd, ['uninstall', '-g', 'super-backlog'], {
200
198
  encoding: 'utf8',
201
199
  windowsHide: true,
202
- shell: process.platform === 'win32',
203
200
  });
204
201
  if (r.error)
205
202
  return 1;
@@ -100,13 +100,12 @@ export async function runUpdate(cwd, _args) {
100
100
  warnings.push(`\`${bin} --version\` failed with exit code ${local.status}`);
101
101
  }
102
102
  }
103
- const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
104
103
  let published = null;
105
104
  try {
106
105
  // test seam: SBL_FORCE_OFFLINE makes e2e runs take the offline path deterministically
107
106
  if (process.env.SBL_FORCE_OFFLINE)
108
107
  throw new Error('forced offline');
109
- const view = runCapture(npm, ['view', 'backlog.md', 'version'], cwd);
108
+ const view = runCapture('npm', ['view', 'backlog.md', 'version'], cwd);
110
109
  if (view.status === 0)
111
110
  published = firstLine(view.stdout);
112
111
  }
@@ -5,6 +5,7 @@ import { readFile } from 'node:fs/promises';
5
5
  import { createServer } from 'node:http';
6
6
  import { isAbsolute, join } from 'node:path';
7
7
  import process from 'node:process';
8
+ import crossSpawn from 'cross-spawn';
8
9
  import { createModelApiHandler } from '../models/dashboard-api.js';
9
10
  import { resolveBacklogBin } from '../lib/run.js';
10
11
  export const DASHBOARD_PORT = 6428;
@@ -66,11 +67,10 @@ export function createRunApiHandler(cwd) {
66
67
  }
67
68
  const args = WHITELIST.get(payload.command);
68
69
  try {
69
- const child = spawn(bin, args, {
70
+ const child = crossSpawn(bin, args, {
70
71
  cwd,
71
72
  detached: true,
72
73
  stdio: 'ignore',
73
- shell: process.platform === 'win32',
74
74
  });
75
75
  child.on('error', () => { });
76
76
  child.unref();
@@ -3,19 +3,17 @@
3
3
  // System-changing fixes (node install, execution policy) require consent or fixAll;
4
4
  // safe fixes run unconditionally. Every fix is verified and reports a manual
5
5
  // fallback command on failure.
6
- import { spawnSync } from 'node:child_process';
7
6
  import { existsSync } from 'node:fs';
8
7
  import { delimiter, join } from 'node:path';
9
8
  import process from 'node:process';
9
+ import spawn from 'cross-spawn';
10
10
  import { getEffectiveExecutionPolicy, isBlockingExecutionPolicy, } from './powershell.js';
11
11
  import { resolveBacklogBin } from './run.js';
12
12
  const defaultExecutor = (cmd, args) => {
13
- // shell on win32 — .cmd/.ps1 shims (npm.cmd etc.) are not directly executable.
14
- // Safe only because callers pass constant args; never pass user input here.
15
- const r = spawnSync(cmd, args, {
13
+ // cross-spawn resolves .cmd shims on Windows without shell: true.
14
+ const r = spawn.sync(cmd, args, {
16
15
  encoding: 'utf8',
17
16
  windowsHide: true,
18
- shell: process.platform === 'win32',
19
17
  });
20
18
  if (r.error)
21
19
  return { status: null, stdout: '', stderr: String(r.error.message) };
@@ -99,13 +97,6 @@ function checkNpmCommand(ctx) {
99
97
  ctx.npmCmd = 'npm';
100
98
  return { id, status: 'ok', detail: `npm ${probe.stdout.trim()}` };
101
99
  }
102
- if (ctx.platform === 'win32') {
103
- const fallback = ctx.executor('npm.cmd', ['--version']);
104
- if (fallback.status === 0) {
105
- ctx.npmCmd = 'npm.cmd';
106
- return { id, status: 'fixed', detail: 'npm not directly callable; using npm.cmd shim' };
107
- }
108
- }
109
100
  return {
110
101
  id,
111
102
  status: 'failed',
@@ -206,7 +197,7 @@ export function runPreflight(cwd, deps = {}) {
206
197
  log: deps.log ?? ((line) => console.log(line)),
207
198
  confirm: deps.confirm,
208
199
  fixAll: deps.fixAll ?? false,
209
- npmCmd: platform === 'win32' ? 'npm.cmd' : 'npm',
200
+ npmCmd: 'npm',
210
201
  };
211
202
  const unitNames = [
212
203
  ['node-version', checkNodeVersion],
package/dist/lib/run.js CHANGED
@@ -1,12 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { spawnSync } from 'node:child_process';
3
2
  import { join } from 'node:path';
4
3
  import process from 'node:process';
4
+ import spawn from 'cross-spawn';
5
5
  export function runCapture(cmd, args, cwd) {
6
- // shell on win32 safe only because callers pass constant args; never pass user input here.
7
- const winShell = process.platform === 'win32';
8
- const file = winShell && /\s/.test(cmd) ? `"${cmd}"` : cmd;
9
- const r = spawnSync(file, args, { cwd, encoding: 'utf8', shell: winShell });
6
+ const r = spawn.sync(cmd, args, { cwd, encoding: 'utf8' });
10
7
  if (r.error && (r.status === null || r.status === undefined)) {
11
8
  return { status: 127, stdout: '', stderr: String(r.error.message) };
12
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-backlog",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "One command to equip any project with Backlog.md + Superpowers, plus a Project Dashboard.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,6 +32,7 @@
32
32
  "screenshot": "npm run build && node scripts/capture-dashboard.mjs"
33
33
  },
34
34
  "devDependencies": {
35
+ "@types/cross-spawn": "^6.0.6",
35
36
  "@types/node": "^26.2.0",
36
37
  "backlog.md": "^1.50.1",
37
38
  "cspell": "^10.1.1",
@@ -52,9 +53,13 @@
52
53
  },
53
54
  "vitest": {
54
55
  "vite": "^8.2.2",
56
+ "esbuild": "^0.25.0",
55
57
  "@vitest/mocker": {
56
58
  "vite": "^8.2.2"
57
59
  }
58
60
  }
61
+ },
62
+ "dependencies": {
63
+ "cross-spawn": "^7.0.6"
59
64
  }
60
65
  }