@indigoai-us/hq-cli 5.345.159 → 5.345.161
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 +48 -0
- package/assets/scaffold/core/scripts/session-journal.sh +116 -24
- package/dist/command-catalog.generated.d.ts +222 -9
- package/dist/command-catalog.generated.js +273 -9
- package/dist/commands/access.d.ts +6 -0
- package/dist/commands/access.js +18 -1
- package/dist/commands/acl-lock.d.ts +41 -0
- package/dist/commands/acl-lock.js +28 -0
- package/dist/commands/agent-enroll.d.ts +2 -0
- package/dist/commands/agent-enroll.js +9 -4
- package/dist/commands/agents.js +5 -11
- package/dist/commands/auth.js +6 -5
- package/dist/commands/billing-sponsor.d.ts +4 -4
- package/dist/commands/billing-sponsor.js +11 -11
- package/dist/commands/billing-usage.d.ts +22 -8
- package/dist/commands/billing-usage.js +57 -22
- package/dist/commands/billing.d.ts +7 -7
- package/dist/commands/billing.js +13 -13
- package/dist/commands/cloud-provision.js +2 -2
- package/dist/commands/cloud.js +7 -7
- package/dist/commands/core-session-helper-runtime.d.ts +1 -0
- package/dist/commands/core-session-journal-native.d.ts +1 -0
- package/dist/commands/core-session-journal-native.js +158 -34
- package/dist/commands/db-remote-deps.js +12 -1
- package/dist/commands/feedback.d.ts +12 -0
- package/dist/commands/feedback.js +52 -0
- package/dist/commands/files-browse.d.ts +8 -0
- package/dist/commands/files-browse.js +83 -1
- package/dist/commands/files.js +19 -7
- package/dist/commands/install-global.d.ts +2 -0
- package/dist/commands/install-global.js +24 -1
- package/dist/commands/integrations-connect.js +1 -0
- package/dist/commands/integrations-manage.d.ts +2 -0
- package/dist/commands/integrations-manage.js +6 -4
- package/dist/commands/lanes-link.js +5 -3
- package/dist/commands/lanes-session-start.d.ts +12 -0
- package/dist/commands/lanes-session-start.js +93 -12
- package/dist/commands/meetings.d.ts +15 -4
- package/dist/commands/meetings.js +58 -19
- package/dist/commands/skill.js +6 -6
- package/dist/commands/whoami.js +1 -1
- package/dist/hook-engine-path.js +13 -2
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +3236 -1988
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/index.js +21 -0
- package/dist/lanes-session-start-argv.d.ts +7 -0
- package/dist/lanes-session-start-argv.js +9 -0
- package/dist/lib/billing/plan-limit-refusals.d.ts +13 -3
- package/dist/lib/billing/plan-limit-refusals.js +26 -3
- package/dist/lib/billing/plan-limit-upgrade-url.d.ts +8 -0
- package/dist/lib/billing/plan-limit-upgrade-url.js +18 -0
- package/dist/lib/billing/plan-lock.d.ts +22 -6
- package/dist/lib/billing/plan-lock.js +58 -30
- package/dist/lib/billing/usage-cost.d.ts +24 -2
- package/dist/lib/billing/usage-cost.js +103 -3
- package/dist/lib/billing/usage-only.d.ts +37 -1
- package/dist/lib/billing/usage-only.js +89 -4
- package/dist/lib/billing/usage-pause.d.ts +27 -0
- package/dist/lib/billing/usage-pause.js +50 -0
- package/dist/lib/bot/log.d.ts +3 -2
- package/dist/lib/bot/progress.d.ts +23 -1
- package/dist/lib/bot/progress.js +43 -5
- package/dist/lib/bot/run.js +2 -0
- package/dist/lib/bot/runtime/claude.d.ts +12 -0
- package/dist/lib/bot/runtime/claude.js +20 -0
- package/dist/lib/bot/runtime/index.d.ts +10 -1
- package/dist/lib/bot/runtime/index.js +14 -2
- package/dist/lib/bot/runtime/types.d.ts +8 -0
- package/dist/lib/db/control-plane.d.ts +5 -0
- package/dist/lib/db/control-plane.js +10 -0
- package/dist/lib/doctor/checks/ps1-shims.d.ts +21 -0
- package/dist/lib/doctor/checks/ps1-shims.js +82 -0
- package/dist/lib/doctor/fix/apply.d.ts +13 -1
- package/dist/lib/doctor/fix/apply.js +38 -3
- package/dist/lib/doctor/fix/remediation.d.ts +9 -1
- package/dist/lib/doctor/fix/remediation.js +22 -0
- package/dist/lib/doctor/registry.js +2 -0
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/hook-engine/resolve.d.ts +14 -0
- package/dist/lib/hook-engine/resolve.js +55 -1
- package/dist/lib/lanes/links.d.ts +2 -1
- package/dist/lib/lanes/links.js +235 -17
- package/dist/lib/lanes/registry-flag.d.ts +24 -0
- package/dist/lib/lanes/registry-flag.js +72 -6
- package/dist/lib/lanes/store.d.ts +4 -2
- package/dist/lib/lanes/store.js +8 -8
- package/dist/lib/plan-limit-nag.d.ts +0 -16
- package/dist/lib/plan-limit-nag.js +17 -18
- package/dist/lib/windows/ps1-shims.d.ts +60 -0
- package/dist/lib/windows/ps1-shims.js +152 -0
- package/dist/main.d.ts +0 -2
- package/dist/main.js +19 -57
- package/dist/utils/plan-gate-error.d.ts +30 -14
- package/dist/utils/plan-gate-error.js +60 -46
- package/dist/utils/self-update.d.ts +2 -0
- package/dist/utils/self-update.js +8 -0
- package/dist/utils/staged-npm-install.js +13 -2
- package/dist/utils/team-upgrade.d.ts +19 -6
- package/dist/utils/team-upgrade.js +37 -39
- package/dist/utils/token-keychain.d.ts +11 -8
- package/dist/utils/token-keychain.js +36 -15
- package/package.json +8 -8
- package/scripts/install-hook-engine.mjs +12 -0
|
@@ -146,6 +146,14 @@ export interface BotRuntime {
|
|
|
146
146
|
resolveBinary?(env: NodeJS.ProcessEnv, cwd?: string, platform?: NodeJS.Platform): string;
|
|
147
147
|
/** Optional full prompt stream sent through stdin instead of argv. */
|
|
148
148
|
stdinInput?(input: RuntimeTurnInput): string | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* Optional: runtime-specific defaults layered onto the turn's environment
|
|
151
|
+
* (per-tool safety defaults the CLI honours, for example). Applied in
|
|
152
|
+
* `runRuntimeTurn` on top of the env that `turnEnv` in run.ts built from
|
|
153
|
+
* the bot's identity rules; it only adds runtime keys and never changes
|
|
154
|
+
* who the turn acts as. Pure; must not mutate `env`.
|
|
155
|
+
*/
|
|
156
|
+
runtimeEnv?(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
149
157
|
/** Optional bidirectional process protocol for this runtime. */
|
|
150
158
|
spawnProtocol?(input: RuntimeTurnInput): SpawnProtocol | undefined;
|
|
151
159
|
/** Build the argv for one turn. Pure. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type UsagePause } from "../billing/usage-pause.js";
|
|
1
2
|
/**
|
|
2
3
|
* Control-plane client for remote vault DB (US-009).
|
|
3
4
|
* Injectable fetch for tests — never logs response bodies that might hold secrets.
|
|
@@ -167,5 +168,9 @@ export type ControlPlaneHttpError = Error & {
|
|
|
167
168
|
status?: number;
|
|
168
169
|
code?: string;
|
|
169
170
|
reason?: string;
|
|
171
|
+
/** The 402 usage pause (hq-pro `usagePause`), when the server sent one. */
|
|
172
|
+
usagePause?: UsagePause;
|
|
173
|
+
/** The body's customer sentence (`message`), when it sent one. */
|
|
174
|
+
serverMessage?: string;
|
|
170
175
|
};
|
|
171
176
|
//# sourceMappingURL=control-plane.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { planGateErrorFromPayload } from "../../utils/plan-gate-error.js";
|
|
2
|
+
import { usagePauseFromBody } from "../billing/usage-pause.js";
|
|
2
3
|
// Provision may allocate remote resources, so allow two minutes and never retry.
|
|
3
4
|
const DB_PROVISION_TIMEOUT_MS = 120_000;
|
|
4
5
|
// Status is a read-only probe; 15 seconds is longer than its normal response time.
|
|
@@ -195,6 +196,8 @@ function httpError(label, status, text) {
|
|
|
195
196
|
let msg = `${label} failed (${status})`;
|
|
196
197
|
let code;
|
|
197
198
|
let reason;
|
|
199
|
+
let usagePause = null;
|
|
200
|
+
let serverMessage;
|
|
198
201
|
try {
|
|
199
202
|
const j = JSON.parse(text);
|
|
200
203
|
if (typeof j.error === "string")
|
|
@@ -203,6 +206,9 @@ function httpError(label, status, text) {
|
|
|
203
206
|
code = j.code;
|
|
204
207
|
if (typeof j.reason === "string")
|
|
205
208
|
reason = j.reason;
|
|
209
|
+
if (typeof j.message === "string" && j.message.trim())
|
|
210
|
+
serverMessage = j.message.trim();
|
|
211
|
+
usagePause = usagePauseFromBody(j);
|
|
206
212
|
}
|
|
207
213
|
catch {
|
|
208
214
|
/* keep */
|
|
@@ -213,6 +219,10 @@ function httpError(label, status, text) {
|
|
|
213
219
|
err.code = code;
|
|
214
220
|
if (reason)
|
|
215
221
|
err.reason = reason;
|
|
222
|
+
if (usagePause)
|
|
223
|
+
err.usagePause = usagePause;
|
|
224
|
+
if (serverMessage)
|
|
225
|
+
err.serverMessage = serverMessage;
|
|
216
226
|
return err;
|
|
217
227
|
}
|
|
218
228
|
function personalHttpError(label, status, text) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CheckContext, CheckFamily, CheckResult } from "../types.js";
|
|
2
|
+
export declare const PS1_SHIM_FAMILY_ID = "cli-ps1-shim";
|
|
3
|
+
export declare const PS1_SHIM_CHECK_ID = "cli-ps1-shim.blocked-by-policy";
|
|
4
|
+
export interface Ps1ShimCheckDeps {
|
|
5
|
+
platform: NodeJS.Platform;
|
|
6
|
+
/** The npm prefix holding `hq.cmd`, or null when hq is not an npm global install. */
|
|
7
|
+
binDir: () => string | null;
|
|
8
|
+
/** The npm-generated hq-cli `.ps1` launchers present in the prefix. */
|
|
9
|
+
findShims: (dir: string) => string[];
|
|
10
|
+
/** The effective PowerShell execution policy, or null when it could not be read. */
|
|
11
|
+
readPolicy: () => string | null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Windows: npm writes `hq.ps1` next to `hq.cmd`, and PowerShell prefers the
|
|
15
|
+
* `.ps1`. Under a policy that blocks unsigned scripts (the Windows client
|
|
16
|
+
* default) `hq` then fails with UnauthorizedAccess. This check only reads:
|
|
17
|
+
* it never changes the execution policy.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ps1ShimsFamily: CheckFamily;
|
|
20
|
+
export declare function checkPs1Shims(_context: CheckContext, deps?: Ps1ShimCheckDeps): CheckResult[];
|
|
21
|
+
//# sourceMappingURL=ps1-shims.d.ts.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { npmShimDir } from "../../windows/user-path.js";
|
|
4
|
+
import { findHqCliPs1Shims, policyBlocksUnsignedScripts, readExecutionPolicy } from "../../windows/ps1-shims.js";
|
|
5
|
+
export const PS1_SHIM_FAMILY_ID = "cli-ps1-shim";
|
|
6
|
+
export const PS1_SHIM_CHECK_ID = "cli-ps1-shim.blocked-by-policy";
|
|
7
|
+
const defaultDeps = {
|
|
8
|
+
platform: process.platform,
|
|
9
|
+
binDir: () => npmShimDir(process.argv[1], fs.existsSync),
|
|
10
|
+
findShims: (dir) => findHqCliPs1Shims(dir),
|
|
11
|
+
readPolicy: () => readExecutionPolicy(),
|
|
12
|
+
};
|
|
13
|
+
const REMEDIATION = "Run `hq.cmd doctor --fix` (or `hq.cmd install --global`) to remove the hq .ps1 launchers. " +
|
|
14
|
+
"Until then, type `hq.cmd` instead of `hq` in PowerShell. Your execution policy is not changed.";
|
|
15
|
+
/**
|
|
16
|
+
* Windows: npm writes `hq.ps1` next to `hq.cmd`, and PowerShell prefers the
|
|
17
|
+
* `.ps1`. Under a policy that blocks unsigned scripts (the Windows client
|
|
18
|
+
* default) `hq` then fails with UnauthorizedAccess. This check only reads:
|
|
19
|
+
* it never changes the execution policy.
|
|
20
|
+
*/
|
|
21
|
+
export const ps1ShimsFamily = {
|
|
22
|
+
id: PS1_SHIM_FAMILY_ID,
|
|
23
|
+
title: "hq PowerShell launchers",
|
|
24
|
+
run: (context) => Promise.resolve(checkPs1Shims(context)),
|
|
25
|
+
};
|
|
26
|
+
export function checkPs1Shims(_context, deps = defaultDeps) {
|
|
27
|
+
const dir = deps.platform === "win32" ? deps.binDir() : null;
|
|
28
|
+
if (!dir) {
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
status: "NA",
|
|
32
|
+
checkId: PS1_SHIM_CHECK_ID,
|
|
33
|
+
message: "Not a Windows npm global install, so there are no hq PowerShell launchers to check.",
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
const shims = deps.findShims(dir);
|
|
38
|
+
if (shims.length === 0) {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
status: "PASS",
|
|
42
|
+
checkId: PS1_SHIM_CHECK_ID,
|
|
43
|
+
target: dir,
|
|
44
|
+
message: `No hq .ps1 launchers in ${dir}; PowerShell runs hq.cmd.`,
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
const names = shims.map((file) => path.win32.basename(file)).join(", ");
|
|
49
|
+
const policy = deps.readPolicy();
|
|
50
|
+
if (policy === null) {
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
status: "UNKNOWN",
|
|
54
|
+
checkId: PS1_SHIM_CHECK_ID,
|
|
55
|
+
target: shims[0],
|
|
56
|
+
message: `Found ${names} in ${dir} but could not read the PowerShell execution policy. If \`hq\` fails with UnauthorizedAccess, use \`hq.cmd\`.`,
|
|
57
|
+
remediation: REMEDIATION,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
if (!policyBlocksUnsignedScripts(policy)) {
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
status: "PASS",
|
|
65
|
+
checkId: PS1_SHIM_CHECK_ID,
|
|
66
|
+
target: shims[0],
|
|
67
|
+
message: `${names} can run under the ${policy} execution policy.`,
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
return [
|
|
72
|
+
{
|
|
73
|
+
status: "WARN",
|
|
74
|
+
checkId: PS1_SHIM_CHECK_ID,
|
|
75
|
+
target: shims[0],
|
|
76
|
+
message: `PowerShell runs ${names} for \`hq\`, and the ${policy} execution policy blocks it, so \`hq\` fails with UnauthorizedAccess.`,
|
|
77
|
+
details: { policy, shims },
|
|
78
|
+
remediation: REMEDIATION,
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=ps1-shims.js.map
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
* execute bit, add a hook id to the gate profiles it is missing from,
|
|
18
18
|
* re-register a script present on disk, or pull a company or personal
|
|
19
19
|
* vault whose local tree exists but whose journal is stale — and NEVER
|
|
20
|
-
* rewrites a hook body or deletes a file
|
|
20
|
+
* rewrites a hook body or deletes a file, with one scoped exception: on
|
|
21
|
+
* Windows, `remove-ps1-shims` deletes npm-generated hq-cli `.ps1`
|
|
22
|
+
* launchers (and only files whose content is that shim) from the npm
|
|
23
|
+
* prefix, outside the HQ tree. Classification is owned by
|
|
21
24
|
* {@link deriveRemediation};
|
|
22
25
|
* a content-drift finding is manual-only and simply never appears in the
|
|
23
26
|
* fixable set.
|
|
@@ -38,6 +41,7 @@ import { type SpawnSyncReturns } from "node:child_process";
|
|
|
38
41
|
import type { CheckResult, DoctorStatus } from "../types.js";
|
|
39
42
|
import { type GateProfile } from "../hook-gate-profiles.js";
|
|
40
43
|
import { type FixClass } from "./remediation.js";
|
|
44
|
+
import { type RemovePs1ShimsResult } from "../../windows/ps1-shims.js";
|
|
41
45
|
import { type RefreshSyncOutcome } from "./refresh-sync-outcome.js";
|
|
42
46
|
/** The tree subtrees whose uncommitted changes block a `--fix` run. */
|
|
43
47
|
export declare const HOOK_CONFIG_DIRS: readonly [".claude", ".codex", ".grok"];
|
|
@@ -109,6 +113,12 @@ export interface ApplyFixesOptions {
|
|
|
109
113
|
from: string | null;
|
|
110
114
|
to: string;
|
|
111
115
|
} | null;
|
|
116
|
+
/**
|
|
117
|
+
* Removal used by the `remove-ps1-shims` class: delete npm's hq-cli `.ps1`
|
|
118
|
+
* launchers from the given npm prefix (Windows only; other files and the
|
|
119
|
+
* execution policy are left alone). Tests inject a stub.
|
|
120
|
+
*/
|
|
121
|
+
removePs1Shims?: (dir: string) => RemovePs1ShimsResult;
|
|
112
122
|
}
|
|
113
123
|
/** One company or personal vault the `refresh-sync` class will pull. */
|
|
114
124
|
export interface RefreshSyncTarget {
|
|
@@ -311,6 +321,8 @@ export declare function defaultRewriteBotNodePath(name: string): {
|
|
|
311
321
|
from: string;
|
|
312
322
|
to: string;
|
|
313
323
|
} | null;
|
|
324
|
+
/** Default `remove-ps1-shims` repair: Windows only, a no-op elsewhere. */
|
|
325
|
+
export declare function defaultRemovePs1Shims(dir: string): RemovePs1ShimsResult;
|
|
314
326
|
/**
|
|
315
327
|
* An hq path a bot may be pointed at unattended: an installed package entry
|
|
316
328
|
* or its stable shim, never a worktree or one-off build that happens to be
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
* execute bit, add a hook id to the gate profiles it is missing from,
|
|
18
18
|
* re-register a script present on disk, or pull a company or personal
|
|
19
19
|
* vault whose local tree exists but whose journal is stale — and NEVER
|
|
20
|
-
* rewrites a hook body or deletes a file
|
|
20
|
+
* rewrites a hook body or deletes a file, with one scoped exception: on
|
|
21
|
+
* Windows, `remove-ps1-shims` deletes npm-generated hq-cli `.ps1`
|
|
22
|
+
* launchers (and only files whose content is that shim) from the npm
|
|
23
|
+
* prefix, outside the HQ tree. Classification is owned by
|
|
21
24
|
* {@link deriveRemediation};
|
|
22
25
|
* a content-drift finding is manual-only and simply never appears in the
|
|
23
26
|
* fixable set.
|
|
@@ -44,6 +47,7 @@ import { parseHookGateProfiles, requiredMissingProfiles, } from "../hook-gate-pr
|
|
|
44
47
|
import { createBackup } from "./backup.js";
|
|
45
48
|
import { removeStrayGateEntries } from "../stray-gate-entries.js";
|
|
46
49
|
import { deriveRemediation } from "./remediation.js";
|
|
50
|
+
import { HQ_CLI_BINS, removeHqCliPs1Shims } from "../../windows/ps1-shims.js";
|
|
47
51
|
import { botDir } from "../../bot/paths.js";
|
|
48
52
|
import { readBotConfig } from "../../bot/config.js";
|
|
49
53
|
import { buildBotDaemonPaths, installedDaemonHqBinary, repairBotDaemonIfStale, rewriteBotDaemonNodePath, } from "../../bot/daemon.js";
|
|
@@ -197,6 +201,7 @@ export async function applyFixes(options) {
|
|
|
197
201
|
};
|
|
198
202
|
const rewriteBotNodePath = options.rewriteBotNodePath ?? defaultRewriteBotNodePath;
|
|
199
203
|
const repairBotHqBinary = options.repairBotHqBinary ?? defaultRepairBotHqBinary;
|
|
204
|
+
const removePs1Shims = options.removePs1Shims ?? defaultRemovePs1Shims;
|
|
200
205
|
// 2. Collect findings, then plan each newly unlocked allowlisted repair.
|
|
201
206
|
// Identity is deliberately the check id + target pair: an unsuccessful
|
|
202
207
|
// repair must not be applied repeatedly in one invocation.
|
|
@@ -238,7 +243,7 @@ export async function applyFixes(options) {
|
|
|
238
243
|
const rem = deriveRemediation(result);
|
|
239
244
|
if (!rem?.autoFixable)
|
|
240
245
|
continue;
|
|
241
|
-
const plan = planFix(hqRoot, result, rem, refreshSync, rewriteBotNodePath, repairBotHqBinary, refreshManifest);
|
|
246
|
+
const plan = planFix(hqRoot, result, rem, refreshSync, rewriteBotNodePath, repairBotHqBinary, refreshManifest, removePs1Shims);
|
|
242
247
|
if (!plan)
|
|
243
248
|
continue;
|
|
244
249
|
const key = JSON.stringify([plan.checkId, plan.target]);
|
|
@@ -683,8 +688,10 @@ export function uncommittedHookConfigChanges(hqRoot) {
|
|
|
683
688
|
}
|
|
684
689
|
// --- Fix planning --------------------------------------------------------------
|
|
685
690
|
/** Build the concrete plan for one auto-fixable finding, or null if unplannable. */
|
|
686
|
-
function planFix(hqRoot, result, rem, refreshSync, rewriteBotNodePath = defaultRewriteBotNodePath, repairBotHqBinary = defaultRepairBotHqBinary, refreshManifest = defaultRefreshManifest) {
|
|
691
|
+
function planFix(hqRoot, result, rem, refreshSync, rewriteBotNodePath = defaultRewriteBotNodePath, repairBotHqBinary = defaultRepairBotHqBinary, refreshManifest = defaultRefreshManifest, removePs1Shims = defaultRemovePs1Shims) {
|
|
687
692
|
switch (rem.fixClass) {
|
|
693
|
+
case "remove-ps1-shims":
|
|
694
|
+
return planRemovePs1Shims(result, rem, removePs1Shims);
|
|
688
695
|
case "bot-node-path":
|
|
689
696
|
return planBotNodePath(result, rem, rewriteBotNodePath);
|
|
690
697
|
case "bot-hq-binary":
|
|
@@ -753,6 +760,34 @@ function planBotHqBinary(result, rem, repair) {
|
|
|
753
760
|
postStatus: (results) => results.find((entry) => entry.checkId === result.checkId)?.status ?? null,
|
|
754
761
|
};
|
|
755
762
|
}
|
|
763
|
+
/** Remove npm's hq-cli `.ps1` launchers so PowerShell falls back to `hq.cmd`. */
|
|
764
|
+
function planRemovePs1Shims(result, rem, remove) {
|
|
765
|
+
const dir = rem.fixTarget;
|
|
766
|
+
if (!dir)
|
|
767
|
+
return null;
|
|
768
|
+
let failure;
|
|
769
|
+
return {
|
|
770
|
+
checkId: result.checkId,
|
|
771
|
+
fixClass: "remove-ps1-shims",
|
|
772
|
+
target: dir,
|
|
773
|
+
relpath: "",
|
|
774
|
+
preview: Object.keys(HQ_CLI_BINS).map((bin) => ` - ${path.win32.join(dir, `${bin}.ps1`)}`).join("\n") +
|
|
775
|
+
"\n only npm's hq-cli launchers are removed; hq.cmd stays and the execution policy is not changed",
|
|
776
|
+
summary: "remove hq .ps1 launchers",
|
|
777
|
+
apply: () => {
|
|
778
|
+
const outcome = remove(dir);
|
|
779
|
+
failure = outcome.failed.length > 0 ? outcome.failed.map((entry) => entry.reason).join("; ") : undefined;
|
|
780
|
+
},
|
|
781
|
+
postStatus: (results) => results.find((entry) => entry.checkId === result.checkId)?.status ?? null,
|
|
782
|
+
failureReason: () => failure,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
/** Default `remove-ps1-shims` repair: Windows only, a no-op elsewhere. */
|
|
786
|
+
export function defaultRemovePs1Shims(dir) {
|
|
787
|
+
if (process.platform !== "win32")
|
|
788
|
+
return { removed: [], kept: [], failed: [] };
|
|
789
|
+
return removeHqCliPs1Shims(dir);
|
|
790
|
+
}
|
|
756
791
|
/** Real launchctl for the `bot-hq-binary` reload; a failure is a non-ok result. */
|
|
757
792
|
const doctorLaunchctl = (args) => {
|
|
758
793
|
const r = spawnSync("launchctl", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -35,6 +35,14 @@
|
|
|
35
35
|
* and hashes); it never writes the HQ tree. A company
|
|
36
36
|
* scope whose slug did not resolve locally stays
|
|
37
37
|
* manual-only.
|
|
38
|
+
* - `remove-ps1-shims` — Windows only: delete the npm-generated `hq.ps1` /
|
|
39
|
+
* `hq-auth-refresh.ps1` launchers from the npm prefix
|
|
40
|
+
* when the PowerShell execution policy blocks them.
|
|
41
|
+
* `hq.cmd` stays and PowerShell falls back to it. The
|
|
42
|
+
* removal only touches files whose content is npm's
|
|
43
|
+
* hq-cli shim (see `lib/windows/ps1-shims.ts`); the
|
|
44
|
+
* execution policy is never changed. This is the one
|
|
45
|
+
* scoped exception to the no-delete rule below.
|
|
38
46
|
*
|
|
39
47
|
* EVERYTHING else — content drift between platform copies, a missing script, a
|
|
40
48
|
* missing Codex counterpart, an unquoted `$CLAUDE_PROJECT_DIR`, a stale
|
|
@@ -46,7 +54,7 @@
|
|
|
46
54
|
*/
|
|
47
55
|
import type { CheckResult } from "../types.js";
|
|
48
56
|
/** The allowlisted safe repair classes `--fix` is permitted to apply. */
|
|
49
|
-
export type FixClass = "executable-bit" | "gate-profile" | "remove-stray-gate-entries" | "refresh-sync" | "refresh-manifest" | "bot-node-path" | "bot-hq-binary";
|
|
57
|
+
export type FixClass = "executable-bit" | "gate-profile" | "remove-stray-gate-entries" | "refresh-sync" | "refresh-manifest" | "bot-node-path" | "bot-hq-binary" | "remove-ps1-shims";
|
|
50
58
|
/**
|
|
51
59
|
* A finding's structured remediation. `autoFixable`, `action`, and `command`
|
|
52
60
|
* are the `--json` surface (AC1); `fixTarget`/`fixClass` are the internal handle
|
|
@@ -35,6 +35,14 @@
|
|
|
35
35
|
* and hashes); it never writes the HQ tree. A company
|
|
36
36
|
* scope whose slug did not resolve locally stays
|
|
37
37
|
* manual-only.
|
|
38
|
+
* - `remove-ps1-shims` — Windows only: delete the npm-generated `hq.ps1` /
|
|
39
|
+
* `hq-auth-refresh.ps1` launchers from the npm prefix
|
|
40
|
+
* when the PowerShell execution policy blocks them.
|
|
41
|
+
* `hq.cmd` stays and PowerShell falls back to it. The
|
|
42
|
+
* removal only touches files whose content is npm's
|
|
43
|
+
* hq-cli shim (see `lib/windows/ps1-shims.ts`); the
|
|
44
|
+
* execution policy is never changed. This is the one
|
|
45
|
+
* scoped exception to the no-delete rule below.
|
|
38
46
|
*
|
|
39
47
|
* EVERYTHING else — content drift between platform copies, a missing script, a
|
|
40
48
|
* missing Codex counterpart, an unquoted `$CLAUDE_PROJECT_DIR`, a stale
|
|
@@ -44,6 +52,7 @@
|
|
|
44
52
|
* content-drift finding is manual-only, because that boundary is load-bearing:
|
|
45
53
|
* deciding which of two diverged copies is correct needs human judgement.
|
|
46
54
|
*/
|
|
55
|
+
import * as path from "node:path";
|
|
47
56
|
import { isCompanyJournalSlug, isSafeCompanySlug, isPersonalJournalSlug, journalSlugFromCheckId, } from "../checks/sync-health.js";
|
|
48
57
|
/** Matches an exec-bit remediation command, quoted or not: `chmod +x <path>`. */
|
|
49
58
|
const CHMOD_EXEC = /^chmod\s+\+x\s+(.+)$/;
|
|
@@ -143,6 +152,19 @@ export function deriveRemediation(result) {
|
|
|
143
152
|
command: remediation ?? `hq bot daemon install ${name}`,
|
|
144
153
|
};
|
|
145
154
|
}
|
|
155
|
+
// remove-ps1-shims — PowerShell resolves `hq` to npm's hq.ps1 and the
|
|
156
|
+
// execution policy blocks it (feedback_29d91817). The target is the shim;
|
|
157
|
+
// the fix acts on its directory, the npm prefix.
|
|
158
|
+
if (checkId === "cli-ps1-shim.blocked-by-policy" && result.status === "WARN" && target) {
|
|
159
|
+
const dir = path.win32.dirname(target);
|
|
160
|
+
return {
|
|
161
|
+
autoFixable: true,
|
|
162
|
+
fixClass: "remove-ps1-shims",
|
|
163
|
+
fixTarget: dir,
|
|
164
|
+
action: `Remove the npm-generated hq .ps1 launchers from ${dir} so PowerShell runs hq.cmd (the execution policy is not changed).`,
|
|
165
|
+
command: "hq.cmd install --global",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
146
168
|
// Available hq-core upgrade. WARN is intentional in `hq doctor` and is
|
|
147
169
|
// not a broken sync; --fix has no mechanical repair for it.
|
|
148
170
|
if (checkId === "sync.update.core") {
|
|
@@ -29,6 +29,7 @@ import { scaffoldCliFloorFamily } from "./checks/scaffold-cli-floor.js";
|
|
|
29
29
|
import { strandedLanesFamily } from "./checks/stranded-lanes.js";
|
|
30
30
|
import { releaseLinksFamily } from "./checks/release-links.js";
|
|
31
31
|
import { cliPathFamily } from "./checks/cli-path.js";
|
|
32
|
+
import { ps1ShimsFamily } from "./checks/ps1-shims.js";
|
|
32
33
|
import { tempHomesFamily } from "./checks/temp-homes.js";
|
|
33
34
|
import { fixtureCoverageFamily } from "./fixtures/discover.js";
|
|
34
35
|
import { checkClaudeWiring } from "./checks/claude-wiring.js";
|
|
@@ -280,6 +281,7 @@ export function createDefaultRegistry() {
|
|
|
280
281
|
// Windows: the npm hq.cmd shim folder must be on PATH for a new terminal to
|
|
281
282
|
// find hq. Registry read only; prints the fix, never edits PATH.
|
|
282
283
|
registry.register(cliPathFamily);
|
|
284
|
+
registry.register(ps1ShimsFamily);
|
|
283
285
|
// Idle tmp.* throwaway homes holding a Rust toolchain download. Dry-run
|
|
284
286
|
// scan only; `hq doctor --sweep-temp-homes` is the delete path.
|
|
285
287
|
registry.register(tempHomesFamily);
|