refactor(tool-fs): drop per-tool subpath plugins; adopt single-tsconfig build

Adapt the four fs packages to master's single-tsconfig build convention
(lib/types outDir + types path + files allowlist), brought in by the merge.

While doing so, drop dsh-tool-fs's /read//write//edit subpath plugins. They
were the only subpath-export package in the tree and forced bespoke tsdown,
tsconfig path, package.json files, and workspace-constraint handling that no
sibling tool package (e.g. dsh-tool-bash) carries, for a focused-deployment
use case no consumer needed. dsh-tool-fs is now a single root plugin that
registers read/write/edit, mirroring dsh-tool-bash; the per-tool registration
helpers stay internal modules the root composes. The file-context event-gate
RFC is amended to record the narrowed scope.
This commit is contained in:
Dudu-0223
2026-06-28 17:43:29 +08:00
parent 89b27ad442
commit 4a1177093a
16 changed files with 34 additions and 189 deletions

View File

@@ -49,7 +49,7 @@ The filesystem seam uses the same dependency direction as the bash trio:
`@deepseek-ai/dsh-tool-fs` depends on `@deepseek-ai/dsh-fs`, `@deepseek-ai/dsh-tools`, `@deepseek-ai/dsh-system-prompt`, and `cordis`. It registers model-facing tools and prompt sections. It must not import `node:fs`, `node:path`, or `@deepseek-ai/dsh-fs-local`; filesystem execution always goes through `ctx.fs`. If the implementation needs concrete agent or session helper types, those dependencies belong in `tool-fs`; they must not leak back into `dsh-fs`.
The root `tool-fs` plugin registers the full filesystem tool suite by composing the per-tool registration helpers (`read`, `write`, and `edit`). The same helpers are exposed as subpath plugins such as `@deepseek-ai/dsh-tool-fs/read`, `@deepseek-ai/dsh-tool-fs/write`, and `@deepseek-ai/dsh-tool-fs/edit` for focused deployments. Root and subpath plugins follow the same rule: they inject `fs` and never import an implementation package.
The root `tool-fs` plugin registers the full filesystem tool suite (`read`, `write`, and `edit`) by composing the per-tool registration helpers. It injects `fs` and never imports an implementation package.
## `ctx.fs` contract
@@ -117,7 +117,7 @@ The tool package must keep model-facing contracts stable when backends change. A
The first implementation requires a prior full `read` before updating an existing file with `write` or `edit`. `tool-fs` does not implement this by checking whether a tool named `read` ran or by reading the file-state cache. It passes the current execution context to `ctx.fs`, and `ctx.fs` derives the file-state owner and enforces file-state/stale-version policy. Creating a new file with `write` does not require prior state or an owner.
The root plugin registers the full suite by composing the per-tool registration helpers. The subpath plugins register one tool each for focused deployments and tests. Both forms inject `fs`, `tools`, and `systemPrompt`.
The root plugin registers the full suite by composing the per-tool registration helpers. It injects `fs`, `tools`, and `systemPrompt`.
## Migration plan
@@ -128,7 +128,7 @@ This RFC starts from `origin/master`, where no filesystem tool package exists ye
3. Add `packages/fs/tool-fs` with the model-facing `read`, `write`, and `edit` tools over `ctx.fs`.
4. Update `docs/architecture.md`, `packages/README.md`, package READMEs, build/typecheck config, and aggregate maintenance scripts such as `scripts/publint-all.ts`.
This first pass does not add a separate `@deepseek-ai/dsh-file-context` package. The file-state store lives behind `ctx.fs` so root and subpath `tool-fs` plugins share the same read-before-write/edit policy automatically.
This first pass does not add a separate `@deepseek-ai/dsh-file-context` package. The file-state store lives behind `ctx.fs` so the `tool-fs` plugin gets the read-before-write/edit policy automatically.
Example leaf configs stay bash-only in this landing. Wiring `examples/coding-agent` or `examples/acp-agent` to `dsh-fs-local` + `dsh-tool-fs` changes the model prompt, visible tool schemas, and ACP snapshot transcript, so it should land as a follow-up UX/example change with prompt and snapshot updates in the same PR.
@@ -156,7 +156,7 @@ Beyond the happy/sad paths above, `dsh-fs-local` tests must cover the defensive-
- **Concurrency / stale races.** The RFC names edit as race-prone (see Risks). Test that two concurrent write/edit operations against the same target settle deterministically: one succeeds and the other is rejected with `FS_STALE_VERSION` rather than silently overwriting, and that a successful edit refreshes recorded state so an immediately-following edit by the same owner proceeds.
- **HMR safety and disposal.** `dsh-fs-local` registers `ctx.fs` and owns the in-memory file-state store, so it needs its own HMR-safety test (register the backend on a fiber, dispose it, assert the `ctx.fs` provider is withdrawn and the file-state store is released — a later provider starts with no inherited state).
`dsh-tool-fs` tests cover the consumer surface with a fake `ctx.fs` implementation. They should verify tool schemas, argument validation, prompt-section registration, formatting of successful results, propagation of backend `FsError` codes into `isError` tool results through `ctx.tools.execute()`, that read/write/edit pass the current execution context or structural projection through to `ctx.fs`, root-plugin suite registration, subpath plugin registration, and HMR cleanup.
`dsh-tool-fs` tests cover the consumer surface with a fake `ctx.fs` implementation. They should verify tool schemas, argument validation, prompt-section registration, formatting of successful results, propagation of backend `FsError` codes into `isError` tool results through `ctx.tools.execute()`, that read/write/edit pass the current execution context or structural projection through to `ctx.fs`, root-plugin suite registration, and HMR cleanup.
Integration tests should load `dsh-fs-local` plus `dsh-tool-fs` and execute `read`, `write`, and `edit` through `ctx.tools.execute()` to prove the three packages work together without bypassing the tool registry. They must verify the world, not the tool's self-report: after a `write`/`edit`, read the file back from disk and assert byte-identical content (and that untouched files are unchanged), rather than trusting the returned `ContentBlock[]`. Each integration/e2e test owns its resources — create the harness in the test, run against a per-test temporary directory, and dispose the harness and remove the directory in `afterEach` even on failure or timeout.

