Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
340 changes: 62 additions & 278 deletions AGENTS.md

Large diffs are not rendered by default.

169 changes: 76 additions & 93 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,112 +1,95 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This file provides guidance to Claude Code when working with code in this repository. It intentionally stays closely aligned with `AGENTS.md`, which remains the general project-level agent entrypoint for this public repository.

## Project Overview
## Overview

DingTalk (钉钉) enterprise bot channel plugin for OpenClaw. Uses Stream mode (WebSocket, no public IP required). Published as `@soimy/dingtalk` on npm. The plugin runs directly via the OpenClaw runtime — there is no build step.
DingTalk (钉钉) enterprise bot channel plugin for OpenClaw using Stream mode (WebSocket, no public IP required). Published as `@soimy/dingtalk` on npm. The plugin runs directly via the OpenClaw runtime — there is no build step.

This repository is licensed under MIT. If you reuse code, retain the copyright and license notice required by MIT.
If you substantially reuse this repository's documentation, prompts, AGENTS/CLAUDE conventions, architecture writeups, or agent-oriented implementation playbooks, please provide attribution to `OpenClaw DingTalk Channel Plugin`, `YM Shen and contributors`, and `https://github.com/soimy/openclaw-channel-dingtalk`.
See `docs/contributor/citation-and-attribution.md` and `CITATION.cff` for the preferred citation and attribution format. This request describes the project's preferred community norm and does not replace or modify the LICENSE file.
Current architecture is modularized by responsibility. `src/channel.ts` is an assembly layer, heavy logic is split into dedicated modules, and the contributor architecture guides are the source of truth for module boundaries and incremental migration rules.

Start with `WORKFLOW.md` for the repository workflow summary, then use the contributor docs for detailed guidance.

## Commands
## Start Here

- `WORKFLOW.md`
- `docs/contributor/agent-workflow.md`
- `docs/contributor/architecture.zh-CN.md`
- `docs/contributor/architecture.en.md`
- `docs/contributor/testing.md`
- `docs/contributor/release-process.md`

## Quick Commands

```bash
# Install dependencies
pnpm install

# Type-check (strict TypeScript)
pnpm run type-check

# Lint (oxlint with type-aware rules)
pnpm run lint

# Lint + auto-fix + format
pnpm run lint:fix

# Format only (oxfmt)
pnpm run format

# Run all tests
pnpm test
pnpm test:coverage
pnpm run docs:build
```

# Run a single test file
pnpm vitest run tests/unit/config.test.ts
## Documentation Placement

# Run tests matching a pattern
pnpm vitest run -t "pattern"
- Specs: `docs/spec/`
- Plans: `docs/plans/`
- User-facing docs: `docs/user/`
- Contributor and process docs: `docs/contributor/`
- Release notes: `docs/releases/`

# Coverage report
pnpm test:coverage
Keep `README.md` as a concise project entry page. Do not expand it with long-form feature, configuration, troubleshooting, or process details that belong under `docs/`.

