@dench.com/cli 3.6.6-staging.386 → 3.8.0-staging.389

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/dench.ts CHANGED
@@ -28,10 +28,13 @@ import {
28
28
  resolveHostFromArgs,
29
29
  STAGING_HOST,
30
30
  } from "./host";
31
+ import { CliArgError, isHelpRequest } from "./lib/cli-args";
31
32
  import { cliVersion } from "./lib/cli-version";
33
+ import { assertDeclaredCommandFlags } from "./lib/command-args";
32
34
  import { denchConfigDirectory } from "./lib/config-path.mjs";
33
35
  import { resolveOrganizationBySelector } from "./lib/organizationSelector";
34
- import { backendErrorText } from "./lib/public-error";
36
+ import { normalizeParentArgv } from "./lib/parent-argv";
37
+ import { backendErrorText, publicErrorDetails } from "./lib/public-error";
35
38
  import { formatApprovalOpenMessage, openUrl } from "./openUrl";
36
39
  import {
37
40
  explicitSessionKeyInput,
@@ -215,10 +218,13 @@ const api = {
215
218
  };
216
219
  const CONFIG_DIR = denchConfigDirectory();
217
220
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
218
- const args = process.argv.slice(2);
219
- const json = args.includes("--json");
220
- const filteredArgs = args.filter((arg) => arg !== "--json");
221
- const scopedStatus = args.includes("--mine") || args.includes("--self");
221
+ // A child process owns every token after `--`, including --host and --json.
222
+ // Normalize only Dench's parent arguments; pass child argv through byte for byte.
223
+ const { args, runtimeArgs } = normalizeParentArgv(process.argv.slice(2));
224
+ const json = runtimeArgs.includes("--json");
225
+ const filteredArgs = runtimeArgs.filter((arg) => arg !== "--json");
226
+ const scopedStatus =
227
+ runtimeArgs.includes("--mine") || runtimeArgs.includes("--self");
222
228
 
223
229
  function readEnv(name: string) {
224
230
  const value = process.env[name]?.trim();
@@ -321,13 +327,18 @@ function throwIfCliError(value: unknown) {
321
327
  }
322
328
  const record = value as JsonRecord;
323
329
  if (record.ok === false && typeof record.error === "string") {
324
- throw new Error(record.error);
330
+ throw new CliError(record.error, publicErrorDetails(record));
325
331
  }
326
332
  }
327
333
 
328
334
  function normalizeCliError(error: unknown): CliError {
329
- if (error instanceof CliError) return error;
335
+ if (error instanceof CliError) {
336
+ error.payload = { ...error.payload, ...publicErrorDetails(error.payload) };
337
+ return error;
338
+ }
330
339
  const message = backendErrorText(error);
340
+ if (error instanceof CliArgError)
341
+ return new CliError(message, { code: error.code, retryable: false });
331
342
  if (message.includes("Invalid dev agent key")) {
332
343
  return new CliError("Invalid dev agent key", {
333
344
  code: "invalid_dev_agent_key",
@@ -369,7 +380,10 @@ function normalizeCliError(error: unknown): CliError {
369
380
  ],
370
381
  });
371
382
  }
372
- return new CliError(message);
383
+ return new CliError(message, {
384
+ code: "cli_error",
385
+ ...publicErrorDetails(error),
386
+ });
373
387
  }
374
388
 
375
389
  function _parseJsonObjectOption(name: string) {
@@ -632,14 +646,11 @@ Past chat threads (own + shared in this workspace):
632
646
  dench chat delete --ids id1,id2 [--yes] [--json] (PERMANENTLY deletes threads)
633
647
 
634
648
  Spawn a new chat-turn workflow (same as the web UI):
635
- dench chat new "<prompt>" [--model <m>] [--file-context <path>] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]
649
+ dench chat new "<prompt>" [--environment NAME] [--model <m>] [--file-context <path>] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]
636
650
  dench chat send <thread-id> "<message>" [--model <m>] [--yolo] [--follow] [--json]
637
651
  dench chat follow <thread-id> [--json] (tail a live thread by id)
638
652
  dench chat tree [<root-thread-id>] [--limit N] [--json]