View File

@@ -112,9 +112,9 @@ The `fs/*` decision events are **unbound waterfalls dispatched by the tool** (li
The tool keeps its model-facing schemas (`read`/`write`/`edit`, byte-for-byte unchanged) and prompt sections. The prompt guidance stays policy-first because a deployment loading the fs tools is expected to also load `dsh-file-context`: the model is still told to read before overwriting or editing, and any wording that says the "backend" requires that should be corrected to say the file-context policy requires it. The bare-provider fallback does not change the prompt stance.
`dsh-tool-fs` gains the executor responsibilities relocated from the old `fileContext` method service, including **read windowing** (`window.ts`, `READ_MAX_BYTES`, `READ_MAX_LINE_LENGTH`, `FileReadRequest`/`FileReadOutcome`/`FileTextLine`, `STREAM_MIN_SIZE`), which is the tool's rendering detail now that the tool owns the read. Those read-windowing types and helpers move into `dsh-tool-fs`; the policy plugin must not remain a type dependency for the tool.
`dsh-tool-fs` gains the executor responsibilities relocated from the old `fileContext` method service, including **read windowing** (`window.ts`, `READ_MAX_BYTES`, `READ_MAX_LINE_LENGTH`, `FileReadOutcome`/`FileTextLine`, `STREAM_MIN_SIZE`), which is the tool's rendering detail now that the tool owns the read. Those read-windowing types and helpers move into `dsh-tool-fs`; the policy plugin must not remain a type dependency for the tool.
`dsh-tool-fs` exposes each tool as a first-class **subpath plugin** (`/read`, `/write`, `/edit`) for focused deployments, plus a root plugin that composes all three. The `inject` change applies to **all four**: each of `read.ts`, `write.ts`, `edit.ts`, and `index.ts` drops `fileContext` from `inject` and adds `fs` (keeping `tools`/`systemPrompt`). Updating only the root plugin would leave a focused deployment that loads just `@deepseek-ai/dsh-tool-fs/edit` still coupled to the old method service, silently breaking the decoupling contract for exactly the deployments subpaths exist to serve.
`dsh-tool-fs` is a single root plugin that registers all three tools (`read`/`write`/`edit`), mirroring `dsh-tool-bash`. It injects `fs` (plus `tools`/`systemPrompt`), never `fileContext`. (The original proposal also exposed each tool as a `/read`/`/write`/`/edit` subpath plugin for focused deployments; that was dropped on implementation — no consumer needed a single-tool deployment, and the subpath publishing forced bespoke `tsdown`/`tsconfig`/`files`/workspace-constraint handling no sibling tool package carries. The per-tool registration helpers (`applyReadTool`/`applyWriteTool`/`applyEditTool`) remain internal modules the root plugin composes.)
`stat` budget is minimized by letting the waterfall produce the expectation lazily — the bare default returns `undefined` (no guard) and never stats:
@@ -154,10 +154,10 @@ This amends — does not reverse — [the split-fs-seam RFC](../simplification/2
## Acceptance Criteria
- All four `dsh-tool-fs` injection points — the root plugin AND the `/read`, `/write`, `/edit` subpath plugins — inject `fs` (+ `tools`/`systemPrompt`), not `fileContext`; each calls `ctx.fs` directly and dispatches the `fs/write-expectation`/`fs/edit-expectation` waterfalls (passing `exec` as the actor) and the contained `fs/observed` emit. Read windowing lives in `dsh-tool-fs`.
- The `dsh-tool-fs` root plugin injects `fs` (+ `tools`/`systemPrompt`), not `fileContext`; it calls `ctx.fs` directly and dispatches the `fs/write-expectation`/`fs/edit-expectation` waterfalls (passing `exec` as the actor) and the contained `fs/observed` emit. Read windowing lives in `dsh-tool-fs`. (No subpath plugins — see the Tool contract above.)
- `dsh-fs` declares the three events with `@mode` tags and an opaque `object` actor argument (no agent/session structure leaks into the provider vocabulary); the generated cordis catalog is regenerated.
- `dsh-file-context` is a plugin, not a service: it does not register `ctx.fileContext`, has no public `read`/`write`/`edit`/`resolve` methods, and does not inject `fs`; it registers the three listeners, keeps observed-state, and has HMR/disposal coverage (dispose the fiber, assert the gate no longer rewrites).
- **Bare-provider test**: a config WITHOUT `dsh-file-context` that loads a **subpath plugin** (e.g. just `@deepseek-ai/dsh-tool-fs/edit`, plus `/read`/`/write` as the scenario needs) boots, and `read`/`write`(create AND overwrite)/`edit` work through `dsh-tool-fs` against the real `dsh-fs-local`; an `edit` of an unread existing file and an overwrite of an existing unread file both succeed (unconditional bare-provider behavior), proving the subpath plugins — not just the root — carry no `fileContext` dependency. A bare-provider edit of a missing target reports `FS_STALE_VERSION`. With `dsh-file-context` present, the same unread `edit` is rejected `FS_NOT_OBSERVED` and the same unread overwrite uses `createIfAbsent` (rejected on an existing file).
- **Bare-provider test**: a config WITHOUT `dsh-file-context` boots the `dsh-tool-fs` root plugin, and `read`/`write`(create AND overwrite)/`edit` work against the real `dsh-fs-local`; an `edit` of an unread existing file and an overwrite of an existing unread file both succeed (unconditional bare-provider behavior), proving the tool carries no `fileContext` dependency. A bare-provider edit of a missing target reports `FS_STALE_VERSION`. With `dsh-file-context` present, the same unread `edit` is rejected `FS_NOT_OBSERVED` and the same unread overwrite uses `createIfAbsent` (rejected on an existing file).
- **Single-slot semantics**: a test registers a second `fs/edit-expectation` listener AFTER `dsh-file-context` and asserts it is NOT reached (first-wins short-circuit), and documents in a comment that a decider registered before/`prepend`ed would instead win — the slot is first-wins by convention, not an enforced invariant.
- **Contained observed recording**: a test with a synchronously throwing `fs/observed` listener performs a write/edit and asserts the tool result is still success (the completed mutation is not turned into an `isError`). The event contract requires synchronous side-effect-only listeners; the try/catch is the synchronous backstop, not async rejection handling.
- `dsh-fs` `writeText`/`editText` make `expected` optional (omit ⇒ unconditional); the `FsWriteExpectation` union is unchanged, and `dsh-file-context`'s guarded paths (`createIfAbsent`/`replaceIfVersion`/`{ version }`) behave exactly as today. A bare-provider test exercises an unconditional overwrite, an unconditional edit, and a missing-target edit reporting `FS_STALE_VERSION`.

View File

@@ -100,7 +100,7 @@ The following are deliberately out of scope for the first filesystem schema pass
- `edit` requires `file_path`, `old_string`, and `new_string`, accepts optional boolean `replace_all`, rejects empty `old_string`, and defaults `replace_all` to false.
- The registered JSON schemas use the snake_case field names in this RFC.
- The tool descriptions accurately describe that existing-file `write` and `edit` require a prior full read in the same execution context, while new-file `write` does not.
- The root plugin and subpath plugins register the same schemas.
- The `tool-fs` root plugin registers all three schemas.
Integration tests should execute `read`, `write`, and `edit` through `ctx.tools.execute()` with a fake or local `ctx.fs` provider and verify that model arguments are translated into the expected `ctx.fs` calls.

View File

@@ -15,7 +15,9 @@
"./package.json": "./package.json"
},
"files": [
"lib",
"lib/index.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
],
"license": "BSD-3-Clause",

View File

@@ -15,7 +15,9 @@
"./package.json": "./package.json"
},
"files": [
"lib",
"lib/index.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
],
"license": "BSD-3-Clause",