# Stream connection monitor (debugging)
pnpm run monitor:stream -- --duration 300 --summary-every 30 --probe-every 20
```
## Collaboration Conventions

- Prefer the GitHub issue templates under `.github/ISSUE_TEMPLATE/`.
- Keep issue communication primarily in Simplified Chinese.
- Use `问题反馈` for bugs and `功能建议` for feature ideas.
- Use an English Conventional-style pull request title.
- Write the pull request description in Simplified Chinese.
- Include clearly labeled `背景`, `目标`, `实现`, `实现 TODO`, and `验证 TODO` sections in pull requests.

## Architecture

### Entry Point

`index.ts` — registers the plugin with OpenClaw via `api.registerChannel()` and `api.registerGatewayMethod()` (for docs API). Sets the DingTalk runtime singleton.

### Core Module Responsibilities

- **`src/channel.ts`** — Assembly layer only. Defines `dingtalkPlugin` (config, gateway, outbound, status, security, messaging, directory). Delegates all heavy logic to service modules. Keep this file thin.
- **`src/inbound-handler.ts`** — Inbound pipeline orchestrator: dedup → self-filter → content extraction → authorization → session routing → media download → message context persistence → reply dispatch.
- **`src/send-service.ts`** — All outbound delivery: session webhook, proactive text/markdown, proactive media, unified `sendMessage` with card/markdown fallback.
- **`src/card-service.ts`** — AI Card state machine (PROCESSING → INPUTING → FINISHED/FAILED), card instance cache, createdAt fallback cache, recovery of unfinished cards on restart.
- **`src/message-context-store.ts`** — Unified short-TTL message persistence under namespace `messages.context`. The **only** production API for quote/media/card context recovery.
- **`src/reply-strategy.ts`** + `reply-strategy-card.ts` + `reply-strategy-markdown.ts` + `reply-strategy-with-reaction.ts` — Strategy pattern for reply delivery.
- **`src/connection-manager.ts`** — Robust stream reconnect lifecycle with exponential backoff, jitter, cycle limits, and warm reconnection (creates fresh DWClient to minimize message-loss window).
- **`src/config.ts`** — Config resolution, multi-account merging, path resolution. `getConfig()` is the canonical way to read DingTalk config.
- **`src/auth.ts`** — Access token cache with clientId-scoped caching and retry.
- **`src/targeting/`** — Learned group/user displayName directory, target normalization, displayNameResolution gate.

### Key Patterns

- **Multi-account support**: `channels.dingtalk.accounts` allows multiple DingTalk bots. Named accounts inherit channel-level defaults with account-level overrides via `mergeAccountWithDefaults`.
- **Card fallback**: If card streaming fails, card is marked FAILED and delivery falls back to markdown/text. Priority: no message loss over card rendering fidelity.
- **Dedup + inflight protection**: `dedup.processed-message`, `session.lock`, and `channel.inflight` are process-local memory-only state. Never introduce cross-process persistence for these.
- **Peer SDK**: Types and APIs come from `openclaw/plugin-sdk`. The `tsconfig.json` paths resolve this from either `../openclaw/src/plugin-sdk` or `../../src/plugin-sdk`.

### Planned Domain Directories

New code should align with these logical boundaries (physical moves are incremental):
- `gateway/` — stream lifecycle, callbacks, inbound entry
- `targeting/` — peer identity, session aliasing, target resolution
- `messaging/` — content parsing, reply strategies, outbound delivery, message context
- `card/` — AI card lifecycle, recovery, caches
- `command/` — slash commands, feedback learning
- `platform/` — config, auth, runtime, logger, types
- `shared/` — persistence primitives, dedup, cross-domain helpers

## Code Conventions

- TypeScript strict mode, ES2023 target, ESNext modules
- 2-space indentation (oxfmt), no tabs
- Formatting: `oxfmt`; Linting: `oxlint` with unicorn/typescript/oxc plugins
- Structured log prefixes: `[DingTalk]`, `[DingTalk][AICard]`, `[accountId]`
- DingTalk API error payloads: `[DingTalk][ErrorPayload][<scope>]` with `code=... message=... payload=...`
- Send APIs return `{ ok: boolean, error?: string }`
- Use `getAccessToken()` before every DingTalk API call
- Use `getLogger()` for logging, never `console.log`
- Never suppress type errors with `@ts-ignore`
- Never log raw access tokens

## Testing

- Vitest with V8 coverage; tests in `tests/unit/` and `tests/integration/`
- All network calls are mocked (`vi.mock`) — no real DingTalk API access in tests
- Unit tests for parser, config, auth, dedup, and service logic
- Integration tests when behavior crosses module boundaries (gateway start, inbound dispatch, send lifecycle, persistence migration)
- `clearMocks`, `restoreMocks`, `mockReset` are all enabled globally in vitest config
- When work involves DingTalk real-device validation, PR-scoped test checklist generation, `验证 TODO` drafting, or contributor guidance for that workflow, read and follow `skills/dingtalk-real-device-testing/SKILL.md` first.

## Important Anti-Patterns

- Do not add business logic to `src/channel.ts`
- Do not re-introduce legacy `quote-journal.ts` or `quoted-msg-cache.ts` wrappers — use `message-context-store.ts` directly
- Do not create multiple active AI Cards for the same `accountId:conversationId`
- Do not hardcode credentials — read from `channels.dingtalk` config
- Write review comments in Simplified Chinese (per `.github/instructions/code-review.instructions.md`)
## High-Priority Repository Rules

- Keep `src/channel.ts` thin; do not add new business logic there.
- Use `src/message-context-store.ts` directly for production quote, media, and card context recovery.
- Do not reintroduce legacy wrappers such as `quote-journal.ts` or `quoted-msg-cache.ts`.
- Use `getAccessToken()` before DingTalk API calls.
- Use `getLogger()` instead of `console.log`.
- Never log raw access tokens.
- Do not create multiple active AI Cards for the same `accountId:conversationId`.
- Keep review comments in Simplified Chinese.

## Architecture Pointers

- `index.ts` registers the plugin and sets the DingTalk runtime singleton.
- `src/inbound-handler.ts` owns inbound orchestration.
- `src/send-service.ts` owns outbound delivery.
- `src/card-service.ts` owns AI Card lifecycle and recovery.
- `src/message-context-store.ts` is the only production message context persistence API.
- `src/targeting/` owns learned target directory and target normalization.

## Optional Tooling

GitNexus is an optional enhancement for repository understanding, impact analysis, and change-scope review.

- If GitNexus is available locally, treat `docs/contributor/gitnexus-optional.md` as the preferred path for repository navigation and impact analysis.
- If GitNexus is unavailable locally, use `docs/contributor/fallback-navigation.md` together with `WORKFLOW.md` and `docs/contributor/agent-workflow.md`.
- Optional tooling must not be the only documented way to complete a required workflow step.
- See `docs/contributor/gitnexus-optional.md` for the enhanced workflow.

## Claude Code Notes

- Prefer dedicated Claude Code tools over shell equivalents when possible.
- Follow the repository workflow documents before editing.
- Keep changes scoped to the request.

## Attribution

This repository is licensed under MIT. If you reuse code, retain the copyright and license notice required by MIT.

If you substantially reuse this repository's documentation, prompts, AGENTS/CLAUDE conventions, architecture writeups, or agent-oriented implementation playbooks, please provide attribution to `OpenClaw DingTalk Channel Plugin`, `YM Shen and contributors`, and `https://github.com/soimy/openclaw-channel-dingtalk`.

