@indigoai-us/hq-cli 5.342.5 → 5.342.7

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/CHANGELOG.md CHANGED
@@ -2,7 +2,20 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.342.7] — 2026-10-04
6
+ - Batch policy frontmatter reads into one `hq core policy frontmatter` invocation while preserving single-file output.
7
+
8
+ ## [5.342.6] — 2026-10-04
9
+ - Add `hq mesh project member add` to add an active company member to a project.
10
+
11
+ - With `cli.company-errors-sdk-warning` enabled, `hq people` now scopes to a sole cloud membership when the local company manifest is missing.
12
+ - Run the package-use lease macOS and Windows checks on pull requests only when lease inputs change, while retaining both checks on main pushes.
13
+ - Keep `hq secrets env --company <slug>` on the lightweight fast route when the company option follows `env`.
14
+
5
15
  ## [5.342.5] — 2026-10-04
16
+
17
+ - Keep pnpm v11 global install detection and Windows drive-path tar extraction unconditional; remove their legacy opt-outs.
18
+
6
19
  - Pass explicit hook stdin to bundled shell companions through a real pipe on Windows.
7
20
  - Run the paired fast-route benchmark only when its executable or build inputs change.
8
21
  - Limit `hq mcp serve` resident preloads to its request handler modules; hosted MCP client dependencies remain in the daemon process.
@@ -2,6 +2,7 @@
2
2
  # read-policy-frontmatter.sh — Extract YAML frontmatter from a policy file
3
3
  #
4
4
  # Usage: bash core/scripts/read-policy-frontmatter.sh <policy-file>
5
+ # bash core/scripts/read-policy-frontmatter.sh <policy-file> <policy-file> ...
5
6
  #
6
7
  # Returns only the YAML frontmatter block (between the first two --- markers),
7
8
  # skipping the policy body. Used by /startwork, /plan, /brainstorm, /run commands
@@ -20,13 +21,19 @@ if [ $# -lt 1 ]; then
20
21
  exit 1
21
22
  fi
22
23
 
23
- FILE="$1"
24
+ INDEX=0
25
+ for FILE in "$@"; do
26
+ if [ ! -r "$FILE" ]; then
27
+ echo "ERROR: File not readable: $FILE" >&2
28
+ exit 1
29
+ fi
24
30
 
25
- if [ ! -r "$FILE" ]; then
26
- echo "ERROR: File not readable: $FILE" >&2
27
- exit 1
28
- fi
31
+ if [ "$INDEX" -gt 0 ]; then
32
+ printf '\n# --- policy-file: %s ---\n' "$FILE"
33
+ fi
34
+ INDEX=$((INDEX + 1))
29
35
 
30
- # Extract frontmatter: lines between first two --- markers.
31
- # c counts --- markers; print lines only while inside the frontmatter (c==1).
32
- awk '/^---$/{c++; next} c==1' "$FILE"
36
+ # Extract frontmatter: lines between first two --- markers.
37
+ # c counts --- markers; print lines only while inside the frontmatter (c==1).
38
+ awk '/^---$/{c++; next} c==1' "$FILE"
39
+ done
@@ -9296,6 +9296,48 @@ export declare const COMMAND_CATALOG: readonly [{
9296
9296
  readonly hidden: false;
9297
9297
  }];
9298
9298
  readonly subcommands: readonly [];
9299
+ }, {
9300
+ readonly name: "member";
9301
+ readonly description: "Manage project members";
9302
+ readonly aliases: readonly [];
9303
+ readonly hidden: false;
9304
+ readonly helpOption: true;
9305
+ readonly helpCommand: true;
9306
+ readonly usage: "[options] [command]";
9307
+ readonly helpText: readonly [];
9308
+ readonly arguments: readonly [];
9309
+ readonly options: readonly [];
9310
+ readonly subcommands: readonly [{
9311
+ readonly name: "add";
9312
+ readonly description: "Add an active company member to a project";
9313
+ readonly aliases: readonly [];
9314
+ readonly hidden: false;
9315
+ readonly helpOption: true;
9316
+ readonly helpCommand: false;
9317
+ readonly usage: "[options] <projectId> <personUid>";
9318
+ readonly helpText: readonly [];
9319
+ readonly arguments: readonly [{
9320
+ readonly name: "projectId";
9321
+ readonly required: true;
9322
+ readonly variadic: false;
9323
+ readonly description: "";
9324
+ }, {
9325
+ readonly name: "personUid";
9326
+ readonly required: true;
9327
+ readonly variadic: false;
9328
+ readonly description: "";
9329
+ }];
9330
+ readonly options: readonly [{
9331
+ readonly flags: "--company <slug|uid>";
9332
+ readonly description: "Company slug or cloud uid";
9333
+ readonly hidden: false;
9334
+ }, {
9335
+ readonly flags: "--json";
9336
+ readonly description: "Print machine-readable JSON";
9337
+ readonly hidden: false;
9338
+ }];
9339
+ readonly subcommands: readonly [];
9340
+ }];
9299
9341
  }];
