@dench.com/cli 3.8.1-staging.390 → 3.8.2-staging.392

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/ads.ts CHANGED
@@ -2000,7 +2000,11 @@ async function runWithdrawCommand(opts: {
2000
2000
  // ---------------------------------------------------------------------------
2001
2001
 
2002
2002
  function adsHelp(): void {
2003
- console.log(`dench ads — monetize the AI "thinking…" line. (aliases: dench ad, dench adline)
2003
+ console.log(adsHelpText());
2004
+ }
2005
+
2006
+ export function adsHelpText(): string {
2007
+ return `\`dench ads\` — monetize the AI "thinking…" line. (aliases: dench ad, dench adline)
2004
2008
 
2005
2009
  Usage:
2006
2010
  dench ads Print the sponsored ad in a decorated rainbow box
@@ -2035,7 +2039,7 @@ Notes:
2035
2039
  • Set DENCH_ADS_ENDPOINT=https://your-server/ads to serve real ads:
2036
2040
  GET returns {id,label,url,icon,iconUrl}; POST {endpoint}/impressions
2037
2041
  takes {events:[{adId,count,flushId}]}.
2038
- `);
2042
+ `;
2039
2043
  }
2040
2044
 
2041
2045
  // ---------------------------------------------------------------------------
package/apps.ts CHANGED
@@ -91,8 +91,12 @@ function withAuth(ctx: Ctx, args: Record<string, unknown>) {
91
91
  } as never;
92
92
  }
93
93
 
94
- function appsHelp() {
95
- console.log(`Usage: dench apps <subcommand> (App Store — not connected apps; see 'dench integrations')
94
+ function appsHelp(): void {
95
+ console.log(appsHelpText());
96
+ }
97
+
98
+ export function appsHelpText(): string {
99
+ return `Usage: dench apps <subcommand> (App Store — not connected apps; see 'dench integrations')
96
100
 
97
101
  dench apps list [--json] Installed apps in this workspace
98
102
  dench apps gallery [--json] Discover listings — Official (Dench) + Community
@@ -113,7 +117,7 @@ function appsHelp() {
113
117
  dench apps webhooks list <slug>
114
118
  dench apps webhooks poll <slug> <hookName> [--since <ms>] [--limit <n>]
115
119
  dench apps webhooks delete <slug> <hookName>
116
- `);
120
+ `;
117
121
  }
118
122
 
119
123
  const TEXT_FILE = /\.(html?|js|mjs|cjs|ts|jsx|tsx|css|json|ya?ml|md|txt|svg)$/i;
package/browser.ts CHANGED
@@ -81,7 +81,11 @@ const ACTIONS = new Set([
81
81
  ]);
82
82
 
83
83
  function browserHelp(): void {
84
- console.log(`Usage: dench browser <action> [options]
84
+ console.log(browserHelpText());
85
+ }
86
+
87
+ export function browserHelpText(): string {
88
+ return `Usage: dench browser <action> [options]
85
89
 
86
90
  Control Dench Browser without opening dench.com locally.
87
91
  Commands authenticate with the active dench signin session or DENCH_API_KEY.
@@ -105,6 +109,7 @@ Common:
105
109
  dench browser activate-tab <targetId>
106
110
  dench browser close-tab <targetId>
107
111
  dench browser stop
112
+ dench browser reset
108
113
 
109
114
  Playwright sessions:
110
115
  dench browser session create --json
@@ -114,8 +119,8 @@ Playwright sessions:
114
119
  State:
115
120
  The CLI remembers the latest browser thread per Dench host/session.
116
121
  Use --thread <threadId> to control a specific thread, or
117
- dench browser reset to forget the local remembered thread.
118
- `);
122
+ \`dench browser reset\` to forget the local remembered thread.
123
+ `;
119
124
  }
120
125
 
121
126
  function consumeFlagValue(args: string[], name: string): string | undefined {
package/campaign.ts CHANGED
@@ -4218,7 +4218,7 @@ export function campaignHelp(subcommand?: string): void {
4218
4218
  );
4219
4219
  }
4220
4220
 
4221
- function campaignHelpText(): string {
4221
+ export function campaignHelpText(): string {
4222
4222
  return `Usage: dench campaign <subcommand> [--json]
4223
4223
 
4224
4224
  Outreach campaigns send email and LinkedIn steps to a list of people on one
package/chat.ts CHANGED
@@ -112,7 +112,11 @@ function out(ctx: ChatCliContext, value: unknown): void {
112
112
  }
113
113
 
114
114
  function chatHelp(): void {
115
- console.log(`Usage: dench chat <subcommand>
115
+ console.log(chatHelpText());
116
+ }
117
+
118
+ export function chatHelpText(): string {
119
+ return `Usage: dench chat <subcommand>
116
120
 
117
121
  List past chat threads (own + shared in this workspace):
118
122
  dench chat list [--query <substr>] [--limit N] [--include-archived] [--include-automated] [--json]
@@ -165,7 +169,7 @@ Auth:
165
169
  Global flags:
166
170
  --json Output raw JSON instead of pretty-printed text.
167
171
  --follow After spawning, stream the chat response to stdout.
168
- `);
172
+ `;
169
173
  }
170
174
 
171
175
  export async function runChatCommand(opts: {
package/cron.ts CHANGED
@@ -871,7 +871,11 @@ async function runHistory(ctx: CronCliContext): Promise<void> {
871
871
  // ─── Help ─────────────────────────────────────────────────────────────────
872
872
 
873
873
  function cronHelp(): void {
874
- console.log(`Usage: dench cron <subcommand>
874
+ console.log(cronHelpText());
875
+ }
876
+
877
+ export function cronHelpText(): string {
878
+ return `Usage: dench cron <subcommand>
875
879
  dench routine <subcommand> (alias)
876
880
  dench routines <subcommand> (alias)
877
881
 
@@ -902,7 +906,7 @@ Create / update:
902
906
  dench cron create "Inbound lead triage" --prompt "Triage the lead…" --type webhook [--signed]
903
907
 
904
908
  # App-event automation (fires when something happens in a connected app).
905
- # 1. dench apps --json → connectedAccountId + toolkit
909
+ # 1. dench integrations --json → connectedAccountId + toolkit
906
910
  # 2. dench cron events <toolkit> → event slug
907
911
  dench cron create "New GitHub star" --prompt "Thank the new stargazer" \\
908
912
  --type app --connection <connectedAccountId> --event <triggerSlug> \\
@@ -955,7 +959,7 @@ Examples:
955
959
  Auth: prefers DENCH_API_KEY (sandbox envVar). Falls back to a session token from
956
960
  \`dench login\` for local terminals. The Bearer token must carry either
957
961
  \`crons:read\` (queries) or \`crons:write\` (mutations) scope, or the wildcard \`*\`.
958
- `);
962
+ `;
959
963
  }
960
964
 
961
965
  // ─── Entry point ──────────────────────────────────────────────────────────
package/dench.ts CHANGED
@@ -18,7 +18,6 @@ import { makeFunctionReference } from "convex/server";
18
18
  import { agentKindLabel, normalizeAgentKind } from "./agentKind";
19
19
  import {
20
20
  apiHostForDenchHost,
21
- DEFAULT_HOST,
22
21
  isLocalHost,
23
22
  LOCAL_HOST,
24
23
  normalizeHost,
@@ -281,6 +280,11 @@ const BOOLEAN_FLAGS = new Set([
281
280
  ]);
282
281
 
283
282
  function positionalArgs() {
283
+ const fileArgs = stripRuntimeFlags(filteredArgs);
284
+ if (fileArgs[0] === "files") {
285
+ const switches = new Set(["--recursive", "-r", "--url"]);
286
+ return fileArgs.filter((value, index) => index < 2 || !switches.has(value));
287
+ }
284
288
  const positionals: string[] = [];
285
289
  for (let i = 0; i < filteredArgs.length; i++) {
286
290
  if (filteredArgs[i].startsWith("--")) {
@@ -467,8 +471,8 @@ function logHuman(message: string) {
467
471
  }
468
472
  }
469
473
 
470
- function help() {
471
- console.log(`Dench CLI
474
+ function rootUsageText() {
475
+ return `Dench CLI
472
476
 
473
477
  Usage:
474
478
  dench signin [--kind <kind>] [--name "AI Agent - Project"] [--no-open] [--json]
@@ -479,7 +483,9 @@ Usage:
479
483
  dench sessions [--host <host>] [--json]
480
484
  dench use <session-key-or-workspace-slug> [--host <host>] [--json]
481
485
  dench logout [session-key-or-workspace-slug] [--session <key-or-scope>] [--host <host>] [--all] [--json]
482
- dench backend [show|set|clear] [<production|staging|local|<url>>] [--json]
486
+ dench backend show [--json]
487
+ dench backend set <production|staging|local|url> [--json]
488
+ dench backend clear [--json]
483
489
  dench context [--instructions] [--json]
484
490
  dench context --sections organisation,user,memory [--json]
485
491
  dench instructions [--json]
@@ -686,62 +692,17 @@ Standing context and memory:
686
692
  dench context --sections identity,organisation,user,memory,rules,tools [--json]
687
693
  dench context set <identity|organisation|user|tools|rules> --stdin
688
694
  dench memory search "<query>" | save <key> "<fact>"
689
- dench model default <id|clear> | thread <threadId> <id|clear>
695
+ dench model default <id|clear>
696
+ dench model thread <threadId> <id|clear>
690
697
  Context describes preferences and instructions. Environments store setup;
691
698
  secrets store credentials. A memory note does not install or save either.
692
699
 
693
- External tools:
694
- dench integrations is an alias for dench tool status -- it lists connected apps.
695
- dench apps is the App Store (create / install / publish / list), not connected apps.
696
- dench tool with no subcommand prints tool help.
697
- Read-only FETCH, GET, LIST, SEARCH, READ, and FIND tools do not need manual approval.
698
-
699
- Billing:
700
- dench billing status shows current AI credits.
701
- dench billing topup --amount 5 creates a Stripe Checkout link for the human to pay.
702
-
703
- Agent setup:
704
- dench signin is the only auth surface. Two transports (browser link
705
- by default, OTP paste-back with --email <addr>) cover both intents:
706
- signing in to an existing workspace or creating a new free CRM-only
707
- workspace (--new-workspace --org-name "<name>"). New workspaces have
708
- full CRM access; run \`dench upgrade\` to unlock chat, file sync,
709
- enrichment, and image generation.
710
- --kind accepts any string. Suggested values: claude_code, codex, cursor,
711
- hermes, openclaw, or any custom kind (e.g. aider, goose, internal_orchestrator).
712
- Defaults to "other" when omitted. Values are normalized to lowercase
713
- snake_case before send (e.g. "Claude Code" -> claude_code).
714
- After sign-in, run dench context --json and dench crm objects list --json
715
- to verify the workspace + CRM are wired up. Tasks live as CRM \`task\`
716
- entries — there is no separate \`dench tasks\` / \`dench task\` /
717
- \`dench claim\` / \`dench log\` command.
718
-
719
- Backends and hosts:
720
- Default backend: ${DEFAULT_HOST} (overridden by \`dench backend set\`)
721
- Show / change the persistent default:
722
- dench backend show
723
- dench backend set production | staging | local | <url>
724
- dench backend clear
725
- One-shot overrides for a single command:
726
- dench --prod <cmd>
727
- dench --staging <cmd>
728
- dench --backend local <cmd>
729
- dench --host https://example.com <cmd>
730
- DENCH_HOST=https://example.com dench <cmd>
731
-
732
- Dev fallback (run against a local dev deployment with a dev key):
733
- DENCH_DEV_AGENT_KEY=... DENCH_WORKSPACE=... dench status --dev
734
- Inside the dench.com repo, --dev also auto-loads .env / .env.local,
735
- which carry the dev backend URL and GATEWAY_URL etc. Use --use-dev-env (or
736
- DENCH_USE_DEV_ENV=1) when you want that auto-load without switching
737
- the runtime to the dev workspace mode.
738
-
739
- Advanced:
740
- For long-lived agents or when the human asks:
741
- DENCH_SESSION_KEY=stable-agent-id dench signin
742
- DENCH_SESSION_KEY=stable-agent-id dench logout
743
- DENCH_SESSION_KEY=stable-agent-id dench status
744
- `);
700
+ `;
701
+ }
702
+
703
+ async function help() {
704
+ const { commandHelp } = await import("./lib/command-help");
705
+ console.log(commandHelp([], rootUsageText()));
745
706
  }
746
707
 
747
708
  function contextHelp() {
@@ -3780,7 +3741,7 @@ async function runFilesCat() {
3780
3741
  code: "files_cat_not_found",
3781
3742
  nextActions: [
3782
3743
  "Run `dench files ls` on the parent directory to confirm.",
3783
- "If the file only exists on the sandbox FS, run `dench fs sync --initial` first.",
3744
+ "If the file exists only on local disk, upload it with `dench stage <local-path> <workspace-path>`.",
3784
3745
  ],
3785
3746
  });
3786
3747
  }
@@ -3850,11 +3811,11 @@ async function runFilesWrite() {
3850
3811
  )) as { path: string; contentHash: string };
3851
3812
 
3852
3813
  if (json) {
3853
- print({ ok: true, ...result, bytes: content.length });
3814
+ print({ ok: true, ...result, bytes: Buffer.byteLength(content, "utf8") });
3854
3815
  return;
3855
3816
  }
3856
3817
  process.stdout.write(
3857
- `dench files write: ${result.path} (${content.length}B)\n`,
3818
+ `dench files write: ${result.path} (${Buffer.byteLength(content, "utf8")}B)\n`,
3858
3819
  );
3859
3820
  }
3860
3821
 
@@ -3916,7 +3877,7 @@ async function runFilesDownload() {
3916
3877
  code: "files_download_not_found",
3917
3878
  nextActions: [
3918
3879
  "Run `dench files ls` on the parent directory to confirm.",
3919
- "If the file only exists on the sandbox FS, run `dench fs sync --initial` first.",
3880
+ "If the file exists only on local disk, upload it with `dench stage <local-path> <workspace-path>`.",
3920
3881
  ],
3921
3882
  });
3922
3883
  }
@@ -3990,25 +3951,8 @@ async function runFilesDownload() {
3990
3951
  async function runFilesCommand() {
3991
3952
  const sub = positional(1);
3992
3953
  if (!sub || sub === "help" || hasFlag("--help")) {
3993
- process.stdout.write(
3994
- [
3995
- "dench files <ls|cat|write|mv|rm|download> — workspace file ops that update the UI tree",
3996
- "",
3997
- " dench files ls [<path>] [--recursive] [--json]",
3998
- " dench files cat <path> [--json]",
3999
- " dench files write <path> < file.md (content on stdin)",
4000
- " dench files mv <src> <dst> [--json]",
4001
- " dench files rm <path> [--recursive] [--json]",
4002
- " dench files download <path> [<local-dest>] [--url] [--json]",
4003
- "",
4004
- "Paths are POSIX paths under /workspace; you can pass either form.",
4005
- "These change workspace storage directly (and the daemon syncs to the volume).",
4006
- "cat prints inline text and is the only way to read the magic files",
4007
- "(IDENTITY.md, ORGANISATION.md, …), which have no stored bytes.",
4008
- "download fetches the canonical bytes from workspace storage (dirs recurse);",
4009
- "--url prints a short-lived signed URL instead of saving.",
4010
- ].join("\n"),
4011
- );
3954
+ const { commandHelp } = await import("./lib/command-help");
3955
+ process.stdout.write(`${commandHelp(["files"], rootUsageText())}\n`);
4012
3956
  return;
4013
3957
  }
4014
3958
  if (sub === "ls") return await runFilesLs();
@@ -4328,7 +4272,7 @@ async function main() {
4328
4272
  );
4329
4273
  if (helpArgs[0] === "help") helpArgs.shift();
4330
4274
  if (!helpArgs.length) {
4331
- help();
4275
+ await help();
4332
4276
  return;
4333
4277
  }
4334
4278
  const path = helpArgs.filter(
@@ -4402,24 +4346,38 @@ async function main() {
4402
4346
  });
4403
4347
  return;
4404
4348
  }
4405
- const { commandHelp } = await import("./lib/command-help");
4406
- const usage =
4407
- helpArgs[0] === "email"
4408
- ? (await import("./email")).emailHelpText()
4409
- : helpArgs[0] === "crm"
4410
- ? (await import("./crm")).crmHelpText()
4411
- : helpArgs[0] === "env"
4412
- ? (await import("./environments")).environmentHelpText()
4413
- : helpArgs[0] === "secrets"
4414
- ? (await import("./workspace-secrets")).workspaceSecretsHelpText()
4415
- : "";
4349
+ const { commandHelp, normalizeHelpCommand } = await import(
4350
+ "./lib/command-help"
4351
+ );
4352
+ const family = normalizeHelpCommand(helpArgs)[0];
4353
+ const helpText: Record<string, () => Promise<string>> = {
4354
+ tool: async () => (await import("./tools")).toolHelpText(),
4355
+ email: async () => (await import("./email")).emailHelpText(),
4356
+ crm: async () => (await import("./crm")).crmHelpText(),
4357
+ env: async () => (await import("./environments")).environmentHelpText(),
4358
+ secrets: async () =>
4359
+ (await import("./workspace-secrets")).workspaceSecretsHelpText(),
4360
+ chat: async () => (await import("./chat")).chatHelpText(),
4361
+ cron: async () => (await import("./cron")).cronHelpText(),
4362
+ apps: async () => (await import("./apps")).appsHelpText(),
4363
+ members: async () => (await import("./members")).membersHelpText(),
4364
+ web: async () => (await import("./web")).webHelpText(),
4365
+ meetings: async () => (await import("./meetings")).meetingsHelpText(),
4366
+ lists: async () => (await import("./leads")).listsHelpText(),
4367
+ browser: async () => (await import("./browser")).browserHelpText(),
4368
+ search: async () => (await import("./search")).searchHelpText(),
4369
+ image: async () => (await import("./image")).imageHelpText(),
4370
+ ads: async () => (await import("./ads")).adsHelpText(),
4371
+ campaign: async () => (await import("./campaign")).campaignHelpText(),
4372
+ };
4373
+ const usage = helpText[family] ? await helpText[family]() : rootUsageText();
4416
4374
  console.log(commandHelp(helpArgs, usage));
4417
4375
  return;
4418
4376
  }
4419
4377
  const command = positional(0);
4420
4378
  const subcommand = positional(1);
4421
4379
 
4422
- assertDeclaredCommandFlags(stripRuntimeFlags(runtimeArgs));
4380
+ assertDeclaredCommandFlags(stripRuntimeFlags(args));
4423
4381
 
4424
4382
  // New setup commands validate before authentication and never consume child flags.
4425
4383
  if (command === "env" || command === "secrets") {
@@ -4555,7 +4513,7 @@ async function main() {
4555
4513
  }
4556
4514
 
4557
4515
  if (!command || command === "help") {
4558
- help();
4516
+ await help();
4559
4517
  return;
4560
4518
  }
4561
4519
 
package/image.ts CHANGED
@@ -384,7 +384,11 @@ async function runEditSubcommand(ctx: ImageCliContext): Promise<void> {
384
384
  }
385
385
 
386
386
  function imageHelp(): void {
387
- console.log(`Usage: dench image <subcommand>
387
+ console.log(imageHelpText());
388
+ }
389
+
390
+ export function imageHelpText(): string {
391
+ return `Usage: dench image <subcommand>
388
392
 
389
393
  Generate or edit images via the Dench Cloud Gateway. Auths
390
394
  with DENCH_API_KEY or the active \`dench signin\` agent session. Agent
@@ -415,7 +419,7 @@ Environment variables:
415
419
  DENCH_ORG_ID Forwarded so wildcard keys can target an org.
416
420
  DENCH_RUN_ID Forwarded for telemetry correlation.
417
421
 
418
- Exit codes: 0 success, non-zero on gateway / network errors.`);
422
+ Exit codes: 0 success, non-zero on gateway / network errors.`;
419
423
  }
420
424
 
421
425
  // ────────────────────────────────────────────────────────────────────────────
package/leads.ts CHANGED
@@ -646,7 +646,11 @@ Rename/delete saved searches with \`dench lists rename|delete\`.
646
646
  }
647
647
 
648
648
  export function listsHelp(): void {
649
- console.log(`Dench lists
649
+ console.log(listsHelpText());
650
+ }
651
+
652
+ export function listsHelpText(): string {
653
+ return `Dench lists
650
654
 
651
655
  Usage:
652
656
  dench lists [--search "September"] [--limit 25] [--cursor <cursor>] [--json]
@@ -680,7 +684,7 @@ Use \`--preview\` first to review the selection without importing or enrolling a
680
684
  Delete removes the list, its exported CSV/JSONL files, and any linked watch.
681
685
  CRM lists (saved views on Leads/Companies/Job Postings) are managed with
682
686
  \`dench crm views list|get|create|rename|delete\`.
683
- `);
687
+ `;
684
688
  }
685
689
 
686
690
  /**
@@ -1,4 +1,5 @@
1
1
  import { CliArgError, missingFlagValue } from "./cli-args";
2
+ import { FILE_COMMAND_ARGUMENTS } from "./file-command-contract";
2
3
 
3
4
  type PositionalArity = readonly [min: number, max: number | null];
4
5
 
@@ -8,6 +9,10 @@ type CommandArguments = {
8
9
  switches: string[];
9
10
  positionals: PositionalArity;
10
11
  flagPositionals: Record<string, PositionalArity>;
12
+ usage?: string;
13
+ description?: string;
14
+ examples?: string[];
15
+ notes?: string[];
11
16
  };
12
17
  const spec = (
13
18
  command: string,
@@ -138,9 +143,7 @@ export const COMMAND_ARGUMENTS = [
138
143
  ...["delete", "enable", "disable"].map((verb) =>
139
144
  spec(`cron ${verb}`, [1, 1]),
140
145
  ),
141
- spec("files rm", [1, 1], "", "--recursive -r"),
142
- spec("files mv", [2, 2]),
143
- spec("files write", [1, 1]),
146
+ ...FILE_COMMAND_ARGUMENTS,
144
147
  spec("stage", [1, 2], "", "--clean"),
145
148
  ...["identity", "organisation", "user", "tools", "mem"].flatMap((group) =>
146
149
  ["edit", "set"].map((verb) =>
@@ -200,16 +203,30 @@ export const COMMAND_ARGUMENTS = [
200
203
  browserSpec("stop", [0, 0]),
201
204
  ];
202
205
 
203
- export function assertDeclaredCommandFlags(argv: readonly string[]): void {
204
- const boundary = argv.indexOf("--");
205
- const args = argv
206
- .slice(0, boundary < 0 ? undefined : boundary)
207
- .filter((arg) => arg !== "--json");
206
+ /** Keep compatibility aliases identical in validation and local discovery. */
207
+ export function normalizeCommandAlias(argv: readonly string[]): string[] {
208
+ const args = [...argv];
208
209
  if (["routine", "routines"].includes(args[0])) args[0] = "cron";
209
210
  if (["ad", "adline"].includes(args[0])) args[0] = "ads";
210
- if (args[0] === "agent" && ["new", "send"].includes(args[1]))
211
+ if (args[0] === "agent" && ["new", "send", "follow"].includes(args[1]))
211
212
  args[0] = "chat";
212
213
  if (args[0] === "crm" && args[1] === "members") args.shift();
214
+ if (args[0] === "campaigns") args[0] = "campaign";
215
+ return args;
216
+ }
217
+
218
+ export function assertDeclaredCommandFlags(argv: readonly string[]): void {
219
+ const boundary = argv.indexOf("--");
220
+ const args = normalizeCommandAlias(
221
+ argv
222
+ .slice(0, boundary < 0 ? undefined : boundary)
223
+ .filter((arg) => arg !== "--json"),
224
+ );
225
+ if (args[0] === "files" && boundary >= 0) {
226
+ throw new CliArgError(
227
+ "dench files does not accept a child-command separator (--). Run `dench files --help` for usage.",
228
+ );
229
+ }
213
230
  const contract = COMMAND_ARGUMENTS.find(({ command }) =>
214
231
  command.split(" ").every((part, index) => args[index] === part),
215
232
  );