@indigoai-us/hq-cli 5.345.14 → 5.345.15

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,6 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.15] — 2026-10-06
6
+ - Route live hook Sentry reports through the fast CLI path.
7
+
8
+ - When `hq-anywhere-runtime` is enabled, `hq install --global --runtime claude --via plugin` installs the `hq` plugin from the `indigoai-us/hq-packages` Claude marketplace, and `hq install @indigoai-us/hq-anywhere` installs the Codex pack from that repository. The flag-off path refuses the HQ Anywhere alias without fetching a package source.
9
+
5
10
  ## [5.345.14] — 2026-10-06
6
11
  - Route frequently called native core commands through isolated fast handlers.
7
12
  - Allow lane hierarchies up to four levels and include actionable senior-chain details when depth limits refuse a create or re-parent.
@@ -1,2 +1,11 @@
1
- export declare function runSentryReportFast(args: readonly string[], stdin: string, _cwd: string, hqRoot: string | undefined): Promise<number | undefined>;
1
+ import type { NodeOptions } from "@sentry/node";
2
+ import { reportSentryEvent } from "../lib/core-utils/sentry-report.js";
3
+ export type FastSentryReportDependencies = {
4
+ initSentry: (overrides?: Partial<Pick<NodeOptions, "transport">>) => void;
5
+ finishSentrySession: () => void;
6
+ flushSentry: (timeoutMs: number) => PromiseLike<boolean>;
7
+ addCommandBreadcrumb: () => void;
8
+ report: typeof reportSentryEvent;
9
+ };
10
+ export declare function runSentryReportFast(args: readonly string[], stdin: string, _cwd: string, hqRoot: string | undefined, _commandName?: string, _hqRootArg?: string, _worktreeScopeFixEnabled?: boolean, dependencies?: FastSentryReportDependencies): Promise<number | undefined>;
2
11
  //# sourceMappingURL=core-sentry-report-fast.d.ts.map
@@ -1,5 +1,17 @@
1
+ import { settleWithin } from "../utils/settle-with-timeout.js";
2
+ import { beforeSend } from "../sentry-before-send.js";
3
+ import { addSentryRuntimeBreadcrumb, flushSentryRuntime, finishSentryRuntimeSession, initSentryRuntime, } from "../sentry-init.js";
4
+ import { addCommandBreadcrumb } from "../utils/command-breadcrumb.js";
1
5
  import { reportSentryEvent } from "../lib/core-utils/sentry-report.js";