View File

@@ -15,7 +15,9 @@
"./package.json": "./package.json"
},
"files": [
"lib",
"lib/index.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
],
"license": "BSD-3-Clause",

View File

@@ -11,14 +11,6 @@ await ctx.plugin(ToolFs) // this package — re
`@deepseek-ai/dsh-file-context` is **optional**: omit it and the tools run against the bare provider (unconditional write/overwrite/edit, no observed-state). A deployment that loads these tools is expected to also load it, so the behavior is read-before-write/edit.
Each tool also ships as a subpath plugin for focused deployments (each injects `fs`, not a policy service):
```ts ignore-check
import * as readPlugin from '@deepseek-ai/dsh-tool-fs/read'
import * as writePlugin from '@deepseek-ai/dsh-tool-fs/write'
import * as editPlugin from '@deepseek-ai/dsh-tool-fs/edit'
```
## Tools (schemas per [the filesystem tool schemas RFC](../../../docs/rfc/implemented/feature/2026-06-17-filesystem-tool-schemas.md))
| Tool | Arguments | Behavior |

View File

@@ -11,23 +11,13 @@
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./read": {
"types": "./lib/types/read.d.ts",
"default": "./lib/read.js"
},
"./write": {
"types": "./lib/types/write.d.ts",
"default": "./lib/write.js"
},
"./edit": {
"types": "./lib/types/edit.d.ts",
"default": "./lib/edit.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": [
"lib",
"lib/index.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
],
"license": "BSD-3-Clause",

View File

@@ -10,7 +10,7 @@
* tool stats ZERO times either way; a missing target is reported by the provider
* as `FS_STALE_VERSION`.
*
* @module @deepseek-ai/dsh-tool-fs/edit
* @module @deepseek-ai/dsh-tool-fs/src/edit
*/
import type { Context } from 'cordis'
@@ -50,7 +50,7 @@ export function formatEditOutput(displayPath: string, outcome: FsEditOutcome): s
}
/** Register the `edit` tool and its system-prompt guidance. */
export function apply(ctx: Context): void {
export function applyEditTool(ctx: Context): void {
ctx.systemPrompt.section({
name: 'tool:edit',
order: 102,
@@ -84,12 +84,3 @@ export function apply(ctx: Context): void {
},
}))
}
/** Cordis plugin name used by loader diagnostics. */
export const name = 'fs-edit'
/** Services required by the `edit` tool plugin. */
export const inject = ['tools', 'fs', 'systemPrompt']
/** Named helper for direct registration in the root plugin and tests. */
export const applyEditTool = apply