639
653
  (parent-child thread tree)
640
- dench agent new ... (alias for dench chat new)
641
- dench agent send ... (alias for dench chat send)
642
- dench agent follow ... (alias for dench chat follow)
643
654
 
644
655
  Help: dench chat help
645
656
 
@@ -661,30 +672,23 @@ returns ENOSYS on the FUSE-backed volume mount):
661
672
  Persist scratch -> workspace (cp from /tmp into /workspace + cloud sync):
662
673
  dench stage <local-path> [<workspace-dest>] [--clean] [--json]
663
674
 
664
- Subagents:
665
- dench agent spawn '<goal>' [--parent-run-id <id>] [--sandbox own|share_parent] [--time-budget-ms N]
666
- dench agent await --hook <token> | --children id1,id2,id3
667
- dench agent message <toRunId> '<text>' [--from-run-id <id>]
668
- dench agent wait-message
669
- dench agent pause <runId>
670
- dench agent resume <runId> ['optional prompt']
671
- dench agent continue <prevRunId> '<prompt>'
672
- dench agent tree [<rootRunId>]
673
- Help: dench agent help
675
+ Reusable workspace setup:
676
+ dench env list | show <name> | save <name> --path <directory> | run <name> -- <command> | delete <name>
677
+ dench secrets set <NAME> --stdin [--kind env|file]
678
+ dench secrets list | delete <NAME> metadata only; values are never printed
679
+ Help: dench env --help, dench secrets --help
674
680
 
675
681
  dench --version
682
+ dench reference <name> [--json] Offline CLI-version-matched skill/reference
683
+ dench reference --list [--json] Bundled reference index and revision
676
684
 
677
- Self-updating agent harness:
678
- dench identity show | edit IDENTITY.md (org-wide)
679
- dench organisation show | edit ORGANISATION.md
680
- dench user show | edit USER.md (per-member)
681
- dench tools show | edit TOOLS.md notes section
682
- dench mem show | append "<text>" | set MEMORY.md aggregate
683
- dench bootstrap status | complete | reopen | template
684
- dench model default <id|clear>
685
- dench model thread <threadId> <id|clear>
686
- dench daily today | show <YYYY-MM-DD>
687
- Help: dench identity help
685
+ Standing context and memory:
686
+ dench context --sections identity,organisation,user,memory,rules,tools [--json]
687
+ dench context set <identity|organisation|user|tools|rules> --stdin
688
+ dench memory search "<query>" | save <key> "<fact>"
689
+ dench model default <id|clear> | thread <threadId> <id|clear>
690
+ Context describes preferences and instructions. Environments store setup;
691
+ secrets store credentials. A memory note does not install or save either.
688
692
 
689
693
  External tools:
690
694
  dench integrations is an alias for dench tool status -- it lists connected apps.
@@ -746,13 +750,14 @@ function contextHelp() {
746
750
  Usage:
747
751
  dench context [--instructions] [--json]
748
752
  dench context --sections organisation,user,memory [--json]
749
- dench instructions [--json]
753
+ dench context set <identity|organisation|user|tools|rules> --stdin | --content "text" [--json]
750
754
 
751
755
  Shows the current workspace, current agent, assigned tasks, pending approvals
752
756
  requested by this agent, connected apps when available, and useful next commands.
753
757
 
754
758
  Use --instructions (or dench instructions) to print the standing workspace
755
- instructions external agents should treat as system-level guidance.
759
+ instructions for the current workspace. Context and memory do not persist
760
+ credentials or installed software: use dench secrets and dench env.
756
761
  `);
757
762
  }
758
763
 
@@ -2694,19 +2699,33 @@ type Runtime = Awaited<ReturnType<typeof getRuntime>>;
2694
2699
  * `dench members list --json` print the human table and `dench crm … --json`
2695
2700
  * fall back to text on empty results (`(no rows)`), breaking `| jq` pipelines.
2696
2701
  */
2697
- function stripRuntimeFlags(args: string[]) {
2702
+ function stripRuntimeFlags(args: string[], validateValues = true) {
2698
2703
  const booleanFlags = new Set([
2699
2704
  "--dev",
2700
2705
  "--prod",
2701
2706
  "--staging",
2702
2707
  "--use-dev-env",
2703
2708
  ]);
2704
- const valueFlags = new Set(["--convex-url", "--host", "--backend"]);
2709
+ const valueFlags = new Set([
2710
+ "--convex-url",
2711
+ "--host",
2712
+ "--backend",
2713
+ "--session-key",
2714
+ "--session",
2715
+ ]);
2705
2716
  const stripped: string[] = [];
2706
2717
  for (let i = 0; i < args.length; i++) {
2707
2718
  const arg = args[i];
2719
+ if (arg === "--") {
2720
+ stripped.push(...args.slice(i));
2721
+ break;
2722
+ }
2708
2723
  if (booleanFlags.has(arg)) continue;
2709
2724
  if (valueFlags.has(arg)) {
2725
+ if (!args[i + 1] || args[i + 1].startsWith("-")) {
2726
+ if (validateValues) throw new CliArgError(`Missing value for ${arg}`);
2727
+ continue;
2728
+ }
2710
2729
  i++;
2711
2730
  continue;
2712
2731
  }
@@ -3392,7 +3411,7 @@ async function resolveFsDaemonEnv(): Promise<NodeJS.ProcessEnv> {
3392
3411
  }
3393
3412
 
3394
3413
  async function runFsCommand() {
3395
- const subArgs = args.slice(args.indexOf("fs") + 1);
3414
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("fs") + 1));
3396
3415
  const configuredBinary = process.env.DENCH_FS_DAEMON_BIN?.trim();
3397
3416
  const binary = configuredBinary || "dench-fs-daemon";
3398
3417
  const daemonEnv = await resolveFsDaemonEnv();
@@ -4303,10 +4322,234 @@ function isCommandNotFound(error: unknown) {
4303
4322
  }
4304
4323
 
4305
4324
  async function main() {
4325
+ if (isHelpRequest(runtimeArgs)) {
4326
+ const helpArgs = stripRuntimeFlags(runtimeArgs, false).filter(
4327
+ (arg) => !["--json", "--help", "-h"].includes(arg),
4328
+ );
4329
+ if (helpArgs[0] === "help") helpArgs.shift();
4330
+ if (!helpArgs.length) {
4331
+ help();
4332
+ return;
4333
+ }
4334
+ const path = helpArgs.filter(
4335
+ (arg) => !["help", "--help", "-h"].includes(arg),
4336
+ );
4337
+ if (path[0] === "daily") {
4338
+ console.log(
4339
+ `dench ${path.join(" ")} was retired. Search history with dench chat search or read existing daily files with dench files download. Harness A does not automatically write daily logs.`,
4340
+ );
4341
+ return;
4342
+ }
4343
+ // Root help retains domain-specific setup, account and pagination guidance.
4344
+ // These are explicit help-only paths; never pass mutation arguments to a handler.
4345
+ if (path.length === 1) {
4346
+ const roots: Record<string, () => void | Promise<void>> = {
4347
+ reference: async () =>
4348
+ console.log((await import("./reference")).REFERENCE_HELP),
4349
+ env: async () =>
4350
+ console.log((await import("./environments")).environmentHelpText()),
4351
+ secrets: async () =>
4352
+ console.log(
4353
+ (await import("./workspace-secrets")).workspaceSecretsHelpText(),
4354
+ ),
4355
+ tool: toolHelp,
4356
+ integrations: integrationsHelp,
4357
+ backend: backendHelp,
4358
+ signin: signinHelp,
4359
+ login: signinHelp,
4360
+ onboard: signinHelp,
4361
+ setup: signinHelp,
4362
+ "what-can-i-do": signinHelp,
4363
+ register: signinHelp,
4364
+ memory: memoryHelp,
4365
+ billing: billingHelp,
4366
+ logout: logoutHelp,
4367
+ context: contextHelp,
4368
+ artifacts: artifactsHelp,
4369
+ "suggested-work": artifactsHelp,
4370
+ lists: listsCliHelp,
4371
+ leads: leadsHelp,
4372
+ companies: companiesHelp,
4373
+ jobs: jobsHelp,
4374
+ status: statusHelp,
4375
+ web: async () => (await import("./web")).webHelp(),
4376
+ crm: async () =>
4377
+ (await import("./crm")).runCrmCommand({
4378
+ convex: {} as ConvexHttpClient,
4379
+ args: ["help"],
4380
+ }),
4381
+ apps: async () =>
4382
+ (await import("./apps")).runAppsCommand({
4383
+ convex: {} as ConvexHttpClient,
4384
+ args: ["help"],
4385
+ }),
4386
+ };
4387
+ if (roots[path[0]]) {
4388
+ await roots[path[0]]();
4389
+ return;
4390
+ }
4391
+ }
4392
+ if (helpArgs[0] === "reference") {
4393
+ console.log((await import("./reference")).REFERENCE_HELP);
4394
+ return;
4395
+ }
4396
+ // Preserve the small, established CRM read help including its cursor contract.
4397
+ if (helpArgs[0] === "crm" && ["query", "aggregate"].includes(helpArgs[1])) {
4398
+ const { runCrmCommand } = await import("./crm");
4399
+ await runCrmCommand({
4400
+ convex: {} as ConvexHttpClient,
4401
+ args: [...helpArgs.slice(1), "--help"],
4402
+ });
4403
+ return;
4404
+ }
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
+ : "";
4416
+ console.log(commandHelp(helpArgs, usage));
4417
+ return;
4418
+ }
4306
4419
  const command = positional(0);
4307
4420
  const subcommand = positional(1);
4308
4421
 
4309
- if (command === "version" || hasFlag("--version")) {
4422
+ assertDeclaredCommandFlags(stripRuntimeFlags(runtimeArgs));
4423
+
4424
+ // New setup commands validate before authentication and never consume child flags.
4425
+ if (command === "env" || command === "secrets") {
4426
+ const rawSubArgs = args.slice(args.indexOf(command) + 1);
4427
+ const helpRequested =
4428
+ runtimeArgs.includes("--help") ||
4429
+ runtimeArgs.includes("-h") ||
4430
+ subcommand === "help";
4431
+ const subArgs = helpRequested ? rawSubArgs : stripRuntimeFlags(rawSubArgs);
4432
+ const mod =
4433
+ command === "env"
4434
+ ? await import("./environments")
4435
+ : await import("./workspace-secrets");
4436
+ if ("validateEnvironmentArgs" in mod) {
4437
+ const parsed = mod.validateEnvironmentArgs(subArgs);
4438
+ if (parsed.verb === "help") {
4439
+ console.log(mod.environmentHelpText());
4440
+ return;
4441
+ }
4442
+ const runtime = requireAuthenticatedRuntime(
4443
+ await getRuntime(),
4444
+ "dench env",
4445
+ );
4446
+ await mod.runEnvironmentCommand({
4447
+ convex: runtime.client,
4448
+ args: subArgs,
4449
+ sessionToken: runtime.sessionToken,
4450
+ apiUrl: runtime.host,
4451
+ });
4452
+ } else {
4453
+ const parsed = mod.validateWorkspaceSecretsArgs(subArgs);
4454
+ if (parsed.verb === "help") {
4455
+ console.log(mod.workspaceSecretsHelpText());
4456
+ return;
4457
+ }
4458
+ const runtime = requireAuthenticatedRuntime(
4459
+ await getRuntime(),
4460
+ "dench secrets",
4461
+ );
4462
+ await mod.runWorkspaceSecretsCommand({
4463
+ convex: runtime.client,
4464
+ args: subArgs,
4465
+ sessionToken: runtime.sessionToken,
4466
+ });
4467
+ }
4468
+ return;
4469
+ }
4470
+ if (
4471
+ command === "context" &&
4472
+ (runtimeArgs.includes("--help") ||
4473
+ runtimeArgs.includes("-h") ||
4474
+ subcommand === "help")
4475
+ ) {
4476
+ contextHelp();
4477
+ return;
4478
+ }
4479
+ if (command === "daily") {
4480
+ const guidance =
4481
+ "Daily-log commands were retired. Search actual history with dench chat search or read existing daily files with dench files download. Nothing writes daily logs automatically in Harness A.";
4482
+ if (
4483
+ runtimeArgs.includes("--help") ||
4484
+ runtimeArgs.includes("-h") ||
4485
+ subcommand === "help"
4486
+ ) {
4487
+ console.log(guidance);
4488
+ return;
4489
+ }
4490
+ throw new CliError(guidance, {
4491
+ code: "deprecated_command",
4492
+ retryable: false,
4493
+ });
4494
+ }
4495
+ if (command === "context" && subcommand === "set") {
4496
+ const { validateContextSet, runContextSet } = await import(
4497
+ "./context-settings"
4498
+ );
4499
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
4500
+ validateContextSet(subArgs);
4501
+ const runtime = requireAuthenticatedRuntime(
4502
+ await getRuntime(),
4503
+ "dench context set",
4504
+ );
4505
+ await runContextSet({
4506
+ convex: runtime.client,
4507
+ args: subArgs,
4508
+ sessionToken: runtime.sessionToken,
4509
+ });
4510
+ return;
4511
+ }
4512
+ if (
4513
+ command === "memory" &&
4514
+ (runtimeArgs.includes("--help") || runtimeArgs.includes("-h"))
4515
+ ) {
4516
+ memoryHelp();
4517
+ return;
4518
+ }
4519
+ if (command === "memory" && subcommand === "save") {
4520
+ const kind = option("--kind") ?? "fact";
4521
+ if (
4522
+ ![
4523
+ "fact",
4524
+ "decision",
4525
+ "preference",
4526
+ "goal",
4527
+ "tool_note",
4528
+ "other",
4529
+ "convention",
4530
+ "project_context",
4531
+ "agent_lesson",
4532
+ "docs_knowledge",
4533
+ "open_goal",
4534
+ ].includes(kind)
4535
+ )
4536
+ throw new CliError(
4537
+ "Unsupported memory kind. Use memory for facts/preferences; use dench secrets set NAME --stdin for credentials and dench env save for reusable setup.",
4538
+ { code: "validation_failed", retryable: false },
4539
+ );
4540
+ }
4541
+
4542
+ if (command === "reference") {
4543
+ await (await import("./reference")).runReferenceCommand(
4544
+ stripRuntimeFlags(args.slice(args.indexOf("reference") + 1)),
4545
+ );
4546
+ return;
4547
+ }
4548
+
4549
+ if (
4550
+ command === "version" ||
4551
+ stripRuntimeFlags(runtimeArgs)[0] === "--version"
4552
+ ) {
4310
4553
  console.log(await cliVersion());
4311
4554
  return;
4312
4555
  }
@@ -4741,7 +4984,7 @@ async function main() {
4741
4984
  }
4742
4985
 
4743
4986
  if (command === "web") {
4744
- const subArgs = args.slice(args.indexOf("web") + 1);
4987
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("web") + 1));
4745
4988
  const filteredSubArgs = subArgs.filter((a) => a !== "--json");
4746
4989
  const { runWebCommand, webHelp } = await import("./web");
4747
4990
  const sub = filteredSubArgs[0];
@@ -4761,7 +5004,7 @@ async function main() {
4761
5004
  }
4762
5005
 
4763
5006
  if (command === "search") {
4764
- const subArgs = args.slice(args.indexOf("search") + 1);
5007
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("search") + 1));
4765
5008
  const filteredSubArgs = subArgs.filter((a) => a !== "--json");
4766
5009
  const { runSearchCommand } = await import("./search");
4767
5010
  const sub = filteredSubArgs[0];
@@ -4784,7 +5027,7 @@ async function main() {
4784
5027
  }
4785
5028
 
4786
5029
  if (command === "image") {
4787
- const subArgs = args.slice(args.indexOf("image") + 1);
5030
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("image") + 1));
4788
5031
  const filteredSubArgs = subArgs.filter((a) => a !== "--json");
4789
5032
  const { runImageCommand } = await import("./image");
4790
5033
  const sub = filteredSubArgs[0];
@@ -4804,7 +5047,7 @@ async function main() {
4804
5047
  }
4805
5048
 
4806
5049
  if (command === "email") {
4807
- const subArgs = args.slice(args.indexOf("email") + 1);
5050
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("email") + 1));
4808
5051
  const { runEmailCommand } = await import("./email");
4809
5052
  const runtime = await getRuntime();
4810
5053
  const authedRuntime = requireAuthenticatedRuntime(runtime, "dench email");
@@ -4813,7 +5056,7 @@ async function main() {
4813
5056
  | undefined;
4814
5057
  try {
4815
5058
  if (
4816
- subArgs[0] !== "draft" &&
5059
+ !["draft", "inbox", "send"].includes(subArgs[0]) &&
4817
5060
  !subArgs.some(
4818
5061
  (arg) => arg === "--mailbox" || arg.startsWith("--mailbox="),
4819
5062
  )
@@ -4922,7 +5165,7 @@ async function main() {
4922
5165
  }
4923
5166
 
4924
5167
  if (command === "browser") {
4925
- const subArgs = args.slice(args.indexOf("browser") + 1);
5168
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("browser") + 1));
4926
5169
  const { runBrowserCommand } = await import("./browser");
4927
5170
  const runtime = await getRuntime();
4928
5171
  const authedRuntime = requireAuthenticatedRuntime(runtime, "dench browser");
@@ -4947,7 +5190,7 @@ async function main() {
4947
5190
  // `dench ads` is canonical; `dench ad` and `dench adline` are aliases for
4948
5191
  // the same command (one code path in ./ads).
4949
5192
  if (command === "ads" || command === "ad" || command === "adline") {
4950
- const subArgs = args.slice(args.indexOf(command) + 1);
5193
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
4951
5194
  const { runAdsCommand } = await import("./ads");
4952
5195
  // Earning is opt-in: resolve the agent session token best-effort so a
4953
5196
  // signed-in developer gets credited on flush, but never require login —
@@ -4983,7 +5226,7 @@ async function main() {
4983
5226
  }
4984
5227
 
4985
5228
  if (command === "chat") {
4986
- const subArgs = args.slice(args.indexOf("chat") + 1);
5229
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("chat") + 1));
4987
5230
  const sub = subArgs[0];
4988
5231
  if (!sub || sub === "help" || sub === "--help") {
4989
5232
  const { runChatCommand } = await import("./chat");
@@ -5033,43 +5276,33 @@ async function main() {
5033
5276
  }
5034
5277
 
5035
5278
  if (command === "agent") {
5036
- const subArgs = args.slice(args.indexOf("agent") + 1);
5279
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("agent") + 1));
5037
5280
  const sub = subArgs[0];
5038
- if (!sub || sub === "help" || sub === "--help") {
5039
- const { runAgentCommand } = await import("./agent");
5040
- // biome-ignore lint/suspicious/noExplicitAny: harmless help-only stub
5041
- await runAgentCommand({ convex: {} as any, args: subArgs });
5042
- return;
5043
- }
5044
- // Aliases: `dench agent new` / `dench agent send` / `dench agent
5045
- // follow` -> the chat-spawn implementation. Surfaced under
5046
- // `agent` so users who associate chat threads with the agent
5047
- // surface still find them; the actual workflow is identical to
5048
- // `dench chat new` / `dench chat send` / `dench chat follow`.
5049
- if (sub === "new" || sub === "send" || sub === "follow") {
5050
- const { runChatSpawnCommand } = await import("./chat-spawn");
5051
- const runtime = await getRuntime();
5052
- const authedRuntime = requireAuthenticatedRuntime(
5053
- runtime,
5054
- `dench agent ${sub}`,
5281
+ if (
5282
+ !sub ||
5283
+ sub === "help" ||
5284
+ subArgs.includes("--help") ||
5285
+ subArgs.includes("-h")
5286
+ ) {
5287
+ console.log(
5288
+ "Use dench chat new, send, follow, or tree. agent new/send/follow remain compatibility aliases. In Harness A, use spawn_task/read_task for child tasks.",
5055
5289
  );
5056
- await runChatSpawnCommand({
5057
- runtime: {
5058
- host: authedRuntime.host,
5059
- bearerToken: authedRuntime.sessionToken,
5060
- },
5061
- args: subArgs.slice(1),
5062
- subcommand: sub,
5063
- });
5064
5290
  return;
5065
5291
  }
5066
- const { runAgentCommand } = await import("./agent");
5067
- const runtime = await getRuntime();
5068
- await runAgentCommand({
5069
- convex: runtime.client,
5070
- args: subArgs,
5071
- sessionToken: runtime.sessionToken,
5072
- callerRunId: process.env.DENCH_RUN_ID?.trim() || undefined,
5292
+ if (sub !== "new" && sub !== "send" && sub !== "follow")
5293
+ throw new CliError(
5294
+ "Unsupported agent command. Use dench chat new/send/follow/tree or the harness subagent tools.",
5295
+ { code: "unsupported_capability", retryable: false },
5296
+ );
5297
+ const { runChatSpawnCommand } = await import("./chat-spawn");
5298
+ const runtime = requireAuthenticatedRuntime(
5299
+ await getRuntime(),
5300
+ `dench chat ${sub}`,
5301
+ );
5302
+ await runChatSpawnCommand({
5303
+ runtime: { host: runtime.host, bearerToken: runtime.sessionToken },
5304
+ args: subArgs.slice(1),
5305
+ subcommand: sub,
5073
5306
  });
5074
5307
  return;
5075
5308
  }
@@ -5110,7 +5343,7 @@ async function main() {
5110
5343
  // `dench cron`. Same dispatch, same auth, same handler — only the
5111
5344
  // top-level command name differs to match the workspace UI rename.
5112
5345
  if (command === "cron" || command === "routine" || command === "routines") {
5113
- const subArgs = args.slice(args.indexOf(command) + 1);
5346
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
5114
5347
  const sub = subArgs[0];
5115
5348
  // Help is a no-auth path so users can read the surface without a
5116
5349
  // session — mirrors `dench crm help`.
@@ -5144,7 +5377,7 @@ async function main() {
5144
5377
  // ── OpenClaw-parity self-update CLI surface ──────────────────────────
5145
5378
  //
5146
5379
  // Dispatch all `dench identity / organisation / user / tools / mem /
5147
- // bootstrap / model / daily` traffic through the shared
5380
+ // bootstrap / model` traffic through the shared
5148
5381
  // agent-config CLI module. Each command requires the same auth shape
5149
5382
  // as `dench cron` (Bearer DENCH_API_KEY or `dench signin` session
5150
5383
  // token) so server-side `requireCronAccess` accepts both.
@@ -5156,10 +5389,9 @@ async function main() {
5156
5389
  "mem",
5157
5390
  "bootstrap",
5158
5391
  "model",
5159
- "daily",
5160
5392
  ]);
5161
5393
  if (AGENT_CONFIG_COMMANDS.has(command)) {
5162
- const subArgs = args.slice(args.indexOf(command) + 1);
5394
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
5163
5395
  if (
5164
5396
  subArgs[0] === "help" ||
5165
5397
  subArgs.includes("--help") ||
@@ -5209,10 +5441,6 @@ async function main() {
5209
5441
  await mod.runModelCommand(ctxBase);
5210
5442
  return;
5211
5443
  }
5212
- if (command === "daily") {
5213
- await mod.runDailyCommand(ctxBase);
5214
- return;
5215
- }
5216
5444
  }
5217
5445
 
5218
5446
  if (command === "signin") {
@@ -5260,7 +5488,7 @@ async function main() {
5260
5488
  return;
5261
5489
  }
5262
5490
 
5263
- if (command === "context" && args.includes("--sections")) {
5491
+ if (command === "context" && runtimeArgs.includes("--sections")) {
5264
5492
  const raw = args[args.indexOf("--sections") + 1];
5265
5493
  const sections = raw
5266
5494
  ?.split(",")
@@ -5298,7 +5526,7 @@ async function main() {
5298
5526
  return;
5299
5527
  }
5300
5528
 
5301
- if (command === "context" && args.includes("--instructions")) {
5529
+ if (command === "context" && runtimeArgs.includes("--instructions")) {
5302
5530
  const instructions = await buildWorkspaceInstructionsContext(runtime);
5303
5531
  print(
5304
5532
  json ? instructions : (stringField(instructions, "instructions") ?? ""),