9300
9342
  }, {
9301
9343
  readonly name: "doctor";
@@ -11271,6 +11271,57 @@ export const COMMAND_CATALOG = [
11271
11271
  }
11272
11272
  ],
11273
11273
  "subcommands": []
11274
+ },
11275
+ {
11276
+ "name": "member",
11277
+ "description": "Manage project members",
11278
+ "aliases": [],
11279
+ "hidden": false,
11280
+ "helpOption": true,
11281
+ "helpCommand": true,
11282
+ "usage": "[options] [command]",
11283
+ "helpText": [],
11284
+ "arguments": [],
11285
+ "options": [],
11286
+ "subcommands": [
11287
+ {
11288
+ "name": "add",
11289
+ "description": "Add an active company member to a project",
11290
+ "aliases": [],
11291
+ "hidden": false,
11292
+ "helpOption": true,
11293
+ "helpCommand": false,
11294
+ "usage": "[options] <projectId> <personUid>",
11295
+ "helpText": [],
11296
+ "arguments": [
11297
+ {
11298
+ "name": "projectId",
11299
+ "required": true,
11300
+ "variadic": false,
11301
+ "description": ""
11302
+ },
11303
+ {
11304
+ "name": "personUid",
11305
+ "required": true,
11306
+ "variadic": false,
11307
+ "description": ""
11308
+ }
11309
+ ],
11310
+ "options": [
11311
+ {
11312
+ "flags": "--company <slug|uid>",
11313
+ "description": "Company slug or cloud uid",
11314
+ "hidden": false
11315
+ },
11316
+ {
11317
+ "flags": "--json",
11318
+ "description": "Print machine-readable JSON",
11319
+ "hidden": false
11320
+ }
11321
+ ],
11322
+ "subcommands": []
11323
+ }
11324
+ ]
11274
11325
  }
11275
11326
  ]
11276
11327
  },
@@ -2,37 +2,58 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import { usesGnuAwk } from '../lib/core-utils/awk-diagnostics.js';
4
4
  const USAGE = Buffer.from('USAGE: read-policy-frontmatter.sh <policy-file>\n');
5
+ const MULTI_FILE_SEPARATOR = (file) => Buffer.from(`\n# --- policy-file: ${file} ---\n`);
5
6
  /** Byte-preserving port of core/scripts/read-policy-frontmatter.sh. */
