@indigoai-us/hq-cli 5.325.0 → 5.327.0

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/bin/hq-auth-refresh.js +2 -3
  3. package/dist/command-catalog.generated.d.ts +19 -1
  4. package/dist/command-catalog.generated.js +21 -1
  5. package/dist/commands/anywhere-runtime-flag.d.ts +2 -2
  6. package/dist/commands/anywhere-runtime-flag.js +2 -2
  7. package/dist/commands/auth.js +3 -1
  8. package/dist/commands/core-detect-stale-review-base-fast.d.ts +2 -0
  9. package/dist/commands/core-detect-stale-review-base-fast.js +23 -0
  10. package/dist/commands/core-job-remote-launcher.js +2 -3
  11. package/dist/commands/core-register-project-native.js +1 -1
  12. package/dist/commands/core-repo-run-registry-fast.d.ts +2 -0
  13. package/dist/commands/core-repo-run-registry-fast.js +35 -0
  14. package/dist/commands/core-rrr-worktree-scope-fix-fast-flag.d.ts +7 -0
  15. package/dist/commands/core-rrr-worktree-scope-fix-fast-flag.js +25 -0
  16. package/dist/commands/core-session-journal-native.js +1 -1
  17. package/dist/commands/core-share-suggestion-state-fast.d.ts +2 -0
  18. package/dist/commands/core-share-suggestion-state-fast.js +25 -0
  19. package/dist/commands/daemon.js +1 -1
  20. package/dist/commands/install-global.d.ts +10 -0
  21. package/dist/commands/install-global.js +96 -41
  22. package/dist/commands/logout.js +2 -0
  23. package/dist/commands/mcp-serve.d.ts +28 -0
  24. package/dist/commands/mcp-serve.js +165 -0
  25. package/dist/commands/mcp-status.js +14 -0
  26. package/dist/fast-core.js +3 -6
  27. package/dist/fast-scaffold.js +5 -1
  28. package/dist/index.d.ts +3 -0
  29. package/dist/index.js +9 -0
  30. package/dist/lanes-drain-hook.js +2 -2
  31. package/dist/lib/anywhere-runtime-fast-flag.d.ts +3 -0
  32. package/dist/lib/anywhere-runtime-fast-flag.js +23 -0
  33. package/dist/lib/anywhere-runtime-flag-key.d.ts +3 -0
  34. package/dist/lib/anywhere-runtime-flag-key.js +3 -0
  35. package/dist/lib/daemon/mcp-endpoint.d.ts +8 -0
  36. package/dist/lib/daemon/mcp-endpoint.js +30 -0
  37. package/dist/lib/daemon/mcp-oauth.d.ts +24 -0
  38. package/dist/lib/daemon/mcp-oauth.js +298 -0
  39. package/dist/lib/daemon/ops/mcp.d.ts +53 -0
  40. package/dist/lib/daemon/ops/mcp.js +217 -0
  41. package/dist/lib/daemon/protocol.d.ts +2 -1
  42. package/dist/lib/daemon/protocol.js +3 -1
  43. package/dist/lib/daemon/server.d.ts +5 -0
  44. package/dist/lib/daemon/server.js +78 -11
  45. package/dist/lib/install/claude-targets.d.ts +11 -4
  46. package/dist/lib/install/claude-targets.js +21 -9
  47. package/dist/lib/install/codex-targets.d.ts +8 -4
  48. package/dist/lib/install/codex-targets.js +18 -9
  49. package/dist/lib/lanes/watcher-main.js +2 -4
  50. package/dist/lib/mcp/hosted-search-tools.d.ts +4 -0
  51. package/dist/lib/mcp/hosted-search-tools.js +28 -0
  52. package/dist/lib/mcp/hq-mcp-tools.generated.d.ts +3 -0
  53. package/dist/lib/mcp/hq-mcp-tools.generated.js +538 -0
  54. package/dist/lib/mcp/prompts.d.ts +30 -0
  55. package/dist/lib/mcp/prompts.js +48 -0
  56. package/dist/lib/mcp/tools.d.ts +22 -0
  57. package/dist/lib/mcp/tools.js +82 -0
  58. package/dist/lib/mcp/types.d.ts +6 -0
  59. package/dist/lib/mcp/types.js +2 -0
  60. package/dist/lib/process-flag-company-context-fast.d.ts +4 -0
  61. package/dist/lib/process-flag-company-context-fast.js +20 -0
  62. package/dist/lib/process-flag-company-context-light.d.ts +9 -0
  63. package/dist/lib/process-flag-company-context-light.js +81 -0
  64. package/dist/lib/process-flag-company-context.d.ts +2 -4
  65. package/dist/lib/process-flag-company-context.js +2 -46
  66. package/dist/utils/hq-roots-core.d.ts +130 -0
  67. package/dist/utils/hq-roots-core.js +354 -0
  68. package/dist/utils/hq-roots.d.ts +2 -129
  69. package/dist/utils/hq-roots.js +2 -353
  70. package/dist/utils/is-main-module.d.ts +3 -0
  71. package/dist/utils/is-main-module.js +19 -0
  72. package/dist/utils/package-root-diagnostics.d.ts +5 -36
  73. package/dist/utils/package-root-diagnostics.js +4 -83
  74. package/dist/utils/package-root-resolution-error.d.ts +32 -0
  75. package/dist/utils/package-root-resolution-error.js +79 -0
  76. package/dist/utils/staged-npm-install.js +2 -1
  77. package/package.json +3 -1
