diff --git a/.trellis/scripts/common/active_task.py b/.trellis/scripts/common/active_task.py index c3caac0db..97a7784a6 100644 --- a/.trellis/scripts/common/active_task.py +++ b/.trellis/scripts/common/active_task.py @@ -55,6 +55,7 @@ "kimi", "zcode", "snow", + "dsh", } # Every name below records how it was checked. Do NOT add a name by analogy @@ -64,6 +65,15 @@ # only "evidence" behind them. A platform with no verified name belongs in no # table; it resolves through TRELLIS_CONTEXT_ID or its hook/plugin bridge. _ENV_SESSION_KEYS: tuple[tuple[str, tuple[str, ...]], ...] = ( + # REAL (reported 2026-08-13 against DSH 0.1.0-rc.6 by @SajoLuo, from a live + # run: DSH exports DSH_SESSION_ID plus DSH_SHELL=1 into its managed shell). + # MUST STAY FIRST. A DSH session can inherit an outer host's identity — a + # DSH launched from Codex still carries CODEX_THREAD_ID — and the untargeted + # lookup below walks this table in order, so any earlier entry would claim + # the session and write a foreign `codex_` pointer for DSH work. + # DSH_SESSION_ID is the only name here no other vendor sets, so first place + # is safe: it cannot mis-claim a non-DSH session. + ("dsh", ("DSH_SESSION_ID",)), # REAL, undocumented (verified 2026-08-05 in a live Claude Code 2.1.221 bash # child; absent from code.claude.com/docs/en/env-vars). CLAUDE_SESSION_ID # was removed here — verified absent from that same live environment. diff --git a/.trellis/workflow.md b/.trellis/workflow.md index bf3d1c136..158142e16 100644 --- a/.trellis/workflow.md +++ b/.trellis/workflow.md @@ -280,13 +280,13 @@ When a user request matches one of these intents inside an active task, route fi [/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] - Planning or unclear requirements -> `trellis-brainstorm`. - Before editing -> `trellis-before-dev`; after editing -> `trellis-check`. - Repeated debugging -> `trellis-break-loop`; spec updates -> `trellis-update-spec`. -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] ### Guardrails @@ -363,11 +363,11 @@ Spawn the research sub-agent: [/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] Do the research in the main session directly and write findings into `{TASK_DIR}/research/`. `codex-inline` is the explicit mode that keeps work in the main session. -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] **Research artifact conventions**: - One file per research topic (e.g. `research/auth-library-comparison.md`) @@ -427,11 +427,11 @@ Skip this step only when both files already have real curated entries. [/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] Skip this step. Context is loaded directly by the `trellis-before-dev` skill in Phase 2. -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] #### 1.4 Activate task `[required · once]` @@ -515,7 +515,7 @@ The platform prelude auto-handles the context load requirement: [/Kiro] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] 1. Load the `trellis-before-dev` skill to read project guidelines 2. Read `{TASK_DIR}/prd.md`, then `design.md` if present, then `implement.md` if present @@ -523,7 +523,7 @@ The platform prelude auto-handles the context load requirement: 4. Implement the code per reviewed artifacts 5. Run project lint and type-check -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] #### 2.2 Quality check `[required · repeatable]` @@ -543,7 +543,7 @@ The check agent's job: [/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] Load the `trellis-check` skill and verify the code per its guidance: - Spec compliance @@ -552,7 +552,7 @@ Load the `trellis-check` skill and verify the code per its guidance: If issues are found → fix → re-check, until green. -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] **Final pass (before Phase 3.4 commit)**: the last 2.2 of a task must run full-scope, not just on the latest implement chunk. List all affected packages with `python3 ./.trellis/scripts/get_context.py --mode packages`, then load each package's spec index Quality Check section. This catches cross-layer / multi-package issues a mid-iteration local 2.2 cannot. diff --git a/README.md b/README.md index bd94f3de0..fd6a38cff 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ | **Task-centered workflow** | Keep PRDs, implementation context, review context, and task status in `.trellis/tasks/` so AI work stays structured. | | **Project memory** | Journals in `.trellis/workspace/` preserve what happened last time, so each new session starts with real context. | | **Team-shared standards** | Specs live in the repo, so one person's hard-won workflow or rule can benefit the whole team. | -| **Multi-platform setup** | Bring the same Trellis structure to 21 AI coding platforms instead of rebuilding your workflow per tool. | +| **Multi-platform setup** | Bring the same Trellis structure to 22 AI coding platforms instead of rebuilding your workflow per tool. | ## Prerequisites: diff --git a/README_CN.md b/README_CN.md index 4f8eb1c51..8a770b0ba 100644 --- a/README_CN.md +++ b/README_CN.md @@ -44,7 +44,7 @@ | **任务驱动工作流** | PRD、实现上下文、审查上下文与任务状态统一存放于 `.trellis/tasks/`,AI 开发过程保持结构化、可追溯。 | | **项目记忆** | `.trellis/workspace/` 中的工作日志(journal)会保留上一次会话的脉络,因此每次新会话都能基于真实上下文开始。 | | **团队共享标准** | Spec 随仓库一同版本化,个人总结出的规则与流程可以直接成为整个团队的基础设施。 | -| **多平台复用** | 同一套 Trellis 结构覆盖 21 个 AI coding 平台,无需为每个工具单独搭建工作流。 | +| **多平台复用** | 同一套 Trellis 结构覆盖 22 个 AI coding 平台,无需为每个工具单独搭建工作流。 | ## 前置要求 diff --git a/docs-site b/docs-site index dabf17c13..1066c727b 160000 --- a/docs-site +++ b/docs-site @@ -1 +1 @@ -Subproject commit dabf17c13e9023e53e284873b27beaf76d23dc89 +Subproject commit 1066c727bee7e7cfb475738b74b5f48e838d1618 diff --git a/marketplace b/marketplace index 1329c65b9..d286b2ce8 160000 --- a/marketplace +++ b/marketplace @@ -1 +1 @@ -Subproject commit 1329c65b949ed03beb0970ac34b5b288bca1a41a +Subproject commit d286b2ce8cd899b40de75446c24efacf1abd7bae diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts index d87046671..830de0324 100644 --- a/packages/cli/src/cli/index.ts +++ b/packages/cli/src/cli/index.ts @@ -84,6 +84,7 @@ program .option("--codebuddy", "Include CodeBuddy commands") .option("--copilot", "Include GitHub Copilot hooks") .option("--droid", "Include Factory Droid commands") + .option("--dsh", "Include DeepSeek Harness (dsh) skills") .option("--pi", "Include Pi Agent extension assets") .option("--reasonix", "Include Reasonix skills") .option("--zcode", "Include ZCode commands") diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 6b51eb2f8..47fcef378 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -1017,6 +1017,7 @@ interface InitOptions { codebuddy?: boolean; copilot?: boolean; droid?: boolean; + dsh?: boolean; pi?: boolean; reasonix?: boolean; zcode?: boolean; diff --git a/packages/cli/src/configurators/dsh.ts b/packages/cli/src/configurators/dsh.ts new file mode 100644 index 000000000..9ce5b8eed --- /dev/null +++ b/packages/cli/src/configurators/dsh.ts @@ -0,0 +1,85 @@ +/** + * DeepSeek Harness (dsh) configurator. + * + * dsh is a class-2 pull-based platform (agentCapable, no shipped + * session-start hook, no project-level hooks/settings Trellis may write). + * The dsh agent loads skills by name through its skill-loader tool and + * discovers them from the project roots `/.dsh/skills` + * (rank 100) and `/.agents/skills` (rank 200). Two output + * paths: + * - `.agents/skills/` — workflow + bundled skills, written via the NEUTRAL + * resolver so the files stay byte-identical to Codex/Gemini/Pi/Kimi + * writes into the same shared root. + * - `.dsh/skills/` — dsh-private user-invocable entry skills + * (`trellis-start` / `trellis-continue` / `trellis-finish-work`), + * platform-resolved (`--platform dsh`, `trellis-` skill refs), in + * dsh's own highest-rank project skill root. + * - `.dsh/DSH.md` — operator guide; also gives the platform a + * configDir-owned tracked file so `trellis platforms` / `uninstall` + * can detect and scope dsh. + * + * dsh ships no project-level sub-agent definition surface, so no + * trellis-implement / trellis-check / trellis-research agent prompts are + * written; implement/check/research run inline through the workflow skills. + */ + +import { AI_TOOLS } from "../types/ai-tools.js"; +import { getDshGuide } from "../templates/dsh/index.js"; +import { + collectSkillTemplates, + resolveAllAsSkills, + resolveBundledSkills, + resolveSkillsNeutral, +} from "./shared.js"; + +/** + * Command templates that become user-invocable dsh skills + * (`trellis-start` / `trellis-continue` / `trellis-finish-work`). dsh has + * no slash-command palette, so the session-boundary commands are delivered + * as SKILL.md files in `.dsh/skills/`. + */ +const DSH_COMMAND_SKILL_NAMES = new Set([ + "trellis-start", + "trellis-continue", + "trellis-finish-work", +]); + +/** Session-boundary commands resolved as dsh skills (dsh-private root, so + * platform-specific `{{CLI_FLAG}}` / `{{CMD_REF}}` resolution is correct). */ +function resolveDshCommandSkills(): ReturnType { + const ctx = AI_TOOLS.dsh.templateContext; + return resolveAllAsSkills(ctx).filter((skill) => + DSH_COMMAND_SKILL_NAMES.has(skill.name), + ); +} + +/** + * The dsh file set — written at init and diffed by `trellis update`. + */ +export function collectDshTemplates(): Map { + const ctx = AI_TOOLS.dsh.templateContext; + const files = new Map(); + + // 1. Workflow + bundled skills → shared `.agents/skills/` (neutral + // rendering, byte-identical to Codex/Gemini/Pi/Kimi writes). + for (const [filePath, content] of collectSkillTemplates( + ".agents/skills", + resolveSkillsNeutral(ctx), + resolveBundledSkills(ctx), + )) { + files.set(filePath, content); + } + + // 2. Commands-as-skills → `.dsh/skills/` (dsh-native project root). + for (const [filePath, content] of collectSkillTemplates( + ".dsh/skills", + resolveDshCommandSkills(), + )) { + files.set(filePath, content); + } + + // 3. Operator guide → `.dsh/DSH.md`. + files.set(".dsh/DSH.md", getDshGuide()); + + return files; +} diff --git a/packages/cli/src/configurators/index.ts b/packages/cli/src/configurators/index.ts index d3415415d..a83c5f550 100644 --- a/packages/cli/src/configurators/index.ts +++ b/packages/cli/src/configurators/index.ts @@ -35,6 +35,7 @@ import { collectQoderTemplates } from "./qoder.js"; import { collectCodebuddyTemplates } from "./codebuddy.js"; import { collectCopilotTemplates } from "./copilot.js"; import { collectDroidTemplates } from "./droid.js"; +import { collectDshTemplates } from "./dsh.js"; import { collectPiTemplates } from "./pi.js"; import { collectReasonixTemplates } from "./reasonix.js"; import { collectZcodeTemplates, configureZcode } from "./zcode.js"; @@ -98,6 +99,7 @@ const PLATFORM_FUNCTIONS: Record = { codebuddy: fromTemplates(collectCodebuddyTemplates), copilot: fromTemplates(collectCopilotTemplates), droid: fromTemplates(collectDroidTemplates), + dsh: fromTemplates(collectDshTemplates), pi: fromTemplates(collectPiTemplates), reasonix: fromTemplates(collectReasonixTemplates), zcode: { configure: configureZcode, collectTemplates: collectZcodeTemplates }, diff --git a/packages/cli/src/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md b/packages/cli/src/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md index 026773f58..f66e1313f 100644 --- a/packages/cli/src/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +++ b/packages/cli/src/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md @@ -19,6 +19,7 @@ This page lists common Trellis file locations in a user project by platform. Whe | CodeBuddy | `--codebuddy` | `.codebuddy/` | `.codebuddy/skills/` | `.codebuddy/agents/` | `.codebuddy/hooks/` + `.codebuddy/settings.json` | | GitHub Copilot | `--copilot` | `.github/` | `.github/skills/` | `.github/agents/` | `.github/copilot/hooks/` + prompts | | Factory Droid | `--droid` | `.factory/` | `.factory/skills/` | `.factory/droids/` | `.factory/hooks/` + settings | +| DeepSeek Harness (dsh) | `--dsh` | `.dsh/` | `.agents/skills/` (shared) + `.dsh/skills/` (entry skills) | None (workflow skills run implement/check inline) | None (class-2 pull-based; no project hooks/settings) | | Pi Agent | `--pi` | `.pi/` | `.agents/skills/` | `.pi/agents/` | `.pi/extensions/trellis/` (native `trellis_subagent` tool) + `.pi/settings.json` | | Trae IDE | `--trae` | `.trae/` | `.trae/skills/` | `.trae/agents/` | `.trae/hooks/` + `.trae/hooks.json` | | Reasonix | `--reasonix` | `.reasonix/` | `.reasonix/skills/` | None — sub-agents are skills with `runAs: subagent` frontmatter | None | @@ -73,7 +74,7 @@ When changing behavior, inspect workflows and skills first. Do not assume Trelli ### Shared `.agents/skills/` -Codex, Gemini CLI, Pi Agent, and Kimi Code write the shared `.agents/skills/` layer. Some tools that support agentskills.io can also read this directory. If the user wants multiple compatible tools to share one skill, consider `.agents/skills/` first, but do not assume every platform reads it. ZCode keeps Trellis-managed skills under `.zcode/skills/`. +Codex, Gemini CLI, Pi Agent, Kimi Code, and DeepSeek Harness (dsh) write the shared `.agents/skills/` layer. Some tools that support agentskills.io can also read this directory. If the user wants multiple compatible tools to share one skill, consider `.agents/skills/` first, but do not assume every platform reads it. ZCode keeps Trellis-managed skills under `.zcode/skills/`. ## Decision Rules When Modifying Platform Files diff --git a/packages/cli/src/templates/dsh/DSH.md b/packages/cli/src/templates/dsh/DSH.md new file mode 100644 index 000000000..110f4339d --- /dev/null +++ b/packages/cli/src/templates/dsh/DSH.md @@ -0,0 +1,56 @@ +# Trellis on DeepSeek Harness (dsh) + +dsh is a **class-2 pull-based** Trellis host: no session-start hook auto-injects +workflow context, so the agent loads the Trellis skills on demand through its +skill-loader tool. + +| Capability | Status | +| --- | --- | +| Skills (`.agents/skills/trellis-*/SKILL.md`) | Works — dsh discovers this shared root natively | +| Entry skills (`.dsh/skills/trellis-*/SKILL.md`) | Works — dsh's own project skill root (highest rank) | +| Context hooks | None — pull-based: skills read `.trellis/` files directly | +| Sub-agents | None shipped — implement/check/research run inline via the workflow skills | + +## Quick start + +```bash +trellis init --dsh -u your-name +dsh web # or: dsh --profile headless "start a Trellis task for ..." +``` + +In dsh: + +1. Open a session in the project root and describe the work in natural + language. For a new task the agent should load the `trellis-start` skill, + which reads the current task state from `.trellis/` and routes to + `trellis-brainstorm` (unclear requirements), `trellis-before-dev` (about to + write code), `trellis-check` (done coding), or `trellis-update-spec` + (learned something worth capturing). +2. Entry skills are `trellis-start` / `trellis-continue` / `trellis-finish-work` + in `.dsh/skills/`. You can also ask for them by name at any time. +3. Type `/trellis:finish-work` is a slash-command convention from other hosts — + dsh has no slash palette, so say "finish the trellis task" instead, and the + agent loads `trellis-finish-work`. + +## File map + +- `.agents/skills/` — auto-triggered workflow skills (`trellis-before-dev`, + `trellis-brainstorm`, `trellis-check`, `trellis-break-loop`, + `trellis-update-spec`) plus the bundled `trellis-meta` / + `trellis-spec-bootstrap` / `trellis-session-insight` skills. Byte-identical + to Codex / Gemini CLI / Pi / Kimi writes into the same shared root. +- `.dsh/skills/` — dsh-private entry skills (`trellis-start` / + `trellis-continue` / `trellis-finish-work`). +- `.trellis/` — specs, tasks, workspace memory, and the shared scripts the + skills invoke (`get_context.py`, `task.py`, ...). + +## Notes + +- Skill scripts pass `--platform dsh` to `get_context.py`; the value is used + as a platform-scoped context key. +- The shipped `minimal` agent preset composes only `bash` + + `str_replace_editor`; the default presets include `web_search` and the + filesystem/terminal tools the skills assume. +- dsh has no project-level sub-agent definition surface, so Trellis ships no + `trellis-implement` / `trellis-check` / `trellis-research` agent prompts + here — the workflow skills run those phases inline in the main session. diff --git a/packages/cli/src/templates/dsh/index.ts b/packages/cli/src/templates/dsh/index.ts new file mode 100644 index 000000000..2d5f216df --- /dev/null +++ b/packages/cli/src/templates/dsh/index.ts @@ -0,0 +1,27 @@ +/** + * DeepSeek Harness (dsh) template module. + * + * dsh is a class-2 pull-based, skills-only platform: + * - Workflow + bundled skills go to the shared `.agents/skills/` root via + * the neutral resolver (byte-identical to Codex/Gemini/Pi/Kimi writes). + * - User-invocable entry points (`trellis-start` / `trellis-continue` / + * `trellis-finish-work`, loaded by the dsh agent through its skill-loader + * tool) live under `.dsh/skills//SKILL.md` — dsh's own highest-rank + * project skill root. + * - Operator guide `.dsh/DSH.md`. + * + * dsh has no shipped session-start hook, so `trellis-start` is kept as a + * user-invocable skill. dsh ships no project-level sub-agent definition + * surface, so no trellis-implement / trellis-check / trellis-research agent + * prompts are written; implement/check/research run inline through the + * workflow skills. + */ + +import { createTemplateReader } from "../template-utils.js"; + +const { readTemplate } = createTemplateReader(import.meta.url); + +/** Operator guide copied to `.dsh/DSH.md`. */ +export function getDshGuide(): string { + return readTemplate("DSH.md"); +} diff --git a/packages/cli/src/templates/trellis/scripts/common/active_task.py b/packages/cli/src/templates/trellis/scripts/common/active_task.py index c3caac0db..97a7784a6 100644 --- a/packages/cli/src/templates/trellis/scripts/common/active_task.py +++ b/packages/cli/src/templates/trellis/scripts/common/active_task.py @@ -55,6 +55,7 @@ "kimi", "zcode", "snow", + "dsh", } # Every name below records how it was checked. Do NOT add a name by analogy @@ -64,6 +65,15 @@ # only "evidence" behind them. A platform with no verified name belongs in no # table; it resolves through TRELLIS_CONTEXT_ID or its hook/plugin bridge. _ENV_SESSION_KEYS: tuple[tuple[str, tuple[str, ...]], ...] = ( + # REAL (reported 2026-08-13 against DSH 0.1.0-rc.6 by @SajoLuo, from a live + # run: DSH exports DSH_SESSION_ID plus DSH_SHELL=1 into its managed shell). + # MUST STAY FIRST. A DSH session can inherit an outer host's identity — a + # DSH launched from Codex still carries CODEX_THREAD_ID — and the untargeted + # lookup below walks this table in order, so any earlier entry would claim + # the session and write a foreign `codex_` pointer for DSH work. + # DSH_SESSION_ID is the only name here no other vendor sets, so first place + # is safe: it cannot mis-claim a non-DSH session. + ("dsh", ("DSH_SESSION_ID",)), # REAL, undocumented (verified 2026-08-05 in a live Claude Code 2.1.221 bash # child; absent from code.claude.com/docs/en/env-vars). CLAUDE_SESSION_ID # was removed here — verified absent from that same live environment. diff --git a/packages/cli/src/templates/trellis/workflow.md b/packages/cli/src/templates/trellis/workflow.md index bf3d1c136..158142e16 100644 --- a/packages/cli/src/templates/trellis/workflow.md +++ b/packages/cli/src/templates/trellis/workflow.md @@ -280,13 +280,13 @@ When a user request matches one of these intents inside an active task, route fi [/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] - Planning or unclear requirements -> `trellis-brainstorm`. - Before editing -> `trellis-before-dev`; after editing -> `trellis-check`. - Repeated debugging -> `trellis-break-loop`; spec updates -> `trellis-update-spec`. -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] ### Guardrails @@ -363,11 +363,11 @@ Spawn the research sub-agent: [/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] Do the research in the main session directly and write findings into `{TASK_DIR}/research/`. `codex-inline` is the explicit mode that keeps work in the main session. -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] **Research artifact conventions**: - One file per research topic (e.g. `research/auth-library-comparison.md`) @@ -427,11 +427,11 @@ Skip this step only when both files already have real curated entries. [/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] Skip this step. Context is loaded directly by the `trellis-before-dev` skill in Phase 2. -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] #### 1.4 Activate task `[required · once]` @@ -515,7 +515,7 @@ The platform prelude auto-handles the context load requirement: [/Kiro] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] 1. Load the `trellis-before-dev` skill to read project guidelines 2. Read `{TASK_DIR}/prd.md`, then `design.md` if present, then `implement.md` if present @@ -523,7 +523,7 @@ The platform prelude auto-handles the context load requirement: 4. Implement the code per reviewed artifacts 5. Run project lint and type-check -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] #### 2.2 Quality check `[required · repeatable]` @@ -543,7 +543,7 @@ The check agent's job: [/Claude Code, Cursor, OpenCode, codex-sub-agent, Kiro, Gemini, Qoder, CodeBuddy, Copilot, Droid, Pi, Oh My Pi, ZCode, Snow, Reasonix, Trae, Grok, Kimi Code] -[codex-inline, Kilo, Antigravity, Devin] +[codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] Load the `trellis-check` skill and verify the code per its guidance: - Spec compliance @@ -552,7 +552,7 @@ Load the `trellis-check` skill and verify the code per its guidance: If issues are found → fix → re-check, until green. -[/codex-inline, Kilo, Antigravity, Devin] +[/codex-inline, Kilo, Antigravity, Devin, DeepSeek Harness] **Final pass (before Phase 3.4 commit)**: the last 2.2 of a task must run full-scope, not just on the latest implement chunk. List all affected packages with `python3 ./.trellis/scripts/get_context.py --mode packages`, then load each package's spec index Quality Check section. This catches cross-layer / multi-package issues a mid-iteration local 2.2 cannot. diff --git a/packages/cli/src/types/ai-tools.ts b/packages/cli/src/types/ai-tools.ts index ee32e26a7..29fb87ce2 100644 --- a/packages/cli/src/types/ai-tools.ts +++ b/packages/cli/src/types/ai-tools.ts @@ -21,6 +21,7 @@ export type AITool = | "codebuddy" | "copilot" | "droid" + | "dsh" | "pi" | "reasonix" | "zcode" @@ -48,6 +49,7 @@ export type TemplateDir = | "codebuddy" | "copilot" | "droid" + | "dsh" | "pi" | "reasonix" | "zcode" @@ -75,6 +77,7 @@ export type CliFlag = | "codebuddy" | "copilot" | "droid" + | "dsh" | "pi" | "reasonix" | "zcode" @@ -96,7 +99,8 @@ export interface TemplateContext { | "$" | "/" | "/skill trellis-" - | "/skill:trellis-"; + | "/skill:trellis-" + | "trellis-"; /** Description of AI executor actions shown in role tables */ executorAI: | "Bash scripts or Task calls" @@ -391,6 +395,31 @@ export const AI_TOOLS: Record = { cliFlag: "droid", }, }, + dsh: { + // DeepSeek Harness (dsh) is a skills-first pull-based host: it reads + // `.agents/skills/` (agentskills.io, rank-200 project root) and its own + // `.dsh/skills/` (rank-100 project root) natively and the agent loads + // skills by name through its skill-loader tool. No session-start hook + // ships in the default web/headless profiles, so `hasHooks: false` and + // `trellis-start` stays as a user-invocable skill. Entry skills reference + // other skills by bare name (`trellis-`), hence `cmdRefPrefix: + // "trellis-"`. + name: "DeepSeek Harness (dsh)", + templateDirs: ["common", "dsh"], + configDir: ".dsh", + supportsAgentSkills: true, + cliFlag: "dsh", + defaultChecked: false, + hasPythonHooks: false, + templateContext: { + cmdRefPrefix: "trellis-", + executorAI: "Bash scripts or tool calls", + userActionLabel: "Skills", + agentCapable: true, + hasHooks: false, + cliFlag: "dsh", + }, + }, pi: { // Pi also writes .agents/skills/, which is read by Cursor, Gemini CLI, // GitHub Copilot, Amp, and Kimi Code. Keep that detail here rather than diff --git a/packages/cli/test/commands/update.integration.test.ts b/packages/cli/test/commands/update.integration.test.ts index b01470f55..fc2c73e33 100644 --- a/packages/cli/test/commands/update.integration.test.ts +++ b/packages/cli/test/commands/update.integration.test.ts @@ -813,8 +813,11 @@ describe("update() integration", () => { // auto-updated to the current packaged template. expect(readProjectFile(PATHS.WORKFLOW_GUIDE_FILE)).toBe(expectedWorkflow); expect(readProjectFile(MANAGED_FILE)).toBe(expectedGetContext); + // Prefix, not the whole marker: the inline block gains members as + // sub-agent-less platforms are added, and this assertion is about the + // block surviving the update, not about who is currently in it. expect(readProjectFile(PATHS.WORKFLOW_GUIDE_FILE)).toContain( - "[codex-inline, Kilo, Antigravity, Devin]", + "[codex-inline, Kilo, Antigravity, Devin", ); expect(readProjectFile(PATHS.WORKFLOW_GUIDE_FILE)).not.toContain("[Codex]"); @@ -1507,7 +1510,7 @@ describe("update() integration", () => { expect(updated).toContain( "[/Claude Code, Cursor, OpenCode, codex-sub-agent, CodeBuddy, Droid, Pi, ZCode, Snow, Oh My Pi]", ); - expect(updated).toContain("[codex-inline, Kilo, Antigravity, Devin]"); + expect(updated).toContain("[codex-inline, Kilo, Antigravity, Devin"); expect(updated).not.toContain("[Codex]"); expect(updated).not.toContain("[Kilo, Antigravity, Windsurf]"); expect(updated).not.toContain("legacy body"); diff --git a/packages/cli/test/configurators/index.test.ts b/packages/cli/test/configurators/index.test.ts index 4b531df7f..268d7d2a2 100644 --- a/packages/cli/test/configurators/index.test.ts +++ b/packages/cli/test/configurators/index.test.ts @@ -77,6 +77,7 @@ describe("isManagedPath", () => { expect(isManagedPath(".github/hooks/trellis.json")).toBe(true); expect(isManagedPath(".pi/extensions/trellis/index.ts")).toBe(true); expect(isManagedPath(".pi/prompts/trellis-continue.md")).toBe(true); + expect(isManagedPath(".dsh/skills/trellis-start/SKILL.md")).toBe(true); }); // Positive: exact match (startsWith(d + "/") = false, === d = true) @@ -91,6 +92,7 @@ describe("isManagedPath", () => { expect(isManagedPath(".devin/workflows")).toBe(true); expect(isManagedPath(".github/prompts")).toBe(true); expect(isManagedPath(".github/hooks")).toBe(true); + expect(isManagedPath(".dsh")).toBe(true); expect(isManagedPath(".trellis")).toBe(true); }); @@ -114,6 +116,7 @@ describe("isManagedPath", () => { expect(isManagedPath(".github/prompts-backup")).toBe(false); expect(isManagedPath(".github/copilot-backup")).toBe(false); expect(isManagedPath(".github/hooks-backup")).toBe(false); + expect(isManagedPath(".dsh-backup")).toBe(false); }); // Boundary: empty string diff --git a/packages/cli/test/configurators/platforms.test.ts b/packages/cli/test/configurators/platforms.test.ts index b3db51edd..e79f8bfae 100644 --- a/packages/cli/test/configurators/platforms.test.ts +++ b/packages/cli/test/configurators/platforms.test.ts @@ -1030,6 +1030,60 @@ describe("configurePlatform", () => { ); }); + it("configurePlatform('dsh') writes shared + dsh-private skills and the operator guide", async () => { + await configurePlatform("dsh", tmpDir); + + // hasHooks=false → trellis-start stays as a user-invocable dsh skill + expect( + fs.existsSync( + path.join(tmpDir, ".dsh", "skills", "trellis-start", "SKILL.md"), + ), + ).toBe(true); + expect( + fs.existsSync( + path.join(tmpDir, ".dsh", "skills", "trellis-continue", "SKILL.md"), + ), + ).toBe(true); + expect( + fs.existsSync( + path.join(tmpDir, ".dsh", "skills", "trellis-finish-work", "SKILL.md"), + ), + ).toBe(true); + expect( + fs.readFileSync( + path.join(tmpDir, ".dsh", "skills", "trellis-start", "SKILL.md"), + "utf-8", + ), + ).toContain("--platform dsh"); + + // Shared workflow skills land in .agents/skills/, entry skills stay private + expect( + fs.existsSync( + path.join(tmpDir, ".agents", "skills", "trellis-check", "SKILL.md"), + ), + ).toBe(true); + expect( + fs.existsSync( + path.join(tmpDir, ".agents", "skills", "trellis-start", "SKILL.md"), + ), + ).toBe(false); + + expect( + fs.readFileSync(path.join(tmpDir, ".dsh", "DSH.md"), "utf-8"), + ).toContain("class-2"); + + // No hooks/settings — dsh ships no project-level hook surface + expect(fs.existsSync(path.join(tmpDir, ".dsh", "settings.json"))).toBe( + false, + ); + expect(fs.existsSync(path.join(tmpDir, ".dsh", "hooks"))).toBe(false); + expect(fs.existsSync(path.join(tmpDir, ".dsh", "agents"))).toBe(false); + + expect(AI_TOOLS.dsh.templateContext.hasHooks).toBe(false); + expect(AI_TOOLS.dsh.hasPythonHooks).toBe(false); + expect(AI_TOOLS.dsh.supportsAgentSkills).toBe(true); + }); + it("configurePlatform('zcode') writes only .zcode-owned skills", async () => { await configurePlatform("zcode", tmpDir); diff --git a/packages/cli/test/regression.test.ts b/packages/cli/test/regression.test.ts index 2e8dad033..749f11717 100644 --- a/packages/cli/test/regression.test.ts +++ b/packages/cli/test/regression.test.ts @@ -3022,6 +3022,16 @@ print(json.dumps({ { CLAUDE_CODE_SESSION_ID: "real", CLAUDE_SESSION_ID: "legacy" }, "zcode", ], + ["dsh", { DSH_SESSION_ID: "probe" }, "dsh"], + // DSH ships no hook, so the shell path resolves with no platform hint + // and walks the whole table. A DSH launched from Codex inherits + // CODEX_THREAD_ID; without DSH sitting first this returned a foreign + // `codex_outer` pointer (reported by @SajoLuo against DSH 0.1.0-rc.6). + [ + "dsh-inherits-codex", + { DSH_SESSION_ID: "own", CODEX_THREAD_ID: "outer" }, + null, + ], ], ); @@ -3043,6 +3053,8 @@ print(json.dumps({ "zcode-real": "claude_probe", "zcode-legacy": "claude_probe", "zcode-prefers-real": "claude_real", + dsh: "dsh_probe", + "dsh-inherits-codex": "dsh_own", }); }); diff --git a/packages/cli/test/templates/dsh.test.ts b/packages/cli/test/templates/dsh.test.ts new file mode 100644 index 000000000..4f2e14098 --- /dev/null +++ b/packages/cli/test/templates/dsh.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from "vitest"; +import { collectDshTemplates } from "../../src/configurators/dsh.js"; +import { collectPiTemplates } from "../../src/configurators/pi.js"; + +describe("dsh collectDshTemplates", () => { + it("writes entry skills under .dsh/skills/ (dsh-native project root)", () => { + const files = collectDshTemplates(); + + // User-invocable entry points (no slash palette → delivered as skills) + expect(files.has(".dsh/skills/trellis-start/SKILL.md")).toBe(true); + expect(files.has(".dsh/skills/trellis-continue/SKILL.md")).toBe(true); + expect(files.has(".dsh/skills/trellis-finish-work/SKILL.md")).toBe(true); + + // Platform-resolved placeholders: get_context.py calls carry --platform dsh + const start = files.get(".dsh/skills/trellis-start/SKILL.md"); + expect(start).toContain("--platform dsh"); + expect(start).toContain("name: trellis-start"); + }); + + it("renders CMD_REF as bare trellis- skill references in entry skills", () => { + const files = collectDshTemplates(); + const finish = files.get(".dsh/skills/trellis-finish-work/SKILL.md"); + // `{{CMD_REF:finish-work}}` → `` `trellis-finish-work` `` (dsh loads + // skills by name through its skill-loader tool) + expect(finish).toContain("`trellis-finish-work`"); + }); + + it("writes workflow + bundled skills to the shared .agents/skills/ root only", () => { + const files = collectDshTemplates(); + expect(files.has(".agents/skills/trellis-check/SKILL.md")).toBe(true); + expect(files.has(".agents/skills/trellis-before-dev/SKILL.md")).toBe(true); + expect(files.has(".agents/skills/trellis-meta/SKILL.md")).toBe(true); + // Command-as-skill files stay dsh-private (Codex owns the shared + // trellis-start/continue/finish-work fallback copies). + expect(files.has(".agents/skills/trellis-start/SKILL.md")).toBe(false); + expect(files.has(".agents/skills/trellis-finish-work/SKILL.md")).toBe( + false, + ); + }); + + it("renders .agents/skills/ files byte-identically to Pi's shared writes", () => { + const dshFiles = collectDshTemplates(); + const piFiles = collectPiTemplates(); + for (const [key, content] of dshFiles) { + if (!key.startsWith(".agents/skills/")) continue; + expect( + piFiles.get(key), + `${key} must be byte-identical to Pi's shared-skill write`, + ).toBe(content); + } + }); + + it("ships an operator guide and no hooks/settings files", () => { + const files = collectDshTemplates(); + expect(files.get(".dsh/DSH.md")).toBeDefined(); + for (const key of files.keys()) { + expect(key.startsWith(".dsh/hooks")).toBe(false); + expect(key).not.toBe(".dsh/settings.json"); + expect(key).not.toBe(".dsh/config.toml"); + } + }); +});