doc-detective 4.2.0 → 4.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doc-detective",
3
- "version": "4.2.0",
3
+ "version": "4.2.2",
4
4
  "description": "Treat doc content as testable assertions to validate doc accuracy and product UX.",
5
5
  "bin": {
6
6
  "doc-detective": "bin/doc-detective.js"
@@ -48,6 +48,8 @@
48
48
  "dev": "node ./dev",
49
49
  "container:build": "node src/container/scripts/build.cjs",
50
50
  "container:rebuild": "node src/container/scripts/build.cjs --no-cache",
51
+ "container:build:base": "node src/container/scripts/build.cjs --target=base",
52
+ "container:build:base:push": "node src/container/scripts/build.cjs --target=base --push",
51
53
  "container:test": "mocha src/container/test/*.test.cjs --timeout 0"
52
54
  },
53
55
  "repository": {
@@ -63,27 +63,46 @@ async function maybePromptInstallAgents() {
63
63
  : "PATH";
64
64
  const originalPath = process.env[pathKey];
65
65
  const pathSep = process.platform === "win32" ? ";" : ":";
66
+ // Windows paths are case-insensitive, so normalize both sides of every
67
+ // comparison to lowercase on win32. Otherwise `C:\Proj\NODE_MODULES\.BIN`
68
+ // would slip past a literal `/node_modules/.bin` check.
69
+ const isWin = process.platform === "win32";
70
+ const caseFold = (s) => (isWin ? s.toLowerCase() : s);
66
71
  const initCwdAbs = process.env.INIT_CWD
67
72
  ? path.resolve(process.env.INIT_CWD)
68
73
  : null;
74
+ const initCwdMatch = initCwdAbs ? caseFold(initCwdAbs) : null;
75
+ // Guard against INIT_CWD resolving to a root ("/" on POSIX, "C:\" on
76
+ // Windows): blindly appending path.sep would produce "//" or "C:\\", and
77
+ // `startsWith` would miss every subpath. When the value already ends with
78
+ // a separator (root case), use it as-is; otherwise append.
79
+ const initCwdPrefix =
80
+ initCwdMatch && !initCwdMatch.endsWith(path.sep)
81
+ ? initCwdMatch + path.sep
82
+ : initCwdMatch;
69
83
  const sanitizedPath = (originalPath || "")
70
84
  .split(pathSep)
71
85
  .filter((entry) => {
72
86
  if (!entry || entry === ".") return false;
73
- const normalized = entry.split(path.sep).join("/");
87
+ const normalized = caseFold(entry.split(path.sep).join("/"));
74
88
  if (normalized.includes("/node_modules/.bin")) return false;
75
- if (initCwdAbs) {
76
- const resolved = path.resolve(entry);
77
- if (
78
- resolved === initCwdAbs ||
79
- resolved.startsWith(initCwdAbs + path.sep)
80
- )
89
+ if (initCwdMatch) {
90
+ const resolved = caseFold(path.resolve(entry));
91
+ if (resolved === initCwdMatch || resolved.startsWith(initCwdPrefix))
81
92
  return false;
82
93
  }
83
94
  return true;
84
95
  })
85
96
  .join(pathSep);
86
97
  process.env[pathKey] = sanitizedPath;
98
+ // Restoring via `process.env[k] = undefined` coerces to the literal string
99
+ // "undefined" (Node stringifies every env value), leaving the process with
100
+ // a broken PATH. Delete the key when the original was absent; only assign
101
+ // when we have a real string.
102
+ const restorePath = () => {
103
+ if (originalPath === undefined) delete process.env[pathKey];
104
+ else process.env[pathKey] = originalPath;
105
+ };
87
106
 
88
107
  // Hard ceiling on the whole detection phase. Some adapters shell out to
89
108
  // external CLIs that could hang on auth prompts, proxy stalls, etc. On
@@ -121,17 +140,17 @@ async function maybePromptInstallAgents() {
121
140
  );
122
141
  const result = await Promise.race([detection, timeout]);
123
142
  if (result === "__timeout__") {
124
- process.env[pathKey] = originalPath;
143
+ restorePath();
125
144
  // Orphaned adapter children would otherwise keep the event loop alive
126
145
  // and freeze `npm install`. See comment above.
127
146
  process.exit(0);
128
147
  }
129
148
  adaptersNeedingInstall = result.filter(Boolean);
130
149
  } catch {
131
- process.env[pathKey] = originalPath;
150
+ restorePath();
132
151
  return;
133
152
  }
134
- process.env[pathKey] = originalPath;
153
+ restorePath();
135
154
 
136
155
  if (adaptersNeedingInstall.length === 0) return;
137
156