zapmyco 0.16.0 → 0.17.2-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,62 +1,47 @@
1
- # zapmyco
1
+ # ai-typescript-starter
2
2
 
3
- [![CI](https://github.com/shenjingnan/zapmyco/actions/workflows/ci.yml/badge.svg)](https://github.com/shenjingnan/zapmyco/actions/workflows/ci.yml)
4
- [![npm version](https://img.shields.io/npm/v/zapmyco.svg)](https://www.npmjs.com/package/zapmyco)
3
+ [![CI](https://github.com/shenjingnan/ai-typescript-starter/actions/workflows/ci.yml/badge.svg)](https://github.com/shenjingnan/ai-typescript-starter/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/ai-typescript-starter.svg)](https://www.npmjs.com/package/ai-typescript-starter)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- AI 原生并行任务编排系统 -- AI 总管 (Personal AI Chief of Staff)。
7
+ AI 原生的 TypeScript 启动模板,专为 AI 辅助开发时代打造。
8
8
 
9
9
  ## 特性
10
10
 
11
- - **Agent 协议**: 统一的 `IAgent` / `IStreamingAgent` 接口,支持请求-响应 + 流式事件双通道模式
12
- - **任务编排**: 目标分解、子任务依赖图、并行执行与结果聚合
13
- - **Agent 运行时**: 基于 pi-agent-core 的适配层,支持 LLM 驱动的 Agent 创建与工具桥接
14
- - **LLM 集成**: Provider 抽象、结构化输出、Token 用量追踪
15
- - **交互式 REPL**: 基于 pi-tui 的终端界面,支持命令注册、历史记录、自定义编辑器
16
- - **CLI 工具链**: `run` 直接执行目标、`agents` 查看注册 Agent、`config` 管理配置
17
- - **配置系统**: cosmiconfig 驱动,支持多级配置覆盖
18
- - **基础设施**: 事件总线、统一错误体系、结构化日志
11
+ - **现代技术栈**: TypeScript + Node.js 24+ + pnpm
12
+ - **构建工具**: tsdown - 基于 rolldown 的 TypeScript 打包器
13
+ - **测试框架**: Vitest - Vite 原生测试框架
14
+ - **代码质量**: Biome (Lint + Format) + cspell (拼写检查)
15
+ - **Git 工作流**: Husky + lint-staged + release-it
16
+ - **AI 集成**: 内置 CLAUDE.md .claude/ 目录配置
17
+ - **CI/CD**: GitHub Actions 自动化测试和发布
19
18
 
20
19
  ## 快速开始
21
20
 
21
+ ### 使用模板
22
+
23
+ 点击仓库页面的 "Use this template" 按钮创建新项目。
24
+
22
25
  ### 安装
23
26
 
24
27
  ```bash
25
28
  # 克隆项目
26
- git clone https://github.com/shenjingnan/zapmyco.git
27
- cd zapmyco
29
+ git clone https://github.com/your-username/your-project.git
30
+ cd your-project
28
31
 
29
32
  # 安装依赖
30
33
  pnpm install
31
34
  ```
32
35
 
33
- ### 使用
34
-
35
- ```bash
36
- # 构建项目
37
- pnpm run build
38
-
39
- # 进入交互式 REPL 模式
40
- pnpm run start
41
- # 或直接运行 CLI
42
- zapmyco
43
-
44
- # 直接执行单次目标(非交互模式)
45
- zapmyco run "重构用户认证模块"
46
-
47
- # 列出可用 Agent
48
- zapmyco agents
49
-
50
- # 显示版本号
51
- zapmyco version
52
- ```
53
-
54
36
  ### 开发
55
37
 
56
38
  ```bash
57
- # 开发模式 (watch)
39
+ # 开发模式
58
40
  pnpm run dev
59
41
 
42
+ # 构建
43
+ pnpm run build
44
+
60
45
  # 测试
61
46
  pnpm run test
62
47
 
@@ -73,27 +58,16 @@ pnpm run check
73
58
  ## 项目结构
74
59
 
75
60
  ```
76
- zapmyco/
77
- ├── .agents/ # AI Agent 配置 (.claude 符号链接指向此目录)
61
+ ai-typescript-starter/
62
+ ├── .claude/ # Claude Code 配置
78
63
  │ ├── commands/ # Slash 命令
79
64
  │ └── skills/ # 技能定义
80
65
  ├── .github/ # GitHub 配置
81
- └── workflows/ # CI/CD 工作流
66
+ ├── workflows/ # CI/CD 工作流
67
+ │ └── ISSUE_TEMPLATE/ # Issue 模板
82
68
  ├── docs/ # 文档
83
69
  ├── examples/ # 示例代码
84
- ├── src/
85
- │ ├── cli/ # CLI 入口 & REPL 交互界面
86
- │ │ └── repl/ # REPL 核心:命令注册、会话、渲染、历史、编辑器
87
- │ ├── config/ # 配置加载与默认值
88
- │ ├── core/ # 核心领域模型
89
- │ │ ├── agent-runtime/# Agent 运行时 (pi-agent-core 适配层)
90
- │ │ ├── aggregator/ # 结果聚合
91
- │ │ ├── intent/ # 目标与意图
92
- │ │ ├── result/ # 任务结果
93
- │ │ └── task/ # 子任务与依赖图
94
- │ ├── infra/ # 基础设施:常量、错误、事件总线、日志
95
- │ ├── llm/ # LLM Provider 抽象 & Token 追踪
96
- │ ├── protocol/ # Agent 协议接口定义
70
+ ├── src/ # 源代码
97
71
  │ └── __tests__/ # 测试文件
98
72
  ├── AGENTS.md # AI Agent 配置
99
73
  └── dist/ # 构建产物
@@ -101,107 +75,41 @@ zapmyco/
101
75
 
102
76
  ## 可用脚本
103
77
 
104
- | 命令 | 说明 |
105
- |------|------|
106
- | `pnpm run build` | 构建项目 |
107
- | `pnpm run dev` | 开发模式 (watch) |
108
- | `pnpm run start` | 启动 CLI (REPL 模式) |
109
- | `pnpm run test` | 运行测试 |
110
- | `pnpm run test:watch` | 测试监听模式 |
111
- | `pnpm run test:coverage` | 测试覆盖率报告 |
112
- | `pnpm run lint` | 代码检查 |
113
- | `pnpm run lint:fix` | 自动修复代码问题 |
114
- | `pnpm run format` | 格式化代码 |
115
- | `pnpm run typecheck` | TypeScript 类型检查 |
116
- | `pnpm run check` | 完整检查 (typecheck + lint) |
117
- | `pnpm run check:fix` | 检查并自动修复 |
118
- | `pnpm run spellcheck` | 拼写检查 |
119
- | `pnpm run release` | 创建发布 |
120
- | `pnpm run release:beta` | 发布 beta 预发布版本 |
121
- | `pnpm run release:dry` | 发布干运行 (不实际发布) |
122
- | `pnpm run release:patch` | 直接发布 patch 版本 |
123
- | `pnpm run release:minor` | 直接发布 minor 版本 |
124
- | `pnpm run release:major` | 直接发布 major 版本 |
125
-
126
- ## 公共 API
127
-
128
- ### 核心导出
129
-
130
- ```typescript
131
- import {
132
- VERSION,
133
- APP_NAME,
134
- // 配置
135
- loadConfig,
136
- DEFAULT_CONFIG,
137
- type ZapmycoConfig,
138
- // Agent 运行时
139
- createLlmBasedAgent,
140
- createToolsFromCapabilities,
141
- createToolFromCapability,
142
- adaptAgentEvent,
143
- createEventBridgeListener,
144
- dispatchToEventBus,
145
- LlmBasedAgent,
146
- // LLM
147
- CostTracker,
148
- costTracker,
149
- // 基础设施
150
- eventBus,
151
- Logger,
152
- logger,
153
- ZapmycoError,
154
- } from 'zapmyco';
155
- ```
156
-
157
- ### Protocol 层类型
158
-
159
- ```typescript
160
- import type {
161
- IAgent,
162
- IStreamingAgent,
163
- AgentExecuteRequest,
164
- AgentExecuteOptions,
165
- AgentStatus,
166
- Capability,
167
- CapabilityCategory,
168
- Goal,
169
- SubTask,
170
- TaskGraph,
171
- TaskResult,
172
- FinalResult,
173
- TokenUsage,
174
- } from 'zapmyco';
175
-
176
- // 或单独导入协议层
177
- import type { IAgent, IStreamingAgent } from 'zapmyco/protocol';
178
- ```
179
-
180
- ### 核心领域类型
181
-
182
- | 模块 | 关键类型 | 说明 |
183
- |------|---------|------|
184
- | `protocol/agent` | `IAgent`, `IStreamingAgent` | Agent 统一接口 |
185
- | `core/intent` | `Goal`, `GoalType`, `GoalConstraints` | 目标与意图定义 |
186
- | `core/task` | `SubTask`, `TaskGraph`, `TaskStatus` | 子任务与依赖图 |
187
- | `core/result` | `TaskResult`, `FinalResult`, `TokenUsage` | 执行结果与用量 |
188
- | `core/aggregator` | `ProgressEvent`, `ProgressPayload` | 进度事件 |
189
- | `core/agent-runtime` | `AgentRuntimeConfig`, `ToolRegistration` | 运行时配置 |
190
- | `llm/types` | `ChatMessage`, `LlmResponse`, `StructuredOutputSchema` | LLM 交互类型 |
191
- | `config/types` | `ZapmycoConfig` | 应用配置 |
78
+ | 命令 | 说明 |
79
+ | ------------------------ | --------------------------- |
80
+ | `pnpm run build` | 构建项目 |
81
+ | `pnpm run dev` | 开发模式 (watch) |
82
+ | `pnpm run test` | 运行测试 |
83
+ | `pnpm run test:watch` | 测试监听模式 |
84
+ | `pnpm run test:coverage` | 测试覆盖率报告 |
85
+ | `pnpm run lint` | 代码检查 |
86
+ | `pnpm run lint:fix` | 自动修复代码问题 |
87
+ | `pnpm run format` | 格式化代码 |
88
+ | `pnpm run typecheck` | TypeScript 类型检查 |
89
+ | `pnpm run check` | 完整检查 (typecheck + lint) |
90
+ | `pnpm run check:fix` | 检查并修复 |
91
+ | `pnpm run spellcheck` | 拼写检查 |
92
+ | `pnpm run release` | 创建发布 |
93
+ | `pnpm run release:beta` | 发布 beta 预发布版本 |
94
+ | `pnpm run release:dry` | 发布干运行 (不实际发布) |
95
+ | `pnpm run release:patch` | 直接发布 patch 版本 |
96
+ | `pnpm run release:minor` | 直接发布 minor 版本 |
97
+ | `pnpm run release:major` | 直接发布 major 版本 |
192
98
 
193
99
  ## AI 辅助开发
194
100
 
195
101
  本项目专为 AI 辅助开发设计,内置了完善的 AI 工程约束:
196
102
 
197
- ### AGENTS.md / CLAUDE.md
103
+ ### CLAUDE.md
198
104
 
199
105
  为 Claude Code 提供项目上下文和开发规范。
200
106
 
201
- ### .agents/ 目录
107
+ ### .claude/ 目录
202
108
 
203
- - `commands/` - 自定义 Slash 命令 (`/build`, `/test`, `/lint`, `/typecheck`, `/release`, `/commit-push-pr`)
204
- - `skills/` - 项目技能定义 (`update-readme`, `resolve-git-conflicts`, `project-context` 等)
109
+ - `commands/` - 自定义 Slash 命令 (`/build`, `/test`, `/lint`, `/typecheck`, `/spellcheck`,
110
+ `/release`, `/commit-push-pr`)
111
+ - `skills/` - 项目技能定义 (`resolve-git-conflicts`, `fix-audit`, `project-context`,
112
+ `update-readme`)
205
113
 
206
114
  ## 代码风格
207
115
 
@@ -226,6 +134,7 @@ pnpm run release
226
134
  ```
227
135
 
228
136
  发布过程会自动:
137
+
229
138
  1. 更新版本号
230
139
  2. 更新 CHANGELOG.md
231
140
  3. 创建 Git tag
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Based on [import-meta-ponyfill](https://github.com/gaubee/import-meta-ponyfill),
3
+ * but instead of using npm to install additional dependencies,
4
+ * this approach manually consolidates cjs/mjs/d.ts into a single file.
5
+ *
6
+ * Note that this code might be imported multiple times
7
+ * (for example, both dnt.test.polyfills.ts and dnt.polyfills.ts contain this code;
8
+ * or Node.js might dynamically clear the cache and then force a require).
9
+ * Therefore, it's important to avoid redundant writes to global objects.
10
+ * Additionally, consider that commonjs is used alongside esm,
11
+ * so the two ponyfill functions are stored independently in two separate global objects.
12
+ */
13
+ //@ts-ignore
14
+ import { createRequire } from "node:module";
15
+ //@ts-ignore
16
+ import { fileURLToPath, pathToFileURL } from "node:url";
17
+ //@ts-ignore
18
+ import { dirname } from "node:path";
19
+ const defineGlobalPonyfill = (symbolFor, fn) => {
20
+ if (!Reflect.has(globalThis, Symbol.for(symbolFor))) {
21
+ Object.defineProperty(globalThis, Symbol.for(symbolFor), {
22
+ configurable: true,
23
+ get() {
24
+ return fn;
25
+ },
26
+ });
27
+ }
28
+ };
29
+ export let import_meta_ponyfill_commonjs = (Reflect.get(globalThis, Symbol.for("import-meta-ponyfill-commonjs")) ??
30
+ (() => {
31
+ const moduleImportMetaWM = new WeakMap();
32
+ return (require, module) => {
33
+ let importMetaCache = moduleImportMetaWM.get(module);
34
+ if (importMetaCache == null) {
35
+ const importMeta = Object.assign(Object.create(null), {
36
+ url: pathToFileURL(module.filename).href,
37
+ main: require.main == module,
38
+ resolve: (specifier, parentURL = importMeta.url) => {
39
+ return pathToFileURL((importMeta.url === parentURL
40
+ ? require
41
+ : createRequire(parentURL))
42
+ .resolve(specifier)).href;
43
+ },
44
+ filename: module.filename,
45
+ dirname: module.path,
46
+ });
47
+ moduleImportMetaWM.set(module, importMeta);
48
+ importMetaCache = importMeta;
49
+ }
50
+ return importMetaCache;
51
+ };
52
+ })());
53
+ defineGlobalPonyfill("import-meta-ponyfill-commonjs", import_meta_ponyfill_commonjs);
54
+ export let import_meta_ponyfill_esmodule = (Reflect.get(globalThis, Symbol.for("import-meta-ponyfill-esmodule")) ??
55
+ ((importMeta) => {
56
+ const resolveFunStr = String(importMeta.resolve);
57
+ const shimWs = new WeakSet();
58
+ //@ts-ignore
59
+ const mainUrl = ("file:///" + process.argv[1].replace(/\\/g, "/"))
60
+ .replace(/\/{3,}/, "///");
61
+ const commonShim = (importMeta) => {
62
+ if (typeof importMeta.main !== "boolean") {
63
+ importMeta.main = importMeta.url === mainUrl;
64
+ }
65
+ if (typeof importMeta.filename !== "string") {
66
+ importMeta.filename = fileURLToPath(importMeta.url);
67
+ importMeta.dirname = dirname(importMeta.filename);
68
+ }
69
+ };
70
+ if (
71
+ // v16.2.0+, v14.18.0+: Add support for WHATWG URL object to parentURL parameter.
72
+ resolveFunStr === "undefined" ||
73
+ // v20.0.0+, v18.19.0+"" This API now returns a string synchronously instead of a Promise.
74
+ resolveFunStr.startsWith("async")
75
+ // enable by --experimental-import-meta-resolve flag
76
+ ) {
77
+ import_meta_ponyfill_esmodule = (importMeta) => {
78
+ if (!shimWs.has(importMeta)) {
79
+ shimWs.add(importMeta);
80
+ const importMetaUrlRequire = {
81
+ url: importMeta.url,
82
+ require: createRequire(importMeta.url),
83
+ };
84
+ importMeta.resolve = function resolve(specifier, parentURL = importMeta.url) {
85
+ return pathToFileURL((importMetaUrlRequire.url === parentURL
86
+ ? importMetaUrlRequire.require
87
+ : createRequire(parentURL)).resolve(specifier)).href;
88
+ };
89
+ commonShim(importMeta);
90
+ }
91
+ return importMeta;
92
+ };
93
+ }
94
+ else {
95
+ /// native support
96
+ import_meta_ponyfill_esmodule = (importMeta) => {
97
+ if (!shimWs.has(importMeta)) {
98
+ shimWs.add(importMeta);
99
+ commonShim(importMeta);
100
+ }
101
+ return importMeta;
102
+ };
103
+ }
104
+ return import_meta_ponyfill_esmodule(importMeta);
105
+ }));
106
+ defineGlobalPonyfill("import-meta-ponyfill-esmodule", import_meta_ponyfill_esmodule);
107
+ export let import_meta_ponyfill = ((...args) => {
108
+ const _MODULE = (() => {
109
+ if (typeof require === "function" && typeof module === "object") {
110
+ return "commonjs";
111
+ }
112
+ else {
113
+ // eval("typeof import.meta");
114
+ return "esmodule";
115
+ }
116
+ })();
117
+ if (_MODULE === "commonjs") {
118
+ //@ts-ignore
119
+ import_meta_ponyfill = (r, m) => import_meta_ponyfill_commonjs(r, m);
120
+ }
121
+ else {
122
+ //@ts-ignore
123
+ import_meta_ponyfill = (im) => import_meta_ponyfill_esmodule(im);
124
+ }
125
+ //@ts-ignore
126
+ return import_meta_ponyfill(...args);
127
+ });
@@ -0,0 +1,61 @@
1
+ import { Deno } from "@deno/shim-deno";
2
+ export { Deno } from "@deno/shim-deno";
3
+ const dntGlobals = {
4
+ Deno,
5
+ };
6
+ export const dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
7
+ function createMergeProxy(baseObj, extObj) {
8
+ return new Proxy(baseObj, {
9
+ get(_target, prop, _receiver) {
10
+ if (prop in extObj) {
11
+ return extObj[prop];
12
+ }
13
+ else {
14
+ return baseObj[prop];
15
+ }
16
+ },
17
+ set(_target, prop, value) {
18
+ if (prop in extObj) {
19
+ delete extObj[prop];
20
+ }
21
+ baseObj[prop] = value;
22
+ return true;
23
+ },
24
+ deleteProperty(_target, prop) {
25
+ let success = false;
26
+ if (prop in extObj) {
27
+ delete extObj[prop];
28
+ success = true;
29
+ }
30
+ if (prop in baseObj) {
31
+ delete baseObj[prop];
32
+ success = true;
33
+ }
34
+ return success;
35
+ },
36
+ ownKeys(_target) {
37
+ const baseKeys = Reflect.ownKeys(baseObj);
38
+ const extKeys = Reflect.ownKeys(extObj);
39
+ const extKeysSet = new Set(extKeys);
40
+ return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
41
+ },
42
+ defineProperty(_target, prop, desc) {
43
+ if (prop in extObj) {
44
+ delete extObj[prop];
45
+ }
46
+ Reflect.defineProperty(baseObj, prop, desc);
47
+ return true;
48
+ },
49
+ getOwnPropertyDescriptor(_target, prop) {
50
+ if (prop in extObj) {
51
+ return Reflect.getOwnPropertyDescriptor(extObj, prop);
52
+ }
53
+ else {
54
+ return Reflect.getOwnPropertyDescriptor(baseObj, prop);
55
+ }
56
+ },
57
+ has(_target, prop) {
58
+ return prop in extObj || prop in baseObj;
59
+ },
60
+ });
61
+ }
package/esm/deno.js ADDED
@@ -0,0 +1,65 @@
1
+ export default {
2
+ "name": "@zapmyco/zapmyco",
3
+ "version": "0.17.2-beta.2",
4
+ "description": "AI 原生的 TypeScript 启动模板,专为 AI 辅助开发时代打造",
5
+ "exports": "./src/index.ts",
6
+ "tasks": {
7
+ "cli": "deno run --allow-env --allow-net src/index.ts",
8
+ "ai": "deno run --allow-env --allow-net src/index.ts ai",
9
+ "install:global": "deno install -g -n zapmyco --allow-env --allow-net src/index.ts",
10
+ "dev": "deno run --watch src/index.ts",
11
+ "test": "deno test --allow-env",
12
+ "test:coverage": "deno test --allow-env --coverage && deno coverage",
13
+ "lint": "deno lint",
14
+ "fmt": "deno fmt",
15
+ "fmt:check": "deno fmt --check",
16
+ "check": "deno check src/",
17
+ "check:all": "deno fmt --check && deno lint && deno check src/ && deno test --allow-env",
18
+ "release": "deno run -A tools/release.ts",
19
+ "release:dry": "deno run -A tools/release.ts --dry-run",
20
+ "build:npm": "deno run -A tools/build-npm.ts"
21
+ },
22
+ "nodeModulesDir": "auto",
23
+ "fmt": {
24
+ "exclude": [
25
+ "docs/"
26
+ ],
27
+ "lineWidth": 100,
28
+ "indentWidth": 2,
29
+ "semiColons": true,
30
+ "singleQuote": true
31
+ },
32
+ "lint": {
33
+ "rules": {
34
+ "tags": [
35
+ "recommended"
36
+ ],
37
+ "exclude": [
38
+ "no-explicit-any"
39
+ ]
40
+ }
41
+ },
42
+ "publish": {
43
+ "exclude": [
44
+ ".agents",
45
+ ".claude",
46
+ ".githooks",
47
+ ".github",
48
+ "coverage",
49
+ "docs",
50
+ "examples",
51
+ "CHANGELOG.md",
52
+ "AGENTS.md",
53
+ "src/**/*_test.ts",
54
+ "tools",
55
+ "dist"
56
+ ]
57
+ },
58
+ "compilerOptions": {
59
+ "strict": true,
60
+ "noUnusedLocals": true,
61
+ "noUnusedParameters": true,
62
+ "noImplicitReturns": true,
63
+ "noUncheckedIndexedAccess": true
64
+ }
65
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * AI Agent - 基于 @anthropic-ai/sdk 的 LLM 对话代理
3
+ */
4
+ import * as dntShim from "../_dnt.shims.js";
5
+ import Anthropic from '@anthropic-ai/sdk';
6
+ import { TextLineStream } from './text-line-stream.js';
7
+ const DEFAULT_BASE_URL = 'https://api.deepseek.com/anthropic';
8
+ const DEFAULT_MODEL = 'deepseek-v4-flash';
9
+ const DEFAULT_SYSTEM_PROMPT = '你是一个 AI 编程助手,帮助用户解决编程问题。';
10
+ /**
11
+ * AI Agent 类 - 封装 LLM 对话功能
12
+ */
13
+ export class AiAgent {
14
+ constructor(options = {}) {
15
+ Object.defineProperty(this, "client", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: void 0
20
+ });
21
+ Object.defineProperty(this, "model", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: void 0
26
+ });
27
+ Object.defineProperty(this, "messages", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: []
32
+ });
33
+ Object.defineProperty(this, "systemPrompt", {
34
+ enumerable: true,
35
+ configurable: true,
36
+ writable: true,
37
+ value: void 0
38
+ });
39
+ const apiKey = options.apiKey ?? dntShim.Deno.env.get('DEEPSEEK_API_KEY');
40
+ if (!apiKey) {
41
+ throw new Error('DEEPSEEK_API_KEY 未设置。请通过环境变量 DEEPSEEK_API_KEY 设置 API Key。');
42
+ }
43
+ this.client = new Anthropic({
44
+ baseURL: options.baseURL ?? DEFAULT_BASE_URL,
45
+ apiKey,
46
+ });
47
+ this.model = options.model ?? DEFAULT_MODEL;
48
+ this.systemPrompt = options.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
49
+ }
50
+ /**
51
+ * 非流式对话 - 发送消息并获取完整回复
52
+ * @param input - 用户输入
53
+ * @returns 完整回复文本
54
+ */
55
+ async chat(input) {
56
+ this.messages.push({ role: 'user', content: input });
57
+ const response = await this.client.messages.create({
58
+ model: this.model,
59
+ max_tokens: 4096,
60
+ system: this.systemPrompt,
61
+ messages: this.messages,
62
+ });
63
+ const firstBlock = response.content[0];
64
+ const content = firstBlock?.type === 'text' ? firstBlock.text : '';
65
+ this.messages.push({ role: 'assistant', content });
66
+ return content;
67
+ }
68
+ /**
69
+ * 流式对话 - 发送消息并通过回调逐块获取回复
70
+ * @param input - 用户输入
71
+ * @param onChunk - 每收到一个文本块的回调
72
+ * @returns 完整回复文本
73
+ */
74
+ async chatStream(input, onChunk) {
75
+ this.messages.push({ role: 'user', content: input });
76
+ const stream = this.client.messages.stream({
77
+ model: this.model,
78
+ max_tokens: 4096,
79
+ system: this.systemPrompt,
80
+ messages: this.messages,
81
+ });
82
+ stream.on('text', (text) => {
83
+ onChunk(text);
84
+ });
85
+ const response = await stream.finalMessage();
86
+ const firstBlock = response.content[0];
87
+ const content = firstBlock?.type === 'text' ? firstBlock.text : '';
88
+ this.messages.push({ role: 'assistant', content });
89
+ return content;
90
+ }
91
+ /**
92
+ * 启动交互式对话 - 从 stdin 读取输入,流式输出到 stdout
93
+ */
94
+ async startInteractiveChat() {
95
+ const encoder = new TextEncoder();
96
+ console.error('进入 AI 对话模式');
97
+ console.error(`模型: ${this.model}`);
98
+ console.error('输入 /exit 退出,/clear 清空上下文');
99
+ console.error('---');
100
+ const lines = dntShim.Deno.stdin.readable
101
+ .pipeThrough(new TextDecoderStream())
102
+ .pipeThrough(new TextLineStream());
103
+ for await (const line of lines) {
104
+ const trimmed = line.trim();
105
+ if (!trimmed)
106
+ continue;
107
+ if (trimmed === '/exit') {
108
+ console.error('\n再见!');
109
+ break;
110
+ }
111
+ if (trimmed === '/clear') {
112
+ this.messages = [];
113
+ console.error('上下文已清空');
114
+ continue;
115
+ }
116
+ // 在 stderr 显示用户输入(不影响 stdout 的纯 AI 输出)
117
+ console.error(`\n❯ ${trimmed}\n`);
118
+ try {
119
+ await this.chatStream(trimmed, (chunk) => {
120
+ dntShim.Deno.stdout.writeSync(encoder.encode(chunk));
121
+ });
122
+ console.error('\n---');
123
+ }
124
+ catch (error) {
125
+ const message = error instanceof Error ? error.message : String(error);
126
+ console.error(`\n[错误] ${message}`);
127
+ }
128
+ }
129
+ }
130
+ /** 清空对话上下文 */
131
+ clearContext() {
132
+ this.messages = [];
133
+ }
134
+ /** 获取当前对话历史 */
135
+ getMessages() {
136
+ return this.messages;
137
+ }
138
+ }