* feat(compat): merge same-name subcommands under shared parent
When multiple server entries attach to the same parent and their cli.command
collides with a subcommand already in the parent tree, the incoming subcommand's
children are merged recursively into the existing one instead of producing a
duplicate sibling. Leaf-name collisions resolve first-wins.
Fixes the duplicate `group` / `message` rows in `dws chat --help` that surfaced
when bot capabilities were distributed across chat.group.members and
chat.message subtrees.
* feat(ir): carry FlagOverlay and ToolAnnotations into canonical catalog
FlagOverlay mirrors the per-parameter CLI overlay (alias, transform,
transformArgs, env default, default value, hidden) sourced from
market.CLIToolOverride.Flags.
ToolAnnotations mirrors MCP 2025+ tool annotations with nullable hints
(destructive / read-only / idempotent / open-world) so absence means
"unknown" rather than "false".
BuildCatalog now propagates both through the canonical layer, with tests
covering group metadata and flag-overlay passthrough.
* feat(output): add -f pretty for schema-aware colored output
dws schema now supports -f pretty alongside json / raw. Pretty mode
partitions output by product / tool / parameter / enum and applies ANSI
color so humans can browse the catalog without piping through jq.
json / raw behaviour is unchanged.
docs/reference.md gains a Schema Introspection section documenting
dws schema and the new pretty format.
* feat(skills): refresh 13-product references; add devdoc / drive / oa
SKILL.md cli_version bumped to >=1.0.15; product index reordered to match
the v1.0.15 command surface.
New references:
- devdoc.md: Open Platform documentation search
- drive.md: DingTalk drive (promoted out of Coming soon)
- oa.md: OA approval end-to-end flow
Major rewrites for aitable (dashboard / chart share, import/export,
attachment flow), chat (bot capabilities merged into chat.message and
chat.group.members), calendar (event suggest / attachments), doc (comment
subtree, file create, upload/download flow), and minutes (list
mine/shared/all unified, record subcommands split).
Minor alignment touch-ups for contact / report / todo flag names.
* docs(release): sync v1.0.15 notes — 159 commands across 13 products
README Key Services fully refreshed: chat 23, calendar 14, aitable 41,
doc 21, minutes 19; drive promoted out of Coming soon; workbench and
standalone bot rows removed; Quick Start expanded to 7 examples spanning
doc / minutes / drive; Coming soon trimmed to mail / conference / aiapp /
live / wiki.
Adds docs/command-index.md, an auto-generated English listing of all 159
runtime commands with description and when-to-use guidance aimed at AI
agents. Replaces the ad-hoc command-index.pre.* / command-index.full.*
snapshots used during development.
CHANGELOG 1.0.15 records the compat subcommand-merging feature, the new
command index, and a flag-naming cleanup across chat / calendar / drive /
minutes / contact / devdoc.
Address reviewer feedback on #125:
- Hoist a single *cache.Store above the discovery fan-out so all HTTP and
stdio goroutines share one instance rather than each spawning its own in
registerHTTPServer/registerStdioServer. Atomic tmp+rename per cache key
keeps concurrent writers on distinct keys collision-free; global runtime
registries (AppendDynamicServer, RegisterStdioClient) already guard
themselves with a mutex. Added a comment pointing at those invariants.
- Relax cold-path timeouts to survive healthy cross-region endpoints and
Python/Node interpreter warm-up: HTTP 500ms → 1s (plain), 700ms → 1.5s
(auth), stdio 1s → 2s. A new DWS_PLUGIN_COLD_TIMEOUT env var overrides
all three with a single duration, registered in configmeta so it shows
up in `dws config --help`.
- Add internal/app/plugin_discovery_concurrency_test.go covering the
thread-safety claims end-to-end: 16 parallel SaveTools with distinct
keys, 32 parallel AppendDynamicServer, 32 parallel RegisterStdioClient,
plus TestResolvePluginColdTimeouts exercising defaults, a valid
override, an unparseable value, and a non-positive value. All pass with
go test -race.
Verified: go build ./... clean; targeted -race tests PASS in 1.5s;
./internal/{app,cli,cache,plugin}/... all green.
Issue #119: an unreachable third-party MCP plugin (e.g. blocked/firewalled
endpoint) blocks `dws --help` for ~10s on every CLI invocation, because
plugin discovery happens eagerly during command-tree construction.
Root cause was a stack of timeouts that multiplied under transport failure:
* `transport.Client.Initialize` loops three supported protocol versions on
every error — including dial timeouts and HTTP 5xx — even though those
failure modes are protocol-version-independent. Three loops × the dial
budget = 3× the worst-case startup cost.
* Default `DialContext.Timeout` was 10s, so a single dial against an
unroutable address (e.g. TEST-NET-1) burned the full plugin context.
* `registerHTTPServer` granted plugins with `AuthHeaders` a 10s outer
context (intended for slow third-party services), and `registerStdioServer`
granted every stdio plugin 10s. Either single misbehaving plugin therefore
stalled the entire CLI.
* Registry-side `defaultDiscoveryTimeout` (10s) and `perServerDiscoveryTimeout`
(5s) had the same shape on the LoadCatalog path.
Changes:
* `transport.Client.Initialize`: short-circuit when the underlying
`*CallError.Stage` is anything other than `CallStageJSONRPC`. Protocol
version negotiation is the only justification for retrying with another
version; transport/HTTP failures fail identically and should surrender.
* `transport.defaultTransport`: `DialContext.Timeout` 10s → 3s.
* `app.registerHTTPServer`: AuthHeaders timeout 10s → 4s.
* `app.registerStdioServer`: outer ctx timeout 10s → 4s.
* `cli.defaultDiscoveryTimeout`: 10s → 4s.
* `discovery.Service`: rename `perServerDiscoveryTimeout` 5s → 2s
(`defaultPerServerDiscoveryTimeout`); add `Service.PerServerTimeout`
override field for tests/callers needing a tighter or looser bound.
Tests:
* `TestInitializeShortCircuitsOnHTTPError` — asserts only ONE protocol
version is attempted on HTTP 5xx.
* `TestInitializeShortCircuitsOnDialFailure` — asserts Initialize returns
in <2s on a refused-connection address.
* Existing `TestInitializeNegotiatesProtocolVersion` continues to pass —
JSON-RPC-stage errors still trigger version fallback.
End-to-end measurement with a blackhole plugin
(endpoint=192.0.2.1, AuthHeaders set):
| Variant | median `dws --help` |
| ------------- | ------------------- |
| main | 10.2s |
| PR #121 alone | 9.1s |
| this branch | 3.72s (-63%) |
The `loader.go` and `discovery/service.go` timeout reductions overlap with
PR #121 by @utafrali; see PR description for attribution.
Refs: #119
Supersedes: #121
EnvironmentLoader.Load() previously returned (empty Catalog, nil) on all
failure paths, making it impossible for callers to distinguish "no services
available" from "discovery failed due to auth/network issues".
Changes:
- Add CatalogDegraded error type with three reasons: unauthenticated,
market_unreachable, runtime_all_failed
- Add auth pre-check: return DegradedUnauthenticated immediately when no
token is available, avoiding doomed MCP connections
- Schema command now handles CatalogDegraded gracefully: outputs hint to
stderr and includes degraded/reason/hint fields in JSON output
- Runner preserves graceful degradation by ignoring CatalogDegraded errors
- Skip empty-products cache to prevent stale cache from masking auth errors
- Edition-aware hint text (open-source vs embedded/wukong)
Made-with: Cursor
- Support schema output in table format for human-readable display
- Add product-level endpoint loading in CLI loader
- Wire schema-text rendering into runner output pipeline
- Add priority field support for todo task helper
- Build tool → endpoint mapping from CLI tools and overrides
- directRuntimeEndpoint resolves by tool name before product fallback
- Support dynamic command routing with compat layer updates
- Add StdinGuard for safe single-read stdin across multiple flags
- Add ResolveInputSource unified resolver for @file, @-, and pipe
- Support @file / @- syntax for all string-typed override flags
- Remove unused writeSchemaText function
- Add stdin.go with ReadStdinIfPiped and ReadFileArg helpers
- Support @file syntax for --json flag to read payload from file
- Fall back to stdin pipe when no --json argument is provided