@indigoai-us/hq-cli 5.117.1 → 5.117.3
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 +41 -0
- package/assets/bot-workers/setup/context/USER-GUIDE.md +44 -3
- package/assets/bot-workers/setup/context/quick-reference.md +1 -1
- package/dist/command-catalog.generated.d.ts +5 -2
- package/dist/command-catalog.generated.js +6 -2
- package/dist/commands/agent-kit.js +16 -1
- package/dist/commands/agent-probe.d.ts +1 -3
- package/dist/commands/agent-probe.js +11 -29
- package/dist/commands/billing.js +1 -1
- package/dist/commands/db-provision.js +4 -4
- package/dist/commands/meetings.js +2 -2
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +55 -1
- package/dist/lib/agent-kit/run/inbox.js +2 -2
- package/dist/lib/agent-kit/run/mesh-listener.js +11 -2
- package/dist/lib/agent-kit/skills.js +2 -2
- package/dist/lib/billing/plan-lock.d.ts +99 -0
- package/dist/lib/billing/plan-lock.js +230 -0
- package/dist/lib/doctor/__testing__/fake-hq-tree.d.ts +1 -1
- package/dist/lib/doctor/__testing__/fake-hq-tree.js +1 -1
- package/dist/lib/doctor/checks/claude-wiring.js +61 -1
- package/dist/lib/doctor/compat.js +1 -0
- package/dist/lib/doctor/fix/apply.js +21 -22
- package/dist/lib/doctor/fix/remediation.d.ts +7 -4
- package/dist/lib/doctor/fix/remediation.js +15 -7
- package/dist/lib/doctor/registry.js +43 -0
- package/dist/lib/doctor/stray-gate-entries.d.ts +35 -0
- package/dist/lib/doctor/stray-gate-entries.js +83 -0
- package/dist/lib/plan-limit-nag.js +1 -1
- package/dist/utils/plan-gate-error.js +9 -9
- package/dist/utils/team-upgrade.d.ts +1 -1
- package/dist/utils/team-upgrade.js +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stray gate registrations written by an old `hq doctor --fix`.
|
|
3
|
+
*
|
|
4
|
+
* hq-cli v5.99.0 through v5.118.x "re-registered" every hook script that no
|
|
5
|
+
* settings command named. Since HQ moved to a single master-hook.sh entry that
|
|
6
|
+
* dispatches from .claude/hooks/hook-registry.json, EVERY script looked
|
|
7
|
+
* unregistered, so doctor appended one PreToolUse entry per script with no
|
|
8
|
+
* matcher:
|
|
9
|
+
*
|
|
10
|
+
* { "hooks": [{ "type": "command", "timeout": 5,
|
|
11
|
+
* "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/hook-gate.sh\" <id> \"$CLAUDE_PROJECT_DIR/.claude/hooks/<id>.sh\"" }] }
|
|
12
|
+
*
|
|
13
|
+
* With no matcher Claude Code runs each of them on every tool call, so guards
|
|
14
|
+
* such as block-hq-glob and protect-core block Bash, Skill and Read. An HQ
|
|
15
|
+
* update later moves them from settings.json into settings.local.json as
|
|
16
|
+
* "user customizations", where the release reset no longer clears them.
|
|
17
|
+
*
|
|
18
|
+
* Detection is deliberately exact: only an entry with no matcher, exactly one
|
|
19
|
+
* hook, timeout 5, and the doctor-generated command for a matching id counts.
|
|
20
|
+
* A hand-written hook is never touched.
|
|
21
|
+
*/
|
|
22
|
+
const GENERATED_COMMAND = /^bash "\$CLAUDE_PROJECT_DIR\/\.claude\/hooks\/hook-gate\.sh" ([A-Za-z0-9._-]+) "\$CLAUDE_PROJECT_DIR\/\.claude\/hooks\/([A-Za-z0-9._-]+)\.sh"$/;
|
|
23
|
+
export function isStrayGateEntry(entry) {
|
|
24
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
25
|
+
return false;
|
|
26
|
+
const e = entry;
|
|
27
|
+
if (e.matcher !== undefined)
|
|
28
|
+
return false;
|
|
29
|
+
const keys = Object.keys(e);
|
|
30
|
+
if (keys.length !== 1 || keys[0] !== "hooks")
|
|
31
|
+
return false;
|
|
32
|
+
if (!Array.isArray(e.hooks) || e.hooks.length !== 1)
|
|
33
|
+
return false;
|
|
34
|
+
const hook = e.hooks[0];
|
|
35
|
+
if (!hook || hook.type !== "command" || hook.timeout !== 5 || typeof hook.command !== "string")
|
|
36
|
+
return false;
|
|
37
|
+
const m = GENERATED_COMMAND.exec(hook.command);
|
|
38
|
+
return !!m && m[1] === m[2];
|
|
39
|
+
}
|
|
40
|
+
/** Count stray entries across every hook event in a parsed settings object. */
|
|
41
|
+
export function countStrayGateEntries(settings) {
|
|
42
|
+
const hooks = settings?.hooks;
|
|
43
|
+
if (!hooks || typeof hooks !== "object")
|
|
44
|
+
return 0;
|
|
45
|
+
let n = 0;
|
|
46
|
+
for (const entries of Object.values(hooks)) {
|
|
47
|
+
if (Array.isArray(entries))
|
|
48
|
+
n += entries.filter(isStrayGateEntry).length;
|
|
49
|
+
}
|
|
50
|
+
return n;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Return a copy of `settings` with every stray entry removed. An event array
|
|
54
|
+
* left empty is dropped, and an empty `hooks` map is dropped, so a file whose
|
|
55
|
+
* only hooks were strays goes back to its original shape. Everything else,
|
|
56
|
+
* including permissions and real hooks, is preserved.
|
|
57
|
+
*/
|
|
58
|
+
export function removeStrayGateEntries(settings) {
|
|
59
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings))
|
|
60
|
+
return { settings, removed: 0 };
|
|
61
|
+
const root = { ...settings };
|
|
62
|
+
const hooks = root.hooks;
|
|
63
|
+
if (!hooks || typeof hooks !== "object" || Array.isArray(hooks))
|
|
64
|
+
return { settings, removed: 0 };
|
|
65
|
+
let removed = 0;
|
|
66
|
+
const next = {};
|
|
67
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
68
|
+
if (!Array.isArray(entries)) {
|
|
69
|
+
next[event] = entries;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const kept = entries.filter((e) => !isStrayGateEntry(e));
|
|
73
|
+
removed += entries.length - kept.length;
|
|
74
|
+
if (kept.length > 0 || entries.length === 0)
|
|
75
|
+
next[event] = kept;
|
|
76
|
+
}
|
|
77
|
+
if (Object.keys(next).length > 0)
|
|
78
|
+
root.hooks = next;
|
|
79
|
+
else
|
|
80
|
+
delete root.hooks;
|
|
81
|
+
return { settings: root, removed };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=stray-gate-entries.js.map
|
|
@@ -224,7 +224,7 @@ export function emitPlanLimitNag(opts = {}) {
|
|
|
224
224
|
if (worst === null)
|
|
225
225
|
return;
|
|
226
226
|
warningShownThisSession = true;
|
|
227
|
-
const line = `⚠ HQ
|
|
227
|
+
const line = `⚠ HQ Starter plan: ${formatEntryLine(worst.key, worst.entry)}. Upgrade: ${resolvedUpgradeUrl}`;
|
|
228
228
|
write(chalk.yellow(line) + "\n");
|
|
229
229
|
}
|
|
230
230
|
catch {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const TEAM_UPGRADE_HINT = "Run `hq billing upgrade` to move to
|
|
1
|
+
const TEAM_UPGRADE_HINT = "Run `hq billing upgrade` to move to HQ Workforce.";
|
|
2
2
|
export class PlanGateError extends Error {
|
|
3
3
|
code;
|
|
4
4
|
details;
|
|
@@ -23,19 +23,19 @@ function formatPlanGateDetails(code, details) {
|
|
|
23
23
|
typeof details.used === "number" &&
|
|
24
24
|
typeof details.limit === "number") {
|
|
25
25
|
const upgrade = typeof details.upgradeUrl === "string"
|
|
26
|
-
? `Upgrade to HQ
|
|
27
|
-
: "Upgrade to HQ
|
|
26
|
+
? `Upgrade to HQ Workforce ($500/mo) to remove limits: ${details.upgradeUrl}`
|
|
27
|
+
: "Upgrade to HQ Workforce ($500/mo) to remove limits.";
|
|
28
28
|
return [
|
|
29
|
-
`
|
|
29
|
+
`Starter plan limit reached: ${details.resource} ${details.used}/${details.limit} used.`,
|
|
30
30
|
upgrade,
|
|
31
31
|
existingResourcesNote,
|
|
32
32
|
].join("\n");
|
|
33
33
|
}
|
|
34
34
|
const upgrade = typeof details.upgradeUrl === "string"
|
|
35
|
-
? `Upgrade to HQ
|
|
36
|
-
: "Upgrade to HQ
|
|
35
|
+
? `Upgrade to HQ Workforce ($500/mo) to remove limits: ${details.upgradeUrl}`
|
|
36
|
+
: "Upgrade to HQ Workforce ($500/mo) to remove limits.";
|
|
37
37
|
return [
|
|
38
|
-
"HQ
|
|
38
|
+
"HQ Workforce plan required for this feature.",
|
|
39
39
|
upgrade,
|
|
40
40
|
existingResourcesNote,
|
|
41
41
|
].join("\n");
|
|
@@ -56,7 +56,7 @@ export async function offerTeamUpgrade(onConfirm, deps = {}) {
|
|
|
56
56
|
if (!(deps.interactive ?? canOfferTeamUpgrade(deps.argv)))
|
|
57
57
|
return false;
|
|
58
58
|
if (deps.ask) {
|
|
59
|
-
const answer = await deps.ask("Upgrade to HQ
|
|
59
|
+
const answer = await deps.ask("Upgrade to HQ Workforce ($500/mo) now? [y/N] ");
|
|
60
60
|
if (!/^y(?:es)?$/i.test(answer.trim()))
|
|
61
61
|
return false;
|
|
62
62
|
await onConfirm();
|
|
@@ -65,7 +65,7 @@ export async function offerTeamUpgrade(onConfirm, deps = {}) {
|
|
|
65
65
|
const { createInterface } = await import("node:readline/promises");
|
|
66
66
|
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
67
67
|
try {
|
|
68
|
-
const answer = await readline.question("Upgrade to HQ
|
|
68
|
+
const answer = await readline.question("Upgrade to HQ Workforce ($500/mo) now? [y/N] ");
|
|
69
69
|
if (!/^y(?:es)?$/i.test(answer.trim()))
|
|
70
70
|
return false;
|
|
71
71
|
await onConfirm();
|
|
@@ -10,7 +10,7 @@ export interface TeamUpgradeOptions {
|
|
|
10
10
|
error?: (text: string) => void;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Start the owner-gated HQ
|
|
13
|
+
* Start the owner-gated HQ Workforce Checkout flow. Stripe collects payment details
|
|
14
14
|
* in its hosted browser page; the CLI never receives card data.
|
|
15
15
|
*/
|
|
16
16
|
export declare function upgradeToTeam(opts?: TeamUpgradeOptions): Promise<string>;
|
|
@@ -14,7 +14,7 @@ function ownerUpgradeError() {
|
|
|
14
14
|
return Object.assign(new Error("Only a company owner can upgrade. Ask a company owner to upgrade."), { expected: true });
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Start the owner-gated HQ
|
|
17
|
+
* Start the owner-gated HQ Workforce Checkout flow. Stripe collects payment details
|
|
18
18
|
* in its hosted browser page; the CLI never receives card data.
|
|
19
19
|
*/
|
|
20
20
|
export async function upgradeToTeam(opts = {}) {
|
|
@@ -35,7 +35,7 @@ export async function upgradeToTeam(opts = {}) {
|
|
|
35
35
|
if (response.status === 403 || /owner/i.test(`${body.error ?? ""} ${body.message ?? ""}`)) {
|
|
36
36
|
throw ownerUpgradeError();
|
|
37
37
|
}
|
|
38
|
-
throw Object.assign(new Error(`Could not start HQ
|
|
38
|
+
throw Object.assign(new Error(`Could not start HQ Workforce checkout: ${body.error ?? body.message ?? response.statusText}`), { expected: true });
|
|
39
39
|
}
|
|
40
40
|
const { url } = (await response.json());
|
|
41
41
|
if (typeof url !== "string" || !url) {
|
|
@@ -46,7 +46,7 @@ export async function upgradeToTeam(opts = {}) {
|
|
|
46
46
|
write(`${JSON.stringify({ url })}\n`);
|
|
47
47
|
return url;
|
|
48
48
|
}
|
|
49
|
-
write("Opening HQ
|
|
49
|
+
write("Opening HQ Workforce checkout ($500/mo) in your browser — finish there and you're upgraded.\n");
|
|
50
50
|
const skipBrowser = opts.noBrowser || (opts.headless ?? isHeadless());
|
|
51
51
|
if (skipBrowser) {
|
|
52
52
|
write(`Open this URL to continue: ${url}\n`);
|