@indigoai-us/hq-cli 5.52.0 → 5.53.0

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.
@@ -468,21 +468,39 @@ export function registerFilesCommand(program: Command): Command {
468
468
  files
469
469
  .command("delete <prefix>")
470
470
  .description(
471
- "Delete vault objects under a prefix (bounded + scoped). Always previews the exact count first; prompts for confirmation unless --yes.",
471
+ "Delete vault objects under a prefix (bounded + scoped). Always previews the exact count first; prompts for confirmation unless --yes. Use --personal to target your own personal vault instead of a company.",
472
472
  )
473
473
  .option(
474
474
  "--dry-run",
475
475
  "List what WOULD be deleted without deleting anything",
476
476
  )
477
+ .option(
478
+ "--personal",
479
+ "Target your own personal vault instead of a company vault (mutually exclusive with --company)",
480
+ )
477
481
  .option("-y, --yes", "Skip the confirmation prompt (for scripts)")
478
482
  .action(
479
- async (prefix: string, opts: { dryRun?: boolean; yes?: boolean }) => {
483
+ async (
484
+ prefix: string,
485
+ opts: { dryRun?: boolean; yes?: boolean; personal?: boolean },
486
+ ) => {
480
487
  try {
488
+ const companySlug = files.opts().company as string | undefined;
489
+ const personal = opts.personal === true;
490
+ if (personal && companySlug) {
491
+ console.error(
492
+ chalk.red(
493
+ "Pass either --personal or --company, not both.",
494
+ ),
495
+ );
496
+ process.exit(1);
497
+ }
481
498
  await runFilesDelete({
482
499
  prefix,
483
500
  dryRun: opts.dryRun === true,
484
501
  yes: opts.yes === true,
485
- companySlug: files.opts().company as string | undefined,
502
+ personal,
503
+ companySlug,
486
504
  });
487
505
  } catch (err) {
488
506
  console.error(
@@ -783,10 +801,16 @@ function realConfirm(message: string): Promise<boolean> {
783
801
  /**
784
802
  * POST /v1/files/delete. Throws FilesDeleteHttpError on any non-2xx so the one
785
803
  * caller renders a single consistent error path.
804
+ *
805
+ * Scope is EITHER a company vault (`companyUid` set) OR the caller's personal
806
+ * vault (`personal: true`). For the personal case the server resolves the target
807
+ * person + bucket from the authenticated caller — we send NO uid, just the
808
+ * `personal` flag — so there is nothing for the client to get wrong or spoof.
786
809
  */
787
810
  async function callDeleteEndpoint(params: {
788
811
  token: string;
789
- companyUid: string;
812
+ companyUid?: string;
813
+ personal?: boolean;
790
814
  prefix: string;
791
815
  dryRun: boolean;
792
816
  }): Promise<FilesDeleteResponse> {
@@ -794,11 +818,17 @@ async function callDeleteEndpoint(params: {
794
818
  token: params.token,
795
819
  path: "/v1/files/delete",
796
820
  method: "POST",
797
- body: {
798
- company: params.companyUid,
799
- prefix: params.prefix,
800
- dryRun: params.dryRun,
801
- },
821
+ body: params.personal
822
+ ? {
823
+ personal: true,
824
+ prefix: params.prefix,
825
+ dryRun: params.dryRun,
826
+ }
827
+ : {
828
+ company: params.companyUid,
829
+ prefix: params.prefix,
830
+ dryRun: params.dryRun,
831
+ },
802
832
  });
803
833
  if (!res.ok) {
804
834
  const body = (await res.json().catch(() => ({}))) as Record<string, string>;
@@ -815,6 +845,8 @@ interface RunFilesDeleteParams {
815
845
  prefix: string;
816
846
  dryRun: boolean;
817
847
  yes: boolean;
848
+ /** Target the caller's personal vault instead of a company vault. */
849
+ personal?: boolean;
818
850
  companySlug: string | undefined;
819
851
  }
820
852
 
@@ -896,7 +928,14 @@ export async function runFilesDelete(
896
928
  }
897
929
 
898
930
  const token = await ensureCognitoToken();
899
- const companyUid = await getCompanyUid(token, params.companySlug);
931
+ // Personal scope resolves the vault server-side from the caller's identity —
932
+ // no company to look up. Company scope resolves the companyUid as before.
933
+ const companyUid = params.personal
934
+ ? undefined
935
+ : await getCompanyUid(token, params.companySlug);
936
+ const scopeArgs = params.personal
937
+ ? { personal: true as const }
938
+ : { companyUid };
900
939
 
901
940
  // 1. Always preview first — this is how we print the EXACT key count before
902
941
  // deleting anything (and the whole behavior of --dry-run).
@@ -904,7 +943,7 @@ export async function runFilesDelete(
904
943
  try {
905
944
  preview = await callDeleteEndpoint({
906
945
  token,
907
- companyUid,
946
+ ...scopeArgs,
908
947
  prefix: normalized,
909
948
  dryRun: true,
910
949
  });
@@ -956,7 +995,9 @@ export async function runFilesDelete(
956
995
 
957
996
  if (!params.yes) {
958
997
  const ok = await confirm(
959
- `Delete ${preview.matched} ${noun}? This removes them from the shared vault for everyone.`,
998
+ params.personal
999
+ ? `Delete ${preview.matched} ${noun}? This removes them from your personal vault.`
1000
+ : `Delete ${preview.matched} ${noun}? This removes them from the shared vault for everyone.`,
960
1001
  );
961
1002
  if (!ok) {
962
1003
  console.log(chalk.dim("Aborted — nothing was deleted."));
@@ -969,7 +1010,7 @@ export async function runFilesDelete(
969
1010
  try {
970
1011
  result = await callDeleteEndpoint({
971
1012
  token,
972
- companyUid,
1013
+ ...scopeArgs,
973
1014
  prefix: normalized,
974
1015
  dryRun: false,
975
1016
  });
package/src/index.ts CHANGED
@@ -48,6 +48,8 @@ import { registerSignalsCommand } from "./commands/signals.js";
48
48
  import { registerReindexCommand } from "./commands/reindex.js";
49
49
  import { registerRescueCommand } from "./commands/rescue.js";
50
50
  import { registerMcpCommand } from "./commands/mcp-status.js";
51
+ import { registerCrmCommand } from "./commands/crm.js";
52
+ import { registerCompanyCommand } from "./commands/company.js";
51
53
  import { sanitizeArgv } from "./utils/feedback-diagnostics.js";
52
54
  import { environmentalFsErrorMessage } from "./utils/environmental-error.js";
53
55
  import { isEpipe } from "./utils/epipe.js";
@@ -214,6 +216,15 @@ registerRescueCommand(program);
214
216
  // off the configs, NOT linkStatus), with secret-redacted output + `--json`.
215
217
  registerMcpCommand(program);
216
218
 
219
+ // Native CRM entity upsert (subcommand group — `hq crm entity upsert`). Wraps
220
+ // POST /crm/entities (the ontology write gate) so an authenticated company
221
+ // member can create/update canonical CRM entities in the company vault.
222
+ registerCrmCommand(program);
223
+
224
+ // Company settings (subcommand group — `hq company settings set`). Owner-only
225
+ // toggles for crmEnabled / ontologyEnabled via PUT /company-settings.
226
+ registerCompanyCommand(program);
227
+
217
228
  (async () => {
218
229
  try {
219
230
  Sentry.addBreadcrumb({