6
7
  export function runPolicyFrontmatter(args, cwd, pathValue = process.env.PATH, platform = process.platform) {
7
- const first = args[0];
8
- if (first === undefined)
8
+ if (args.length === 0)
9
9
  return { code: 1, stdout: Buffer.alloc(0), stderr: USAGE };
10
- const filePath = path.isAbsolute(first) ? first : path.resolve(cwd, first);
11
- let source;
12
- try {
13
- fs.accessSync(filePath, fs.constants.R_OK);
14
- source = fs.readFileSync(filePath);
15
- }
16
- catch (error) {
17
- const code = error.code;
18
- if (code === 'EISDIR') {
19
- return {
20
- code: 0,
21
- stdout: Buffer.alloc(0),
22
- stderr: Buffer.from(usesGnuAwk(pathValue, platform)
23
- ? `awk: warning: command line argument \`${first}' is a directory: skipped\n`
24
- : ''),
25
- };
10
+ const output = [];
11
+ const diagnostics = [];
12
+ for (let index = 0; index < args.length; index += 1) {
13
+ const file = args[index];
14
+ const filePath = path.isAbsolute(file) ? file : path.resolve(cwd, file);
15
+ try {
16
+ fs.accessSync(filePath, fs.constants.R_OK);
17
+ }
18
+ catch (error) {
19
+ const code = error.code;
20
+ if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES' || code === 'ELOOP') {
21
+ return {
22
+ code: 1,
23
+ stdout: Buffer.concat(output),
24
+ stderr: Buffer.concat([...diagnostics, Buffer.from(`ERROR: File not readable: ${file}\n`)]),
25
+ };
26
+ }
27
+ throw error;
26
28
  }
27
- if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES' || code === 'ELOOP') {
28
- return {
29
- code: 1,
30
- stdout: Buffer.alloc(0),
31
- stderr: Buffer.from(`ERROR: File not readable: ${first}\n`),
32
- };
29
+ if (index > 0)
30
+ output.push(MULTI_FILE_SEPARATOR(file));
31
+ let source;
32
+ try {
33
+ source = fs.readFileSync(filePath);
33
34
  }
34
- throw error;
35
+ catch (error) {
36
+ const code = error.code;
37
+ if (code === 'EISDIR') {
38
+ if (usesGnuAwk(pathValue, platform)) {
39
+ diagnostics.push(Buffer.from(`awk: warning: command line argument \`${file}' is a directory: skipped\n`));
40
+ }
41
+ continue;
42
+ }
43
+ if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES' || code === 'ELOOP') {
44
+ return {
45
+ code: 1,
46
+ stdout: Buffer.concat(output),
47
+ stderr: Buffer.concat([...diagnostics, Buffer.from(`ERROR: File not readable: ${file}\n`)]),
48
+ };
49
+ }
50
+ throw error;
51
+ }
52
+ output.push(extractFrontmatter(source, platform));
35
53
  }
54
+ return { code: 0, stdout: Buffer.concat(output), stderr: Buffer.concat(diagnostics) };
55
+ }
56
+ function extractFrontmatter(source, platform) {
36
57
  const output = [];
37
58
  let markers = 0;
38
59
  let start = 0;
@@ -53,6 +74,6 @@ export function runPolicyFrontmatter(args, cwd, pathValue = process.env.PATH, pl
53
74
  break;
54
75
  start = end + 1;
55
76
  }
56
- return { code: 0, stdout: Buffer.concat(output), stderr: Buffer.alloc(0) };
77
+ return Buffer.concat(output);
57
78
  }
58
79
  //# sourceMappingURL=core-policy-frontmatter.js.map
@@ -13,7 +13,7 @@ import { CLI_VERSION } from "../cli-version.js";
13
13
  import chalk from "chalk";
14
14
  import { ensureCognitoToken, isExpiring, isMachineIdentity, loadCachedTokens, } from "../utils/cognito-session.js";
15
15
  import * as readline from "node:readline/promises";
16
- import { STORY_STATUSES, appendThreadEvent, ensureProjectThread, eventPayload, getProjectView, listActiveMembershipCompanies, listActiveThreads, patchStory, putProjectView, registerMeshProject, resolveActiveMembershipCompany, resolveMeshPrincipalUid, warmMeshConversationCache, } from "../lib/mesh/api.js";
16
+ import { STORY_STATUSES, appendThreadEvent, addProjectMember, ensureProjectThread, eventPayload, getProjectView, listActiveMembershipCompanies, listActiveThreads, patchStory, putProjectView, registerMeshProject, resolveActiveMembershipCompany, resolveMeshPrincipalUid, warmMeshConversationCache, } from "../lib/mesh/api.js";
17
17
  import { closeSupersededProjectThread, ensureMissingBoardStories, ensureProjectBoard, boardStoryStatus, formatStoryStatusChanges, mergeProjectStories, prdSyncEventsThatChangeBoard, } from "../lib/mesh/project-board.js";
18
18
  import { mapSourceStoryStatus } from "../lib/mesh/story-status.js";