See `docs/contributor/citation-and-attribution.md` and `CITATION.cff` for the preferred citation and attribution format. This request describes the project's preferred community norm and does not replace or modify the LICENSE file.
48 changes: 48 additions & 0 deletions WORKFLOW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Repository Workflow

This file is the repository-wide workflow summary for maintainers, contributors, and coding agents.

## Start Here

This file is the starting point. After reading it, continue in this order:

1. `docs/contributor/agent-workflow.md`
2. `docs/contributor/architecture.zh-CN.md` or `docs/contributor/architecture.en.md`
3. `docs/contributor/testing.md`
4. `docs/contributor/release-process.md` when preparing a release-related change

## Base Workflow

1. Understand the task and read the relevant files and docs.
2. Assess impact before editing by checking affected callers, imports, and execution paths.
3. Keep changes within the requested scope and follow the documented architecture boundaries.
4. Run validation that matches the change, including docs build for docs or workflow changes.
5. Summarize the changed scope, validation, and any follow-up before handoff.

## Detailed Guides

- Base contributor and agent workflow: `docs/contributor/agent-workflow.md`
- Architecture: `docs/contributor/architecture.zh-CN.md`
- English architecture guide: `docs/contributor/architecture.en.md`
- Testing and validation: `docs/contributor/testing.md`
- Release process: `docs/contributor/release-process.md`
- GitNexus-first navigation and impact workflow: `docs/contributor/gitnexus-optional.md`
- Manual fallback navigation without GitNexus: `docs/contributor/fallback-navigation.md`

## Documentation Placement

- Specs: `docs/spec/`
- Plans: `docs/plans/`
- User-facing docs: `docs/user/`
- Contributor and process docs: `docs/contributor/`
- Release notes: `docs/releases/`

Keep `README.md` concise. Long-form user, contributor, troubleshooting, and process documentation belongs under `docs/`.

## Optional Tooling

GitNexus is an optional enhancement for repository understanding, impact analysis, and change-scope review.