View File

@@ -1,9 +1,6 @@
/**
* The model-facing filesystem tool suite (`read`, `write`, `edit`) over the
* `ctx.fs` provider seam. This root plugin registers all three tools by
* composing the per-tool registration helpers; each tool is also exposed as a
* subpath plugin (`@deepseek-ai/dsh-tool-fs/read`, `/write`, `/edit`) for focused
* deployments.
* `ctx.fs` provider seam. This single plugin registers all three tools.
*
* ## The tool is the executor; policy is an event gate
*

View File

@@ -8,7 +8,7 @@
* the model-facing schema, argument validation, read windowing, and result
* formatting; the freshness/observation policy is not its concern.
*
* @module @deepseek-ai/dsh-tool-fs/read
* @module @deepseek-ai/dsh-tool-fs/src/read
*/
import type { Context } from 'cordis'
@@ -72,7 +72,7 @@ ${body}
}
/** Register the `read` tool and its system-prompt guidance. */
export function apply(ctx: Context): void {
export function applyReadTool(ctx: Context): void {
ctx.systemPrompt.section({
name: 'tool:read',
order: 100,
@@ -119,12 +119,3 @@ export function apply(ctx: Context): void {
},
}))
}
/** Cordis plugin name used by loader diagnostics. */
export const name = 'fs-read'
/** Services required by the `read` tool plugin. */
export const inject = ['tools', 'fs', 'systemPrompt']
/** Named helper for direct registration in the root plugin and tests. */
export const applyReadTool = apply