@@ -1,354 +1,3 @@
1
- /**
2
- * Root resolution for CLI-hosted HQ scaffold code.
3
- *
4
- * When shell that used to live inside the HQ scaffold moves into this npm
5
- * package, it loses the one thing it relied on: `BASH_SOURCE`/`$0` pointed at
6
- * the user's live HQ tree, so "where am I" and "where is HQ" were the same
7
- * answer. Inside a globally installed package they are three different answers,
8
- * and conflating any two of them silently corrupts the wrong tree.
9
- *
10
- * liveRoot The user's HQ installation — companies/, workspace/, personal/,
11
- * the installed .claude/ and core/. This is runtime state. Hidden
12
- * runtime commands operate here.
13
- *
14
- * packageRoot This npm package's own directory, where bundled scaffold assets
15
- * ship. Read-only in practice: a global install may sit in a
16
- * root-owned prefix. NEVER usable as liveRoot.
17
- *
18
- * There is deliberately no third "source root". Release-source tooling — the
19
- * linters, generators, and validators that rewrite checked-in artifacts — stays
20
- * scaffold-owned, so nothing here ever needs to resolve an hq-core checkout. See
21
- * the scaffold-vs-cli-code-ownership policy for why.
22
- *
23
- * The invariant that matters most: `resolveLiveRoot` never consults
24
- * `import.meta.url`. A relocated script asking "where is HQ" must get the live
25
- * tree, not the package it happens to be bundled in.
26
- */
27
- import * as fs from "fs";
28
- import * as path from "path";
29
- import { fileURLToPath } from "url";
30
- import { PackageRootResolutionError, } from "./package-root-diagnostics.js";
31
- /** This package's name, used to identify its root when walking upward. */
32
- const CLI_PACKAGE_NAME = "@indigoai-us/hq-cli";
33
- /**
34
- * A root that cannot be resolved is the caller's state — a wrong `--hq-root`, or a
35
- * command run from outside any HQ tree. Marking
36
- * these `expected` makes the top-level handler print the message and skip Sentry;
37
- * an unmarked error there prints NOTHING and only ships a crash report, which is
38
- * the worst possible outcome for a misconfiguration the user can fix.
39
- *
40
- * Deliberately not used for packaging faults — a missing bundled asset IS a
41
- * defect and should reach Sentry.
42
- */
43
- function rootError(message) {
44
- const err = new Error(message);
45
- err.expected = true;
46
- return err;
47
- }
48
- /** Where bundled scaffold assets live, relative to the package root. */
49
- export const BUNDLED_ASSET_DIR = path.join("assets", "scaffold");
50
- /**
51
- * Does this directory look like an HQ tree root?
52
- *
53
- * `companies/` is the load-bearing marker — it is the source of truth for
54
- * tenants and exists in every real install. The second clause tolerates the
55
- * three shapes a tree can legitimately be in: a full install, a scaffold
56
- * checkout without a personal overlay, and a personal-only overlay.
57
- */
58
- export function isHqRoot(dir) {
59
- return (fs.existsSync(path.join(dir, "companies")) &&
60
- (fs.existsSync(path.join(dir, ".claude")) ||
61
- fs.existsSync(path.join(dir, "core")) ||
62
- fs.existsSync(path.join(dir, "personal"))));
63
- }
64
- function realpathOrResolve(p, fileSystem = fs) {
65
- try {
66
- return fileSystem.realpathSync(p);
67
- }
68
- catch {
69
- return path.resolve(p);
70
- }
71
- }
72
- function errorCode(err) {
73
- if (err && typeof err === "object" && "code" in err) {
74
- const code = err.code;
75
- return typeof code === "string" ? code : undefined;
76
- }
77
- return undefined;
78
- }
79
- function failureOutcome(code) {
80
- if (code === "ENOENT")
81
- return "no-manifest";
82
- if (code === "EACCES")
83
- return "eacces";
84
- if (code === "EISDIR")
85
- return "eisdir";
86
- return "read-error";
87
- }
88
- /**
89
- * Make a resolver for this package's root. It caches only a successful lookup:
90
- * a concurrently replaced manifest must be retried by the next command, not
91
- * converted into a permanent process failure.
92
- */
93
- export function createPackageRootResolver(options) {
94
- const fileSystem = options.fs ?? fs;
95
- const modulePath = options.modulePath ?? options.moduleDir;
96
- let cachedPackageRoot;
97
- return () => {
98
- if (cachedPackageRoot !== undefined)
99
- return cachedPackageRoot;
100
- const visited = [];
101
- let dir = options.moduleDir;
102
- for (;;) {
103
- const manifest = path.join(dir, "package.json");
104
- try {
105
- const parsed = JSON.parse(fileSystem.readFileSync(manifest, "utf8"));
106
- if (parsed.name === CLI_PACKAGE_NAME) {
107
- cachedPackageRoot = realpathOrResolve(dir, fileSystem);
108
- return cachedPackageRoot;
109
- }
110
- visited.push({
111
- directory: dir,
112
- outcome: "name-mismatch",
113
- manifestName: String(parsed.name),
114
- });
115
- }
116
- catch (err) {
117
- const code = errorCode(err);
118
- if (code) {
119
- visited.push({ directory: dir, outcome: failureOutcome(code), code });
120
- }
121
- else {
122
- visited.push({ directory: dir, outcome: "parse-error" });
123
- }
124
- }
125
- const parent = path.dirname(dir);
126
- if (parent === dir)
127
- break;
128
- dir = parent;
129
- }
130
- let fallbackRejectedBecause = "no dist-owner ancestor";
131
- try {
132
- const resolvedModuleDir = fileSystem.realpathSync(options.moduleDir);
133
- let candidate = resolvedModuleDir;
134
- for (;;) {
135
- const relativeModulePath = path.relative(candidate, resolvedModuleDir);
136
- if (relativeModulePath.split(path.sep)[0] === "dist") {
137
- const assetDir = path.join(candidate, BUNDLED_ASSET_DIR);
138
- try {
139
- if (fileSystem.existsSync(assetDir) && fileSystem.statSync(assetDir).isDirectory()) {
140
- cachedPackageRoot = candidate;
141
- return cachedPackageRoot;
142
- }
143
- fallbackRejectedBecause =
144
- "deepest dist-owner lacks assets/scaffold";
145
- }
146
- catch (err) {
147
- fallbackRejectedBecause =
148
- `could not inspect deepest dist-owner assets/scaffold (${errorCode(err) ?? "unknown"})`;
149
- }
150
- // Never climb to a shallower dist owner after the nearest owner fails.
151
- break;
152
- }
153
- const parent = path.dirname(candidate);
154
- if (parent === candidate)
155
- break;
156
- candidate = parent;
157
- }
158
- }
159
- catch (err) {
160
- fallbackRejectedBecause =
161
- `could not realpath module directory (${errorCode(err) ?? "unknown"})`;
162
- }
163
- throw new PackageRootResolutionError({
164
- modulePath,
165
- startDir: options.moduleDir,
166
- strategiesTried: ["manifest-walk", "dist-owner-fallback"],
167
- visited,
168
- fallbackRejectedBecause,
169
- });
170
- };
171
- }
172
- const currentModulePath = fileURLToPath(import.meta.url);
173
- const defaultPackageRootResolver = createPackageRootResolver({
174
- moduleDir: path.dirname(currentModulePath),
175
- modulePath: currentModulePath,
176
- });
177
- /**
178
- * This package's installed root — the directory holding its `package.json`.
179
- *
180
- * Walks up from the compiled module (`dist/utils/hq-roots.js`) until it finds a
181
- * `package.json` whose name matches, rather than hard-coding `..` hops, so the
182
- * answer survives a change in output layout.
183
- */
184
- export function packageRoot() {
185
- return defaultPackageRootResolver();
186
- }
187
- /**
188
- * The trailing path segments that mark an installed copy of this package:
189
- * `node_modules` followed by the package name's own segments (for
190
- * `@indigoai-us/hq-cli`, that is `node_modules/@indigoai-us/hq-cli`).
191
- */
192
- const INSTALLED_ROOT_SEGMENTS = ["node_modules", ...CLI_PACKAGE_NAME.split("/")];
193
- /** A Windows-shaped absolute path (drive letter or UNC), regardless of host OS. */
194
- function looksWin32Path(p) {
195
- return /^[a-zA-Z]:[\\/]/.test(p) || /^\\\\/.test(p);
196
- }
197
- /**
198
- * Derive this package's installed root from the running module's OWN path using
199
- * STRING operations only — no `readFileSync`, `realpathSync`, or `existsSync`.
200
- *
201
- * {@link packageRoot} must read `<dir>/package.json` and `realpathSync` the
202
- * module directory to answer, so it THROWS in exactly the situation that most
203
- * needs an answer: a concurrent global install has renamed the installed
204
- * package directory aside (`.hq-cli-<rand>`) and is re-extracting it file by
205
- * file, so both the manifest walk and the dist-owner fallback fail on a
206
- * directory that is momentarily gone. This walks the compiled module's
207
- * ancestors as strings and returns the DEEPEST one whose trailing segments are
208
- * `node_modules/@indigoai-us/hq-cli`, which stays correct while that directory
209
- * does not exist on disk.
210
- *
211
- * It is a FALLBACK only — {@link packageRoot} validates the manifest name and
212
- * is authoritative whenever it succeeds. Returns null for a dev checkout or any
213
- * layout without that segment triple, so a caller that falls back to it can
214
- * only ever degrade to today's behaviour (no resolution), never widen it.
215
- *
216
- * The deepest match is deliberate: a nested `node_modules/@indigoai-us/hq-cli`
217
- * inside another package resolves to ITSELF (the copy the module belongs to),
218
- * never an outer decoy. Case is folded only for a Windows-shaped path, matching
219
- * the POSIX case-sensitivity the rest of this module assumes.
220
- */
221
- export function stringDerivedPackageRoot(moduleFilePath = currentModulePath) {
222
- if (typeof moduleFilePath !== "string" || moduleFilePath.length === 0) {
223
- return null;
224
- }
225
- const wanted = INSTALLED_ROOT_SEGMENTS;
226
- const caseInsensitive = looksWin32Path(moduleFilePath);
227
- const sameSegment = (a, b) => caseInsensitive ? a.toLowerCase() === b.toLowerCase() : a === b;
228
- // Tokenise into segments with each one's end offset in the ORIGINAL string,
229
- // so the returned root keeps the source path's leading root and separators
230
- // verbatim (a leading `/`, or a `C:\` drive) rather than a rejoined guess.
231
- const segments = [];
232
- const segmentPattern = /[^\\/]+/g;
233
- let match;
234
- while ((match = segmentPattern.exec(moduleFilePath)) !== null) {
235
- segments.push({ text: match[0], end: match.index + match[0].length });
236
- }
237
- if (segments.length < wanted.length)
238
- return null;
239
- // Scan from the deepest segment upward; the first (deepest) leaf whose
240
- // preceding segments complete the triple wins.
241
- for (let leaf = segments.length - 1; leaf >= wanted.length - 1; leaf--) {
242
- let matched = true;
243
- for (let k = 0; k < wanted.length; k++) {
244
- if (!sameSegment(segments[leaf - (wanted.length - 1) + k].text, wanted[k])) {
245
- matched = false;
246
- break;
247
- }
248
- }
249
- if (matched)
250
- return moduleFilePath.slice(0, segments[leaf].end);
251
- }
252
- return null;
253
- }
254
- /**
255
- * Resolve the user's live HQ installation.
256
- *
257
- * Precedence: `--hq-root` → `HQ_ROOT` → `CLAUDE_PROJECT_DIR` → walk up from cwd.
258
- *
259
- * ## An explicit root is trusted; a discovered one must be recognized
260
- *
261
- * The FIRST explicit candidate present wins outright — there is no fall-through
262
- * past it, because silently operating on a different tree than the caller named
263
- * is the worst available outcome.
264
- *
265
- * That explicit candidate is checked only for the two things that are always
266
- * wrong: it must be a directory, and it must not be inside {@link packageRoot}
267
- * (that is this package, not anyone's HQ). It is deliberately NOT required to
268
- * satisfy {@link isHqRoot}.
269
- *
270
- * Requiring markers here was tried and was wrong: the scripts these roots feed
271
- * accepted any `HQ_ROOT` before they moved, and several callers legitimately pass
272
- * a minimal tree — e.g. `rebuild-threads-index` against a fixture holding nothing
273
- * but `workspace/threads/`. Enforcing markers on an explicit value turned a
274
- * relocation into a silent contract change and broke those callers.
275
- *
276
- * The cwd walk is the opposite case: there the marker check is the only thing
277
- * that can identify a root at all, so it stays mandatory.
278
- */
279
- export function resolveLiveRoot(opts = {}) {
280
- const env = opts.env ?? process.env;
281
- const candidates = [];
282
- if (opts.hqRoot) {
283
- candidates.push({ value: opts.hqRoot, source: "--hq-root" });
284
- }
285
- if (env.HQ_ROOT) {
286
- candidates.push({ value: env.HQ_ROOT, source: "$HQ_ROOT" });
287
- }
288
- if (env.CLAUDE_PROJECT_DIR) {
289
- candidates.push({
290
- value: env.CLAUDE_PROJECT_DIR,
291
- source: "$CLAUDE_PROJECT_DIR",
292
- });
293
- }
294
- const explicit = candidates[0];
295
- if (explicit) {
296
- const resolved = realpathOrResolve(explicit.value);
297
- if (isWithinPackage(resolved)) {
298
- throw rootError(`${explicit.source} points inside the ${CLI_PACKAGE_NAME} package ` +
299
- `(${resolved}). That is the CLI's own install directory, not an HQ tree.`);
300
- }
301
- if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
302
- throw rootError(`${explicit.source} is not a directory: ${resolved}`);
303
- }
304
- return resolved;
305
- }
306
- // Walk up from cwd. Unlike the explicit tiers this is a guess, so failing to
307
- // find a root here is reported as "run this from inside HQ" rather than as a
308
- // bad value.
309
- let dir = realpathOrResolve(opts.cwd ?? process.cwd());
310
- for (;;) {
311
- if (isHqRoot(dir) && !isWithinPackage(dir))
312
- return dir;
313
- const parent = path.dirname(dir);
314
- if (parent === dir)
315
- break;
316
- dir = parent;
317
- }
318
- throw rootError("Could not find your HQ installation. Run this from inside your HQ " +
319
- "directory, or pass --hq-root <path> / set $HQ_ROOT.");
320
- }
321
- function isWithinPackage(candidate) {
322
- let root;
323
- try {
324
- root = packageRoot();
325
- }
326
- catch {
327
- // If we cannot locate our own package we cannot be inside it either.
328
- return false;
329
- }
330
- const rel = path.relative(root, candidate);
331
- return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
332
- }
333
- /**
334
- * Absolute path to a scaffold asset bundled in this package.
335
- *
336
- * Refuses to resolve outside the asset directory so a manifest entry can never
337
- * be coaxed into executing an arbitrary path, and fails loudly when the asset
338
- * is missing — an absent asset means the packlist dropped it, which is a
339
- * packaging bug worth surfacing rather than a runtime fallback.
340
- */
341
- export function resolveBundledAsset(relativePath) {
342
- const assetDir = path.join(packageRoot(), BUNDLED_ASSET_DIR);
343
- const resolved = path.resolve(assetDir, relativePath);
344
- const rel = path.relative(assetDir, resolved);
345
- if (rel.startsWith("..") || path.isAbsolute(rel)) {
346
- throw new Error(`Bundled asset path escapes ${BUNDLED_ASSET_DIR}: ${relativePath}`);
347
- }
348
- if (!fs.existsSync(resolved)) {
349
- throw new Error(`Bundled asset is missing from the installed package: ${BUNDLED_ASSET_DIR}/${relativePath}\n` +
350
- "This is a packaging fault — the file exists in source but was not published.");
351
- }
352
- return resolved;
353
- }
1
+ /** Compatibility entrypoint for the full command graph. */
2
+ export * from "./hq-roots-core.js";
354
3
  //# sourceMappingURL=hq-roots.js.map