19
19
  import { buildPreflightFixtures } from "../lib/work-context/preflight-contract.js";
@@ -461,6 +461,17 @@ async function runProjectRegister(projectId, opts) {
461
461
  }
462
462
  console.log(`Work mesh: project ${registration.projectId} registered (${boardState}; channel ${registration.channelId} ${registration.channelCreated ? "created" : "bound"})`);
463
463
  }
464
+ async function runProjectMemberAdd(projectId, personUid, opts) {
465
+ const { token, company } = await withCompany(opts);
466
+ const result = await addProjectMember(token, company.companyUid, projectId, personUid);
467
+ if (opts.json) {
468
+ console.log(JSON.stringify({ action: "project.member.add", ...result }, null, 2));
469
+ return;
470
+ }
471
+ console.log(result.added
472
+ ? `Work mesh: added ${personUid} to project ${projectId}`
473
+ : `Work mesh: ${personUid} is already a member of project ${projectId}`);
474
+ }
464
475
  async function runProjectEnsure(projectId, opts) {
465
476
  if (!opts.storiesFile?.trim())
466
477
  fail("`--stories-file <prd.json>` is required.");
@@ -1910,6 +1921,14 @@ export function registerMeshCommand(program) {
1910
1921
  .option("--channel <channelId>", "Bind the project to an existing company or project channel")
1911
1922
  .option("--json", "Print machine-readable JSON")
1912
1923
  .action((projectId, opts) => wrap(() => runProjectRegister(projectId, opts))());
1924
+ project
1925
+ .command("member")
1926
+ .description("Manage project members")
1927
+ .command("add <projectId> <personUid>")
1928
+ .description("Add an active company member to a project")
1929
+ .requiredOption("--company <slug|uid>", "Company slug or cloud uid")
1930
+ .option("--json", "Print machine-readable JSON")
1931
+ .action((projectId, personUid, opts) => wrap(() => runProjectMemberAdd(projectId, personUid, opts))());
1913
1932
  mesh
1914
1933
  .command("doctor")
1915
1934
  .description("Warm ~/.hq/work-mesh/cache from hq-pro (directory, inbox, pair DMs). Not `hq doctor`.")
@@ -18,7 +18,7 @@ export type FetchPeopleRoster = (hqRoot: string, companySlug: string) => Promise
18
18
  interface PeopleCommandDeps {
19
19
  fetchRoster?: FetchPeopleRoster;
20
20
  /** Test seam for the shared active-membership ambiguity resolver. */
21
- resolveMissingManifestMemberships?: () => Promise<void>;
21
+ resolveMissingManifestMemberships?: () => Promise<string | void>;
22
22
  companySelectionGuidanceEnabled?: () => boolean;
23
23
  }
24
24
  interface PeopleLookupOpts {
@@ -122,12 +122,16 @@ async function resolvePeopleCompanySlug(hqRoot, explicit, localOnly, deps) {
122
122
  const resolveMemberships = deps.resolveMissingManifestMemberships ??
123
123
  (async () => {
124
124
  const token = await ensureCognitoToken();
125
- await getCompanyUid(token, undefined);
125
+ return getCompanyUid(token, undefined);
126
126
  });
127
127
  // The shared resolver throws members-style guidance for multiple active
128
- // companies. A single membership cannot provide a local slug without a
129
- // manifest, so preserve the existing missing-manifest error in that case.
130
- await resolveMemberships();
128
+ // companies. With no manifest, its sole-membership UID is also a valid
129
+ // scope for the people roster and company directory.
130
+ const membershipCompany = await resolveMemberships();
131
+ if (message.includes("companies/manifest.yaml not found") &&
132
+ typeof membershipCompany === "string") {
133
+ return membershipCompany;
134
+ }
131
135
  }
132
136
  throw error;
133
137
  }
@@ -111,12 +111,11 @@ export declare function assertLinkTargetContained(packDir: string, entryAbs: str
111
111
  /**
112
112
  * List a gzip-compressed tarball's entries via `tar -tvf` (no extraction).
113
113
  * argv form, never a shell string, keeps hostile filenames out of shell syntax.
114
- * Windows drive paths use a basename relative to the archive directory by
115
- * default. GNU tar cannot parse `C:\\...` as remote `host:path`, and Windows'
116
- * bundled bsdtar accepts the same relative argv without GNU-only `--force-local`.
117
- * Callers can still explicitly opt out with `useRelativeWindowsDrivePath: false`.
114
+ * Windows drive paths always use a basename relative to the archive directory.
115
+ * GNU tar cannot parse `C:\\...` as remote `host:path`, and Windows' bundled
116
+ * bsdtar accepts the same relative argv without GNU-only `--force-local`.
118
117
  */
119
- export declare function listTarball(tarballPath: string, opts?: Pick<SafeExtractOptions, 'platform' | 'useRelativeWindowsDrivePath' | 'execFileSync'>): TarEntry[];
118
+ export declare function listTarball(tarballPath: string, opts?: Pick<SafeExtractOptions, 'platform' | 'execFileSync'>): TarEntry[];
120
119
  /**
121
120
  * Pre-flight validation against the entry headers. Enforces (a) containment of
122
121
  * every entry name under `targetDir`, (b) containment of every link target
@@ -134,9 +133,8 @@ export declare function validateEntries(entries: TarEntry[], targetDir: string,
134
133
  export declare function assertExtractContained(stagingDir: string): void;
135
134
  export interface SafeExtractOptions {
136
135
  caps?: ExtractCaps;
137
- /** Test seams for platform-specific argv construction; runtime gating uses hq-flags. */
136
+ /** Test seam for platform-specific argv construction. */
138
137
  platform?: NodeJS.Platform;
139
- useRelativeWindowsDrivePath?: boolean;
140
138
  execFileSync?: typeof execFileSync;
141
139
  /**
142
140
  * Test seam: invoked AFTER `tar` extracts into the staging dir but BEFORE
@@ -203,10 +203,8 @@ export function assertLinkTargetContained(packDir, entryAbs, linkTarget) {
203
203
  `"${linkTarget}" -> "${resolvedTarget}", outside pack dir "${path.resolve(packDir)}".`);
204
204
  }
205
205
  }
206
- function tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath) {
207
- if (platform === 'win32' &&
208
- useRelativeWindowsDrivePath &&
209
- /^[A-Za-z]:[\\/]/.test(tarballPath)) {
206
+ function tarballArgvLocation(tarballPath, platform) {
207
+ if (platform === 'win32' && /^[A-Za-z]:[\\/]/.test(tarballPath)) {
210
208
  return {
211
209
  archiveArg: path.win32.basename(tarballPath),
212
210
  cwd: path.win32.dirname(tarballPath),
@@ -217,15 +215,13 @@ function tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath)
217
215
  /**
218
216
  * List a gzip-compressed tarball's entries via `tar -tvf` (no extraction).
219
217
  * argv form, never a shell string, keeps hostile filenames out of shell syntax.
220
- * Windows drive paths use a basename relative to the archive directory by
221
- * default. GNU tar cannot parse `C:\\...` as remote `host:path`, and Windows'
222
- * bundled bsdtar accepts the same relative argv without GNU-only `--force-local`.
223
- * Callers can still explicitly opt out with `useRelativeWindowsDrivePath: false`.
218
+ * Windows drive paths always use a basename relative to the archive directory.
219
+ * GNU tar cannot parse `C:\\...` as remote `host:path`, and Windows' bundled
220
+ * bsdtar accepts the same relative argv without GNU-only `--force-local`.
224
221
  */
225
222
  export function listTarball(tarballPath, opts = {}) {
226
223
  const platform = opts.platform ?? process.platform;
227
- const useRelativeWindowsDrivePath = opts.useRelativeWindowsDrivePath ?? true;
228
- const location = tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath);
224
+ const location = tarballArgvLocation(tarballPath, platform);
229
225
  const out = (opts.execFileSync ?? execFileSync)('tar', ['-tvf', location.archiveArg], {
230
226
  encoding: 'utf-8',
231
227
  maxBuffer: 64 * 1024 * 1024,
@@ -329,13 +325,11 @@ export function safeExtractTarball(tarballPath, finalDir, opts = {}) {
329
325
  }
330
326
  // Resolve once so listing and extraction use the same archive location.
331
327
  const platform = opts.platform ?? process.platform;
332
- const useRelativeWindowsDrivePath = opts.useRelativeWindowsDrivePath ?? true;
333
328
  const exec = opts.execFileSync ?? execFileSync;
334
- const location = tarballArgvLocation(tarballPath, platform, useRelativeWindowsDrivePath);
329
+ const location = tarballArgvLocation(tarballPath, platform);
335
330
  // 1. Pre-flight against headers (no bytes written yet).
336
331
  const entries = listTarball(tarballPath, {
337
332
  platform,
338
- useRelativeWindowsDrivePath,
339
333
  execFileSync: exec,
340
334
  });
341
335
  validateEntries(entries, finalDir, caps);
@@ -343,6 +343,24 @@ export function parseSecretsEnvFastRequest(argv) {
343
343
  const arg = argv[index];
344
344
  if (arg === "-h" || arg === "--help" || arg === "--")
345
345
  return null;
346
+ // Commander accepts the parent secrets scope option after the `env`
347
+ // subcommand too. Keep that valid spelling on the fast route.
348
+ if (arg === "--company") {
349
+ const value = argv[index + 1];
350
+ if (!value || value.startsWith("-") || hasPersonalScope)
351
+ return null;
352
+ scope.company = value;
353
+ index += 2;
354
+ continue;
355
+ }
356
+ if (arg?.startsWith("--company=")) {
357
+ const value = arg.slice("--company=".length);
358
+ if (!value || hasPersonalScope)
359
+ return null;
360
+ scope.company = value;
361
+ index += 1;
362
+ continue;
363
+ }
346
364
  let option;
347
365
  let value;
348
366
  if (arg === "--only" || arg === "--script" || arg === "--script-id") {
@@ -154,6 +154,13 @@ export declare function patchStoryStatus(token: string, companyUid: string, proj
154
154
  }>;
155
155
  /** Read the full durable Board view before preparing a replacement PUT. */
156
156
  export declare function getProjectView(token: string, companyUid: string, projectId: string): Promise<MeshProjectView>;
157
+ /** Add an active company member to the project's Work Mesh channel. */
158
+ export declare function addProjectMember(token: string, companyUid: string, projectId: string, personUid: string): Promise<{
159
+ ok: true;
160
+ projectId: string;
161
+ personUid: string;
162
+ added: boolean;
163
+ }>;
157
164
  /** Replace a project's durable Board view and keep the local project cache current. */
158
165
  export declare function putProjectView(token: string, companyUid: string, projectId: string, input: {
159
166
  name?: string;
@@ -465,6 +465,14 @@ export async function getProjectView(token, companyUid, projectId) {
465
465
  throw new Error("Work mesh returned an invalid project view");
466
466
  return projectViewFromResponse(view, companyUid, projectId);
467
467
  }
468
+ /** Add an active company member to the project's Work Mesh channel. */
469
+ export async function addProjectMember(token, companyUid, projectId, personUid) {
470
+ const result = asRecord(await meshJson(token, `/v1/work-mesh/projects/${encodeURIComponent(projectId)}`, { method: "PUT", body: { operation: "add-member", companyUid, personUid } }));
471
+ if (result?.ok !== true || typeof result.added !== "boolean") {
472
+ throw new Error("Work mesh returned an invalid project membership result");
473
+ }
474
+ return { ok: true, projectId, personUid, added: result.added };
475
+ }
468
476
  /** Replace a project's durable Board view and keep the local project cache current. */
469
477
  export async function putProjectView(token, companyUid, projectId, input) {
470
478
  const { expectedVersion, ...document } = input;
@@ -372,7 +372,7 @@ function isRecognizedGlobalInstall(install, packageRoot) {
372
372
  if (!isInsideNodeModules(packageRoot))
373
373
  return false;
374
374
  if (install.manager === "pnpm")
375
- return isPnpmManagedPackageDir(packageRoot, true);
375
+ return isPnpmManagedPackageDir(packageRoot);
376
376
  if (install.manager === "bun")
377
377
  return isBunManagedPackageDir(packageRoot);
378
378
  if (!install.prefix)
@@ -70,7 +70,7 @@ export declare function npmPrefixFromPackageDir(pkgDir: string): string | null;
70
70
  * i.e. the gate would re-fire and re-install globally on every subsequent run.
71
71
  * Those layouts fall through to the ordinary npm-prefix/manual handling.
72
72
  */
73
- export declare function isPnpmManagedPackageDir(pkgDir: string, acceptVersionPrefixedGlobalDir?: boolean): boolean;
73
+ export declare function isPnpmManagedPackageDir(pkgDir: string): boolean;
74
74
  /** Whether the running package lives inside Bun's global install tree. */
75
75
  export declare function isBunManagedPackageDir(pkgDir: string): boolean;
76
76
  /**
@@ -128,7 +128,7 @@ function findPackageRootForExecutable(bin) {
128
128
  const packageRoot = findCliPackageRoot(candidate);
129
129
  if (packageRoot &&
130
130
  samePath(packageRoot, candidate) &&
131
- isPnpmManagedPackageDir(packageRoot, true)) {
131
+ isPnpmManagedPackageDir(packageRoot)) {
132
132
  return packageRoot;
133
133
  }
134
134
  }
@@ -151,7 +151,7 @@ function samePath(left, right) {
151
151
  : a === b;
152
152
  }
153
153
  function globalInstallManagerForPath(bin, packageRoot) {
154
- if (isPnpmManagedPackageDir(packageRoot, true)) {
154
+ if (isPnpmManagedPackageDir(packageRoot)) {
155
155
  return "pnpm";
156
156
  }
157
157
  if (isBunManagedPackageDir(packageRoot))
@@ -215,7 +215,7 @@ function pnpmHomeGlobalRoot() {
215
215
  * i.e. the gate would re-fire and re-install globally on every subsequent run.
216
216
  * Those layouts fall through to the ordinary npm-prefix/manual handling.
217
217
  */
218
- export function isPnpmManagedPackageDir(pkgDir, acceptVersionPrefixedGlobalDir = true) {
218
+ export function isPnpmManagedPackageDir(pkgDir) {
219
219
  const normalized = pkgDir.replace(/\\/g, "/").replace(/\/+$/, "");
220
220
  const segments = normalized.split("/").filter(Boolean);
221
221
  // pnpm through v10 uses `global/<digits>`; pnpm v11+ uses
@@ -228,13 +228,11 @@ export function isPnpmManagedPackageDir(pkgDir, acceptVersionPrefixedGlobalDir =
228
228
  const globalDirVersion = segments[i + 1];
229
229
  if (/^\d+$/.test(globalDirVersion))
230
230
  return true;
231
- if (acceptVersionPrefixedGlobalDir && /^v\d+$/.test(globalDirVersion)) {
231
+ if (/^v\d+$/.test(globalDirVersion))
232
232
  return true;
233
- }
234
233
  }
235
- // PNPM_HOME/global is pnpm's configured global root by construction, so
236
- // keep this fallback in both flag states. The flag above gates only the new
237
- // documented global/v<major> segment match outside that configured root.
234
+ // PNPM_HOME/global is pnpm's configured global root by construction, even
235
+ // when the version segment is not one of the documented numeric forms.
238
236
  const globalRoot = pnpmHomeGlobalRoot();
239
237
  if (globalRoot) {
240
238
  if (normalized === globalRoot || normalized.startsWith(`${globalRoot}/`)) {
@@ -361,7 +359,7 @@ export function resolveRunningInstall() {
361
359
  const packageRoot = findRunningPackageRoot();
362
360
  if (!packageRoot)
363
361
  return { manager: "npm", prefix: null, packageRoot: null };
364
- if (isPnpmManagedPackageDir(packageRoot, true)) {
362
+ if (isPnpmManagedPackageDir(packageRoot)) {
365
363
  return { manager: "pnpm", prefix: null, packageRoot };
366
364
  }
367
365
  if (isBunManagedPackageDir(packageRoot)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.342.5",
3
+ "version": "5.342.7",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {