From 93545d580a3c0ec67c15a97863de313f99e183af Mon Sep 17 00:00:00 2001 From: a Date: Thu, 3 Sep 2026 06:04:34 +0000 Subject: [PATCH] refactor(mtmharness): drive coding skills from config --- packages/mtmharness/README.md | 4 +- packages/mtmharness/package.json | 2 +- packages/mtmharness/src/client/index.test.ts | 2 - .../features/coding/client/MtmCodingCard.tsx | 1 - .../features/coding/client/controller.test.ts | 2 +- .../src/features/coding/client/controller.ts | 1 - .../src/features/coding/client/locales.ts | 6 -- .../src/features/coding/config.json | 102 ++++++++++++++++++ .../mtmharness/src/features/coding/index.ts | 40 ++----- .../src/features/coding/manifest.ts | 36 +++++-- .../src/features/coding/packages.json | 100 ----------------- .../src/features/coding/ponytail.ts | 4 +- .../mtmharness/src/features/coding/rtk.ts | 4 +- .../mtmharness/src/features/coding/types.ts | 2 - packages/mtmharness/src/index.ts | 2 +- .../tests/coding-controller.test.mjs | 1 - .../mtmharness/tests/coding-plugin.test.mjs | 18 +--- .../mtmharness/tests/packed-install.test.mjs | 7 +- 18 files changed, 152 insertions(+), 182 deletions(-) create mode 100644 packages/mtmharness/src/features/coding/config.json delete mode 100644 packages/mtmharness/src/features/coding/packages.json diff --git a/packages/mtmharness/README.md b/packages/mtmharness/README.md index f6ba895..40fa5f8 100644 --- a/packages/mtmharness/README.md +++ b/packages/mtmharness/README.md @@ -7,13 +7,13 @@ The DSH plugin is assembled from coding and secondary frontend domains under one Host/Client lifecycle. Codebase Memory keeps its `codebase_memory` server namespace and `mcp__codebase_memory__*` tool names; Ponytail exposes six externally installed skills through DSH's native skill surface. The `/ponytail` command controls per-agent automatic prompt intensity; `off` suppresses that section while explicit native skill invocation remains available. The five companion names are not duplicated as host commands. The `mtm-coding` settings namespace contains the runtime extension toggles. -Modern Go Guidelines is enabled by default as the externally installed `use-modern-go` skill. Its trusted manifest pins the JetBrains `v0.1.1` source and SHA-256; the first enable downloads it into `$DSH_HOME/mtmharness/skills/modern-go`, and `modernGoEnabled` removes the skill from the DSH catalog. Existing files are user-owned and are never overwritten automatically. In the default DSH `workspace-write` sandbox, follow the mounted Modern Go cache prompt and set `HOME`, `XDG_CACHE_HOME`, `GOPATH`, `GOMODCACHE`, and `GOCACHE` beneath the current workspace before the first wrapper call. +Modern Go Guidelines is a configured data-only package that exposes the externally installed `use-modern-go` skill. Its trusted manifest pins the JetBrains `v0.1.1` source and SHA-256; the first load downloads it into `$DSH_HOME/mtmharness/skills/modern-go`. Existing files are user-owned and are never overwritten automatically. In the default DSH `workspace-write` sandbox, follow the mounted Modern Go cache prompt and set `HOME`, `XDG_CACHE_HOME`, `GOPATH`, `GOMODCACHE`, and `GOCACHE` beneath the current workspace before the first wrapper call. RTK is an optional coding feature in the same `mtm-coding` settings namespace. `rtkMode` defaults to `auto` and provides guidance for explicitly invoking an RTK command when the executable is available. The current DSH ToolRuntime has no supported pre-dispatch input-rewrite seam, so the plugin does not register a speculative hook; `rewrite` reports `unavailable` and leaves frozen tool arguments unchanged. The pinned RTK `v0.45.0` runtime helpers remain checksum-verified for explicit integrations; there is no separate RTK skill document. RTK telemetry, tracking, and tee output are disabled for plugin-managed runs. ## Coding Skill Files -The trusted package catalog is data in `src/features/coding/packages.json`: it owns labels, icon keys, immutable GitHub commit pins, expected file digests, and static prompt declarations. On first enable, Ponytail and Modern Go documents plus their pinned resources are downloaded into `$DSH_HOME/mtmharness/skills/` only when that directory is absent; an existing older directory is completed only with missing bundle files. Downloads are size-limited, SHA-256 checked, written to a temporary sibling, and atomically renamed; existing files remain user-owned and are never overwritten. Each enabled package mounts one uniquely named official `@deepseek-ai/dsh-skill-filesystem` provider against its managed custom root, so agents can read the files while writes remain subject to the active DSH filesystem policy. Static manifest prompts are mounted only while their package is enabled. +The coding package configuration is data in `src/features/coding/config.json`: it owns package metadata, immutable GitHub commit pins, expected file digests, and static prompt declarations. The generic loader installs every configured `data-only` package into `$DSH_HOME/mtmharness/skills/` and mounts its skills and prompt. Runtime-backed packages retain their DSH capability handlers while reusing the same manifest installer. Downloads are size-limited, SHA-256 checked, written to a temporary sibling, and atomically renamed; existing files remain user-owned and are never overwritten. Each skill package mounts one uniquely named official `@deepseek-ai/dsh-skill-filesystem` provider against its managed custom root, so agents can read the files while writes remain subject to the active DSH filesystem policy. ## DSH Web Plugin diff --git a/packages/mtmharness/package.json b/packages/mtmharness/package.json index 22ea7ba..6bc3ece 100644 --- a/packages/mtmharness/package.json +++ b/packages/mtmharness/package.json @@ -1,6 +1,6 @@ { "name": "mtmharness", - "version": "0.9.5", + "version": "0.9.6", "description": "Unified DeepSeek Harness Web plugin with Connect, Codebase Memory, Modern Go, Ponytail, and independent static/embed clients.", "type": "module", "engines": { "node": ">=22.19.0", "pnpm": ">=11.7.0" }, diff --git a/packages/mtmharness/src/client/index.test.ts b/packages/mtmharness/src/client/index.test.ts index 273ce67..4860976 100644 --- a/packages/mtmharness/src/client/index.test.ts +++ b/packages/mtmharness/src/client/index.test.ts @@ -18,7 +18,6 @@ function clientBench(loopback = true): { registered: Registered[]; cleanups: Arr codebaseMemoryEnabled: false, dynamicCanvasEnabled: false, codebaseMemoryAugmentHooks: true, - modernGoEnabled: true, ponytailEnabled: true, ponytailMode: "full", ponytailSubagents: true, @@ -99,7 +98,6 @@ async function hostBench(): Promise<{ registeredNamespaces: string[]; cleanups: codebaseMemoryEnabled: false, dynamicCanvasEnabled: false, codebaseMemoryAugmentHooks: true, - modernGoEnabled: false, ponytailEnabled: false, ponytailMode: "full", ponytailSubagents: true, diff --git a/packages/mtmharness/src/features/coding/client/MtmCodingCard.tsx b/packages/mtmharness/src/features/coding/client/MtmCodingCard.tsx index d76d5f0..7717d19 100644 --- a/packages/mtmharness/src/features/coding/client/MtmCodingCard.tsx +++ b/packages/mtmharness/src/features/coding/client/MtmCodingCard.tsx @@ -96,7 +96,6 @@ export function MtmCodingCard(props: MtmCodingCardProps) { { props.edit("codebaseMemoryEnabled", String(value)); }} onReset={() => { props.resetField("codebaseMemoryEnabled"); }} /> { props.edit("dynamicCanvasEnabled", String(value)); }} onReset={() => { props.resetField("dynamicCanvasEnabled"); }} /> { props.edit("codebaseMemoryAugmentHooks", String(value)); }} onReset={() => { props.resetField("codebaseMemoryAugmentHooks"); }} /> - { props.edit("modernGoEnabled", String(value)); }} onReset={() => { props.resetField("modernGoEnabled"); }} /> { props.edit("ponytailEnabled", String(value)); }} onReset={() => { props.resetField("ponytailEnabled"); }} />
{fieldLabel(t, "ponytailMode", mode, () => { props.resetField("ponytailMode"); }, disabled)} diff --git a/packages/mtmharness/src/features/coding/client/controller.test.ts b/packages/mtmharness/src/features/coding/client/controller.test.ts index b45ab1b..7a6d46e 100644 --- a/packages/mtmharness/src/features/coding/client/controller.test.ts +++ b/packages/mtmharness/src/features/coding/client/controller.test.ts @@ -5,7 +5,7 @@ import { MtmCodingCardController } from "./controller.ts"; function settingsScope() { let snapshot = { status: "ready", - value: { codebaseMemoryEnabled: true, dynamicCanvasEnabled: false, codebaseMemoryAugmentHooks: true, modernGoEnabled: true, ponytailEnabled: true, ponytailMode: "full", ponytailSubagents: true, rtkMode: "auto" }, + value: { codebaseMemoryEnabled: true, dynamicCanvasEnabled: false, codebaseMemoryAugmentHooks: true, ponytailEnabled: true, ponytailMode: "full", ponytailSubagents: true, rtkMode: "auto" }, base: {}, user: {}, revision: 1, diff --git a/packages/mtmharness/src/features/coding/client/controller.ts b/packages/mtmharness/src/features/coding/client/controller.ts index 048693e..44ec477 100644 --- a/packages/mtmharness/src/features/coding/client/controller.ts +++ b/packages/mtmharness/src/features/coding/client/controller.ts @@ -11,7 +11,6 @@ const FIELD_NAMES = [ "codebaseMemoryEnabled", "dynamicCanvasEnabled", "codebaseMemoryAugmentHooks", - "modernGoEnabled", "ponytailEnabled", "ponytailMode", "ponytailSubagents", diff --git a/packages/mtmharness/src/features/coding/client/locales.ts b/packages/mtmharness/src/features/coding/client/locales.ts index 0e68811..d3e0532 100644 --- a/packages/mtmharness/src/features/coding/client/locales.ts +++ b/packages/mtmharness/src/features/coding/client/locales.ts @@ -8,8 +8,6 @@ export type MtmCodingLocaleKey = | "dynamicCanvasEnabledHint" | "codebaseMemoryAugmentHooks" | "codebaseMemoryAugmentHooksHint" - | "modernGoEnabled" - | "modernGoEnabledHint" | "ponytailEnabled" | "ponytailEnabledHint" | "ponytailMode" @@ -59,8 +57,6 @@ export const en: Record = { dynamicCanvasEnabledHint: "Load the pinned Canvas extension at runtime from its published artifact.", codebaseMemoryAugmentHooks: "Codebase Memory context augmentation", codebaseMemoryAugmentHooksHint: "Add bounded repository context around session and read/search events.", - modernGoEnabled: "Modern Go Guidelines", - modernGoEnabledHint: "Use JetBrains Modern Go guidance when working on Go code.", ponytailEnabled: "Ponytail", ponytailEnabledHint: "Apply the lazy-senior-dev rules and managed editable skills.", ponytailMode: "Ponytail intensity", @@ -111,8 +107,6 @@ export const zh: Record = { dynamicCanvasEnabledHint: "从已发布的固定版本载荷中运行时加载 Canvas 扩展。", codebaseMemoryAugmentHooks: "Codebase Memory 上下文增强", codebaseMemoryAugmentHooksHint: "在会话和读/搜索事件周围加入有边界的仓库上下文。", - modernGoEnabled: "Modern Go Guidelines", - modernGoEnabledHint: "编写 Go 代码时使用 JetBrains Modern Go 指导。", ponytailEnabled: "Ponytail", ponytailEnabledHint: "启用 lazy senior dev 规则和可编辑的托管 skills。", ponytailMode: "Ponytail 强度", diff --git a/packages/mtmharness/src/features/coding/config.json b/packages/mtmharness/src/features/coding/config.json new file mode 100644 index 0000000..4276065 --- /dev/null +++ b/packages/mtmharness/src/features/coding/config.json @@ -0,0 +1,102 @@ +{ + "packages": [ + { + "id": "codebase-memory", + "label": "Codebase Memory", + "description": "Graph-first code discovery and repository context.", + "icon": "search", + "kind": "runtime-backed" + }, + { + "id": "modern-go", + "label": "Modern Go Guidelines", + "description": "Version-specific guidance for modern Go code.", + "icon": "code", + "kind": "data-only", + "prompt": { + "order": 89, + "text": "Modern Go cache policy for DSH workspace-write shells: /home/code may be read-only. Before invoking the pinned wrapper, keep every write inside the current workspace. Linux/macOS: `cache=\"$PWD/.mtmharness-cache\"; mkdir -p \"$cache\"; env HOME=\"$cache/home\" XDG_CACHE_HOME=\"$cache/cache\" GOPATH=\"$cache/gopath\" GOMODCACHE=\"$cache/gopath/pkg/mod\" GOCACHE=\"$cache/go-build\" GOTOOLCHAIN=local sh \"/scripts/run-tool.sh\" ...`. PowerShell: `$cache = Join-Path (Get-Location) \".mtmharness-cache\"; New-Item -ItemType Directory -Force $cache | Out-Null; $env:HOME = Join-Path $cache \"home\"; $env:XDG_CACHE_HOME = Join-Path $cache \"cache\"; $env:GOPATH = Join-Path $cache \"gopath\"; $env:GOMODCACHE = Join-Path $env:GOPATH \"pkg/mod\"; $env:GOCACHE = Join-Path $cache \"go-build\"; $env:LOCALAPPDATA = Join-Path $cache \"localappdata\"; $env:GOTOOLCHAIN = \"local\"; & \"\\scripts\\run-tool.ps1\" ...`. Do not use the default HOME or request elevated sandbox access just for Go caches." + }, + "skills": { + "repository": "https://github.com/JetBrains/go-modern-guidelines", + "revision": "937827b86eeff98f45150ce00236bc34a30ed20f", + "files": [ + { + "name": "use-modern-go", + "path": "plugin/skills/use-modern-go/SKILL.md", + "sha256": "333346bebb212d7acded062cdeb1f5a01e421ebdab976c4c4fcb02cca31508f7" + }, + { + "name": "use-modern-go", + "path": "plugin/skills/use-modern-go/scripts/VERSION", + "sha256": "d0b4f9120ba026c00fa23cb84b4e1620a2e6436592e58155a5151653179572c0" + }, + { + "name": "use-modern-go", + "path": "plugin/skills/use-modern-go/scripts/run-tool.ps1", + "sha256": "7bd7bd8c8394e9cdbd290251b143e54639e527d52a1099135f1ba91edb5a677b" + }, + { + "name": "use-modern-go", + "path": "plugin/skills/use-modern-go/scripts/run-tool.sh", + "sha256": "a593b2484834fc7d068e7cbe1a09f92b149ed61b9a07556ea50790d95f50a25b" + } + ] + } + }, + { + "id": "ponytail", + "label": "Ponytail", + "description": "Minimal coding rules and over-engineering reviews.", + "icon": "code", + "kind": "runtime-backed", + "skills": { + "repository": "https://github.com/dietrichgebert/ponytail", + "revision": "0a4dd63ad4541f4f655c4108a295916f3c1d8fda", + "files": [ + { + "name": "ponytail", + "path": "skills/ponytail/SKILL.md", + "sha256": "1316a2f3f95741d2300b116fe0c2d81ce4a9568656ed0a62643f54aaf09957f2" + }, + { + "name": "ponytail-audit", + "path": "skills/ponytail-audit/SKILL.md", + "sha256": "5560b8e383dbe2ddfddc873a1e2bf2e586e23e0cd7d995537482b2315331f6d1" + }, + { + "name": "ponytail-debt", + "path": "skills/ponytail-debt/SKILL.md", + "sha256": "c84fba75f0ca12bfe83f9a78ea02fd125c5dd3f1fbb18124105a489937f284e6" + }, + { + "name": "ponytail-gain", + "path": "skills/ponytail-gain/SKILL.md", + "sha256": "24e01d1c9715cb136ba1c4f1e52a95940c0193558b876828e537736480d6408b" + }, + { + "name": "ponytail-help", + "path": "skills/ponytail-help/SKILL.md", + "sha256": "2264d1615117b02b0fd5a69ec84cd2757006471a78e4d6c22eed6d581c1d37a4" + }, + { + "name": "ponytail-review", + "path": "skills/ponytail-review/SKILL.md", + "sha256": "40df33b58fc6ef889b93585733feb9566b76e9586efa7f376785c1e995197ac0" + } + ] + } + }, + { + "id": "rtk", + "label": "RTK", + "description": "Reduced shell output with transparent Bash rewriting.", + "icon": "terminal", + "kind": "runtime-backed", + "prompt": { + "order": 109, + "text": "RTK only concerns Bash shell commands. DSH read, grep, glob, PowerShell, and persistent terminal calls are not covered by this integration.\nRTK failures and unsupported commands pass through; RTK_DISABLED=1 disables one command." + } + } + ] +} diff --git a/packages/mtmharness/src/features/coding/index.ts b/packages/mtmharness/src/features/coding/index.ts index 48ba39d..ee5bf32 100644 --- a/packages/mtmharness/src/features/coding/index.ts +++ b/packages/mtmharness/src/features/coding/index.ts @@ -2,7 +2,7 @@ import type { Context, Fiber } from "@deepseek-ai/cordis"; import type {} from "@deepseek-ai/dsh-settings"; import { apply as applyCodebaseMemory, type Config as CodebaseMemoryConfig } from "./codebase-memory.js"; import { apply as applyPonytail } from "./ponytail.js"; -import { applyManifestPackage, MTM_CODING_PACKAGES } from "./manifest.js"; +import { applyDataOnlyPackages } from "./manifest.js"; import { apply as applyRtk } from "./rtk.js"; import { MtmCodingSettingsSchema, @@ -13,7 +13,7 @@ import { export { buildMcpConfig, resolveConfig } from "./codebase-memory.js"; export { MtmCodingSettingsSchema, codebaseMemoryConfig } from "./types.js"; -export { MTM_CODING_PACKAGES } from "./manifest.js"; +export { MTM_CODING_PACKAGES, codingPackage } from "./manifest.js"; export type { MtmCodingPackageCatalog, MtmCodingPackageKind, MtmCodingPackageManifest, MtmCodingSkillSource } from "./manifest.js"; export { extractHookContext, @@ -36,12 +36,10 @@ const CodebaseMemoryFeature = { apply: applyCodebaseMemory, }; -const ModernGoFeature = { - name: "mtm-coding-modern-go", - inject: ["skills"], - apply: async (ctx: Context): Promise => { - await applyManifestPackage(ctx, MTM_CODING_PACKAGES.modernGo); - }, +const DataOnlyFeature = { + name: "mtm-coding-data-only", + inject: ["skills", "systemPrompt"], + apply: applyDataOnlyPackages, }; const PonytailFeature = { @@ -81,12 +79,6 @@ function ponytailKey(settings: MtmCodingSettings): string { }); } -function modernGoKey(settings: MtmCodingSettings): string { - return jsonKey({ - enabled: settings.modernGoEnabled, - }); -} - function rtkKey(settings: MtmCodingSettings): string { return jsonKey({ mode: settings.rtkMode, @@ -101,11 +93,9 @@ export async function apply(ctx: Context, rawConfig: MtmCodingConfig = {}): Prom { base: rawConfig }, ); let codebaseMemoryFiber: MountedFiber | undefined; - let modernGoFiber: MountedFiber | undefined; let ponytailFiber: MountedFiber | undefined; let rtkFiber: MountedFiber | undefined; let activeCodebaseKey = ""; - let activeModernGoKey = ""; let activePonytailKey = ""; let activeRtkKey = ""; let reconciling = Promise.resolve(); @@ -113,7 +103,6 @@ export async function apply(ctx: Context, rawConfig: MtmCodingConfig = {}): Prom const reconcile = async (next: MtmCodingSettings): Promise => { const nextCodebaseKey = codebaseKey(next); - const nextModernGoKey = modernGoKey(next); const nextPonytailKey = ponytailKey(next); const nextRtkKey = rtkKey(next); @@ -136,22 +125,6 @@ export async function apply(ctx: Context, rawConfig: MtmCodingConfig = {}): Prom } } - if (nextModernGoKey !== activeModernGoKey || (next.modernGoEnabled && modernGoFiber === undefined)) { - await dispose(modernGoFiber); - modernGoFiber = undefined; - activeModernGoKey = ""; - if (next.modernGoEnabled) { - try { - modernGoFiber = await ctx.plugin(ModernGoFeature); - activeModernGoKey = nextModernGoKey; - } catch (error) { - ctx.logger.warn("mtm-coding: Modern Go is unavailable: " + String(error)); - } - } else { - activeModernGoKey = nextModernGoKey; - } - } - if (nextPonytailKey !== activePonytailKey || (next.ponytailEnabled && ponytailFiber === undefined)) { await dispose(ponytailFiber); ponytailFiber = undefined; @@ -207,5 +180,6 @@ export async function apply(ctx: Context, rawConfig: MtmCodingConfig = {}): Prom await reconciling; }, "mtm-coding.lifecycle"); + if (typeof ctx.plugin === "function") await ctx.plugin(DataOnlyFeature); await queueReconcile(); } diff --git a/packages/mtmharness/src/features/coding/manifest.ts b/packages/mtmharness/src/features/coding/manifest.ts index 11bbfb9..0f9e92c 100644 --- a/packages/mtmharness/src/features/coding/manifest.ts +++ b/packages/mtmharness/src/features/coding/manifest.ts @@ -1,6 +1,6 @@ import type { Context } from "@deepseek-ai/cordis"; import { applyFileSkills, ensureSkillPackage } from "../../skill-files.js"; -import packageCatalog from "./packages.json" with { type: "json" }; +import packageCatalog from "./config.json" with { type: "json" }; export type MtmCodingPackageKind = "data-only" | "runtime-backed"; @@ -32,10 +32,7 @@ export interface MtmCodingPackageManifest { } export interface MtmCodingPackageCatalog { - readonly codebaseMemory: MtmCodingPackageManifest; - readonly modernGo: MtmCodingPackageManifest & { readonly skills: MtmCodingSkillSource }; - readonly ponytail: MtmCodingPackageManifest & { readonly skills: MtmCodingSkillSource }; - readonly rtk: MtmCodingPackageManifest; + readonly packages: readonly MtmCodingPackageManifest[]; } const ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u; @@ -105,10 +102,13 @@ function assertManifest(value: unknown, label: string): asserts value is MtmCodi } function assertCatalog(value: unknown): asserts value is MtmCodingPackageCatalog { - if (!isRecord(value)) throw new Error("mtm-coding: package catalog is invalid"); - for (const key of ["codebaseMemory", "modernGo", "ponytail", "rtk"]) assertManifest(value[key], key); - const catalog = value as unknown as MtmCodingPackageCatalog; - if (catalog.modernGo.skills === undefined || catalog.ponytail.skills === undefined) throw new Error("mtm-coding: package catalog skill metadata is incomplete"); + if (!isRecord(value) || !Array.isArray(value.packages) || value.packages.length === 0) throw new Error("mtm-coding: package catalog is invalid"); + const ids = new Set(); + for (const packageManifest of value.packages) { + assertManifest(packageManifest, "package"); + if (ids.has(packageManifest.id)) throw new Error("mtm-coding: duplicate package id " + packageManifest.id); + ids.add(packageManifest.id); + } } assertCatalog(packageCatalog); @@ -116,6 +116,24 @@ assertCatalog(packageCatalog); /** Trusted release metadata; mutable enabled state remains in mtm-coding settings. */ export const MTM_CODING_PACKAGES: MtmCodingPackageCatalog = packageCatalog; +export function codingPackage(id: string): MtmCodingPackageManifest { + const packageManifest = MTM_CODING_PACKAGES.packages.find((item) => item.id === id); + if (packageManifest === undefined) throw new Error("mtm-coding: package is not configured: " + id); + return packageManifest; +} + +/** Install every configured data-only package with one shared lifecycle. */ +export async function applyDataOnlyPackages(ctx: Context): Promise { + for (const packageManifest of MTM_CODING_PACKAGES.packages) { + if (packageManifest.kind !== "data-only") continue; + try { + await applyManifestPackage(ctx, packageManifest); + } catch { + // The package-specific warning is emitted by applyManifestPackage; other packages remain usable. + } + } +} + /** Install and mount one package's external skills, then its static prompt. */ export async function applyManifestPackage(ctx: Context, packageManifest: MtmCodingPackageManifest): Promise { if (packageManifest.skills !== undefined) { diff --git a/packages/mtmharness/src/features/coding/packages.json b/packages/mtmharness/src/features/coding/packages.json deleted file mode 100644 index 8ee2a01..0000000 --- a/packages/mtmharness/src/features/coding/packages.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "codebaseMemory": { - "id": "codebase-memory", - "label": "Codebase Memory", - "description": "Graph-first code discovery and repository context.", - "icon": "search", - "kind": "runtime-backed" - }, - "modernGo": { - "id": "modern-go", - "label": "Modern Go Guidelines", - "description": "Version-specific guidance for modern Go code.", - "icon": "code", - "kind": "data-only", - "prompt": { - "order": 89, - "text": "Modern Go cache policy for DSH workspace-write shells: /home/code may be read-only. Before invoking the pinned wrapper, keep every write inside the current workspace. Linux/macOS: `cache=\"$PWD/.mtmharness-cache\"; mkdir -p \"$cache\"; env HOME=\"$cache/home\" XDG_CACHE_HOME=\"$cache/cache\" GOPATH=\"$cache/gopath\" GOMODCACHE=\"$cache/gopath/pkg/mod\" GOCACHE=\"$cache/go-build\" GOTOOLCHAIN=local sh \"/scripts/run-tool.sh\" ...`. PowerShell: `$cache = Join-Path (Get-Location) \".mtmharness-cache\"; New-Item -ItemType Directory -Force $cache | Out-Null; $env:HOME = Join-Path $cache \"home\"; $env:XDG_CACHE_HOME = Join-Path $cache \"cache\"; $env:GOPATH = Join-Path $cache \"gopath\"; $env:GOMODCACHE = Join-Path $env:GOPATH \"pkg/mod\"; $env:GOCACHE = Join-Path $cache \"go-build\"; $env:LOCALAPPDATA = Join-Path $cache \"localappdata\"; $env:GOTOOLCHAIN = \"local\"; & \"\\scripts\\run-tool.ps1\" ...`. Do not use the default HOME or request elevated sandbox access just for Go caches." - }, - "skills": { - "repository": "https://github.com/JetBrains/go-modern-guidelines", - "revision": "937827b86eeff98f45150ce00236bc34a30ed20f", - "files": [ - { - "name": "use-modern-go", - "path": "plugin/skills/use-modern-go/SKILL.md", - "sha256": "333346bebb212d7acded062cdeb1f5a01e421ebdab976c4c4fcb02cca31508f7" - }, - { - "name": "use-modern-go", - "path": "plugin/skills/use-modern-go/scripts/VERSION", - "sha256": "d0b4f9120ba026c00fa23cb84b4e1620a2e6436592e58155a5151653179572c0" - }, - { - "name": "use-modern-go", - "path": "plugin/skills/use-modern-go/scripts/run-tool.ps1", - "sha256": "7bd7bd8c8394e9cdbd290251b143e54639e527d52a1099135f1ba91edb5a677b" - }, - { - "name": "use-modern-go", - "path": "plugin/skills/use-modern-go/scripts/run-tool.sh", - "sha256": "a593b2484834fc7d068e7cbe1a09f92b149ed61b9a07556ea50790d95f50a25b" - } - ] - } - }, - "ponytail": { - "id": "ponytail", - "label": "Ponytail", - "description": "Minimal coding rules and over-engineering reviews.", - "icon": "code", - "kind": "runtime-backed", - "skills": { - "repository": "https://github.com/dietrichgebert/ponytail", - "revision": "0a4dd63ad4541f4f655c4108a295916f3c1d8fda", - "files": [ - { - "name": "ponytail", - "path": "skills/ponytail/SKILL.md", - "sha256": "1316a2f3f95741d2300b116fe0c2d81ce4a9568656ed0a62643f54aaf09957f2" - }, - { - "name": "ponytail-audit", - "path": "skills/ponytail-audit/SKILL.md", - "sha256": "5560b8e383dbe2ddfddc873a1e2bf2e586e23e0cd7d995537482b2315331f6d1" - }, - { - "name": "ponytail-debt", - "path": "skills/ponytail-debt/SKILL.md", - "sha256": "c84fba75f0ca12bfe83f9a78ea02fd125c5dd3f1fbb18124105a489937f284e6" - }, - { - "name": "ponytail-gain", - "path": "skills/ponytail-gain/SKILL.md", - "sha256": "24e01d1c9715cb136ba1c4f1e52a95940c0193558b876828e537736480d6408b" - }, - { - "name": "ponytail-help", - "path": "skills/ponytail-help/SKILL.md", - "sha256": "2264d1615117b02b0fd5a69ec84cd2757006471a78e4d6c22eed6d581c1d37a4" - }, - { - "name": "ponytail-review", - "path": "skills/ponytail-review/SKILL.md", - "sha256": "40df33b58fc6ef889b93585733feb9566b76e9586efa7f376785c1e995197ac0" - } - ] - } - }, - "rtk": { - "id": "rtk", - "label": "RTK", - "description": "Reduced shell output with transparent Bash rewriting.", - "icon": "terminal", - "kind": "runtime-backed", - "prompt": { - "order": 109, - "text": "RTK only concerns Bash shell commands. DSH read, grep, glob, PowerShell, and persistent terminal calls are not covered by this integration.\nRTK failures and unsupported commands pass through; RTK_DISABLED=1 disables one command." - } - } -} diff --git a/packages/mtmharness/src/features/coding/ponytail.ts b/packages/mtmharness/src/features/coding/ponytail.ts index 257ff5d..ac190b4 100644 --- a/packages/mtmharness/src/features/coding/ponytail.ts +++ b/packages/mtmharness/src/features/coding/ponytail.ts @@ -3,7 +3,7 @@ import type { Agent } from "@deepseek-ai/dsh-agent"; import type { AssembleContext } from "@deepseek-ai/dsh-system-prompt"; import { isModelInvocable, type SkillDefinition } from "@deepseek-ai/dsh-skill"; import type { CommandResult } from "@deepseek-ai/dsh-commands"; -import { applyManifestPackage, MTM_CODING_PACKAGES } from "./manifest.js"; +import { applyManifestPackage, codingPackage } from "./manifest.js"; import type { PonytailMode } from "./types.js"; export const name = "mtm-coding-ponytail"; @@ -58,7 +58,7 @@ export async function apply(ctx: Context, config: { mode?: PonytailMode; applyToSubagents?: boolean; } = {}): Promise { - await applyManifestPackage(ctx, MTM_CODING_PACKAGES.ponytail); + await applyManifestPackage(ctx, codingPackage("ponytail")); const defaultMode = normalizeMode(config.mode); const applyToSubagents = config.applyToSubagents ?? true; const states = new WeakMap(); diff --git a/packages/mtmharness/src/features/coding/rtk.ts b/packages/mtmharness/src/features/coding/rtk.ts index 0e9d0cb..0029ce5 100644 --- a/packages/mtmharness/src/features/coding/rtk.ts +++ b/packages/mtmharness/src/features/coding/rtk.ts @@ -2,7 +2,7 @@ import type { Context } from "@deepseek-ai/cordis"; import { createUserMessage } from "@deepseek-ai/dsh-llm"; import type { AssembleContext } from "@deepseek-ai/dsh-system-prompt"; import type { CommandResult } from "@deepseek-ai/dsh-commands"; -import { applyManifestPackage, MTM_CODING_PACKAGES } from "./manifest.js"; +import { applyManifestPackage, codingPackage } from "./manifest.js"; import type { RtkMode } from "./types.js"; export const name = "mtm-coding-rtk"; @@ -42,7 +42,7 @@ function prompt(status: RtkStatus): string { export async function apply(ctx: Context, config: { mode?: RtkMode } = {}): Promise { const requested = config.mode ?? "auto"; if (requested === "off") return; - await applyManifestPackage(ctx, MTM_CODING_PACKAGES.rtk); + await applyManifestPackage(ctx, codingPackage("rtk")); const status: RtkStatus = requested === "rewrite" ? "unavailable" : "guidance"; ctx.systemPrompt.section({ name: "mtm-coding:rtk:status", diff --git a/packages/mtmharness/src/features/coding/types.ts b/packages/mtmharness/src/features/coding/types.ts index 2123571..fd68dba 100644 --- a/packages/mtmharness/src/features/coding/types.ts +++ b/packages/mtmharness/src/features/coding/types.ts @@ -8,7 +8,6 @@ export interface MtmCodingSettings { codebaseMemoryEnabled: boolean; dynamicCanvasEnabled: boolean; codebaseMemoryAugmentHooks: boolean; - modernGoEnabled: boolean; ponytailEnabled: boolean; ponytailMode: PonytailMode; ponytailSubagents: boolean; @@ -41,7 +40,6 @@ export const MtmCodingSettingsSchema: z = z.object({ codebaseMemoryEnabled: z.boolean().default(true), dynamicCanvasEnabled: z.boolean().default(false), codebaseMemoryAugmentHooks: z.boolean().default(true), - modernGoEnabled: z.boolean().default(true), ponytailEnabled: z.boolean().default(true), ponytailMode: z.union(["off", "lite", "full", "ultra"] as const).default("full"), ponytailSubagents: z.boolean().default(true), diff --git a/packages/mtmharness/src/index.ts b/packages/mtmharness/src/index.ts index ba998e9..6027444 100644 --- a/packages/mtmharness/src/index.ts +++ b/packages/mtmharness/src/index.ts @@ -5,7 +5,7 @@ import { apply as applyCodingHost } from "./features/coding/index.ts"; import { apply as applyMtmConnectSettings } from "./features/mtm-connect/index.ts"; import { apply as applyUpdateHost } from "./features/update/index.ts"; -export { buildMcpConfig, resolveConfig, MTM_CODING_PACKAGES } from "./features/coding/index.ts"; +export { buildMcpConfig, codingPackage, resolveConfig, MTM_CODING_PACKAGES } from "./features/coding/index.ts"; export type { MtmCodingPackageCatalog, MtmCodingPackageKind, MtmCodingPackageManifest, MtmCodingSkillSource } from "./features/coding/manifest.ts"; export { extractHookContext, diff --git a/packages/mtmharness/tests/coding-controller.test.mjs b/packages/mtmharness/tests/coding-controller.test.mjs index b6e1255..be23d76 100644 --- a/packages/mtmharness/tests/coding-controller.test.mjs +++ b/packages/mtmharness/tests/coding-controller.test.mjs @@ -6,7 +6,6 @@ import { runInNewContext } from "node:vm"; const base = { codebaseMemoryEnabled: true, codebaseMemoryAugmentHooks: true, - modernGoEnabled: true, ponytailEnabled: true, ponytailMode: "full", ponytailSubagents: true, diff --git a/packages/mtmharness/tests/coding-plugin.test.mjs b/packages/mtmharness/tests/coding-plugin.test.mjs index 8b3dd67..716b272 100644 --- a/packages/mtmharness/tests/coding-plugin.test.mjs +++ b/packages/mtmharness/tests/coding-plugin.test.mjs @@ -16,7 +16,6 @@ import { const DEFAULT_SETTINGS = { codebaseMemoryEnabled: true, codebaseMemoryAugmentHooks: true, - modernGoEnabled: true, ponytailEnabled: true, ponytailMode: "full", ponytailSubagents: true, @@ -46,7 +45,7 @@ function writeSkill(root, name, description, body) { } function manifestSkillNames() { - return Object.values(MTM_CODING_PACKAGES).flatMap((packageManifest) => + return MTM_CODING_PACKAGES.packages.flatMap((packageManifest) => packageManifest.skills?.files.map((file) => file.name) ?? []); } @@ -291,7 +290,6 @@ test("Codebase Memory does not preflight its lazy npx command", async () => { const fake = createContext({ codebaseMemoryEnabled: true, command: "", - modernGoEnabled: false, ponytailEnabled: false, rtkMode: "off", }); @@ -381,6 +379,7 @@ test("unified settings reconcile coding features and unregister the watcher", as assert.equal(name, "mtmharness"); const listedSkills = await fake.context.skills.list({}); assert.equal(listedSkills.length, 7); + assert.equal(MTM_CODING_PACKAGES.packages.filter((item) => item.kind === "data-only").length, 1); assert.equal(fake.skillProviders.length, 2); const modernGo = await fake.context.skills.get("use-modern-go"); assert.ok(modernGo); @@ -402,11 +401,6 @@ test("unified settings reconcile coding features and unregister the watcher", as assert.equal(fake.pluginCalls.filter((call) => call.config?.transport === "stdio").length, 0); await fake.trigger({ ...fake.getSettings(), codebaseMemoryEnabled: true }); assert.equal(fake.pluginCalls.filter((call) => call.config?.transport === "stdio").length, 1); - await fake.trigger({ ...fake.getSettings(), modernGoEnabled: false }); - assert.equal(fake.skillProviders.length, 1); - assert.equal((await fake.context.skills.list({})).some((skill) => skill.name === "use-modern-go"), false); - await fake.trigger({ ...fake.getSettings(), modernGoEnabled: true }); - assert.equal(fake.skillProviders.length, 2); await fake.trigger({ ...fake.getSettings(), rtkMode: "off" }); assert.equal(fake.skillProviders.length, 2); assert.equal(fake.sections.some((section) => section.name === "mtm-coding:rtk:prompt"), false); @@ -431,13 +425,7 @@ test("coding features do not duplicate manifest skills as commands", async () => assert.equal(fake.skillProviders.length, 0); }); -test("Modern Go can be disabled and uses the pinned Go module command", async () => { - const disabled = createContext({ modernGoEnabled: false }); - await applyCoding(disabled.context, {}); - assert.equal((await disabled.context.skills.list({})).some((skill) => skill.name === "use-modern-go"), false); - await disabled.dispose(); - assert.equal(disabled.skillProviders.length, 0); - +test("data-only manifest packages mount without package-specific feature code", async () => { const enabled = createContext(); await applyCoding(enabled.context, {}); const skill = await enabled.context.skills.get("use-modern-go"); diff --git a/packages/mtmharness/tests/packed-install.test.mjs b/packages/mtmharness/tests/packed-install.test.mjs index 9cd61dc..49e353b 100644 --- a/packages/mtmharness/tests/packed-install.test.mjs +++ b/packages/mtmharness/tests/packed-install.test.mjs @@ -149,18 +149,19 @@ test("packed mtmharness discovers skills from its editable DSH home", async () = const installedPackage = resolve(installRoot, "node_modules", "mtmharness"); const installed = await import(pathToFileURL(join(installedPackage, "lib/index.js")).href); - assert.deepEqual(installed.MTM_CODING_PACKAGES.modernGo.skills.files.map((file) => file.path), [ + const modernGo = installed.MTM_CODING_PACKAGES.packages.find((item) => item.id === "modern-go"); + assert.ok(modernGo); + assert.deepEqual(modernGo.skills.files.map((file) => file.path), [ "plugin/skills/use-modern-go/SKILL.md", "plugin/skills/use-modern-go/scripts/VERSION", "plugin/skills/use-modern-go/scripts/run-tool.ps1", "plugin/skills/use-modern-go/scripts/run-tool.sh", ]); - assert.match(installed.MTM_CODING_PACKAGES.modernGo.prompt.text, /HOME=.*XDG_CACHE_HOME=.*GOPATH=.*GOMODCACHE=.*GOCACHE/s); + assert.match(modernGo.prompt.text, /HOME=.*XDG_CACHE_HOME=.*GOPATH=.*GOMODCACHE=.*GOCACHE/s); const fake = createContext(); await installed.applyCoding(fake.context, { codebaseMemoryEnabled: false, - modernGoEnabled: true, ponytailEnabled: false, rtkMode: "off", });