@@ -0,0 +1,3 @@
1
+ /** Compare executable and module paths after resolving npm-style symlinks. */
2
+ export declare function isMainModule(moduleUrl: string, argvEntryPath?: string | undefined): boolean;
3
+ //# sourceMappingURL=is-main-module.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { realpathSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ /** Compare executable and module paths after resolving npm-style symlinks. */
5
+ export function isMainModule(moduleUrl, argvEntryPath = process.argv[1]) {
6
+ if (!argvEntryPath)
7
+ return false;
8
+ const modulePath = fileURLToPath(moduleUrl);
9
+ const invokedPath = path.resolve(argvEntryPath);
10
+ try {
11
+ return realpathSync(modulePath) === realpathSync(invokedPath);
12
+ }
13
+ catch {
14
+ // Direct invocation still works if path canonicalization is unavailable;
15
+ // an unresolved symlink does not get treated as the module entry point.
16
+ return modulePath === invokedPath;
17
+ }
18
+ }
19
+ //# sourceMappingURL=is-main-module.js.map
@@ -1,40 +1,10 @@
1
1
  /**
2
- * Bounded diagnostics for failures to locate the CLI package root.
3
- *
4
- * These values are sent to Sentry, whose general scrubber deliberately keeps
5
- * filesystem paths intact. Keep this module the single construction boundary
6
- * so a malformed install cannot turn an error report into an unbounded or
7
- * multi-line payload.
2
+ * Bounded Sentry context for failures to locate the CLI package root.
3
+ * The error value itself is kept in a dependency-light module for fast paths.
8
4
  */
9
- type PackageRootVisitOutcome = "no-manifest" | "eacces" | "eisdir" | "parse-error" | "name-mismatch" | "read-error" | "omitted";
10
- export type PackageRootVisit = {
11
- directory: string;
12
- outcome: PackageRootVisitOutcome;
13
- code?: string;
14
- manifestName?: string;
15
- };
16
- export type PackageRootResolutionDiagnostics = {
17
- modulePath: string;
18
- startDir: string;
19
- strategiesTried: string[];
20
- visited: PackageRootVisit[];
21
- fallbackRejectedBecause: string;
22
- };
23
- type RawDiagnostics = {
24
- modulePath: string;
25
- startDir: string;
26
- strategiesTried: string[];
27
- visited: PackageRootVisit[];
28
- fallbackRejectedBecause: string;
29
- };
30
- /** Replace every Unicode control character before bounding a diagnostic value. */
31
- export declare function boundedDiagnosticValue(value: unknown, maxBytes: number): string;
32
- /** An unmarked packaging fault that should remain visible in Sentry. */
33
- export declare class PackageRootResolutionError extends Error {
34
- readonly diagnostics: PackageRootResolutionDiagnostics;
35
- constructor(raw: RawDiagnostics);
36
- }
37
- export declare function isPackageRootResolutionError(err: unknown): err is PackageRootResolutionError;
5
+ import { PackageRootResolutionError, type PackageRootResolutionDiagnostics } from "./package-root-resolution-error.js";
6
+ export { PackageRootResolutionError, isPackageRootResolutionError, type PackageRootVisit, type PackageRootResolutionDiagnostics, } from "./package-root-resolution-error.js";
7
+ export { boundedDiagnosticValue } from "./package-root-resolution-error.js";
38
8
  /**
39
9
  * Produce the exact Sentry contexts shape while enforcing a whole-payload cap.
40
10
  * The tail is dropped first because nearest directories contain the useful
@@ -43,5 +13,4 @@ export declare function isPackageRootResolutionError(err: unknown): err is Packa
43
13
  export declare function packageRootCaptureContext(err: PackageRootResolutionError): {
44
14
  package_root_resolution: PackageRootResolutionDiagnostics;
45
15
  };
46
- export {};
47
16
  //# sourceMappingURL=package-root-diagnostics.d.ts.map
@@ -1,90 +1,11 @@
1
1
  /**
2
- * Bounded diagnostics for failures to locate the CLI package root.
3
- *
4
- * These values are sent to Sentry, whose general scrubber deliberately keeps
5
- * filesystem paths intact. Keep this module the single construction boundary
6
- * so a malformed install cannot turn an error report into an unbounded or
7
- * multi-line payload.
2
+ * Bounded Sentry context for failures to locate the CLI package root.
3
+ * The error value itself is kept in a dependency-light module for fast paths.
8
4
  */
