refactor: detect md hard-wraps via mdast AST, not regex

Per review feedback (use a real markdown parser with an AST linked to
source positions), rewrite verify-md-wrap to parse each file with
mdast-util-from-markdown (the CommonMark parser behind remark) + the GFM
extension, then flag any `paragraph` node whose source span covers more
than one line.

Why a parser over the hand-rolled line scanner:
- It is a checker, not a formatter — it reports and never rewrites, so
  zero cosmetic churn (no emphasis-marker or table-delimiter
  normalization, which is why Prettier was rejected for this).
- The AST owns every structural exemption (fenced code of any fence
  length, tables, lists, blockquotes, HTML, headings, reference defs),
  fixing both bugs the regex version had: it now catches wrapped
  list-item / blockquote prose (a `paragraph` inside those nodes) and no
  longer false-positives on a longer ```` fence wrapping an inner ```.

Also unwrap two pre-existing hard-wrapped blockquotes (architecture.md,
adding-a-tool.md) that the stricter AST check correctly surfaced.
This commit is contained in:
Tianyi Cui
2026-06-16 23:35:33 +08:00
parent 67447fcdc3
commit 63425a2b87
5 changed files with 570 additions and 122 deletions

View File

@@ -61,17 +61,7 @@ Swappable capabilities are split into **three packages** so each part evolves in
The LLM seam has the same topology folded differently: `dsh-llm` carries the interface (`LlmAdapter`) AND the consumer surface (`ctx.llm.stream()`), with adapters as implementation packages — there the consumer is the loop itself, not a swappable schema surface. Use the full three-package split when the consumer is independently replaceable; keep interface + consumer together when they are one concern. Don't split preemptively: a capability with one conceivable implementation and one consumer stays one package until proven otherwise.
> **"Capability" — two unrelated meanings.** (1) The *seam pattern* above
> ("one plugin provides a capability, another needs it") is realized by
> plain Cordis **services + `inject`**: a provider registers a service
> (`ctx.bash`, declared in `interface Context`); a consumer declares
> `inject: ['bash']` and its fiber stays pending until the service exists,
> tearing down via HMR if it later vanishes. No extra library is needed.
> (2) `@cordisjs/plugin-capability` is a different axis entirely — a
> **permission/capability-security** service (named permissions with
> inheritance/dependency, tested against a session via `ctx.capability.test`).
> It is a candidate for the deferred permissions/sandbox work (the
> `tools/execute` veto seam), NOT a mechanism for swapping implementations.
> **"Capability" — two unrelated meanings.** (1) The *seam pattern* above ("one plugin provides a capability, another needs it") is realized by plain Cordis **services + `inject`**: a provider registers a service (`ctx.bash`, declared in `interface Context`); a consumer declares `inject: ['bash']` and its fiber stays pending until the service exists, tearing down via HMR if it later vanishes. No extra library is needed. (2) `@cordisjs/plugin-capability` is a different axis entirely — a **permission/capability-security** service (named permissions with inheritance/dependency, tested against a session via `ctx.capability.test`). It is a candidate for the deferred permissions/sandbox work (the `tools/execute` veto seam), NOT a mechanism for swapping implementations.
## The vocabulary (dsh-llm)

View File

@@ -42,9 +42,7 @@ Registration is effect-based: disposing the plugin fiber unregisters the tool (w
Follow tool-bash's background pattern: a `run_in_background` flag returns a task id immediately; companion tools poll incrementally and kill; completion notices arrive via `agent.inject()`. Bound buffers and spill full output to disk so nothing is silently lost.
> TODO: each tool reimplements this background pattern by hand today. At some
> point we need a generic long-running-tool layer that handles task ids,
> incremental polling, kill, and completion notices uniformly.
> TODO: each tool reimplements this background pattern by hand today. At some point we need a generic long-running-tool layer that handles task ids, incremental polling, kill, and completion notices uniformly.
## Permissions / sandboxing