View File

@@ -8,7 +8,7 @@
* returns `createIfAbsent`/`replaceIfVersion` instead. The tool stats ZERO
* times either way.
*
* @module @deepseek-ai/dsh-tool-fs/write
* @module @deepseek-ai/dsh-tool-fs/src/write
*/
import type { Context } from 'cordis'
@@ -36,7 +36,7 @@ ${verb} file
}
/** Register the `write` tool and its system-prompt guidance. */
export function apply(ctx: Context): void {
export function applyWriteTool(ctx: Context): void {
ctx.systemPrompt.section({
name: 'tool:write',
order: 101,
@@ -62,12 +62,3 @@ export function apply(ctx: Context): void {
},
}))
}
/** Cordis plugin name used by loader diagnostics. */
export const name = 'fs-write'
/** Services required by the `write` tool plugin. */
export const inject = ['tools', 'fs', 'systemPrompt']
/** Named helper for direct registration in the root plugin and tests. */
export const applyWriteTool = apply

View File

@@ -5,10 +5,9 @@
*
* - DEFAULT — with the real `dsh-file-context` policy gate plugin: read-before-
* write/edit, version-guarded mutation, FS_NOT_OBSERVED for unread edits.
* - BARE — WITHOUT the policy plugin, loading only SUBPATH plugins: every
* `fs/*` waterfall falls through to its undefined default, so write/edit are
* unconditional. This proves the subpaths (not just the root) carry no policy
* dependency.
* - BARE — WITHOUT the policy plugin: every `fs/*` waterfall falls through to
* its undefined default, so write/edit are unconditional. This proves the
* tool carries no dependency on the policy plugin.
*
* These verify the WORLD — files are read back from disk and asserted
* byte-for-byte — not the tool's self-report.
@@ -25,9 +24,6 @@ import ToolRegistry from '@deepseek-ai/dsh-tools'
import { LocalFileSystem } from '@deepseek-ai/dsh-fs-local'
import * as FileContext from '@deepseek-ai/dsh-file-context'
import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
import * as readPlugin from '@deepseek-ai/dsh-tool-fs/read'
import * as writePlugin from '@deepseek-ai/dsh-tool-fs/write'
import * as editPlugin from '@deepseek-ai/dsh-tool-fs/edit'
let dir: string
let ctx: Context
@@ -243,18 +239,16 @@ describe('default deployment (with dsh-file-context)', () => {
})
// --------------------------------------------------------------------------
// BARE deployment: SUBPATH plugins only, NO policy gate.
// BARE deployment: the tool suite WITHOUT the policy gate.
// --------------------------------------------------------------------------
describe('bare provider (subpath plugins, no dsh-file-context)', () => {
describe('bare provider (no dsh-file-context)', () => {
beforeEach(async () => {
dir = await mkdtemp(join(tmpdir(), 'dsh-tool-fs-bare-'))
ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(LocalFileSystem, { cwd: dir })
await ctx.plugin(readPlugin)
await ctx.plugin(writePlugin)
fiber = await ctx.plugin(editPlugin)
fiber = await ctx.plugin(ToolFs)
})
it('read works (it never needed policy)', async () => {

View File

@@ -1,83 +0,0 @@
/**
* Tests for the per-tool subpath plugins (`@deepseek-ai/dsh-tool-fs/read`,
* `/write`, `/edit`): each registers exactly one tool, injects the same services
* (`tools`, `fs`, `systemPrompt`) — NOT a policy service — and cleans up on
* disposal. They boot over the bare `ctx.fs` provider with NO
* `@deepseek-ai/dsh-file-context`, proving each subpath carries no policy-plugin
* dependency.
*/
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import { FileSystem, FsTargetKey, FsVersion } from '@deepseek-ai/dsh-fs'
import type {
FsEditOutcome,
FsInfo,
FsTarget,
FsWriteOutcome,
} from '@deepseek-ai/dsh-fs'
import * as readPlugin from '@deepseek-ai/dsh-tool-fs/read'
import * as writePlugin from '@deepseek-ai/dsh-tool-fs/write'
import * as editPlugin from '@deepseek-ai/dsh-tool-fs/edit'
class StubFs extends FileSystem {
override async resolve(path: string): Promise<FsTarget> {
return { inputPath: path, targetKey: FsTargetKey(path), displayPath: path }
}
override async stat(): Promise<FsInfo | undefined> {
return { version: FsVersion('v'), type: 'file', size: 0 }
}
override async readText(): Promise<string> {
return ''
}
override async streamText(): Promise<AsyncIterable<string>> {
return (async function* () { yield '' })()
}
override async writeText(): Promise<FsWriteOutcome> {
return { operation: 'create', version: FsVersion('v') }
}
override async editText(): Promise<FsEditOutcome> {
return { replacements: 1, replaceAll: false, version: FsVersion('v') }
}
}
async function base() {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(StubFs)
return ctx
}
describe('subpath plugins', () => {
it('each registers exactly its one tool (over the bare provider, no policy plugin)', async () => {
const cases: Array<[unknown, string]> = [
[readPlugin, 'read'],
[writePlugin, 'write'],
[editPlugin, 'edit'],
]
for (const [plugin, toolName] of cases) {
const ctx = await base()
await ctx.plugin(plugin as Parameters<Context['plugin']>[0])
expect(ctx.tools.schemas().map(s => s.name)).toEqual([toolName])
}
})
it('cleans up on disposal (HMR safety)', async () => {
const ctx = await base()
const fiber = await ctx.plugin(readPlugin as Parameters<Context['plugin']>[0])
expect(ctx.tools.schemas()).toHaveLength(1)
await fiber.dispose()
expect(ctx.tools.schemas()).toHaveLength(0)
})
it('stays pending without a ctx.fs provider', async () => {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(writePlugin as Parameters<Context['plugin']>[0])
expect(ctx.tools.schemas()).toHaveLength(0)
})
})

View File

@@ -1,21 +0,0 @@
import { defineConfig } from 'tsdown'
/**
* tool-fs exposes one package root plus one entry per tool plugin, so each tool
* can be loaded or replaced independently as a subpath plugin
* (`@deepseek-ai/dsh-tool-fs/read`, `/write`, `/edit`). The root tsdown builds
* only `lib/types/index.js`, so this override adds the per-tool entries. tsdown
* reads the emitted JS under `lib/types` (from `tsc -b`); declarations come from
* `tsc -b` too (dts: false), matching every package.
*/
export default defineConfig({
entry: ['lib/types/index.js', 'lib/types/read.js', 'lib/types/write.js', 'lib/types/edit.js'],
outDir: 'lib',
format: ['esm'],
platform: 'node',
target: 'es2024',
fixedExtension: false,
dts: false,
clean: false,
})

View File

@@ -34,9 +34,6 @@
"@cordisjs/plugin-timer": ["./vendor/timer/src"],
"@cordisjs/plugin-hmr": ["./vendor/hmr/src"],
"@cordisjs/plugin-logger-console": ["./vendor/logger-console/src"],
"@deepseek-ai/dsh-tool-fs/read": ["./packages/fs/tool-fs/src/read.ts"],
"@deepseek-ai/dsh-tool-fs/write": ["./packages/fs/tool-fs/src/write.ts"],
"@deepseek-ai/dsh-tool-fs/edit": ["./packages/fs/tool-fs/src/edit.ts"],
// One wildcard maps every @deepseek-ai/dsh-<name> to its source. Package
// dir names are unique across groups, so first-on-disk-wins resolution is
// unambiguous; adding a package under an existing group needs no edit