- If GitNexus is available locally, treat `docs/contributor/gitnexus-optional.md` as the preferred path for repository navigation and impact analysis.
- If GitNexus is not available locally, use `docs/contributor/fallback-navigation.md` together with the base workflow in this file and `docs/contributor/agent-workflow.md`.
- Optional tooling must never be the only documented way to complete a required engineering step.
6 changes: 5 additions & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default defineConfig({

const defaultImageRenderer =
md.renderer.rules.image ??
((tokens, idx, options, env, self) => self.renderToken(tokens, idx, options))
((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options))

md.renderer.rules.image = (tokens, idx, options, env, self) => {
const token = tokens[idx]
Expand Down Expand Up @@ -167,8 +167,12 @@ export default defineConfig({
items: [
{ text: '概览', link: '/contributor/' },
{ text: '仓库 TODO', link: '/contributor/todo' },
{ text: '贡献者与 Agent 工作流', link: '/contributor/agent-workflow' },
{ text: '本地开发', link: '/contributor/development' },
{ text: '测试与验证', link: '/contributor/testing' },
{ text: '发布流程', link: '/contributor/release-process' },
{ text: 'GitNexus 首选工作流', link: '/contributor/gitnexus-optional' },
{ text: '无 GitNexus 的手工导航', link: '/contributor/fallback-navigation' },
{ text: 'NPM 发布', link: '/contributor/npm-publish' },
{ text: '架构说明(中文详版)', link: '/contributor/architecture.zh-CN' },
{ text: 'Persistence API 使用指南', link: '/contributor/reference/persistence-api-usage.zh-CN' },
Expand Down
88 changes: 88 additions & 0 deletions docs/contributor/agent-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Contributor and Agent Workflow

This document defines the shared base workflow for contributors and coding agents in this repository. It must remain usable without optional local tools such as GitNexus.

## Purpose

Use this document as the authoritative workflow reference when changing code, docs, tests, or release-related assets in this repository.

## Core Principles

- Read before editing.
- Keep changes scoped to the request.
- Prefer existing architecture boundaries over ad hoc placement.
- Validate before claiming completion.
- Do not make optional tools the only documented path.

## Workflow

### 1. Understand the task

- Clarify the requested outcome before changing files.
- Read the relevant source files and supporting docs first.
- Use `docs/contributor/architecture.zh-CN.md` or `docs/contributor/architecture.en.md` as the source of truth for module boundaries.
- If the task produces design or implementation planning documents, write them to `docs/spec/` and `docs/plans/`.

### 2. Assess impact before editing

- Review direct callers, importers, and affected execution paths before changing a function, class, method, or public behavior.
- For refactors, renames, or broad workflow changes, inspect the surrounding modules and likely downstream consumers.
- If optional graph-aware tooling is available locally, prefer it for impact analysis. If not, perform the same reasoning manually by reading the code and tests.

### 3. Make the change

- Prefer editing existing files over creating new ones unless a new file clearly improves structure.
- Keep `src/channel.ts` as an assembly layer; do not move new business logic into it.
- Do not reintroduce legacy quote persistence wrappers such as `quote-journal.ts` or `quoted-msg-cache.ts`; use `src/message-context-store.ts` directly.
- Do not add unrelated refactors or speculative abstractions outside the requested scope.
- Follow the documented domain boundaries for gateway, targeting, messaging, card, command, platform, and shared logic.
- Preserve current user-visible delivery priorities, including card-to-markdown fallback when AI Card delivery fails.
- Keep repository-specific return shapes and structured logging conventions stable unless the task explicitly changes them.

### 4. Validate the change

- Run validation that matches the changed scope.
- For code changes, the typical baseline is `pnpm run type-check`, `pnpm run lint`, and relevant tests.
- For docs or workflow changes, also run `pnpm run docs:build`.
- If work affects DingTalk user-visible message behavior, follow the real-device testing guidance in `docs/contributor/testing.md` and use the dedicated real-device-testing skill when that workflow applies.

### 5. Prepare handoff

- Summarize what changed and why.
- State which validation steps were run.
- Call out any known limitations, follow-up work, or intentionally untested areas.

## Repository-Specific Rules

- Use `getAccessToken()` before DingTalk API calls.
- Use `getLogger()` instead of `console.log`.
- Never log raw access tokens.
- Do not create multiple active AI Cards for the same `accountId:conversationId`.
- Keep review comments in Simplified Chinese, following `.github/instructions/code-review.instructions.md`.
- Keep process-local memory-only state such as inbound dedup and inflight protection out of cross-process persistence.
- Treat `src/message-context-store.ts` as the only production API for quote, media, and card context persistence.
- Preserve the current multi-account model based on `channels.dingtalk.accounts`; account-level settings inherit channel defaults unless explicitly overridden.

## Documentation Conventions

- Keep `README.md` as a concise project entry page.
- Put user-facing details in `docs/user/`.
- Put contributor, process, testing, and architecture docs in `docs/contributor/`.
- Put release notes in `docs/releases/`.
- Do not create tool-specific doc roots such as `docs/superpowers/`.

## Collaboration Conventions

- Prefer the GitHub issue templates under `.github/ISSUE_TEMPLATE/`.
- Keep issue communication primarily in Simplified Chinese.
- Use an English Conventional-style pull request title.
- Write the pull request description in Simplified Chinese.
- Include clearly labeled `背景`, `目标`, `实现`, `实现 TODO`, and `验证 TODO` sections in pull requests.

## Optional Tooling Policy

Optional tools may strengthen navigation, impact analysis, and verification, but they are not required local dependencies.

- If optional tooling is available, contributors should use it to improve confidence and speed.
- If optional tooling is unavailable, contributors must continue with the base workflow instead of being blocked.
- Tool-specific commands must never be the only documented way to perform a required repository workflow step.
Loading
Loading