@dench.com/cli 3.7.0-staging.387 → 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
@@ -33,6 +33,7 @@ import { cliVersion } from "./lib/cli-version";
33
33
  import { assertDeclaredCommandFlags } from "./lib/command-args";
34
34
  import { denchConfigDirectory } from "./lib/config-path.mjs";
35
35
  import { resolveOrganizationBySelector } from "./lib/organizationSelector";
36
+ import { normalizeParentArgv } from "./lib/parent-argv";
36
37
  import { backendErrorText, publicErrorDetails } from "./lib/public-error";
37
38
  import { formatApprovalOpenMessage, openUrl } from "./openUrl";
38
39
  import {
@@ -217,13 +218,13 @@ const api = {
217
218
  };
218
219
  const CONFIG_DIR = denchConfigDirectory();
219
220
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
220
- const args = process.argv.slice(2).flatMap((arg) => {
221
- const split = arg.startsWith("--") ? arg.indexOf("=") : -1;
222
- return split > 2 ? [arg.slice(0, split), arg.slice(split + 1)] : [arg];
223
- });
224
- const json = args.includes("--json");
225
- const filteredArgs = args.filter((arg) => arg !== "--json");
226
- 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");
227
228
 
228
229
  function readEnv(name: string) {
229
230
  const value = process.env[name]?.trim();
@@ -645,14 +646,11 @@ Past chat threads (own + shared in this workspace):
645
646
  dench chat delete --ids id1,id2 [--yes] [--json] (PERMANENTLY deletes threads)
646
647
 
647
648
  Spawn a new chat-turn workflow (same as the web UI):
648
- 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]
649
650
  dench chat send <thread-id> "<message>" [--model <m>] [--yolo] [--follow] [--json]
650
651
  dench chat follow <thread-id> [--json] (tail a live thread by id)
651
652
  dench chat tree [<root-thread-id>] [--limit N] [--json]
652
653
  (parent-child thread tree)
653
- dench agent new ... (alias for dench chat new)
654
- dench agent send ... (alias for dench chat send)
655
- dench agent follow ... (alias for dench chat follow)
656
654
 
657
655
  Help: dench chat help
658
656
 
@@ -674,32 +672,23 @@ returns ENOSYS on the FUSE-backed volume mount):
674
672
  Persist scratch -> workspace (cp from /tmp into /workspace + cloud sync):
675
673
  dench stage <local-path> [<workspace-dest>] [--clean] [--json]
676
674
 
677
- Subagents:
678
- dench agent spawn '<goal>' [--parent-run-id <id>] [--sandbox own|share_parent] [--time-budget-ms N]
679
- dench agent await --hook <token> | --children id1,id2,id3
680
- dench agent message <toRunId> '<text>' [--from-run-id <id>]
681
- dench agent wait-message
682
- dench agent pause <runId>
683
- dench agent resume <runId> ['optional prompt']
684
- dench agent continue <prevRunId> '<prompt>'
685
- dench agent tree [<rootRunId>]
686
- 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
687
680
 
688
681
  dench --version
689
682
  dench reference <name> [--json] Offline CLI-version-matched skill/reference
690
683
  dench reference --list [--json] Bundled reference index and revision
691
684
 
692
- Self-updating agent harness:
693
- dench identity show | edit IDENTITY.md (org-wide)
694
- dench organisation show | edit ORGANISATION.md
695
- dench user show | edit USER.md (per-member)
696
- dench tools show | edit TOOLS.md notes section
697
- dench mem show | append "<text>" | set MEMORY.md aggregate
698
- dench bootstrap status | complete | reopen | template
699
- dench model default <id|clear>
700
- dench model thread <threadId> <id|clear>
701
- dench daily today | show <YYYY-MM-DD>
702
- 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.
703
692
 
704
693
  External tools:
705
694
  dench integrations is an alias for dench tool status -- it lists connected apps.
@@ -761,13 +750,14 @@ function contextHelp() {
761
750
  Usage:
762
751
  dench context [--instructions] [--json]
763
752
  dench context --sections organisation,user,memory [--json]
764
- dench instructions [--json]
753
+ dench context set <identity|organisation|user|tools|rules> --stdin | --content "text" [--json]
765
754
 
766
755
  Shows the current workspace, current agent, assigned tasks, pending approvals
767
756
  requested by this agent, connected apps when available, and useful next commands.
768
757
 
769
758
  Use --instructions (or dench instructions) to print the standing workspace
770
- 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.
771
761
  `);
772
762
  }
773
763
 
@@ -2709,7 +2699,7 @@ type Runtime = Awaited<ReturnType<typeof getRuntime>>;
2709
2699
  * `dench members list --json` print the human table and `dench crm … --json`
2710
2700
  * fall back to text on empty results (`(no rows)`), breaking `| jq` pipelines.
2711
2701
  */
2712
- function stripRuntimeFlags(args: string[]) {
2702
+ function stripRuntimeFlags(args: string[], validateValues = true) {
2713
2703
  const booleanFlags = new Set([
2714
2704
  "--dev",
2715
2705
  "--prod",
@@ -2726,10 +2716,15 @@ function stripRuntimeFlags(args: string[]) {
2726
2716
  const stripped: string[] = [];
2727
2717
  for (let i = 0; i < args.length; i++) {
2728
2718
  const arg = args[i];
2719
+ if (arg === "--") {
2720
+ stripped.push(...args.slice(i));
2721
+ break;
2722
+ }
2729
2723
  if (booleanFlags.has(arg)) continue;
2730
2724
  if (valueFlags.has(arg)) {
2731
- if (!args[i + 1] || args[i + 1].startsWith("--")) {
2732
- throw new CliArgError(`Missing value for ${arg}`);
2725
+ if (!args[i + 1] || args[i + 1].startsWith("-")) {
2726
+ if (validateValues) throw new CliArgError(`Missing value for ${arg}`);
2727
+ continue;
2733
2728
  }
2734
2729
  i++;
2735
2730
  continue;
@@ -4327,22 +4322,36 @@ function isCommandNotFound(error: unknown) {
4327
4322
  }
4328
4323
 
4329
4324
  async function main() {
4330
- if (isHelpRequest(args)) {
4331
- const helpArgs = stripRuntimeFlags(args).filter((arg) => arg !== "--json");
4325
+ if (isHelpRequest(runtimeArgs)) {
4326
+ const helpArgs = stripRuntimeFlags(runtimeArgs, false).filter(
4327
+ (arg) => !["--json", "--help", "-h"].includes(arg),
4328
+ );
4332
4329
  if (helpArgs[0] === "help") helpArgs.shift();
4333
- if (!helpArgs.length || ["--help", "-h"].includes(helpArgs[0])) {
4330
+ if (!helpArgs.length) {
4334
4331
  help();
4335
4332
  return;
4336
4333
  }
4337
4334
  const path = helpArgs.filter(
4338
4335
  (arg) => !["help", "--help", "-h"].includes(arg),
4339
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
+ }
4340
4343
  // Root help retains domain-specific setup, account and pagination guidance.
4341
4344
  // These are explicit help-only paths; never pass mutation arguments to a handler.
4342
4345
  if (path.length === 1) {
4343
4346
  const roots: Record<string, () => void | Promise<void>> = {
4344
4347
  reference: async () =>
4345
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
+ ),
4346
4355
  tool: toolHelp,
4347
4356
  integrations: integrationsHelp,
4348
4357
  backend: backendHelp,
@@ -4399,14 +4408,136 @@ async function main() {
4399
4408
  ? (await import("./email")).emailHelpText()
4400
4409
  : helpArgs[0] === "crm"
4401
4410
  ? (await import("./crm")).crmHelpText()
4402
- : "";
4411
+ : helpArgs[0] === "env"
4412
+ ? (await import("./environments")).environmentHelpText()
4413
+ : helpArgs[0] === "secrets"
4414
+ ? (await import("./workspace-secrets")).workspaceSecretsHelpText()
4415
+ : "";
4403
4416
  console.log(commandHelp(helpArgs, usage));
4404
4417
  return;
4405
4418
  }
4406
4419
  const command = positional(0);
4407
4420
  const subcommand = positional(1);
4408
4421
 
4409
- assertDeclaredCommandFlags(stripRuntimeFlags(args));
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
+ }
4410
4541
 
4411
4542
  if (command === "reference") {
4412
4543
  await (await import("./reference")).runReferenceCommand(
@@ -4415,7 +4546,10 @@ async function main() {
4415
4546
  return;
4416
4547
  }
4417
4548
 
4418
- if (command === "version" || stripRuntimeFlags(args)[0] === "--version") {
4549
+ if (
4550
+ command === "version" ||
4551
+ stripRuntimeFlags(runtimeArgs)[0] === "--version"
4552
+ ) {
4419
4553
  console.log(await cliVersion());
4420
4554
  return;
4421
4555
  }
@@ -5144,41 +5278,31 @@ async function main() {
5144
5278
  if (command === "agent") {
5145
5279
  const subArgs = stripRuntimeFlags(args.slice(args.indexOf("agent") + 1));
5146
5280
  const sub = subArgs[0];
5147
- if (!sub || sub === "help" || sub === "--help") {
5148
- const { runAgentCommand } = await import("./agent");
5149
- // biome-ignore lint/suspicious/noExplicitAny: harmless help-only stub
5150
- await runAgentCommand({ convex: {} as any, args: subArgs });
5151
- return;
5152
- }
5153
- // Aliases: `dench agent new` / `dench agent send` / `dench agent
5154
- // follow` -> the chat-spawn implementation. Surfaced under
5155
- // `agent` so users who associate chat threads with the agent
5156
- // surface still find them; the actual workflow is identical to
5157
- // `dench chat new` / `dench chat send` / `dench chat follow`.
5158
- if (sub === "new" || sub === "send" || sub === "follow") {
5159
- const { runChatSpawnCommand } = await import("./chat-spawn");
5160
- const runtime = await getRuntime();
5161
- const authedRuntime = requireAuthenticatedRuntime(
5162
- runtime,
5163
- `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.",
5164
5289
  );
5165
- await runChatSpawnCommand({
5166
- runtime: {
5167
- host: authedRuntime.host,
5168
- bearerToken: authedRuntime.sessionToken,
5169
- },
5170
- args: subArgs.slice(1),
5171
- subcommand: sub,
5172
- });
5173
5290
  return;
5174
5291
  }
5175
- const { runAgentCommand } = await import("./agent");
5176
- const runtime = await getRuntime();
5177
- await runAgentCommand({
5178
- convex: runtime.client,
5179
- args: subArgs,
5180
- sessionToken: runtime.sessionToken,
5181
- 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,
5182
5306
  });
5183
5307
  return;
5184
5308
  }
@@ -5253,7 +5377,7 @@ async function main() {
5253
5377
  // ── OpenClaw-parity self-update CLI surface ──────────────────────────
5254
5378
  //
5255
5379
  // Dispatch all `dench identity / organisation / user / tools / mem /
5256
- // bootstrap / model / daily` traffic through the shared
5380
+ // bootstrap / model` traffic through the shared
5257
5381
  // agent-config CLI module. Each command requires the same auth shape
5258
5382
  // as `dench cron` (Bearer DENCH_API_KEY or `dench signin` session
5259
5383
  // token) so server-side `requireCronAccess` accepts both.
@@ -5265,7 +5389,6 @@ async function main() {
5265
5389
  "mem",
5266
5390
  "bootstrap",
5267
5391
  "model",
5268
- "daily",
5269
5392
  ]);
5270
5393
  if (AGENT_CONFIG_COMMANDS.has(command)) {
5271
5394
  const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
@@ -5318,10 +5441,6 @@ async function main() {
5318
5441
  await mod.runModelCommand(ctxBase);
5319
5442
  return;
5320
5443
  }
5321
- if (command === "daily") {
5322
- await mod.runDailyCommand(ctxBase);
5323
- return;
5324
- }
5325
5444
  }
5326
5445
 
5327
5446
  if (command === "signin") {
@@ -5369,7 +5488,7 @@ async function main() {
5369
5488
  return;
5370
5489
  }
5371
5490
 
5372
- if (command === "context" && args.includes("--sections")) {
5491
+ if (command === "context" && runtimeArgs.includes("--sections")) {
5373
5492
  const raw = args[args.indexOf("--sections") + 1];
5374
5493
  const sections = raw
5375
5494
  ?.split(",")
@@ -5407,7 +5526,7 @@ async function main() {
5407
5526
  return;
5408
5527
  }
5409
5528
 
5410
- if (command === "context" && args.includes("--instructions")) {
5529
+ if (command === "context" && runtimeArgs.includes("--instructions")) {
5411
5530
  const instructions = await buildWorkspaceInstructionsContext(runtime);
5412
5531
  print(
5413
5532
  json ? instructions : (stringField(instructions, "instructions") ?? ""),