@indigoai-us/hq-cli 5.345.169 → 5.345.170
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 +14 -0
- package/dist/command-catalog.generated.d.ts +49 -4
- package/dist/command-catalog.generated.js +60 -4
- package/dist/commands/access.js +7 -2
- package/dist/commands/bot.js +4 -0
- package/dist/commands/cloud.d.ts +20 -0
- package/dist/commands/cloud.js +55 -4
- package/dist/commands/feedback-triage.d.ts +43 -1
- package/dist/commands/feedback-triage.js +123 -23
- package/dist/commands/feedback.d.ts +40 -1
- package/dist/commands/feedback.js +184 -4
- package/dist/commands/files.d.ts +42 -0
- package/dist/commands/files.js +223 -30
- package/dist/commands/mesh.js +6 -0
- package/dist/commands/sync-manifest.d.ts +16 -0
- package/dist/commands/sync-manifest.js +17 -1
- package/dist/lib/bot/decisions.d.ts +62 -0
- package/dist/lib/bot/decisions.js +228 -0
- package/dist/lib/bot/index.d.ts +1 -0
- package/dist/lib/bot/index.js +1 -0
- package/dist/lib/bot/run.d.ts +7 -0
- package/dist/lib/bot/run.js +104 -1
- package/dist/lib/daemon/catalog.d.ts +4 -0
- package/dist/lib/daemon/catalog.js +60 -29
- package/dist/lib/daemon/config.d.ts +7 -0
- package/dist/lib/daemon/config.js +32 -0
- package/dist/lib/daemon/run.d.ts +3 -2
- package/dist/lib/daemon/run.js +23 -3
- package/dist/lib/daemon/supervisor.d.ts +1 -0
- package/dist/lib/daemon/supervisor.js +4 -0
- package/dist/lib/daemon/sync-core-canary.d.ts +9 -0
- package/dist/lib/daemon/sync-core-canary.js +56 -0
- package/dist/lib/doctor/checks/sync-health.d.ts +32 -0
- package/dist/lib/doctor/checks/sync-health.js +54 -1
- package/dist/lib/doctor/fix/apply.d.ts +26 -4
- package/dist/lib/doctor/fix/apply.js +95 -11
- package/dist/lib/doctor/types.d.ts +1 -1
- package/dist/lib/doctor/types.js +1 -0
- package/dist/lib/mesh/live/daemon/work-subscribe.js +31 -0
- package/dist/lib/work-context/organize.d.ts +7 -0
- package/dist/lib/work-context/organize.js +21 -1
- package/dist/sentry-before-send.js +1 -0
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.170] — 2026-10-25
|
|
6
|
+
- `hq access --fix` now labels its suggested recipient as an access contact and explicitly distinguishes an ACL creator from a fallback company owner or admin. The label no longer implies that the contact wrote the file; access checks and sharing permissions are unchanged.
|
|
7
|
+
|
|
8
|
+
- `hq doctor --fix` no longer counts a manifest upload as done when `hq sync manifest` exits 0 without uploading. It now runs `hq sync manifest --json` and reports `soft_skipped`, `throttled`, `disabled`, `locked` and `resend_full_scheduled` as not uploaded, with the reason in the unresolved-check next step. When the server asks for a full resend, `--fix` retries once with `--full`, so that scope can clear in the same run; if the server asks again, the reason says a full resend is queued. An `hq` on PATH that predates `--json` falls back to exit-code handling. `hq sync manifest --json` prints the pass outcome as one JSON line (status, mode, error kind and HTTP status; no paths or error text). On hq-cloud releases that store the last failed pass, a stale `sync.manifest.*` row names that failure instead of saying the daily pass is not running, and a failure `--fix` cannot clear (404 route, 403, rejected identity; a 401 keeps its `--fix` target) is reported as `manifest-upload-rejected` with no `--fix` target (feedback_84d7a862).
|
|
9
|
+
- `hq feedback` adds a `stalled` status for work that started and then stopped. A feedback row is in progress only while it has a linked open PR or a live lane; after 48 hours with no activity it moves to `stalled` (the hq-pro sweep, guard and `prUrl`/`laneId` fields ship separately), which is listed apart from `new` (not started). `hq feedback list --status stalled` filters on it, the list table now has a STATUS column that marks stalled rows `stalled!`, and `--status` accepts `in-progress` for `in_progress`; the server validates the value. `hq feedback update --state` accepts `stalled`.
|
|
10
|
+
- `hq feedback update --state in-progress` (or `in_progress`) now requires `--pr <url>` or `--lane <id>`. The CLI first stores the link as `prUrl`/`laneId` and reads the ticket back to check that the server saved it, then posts the state. If the server rejects or ignores the link fields (a server without the new fields), the command exits 1 without posting the state, and the message gives the ticket's current state as read back from the server. The new `hq feedback claim <id>` assigns a ticket to you, and it and `hq feedback assign` send only the assignee and never set a status.
|
|
11
|
+
- `hq files share <name> --with <member>` now looks an extension-less name such as `knowledge` up in the vault before granting it. If `knowledge/` has files and no object `knowledge` exists, the grant covers the folder glob `knowledge/*` and a note says so. Before, it granted an exact object named `knowledge`, which covered nothing under the folder. If only the object exists, the exact key is granted as before. If both exist, neither exists, or the lookup fails, the command refuses with exit 1 and points to `--exact` or `'knowledge/*'`. The share-session path (no `--with`) resolves the name the same way. `hq files unshare <name> --with <member>` first removes the exact `<name>` grant (including rows left by older versions), and when none matches it tries `<name>/*` unless the vault shows `<name>` is only a file; it says which row it removed, lists both rows when nothing matched, and is never blocked by the vault lookup. `unshare` without `--with` still uses the prefix exactly as typed. `--exact` keeps the literal key, and now errors without `--with`. Prefixes with a `*` inside a segment (`**`, `know*`) or an empty segment (`/knowledge`) are refused. The access-request prompt sent to an owner now passes `--exact`, so it grants only the requested object.
|
|
12
|
+
- Fix `hq feedback list` hiding most items: `--status <s>` alone now lists every item in that status instead of only untriaged ones, the new `--open` lists every item that is not resolved, wont-fix or accepted, and a list cut short by `--limit`, the page cap or the server prints a notice on stderr and adds `truncated: true` to `--json` output.
|
|
13
|
+
- `hq mesh context organize` now lists project candidates for a session that attached a project existing only on this machine. Before this fix, such a session could be saved with its company slug but no company id (when the membership check was offline or failed during reconcile), and organize answered `candidates_unavailable` even when signed in. Organize now looks the slug up in your active memberships (5-second limit), saves the company id and fetches candidates. Offline sessions and slugs you are not an active member of keep the old answer. Follow-up to hq-pro #5114.
|
|
14
|
+
- `hq sync push` no longer prints a success line when uploads are held after an account sign-in change on this computer. Before, the push skipped every named file without counting it and reported "Pushed 0 file(s) (0 B, 0 skipped)" with exit 0, while the vault kept the old copy. It now prints how many files were not uploaded, names `hq sync held` and `hq sync release-held`, adds `filesHeldByIdentityFence` to the `--json` complete event, and exits 1. Feedback a69e12ba-c8c6-40a9-ba00-01e60d302e18.
|
|
15
|
+
- HQ's existing daemon can now supervise one explicitly configured personal-vault sync-core canary on macOS or Linux. Activation fails closed unless the binary, vault ownership and full-mode configuration match, hands ownership over without overlapping the existing sync runner, and restarts a core process that stops reporting health. The existing realtime connection accepts only the versioned, content-free sync wake envelope. Company vaults and machines without the canary setting keep the existing sync path unchanged.
|
|
16
|
+
- The personal local bot records a decision pick from the owner's HQ DM. When a decision is in conversation in that DM (`hq decide talk`) and the owner's whole reply names one option (`Release`, `ship`, `go with Release`), the bot records it through the decisions API with source `thread` and replies `Recorded: <option>.` Any other reply goes to the model with the decision loaded, and nothing is recorded. Only the owner's messages count, only decisions addressed to the owner in the owner's own companies are used, and the call uses the bot's own sign-in. After 24 hours with no pick the bot restates the full decision once. If the decisions API is not deployed (404), a pick gets a plain reply saying it was not recorded.
|
|
17
|
+
|
|
18
|
+
|
|
5
19
|
## [5.345.169] — 2026-10-24
|
|
6
20
|
- Sentry error reports now redact CloudFront signed-URL params (`Policy`, `Key-Pair-Id`) and the `AWSAccessKeyId` and `GoogleAccessId` ids when they appear as structured keys in event data, for example a parsed query under `extra`. The feedback diagnostics scrubber already redacted them (#2177); the Sentry scrubber caught them only inside strings. The shared structured-key cases now run against the Sentry scrubber too.
|
|
7
21
|
- A debounced automatic checkpoint is now queued for the next checkpoint sibling. Before this, `hq core checkpoint` wrote the `-auto-` thread when the per-session debounce skipped the sibling, but it never queued the payload, so no sibling ever filled the thread in. The payload is now added to the pending queue once, and the next sibling run picks it up. The debounce still limits how many siblings start. When the pending queue goes over its cap of 50, a message on stderr now gives the number of oldest payloads dropped. Feedback a2a5c2c3-5ea0-4e1f-8155-1f39ce6ac750.
|
|
@@ -1164,6 +1164,10 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
1164
1164
|
readonly flags: "--print";
|
|
1165
1165
|
readonly description: "Build and print the chunks as JSON; upload nothing";
|
|
1166
1166
|
readonly hidden: false;
|
|
1167
|
+
}, {
|
|
1168
|
+
readonly flags: "--json";
|
|
1169
|
+
readonly description: "Print the pass outcome as one JSON line instead of a summary";
|
|
1170
|
+
readonly hidden: false;
|
|
1167
1171
|
}, {
|
|
1168
1172
|
readonly flags: "--respect-throttle";
|
|
1169
1173
|
readonly description: "Honour the 24h per-scope upload throttle instead of forcing a pass (for scheduled callers)";
|
|
@@ -3067,6 +3071,10 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3067
3071
|
readonly flags: "--permission <level>";
|
|
3068
3072
|
readonly description: "Permission level (only with --with): read | write";
|
|
3069
3073
|
readonly hidden: false;
|
|
3074
|
+
}, {
|
|
3075
|
+
readonly flags: "--exact";
|
|
3076
|
+
readonly description: "With --with: grant exactly the object named by <prefix>, even when it looks like a folder (by default a bare folder name such as 'knowledge' is granted as the folder glob 'knowledge/*').";
|
|
3077
|
+
readonly hidden: false;
|
|
3070
3078
|
}, {
|
|
3071
3079
|
readonly flags: "--full";
|
|
3072
3080
|
readonly description: "Grant access to the ENTIRE vault (the '*' wildcard prefix) — no need to quote a glob. Requires --with; defaults to write permission.";
|
|
@@ -3092,7 +3100,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3092
3100
|
readonly subcommands: readonly [];
|
|
3093
3101
|
}, {
|
|
3094
3102
|
readonly name: "unshare";
|
|
3095
|
-
readonly description: "Remove a named file access grant, or remove an empty ACL row or grantee-less entries without --with.";
|
|
3103
|
+
readonly description: "Remove a named file access grant, or remove an empty ACL row or grantee-less entries without --with. With --with, a bare name such as 'knowledge' first removes the exact 'knowledge' grant (rows from older `hq files share` versions) and, when none matches, the folder-glob 'knowledge/*' grant. Without --with, the prefix is used exactly as typed: pass 'knowledge/*' (quoted) to clean up the folder-glob row.";
|
|
3096
3104
|
readonly aliases: readonly [];
|
|
3097
3105
|
readonly hidden: false;
|
|
3098
3106
|
readonly helpOption: true;
|
|
@@ -3109,6 +3117,10 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3109
3117
|
readonly flags: "--with <principal>";
|
|
3110
3118
|
readonly description: "Person uid, agent uid, company Outpost id (out_...), email address, group id, or '@all' to remove the grant";
|
|
3111
3119
|
readonly hidden: false;
|
|
3120
|
+
}, {
|
|
3121
|
+
readonly flags: "--exact";
|
|
3122
|
+
readonly description: "With --with: remove the grant on exactly the object named by <prefix>. By default a bare folder name such as 'knowledge' resolves to the folder glob 'knowledge/*', the same way `hq files share` resolves it.";
|
|
3123
|
+
readonly hidden: false;
|
|
3112
3124
|
}];
|
|
3113
3125
|
readonly subcommands: readonly [];
|
|
3114
3126
|
}, {
|
|
@@ -4565,12 +4577,20 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
4565
4577
|
}];
|
|
4566
4578
|
readonly options: readonly [{
|
|
4567
4579
|
readonly flags: "--state <state>";
|
|
4568
|
-
readonly description: "One of: triaged, in-progress, shipped, verified, resolved";
|
|
4580
|
+
readonly description: "One of: triaged, in-progress, stalled, shipped, verified, resolved";
|
|
4569
4581
|
readonly hidden: false;
|
|
4570
4582
|
}, {
|
|
4571
4583
|
readonly flags: "--note <text>";
|
|
4572
4584
|
readonly description: "What the customer should read (plain text, up to 2000 characters)";
|
|
4573
4585
|
readonly hidden: false;
|
|
4586
|
+
}, {
|
|
4587
|
+
readonly flags: "--pr <url>";
|
|
4588
|
+
readonly description: "Open pull request for this ticket (required with --state in-progress unless --lane is given)";
|
|
4589
|
+
readonly hidden: false;
|
|
4590
|
+
}, {
|
|
4591
|
+
readonly flags: "--lane <id>";
|
|
4592
|
+
readonly description: "Live lane working this ticket (required with --state in-progress unless --pr is given)";
|
|
4593
|
+
readonly hidden: false;
|
|
4574
4594
|
}];
|
|
4575
4595
|
readonly subcommands: readonly [];
|
|
4576
4596
|
}, {
|
|
@@ -4632,6 +4652,27 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
4632
4652
|
readonly hidden: false;
|
|
4633
4653
|
}];
|
|
4634
4654
|
readonly subcommands: readonly [];
|
|
4655
|
+
}, {
|
|
4656
|
+
readonly name: "claim";
|
|
4657
|
+
readonly description: "Assign a ticket to yourself (sets the assignee only, never the status)";
|
|
4658
|
+
readonly aliases: readonly [];
|
|
4659
|
+
readonly hidden: false;
|
|
4660
|
+
readonly helpOption: true;
|
|
4661
|
+
readonly helpCommand: false;
|
|
4662
|
+
readonly usage: "[options] <id>";
|
|
4663
|
+
readonly helpText: readonly [];
|
|
4664
|
+
readonly arguments: readonly [{
|
|
4665
|
+
readonly name: "id";
|
|
4666
|
+
readonly required: true;
|
|
4667
|
+
readonly variadic: false;
|
|
4668
|
+
readonly description: "Feedback id (feedback_<uuid>)";
|
|
4669
|
+
}];
|
|
4670
|
+
readonly options: readonly [{
|
|
4671
|
+
readonly flags: "--json";
|
|
4672
|
+
readonly description: "Print { id, assignee } as JSON";
|
|
4673
|
+
readonly hidden: false;
|
|
4674
|
+
}];
|
|
4675
|
+
readonly subcommands: readonly [];
|
|
4635
4676
|
}, {
|
|
4636
4677
|
readonly name: "list";
|
|
4637
4678
|
readonly description: "List feedback: untriaged (default), yours, or one person's";
|
|
@@ -4646,6 +4687,10 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
4646
4687
|
readonly flags: "--untriaged";
|
|
4647
4688
|
readonly description: "Items nobody has triaged yet (the default)";
|
|
4648
4689
|
readonly hidden: false;
|
|
4690
|
+
}, {
|
|
4691
|
+
readonly flags: "--open";
|
|
4692
|
+
readonly description: "Every item not resolved, won't-fix or accepted, triaged or not";
|
|
4693
|
+
readonly hidden: false;
|
|
4649
4694
|
}, {
|
|
4650
4695
|
readonly flags: "--mine";
|
|
4651
4696
|
readonly description: "Items assigned to you";
|
|
@@ -4656,7 +4701,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
4656
4701
|
readonly hidden: false;
|
|
4657
4702
|
}, {
|
|
4658
4703
|
readonly flags: "--status <status>";
|
|
4659
|
-
readonly description: "Only items with this status";
|
|
4704
|
+
readonly description: "Only items with this status (new, triaging, accepted, wont_fix, in_progress, resolved, waiting_on_customer, reopened, stalled, needs_info). Without --untriaged, --mine or --assignee it lists every item in that status";
|
|
4660
4705
|
readonly hidden: false;
|
|
4661
4706
|
}, {
|
|
4662
4707
|
readonly flags: "--limit <n>";
|
|
@@ -4668,7 +4713,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
4668
4713
|
readonly hidden: false;
|
|
4669
4714
|
}, {
|
|
4670
4715
|
readonly flags: "--json";
|
|
4671
|
-
readonly description: "Print { items, count } as JSON";
|
|
4716
|
+
readonly description: "Print { items, count } as JSON, plus truncated: true when more items match";
|
|
4672
4717
|
readonly hidden: false;
|
|
4673
4718
|
}];
|
|
4674
4719
|
readonly subcommands: readonly [];
|
|
@@ -1399,6 +1399,11 @@ export const COMMAND_CATALOG = [
|
|
|
1399
1399
|
"description": "Build and print the chunks as JSON; upload nothing",
|
|
1400
1400
|
"hidden": false
|
|
1401
1401
|
},
|
|
1402
|
+
{
|
|
1403
|
+
"flags": "--json",
|
|
1404
|
+
"description": "Print the pass outcome as one JSON line instead of a summary",
|
|
1405
|
+
"hidden": false
|
|
1406
|
+
},
|
|
1402
1407
|
{
|
|
1403
1408
|
"flags": "--respect-throttle",
|
|
1404
1409
|
"description": "Honour the 24h per-scope upload throttle instead of forcing a pass (for scheduled callers)",
|
|
@@ -3693,6 +3698,11 @@ export const COMMAND_CATALOG = [
|
|
|
3693
3698
|
"description": "Permission level (only with --with): read | write",
|
|
3694
3699
|
"hidden": false
|
|
3695
3700
|
},
|
|
3701
|
+
{
|
|
3702
|
+
"flags": "--exact",
|
|
3703
|
+
"description": "With --with: grant exactly the object named by <prefix>, even when it looks like a folder (by default a bare folder name such as 'knowledge' is granted as the folder glob 'knowledge/*').",
|
|
3704
|
+
"hidden": false
|
|
3705
|
+
},
|
|
3696
3706
|
{
|
|
3697
3707
|
"flags": "--full",
|
|
3698
3708
|
"description": "Grant access to the ENTIRE vault (the '*' wildcard prefix) — no need to quote a glob. Requires --with; defaults to write permission.",
|
|
@@ -3724,7 +3734,7 @@ export const COMMAND_CATALOG = [
|
|
|
3724
3734
|
},
|
|
3725
3735
|
{
|
|
3726
3736
|
"name": "unshare",
|
|
3727
|
-
"description": "Remove a named file access grant, or remove an empty ACL row or grantee-less entries without --with.",
|
|
3737
|
+
"description": "Remove a named file access grant, or remove an empty ACL row or grantee-less entries without --with. With --with, a bare name such as 'knowledge' first removes the exact 'knowledge' grant (rows from older `hq files share` versions) and, when none matches, the folder-glob 'knowledge/*' grant. Without --with, the prefix is used exactly as typed: pass 'knowledge/*' (quoted) to clean up the folder-glob row.",
|
|
3728
3738
|
"aliases": [],
|
|
3729
3739
|
"hidden": false,
|
|
3730
3740
|
"helpOption": true,
|
|
@@ -3744,6 +3754,11 @@ export const COMMAND_CATALOG = [
|
|
|
3744
3754
|
"flags": "--with <principal>",
|
|
3745
3755
|
"description": "Person uid, agent uid, company Outpost id (out_...), email address, group id, or '@all' to remove the grant",
|
|
3746
3756
|
"hidden": false
|
|
3757
|
+
},
|
|
3758
|
+
{
|
|
3759
|
+
"flags": "--exact",
|
|
3760
|
+
"description": "With --with: remove the grant on exactly the object named by <prefix>. By default a bare folder name such as 'knowledge' resolves to the folder glob 'knowledge/*', the same way `hq files share` resolves it.",
|
|
3761
|
+
"hidden": false
|
|
3747
3762
|
}
|
|
3748
3763
|
],
|
|
3749
3764
|
"subcommands": []
|
|
@@ -5529,13 +5544,23 @@ export const COMMAND_CATALOG = [
|
|
|
5529
5544
|
"options": [
|
|
5530
5545
|
{
|
|
5531
5546
|
"flags": "--state <state>",
|
|
5532
|
-
"description": "One of: triaged, in-progress, shipped, verified, resolved",
|
|
5547
|
+
"description": "One of: triaged, in-progress, stalled, shipped, verified, resolved",
|
|
5533
5548
|
"hidden": false
|
|
5534
5549
|
},
|
|
5535
5550
|
{
|
|
5536
5551
|
"flags": "--note <text>",
|
|
5537
5552
|
"description": "What the customer should read (plain text, up to 2000 characters)",
|
|
5538
5553
|
"hidden": false
|
|
5554
|
+
},
|
|
5555
|
+
{
|
|
5556
|
+
"flags": "--pr <url>",
|
|
5557
|
+
"description": "Open pull request for this ticket (required with --state in-progress unless --lane is given)",
|
|
5558
|
+
"hidden": false
|
|
5559
|
+
},
|
|
5560
|
+
{
|
|
5561
|
+
"flags": "--lane <id>",
|
|
5562
|
+
"description": "Live lane working this ticket (required with --state in-progress unless --pr is given)",
|
|
5563
|
+
"hidden": false
|
|
5539
5564
|
}
|
|
5540
5565
|
],
|
|
5541
5566
|
"subcommands": []
|
|
@@ -5613,6 +5638,32 @@ export const COMMAND_CATALOG = [
|
|
|
5613
5638
|
],
|
|
5614
5639
|
"subcommands": []
|
|
5615
5640
|
},
|
|
5641
|
+
{
|
|
5642
|
+
"name": "claim",
|
|
5643
|
+
"description": "Assign a ticket to yourself (sets the assignee only, never the status)",
|
|
5644
|
+
"aliases": [],
|
|
5645
|
+
"hidden": false,
|
|
5646
|
+
"helpOption": true,
|
|
5647
|
+
"helpCommand": false,
|
|
5648
|
+
"usage": "[options] <id>",
|
|
5649
|
+
"helpText": [],
|
|
5650
|
+
"arguments": [
|
|
5651
|
+
{
|
|
5652
|
+
"name": "id",
|
|
5653
|
+
"required": true,
|
|
5654
|
+
"variadic": false,
|
|
5655
|
+
"description": "Feedback id (feedback_<uuid>)"
|
|
5656
|
+
}
|
|
5657
|
+
],
|
|
5658
|
+
"options": [
|
|
5659
|
+
{
|
|
5660
|
+
"flags": "--json",
|
|
5661
|
+
"description": "Print { id, assignee } as JSON",
|
|
5662
|
+
"hidden": false
|
|
5663
|
+
}
|
|
5664
|
+
],
|
|
5665
|
+
"subcommands": []
|
|
5666
|
+
},
|
|
5616
5667
|
{
|
|
5617
5668
|
"name": "list",
|
|
5618
5669
|
"description": "List feedback: untriaged (default), yours, or one person's",
|
|
@@ -5629,6 +5680,11 @@ export const COMMAND_CATALOG = [
|
|
|
5629
5680
|
"description": "Items nobody has triaged yet (the default)",
|
|
5630
5681
|
"hidden": false
|
|
5631
5682
|
},
|
|
5683
|
+
{
|
|
5684
|
+
"flags": "--open",
|
|
5685
|
+
"description": "Every item not resolved, won't-fix or accepted, triaged or not",
|
|
5686
|
+
"hidden": false
|
|
5687
|
+
},
|
|
5632
5688
|
{
|
|
5633
5689
|
"flags": "--mine",
|
|
5634
5690
|
"description": "Items assigned to you",
|
|
@@ -5641,7 +5697,7 @@ export const COMMAND_CATALOG = [
|
|
|
5641
5697
|
},
|
|
5642
5698
|
{
|
|
5643
5699
|
"flags": "--status <status>",
|
|
5644
|
-
"description": "Only items with this status",
|
|
5700
|
+
"description": "Only items with this status (new, triaging, accepted, wont_fix, in_progress, resolved, waiting_on_customer, reopened, stalled, needs_info). Without --untriaged, --mine or --assignee it lists every item in that status",
|
|
5645
5701
|
"hidden": false
|
|
5646
5702
|
},
|
|
5647
5703
|
{
|
|
@@ -5656,7 +5712,7 @@ export const COMMAND_CATALOG = [
|
|
|
5656
5712
|
},
|
|
5657
5713
|
{
|
|
5658
5714
|
"flags": "--json",
|
|
5659
|
-
"description": "Print { items, count } as JSON",
|
|
5715
|
+
"description": "Print { items, count } as JSON, plus truncated: true when more items match",
|
|
5660
5716
|
"hidden": false
|
|
5661
5717
|
}
|
|
5662
5718
|
],
|
package/dist/commands/access.js
CHANGED
|
@@ -611,7 +611,12 @@ async function completeTarget(input, deps, located) {
|
|
|
611
611
|
}
|
|
612
612
|
else {
|
|
613
613
|
grantor = resolved;
|
|
614
|
-
const
|
|
614
|
+
const contactRole = resolved.source === "acl-creator"
|
|
615
|
+
? "ACL creator"
|
|
616
|
+
: resolved.source === "owner"
|
|
617
|
+
? "company owner fallback"
|
|
618
|
+
: "company admin fallback";
|
|
619
|
+
const grantorLine = `Access contact: ${grantorLabel(resolved)} (${contactRole}; file authorship not verified)`;
|
|
615
620
|
// The recipient is always shown BEFORE any send. Under --json stdout is
|
|
616
621
|
// reserved for the single JSON object, so the line goes to stderr.
|
|
617
622
|
if (json)
|
|
@@ -659,7 +664,7 @@ async function completeTarget(input, deps, located) {
|
|
|
659
664
|
}
|
|
660
665
|
if (approved && outcome === "no-access") {
|
|
661
666
|
const message = `${requester.email} is asking for read access to ${resolvedKey} in ${slug}.`;
|
|
662
|
-
const prompt = `hq files share ${resolvedKey} --with ${requester.email} --permission read --company ${slug}`;
|
|
667
|
+
const prompt = `hq files share ${resolvedKey} --exact --with ${requester.email} --permission read --company ${slug}`;
|
|
663
668
|
const sent = await deps.dm.send({
|
|
664
669
|
recipient: resolved.email,
|
|
665
670
|
message,
|
package/dist/commands/bot.js
CHANGED
|
@@ -26,6 +26,7 @@ import { hasPromotionHold, promotionHoldSummary } from "../lib/bot/promotion-hol
|
|
|
26
26
|
* list (online flag), and rm. Secrets are never printed.
|
|
27
27
|
*/
|
|
28
28
|
import { promoteLocalBot } from "../lib/bot/promote.js";
|
|
29
|
+
import { DecisionsClient } from "../lib/decisions/client.js";
|
|
29
30
|
import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload } from "../utils/package-use-lease.js";
|
|
30
31
|
import { preloadResidentPackageModules, reportResidentPackagePreloadFailure, runResidentCommandWithPackageLease, } from "../utils/resident-package-preload.js";
|
|
31
32
|
import { followLogFile } from "../lib/log-follow.js";
|
|
@@ -1063,6 +1064,9 @@ async function runBotResident(nameArg) {
|
|
|
1063
1064
|
api,
|
|
1064
1065
|
runtime: runtimeFor(config.runtime),
|
|
1065
1066
|
ownerContext: () => owner.get(),
|
|
1067
|
+
// US-005: decision picks in the owner DM, with the bot's own machine token
|
|
1068
|
+
// (never the owner's login).
|
|
1069
|
+
decisions: new DecisionsClient(token),
|
|
1066
1070
|
// Chat attachments are fetched with the bot's own machine identity against
|
|
1067
1071
|
// the same API its inbox and posts use (BotApi has no baseUrl override either).
|
|
1068
1072
|
downloadAttachment: createVaultObjectDownloader({ getToken: token }),
|
package/dist/commands/cloud.d.ts
CHANGED
|
@@ -67,6 +67,26 @@ export declare function withSyncHealthReport<T>(run: () => Promise<T>): Promise<
|
|
|
67
67
|
*/
|
|
68
68
|
export declare function scopeExcludedExitCode(count: number): 0 | 1;
|
|
69
69
|
export declare function scopeExcludedWarning(count: number): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Counts the files share() held back because this sync scope is fenced after an account
|
|
72
|
+
* switch on this computer (feedback_a69e12ba). share() reports the hold only as
|
|
73
|
+
* an `identity-switch-push-held` event and leaves the files out of every
|
|
74
|
+
* result counter, so without this the push printed "Pushed 0 file(s) (0 B,
|
|
75
|
+
* 0 skipped)" as a success while the vault kept the old copy.
|
|
76
|
+
*
|
|
77
|
+
* Each event carries the size of one fenced hold, so the tracker keeps the
|
|
78
|
+
* largest count it sees instead of a sum: a repeated emit for the same hold
|
|
79
|
+
* cannot inflate the total. A missing, non-numeric or negative count reads as 0.
|
|
80
|
+
*/
|
|
81
|
+
export declare function createIdentityHeldTracker(): {
|
|
82
|
+
observe: (event: {
|
|
83
|
+
type: string;
|
|
84
|
+
count?: unknown;
|
|
85
|
+
}) => void;
|
|
86
|
+
count: () => number;
|
|
87
|
+
};
|
|
88
|
+
/** Recovery lines for files held by the account-switch push fence. */
|
|
89
|
+
export declare function identityHeldLines(count: number): string[];
|
|
70
90
|
/** Actionable recovery for files refused because the vault baseline is stale. */
|
|
71
91
|
export declare function staleUploadRefusalLines(count: number, paths?: readonly string[]): string[];
|
|
72
92
|
/**
|
package/dist/commands/cloud.js
CHANGED
|
@@ -153,6 +153,41 @@ export function scopeExcludedWarning(count) {
|
|
|
153
153
|
`They were skipped, not synced. Re-run with --json to list them, or ` +
|
|
154
154
|
`ask an admin to grant you write on those paths.`);
|
|
155
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Counts the files share() held back because this sync scope is fenced after an account
|
|
158
|
+
* switch on this computer (feedback_a69e12ba). share() reports the hold only as
|
|
159
|
+
* an `identity-switch-push-held` event and leaves the files out of every
|
|
160
|
+
* result counter, so without this the push printed "Pushed 0 file(s) (0 B,
|
|
161
|
+
* 0 skipped)" as a success while the vault kept the old copy.
|
|
162
|
+
*
|
|
163
|
+
* Each event carries the size of one fenced hold, so the tracker keeps the
|
|
164
|
+
* largest count it sees instead of a sum: a repeated emit for the same hold
|
|
165
|
+
* cannot inflate the total. A missing, non-numeric or negative count reads as 0.
|
|
166
|
+
*/
|
|
167
|
+
export function createIdentityHeldTracker() {
|
|
168
|
+
let held = 0;
|
|
169
|
+
return {
|
|
170
|
+
observe(event) {
|
|
171
|
+
if (event.type !== "identity-switch-push-held")
|
|
172
|
+
return;
|
|
173
|
+
const n = typeof event.count === "number" && Number.isFinite(event.count)
|
|
174
|
+
? Math.max(0, Math.trunc(event.count))
|
|
175
|
+
: 0;
|
|
176
|
+
held = Math.max(held, n);
|
|
177
|
+
},
|
|
178
|
+
count: () => held,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Recovery lines for files held by the account-switch push fence. */
|
|
182
|
+
export function identityHeldLines(count) {
|
|
183
|
+
if (count <= 0)
|
|
184
|
+
return [];
|
|
185
|
+
return [
|
|
186
|
+
`⚠ ${count} file(s) were NOT uploaded: uploads for this company are held on this computer ` +
|
|
187
|
+
`after an account sign-in change. Run \`hq sync held\` to see what is held, then ` +
|
|
188
|
+
`\`hq sync release-held\` to upload it.`,
|
|
189
|
+
];
|
|
190
|
+
}
|
|
156
191
|
/** Actionable recovery for files refused because the vault baseline is stale. */
|
|
157
192
|
export function staleUploadRefusalLines(count, paths = []) {
|
|
158
193
|
if (count <= 0)
|
|
@@ -1028,6 +1063,7 @@ export function registerCloudCommands(program) {
|
|
|
1028
1063
|
companyRef: targetCompany ?? readActiveCompany(options.hqRoot),
|
|
1029
1064
|
});
|
|
1030
1065
|
const planLimitTracker = createPlanLimitRefusalTracker(companySlugForBilling(progressCompany));
|
|
1066
|
+
const identityHeld = createIdentityHeldTracker();
|
|
1031
1067
|
syncHealth = beginSyncHealthReport();
|
|
1032
1068
|
if (!personalMode) {
|
|
1033
1069
|
const companyUid = entityContext?.uid ??
|
|
@@ -1055,7 +1091,10 @@ export function registerCloudCommands(program) {
|
|
|
1055
1091
|
}),
|
|
1056
1092
|
hqRoot: options.hqRoot,
|
|
1057
1093
|
onEvent,
|
|
1058
|
-
progressRecorder:
|
|
1094
|
+
progressRecorder: ((record) => (event) => {
|
|
1095
|
+
identityHeld.observe(event);
|
|
1096
|
+
record(event);
|
|
1097
|
+
})(trackingProgressRecorder(planLimitTracker, progressCompany)),
|
|
1059
1098
|
...(personalMode ? { personalMode: true } : {}),
|
|
1060
1099
|
...(journalSlug !== undefined ? { journalSlug } : {}),
|
|
1061
1100
|
...(author ? { author } : {}),
|
|
@@ -1063,6 +1102,7 @@ export function registerCloudCommands(program) {
|
|
|
1063
1102
|
...namedPathShareOptions(userNamedPaths),
|
|
1064
1103
|
});
|
|
1065
1104
|
const planLimitRefused = planLimitTracker.summary();
|
|
1105
|
+
const identityHeldCount = identityHeld.count();
|
|
1066
1106
|
const staleRefusalCount = result.filesRefusedStale ?? 0;
|
|
1067
1107
|
const uploadFailed = readUploadFailures(result);
|
|
1068
1108
|
const refusalRecovery = [
|
|
@@ -1075,6 +1115,9 @@ export function registerCloudCommands(program) {
|
|
|
1075
1115
|
uploadFailed.count > 0
|
|
1076
1116
|
? "Push again; the files that failed are still on this machine."
|
|
1077
1117
|
: null,
|
|
1118
|
+
identityHeldCount > 0
|
|
1119
|
+
? "Run `hq sync held`, then `hq sync release-held` to upload the held files."
|
|
1120
|
+
: null,
|
|
1078
1121
|
]
|
|
1079
1122
|
.filter((message) => message !== null)
|
|
1080
1123
|
.join(" ");
|
|
@@ -1095,6 +1138,7 @@ export function registerCloudCommands(program) {
|
|
|
1095
1138
|
filesExcludedByScope: result.filesExcludedByScope,
|
|
1096
1139
|
filesRefusedStale: staleRefusalCount,
|
|
1097
1140
|
filesRefusedStalePaths: result.filesRefusedStalePaths ?? [],
|
|
1141
|
+
filesHeldByIdentityFence: identityHeldCount,
|
|
1098
1142
|
filesUploadFailed: uploadFailed.count,
|
|
1099
1143
|
uploadFailedPaths: uploadFailed.paths,
|
|
1100
1144
|
...(refusalRecovery ? { refusalRecovery } : {}),
|
|
@@ -1115,8 +1159,8 @@ export function registerCloudCommands(program) {
|
|
|
1115
1159
|
// Never a success line over a refusal: say what was pushed, what
|
|
1116
1160
|
// was refused and how to fix it, then exit non-zero. The sync
|
|
1117
1161
|
// itself worked, so client health records a success. `--json`
|
|
1118
|
-
// callers read `planLimitRefused` on the complete event
|
|
1119
|
-
//
|
|
1162
|
+
// callers also read `planLimitRefused` on the complete event; the
|
|
1163
|
+
// exit status is non-zero in both modes.
|
|
1120
1164
|
log(chalk.yellow(`\n⚠ Pushed ${result.filesUploaded} file(s) (${formatBytes(result.bytesUploaded)}, ` +
|
|
1121
1165
|
`${result.filesSkipped} skipped), but ${describeRefusedFiles(planLimitRefused)} ` +
|
|
1122
1166
|
"refused: new files are paused for this account."));
|
|
@@ -1132,11 +1176,15 @@ export function registerCloudCommands(program) {
|
|
|
1132
1176
|
for (const line of uploadFailedLines(uploadFailed.count, uploadFailed.paths)) {
|
|
1133
1177
|
log(chalk.yellow(line));
|
|
1134
1178
|
}
|
|
1179
|
+
for (const line of identityHeldLines(identityHeldCount)) {
|
|
1180
|
+
log(chalk.yellow(line));
|
|
1181
|
+
}
|
|
1135
1182
|
await syncHealth.succeeded();
|
|
1136
1183
|
process.exit(1);
|
|
1137
1184
|
return;
|
|
1138
1185
|
}
|
|
1139
|
-
const notUploadedCount = result.filesExcludedByScope + staleRefusalCount + uploadFailed.count
|
|
1186
|
+
const notUploadedCount = result.filesExcludedByScope + staleRefusalCount + uploadFailed.count +
|
|
1187
|
+
identityHeldCount;
|
|
1140
1188
|
if (notUploadedCount > 0) {
|
|
1141
1189
|
log(chalk.yellow(`\n⚠ Pushed ${result.filesUploaded} file(s) (${formatBytes(result.bytesUploaded)}, ` +
|
|
1142
1190
|
`${result.filesSkipped} skipped, ${notUploadedCount} files not uploaded)`));
|
|
@@ -1149,6 +1197,9 @@ export function registerCloudCommands(program) {
|
|
|
1149
1197
|
for (const line of uploadFailedLines(uploadFailed.count, uploadFailed.paths)) {
|
|
1150
1198
|
log(chalk.yellow(line));
|
|
1151
1199
|
}
|
|
1200
|
+
for (const line of identityHeldLines(identityHeldCount)) {
|
|
1201
|
+
log(chalk.yellow(line));
|
|
1202
|
+
}
|
|
1152
1203
|
}
|
|
1153
1204
|
else {
|
|
1154
1205
|
const skipSummary = formatPushSkipSummary(result);
|
|
@@ -9,6 +9,18 @@ import { Command } from "commander";
|
|
|
9
9
|
*/
|
|
10
10
|
export declare const TRIAGE_KINDS: readonly ["bug", "feature", "question"];
|
|
11
11
|
export type TriageKind = (typeof TRIAGE_KINDS)[number];
|
|
12
|
+
/**
|
|
13
|
+
* Internal feedback row statuses `hq feedback list --status` accepts.
|
|
14
|
+
* `stalled` is open work that started and then stopped: a row is in_progress
|
|
15
|
+
* only while it has a linked open PR or a live lane, and moves to stalled after
|
|
16
|
+
* 48h with no activity. It is listed apart from `new` (not started).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Statuses shown in `list --status` help. Help text only: the CLI does not
|
|
20
|
+
* validate against this list, because hq-pro owns the set and returns a 400
|
|
21
|
+
* for an unknown value.
|
|
22
|
+
*/
|
|
23
|
+
export declare const FEEDBACK_LIST_STATUSES: readonly ["new", "triaging", "accepted", "wont_fix", "in_progress", "resolved", "waiting_on_customer", "reopened", "stalled", "needs_info"];
|
|
12
24
|
export declare const MAX_TRIAGE_SUMMARY = 2000;
|
|
13
25
|
export declare const DEFAULT_LIST_LIMIT = 50;
|
|
14
26
|
export interface FeedbackListRow {
|
|
@@ -24,6 +36,14 @@ export type FeedbackListSelector = {
|
|
|
24
36
|
} | {
|
|
25
37
|
kind: "assignee";
|
|
26
38
|
assignee: string;
|
|
39
|
+
}
|
|
40
|
+
/** Every visible row, untriaged or not. Used for `--status` with no other selector. */
|
|
41
|
+
| {
|
|
42
|
+
kind: "any";
|
|
43
|
+
}
|
|
44
|
+
/** Every row not in a terminal status, untriaged or not (`--open`). */
|
|
45
|
+
| {
|
|
46
|
+
kind: "open";
|
|
27
47
|
};
|
|
28
48
|
/** Email claim of the caller's staff token, used by `list --mine`. */
|
|
29
49
|
export declare function emailFromToken(token: string): string | null;
|
|
@@ -36,14 +56,36 @@ export declare function formatFeedbackAge(iso: string | undefined, now?: number)
|
|
|
36
56
|
* back as `?cursor=`.
|
|
37
57
|
*/
|
|
38
58
|
export declare function cursorFrom(data: unknown): string | null;
|
|
59
|
+
/**
|
|
60
|
+
* Lifecycle states hq-pro treats as closed (`TERMINAL_STATUSES` in
|
|
61
|
+
* feedback-status.ts). `--open` lists every row outside this set; a row with
|
|
62
|
+
* no status counts as open.
|
|
63
|
+
*/
|
|
64
|
+
export declare const TERMINAL_FEEDBACK_STATUSES: readonly string[];
|
|
65
|
+
/** Why a list stopped before the server ran out of rows, if it did. */
|
|
66
|
+
export type FeedbackListTruncation = "limit" | "page-cap" | "server";
|
|
67
|
+
export interface FeedbackListResult {
|
|
68
|
+
items: FeedbackListRow[];
|
|
69
|
+
/** Set when matching rows may exist beyond `items`. */
|
|
70
|
+
truncated?: FeedbackListTruncation;
|
|
71
|
+
}
|
|
39
72
|
/** Page through `GET /v1/feedback` until `limit` rows (or every row with `limit: null`). */
|
|
40
73
|
export declare function listFeedback(opts: {
|
|
41
74
|
token: string;
|
|
42
75
|
selector: FeedbackListSelector;
|
|
43
76
|
status?: string;
|
|
44
77
|
limit: number | null;
|
|
45
|
-
}): Promise<
|
|
78
|
+
}): Promise<FeedbackListResult>;
|
|
79
|
+
/** One-line stderr notice for a list that stopped before the end. */
|
|
80
|
+
export declare function feedbackTruncationNotice(reason: FeedbackListTruncation, shown: number): string;
|
|
46
81
|
export declare function formatFeedbackTable(rows: FeedbackListRow[], now?: number): string;
|
|
82
|
+
/** Status cell for the list table. A stalled row is flagged so it does not read as not started. */
|
|
83
|
+
export declare function formatFeedbackStatus(status: unknown): string;
|
|
84
|
+
/**
|
|
85
|
+
* Normalize `list --status`; accepts `in-progress` as an alias of `in_progress`.
|
|
86
|
+
* Any non-empty value goes to the server, which validates it.
|
|
87
|
+
*/
|
|
88
|
+
export declare function parseListStatus(raw: string): string;
|
|
47
89
|
export declare function parseListLimit(raw: string): number;
|
|
48
90
|
export declare function buildTriagePatch(opts: {
|
|
49
91
|
id: string;
|