9
5
  import { beforeSend } from "../sentry-before-send.js";
10
- const CONTROL_CHARACTER = /[\p{C}\p{Zl}\p{Zp}]/gu;
11
- const REPLACEMENT = "?";
12
- const ELLIPSIS = "…";
13
- const MODULE_PATH_BYTES = 256;
14
- const START_DIR_BYTES = 256;
15
- const DIRECTORY_BYTES = 256;
16
- const MANIFEST_NAME_BYTES = 64;
17
- const CODE_BYTES = 32;
18
- const MESSAGE_BYTES = 2048;
6
+ export { PackageRootResolutionError, isPackageRootResolutionError, } from "./package-root-resolution-error.js";
7
+ export { boundedDiagnosticValue } from "./package-root-resolution-error.js";
19
8
  const CONTEXT_BYTES = 4096;
20
- const MAX_VISITED = 12;
21
- /** Replace every Unicode control character before bounding a diagnostic value. */
22
- export function boundedDiagnosticValue(value, maxBytes) {
23
- const normalized = String(value).replace(CONTROL_CHARACTER, REPLACEMENT);
24
- if (Buffer.byteLength(normalized, "utf8") <= maxBytes)
25
- return normalized;
26
- const markerBytes = Buffer.byteLength(ELLIPSIS, "utf8");
27
- let result = "";
28
- let used = 0;
29
- for (const character of normalized) {
30
- const bytes = Buffer.byteLength(character, "utf8");
31
- if (used + bytes + markerBytes > maxBytes)
32
- break;
33
- result += character;
34
- used += bytes;
35
- }
36
- return result + ELLIPSIS;
37
- }
38
- function boundedVisit(visit) {
39
- return {
40
- directory: boundedDiagnosticValue(visit.directory, DIRECTORY_BYTES),
41
- outcome: visit.outcome,
42
- ...(visit.code === undefined
43
- ? {}
44
- : { code: boundedDiagnosticValue(visit.code, CODE_BYTES) }),
45
- ...(visit.manifestName === undefined
46
- ? {}
47
- : { manifestName: boundedDiagnosticValue(visit.manifestName, MANIFEST_NAME_BYTES) }),
48
- };
49
- }
50
- function boundedVisits(visits) {
51
- if (visits.length <= MAX_VISITED)
52
- return visits.map(boundedVisit);
53
- const shown = visits.slice(0, MAX_VISITED - 1).map(boundedVisit);
54
- shown.push({
55
- directory: `${visits.length - shown.length} more omitted`,
56
- outcome: "omitted",
57
- });
58
- return shown;
59
- }
60
- function normalizeDiagnostics(raw) {
61
- return {
62
- modulePath: boundedDiagnosticValue(raw.modulePath, MODULE_PATH_BYTES),
63
- startDir: boundedDiagnosticValue(raw.startDir, START_DIR_BYTES),
64
- strategiesTried: raw.strategiesTried.map((strategy) => boundedDiagnosticValue(strategy, CODE_BYTES)),
65
- visited: boundedVisits(raw.visited),
66
- fallbackRejectedBecause: boundedDiagnosticValue(raw.fallbackRejectedBecause, DIRECTORY_BYTES),
67
- };
68
- }
69
- function messageFor(diagnostics) {
70
- return boundedDiagnosticValue("Could not locate the @indigoai-us/hq-cli package root from " +
71
- `${diagnostics.modulePath}; manifest walk exhausted from ${diagnostics.startDir}; ` +
72
- `dist-owner fallback ${diagnostics.fallbackRejectedBecause}.`, MESSAGE_BYTES);
73
- }
74
- /** An unmarked packaging fault that should remain visible in Sentry. */
75
- export class PackageRootResolutionError extends Error {
76
- diagnostics;
77
- constructor(raw) {
78
- const diagnostics = normalizeDiagnostics(raw);
79
- super(messageFor(diagnostics));
80
- this.name = "PackageRootResolutionError";
81
- this.diagnostics = diagnostics;
82
- Object.setPrototypeOf(this, new.target.prototype);
83
- }
84
- }
85
- export function isPackageRootResolutionError(err) {
86
- return err instanceof PackageRootResolutionError;
87
- }
88
9
  /**
89
10
  * Produce the exact Sentry contexts shape while enforcing a whole-payload cap.
90
11
  * The tail is dropped first because nearest directories contain the useful
@@ -0,0 +1,32 @@
1
+ /** Lightweight packaging error payload; Sentry formatting lives separately. */
2
+ type PackageRootVisitOutcome = "no-manifest" | "eacces" | "eisdir" | "parse-error" | "name-mismatch" | "read-error" | "omitted";
3
+ export type PackageRootVisit = {
4
+ directory: string;
5
+ outcome: PackageRootVisitOutcome;
6
+ code?: string;
7
+ manifestName?: string;
8
+ };
9
+ export type PackageRootResolutionDiagnostics = {
10
+ modulePath: string;
11
+ startDir: string;
12
+ strategiesTried: string[];
13
+ visited: PackageRootVisit[];
14
+ fallbackRejectedBecause: string;
15
+ };
16
+ type RawDiagnostics = {
17
+ modulePath: string;
18
+ startDir: string;
19
+ strategiesTried: string[];
20
+ visited: PackageRootVisit[];
21
+ fallbackRejectedBecause: string;
22
+ };
23
+ /** Replace every Unicode control character before bounding a diagnostic value. */
24
+ export declare function boundedDiagnosticValue(value: unknown, maxBytes: number): string;
25
+ /** An unmarked packaging fault that should remain visible in Sentry. */
26
+ export declare class PackageRootResolutionError extends Error {
27
+ readonly diagnostics: PackageRootResolutionDiagnostics;
28
+ constructor(raw: RawDiagnostics);
29
+ }
30
+ export declare function isPackageRootResolutionError(err: unknown): err is PackageRootResolutionError;
31
+ export {};
32
+ //# sourceMappingURL=package-root-resolution-error.d.ts.map
@@ -0,0 +1,79 @@
1
+ /** Lightweight packaging error payload; Sentry formatting lives separately. */
2
+ const CONTROL_CHARACTER = /[\p{C}\p{Zl}\p{Zp}]/gu;
3
+ const REPLACEMENT = "?";
4
+ const ELLIPSIS = "…";
5
+ const MODULE_PATH_BYTES = 256;
6
+ const START_DIR_BYTES = 256;
7
+ const DIRECTORY_BYTES = 256;
8
+ const MANIFEST_NAME_BYTES = 64;
9
+ const CODE_BYTES = 32;
10
+ const MESSAGE_BYTES = 2048;
11
+ const MAX_VISITED = 12;
12
+ /** Replace every Unicode control character before bounding a diagnostic value. */
13
+ export function boundedDiagnosticValue(value, maxBytes) {
14
+ const normalized = String(value).replace(CONTROL_CHARACTER, REPLACEMENT);
15
+ if (Buffer.byteLength(normalized, "utf8") <= maxBytes)
16
+ return normalized;
17
+ const markerBytes = Buffer.byteLength(ELLIPSIS, "utf8");
18
+ let result = "";
19
+ let used = 0;
20
+ for (const character of normalized) {
21
+ const bytes = Buffer.byteLength(character, "utf8");
22
+ if (used + bytes + markerBytes > maxBytes)
23
+ break;
24
+ result += character;
25
+ used += bytes;
26
+ }
27
+ return result + ELLIPSIS;
28
+ }
29
+ function boundedVisit(visit) {
30
+ return {
31
+ directory: boundedDiagnosticValue(visit.directory, DIRECTORY_BYTES),
32
+ outcome: visit.outcome,
33
+ ...(visit.code === undefined
34
+ ? {}
35
+ : { code: boundedDiagnosticValue(visit.code, CODE_BYTES) }),
36
+ ...(visit.manifestName === undefined
37
+ ? {}
38
+ : { manifestName: boundedDiagnosticValue(visit.manifestName, MANIFEST_NAME_BYTES) }),
39
+ };
40
+ }
41
+ function boundedVisits(visits) {
42
+ if (visits.length <= MAX_VISITED)
43
+ return visits.map(boundedVisit);
44
+ const shown = visits.slice(0, MAX_VISITED - 1).map(boundedVisit);
45
+ shown.push({
46
+ directory: String(visits.length - shown.length) + " more omitted",
47
+ outcome: "omitted",
48
+ });
49
+ return shown;
50
+ }
51
+ function normalizeDiagnostics(raw) {
52
+ return {
53
+ modulePath: boundedDiagnosticValue(raw.modulePath, MODULE_PATH_BYTES),
54
+ startDir: boundedDiagnosticValue(raw.startDir, START_DIR_BYTES),
55
+ strategiesTried: raw.strategiesTried.map((strategy) => boundedDiagnosticValue(strategy, CODE_BYTES)),
56
+ visited: boundedVisits(raw.visited),
57
+ fallbackRejectedBecause: boundedDiagnosticValue(raw.fallbackRejectedBecause, DIRECTORY_BYTES),
58
+ };
59
+ }
60
+ function messageFor(diagnostics) {
61
+ return boundedDiagnosticValue("Could not locate the @indigoai-us/hq-cli package root from " +
62
+ diagnostics.modulePath + "; manifest walk exhausted from " + diagnostics.startDir +
63
+ "; dist-owner fallback " + diagnostics.fallbackRejectedBecause + ".", MESSAGE_BYTES);
64
+ }
65
+ /** An unmarked packaging fault that should remain visible in Sentry. */
66
+ export class PackageRootResolutionError extends Error {
67
+ diagnostics;
68
+ constructor(raw) {
69
+ const diagnostics = normalizeDiagnostics(raw);
70
+ super(messageFor(diagnostics));
71
+ this.name = "PackageRootResolutionError";
72
+ this.diagnostics = diagnostics;
73
+ Object.setPrototypeOf(this, new.target.prototype);
74
+ }
75
+ }
76
+ export function isPackageRootResolutionError(err) {
77
+ return err instanceof PackageRootResolutionError;
78
+ }
79
+ //# sourceMappingURL=package-root-resolution-error.js.map
@@ -3,6 +3,7 @@ import { randomBytes } from "node:crypto";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
+ import { isMainModule } from "./is-main-module.js";
6
7
  const PACKAGE_NAME = "@indigoai-us/hq-cli";
7
8
  const RANDOM_HEX_BYTES = 6;
8
9
  function packageRoot(prefix, platform) {
@@ -239,7 +240,7 @@ export function activateStagedNpmPackage(prefix, stagePrefix, platform = process
239
240
  bestEffortRemove(backupPackage);
240
241
  }
241
242
  }
242
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url) && process.argv[2] === "--activate") {
243
+ if (isMainModule(import.meta.url) && process.argv[2] === "--activate") {
243
244
  const [prefix, stagePrefix, platform] = process.argv.slice(3);
244
245
  if (!prefix || !stagePrefix) {
245
246
  console.error("usage: staged-npm-install.js --activate <prefix> <stage-prefix> [platform]");