本章总览
先建立地图:misc-types 负责承接核心主干之外的“边界约束类型”,保证 ID、插件、输入状态、生成代码与轻量工具类型不会互相污染。对应到源码,它把 ids.ts、plugin.ts、textInputTypes.ts、utils.ts 与 generated/ 这几类不同稳定性的类型分层组织。
学完本章你应该能
- 使用 asSessionId / toAgentId 品牌类型防止 ID 混用
- 理解 LoadedPlugin 与 PluginManifest 的字段分工
- 知道 DeepImmutable 在 AppState 上的应用与例外
- 阅读 textInputTypes 与 useVimInput 的模式状态
- 区分 generated/ 手改禁止与消费方式
- 能在 analytics 找到 claude_code_internal_event 用法
核心概念(先读懂这些)
Branded types 防呆
SessionId 与 AgentId 是 string & { __brand },编译期不可互换。toAgentId 用 AGENT_ID_PATTERN 校验 createAgentId 格式,teammate 名称等非 agent id 返回 null。运行时仍是一般的 string,品牌仅在 TS 层。
plugin.ts 桥接 marketplace 与 runtime
LoadedPlugin 聚合 manifest、path、commandsPaths、hooksConfig、mcpServers、lspServers。PluginError 判别联合区分 manifest 解析、git clone、版本冲突等。BuiltinPluginDefinition 描述内置插件默认启用与 isAvailable 门控。
generated/ 是 analytics 与 GrowthBook 的 schema 源
claude_code_internal_event.ts、growthbook_experiment_event.ts、auth.ts、timestamp.ts 由 codegen 生成,types/ 手改类型与之并存。业务代码 import generated 类型发 tengu_* 事件,不在 generated 内加业务逻辑。
建议学习步骤
- 阅读 ids.ts 品牌函数与 AGENT_ID_PATTERN
- 浏览 plugin.ts LoadedPlugin 与 PluginError
- 打开 utils.ts DeepImmutable 定义
- 阅读 textInputTypes VimMode 与 VimInputState
- 扫一眼 generated/claude_code_internal_event 结构
- 在 services/analytics 搜索 import generated
常见误区
注意
plugin.ts re-export PluginManifest 自 utils/plugins/schemas——改 manifest 先改 schema
注意
statusLine.ts 极薄,StatusLineItem 用 Record 松散类型
注意
notebook.ts 与 Jupyter 导出相关,与 memdir 无关
注意
勿编辑 generated/ 文件,重新 codegen 会覆盖
ids.ts 品牌类型
| 类型 | 含义 | 工厂 |
|---|---|---|
| SessionId | Claude Code 会话 | getSessionId(), asSessionId() |
| AgentId | 会话内 subagent | createAgentId(), toAgentId() |
AGENT_ID_PATTERN = ^a(?:.+-)?[0-9a-f]{16}$——可选 label 前缀 + 16 hex。teammate 寻址字符串不匹配则 toAgentId 返回 null,避免误当 subagent transcript 过滤。
源码引用: src/types/ids.ts · 第 6–17 行(共 45 行)
6| /**
7| * A session ID uniquely identifies a Claude Code session.
8| * Returned by getSessionId().
9| */
10| export type SessionId = string & { readonly __brand: 'SessionId' }
11|
12| /**
13| * An agent ID uniquely identifies a subagent within a session.
14| * Returned by createAgentId().
15| * When present, indicates the context is a subagent (not the main session).
16| */
17| export type AgentId = string & { readonly __brand: 'AgentId' }
源码引用: src/types/ids.ts · 第 35–44 行(共 45 行)
35| const AGENT_ID_PATTERN = /^a(?:.+-)?[0-9a-f]{16}$/
36|
37| /**
38| * Validate and brand a string as AgentId.
39| * Matches the format produced by createAgentId(): `a` + optional `<label>-` + 16 hex chars.
40| * Returns null if the string doesn't match (e.g. teammate names, team-addressing).
41| */
42| export function toAgentId(s: string): AgentId | null {
43| return AGENT_ID_PATTERN.test(s) ? (s as AgentId) : null
44| }
plugin.ts 插件类型族
BuiltinPluginDefinition:name、description、skills、hooks、mcpServers、defaultEnabled、isAvailable()。
LoadedPlugin:运行时实例,含 commandsPaths[]、agentsPaths[]、sha(marketplace pin)、isBuiltin。
PluginError 联合覆盖 manifestValidation、cloneFailed、incompatibleVersion 等,/plugin UI 与 Doctor 展示人类可读错误。
PluginComponent = commands | agents | skills | hooks | output-styles,用于按组件 reload。
源码引用: src/types/plugin.ts · 第 18–35 行(共 364 行)
18| export type BuiltinPluginDefinition = {
19| /** Plugin name (used in `{name}@builtin` identifier) */
20| name: string
21| /** Description shown in the /plugin UI */
22| description: string
23| /** Optional version string */
24| version?: string
25| /** Skills provided by this plugin */
26| skills?: BundledSkillDefinition[]
27| /** Hooks provided by this plugin */
28| hooks?: HooksSettings
29| /** MCP servers provided by this plugin */
30| mcpServers?: Record<string, McpServerConfig>
31| /** Whether this plugin is available (e.g. based on system capabilities). Unavailable plugins are hidden entirely. */
32| isAvailable?: () => boolean
33| /** Default enabled state before the user sets a preference (defaults to true) */
34| defaultEnabled?: boolean
35| }
源码引用: src/types/plugin.ts · 第 48–70 行(共 364 行)
48| export type LoadedPlugin = {
49| name: string
50| manifest: PluginManifest
51| path: string
52| source: string
53| repository: string // Repository identifier, usually same as source
54| enabled?: boolean
55| isBuiltin?: boolean // true for built-in plugins that ship with the CLI
56| sha?: string // Git commit SHA for version pinning (from marketplace entry source)
57| commandsPath?: string
58| commandsPaths?: string[] // Additional command paths from manifest
59| commandsMetadata?: Record<string, CommandMetadata> // Metadata for named commands from object-mapping format
60| agentsPath?: string
61| agentsPaths?: string[] // Additional agent paths from manifest
62| skillsPath?: string
63| skillsPaths?: string[] // Additional skill paths from manifest
64| outputStylesPath?: string
65| outputStylesPaths?: string[] // Additional output style paths from manifest
66| hooksConfig?: HooksSettings
67| mcpServers?: Record<string, McpServerConfig>
68| lspServers?: Record<string, LspServerConfig>
69| settings?: Record<string, unknown>
70| }
源码引用: src/types/plugin.ts · 第 72–120 行(共 364 行)
72| export type PluginComponent =
73| | 'commands'
74| | 'agents'
75| | 'skills'
76| | 'hooks'
77| | 'output-styles'
78|
79| /**
80| * Discriminated union of plugin error types.
81| * Each error type has specific contextual data for better debugging and user guidance.
82| *
83| * This replaces the previous string-based error matching approach with type-safe
84| * error handling that can't break when error messages change.
85| *
86| * IMPLEMENTATION STATUS:
87| * Currently used in production (2 types):
88| * - generic-error: Used for various plugin loading failures
89| * - plugin-not-found: Used when plugin not found in marketplace
90| *
91| * Planned for future use (10 types - see TODOs in pluginLoader.ts):
92| * - path-not-found, git-auth-failed, git-timeout, network-error
93| * - manifest-parse-error, manifest-validation-error
94| * - marketplace-not-found, marketplace-load-failed
95| * - mcp-config-invalid, hook-load-failed, component-load-failed
96| *
97| * These unused types support UI formatting and provide a clear roadmap for
98| * improving error specificity. They can be incrementally implemented as
99| * error creation sites are refactored.
100| */
101| export type PluginError =
102| | {
103| type: 'path-not-found'
104| source: string
105| plugin?: string
106| path: string
107| component: PluginComponent
108| }
109| | {
110| type: 'git-auth-failed'
111| source: string
112| plugin?: string
113| gitUrl: string
114| authType: 'ssh' | 'https'
115| }
116| | {
117| type: 'git-timeout'
118| source: string
119| plugin?: string
120| gitUrl: string
utils.ts 与 textInputTypes.ts
DeepImmutable 递归 readonly,AppState 主体使用;Map/Set/含函数字段除外。
textInputTypes.ts 定义 VimMode = NORMAL | INSERT,VimInputState 暴露 mode、offset、handleVimInput 等给 VimTextInput。与 keybindings 模块分离——Esc 切 NORMAL 不走可配置 binding(见 vim-bindings 子章节)。
fileSuggestion.ts、connectorText.ts 服务 autocomplete 与连接器文案类型。
源码引用: src/types/utils.ts · 第 1–10 行(共 10 行)
1| export type DeepImmutable<T> = T extends (...args: never[]) => unknown
2| ? T
3| : T extends readonly (infer U)[]
4| ? ReadonlyArray<DeepImmutable<U>>
5| : T extends object
6| ? { readonly [K in keyof T]: DeepImmutable<T[K]> }
7| : T
8|
9| export type Permutations<T> = T
10|
源码引用: src/types/textInputTypes.ts · 第 1–50 行(共 388 行)
1| import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'
2| import type { UUID } from 'crypto'
3| import type React from 'react'
4| import type { PermissionResult } from '../entrypoints/agentSdkTypes.js'
5| import type { Key } from '../ink.js'
6| import type { PastedContent } from '../utils/config.js'
7| import type { ImageDimensions } from '../utils/imageResizer.js'
8| import type { TextHighlight } from '../utils/textHighlighting.js'
9| import type { AgentId } from './ids.js'
10| import type { AssistantMessage, MessageOrigin } from './message.js'
11|
12| /**
13| * Inline ghost text for mid-input command autocomplete
14| */
15| export type InlineGhostText = {
16| /** The ghost text to display (e.g., "mit" for /commit) */
17| readonly text: string
18| /** The full command name (e.g., "commit") */
19| readonly fullCommand: string
20| /** Position in the input where the ghost text should appear */
21| readonly insertPosition: number
22| }
23|
24| /**
25| * Base props for text input components
26| */
27| export type BaseTextInputProps = {
28| /**
29| * Optional callback for handling history navigation on up arrow at start of input
30| */
31| readonly onHistoryUp?: () => void
32|
33| /**
34| * Optional callback for handling history navigation on down arrow at end of input
35| */
36| readonly onHistoryDown?: () => void
37|
38| /**
39| * Text to display when `value` is empty.
40| */
41| readonly placeholder?: string
42|
43| /**
44| * Allow multi-line input via line ending with backslash (default: `true`)
45| */
46| readonly multiline?: boolean
47|
48| /**
49| * Listen to user's input. Useful in case there are multiple input components
50| * at the same time and input must be "routed" to a specific component.
源码引用: src/types/fileSuggestion.ts · 第 1–2 行(共 2 行)
1| export type FileSuggestion = Record<string, unknown>
2|
statusLine 与 notebook
StatusLineItem = Record<string, unknown>——statusline 插件 JSON 输出松散解析,避免每种 item 形状都进中央类型。
notebook.ts 定义 notebook 导出单元结构(cells、metadata),供特定 export 路径使用,与 REPL transcript 类型正交。
源码引用: src/types/statusLine.ts · 第 1–2 行(共 2 行)
1| export type StatusLineItem = Record<string, unknown>
2|
源码引用: src/types/notebook.ts · 第 1–2 行(共 2 行)
1| export type NotebookCell = Record<string, unknown>
2|
generated/ protobuf 事件
claude_code_internal_event.ts 定义 tengu 内部 analytics 事件 protobuf 消息类型,体积最大(数千行)。growthbook_experiment_event.ts 对接 GrowthBook experiment 上报。auth.ts、timestamp.ts 为公共子消息。
消费模式:
业务 logEvent('tengu_*', metadata)
→ metadata 类型与 generated 字段对齐
→ 批量上传 events pipeline
types/ 手写的 message、plugin 类型 不 import generated,保持 REPL 核心与 analytics codegen 解耦。
源码引用: src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts · 第 1–60 行(共 866 行)
1| // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
2| // versions:
3| // protoc-gen-ts_proto v2.6.1
4| // protoc unknown
5| // source: events_mono/claude_code/v1/claude_code_internal_event.proto
6|
7| /* eslint-disable */
8| import { Timestamp } from '../../../google/protobuf/timestamp.js'
9| import { PublicApiAuth } from '../../common/v1/auth.js'
10|
11| /** GitHubActionsMetadata contains GitHub Actions-specific environment information */
12| export interface GitHubActionsMetadata {
13| actor_id?: string | undefined
14| repository_id?: string | undefined
15| repository_owner_id?: string | undefined
16| }
17|
18| /**
19| * EnvironmentMetadata contains environment and runtime information
20| * See claude-cli-internal/src/services/statsig.ts for the source of these fields
21| */
22| export interface EnvironmentMetadata {
23| platform?: string | undefined
24| node_version?: string | undefined
25| terminal?: string | undefined
26| package_managers?: string | undefined
27| runtimes?: string | undefined
28| is_running_with_bun?: boolean | undefined
29| is_ci?: boolean | undefined
30| is_claubbit?: boolean | undefined
31| is_github_action?: boolean | undefined
32| is_claude_code_action?: boolean | undefined
33| is_claude_ai_auth?: boolean | undefined
34| version?: string | undefined
35| /** GitHub Actions specific fields (only present when is_github_action is true) */
36| github_event_name?: string | undefined
37| github_actions_runner_environment?: string | undefined
38| github_actions_runner_os?: string | undefined
39| github_action_ref?: string | undefined
40| /** WSL specific field */
41| wsl_version?: string | undefined
42| /** GitHub metadata (only present when is_github_action is true) */
43| github_actions_metadata?: GitHubActionsMetadata | undefined
44| arch?: string | undefined
45| is_claude_code_remote?: boolean | undefined
46| remote_environment_type?: string | undefined
47| claude_code_container_id?: string | undefined
48| claude_code_remote_session_id?: string | undefined
49| tags?: string[] | undefined
50| deployment_environment?: string | undefined
51| is_conductor?: boolean | undefined
52| version_base?: string | undefined
53| coworker_type?: string | undefined
54| build_time?: string | undefined
55| is_local_agent_mode?: boolean | undefined
56| linux_distro_id?: string | undefined
57| linux_distro_version?: string | undefined
58| linux_kernel?: string | undefined
59| vcs?: string | undefined
60| platform_raw?: string | undefined
源码引用: src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts · 第 1–40 行(共 224 行)
1| // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
2| // versions:
3| // protoc-gen-ts_proto v2.6.1
4| // protoc unknown
5| // source: events_mono/growthbook/v1/growthbook_experiment_event.proto
6|
7| /* eslint-disable */
8| import { Timestamp } from '../../../google/protobuf/timestamp.js'
9| import { PublicApiAuth } from '../../common/v1/auth.js'
10|
11| /**
12| * GrowthBook experiment assignment event
13| * This event tracks when a user is exposed to an experiment variant
14| * See: https://docs.growthbook.io/guide/bigquery
15| */
16| export interface GrowthbookExperimentEvent {
17| /** Unique event identifier (for deduplication) */
18| event_id?: string | undefined
19| /** When user was exposed to experiment (maps to GrowthBook's timestamp column) */
20| timestamp?: Date | undefined
21| /** Experiment tracking key (maps to GrowthBook's experiment_id column) */
22| experiment_id?: string | undefined
23| /** Variation index: 0=control, 1+=variants (maps to GrowthBook's variation_id column) */
24| variation_id?: number | undefined
25| /** Environment where assignment occurred */
26| environment?: string | undefined
27| /** User attributes at time of assignment */
28| user_attributes?: string | undefined
29| /** Experiment metadata */
30| experiment_metadata?: string | undefined
31| /** Device identifier for the client */
32| device_id?: string | undefined
33| /** Authentication context automatically injected by the API */
34| auth?: PublicApiAuth | undefined
35| /** Session identifier for tracking user sessions */
36| session_id?: string | undefined
37| /** Anonymous identifier for unauthenticated users */
38| anonymous_id?: string | undefined
39| /** Event metadata variables (automatically populated by internal-tools-common event_logging library) */
40| event_metadata_vars?: string | undefined
源码引用: src/types/generated/events_mono/common/v1/auth.ts · 第 1–30 行(共 101 行)
1| // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
2| // versions:
3| // protoc-gen-ts_proto v2.6.1
4| // protoc unknown
5| // source: events_mono/common/v1/auth.proto
6|
7| /* eslint-disable */
8|
9| /** PublicApiAuth contains authentication context automatically injected by the API */
10| export interface PublicApiAuth {
11| account_id?: number | undefined
12| organization_uuid?: string | undefined
13| account_uuid?: string | undefined
14| }
15|
16| function createBasePublicApiAuth(): PublicApiAuth {
17| return { account_id: 0, organization_uuid: '', account_uuid: '' }
18| }
19|
20| export const PublicApiAuth: MessageFns<PublicApiAuth> = {
21| fromJSON(object: any): PublicApiAuth {
22| return {
23| account_id: isSet(object.account_id)
24| ? globalThis.Number(object.account_id)
25| : 0,
26| organization_uuid: isSet(object.organization_uuid)
27| ? globalThis.String(object.organization_uuid)
28| : '',
29| account_uuid: isSet(object.account_uuid)
30| ? globalThis.String(object.account_uuid)
connectorText 与 messageQueue 交叉引用
connectorText.ts 类型化 MCP connector 展示字符串,避免 UI 硬编码。
messageQueueTypes.ts(见 message-types 章)与 ids.ts 在 task/agent 寻址场景交汇——Background task UI 用 AgentId 过滤 transcript。
misc 文件虽小,grep import 'types/' 时高频出现,值得单独索引。
源码引用: src/types/connectorText.ts · 第 1–10 行(共 10 行)
1| export type ConnectorTextBlock = {
2| type?: string
3| text?: string
4| [key: string]: unknown
5| }
6|
7| export function isConnectorTextBlock(value: unknown): value is ConnectorTextBlock {
8| return !!value && typeof value === 'object' && 'text' in (value as Record<string, unknown>)
9| }
10|
源码引用: src/types/messageQueueTypes.ts · 第 1–2 行(共 2 行)
1| export type MessageQueueEntry = Record<string, unknown>
2|
types/ 行数分布提示
19 文件中 generated/ 约占 70% 行数;手写类型以 message、permissions、command、hooks、plugin 为核心。读源码时优先手写文件,generated 当字典查阅。
升级 protobuf codegen 后跑 TypeScript build,analytics metadata 类型错误通常指向 generated 字段 rename。
长尾类型的共同模式
misc-types 里的文件看起来分散,但共同目标都是“让边界更难被误用”。ids.ts 用 branded string 区分 SessionId 与 AgentId,运行时仍是字符串,编译期却能防止把 subagent id 当成 session id 传入 transcript 或 analytics。plugin.ts 把 BuiltinPluginDefinition、LoadedPlugin、PluginComponent、PluginError 拆清楚:manifest 是外部声明,LoadedPlugin 是解析后的运行时实体,PluginError 是 UI/Doctor 能展示的结构化失败。textInputTypes.ts 则把 VimMode、VimInputState 放在纯类型层,让 hooks/useVimInput 和组件共享状态形状,却不让 types/ 反向 import React 组件。
阅读这些小文件时,可以按“来源、运行时形态、消费点”三步归类。来源可能是用户配置、插件 manifest、终端输入、protobuf codegen 或会话 id;运行时形态可能是品牌字符串、Record 松散对象、判别联合或只读工具类型;消费点则通常在 UI、analytics、commands 或 hooks。只要抓住这个模式,就不会因为文件短而忽略其约束作用。尤其 generated/ 不应手改,plugin manifest 字段应先改 schema,Vim 输入类型应先看 hooks/useVimInput 的实际返回值,再决定是否扩展 textInputTypes。
这些长尾类型还有一个共同特征:它们经常承担跨模块“薄适配层”。StatusLineItem 用 Record 接住插件输出,是因为 statusline 内容由外部脚本决定;connectorText 把连接器文案集中,避免多个组件手写同一展示结构;fileSuggestion 和 notebook 则服务特定 UI 或导出路径,不应被 message、permission、hooks 等核心类型反向依赖。把薄类型保留在 types/ 中,可以让消费方 import 一个稳定名字,而实现细节仍留在 utils、components 或 services。
维护时不要因为类型文件短就随手扩大范围。例如给 LoadedPlugin 增字段,要检查插件 loader、marketplace pin、/plugin UI 和 Doctor;给 AgentId 放宽 pattern,要确认 subagent transcript、teammate 寻址和 analytics 是否仍能区分;给 VimInputState 加方法,要确认 useVimInput、VimTextInput、PromptInput 三处都能提供同一语义。misc-types 的价值正是在这些边界处阻止“看似都是字符串或对象”的混用。
如果某个新增类型暂时只被一个实现使用,先放在实现相邻文件通常更清晰;只有当它成为多个模块的共享语言,才提升到 types/。这能避免 types/ 变成杂物桶,也能让读者根据目录推断稳定性。
源码引用: src/types/ids.ts · 第 1–44 行(共 45 行)
1| /**
2| * Branded types for session and agent IDs.
3| * These prevent accidentally mixing up session IDs and agent IDs at compile time.
4| */
5|
6| /**
7| * A session ID uniquely identifies a Claude Code session.
8| * Returned by getSessionId().
9| */
10| export type SessionId = string & { readonly __brand: 'SessionId' }
11|
12| /**
13| * An agent ID uniquely identifies a subagent within a session.
14| * Returned by createAgentId().
15| * When present, indicates the context is a subagent (not the main session).
16| */
17| export type AgentId = string & { readonly __brand: 'AgentId' }
18|
19| /**
20| * Cast a raw string to SessionId.
21| * Use sparingly - prefer getSessionId() when possible.
22| */
23| export function asSessionId(id: string): SessionId {
24| return id as SessionId
25| }
26|
27| /**
28| * Cast a raw string to AgentId.
29| * Use sparingly - prefer createAgentId() when possible.
30| */
31| export function asAgentId(id: string): AgentId {
32| return id as AgentId
33| }
34|
35| const AGENT_ID_PATTERN = /^a(?:.+-)?[0-9a-f]{16}$/
36|
37| /**
38| * Validate and brand a string as AgentId.
39| * Matches the format produced by createAgentId(): `a` + optional `<label>-` + 16 hex chars.
40| * Returns null if the string doesn't match (e.g. teammate names, team-addressing).
41| */
42| export function toAgentId(s: string): AgentId | null {
43| return AGENT_ID_PATTERN.test(s) ? (s as AgentId) : null
44| }
源码引用: src/types/plugin.ts · 第 18–70 行(共 364 行)
18| export type BuiltinPluginDefinition = {
19| /** Plugin name (used in `{name}@builtin` identifier) */
20| name: string
21| /** Description shown in the /plugin UI */
22| description: string
23| /** Optional version string */
24| version?: string
25| /** Skills provided by this plugin */
26| skills?: BundledSkillDefinition[]
27| /** Hooks provided by this plugin */
28| hooks?: HooksSettings
29| /** MCP servers provided by this plugin */
30| mcpServers?: Record<string, McpServerConfig>
31| /** Whether this plugin is available (e.g. based on system capabilities). Unavailable plugins are hidden entirely. */
32| isAvailable?: () => boolean
33| /** Default enabled state before the user sets a preference (defaults to true) */
34| defaultEnabled?: boolean
35| }
36|
37| export type PluginRepository = {
38| url: string
39| branch: string
40| lastUpdated?: string
41| commitSha?: string
42| }
43|
44| export type PluginConfig = {
45| repositories: Record<string, PluginRepository>
46| }
47|
48| export type LoadedPlugin = {
49| name: string
50| manifest: PluginManifest
51| path: string
52| source: string
53| repository: string // Repository identifier, usually same as source
54| enabled?: boolean
55| isBuiltin?: boolean // true for built-in plugins that ship with the CLI
56| sha?: string // Git commit SHA for version pinning (from marketplace entry source)
57| commandsPath?: string
58| commandsPaths?: string[] // Additional command paths from manifest
59| commandsMetadata?: Record<string, CommandMetadata> // Metadata for named commands from object-mapping format
60| agentsPath?: string
61| agentsPaths?: string[] // Additional agent paths from manifest
62| skillsPath?: string
63| skillsPaths?: string[] // Additional skill paths from manifest
64| outputStylesPath?: string
65| outputStylesPaths?: string[] // Additional output style paths from manifest
66| hooksConfig?: HooksSettings
67| mcpServers?: Record<string, McpServerConfig>
68| lspServers?: Record<string, LspServerConfig>
69| settings?: Record<string, unknown>
70| }
源码引用: src/types/textInputTypes.ts · 第 208–260 行(共 388 行)
208| /**
209| * Initial vim mode to use
210| */
211| readonly initialMode?: VimMode
212|
213| /**
214| * Optional callback for mode changes
215| */
216| readonly onModeChange?: (mode: VimMode) => void
217| }
218|
219| /**
220| * Vim editor modes
221| */
222| export type VimMode = 'INSERT' | 'NORMAL'
223|
224| /**
225| * Common properties for input hook results
226| */
227| export type BaseInputState = {
228| onInput: (input: string, key: Key) => void
229| renderedValue: string
230| offset: number
231| setOffset: (offset: number) => void
232| /** Cursor line (0-indexed) within the rendered text, accounting for wrapping. */
233| cursorLine: number
234| /** Cursor column (display-width) within the current line. */
235| cursorColumn: number
236| /** Character offset in the full text where the viewport starts (0 when no windowing). */
237| viewportCharOffset: number
238| /** Character offset in the full text where the viewport ends (text.length when no windowing). */
239| viewportCharEnd: number
240|
241| // For paste handling
242| isPasting?: boolean
243| pasteState?: {
244| chunks: string[]
245| timeoutId: ReturnType<typeof setTimeout> | null
246| }
247| }
248|
249| /**
250| * State for text input
251| */
252| export type TextInputState = BaseInputState
253|
254| /**
255| * State for vim input with mode
256| */
257| export type VimInputState = BaseInputState & {
258| mode: VimMode
259| setMode: (mode: VimMode) => void
260| }
何时把类型提升到 types/ 根目录
维护 misc 类型时,一个实用判断标准是“这个类型是否已经成为跨模块语言”。如果某类型只在单一实现内部使用,留在同目录最清晰;若它被 commands、hooks、services、components 多方共享,且字段名会进入文档、配置或外部接口,再提升到 types/ 会更稳妥。ids.ts 与 plugin.ts 都属于后者:它们的名字和字段会被多个子系统复用,改动需要跨模块评估。
另一个标准是“是否承担迁移与兼容”。generated/ 文件由 codegen 管理,不应手改;StatusLineItem、connectorText 这类松散类型则用于接外部输入并隔离不稳定结构。把这两类都放进 misc-types 的阅读路径,有助于新人建立同一心智:并不是所有类型都追求极致严格,很多类型的价值是把不稳定数据圈在边界里,让核心 message/permission/hook 体系保持可演进。
源码引用: src/types/ids.ts · 第 6–44 行(共 45 行)
6| /**
7| * A session ID uniquely identifies a Claude Code session.
8| * Returned by getSessionId().
9| */
10| export type SessionId = string & { readonly __brand: 'SessionId' }
11|
12| /**
13| * An agent ID uniquely identifies a subagent within a session.
14| * Returned by createAgentId().
15| * When present, indicates the context is a subagent (not the main session).
16| */
17| export type AgentId = string & { readonly __brand: 'AgentId' }
18|
19| /**
20| * Cast a raw string to SessionId.
21| * Use sparingly - prefer getSessionId() when possible.
22| */
23| export function asSessionId(id: string): SessionId {
24| return id as SessionId
25| }
26|
27| /**
28| * Cast a raw string to AgentId.
29| * Use sparingly - prefer createAgentId() when possible.
30| */
31| export function asAgentId(id: string): AgentId {
32| return id as AgentId
33| }
34|
35| const AGENT_ID_PATTERN = /^a(?:.+-)?[0-9a-f]{16}$/
36|
37| /**
38| * Validate and brand a string as AgentId.
39| * Matches the format produced by createAgentId(): `a` + optional `<label>-` + 16 hex chars.
40| * Returns null if the string doesn't match (e.g. teammate names, team-addressing).
41| */
42| export function toAgentId(s: string): AgentId | null {
43| return AGENT_ID_PATTERN.test(s) ? (s as AgentId) : null
44| }
源码引用: src/types/plugin.ts · 第 48–120 行(共 364 行)
48| export type LoadedPlugin = {
49| name: string
50| manifest: PluginManifest
51| path: string
52| source: string
53| repository: string // Repository identifier, usually same as source
54| enabled?: boolean
55| isBuiltin?: boolean // true for built-in plugins that ship with the CLI
56| sha?: string // Git commit SHA for version pinning (from marketplace entry source)
57| commandsPath?: string
58| commandsPaths?: string[] // Additional command paths from manifest
59| commandsMetadata?: Record<string, CommandMetadata> // Metadata for named commands from object-mapping format
60| agentsPath?: string
61| agentsPaths?: string[] // Additional agent paths from manifest
62| skillsPath?: string
63| skillsPaths?: string[] // Additional skill paths from manifest
64| outputStylesPath?: string
65| outputStylesPaths?: string[] // Additional output style paths from manifest
66| hooksConfig?: HooksSettings
67| mcpServers?: Record<string, McpServerConfig>
68| lspServers?: Record<string, LspServerConfig>
69| settings?: Record<string, unknown>
70| }
71|
72| export type PluginComponent =
73| | 'commands'
74| | 'agents'
75| | 'skills'
76| | 'hooks'
77| | 'output-styles'
78|
79| /**
80| * Discriminated union of plugin error types.
81| * Each error type has specific contextual data for better debugging and user guidance.
82| *
83| * This replaces the previous string-based error matching approach with type-safe
84| * error handling that can't break when error messages change.
85| *
86| * IMPLEMENTATION STATUS:
87| * Currently used in production (2 types):
88| * - generic-error: Used for various plugin loading failures
89| * - plugin-not-found: Used when plugin not found in marketplace
90| *
91| * Planned for future use (10 types - see TODOs in pluginLoader.ts):
92| * - path-not-found, git-auth-failed, git-timeout, network-error
93| * - manifest-parse-error, manifest-validation-error
94| * - marketplace-not-found, marketplace-load-failed
95| * - mcp-config-invalid, hook-load-failed, component-load-failed
96| *
97| * These unused types support UI formatting and provide a clear roadmap for
98| * improving error specificity. They can be incrementally implemented as
99| * error creation sites are refactored.
100| */
101| export type PluginError =
102| | {
103| type: 'path-not-found'
104| source: string
105| plugin?: string
106| path: string
107| component: PluginComponent
108| }
109| | {
110| type: 'git-auth-failed'
111| source: string
112| plugin?: string
113| gitUrl: string
114| authType: 'ssh' | 'https'
115| }
116| | {
117| type: 'git-timeout'
118| source: string
119| plugin?: string
120| gitUrl: string
源码引用: src/types/statusLine.ts · 第 1–2 行(共 2 行)
1| export type StatusLineItem = Record<string, unknown>
2|
本章小结与延伸
misc-types 补齐 types/ 长尾文件与 codegen 子树。回到 types 总览对照 19 文件全貌。 继续学习: