From bdd2f49df780e056bc2897669e86f5f8dc37ae8c Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sat, 8 Aug 2026 15:05:50 +0800 Subject: [PATCH] fix(cli): address dsh run review feedback --- .../architecture/2026-08-05-profile-plugin-bundles.i18n.yaml | 4 ++-- .../architecture/2026-08-05-profile-plugin-bundles.md | 2 ++ .../architecture/2026-08-05-profile-plugin-bundles.zh.md | 2 ++ .../feature/2026-08-08-dsh-run-headless-command.i18n.yaml | 4 ++-- .../feature/2026-08-08-dsh-run-headless-command.md | 2 ++ .../feature/2026-08-08-dsh-run-headless-command.zh.md | 2 ++ apps/cli/src/args.ts | 4 ++-- 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.i18n.yaml index 7fa37eeb8e..3e1d038514 100644 --- a/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md -2026-08-05-profile-plugin-bundles.md: b5bf5411d22ab99b598f667886b3c29ba8ee7b06 -2026-08-05-profile-plugin-bundles.zh.md: ae790028b5768c05c57acd27d7f68bdc4d612c11 +2026-08-05-profile-plugin-bundles.md: 8613e600ad633818abb4319e614230340c3b3876 +2026-08-05-profile-plugin-bundles.zh.md: 03a771364c4f3262801f28e68b96ec5de633e5ec diff --git a/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md b/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md index b5bf5411d2..8613e600ad 100644 --- a/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md +++ b/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md @@ -14,6 +14,8 @@ Everything becomes a **profile**: a directory `$DSH_HOME/profiles/` with a The shipped compositions became bundles: `@deepseek-ai/dsh-base` (the former base rows as one insert), `@deepseek-ai/dsh-web-app` (the former web overlay plus a runtime glue plugin that owns what used to be launcher code — frontend-dist resolution, the web-surface prompt section, bash runtime variables, the URL line), and `@deepseek-ai/dsh-headless` (a one-shot runner plugin over base + web-app). `dsh web` stays as an alias for `--profile web` carrying the Web flag family; `dsh run [--profile ] "task"` owns one-shot execution and defaults to the headless profile, while generic `dsh --profile ` boots without a task; `dsh --config` is removed (its uses migrate to `--patch`). `dsh plugin --profile ` is a thin pnpm forwarder that initializes the profile and reconciles `dsh.profile.bundles` after `add`/`remove` (a bundle-less package warns and stays a plain dependency). +The [`dsh run` command decision](../feature/2026-08-08-dsh-run-headless-command.md) owns the one-shot grammar; this note owns the profile composition it selects. + Resolution is two-anchored by construction: `dsh.profile.bundles` names resolve from the dsh installation first, then the profile directory — so in-box bundles always come from the same installation as the running `dsh` and pnpm never manages them — while bare plugin names in patch rows resolve through the profile directory's Node parent-walk into the maintained flat fallback `$DSH_HOME/profiles/node_modules` (one symlink per package the installation's app and bundles depend on, healed on every launch). Two supporting refactors: the webserver's built-in static dist serving became the single-owner **fallback seat** (`registerFallback`/`applyIndexTaps`), with the SPA server extracted to `@deepseek-ai/dsh-frontend-static` so the web bundle owns its dist as composition, not launcher code; and the personal-overlay machinery of the [dsh CLI personal-config decision](../feature/2026-07-20-dsh-cli-personal-config.md) (`loadPersonalPatches`, `$DSH_HOME/config.yaml`) was retargeted to the per-profile and home-level `cordis.patch.yml` layers (`loadOptionalPatches`, `watchUserPatches` taking a filename), superseding that note's entry modes and file location while keeping its Harness-home root, patch semantics, and fail-loud parsing. diff --git a/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md b/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md index ae790028b5..03a771364c 100644 --- a/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md @@ -14,6 +14,8 @@ Status: implemented 已交付的组合改造成了组合包:`@deepseek-ai/dsh-base`(原有基础行合并为一次插入)、`@deepseek-ai/dsh-web-app`(原 web overlay,外加一个接管原启动器代码的运行时粘合插件——前端 dist 解析、web 表层提示词段落、bash 运行时变量、URL 行)、`@deepseek-ai/dsh-headless`(叠加在 base + web-app 之上的一次性 runner 插件)。`dsh web` 保留为携带 Web flag 家族的 `--profile web` 别名;`dsh run [--profile ] "task"` 负责一次性执行,默认使用 headless profile,而通用的 `dsh --profile ` 只启动 profile,不携带任务;`dsh --config` 被移除(其用途迁移到 `--patch`)。`dsh plugin --profile ` 是一层薄薄的 pnpm 转发器,负责初始化 profile,并在 `add`/`remove` 后调和 `dsh.profile.bundles`(没有组合包声明的包会给出警告,保持为普通依赖)。 +[`dsh run` 命令决策](../feature/2026-08-08-dsh-run-headless-command.md)负责一次性语法;本 Agent Note 负责该语法所选择的 profile 组合。 + 解析在构造上就是双锚点的:`dsh.profile.bundles` 中的名称先从 dsh 安装目录解析,再从 profile 目录解析——因此内置组合包始终来自与运行中 `dsh` 相同的安装,pnpm 从不管理它们——而 patch 行中的裸插件名称经 profile 目录的 Node 父目录逐级查找,落到受维护的扁平回退目录 `$DSH_HOME/profiles/node_modules`(安装目录的应用与各组合包所依赖的每个包各一个符号链接,每次启动时修复)。 两项配套重构:webserver 内置的静态 dist 服务改为单一所有者的**回退席位**(`registerFallback`/`applyIndexTaps`),SPA 服务器提取到 `@deepseek-ai/dsh-frontend-static`,使 web 组合包以组合的方式持有自己的 dist,而不是靠启动器代码;[dsh CLI 个人配置决策](../feature/2026-07-20-dsh-cli-personal-config.md)的个人 overlay 机制(`loadPersonalPatches`、`$DSH_HOME/config.yaml`)改为面向逐 profile 与 home 级的 `cordis.patch.yml` 层(`loadOptionalPatches`、接受文件名的 `watchUserPatches`),取代该笔记的各入口模式与文件位置,同时保留其 Harness home 根目录、patch 语义与大声失败的解析。 diff --git a/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.i18n.yaml b/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.i18n.yaml index 8d5ec1c9f6..bc2d1575ee 100644 --- a/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.md -2026-08-08-dsh-run-headless-command.md: aac2a473760509626d315df8d57eb405eb547abf -2026-08-08-dsh-run-headless-command.zh.md: d71d2a34addf1c64b8cb37c54117be5b9c643370 +2026-08-08-dsh-run-headless-command.md: d1cc0573d1d94bb7b30e98aa685d2b7d89878a99 +2026-08-08-dsh-run-headless-command.zh.md: 7b48dfd708efb197babcfcbeaf4f8a4ca76cde3f diff --git a/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.md b/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.md index aac2a47376..d1cc0573d1 100644 --- a/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.md +++ b/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.md @@ -20,6 +20,8 @@ dsh run [--profile ] [--patch ...] `--profile` defaults to `headless` and remains available for custom one-shot compositions. `--patch` is repeatable and occupies the existing overlay layer. Commander joins the variadic task arguments with spaces and rejects a missing or blank task before boot. +The [profile plugin bundle decision](../architecture/2026-08-05-profile-plugin-bundles.md) owns the composition selected by this grammar. + `RunInvocation` is a separate `DshInvocation` member. The generic profile invocation no longer carries task text, and its root command accepts no positional arguments. Both dispatch paths call the existing deep `runProfile` module: `profile` omits `task`, while `run` supplies it. There is no shallow `run.ts` forwarding module and no alias, warning, or custom detector for former spellings; they fail through the ordinary Commander grammar. A one-shot profile without `headless-runner` still fails through the existing composed-row check, while booting a profile that contains that row without a task points to `dsh run --profile ""`. The `run` verb belongs to one-shot task execution. Launching an application file must choose another command name; two top-level meanings selected by positional shape would recreate the ambiguity this command removes. diff --git a/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.zh.md b/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.zh.md index d71d2a34ad..7b48dfd708 100644 --- a/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.zh.md +++ b/.agents/notes/implemented/feature/2026-08-08-dsh-run-headless-command.zh.md @@ -20,6 +20,8 @@ dsh run [--profile ] [--patch ...] `--profile` 默认为 `headless`,同时保留对自定义一次性组合的支持。`--patch` 可重复使用,并沿用既有 overlay 层的位置。Commander 用空格拼接可变数量的任务参数,并在启动前拒绝缺失或空白任务。 +[profile 插件组合包决策](../architecture/2026-08-05-profile-plugin-bundles.md)负责该语法所选择的组合。 + `RunInvocation` 是单独的 `DshInvocation` 成员。通用 profile 调用不再携带任务文本,其根命令也不接受位置参数。两条分派路径都调用已有的深层 `runProfile` 模块:`profile` 省略 `task`,`run` 则提供该字段。实现中没有只负责转发的浅层 `run.ts` 模块,也没有面向旧写法的别名、警告或自定义检测器;旧写法会按普通 Commander 语法失败。缺少 `headless-runner` 的一次性 profile 仍会触发既有的组合行检查;如果启动的 profile 包含该行却未提供任务,错误会指向 `dsh run --profile ""`。 `run` 动词只负责一次性任务执行。应用文件启动必须选择其他命令名;如果让两个顶层含义由位置参数形态决定,就会重新引入本命令消除的歧义。 diff --git a/apps/cli/src/args.ts b/apps/cli/src/args.ts index 0b72f76c6a..d96b7a503f 100644 --- a/apps/cli/src/args.ts +++ b/apps/cli/src/args.ts @@ -78,7 +78,7 @@ interface WebOptions { /** Raw run-subcommand options straight from Commander. */ interface RunOptions { - profile?: string + profile: string patch?: string[] } @@ -161,7 +161,7 @@ Examples: .argument('', 'task text') .action((task: string[], options: RunOptions) => { rejectParentOptions('run') - const profile = options.profile ?? 'headless' + const profile = options.profile if (profile === '') program.error('error: --profile needs a name') const patches = options.patch ?? [] if (patches.includes('')) program.error('error: --patch needs a path')