* fix(install): add .hermes/skills to AGENT_DIRS (#188)
dws 安装后未自动复制 skill 到 .hermes/skills/。在 4 份硬编码的
AGENT_DIRS 清单(build/npm/install.js、scripts/install.sh、
scripts/install.ps1、scripts/install-skills.sh)末尾追加
.hermes/skills,与现有'父目录守卫'逻辑天然兼容:
- 已安装 Hermes 的用户:自动获得 ~/.hermes/skills/dws
- 未安装 Hermes 的用户:父目录不存在则跳过,零副作用
Closes#188
* fix(install): cover remaining 4 AGENT_DIRS sources for .hermes/skills (review feedback)
Address review feedback from #221: the AGENT_DIRS list actually has 8 sources
in the repo, not 4. Without this commit, dws upgrade users and Homebrew users
would still NOT get .hermes/skills/dws populated even after the previous
4-script fix landed.
Fixes (functional):
- internal/upgrade/paths.go (knownSkillDirs):
Append .hermes/skills so 'dws upgrade' refreshes ~/.hermes/skills/dws/
for users who have Hermes installed.
- build/homebrew.rb.tmpl (post_install targets):
Append .hermes/skills/dws so brew users also get the skill copied to
~/.hermes/skills/dws on post_install.
Fixes (test / smoke coverage):
- test/scripts/package_script_test.go (expectedPackagedSkillTargets):
Append .hermes/skills/dws so future regressions (e.g. someone removes
.hermes from install.js) are caught by CI.
- scripts/release/verify-package-managers.sh (HOME_AGENT_PARENTS / HOME_SKILL_TARGETS):
Append .hermes to both lists so the release-time npm/brew smoke test
actually asserts ~/.hermes/skills/dws/SKILL.md exists.
Documentation:
- internal/upgrade/paths.go: expanded the 'Kept in sync with' comment from
a single file reference (build/npm/install.js) to all 7 in-sync sources,
so future maintainers don't have to rediscover this list.
Verification:
- go vet ./internal/upgrade/... ./test/scripts/... PASS
- go build ./... PASS
- sh -n scripts/install.sh PASS
- sh -n scripts/install-skills.sh PASS
- sh -n scripts/release/verify-package-managers.sh PASS
- node --check build/npm/install.js PASS
---------
Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
* fix(attendance): add --stats-type flag to summary to fix C0002 error
The MCP tool get_attendance_summary requires statsType in QueryUserAttendVO
at the server-side business layer (DingTalk schema marks it required:[] but
service rejects with C0002 'statistics type error' when omitted).
Changes:
- internal/helpers/attendance.go: add --stats-type flag (week/month);
conditionally write statsType into QueryUserAttendVO when provided;
update Long description and flag help to mark --stats-type as required
- test/cli_compat/attendance_test.go: add execSummaryDryRun helper and
3 new tests verifying statsType is correctly threaded through VO
- skills/references/products/attendance.md: document --stats-type flag
with warning about server-side mandatory enforcement
Verification:
- go build ./... PASS
- go vet ./internal/helpers/... ./test/cli_compat/... PASS
- go test -run TestAttendanceSummary_should_pass_stats_type_* PASS (3/3)
- go test -run TestAttendanceSummary_should_not_pass_stats_type_* PASS
- Real CLI: dws attendance summary --stats-type month success:true
(C0002 fully gone; full attendance data returned)
Closes#227
* fix(attendance): enforce --stats-type as required at CLI layer (review feedback)
Address review feedback from #228:
1. Fail-fast validation: --stats-type is now required at the CLI layer
instead of being conditionally written into VO. Previously the doc
said 'required' but RunE silently let omission through, causing the
same C0002 the fix was supposed to prevent.
2. Enum validation: only 'week' or 'month' are accepted; any other value
is rejected at the CLI layer instead of being forwarded to the server.
3. Cosmetic: split the single-line Long description into multiple lines
for readability.
Test changes:
- Replaced TestAttendanceSummary_should_not_pass_stats_type_when_omitted
with two semantically-correct tests:
* TestAttendanceSummary_should_error_when_stats_type_missing
* TestAttendanceSummary_should_error_when_stats_type_invalid
- Updated other TestAttendanceSummary_* tests to pass --stats-type=month
to match the new fail-fast contract.
- Renamed should_pass_only_user_flag to should_pass_user_id_through_vo
(the old name no longer makes sense now that --stats-type is mandatory).
Skill doc:
- skills/references/products/attendance.md: clarified that the CLI now
rejects missing/invalid --stats-type at the client side (won't even
reach the server).
Verification:
- go build ./... PASS
- go vet ./internal/helpers/... ./test/cli_compat/ PASS
- go test -run 'TestAttendanceSummary_should_(pass_stats_type|error_when_stats_type)' -v
PASS (4/4)
- dws attendance summary --help new layout shown
---------
Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
When two MCP servers register tools with the same name (e.g. both drive
and doc have 'create_folder'), the tool-level endpoint map uses
last-writer-wins, causing invocations to route to the wrong server.
Fix: swap Priority 1 (product-level) and Priority 2 (tool-level) in
directRuntimeEndpoint so that when the caller already knows the
productID, the product endpoint is authoritative. Tool-level lookup
remains as a fallback for cases where productID is empty.
This fixes 'dws drive mkdir' and 'dws drive download' being silently
routed to the doc MCP server instead of the drive MCP server.
Closes#219
Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
PR #184 merge conflict resolution inadvertently introduced a conditional
branch that preserved stale clientID from previous login sessions instead
of always re-fetching from MCP server. This caused exchangeCode() to use
direct DingTalk API mode (which requires clientSecret) instead of MCP
proxy mode, resulting in 'clientId或者clientSecret错误' errors.
This commit restores the original PR #157 logic: both DeviceFlowProvider
and OAuthProvider unconditionally call resetCredentialState() followed by
FetchClientIDFromMCP() + SetClientIDFromMCP(), ensuring exchangeCode()
always uses the MCP proxy path regardless of prior login state.
Root cause: the conditional branch treated any non-empty clientID (from
runtimeClientID or app.json) as a user-provided --client-id flag value,
skipping MCP re-fetch and leaving clientIDFromMCP=false after reset.
Affected scenarios:
- OAuth login → device flow login
- Any login → --force login
- New terminal with existing app.json → device flow login
Fixes regression introduced in PR #184 (commit 46192d6).
Restores fix from PR #157 (commit ad33a46).
Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
The --keyword flag was renamed to --query for contact user search,
contact dept search, and devdoc article search (as noted in CHANGELOG).
This commit updates all documentation references for the contact
commands. devdoc will be addressed in a follow-up PR.
Refs #105
Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
The todo task get command was calling query_todo_detail which returns
empty results. Changed to get_todo_detail to match the tool name
defined in discovery.json, restoring correct behavior.
Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>
Device flow now always clears stale credential state and re-fetches
clientID from MCP server, regardless of what previous login methods
(OAuth scan, PAT, etc.) left in app.json or runtime globals.
This replaces the previous Source-field approach with a simpler
defensive reset that is future-proof against new login methods.
Root cause: after OAuth login saved app.json without MCP source marker,
subsequent --device login reused the stale clientID without setting
clientIDFromMCP flag, causing exchangeCode() to use direct mode which
requires clientSecret.
Changes:
- Add resetCredentialState() to DeviceFlowProvider
- Login() always calls resetCredentialState() + FetchClientIDFromMCP()
- Remove AppConfig.Source field (no longer needed)
- Remove app_config_source_test.go (replaced by device_flow_reset_test.go)
- Add 5 tests covering OAuth→device, legacy→device, direct→device scenarios
Co-authored-by: 猷诺 <bicheng.ybc@alibaba-inc.com>