2
- export function runSentryReportFast(args, stdin, _cwd, hqRoot) {
6
+ const SENTRY_FINALIZATION_TIMEOUT_MS = 500;
7
+ const defaultDependencies = {
8
+ initSentry: (overrides) => { initSentryRuntime(beforeSend, overrides); },
9
+ finishSentrySession: finishSentryRuntimeSession,
10
+ flushSentry: flushSentryRuntime,
11
+ addCommandBreadcrumb: () => addCommandBreadcrumb(process.argv.slice(2), addSentryRuntimeBreadcrumb),
12
+ report: reportSentryEvent,
13
+ };
14
+ export function runSentryReportFast(args, stdin, _cwd, hqRoot, _commandName, _hqRootArg, _worktreeScopeFixEnabled, dependencies = defaultDependencies) {
3
15
  let dryRun = false;
4
16
  let timeoutMs;
5
17
  for (let index = 0; index < args.length; index += 1) {
@@ -24,10 +36,20 @@ export function runSentryReportFast(args, stdin, _cwd, hqRoot) {
24
36
  // lightweight parser does not own. This branch emits no output.
25
37
  return Promise.resolve(undefined);
26
38
  }
27
- // Live sends need main.ts's initialized Sentry transport. Keep those calls on
28
- // the authoritative command graph; only side-effect-free validation is fast.
29
- if (!dryRun)
30
- return Promise.resolve(undefined);
31
- return reportSentryEvent(stdin, { dryRun, timeoutMs, hqRoot }).then((result) => result.code);
39
+ if (dryRun) {
40
+ return dependencies.report(stdin, { dryRun: true, timeoutMs, hqRoot }).then((result) => result.code);
41
+ }
42
+ // The report command's constrained message path uses the same DSN, release,
43
+ // environment, repo tag, user attribution, breadcrumb hook and release-health
44
+ // session as main.ts. The shared initializer keeps that setup in one place;
45
+ // the report payload itself is a message without an exception, so the shared
46
+ // fleet scrubber is the effective beforeSend behavior on this path.
47
+ dependencies.initSentry();
48
+ dependencies.addCommandBreadcrumb();
49
+ return dependencies.report(stdin, { dryRun: false, timeoutMs, hqRoot }).then(async (result) => {
50
+ dependencies.finishSentrySession();
51
+ await settleWithin([Promise.resolve(dependencies.flushSentry(SENTRY_FINALIZATION_TIMEOUT_MS))], SENTRY_FINALIZATION_TIMEOUT_MS);
52
+ return result.code;
53
+ });
32
54
  }
33
55
  //# sourceMappingURL=core-sentry-report-fast.js.map
@@ -21,6 +21,8 @@ export interface GlobalInstallOptions {
21
21
  via?: 'direct' | 'plugin';
22
22
  /** Local Claude Code marketplace directory (or HQ_CLAUDE_PLUGIN_MARKETPLACE). */
23
23
  marketplaceDir?: string;
24
+ /** GitHub repository for the published HQ plugin marketplace. */
25
+ marketplaceRepository?: string;
24
26
  /** Test seam for the Claude Code CLI; production uses `claude`. */
25
27
  runClaude?: (args: string[], home: string, env: NodeJS.ProcessEnv) => void;
26
28
  }
@@ -38,7 +40,7 @@ export interface ClaudePluginSource {
38
40
  pluginId: string;
39
41
  }
40
42
  /** Resolve the local marketplace source; published hosting remains an operator decision. */
41
- export declare function resolveClaudePluginSource(explicitDir?: string, env?: NodeJS.ProcessEnv): ClaudePluginSource;
43
+ export declare function resolveClaudePluginSource(explicitDir?: string, env?: NodeJS.ProcessEnv, repository?: string): ClaudePluginSource;
42
44
  export { AnywhereRuntimeDisabledError as AnywhereDisabledError };
43
45
  export declare const isAnywhereEnabled: (_home: string, env?: NodeJS.ProcessEnv) => boolean;
44
46
  /** Install HQ into Claude Code user scope. Returns the files touched (or that would be). */
@@ -67,10 +69,12 @@ export interface GlobalCommandOpts {
67
69
  hqRoot?: string;
68
70
  via?: 'direct' | 'plugin';
69
71
  marketplaceDir?: string;
72
+ marketplaceRepository?: string;
70
73
  /** Dependency injection for tests. */
71
74
  env?: NodeJS.ProcessEnv;
72
75
  log?: (line: string) => void;
73
76
  }
77
+ export declare function resolveInstallRoot(explicit: string | undefined): string;
74
78
  /** Entry used by `hq install --global` (wired from pkg-install.ts). */
75
79
  export declare function runInstallGlobal(opts: GlobalCommandOpts): void;
76
80
  export declare function registerUninstallCommand(program: Command): void;
@@ -46,9 +46,12 @@ export function hookTargetFor(hqRoot, home, daemonInstalled, env = process.env)
46
46
  return installed && daemonMatchesRoot && fs.existsSync(shimPath(hqRoot)) ? 'shim' : 'master';
47
47
  }
48
48
  /** Resolve the local marketplace source; published hosting remains an operator decision. */
49
- export function resolveClaudePluginSource(explicitDir, env = process.env) {
49
+ export function resolveClaudePluginSource(explicitDir, env = process.env, repository) {
50
50
  const configured = explicitDir?.trim() || env.HQ_CLAUDE_PLUGIN_MARKETPLACE?.trim();
51
51
  if (!configured) {
52
+ if (repository === 'indigoai-us/hq-packages') {
53
+ return { sourceDir: repository, marketplaceName: 'hq-packages', pluginName: 'hq', pluginId: 'hq@hq-packages' };
54
+ }
52
55
  throw Object.assign(new Error('Published HQ Claude plugin source is not configured; its repository/host awaits an operator decision. Pass --marketplace-dir <dir> or set HQ_CLAUDE_PLUGIN_MARKETPLACE.'), { expected: true });
53
56
  }
54
57
  const sourceDir = path.resolve(configured);
@@ -94,7 +97,9 @@ function removePluginSettings(doc, plugin, removePlugin, removeMarketplace, plug
94
97
  if (marketplaces && typeof marketplaces === 'object' && !Array.isArray(marketplaces)) {
95
98
  const entries = { ...marketplaces };
96
99
  const registration = entries[plugin.marketplaceName];
97
- if (removeMarketplace && registration?.source?.source === 'directory' && registration.source.path === plugin.sourceDir)
100
+ const sameDirectory = registration?.source?.source === 'directory' && registration.source.path === plugin.sourceDir;
101
+ const sameRepository = registration?.source?.source === 'github' && registration.source.repo === plugin.sourceDir;
102
+ if (removeMarketplace && (sameDirectory || sameRepository))
98
103
  delete entries[plugin.marketplaceName];
99
104
  if (Object.keys(entries).length === 0)
100
105
  delete next.extraKnownMarketplaces;
@@ -278,7 +283,7 @@ export function installGlobalClaude(opts) {
278
283
  throw new AnywhereRuntimeDisabledError();
279
284
  const t = claudeTargets(home);
280
285
  const prior = readState(t);
281
- const pluginSource = opts.via === 'plugin' ? resolveClaudePluginSource(opts.marketplaceDir, env) : undefined;
286
+ const pluginSource = opts.via === 'plugin' ? resolveClaudePluginSource(opts.marketplaceDir, env, opts.marketplaceRepository) : undefined;
282
287
  if (prior?.plugin && !pluginSource) {
283
288
  throw Object.assign(new Error('HQ is installed through a Claude plugin marketplace. Run `hq uninstall --global --runtime claude` before switching back to direct installation.'), { expected: true });
284
289
  }
@@ -312,7 +317,8 @@ export function installGlobalClaude(opts) {
312
317
  const enabledPlugins = settingsDoc.enabledPlugins;
313
318
  const knownMarketplaces = settingsDoc.extraKnownMarketplaces;
314
319
  const existingMarketplace = pluginSource ? knownMarketplaces?.[pluginSource.marketplaceName] : undefined;
315
- if (pluginSource && existingMarketplace && (existingMarketplace.source?.source !== 'directory' || existingMarketplace.source.path !== pluginSource.sourceDir)) {
320
+ if (pluginSource && existingMarketplace && !((existingMarketplace.source?.source === 'directory' && existingMarketplace.source.path === pluginSource.sourceDir) ||
321
+ (existingMarketplace.source?.source === 'github' && existingMarketplace.source.repo === pluginSource.sourceDir))) {
316
322
  throw Object.assign(new Error(`Claude marketplace "${pluginSource.marketplaceName}" is already registered from a different source. Remove or rename that marketplace before installing HQ.`), { expected: true });
317
323
  }
318
324
  const hadPluginSetting = !!pluginSource && !!enabledPlugins && Object.prototype.hasOwnProperty.call(enabledPlugins, pluginSource.pluginId);
@@ -927,7 +933,7 @@ function assertRuntime(runtime) {
927
933
  }
928
934
  return r;
929
935
  }
930
- function resolveInstallRoot(explicit) {
936
+ export function resolveInstallRoot(explicit) {
931
937
  const fromFlag = explicit?.trim() || process.env.HQ_ROOT?.trim();
932
938
  const root = fromFlag ? path.resolve(fromFlag) : resolveHqRoot();
933
939
  if (!root) {
@@ -948,7 +954,7 @@ export function runInstallGlobal(opts) {
948
954
  return;
949
955
  }
950
956
  const install = runtime === 'codex' ? installGlobalCodex : installGlobalClaude;
951
- install({ hqRoot: resolveInstallRoot(opts.hqRoot), dryRun: opts.dryRun, via: opts.via, marketplaceDir: opts.marketplaceDir });
957
+ install({ hqRoot: resolveInstallRoot(opts.hqRoot), dryRun: opts.dryRun, via: opts.via, marketplaceDir: opts.marketplaceDir, marketplaceRepository: opts.marketplaceRepository });
952
958
  }
953
959
  export function registerUninstallCommand(program) {
954
960
  program
@@ -1,6 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import { peekHqApiKey, requireApiKeyCapability, } from '../utils/resolve-vault-credential.js';
3
3
  import { MARKETPLACE_PREFIX, installPack, sourceMatchesPackPattern, } from './pack-install.js';
4
+ import { anywhereRuntimeEnabled } from './anywhere-runtime-flag.js';
4
5
  /**
5
6
  * True when `source` installs through the marketplace — an explicit
6
7
  * `marketplace:` source, or a bare slug (which the dispatch below rewrites into
@@ -67,7 +68,15 @@ export function registerPackageInstallCommand(parent) {
67
68
  process.exitCode = 1;
68
69
  return;
69
70
  }
70
- runInstallGlobal(opts);
71
+ let marketplaceDir = opts.marketplaceDir;
72
+ if (opts.via === 'plugin' &&
73
+ !marketplaceDir?.trim() &&
74
+ !process.env.HQ_CLAUDE_PLUGIN_MARKETPLACE?.trim() &&
75
+ anywhereRuntimeEnabled(process.env)) {
76
+ runInstallGlobal({ ...opts, marketplaceRepository: 'indigoai-us/hq-packages' });
77
+ return;
78
+ }
79
+ runInstallGlobal({ ...opts, marketplaceDir });
71
80
  return;
72
81
  }
73
82
  if (!source) {
@@ -85,6 +94,19 @@ export function registerPackageInstallCommand(parent) {
85
94
  // with packs:install (hq-pro serves POST /v1/keys/listings/…/installs).
86
95
  requireApiKeyCapability('packs:install', 'hq install');
87
96
  assertApiKeyCanAuthorizeInstall(source);
97
+ if (source === '@indigoai-us/hq-anywhere') {
98
+ if (!anywhereRuntimeEnabled()) {
99
+ console.error(chalk.red('Install failed:'), 'HQ Anywhere installation is disabled by the hq-anywhere-runtime flag.');
100
+ process.exit(1);
101
+ }
102
+ await installPack('github:indigoai-us/hq-packages#packages/hq-pack-hq-anywhere', {
103
+ company: opts.company,
104
+ allowHooks: opts.allowHooks,
105
+ allowMcp: opts.allowMcp,
106
+ followBranch: opts.branch,
107
+ });
108
+ return;
109
+ }
88
110
  if (sourceMatchesPackPattern(source)) {
89
111
  await installPack(source, {
90
112
  company: opts.company,
@@ -19,13 +19,10 @@ export const FAST_SCAFFOLD_SCRIPTS = [
19
19
  flagKey: "hq-core-native-fast-route",
20
20
  root: "live",
21
21
  readsStdinFor: (args) => {
22
- let dryRun = false;
23
22
  for (let index = 0; index < args.length; index += 1) {
24
23
  const arg = args[index];
25
- if (arg === "--dry-run") {
26
- dryRun = true;
24
+ if (arg === "--dry-run")
27
25
  continue;
28
- }
29
26
  if (arg === "--timeout-ms") {
30
27
  const value = args[index + 1];
31
28
  if (value === undefined || value.startsWith("-"))
@@ -37,7 +34,7 @@ export const FAST_SCAFFOLD_SCRIPTS = [
37
34
  continue;
38
35
  return false;
39
36
  }
40
- return dryRun;
37
+ return true;
41
38
  },
42
39
  stdinMaxBytes: 2_048,
43
40
  requiresNodeNetworkCompatibility: true,
package/dist/main.js CHANGED
@@ -11,7 +11,7 @@ import path from "node:path";
11
11
  import { fileURLToPath } from "node:url";
12
12
  import { Command } from "commander";
13
13
  import { finishSentrySession, initSentry, Sentry } from "./sentry.js";
14
- import { sanitizeArgv } from "./utils/feedback-diagnostics.js";
14
+ import { addCommandBreadcrumb } from "./utils/command-breadcrumb.js";
15
15
  import { environmentalFsErrorMessage, filesystemErrorCaptureContext, filesystemErrorMessage, } from "./utils/environmental-error.js";
16
16
  import { syncStateLockMessage } from "./utils/sync-state-lock-error.js";
17
17
  import { networkTransportErrorMessage } from "./utils/network-transport-error.js";
@@ -191,11 +191,7 @@ export async function runCli() {
191
191
  let versionUpdateHandled = false;
192
192
  let commandParsed = false;
193
193
  try {
194
- runGlobalNetworkLifecycle(process.argv, () => Sentry.addBreadcrumb({
195
- category: "command",
196
- message: sanitizeArgv(process.argv.slice(2)).join(" "),
197
- level: "info",
198
- }));
194
+ runGlobalNetworkLifecycle(process.argv, () => addCommandBreadcrumb(process.argv.slice(2), (breadcrumb) => Sentry.addBreadcrumb(breadcrumb)));
199
195
  // Version handling, both halves. Skipped for inspection flags
200
196
  // (`--version`, `--help`) so users debugging a broken install can still
201
197
  // introspect what they have, and silent on any failure — neither half may
@@ -0,0 +1,10 @@
1
+ import type { NodeOptions } from "@sentry/node";
2
+ type SentryModule = typeof import("@sentry/node");
3
+ type SentryBreadcrumb = Parameters<SentryModule["addBreadcrumb"]>[0];
4
+ /** Shared Sentry SDK setup for the command graph and isolated fast handlers. */
5
+ export declare function initSentryRuntime(beforeSend: NodeOptions["beforeSend"], overrides?: Partial<Pick<NodeOptions, "transport">>, pendingBreadcrumbs?: readonly SentryBreadcrumb[]): SentryModule | undefined;
6
+ export declare function finishSentryRuntimeSession(): void;
7
+ export declare function addSentryRuntimeBreadcrumb(breadcrumb: SentryBreadcrumb): void;
8
+ export declare function flushSentryRuntime(timeoutMs: number): PromiseLike<boolean>;
9
+ export {};
10
+ //# sourceMappingURL=sentry-init.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { createRequire } from "node:module";
2
+ import { BUNDLED_DSN } from "./sentry-dsn.generated.js";
3
+ import { CLI_VERSION } from "./cli-version.js";
4
+ import { beforeBreadcrumb } from "./utils/breadcrumb-buffer.js";
5
+ import { getCachedSentryUser } from "./utils/sentry-identity.js";
6
+ const requireSentry = createRequire(import.meta.url);
7
+ let sentryModule;
8
+ function loadSentryModule() {
9
+ sentryModule ??= requireSentry("@sentry/node");
10
+ return sentryModule;
11
+ }
12
+ /** Shared Sentry SDK setup for the command graph and isolated fast handlers. */
13
+ export function initSentryRuntime(beforeSend, overrides = {}, pendingBreadcrumbs = []) {
14
+ const dsn = BUNDLED_DSN || process.env.SENTRY_DSN;
15
+ if (!dsn)
16
+ return undefined;
17
+ const sdk = loadSentryModule();
18
+ sdk.init({
19
+ dsn,
20
+ // This async ESM hook adds large startup cost. HQ retains its explicit
21
+ // breadcrumbs without enabling automatic third-party instrumentation.
22
+ registerEsmLoaderHooks: false,
23
+ release: `hq-cli@${CLI_VERSION}`,
24
+ environment: process.env.HQ_CLI_ENV ?? "production",
25
+ initialScope: { tags: { repo: "hq-cli" } },
26
+ beforeSend,
27
+ beforeBreadcrumb,
28
+ integrations: (defaults) => defaults.filter((integration) => integration.name !== "ProcessSession"),
29
+ ...overrides,
30
+ });
31
+ const user = getCachedSentryUser();
32
+ if (user)
33
+ sdk.setUser(user);
34
+ for (const breadcrumb of pendingBreadcrumbs)
35
+ sdk.addBreadcrumb(breadcrumb);
36
+ sdk.startSession();
37
+ return sdk;
38
+ }
39
+ export function finishSentryRuntimeSession() {
40
+ sentryModule?.endSession();
41
+ }
42
+ export function addSentryRuntimeBreadcrumb(breadcrumb) {
43
+ sentryModule?.addBreadcrumb(breadcrumb);
44
+ }
45
+ export function flushSentryRuntime(timeoutMs) {
46
+ return sentryModule?.flush(timeoutMs) ?? Promise.resolve(true);
47
+ }
48
+ //# sourceMappingURL=sentry-init.js.map
package/dist/sentry.js CHANGED
@@ -1,14 +1,11 @@
1
1
  import { createRequire } from "node:module";
2
- import { BUNDLED_DSN } from "./sentry-dsn.generated.js";
3
2
  import { beforeSend } from "./sentry-before-send.js";
4
- import { beforeBreadcrumb } from "./utils/breadcrumb-buffer.js";
5
- import { CLI_VERSION } from "./cli-version.js";
6
- import { getCachedSentryUser } from "./utils/sentry-identity.js";
7
3
  import { isEpipe } from "./utils/epipe.js";
8
4
  import { companySkillWrapperSentryTagsFor } from "./lib/company-skill-wrapper.js";
9
5
  import { environmentalFsErrorMessage } from "./utils/environmental-error.js";
10
6
  import { incompleteInstallCaptureContext, incompleteInstallMessage, } from "./utils/incomplete-install-error.js";
11
7
  import { installTreeTornSentryTagsFor, sentryFingerprintFor, sentryMcpCollisionContextFor, } from "./utils/sentry-fingerprint.js";
8
+ import { finishSentryRuntimeSession, initSentryRuntime } from "./sentry-init.js";
12
9
  const inertSentryScope = {
13
10
  setTag: () => inertSentryScope,
14
11
  setUser: () => inertSentryScope,
@@ -17,11 +14,10 @@ const inertSentryScope = {
17
14
  setLevel: () => inertSentryScope,
18
15
  setFingerprint: () => inertSentryScope,
19
16
  };
20
- const requireSentry = createRequire(import.meta.url);
21
17
  let sentryModule;
22
18
  let pendingSentryBreadcrumbs = [];
23
19
  function loadSentryModule() {
24
- sentryModule ??= requireSentry("@sentry/node");
20
+ sentryModule ??= createRequire(import.meta.url)("@sentry/node");
25
21
  return sentryModule;
26
22
  }
27
23
  /** Load the SDK through the same resolver used by the lazy Sentry facade. */
@@ -227,46 +223,13 @@ function flagRegistryWorkerMissingFileTags(error, event) {
227
223
  * exercised without an ingest request. Production callers pass nothing.
228
224
  */
229
225
  export function initSentry(overrides = {}) {
230
- const dsn = BUNDLED_DSN || process.env.SENTRY_DSN;
231
- if (!dsn)
226
+ sentryModule = initSentryRuntime(epipeAwareBeforeSend, overrides, pendingSentryBreadcrumbs);
227
+ if (!sentryModule)
232
228
  return;
233
- const sdk = loadSentryModule();
234
- sdk.init({
235
- dsn,
236
- // import-in-the-middle's async ESM loader makes every later CLI import
237
- // substantially slower. Keep HQ's explicit breadcrumbs, but give up
238
- // automatic ESM third-party instrumentation (its spans/breadcrumbs).
239
- registerEsmLoaderHooks: false,
240
- // CLI_VERSION reads package.json at runtime; npm_package_version is only
241
- // set under `npm run`, so an installed `hq` binary would always report
242
- // 0.0.0 and never match the uploaded source maps.
243
- release: `hq-cli@${CLI_VERSION}`,
244
- environment: process.env.HQ_CLI_ENV ?? "production",
245
- initialScope: {
246
- tags: { repo: "hq-cli" },
247
- },
248
- beforeSend: epipeAwareBeforeSend,
249
- beforeBreadcrumb,
250
- // HQ owns this short-lived CLI session explicitly below and closes it from
251
- // main.ts. Leaving the SDK default enabled starts a competing session.
252
- integrations: (defaults) => defaults.filter((integration) => integration.name !== "ProcessSession"),
253
- ...overrides,
254
- });
255
- // Attribute events to the logged-in HQ identity (best-effort; null when not
256
- // logged in). A CLI process is one user, so global setUser is correct here.
257
- const user = getCachedSentryUser();
258
- if (user)
259
- sdk.setUser(user);
260
- for (const breadcrumb of pendingSentryBreadcrumbs)
261
- sdk.addBreadcrumb(breadcrumb);
262
229
  pendingSentryBreadcrumbs = [];
263
- // Release health: one session per CLI run. Ended + flushed in index.ts's
264
- // finally (covers every exit path). A captured error auto-marks the session
265
- // "errored", so crash-free numbers per release stay accurate.
266
- sdk.startSession();
267
230
  }
268
231
  /** Finish HQ's one per-invocation release-health session. */
269
232
  export function finishSentrySession() {
270
- sentryModule?.endSession();
233
+ finishSentryRuntimeSession();
271
234
  }
272
235
  //# sourceMappingURL=sentry.js.map
@@ -0,0 +1,7 @@
1
+ export type CommandBreadcrumb = {
2
+ category: "command";
3
+ message: string;
4
+ level: "info";
5
+ };
6
+ export declare function addCommandBreadcrumb(argv: readonly string[], addBreadcrumb: (breadcrumb: CommandBreadcrumb) => void): void;
7
+ //# sourceMappingURL=command-breadcrumb.d.ts.map
@@ -0,0 +1,9 @@
1
+ import { sanitizeArgv } from "./sanitize-argv.js";
2
+ export function addCommandBreadcrumb(argv, addBreadcrumb) {
3
+ addBreadcrumb({
4
+ category: "command",
5
+ message: sanitizeArgv(argv).join(" "),
6
+ level: "info",
7
+ });
8
+ }
9
+ //# sourceMappingURL=command-breadcrumb.js.map
@@ -1,5 +1,6 @@
1
1
  import { type VersionInfo } from "./feedback-versions.js";
2
2
  import type { FeedbackLogsBlob } from "./feedback-logs.js";
3
+ export { sanitizeArgv } from "./sanitize-argv.js";
3
4
  interface GitContext {
4
5
  branch: string | null;
5
6
  head: string | null;
@@ -37,7 +38,5 @@ export interface DiagnosticsBlob {
37
38
  */
38
39
  logs?: FeedbackLogsBlob;
39
40
  }
40
- export declare function sanitizeArgv(argv: string[]): string[];
41
41
  export declare function collectDiagnostics(): DiagnosticsBlob;
42
- export {};
43
42
  //# sourceMappingURL=feedback-diagnostics.d.ts.map
@@ -3,35 +3,8 @@ import { execFileSync } from "child_process";
3
3
  import { getRecentBreadcrumbs } from "./breadcrumb-buffer.js";
4
4
  import { CLI_VERSION } from "../cli-version.js";
5
5
  import { collectVersions } from "./feedback-versions.js";
6
- const SECRET_FLAGS = new Set([
7
- "--token",
8
- "--secret",
9
- "--password",
10
- "--key",
11
- "--api-key",
12
- "--access-token",
13
- "--auth-token",
14
- ]);
15
- export function sanitizeArgv(argv) {
16
- const result = [];
17
- for (let i = 0; i < argv.length; i++) {
18
- const arg = argv[i];
19
- if (arg.startsWith("--") && arg.includes("=")) {
20
- const eqIdx = arg.indexOf("=");
21
- const flag = arg.slice(0, eqIdx);
22
- if (SECRET_FLAGS.has(flag)) {
23
- result.push(`${flag}=***`);
24
- continue;
25
- }
26
- }
27
- result.push(arg);
28
- if (SECRET_FLAGS.has(arg) && i + 1 < argv.length) {
29
- result.push("***");
30
- i++;
31
- }
32
- }
33
- return result;
34
- }
6
+ import { sanitizeArgv } from "./sanitize-argv.js";
7
+ export { sanitizeArgv } from "./sanitize-argv.js";
35
8
  function sanitizeRemoteUrl(url) {
36
9
  return url.replace(/https?:\/\/[^@]+@/, "https://***@");
37
10
  }
@@ -0,0 +1,2 @@
1
+ export declare function sanitizeArgv(argv: readonly string[]): string[];
2
+ //# sourceMappingURL=sanitize-argv.d.ts.map
@@ -0,0 +1,30 @@
1
+ const SECRET_FLAGS = new Set([
2
+ "--token",
3
+ "--secret",
4
+ "--password",
5
+ "--key",
6
+ "--api-key",
7
+ "--access-token",
8
+ "--auth-token",
9
+ ]);
10
+ export function sanitizeArgv(argv) {
11
+ const result = [];
12
+ for (let i = 0; i < argv.length; i += 1) {
13
+ const arg = argv[i];
14
+ if (arg.startsWith("--") && arg.includes("=")) {
15
+ const eqIdx = arg.indexOf("=");
16
+ const flag = arg.slice(0, eqIdx);
17
+ if (SECRET_FLAGS.has(flag)) {
18
+ result.push(`${flag}=***`);
19
+ continue;
20
+ }
21
+ }
22
+ result.push(arg);
23
+ if (SECRET_FLAGS.has(arg) && i + 1 < argv.length) {
24
+ result.push("***");
25
+ i += 1;
26
+ }
27
+ }
28
+ return result;
29
+ }
30
+ //# sourceMappingURL=sanitize-argv.js.map
@@ -1,2 +1,2 @@
1
- export declare function assertFastPathStaysCommandGraphFree(loadedModules: readonly string[], distDirectory: string, allowedCommandModules?: readonly string[]): void;
1
+ export declare function assertFastPathStaysCommandGraphFree(loadedModules: readonly string[], distDirectory: string, allowedCommandModules?: readonly string[], allowedSentryPackageRoots?: readonly string[]): void;
2
2
  //# sourceMappingURL=scaffold-fast-import-guard.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import * as path from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
- function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules = []) {
3
+ function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules = [], allowedSentryPackageRoots = []) {
4
4
  const dist = path.resolve(distDirectory);
5
5
  const main = path.join(dist, "main.js");
6
6
  const commands = `${path.join(dist, "commands")}${path.sep}`;
@@ -9,6 +9,7 @@ function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandMo
9
9
  const fullNetworkCompat = path.join(dist, "node-network-compat.js");
10
10
  const sentryEntrypoint = path.join(dist, "sentry.js");
11
11
  const allowedCommands = new Set(allowedCommandModules.map((module) => path.resolve(module)));
12
+ const allowedSentryRoots = allowedSentryPackageRoots.map((module) => path.resolve(module));
12
13
  return [...new Set(loadedModules.map((loaded) => {
13
14
  if (loaded.startsWith("file:")) {
14
15
  try {
@@ -26,13 +27,15 @@ function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandMo
26
27
  path.resolve(loaded) === coreCommand ||
27
28
  path.resolve(loaded) === fullNetworkCompat ||
28
29
  path.resolve(loaded) === sentryEntrypoint ||
29
- path.resolve(loaded).includes(`${path.sep}node_modules${path.sep}@sentry${path.sep}`))
30
+ (path.resolve(loaded).includes(`${path.sep}node_modules${path.sep}@sentry${path.sep}`) &&
31
+ !allowedSentryRoots.some((root) => path.resolve(loaded) === root ||
32
+ path.resolve(loaded).startsWith(root + path.sep))))
30
33
  .filter((loaded) => !allowedCommands.has(path.resolve(loaded)) ||
31
34
  path.resolve(loaded) === main || path.resolve(loaded) === coreCommand)
32
35
  .sort();
33
36
  }
34
- export function assertFastPathStaysCommandGraphFree(loadedModules, distDirectory, allowedCommandModules = []) {
35
- const forbidden = forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules);
37
+ export function assertFastPathStaysCommandGraphFree(loadedModules, distDirectory, allowedCommandModules = [], allowedSentryPackageRoots = []) {
38
+ const forbidden = forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules, allowedSentryPackageRoots);
36
39
  if (forbidden.length > 0) {
37
40
  throw new Error(`fast core loaded forbidden command modules:\n${forbidden.join("\n")}`);
38
41
  }
@@ -80,6 +80,14 @@ resident daemon does not support the health operation, doctor falls back to
80
80
  `ping` for reachability, version and uptime, then warns that a restart is needed
81
81
  for full diagnostics.
82
82
 
83
+ The hq-anywhere-runtime flag also gates the published Claude plugin marketplace
84
+ and Codex pack source. When enabled, hq-cli adds the `indigoai-us/hq-packages`
85
+ Claude marketplace and installs the `hq` plugin, or resolves the exact
86
+ `@indigoai-us/hq-anywhere` alias to
87
+ `github:indigoai-us/hq-packages#packages/hq-pack-hq-anywhere`. The flag-off
88
+ path does not resolve either published source. Use `--marketplace-dir` to
89
+ override the Claude marketplace with a local build.
90
+
83
91
  ## Link a repository
84
92
 
85
93
  Run `hq link <company>` from a repository to associate it with a company. The
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.14",
3
+ "version": "5.345.15",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {