@indigoai-us/hq-cli 5.119.8 → 5.119.10
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 +43 -0
- package/dist/command-catalog.generated.d.ts +54 -1
- package/dist/command-catalog.generated.js +71 -1
- package/dist/commands/agent-inbox.js +6 -0
- package/dist/commands/dm.d.ts +31 -0
- package/dist/commands/dm.js +24 -3
- package/dist/commands/skill.d.ts +20 -1
- package/dist/commands/skill.js +250 -15
- package/dist/commands/whoami.d.ts +3 -3
- package/dist/commands/whoami.js +5 -5
- package/dist/lib/agent-kit/inbox-state.d.ts +22 -2
- package/dist/lib/agent-kit/inbox-state.js +40 -1
- package/dist/lib/billing/plan-lock.d.ts +33 -18
- package/dist/lib/billing/plan-lock.js +46 -26
- package/dist/lib/plan-limit-nag.d.ts +71 -5
- package/dist/lib/plan-limit-nag.js +224 -37
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.119.10] — 2026-09-18
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `hq skill register` reserves a company skill UID from a proposal-lane
|
|
10
|
+
source and writes the server-stamped bytes only to `--stamped-output`.
|
|
11
|
+
It does not write `companies/<company>/skills/`, create a discovery
|
|
12
|
+
wrapper, or sync. `hq skill promote` copies those exact bytes into the
|
|
13
|
+
canonical skill path only after `--expected-sha256` matches and the
|
|
14
|
+
stamped `skill_uid` matches the server registration. A differing
|
|
15
|
+
canonical file requires `--reviewed-update`. Use `--no-sync` to leave
|
|
16
|
+
upload to a later steward command.
|
|
17
|
+
|
|
18
|
+
## [5.119.9] — 2026-09-17
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- The CLI now prints a one-time notice when your workspace crosses a plan
|
|
23
|
+
threshold — reaching 80% of a Starter limit, or going past one. The server
|
|
24
|
+
decides when a threshold has been crossed and sends it with the response, so
|
|
25
|
+
a session you already have open surfaces the notice on its next API call
|
|
26
|
+
rather than on its next launch. Each `{resource}:{band}` is printed exactly
|
|
27
|
+
once per episode, remembered in `~/.hq/plan-limit-nag.json`; a dimension that
|
|
28
|
+
drops back under the threshold and climbs again is announced again.
|
|
29
|
+
`HQ_NO_PLAN_LIMIT_NAG=1` silences it along with the other plan-limit lines.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- A file attached to a DM is now visible to a bot reading its mail. `hq dm
|
|
34
|
+
inbox`, `hq dm thread`, `hq dm channel` and `hq agent inbox` print each
|
|
35
|
+
attached file's name, type and vault path under the message body; the JSON
|
|
36
|
+
forms carry an `attachments[]` field. Previously only the body text was
|
|
37
|
+
rendered, so an agent sent a screenshot answered "no image came through".
|
|
38
|
+
|
|
5
39
|
## [5.119.8] — 2026-09-17
|
|
6
40
|
|
|
7
41
|
### Fixed
|
|
@@ -44,6 +78,15 @@
|
|
|
44
78
|
on Starter: 1", and tells you to "disconnect integrations until you are at 1
|
|
45
79
|
or fewer"; the per-turn line reads "over its 1-integration limit". Nothing in
|
|
46
80
|
the CLI says Starter has no integrations any more.
|
|
81
|
+
- Plan-limit messages now say what is actually happening. Going over a Starter
|
|
82
|
+
limit never stopped the workspace, and the copy no longer implies it did:
|
|
83
|
+
the words "locked" and "read-only" are gone from every line `hq` prints, and
|
|
84
|
+
the notice states the two things that do pause — adding new files and adding
|
|
85
|
+
new secrets — while everything else keeps working. A test scans every
|
|
86
|
+
rendered string for both words so the copy cannot drift back.
|
|
87
|
+
- The over-limit per-turn nag is one line instead of a box, and it repeats at
|
|
88
|
+
most once every 6 hours (was once a day). `HQ_NO_PLAN_LIMIT_NAG=1` still
|
|
89
|
+
silences it.
|
|
47
90
|
|
|
48
91
|
## [5.119.4] — 2026-09-17
|
|
49
92
|
|
|
@@ -1877,7 +1877,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
1877
1877
|
readonly subcommands: readonly [];
|
|
1878
1878
|
}, {
|
|
1879
1879
|
readonly name: "skill";
|
|
1880
|
-
readonly description: "Create
|
|
1880
|
+
readonly description: "Create, stage, promote, and discuss company skills";
|
|
1881
1881
|
readonly aliases: readonly [];
|
|
1882
1882
|
readonly hidden: false;
|
|
1883
1883
|
readonly usage: "[options] [command]";
|
|
@@ -1914,6 +1914,59 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
1914
1914
|
readonly description: "Refresh local skill discovery without registration or sync";
|
|
1915
1915
|
}];
|
|
1916
1916
|
readonly subcommands: readonly [];
|
|
1917
|
+
}, {
|
|
1918
|
+
readonly name: "register";
|
|
1919
|
+
readonly description: "Register a company skill to a proposal-lane stamped file without writing the canonical tree";
|
|
1920
|
+
readonly aliases: readonly [];
|
|
1921
|
+
readonly hidden: false;
|
|
1922
|
+
readonly usage: "[options] <slug>";
|
|
1923
|
+
readonly arguments: readonly [{
|
|
1924
|
+
readonly name: "slug";
|
|
1925
|
+
readonly required: true;
|
|
1926
|
+
readonly variadic: false;
|
|
1927
|
+
}];
|
|
1928
|
+
readonly options: readonly [{
|
|
1929
|
+
readonly flags: "--source <path>";
|
|
1930
|
+
readonly description: "Caller-authored SKILL.md (or its directory) to send to the server validator";
|
|
1931
|
+
}, {
|
|
1932
|
+
readonly flags: "--stamped-output <path>";
|
|
1933
|
+
readonly description: "Write the complete server-stamped bytes here, outside companies/<company>/skills/";
|
|
1934
|
+
}, {
|
|
1935
|
+
readonly flags: "--no-canonical-write";
|
|
1936
|
+
readonly description: "Refuse to write companies/<company>/skills/ (always the register behavior)";
|
|
1937
|
+
}, {
|
|
1938
|
+
readonly flags: "--no-surface";
|
|
1939
|
+
readonly description: "Do not create a discovery wrapper (always the register behavior)";
|
|
1940
|
+
}, {
|
|
1941
|
+
readonly flags: "--no-sync";
|
|
1942
|
+
readonly description: "Do not upload the skill file (always the register behavior)";
|
|
1943
|
+
}];
|
|
1944
|
+
readonly subcommands: readonly [];
|
|
1945
|
+
}, {
|
|
1946
|
+
readonly name: "promote";
|
|
1947
|
+
readonly description: "Copy independently-cleared stamped bytes into the canonical skill path after SHA-256 and skill_uid checks";
|
|
1948
|
+
readonly aliases: readonly [];
|
|
1949
|
+
readonly hidden: false;
|
|
1950
|
+
readonly usage: "[options] <slug>";
|
|
1951
|
+
readonly arguments: readonly [{
|
|
1952
|
+
readonly name: "slug";
|
|
1953
|
+
readonly required: true;
|
|
1954
|
+
readonly variadic: false;
|
|
1955
|
+
}];
|
|
1956
|
+
readonly options: readonly [{
|
|
1957
|
+
readonly flags: "--from <path>";
|
|
1958
|
+
readonly description: "Independently-cleared stamped SKILL.md (or its directory)";
|
|
1959
|
+
}, {
|
|
1960
|
+
readonly flags: "--expected-sha256 <hex>";
|
|
1961
|
+
readonly description: "SHA-256 of the exact stamped file bytes that were cleared";
|
|
1962
|
+
}, {
|
|
1963
|
+
readonly flags: "--reviewed-update";
|
|
1964
|
+
readonly description: "Replace an existing canonical SKILL.md whose bytes differ from the stamped file";
|
|
1965
|
+
}, {
|
|
1966
|
+
readonly flags: "--no-sync";
|
|
1967
|
+
readonly description: "Promote locally without uploading the canonical file";
|
|
1968
|
+
}];
|
|
1969
|
+
readonly subcommands: readonly [];
|
|
1917
1970
|
}, {
|
|
1918
1971
|
readonly name: "propose";
|
|
1919
1972
|
readonly description: "Post a comment-only improvement for a skill";
|
|
@@ -2403,7 +2403,7 @@ export const COMMAND_CATALOG = [
|
|
|
2403
2403
|
},
|
|
2404
2404
|
{
|
|
2405
2405
|
"name": "skill",
|
|
2406
|
-
"description": "Create
|
|
2406
|
+
"description": "Create, stage, promote, and discuss company skills",
|
|
2407
2407
|
"aliases": [],
|
|
2408
2408
|
"hidden": false,
|
|
2409
2409
|
"usage": "[options] [command]",
|
|
@@ -2452,6 +2452,76 @@ export const COMMAND_CATALOG = [
|
|
|
2452
2452
|
],
|
|
2453
2453
|
"subcommands": []
|
|
2454
2454
|
},
|
|
2455
|
+
{
|
|
2456
|
+
"name": "register",
|
|
2457
|
+
"description": "Register a company skill to a proposal-lane stamped file without writing the canonical tree",
|
|
2458
|
+
"aliases": [],
|
|
2459
|
+
"hidden": false,
|
|
2460
|
+
"usage": "[options] <slug>",
|
|
2461
|
+
"arguments": [
|
|
2462
|
+
{
|
|
2463
|
+
"name": "slug",
|
|
2464
|
+
"required": true,
|
|
2465
|
+
"variadic": false
|
|
2466
|
+
}
|
|
2467
|
+
],
|
|
2468
|
+
"options": [
|
|
2469
|
+
{
|
|
2470
|
+
"flags": "--source <path>",
|
|
2471
|
+
"description": "Caller-authored SKILL.md (or its directory) to send to the server validator"
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
"flags": "--stamped-output <path>",
|
|
2475
|
+
"description": "Write the complete server-stamped bytes here, outside companies/<company>/skills/"
|
|
2476
|
+
},
|
|
2477
|
+
{
|
|
2478
|
+
"flags": "--no-canonical-write",
|
|
2479
|
+
"description": "Refuse to write companies/<company>/skills/ (always the register behavior)"
|
|
2480
|
+
},
|
|
2481
|
+
{
|
|
2482
|
+
"flags": "--no-surface",
|
|
2483
|
+
"description": "Do not create a discovery wrapper (always the register behavior)"
|
|
2484
|
+
},
|
|
2485
|
+
{
|
|
2486
|
+
"flags": "--no-sync",
|
|
2487
|
+
"description": "Do not upload the skill file (always the register behavior)"
|
|
2488
|
+
}
|
|
2489
|
+
],
|
|
2490
|
+
"subcommands": []
|
|
2491
|
+
},
|
|
2492
|
+
{
|
|
2493
|
+
"name": "promote",
|
|
2494
|
+
"description": "Copy independently-cleared stamped bytes into the canonical skill path after SHA-256 and skill_uid checks",
|
|
2495
|
+
"aliases": [],
|
|
2496
|
+
"hidden": false,
|
|
2497
|
+
"usage": "[options] <slug>",
|
|
2498
|
+
"arguments": [
|
|
2499
|
+
{
|
|
2500
|
+
"name": "slug",
|
|
2501
|
+
"required": true,
|
|
2502
|
+
"variadic": false
|
|
2503
|
+
}
|
|
2504
|
+
],
|
|
2505
|
+
"options": [
|
|
2506
|
+
{
|
|
2507
|
+
"flags": "--from <path>",
|
|
2508
|
+
"description": "Independently-cleared stamped SKILL.md (or its directory)"
|
|
2509
|
+
},
|
|
2510
|
+
{
|
|
2511
|
+
"flags": "--expected-sha256 <hex>",
|
|
2512
|
+
"description": "SHA-256 of the exact stamped file bytes that were cleared"
|
|
2513
|
+
},
|
|
2514
|
+
{
|
|
2515
|
+
"flags": "--reviewed-update",
|
|
2516
|
+
"description": "Replace an existing canonical SKILL.md whose bytes differ from the stamped file"
|
|
2517
|
+
},
|
|
2518
|
+
{
|
|
2519
|
+
"flags": "--no-sync",
|
|
2520
|
+
"description": "Promote locally without uploading the canonical file"
|
|
2521
|
+
}
|
|
2522
|
+
],
|
|
2523
|
+
"subcommands": []
|
|
2524
|
+
},
|
|
2455
2525
|
{
|
|
2456
2526
|
"name": "propose",
|
|
2457
2527
|
"description": "Post a comment-only improvement for a skill",
|
|
@@ -52,6 +52,12 @@ export function registerAgentInboxCommand(agent) {
|
|
|
52
52
|
console.log(`${chalk.bold(r.id)}${state} ${r.channel ?? "?"} from ${r.from}${r.fromUid ? ` <${r.fromUid}>` : ""} ${r.at ?? ""}`);
|
|
53
53
|
if (r.text)
|
|
54
54
|
console.log(` ${r.text.replace(/\n/g, "\n ")}`);
|
|
55
|
+
for (const f of r.attachments ?? []) {
|
|
56
|
+
const label = f.name ?? f.vaultPath.split("/").pop() ?? "file";
|
|
57
|
+
const type = f.contentType ? ` (${f.contentType})` : "";
|
|
58
|
+
console.log(` ${chalk.dim("[file]")} ${label}${type}`);
|
|
59
|
+
console.log(` ${chalk.dim(`vault: ${f.vaultPath}`)}`);
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
62
|
if (!opts.all)
|
|
57
63
|
console.log(chalk.dim(`\nReply with hq dm <uid> "…", then: hq agent inbox done <id>`));
|
package/dist/commands/dm.d.ts
CHANGED
|
@@ -201,6 +201,9 @@ export interface DmInboxEvent {
|
|
|
201
201
|
details?: string;
|
|
202
202
|
prompt?: string;
|
|
203
203
|
acknowledgedAt?: string;
|
|
204
|
+
/** US-007 file references. Singular `attachment` is the legacy form. */
|
|
205
|
+
attachments?: DmAttachment[];
|
|
206
|
+
attachment?: DmAttachment;
|
|
204
207
|
}
|
|
205
208
|
/** One message in a 1:1 thread as returned by GET /v1/notify/thread. */
|
|
206
209
|
export interface DmThreadMessage {
|
|
@@ -213,6 +216,8 @@ export interface DmThreadMessage {
|
|
|
213
216
|
direction: "in" | "out";
|
|
214
217
|
details?: string;
|
|
215
218
|
prompt?: string;
|
|
219
|
+
attachments?: DmAttachment[];
|
|
220
|
+
attachment?: DmAttachment;
|
|
216
221
|
}
|
|
217
222
|
/** One channel/group message from GET /v1/notify/channels/{id}/messages. */
|
|
218
223
|
export interface ChannelMessageItem {
|
|
@@ -223,7 +228,33 @@ export interface ChannelMessageItem {
|
|
|
223
228
|
fromDisplayName?: string;
|
|
224
229
|
body: string;
|
|
225
230
|
createdAt: string;
|
|
231
|
+
attachments?: DmAttachment[];
|
|
232
|
+
attachment?: DmAttachment;
|
|
226
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* One file attached to a DM (US-007 `attachments[]` on the read endpoints). The
|
|
236
|
+
* server returns a vault path, not bytes: a reader fetches it with
|
|
237
|
+
* a presigned read of that key.
|
|
238
|
+
*/
|
|
239
|
+
export interface DmAttachment {
|
|
240
|
+
vaultPath: string;
|
|
241
|
+
name?: string;
|
|
242
|
+
sizeBytes?: number;
|
|
243
|
+
contentType?: string;
|
|
244
|
+
kind?: string;
|
|
245
|
+
companyUid?: string;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Render the attachment lines for one message. An agent reading its DMs through
|
|
249
|
+
* the CLI only ever sees this text, so the vault path AND the command that
|
|
250
|
+
* fetches it both have to be on screen — a bare "[image]" marker is why a bot
|
|
251
|
+
* answers "no image came through". Returns "" when there is nothing attached.
|
|
252
|
+
* Pure → unit-testable.
|
|
253
|
+
*/
|
|
254
|
+
export declare function formatAttachments(m: {
|
|
255
|
+
attachments?: DmAttachment[];
|
|
256
|
+
attachment?: DmAttachment;
|
|
257
|
+
}, indent?: string): string;
|
|
227
258
|
/**
|
|
228
259
|
* Human label for a message sender — display name, else email, else uid. Pure →
|
|
229
260
|
* unit-testable.
|
package/dist/commands/dm.js
CHANGED
|
@@ -693,6 +693,27 @@ async function runDmRequests() {
|
|
|
693
693
|
process.exit(1);
|
|
694
694
|
}
|
|
695
695
|
}
|
|
696
|
+
/**
|
|
697
|
+
* Render the attachment lines for one message. An agent reading its DMs through
|
|
698
|
+
* the CLI only ever sees this text, so the vault path AND the command that
|
|
699
|
+
* fetches it both have to be on screen — a bare "[image]" marker is why a bot
|
|
700
|
+
* answers "no image came through". Returns "" when there is nothing attached.
|
|
701
|
+
* Pure → unit-testable.
|
|
702
|
+
*/
|
|
703
|
+
export function formatAttachments(m, indent = " ") {
|
|
704
|
+
const files = (m.attachments?.length ? m.attachments : m.attachment ? [m.attachment] : [])
|
|
705
|
+
.filter((f) => Boolean(f?.vaultPath));
|
|
706
|
+
if (files.length === 0)
|
|
707
|
+
return "";
|
|
708
|
+
return files
|
|
709
|
+
.map((f) => {
|
|
710
|
+
const label = f.name?.trim() || f.vaultPath.split("/").pop() || "file";
|
|
711
|
+
const type = f.contentType ? ` ${chalk.dim(f.contentType)}` : "";
|
|
712
|
+
const company = f.companyUid ? chalk.dim(` company=${f.companyUid}`) : "";
|
|
713
|
+
return `\n${indent}${chalk.dim("[file]")} ${label}${type}\n${indent} ${chalk.dim(`vault: ${f.vaultPath}`)}${company}`;
|
|
714
|
+
})
|
|
715
|
+
.join("");
|
|
716
|
+
}
|
|
696
717
|
/**
|
|
697
718
|
* Human label for a message sender — display name, else email, else uid. Pure →
|
|
698
719
|
* unit-testable.
|
|
@@ -773,20 +794,20 @@ export function formatInboxEvent(e, nowMs) {
|
|
|
773
794
|
const when = chalk.dim(formatRelativeTime(e.createdAt, nowMs));
|
|
774
795
|
const who = chalk.bold(senderLabel(e));
|
|
775
796
|
const email = e.fromEmail && e.fromDisplayName ? chalk.dim(` <${e.fromEmail}>`) : "";
|
|
776
|
-
return `${marker} ${when} ${who}${email}\n ${firstLine(e.body)}`;
|
|
797
|
+
return `${marker} ${when} ${who}${email}\n ${firstLine(e.body)}${formatAttachments(e)}`;
|
|
777
798
|
}
|
|
778
799
|
/** Render one 1:1 thread line, tagged by direction. */
|
|
779
800
|
export function formatThreadMessage(m, nowMs) {
|
|
780
801
|
const arrow = m.direction === "out" ? chalk.dim("→") : chalk.cyan("←");
|
|
781
802
|
const who = m.direction === "out" ? "you" : senderLabel(m);
|
|
782
803
|
const when = chalk.dim(formatRelativeTime(m.createdAt, nowMs));
|
|
783
|
-
return `${arrow} ${chalk.bold(who)} ${when}\n ${firstLine(m.body)}`;
|
|
804
|
+
return `${arrow} ${chalk.bold(who)} ${when}\n ${firstLine(m.body)}${formatAttachments(m)}`;
|
|
784
805
|
}
|
|
785
806
|
/** Render one channel/group message line. */
|
|
786
807
|
export function formatChannelMessage(m, nowMs) {
|
|
787
808
|
const who = chalk.bold(senderLabel(m));
|
|
788
809
|
const when = chalk.dim(formatRelativeTime(m.createdAt, nowMs));
|
|
789
|
-
return `${who} ${when}\n ${firstLine(m.body)}`;
|
|
810
|
+
return `${who} ${when}\n ${firstLine(m.body)}${formatAttachments(m)}`;
|
|
790
811
|
}
|
|
791
812
|
/** POST /v1/notify/inbox/ack — idempotently mark messages read. */
|
|
792
813
|
async function ackEvents(token, eventIds) {
|
package/dist/commands/skill.d.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `hq skill create <slug>` registers a canonical company skill, stamps its
|
|
5
5
|
* immutable UID, surfaces its generated runtime wrapper, and syncs it.
|
|
6
|
+
* `hq skill register <slug>` reserves that UID from a proposal-lane source and
|
|
7
|
+
* writes server-stamped bytes only to `--stamped-output` — never under
|
|
8
|
+
* `companies/<company>/skills/`, and never as a discovery wrapper or sync.
|
|
9
|
+
* `hq skill promote <slug>` copies independently-cleared stamped bytes into the
|
|
10
|
+
* canonical tree only after the expected SHA-256 and registered skill_uid match.
|
|
6
11
|
* `hq skill propose <uid|path> --message "…"` posts a whole-skill comment to
|
|
7
12
|
* the same improvement thread shown in HQ Console. It never uploads a modified
|
|
8
13
|
* SKILL.md and cannot overwrite live content. `hq skill delete <target>` removes
|
|
@@ -39,7 +44,21 @@ export declare function makeSkillTemplate(input: {
|
|
|
39
44
|
description?: string;
|
|
40
45
|
}): string;
|
|
41
46
|
/** Replace SKILL.md without exposing a partially-written identity to agents. */
|
|
42
|
-
export declare function writeSkillFileAtomically(filePath: string, content: string): void;
|
|
47
|
+
export declare function writeSkillFileAtomically(filePath: string, content: string | Buffer): void;
|
|
48
|
+
export declare function sha256Hex(data: string | Buffer): string;
|
|
49
|
+
export declare function skillVaultPath(skillSlug: string): string;
|
|
50
|
+
export declare function isInsideCompanySkillsTree(hqRoot: string, candidatePath: string): boolean;
|
|
51
|
+
export declare function resolveSkillMarkdownPath(target: string, cwd: string): string;
|
|
52
|
+
export declare function assertStagedOutputPath(hqRoot: string, outputPath: string): string;
|
|
53
|
+
export declare function assertSha256HexMatch(actualHex: string, expectedHex: string): void;
|
|
54
|
+
export declare function assertRegisteredSkillResponse(registered: {
|
|
55
|
+
skillUid?: unknown;
|
|
56
|
+
content?: unknown;
|
|
57
|
+
}): {
|
|
58
|
+
skillUid: string;
|
|
59
|
+
content: string;
|
|
60
|
+
};
|
|
61
|
+
export declare function normalizeLiveSkillVaultPath(rawPath: string): string;
|
|
43
62
|
export declare function mapSkillError(status: number, body: Record<string, unknown>): string;
|
|
44
63
|
/**
|
|
45
64
|
* Type a failed skills-API response by its HTTP status so the top-level error
|