agent-handoff 0.1.0
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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +418 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-03-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- CLI 框架(commander)
|
|
9
|
+
- `agent-handoff init` 命令 - 创建 workspace
|
|
10
|
+
- `agent-handoff status` 命令 - 显示 workspace 状态
|
|
11
|
+
- `agent-handoff next` 命令 - 输出下一步 prompt
|
|
12
|
+
- Core 模型定义(Workflow/Step/State/Event)
|
|
13
|
+
- Workspace 读取与 workflow.yaml 解析
|
|
14
|
+
- 状态机(根据产物推导当前步骤)
|
|
15
|
+
- TRAE prompt 生成器
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# AgentHandoff
|
|
2
|
+
|
|
3
|
+
轻量级多 Agent 协作接力工具。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g agent-handoff
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
### 1. 创建 workspace
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
agent-handoff init my-project
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. 编辑 brief.md
|
|
20
|
+
|
|
21
|
+
编辑 `my-project/brief.md` 描述你的需求。
|
|
22
|
+
|
|
23
|
+
### 3. 查看状态
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
agent-handoff status my-project
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 4. 获取下一步 prompt
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
agent-handoff next my-project
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
将输出的 Prompt 复制到 TRAE 新 Task 中执行。
|
|
36
|
+
|
|
37
|
+
## 命令
|
|
38
|
+
|
|
39
|
+
### init
|
|
40
|
+
|
|
41
|
+
创建新的 workspace。
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
agent-handoff init <name> [--path <path>]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
参数:
|
|
48
|
+
- `<name>` - workspace 名称
|
|
49
|
+
- `--path, -p` - 父目录路径(默认当前目录)
|
|
50
|
+
|
|
51
|
+
### status
|
|
52
|
+
|
|
53
|
+
显示 workspace 状态。
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
agent-handoff status [workspace] [--json]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
参数:
|
|
60
|
+
- `[workspace]` - workspace 路径(默认当前目录)
|
|
61
|
+
- `--json, -j` - JSON 格式输出
|
|
62
|
+
|
|
63
|
+
### next
|
|
64
|
+
|
|
65
|
+
输出下一步执行指令和 prompt。
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
agent-handoff next [workspace]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
参数:
|
|
72
|
+
- `[workspace]` - workspace 路径(默认当前目录)
|
|
73
|
+
|
|
74
|
+
## Workspace 结构
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
<workspace>/
|
|
78
|
+
├── workflow.yaml # 工作流定义
|
|
79
|
+
├── state.json # 当前状态
|
|
80
|
+
├── brief.md # 需求描述
|
|
81
|
+
├── events.jsonl # 事件日志
|
|
82
|
+
└── steps/
|
|
83
|
+
└── <nn>-<id>/
|
|
84
|
+
└── output.md
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 文档
|
|
88
|
+
|
|
89
|
+
- [项目愿景](docs/vision.md)
|
|
90
|
+
- [技术方案](docs/TECH_SPEC.md)
|
|
91
|
+
- [产物协议](docs/protocol.md)
|
|
92
|
+
- [路线图](docs/roadmap.md)
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command as Command4 } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/cli/commands/init.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import fs from "fs/promises";
|
|
9
|
+
import path from "path";
|
|
10
|
+
var initCommand = new Command("init").description("\u521B\u5EFA\u65B0\u7684 workspace").argument("<name>", "workspace \u540D\u79F0").option("-p, --path <path>", "\u7236\u76EE\u5F55\u8DEF\u5F84", process.cwd()).action(async (name, options) => {
|
|
11
|
+
const workspacePath = path.resolve(options.path, name);
|
|
12
|
+
try {
|
|
13
|
+
await fs.access(workspacePath);
|
|
14
|
+
console.error(`Error: workspace "${name}" already exists at ${workspacePath}`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
await fs.mkdir(workspacePath, { recursive: true });
|
|
20
|
+
await fs.mkdir(path.join(workspacePath, "steps"), { recursive: true });
|
|
21
|
+
const workflowTemplate = `name: ${name}
|
|
22
|
+
steps:
|
|
23
|
+
- id: clarify
|
|
24
|
+
executor: trae
|
|
25
|
+
input: brief.md
|
|
26
|
+
output: steps/01-clarify/output.md
|
|
27
|
+
acceptance:
|
|
28
|
+
- \u6F84\u6E05\u9700\u6C42\u8303\u56F4\u4E0E\u975E\u76EE\u6807
|
|
29
|
+
- \u4EA7\u51FA\u7ED3\u6784\u5316\u9700\u6C42\u6587\u6863
|
|
30
|
+
`;
|
|
31
|
+
const stateTemplate = JSON.stringify({
|
|
32
|
+
currentIndex: 0,
|
|
33
|
+
status: "running",
|
|
34
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35
|
+
}, null, 2);
|
|
36
|
+
const briefTemplate = `# brief\uFF1A\u9700\u6C42\u63CF\u8FF0
|
|
37
|
+
|
|
38
|
+
## \u80CC\u666F
|
|
39
|
+
\uFF08\u8BF7\u63CF\u8FF0\u9879\u76EE\u80CC\u666F\uFF09
|
|
40
|
+
|
|
41
|
+
## \u76EE\u6807
|
|
42
|
+
\uFF08\u8BF7\u63CF\u8FF0\u672C\u8F6E\u76EE\u6807\uFF09
|
|
43
|
+
|
|
44
|
+
## \u975E\u76EE\u6807
|
|
45
|
+
\uFF08\u8BF7\u63CF\u8FF0\u672C\u8F6E\u4E0D\u505A\u7684\u4E8B\u60C5\uFF09
|
|
46
|
+
|
|
47
|
+
## \u9A8C\u6536\u6807\u51C6
|
|
48
|
+
\uFF08\u8BF7\u63CF\u8FF0\u9A8C\u6536\u6807\u51C6\uFF09
|
|
49
|
+
`;
|
|
50
|
+
await fs.writeFile(path.join(workspacePath, "workflow.yaml"), workflowTemplate);
|
|
51
|
+
await fs.writeFile(path.join(workspacePath, "state.json"), stateTemplate);
|
|
52
|
+
await fs.writeFile(path.join(workspacePath, "brief.md"), briefTemplate);
|
|
53
|
+
console.log(`\u2705 Created workspace "${name}" at ${workspacePath}`);
|
|
54
|
+
console.log(`
|
|
55
|
+
Next steps:
|
|
56
|
+
1. Edit brief.md to describe your requirements
|
|
57
|
+
2. Run "agent-handoff status ${name}" to check workspace status
|
|
58
|
+
3. Run "agent-handoff next ${name}" to get the first step prompt
|
|
59
|
+
`);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(`Error creating workspace: ${error}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// src/cli/commands/status.ts
|
|
67
|
+
import { Command as Command2 } from "commander";
|
|
68
|
+
import path3 from "path";
|
|
69
|
+
|
|
70
|
+
// src/core/workspace.ts
|
|
71
|
+
import fs3 from "fs/promises";
|
|
72
|
+
import path2 from "path";
|
|
73
|
+
|
|
74
|
+
// src/core/workflow-parser.ts
|
|
75
|
+
import fs2 from "fs/promises";
|
|
76
|
+
import YAML from "yaml";
|
|
77
|
+
async function parseWorkflow(filePath) {
|
|
78
|
+
const content = await fs2.readFile(filePath, "utf-8");
|
|
79
|
+
const parsed = YAML.parse(content);
|
|
80
|
+
if (!parsed || typeof parsed !== "object") {
|
|
81
|
+
throw new Error(`Invalid workflow.yaml: ${filePath}`);
|
|
82
|
+
}
|
|
83
|
+
if (!parsed.name || typeof parsed.name !== "string") {
|
|
84
|
+
throw new Error("workflow.yaml missing required field: name");
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) {
|
|
87
|
+
throw new Error("workflow.yaml missing required field: steps (non-empty array)");
|
|
88
|
+
}
|
|
89
|
+
const steps = parsed.steps.map((step, index) => {
|
|
90
|
+
if (!step.id || typeof step.id !== "string") {
|
|
91
|
+
throw new Error(`Step ${index} missing required field: id`);
|
|
92
|
+
}
|
|
93
|
+
if (!step.executor || typeof step.executor !== "string") {
|
|
94
|
+
throw new Error(`Step ${index} missing required field: executor`);
|
|
95
|
+
}
|
|
96
|
+
if (!step.input || typeof step.input !== "string") {
|
|
97
|
+
throw new Error(`Step ${index} missing required field: input`);
|
|
98
|
+
}
|
|
99
|
+
if (!step.output || typeof step.output !== "string") {
|
|
100
|
+
throw new Error(`Step ${index} missing required field: output`);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
id: step.id,
|
|
104
|
+
executor: step.executor,
|
|
105
|
+
input: step.input,
|
|
106
|
+
output: step.output,
|
|
107
|
+
workItemId: step.workItemId,
|
|
108
|
+
acceptance: step.acceptance
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
name: parsed.name,
|
|
113
|
+
steps
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/core/workspace.ts
|
|
118
|
+
async function loadWorkspace(workspacePath) {
|
|
119
|
+
const absolutePath = path2.resolve(workspacePath);
|
|
120
|
+
const workflowPath = path2.join(absolutePath, "workflow.yaml");
|
|
121
|
+
const statePath = path2.join(absolutePath, "state.json");
|
|
122
|
+
let exists = false;
|
|
123
|
+
let hasWorkflow = false;
|
|
124
|
+
let hasState = false;
|
|
125
|
+
let workflow;
|
|
126
|
+
let state;
|
|
127
|
+
let stepOutputs = /* @__PURE__ */ new Map();
|
|
128
|
+
try {
|
|
129
|
+
await fs3.access(absolutePath);
|
|
130
|
+
exists = true;
|
|
131
|
+
} catch {
|
|
132
|
+
return {
|
|
133
|
+
path: absolutePath,
|
|
134
|
+
exists: false,
|
|
135
|
+
hasWorkflow: false,
|
|
136
|
+
hasState: false,
|
|
137
|
+
stepOutputs
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
await fs3.access(workflowPath);
|
|
142
|
+
hasWorkflow = true;
|
|
143
|
+
workflow = await parseWorkflow(workflowPath);
|
|
144
|
+
} catch {
|
|
145
|
+
hasWorkflow = false;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const stateContent = await fs3.readFile(statePath, "utf-8");
|
|
149
|
+
hasState = true;
|
|
150
|
+
state = JSON.parse(stateContent);
|
|
151
|
+
} catch {
|
|
152
|
+
hasState = false;
|
|
153
|
+
}
|
|
154
|
+
if (workflow) {
|
|
155
|
+
stepOutputs = await detectStepOutputs(absolutePath, workflow);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
path: absolutePath,
|
|
159
|
+
exists,
|
|
160
|
+
hasWorkflow,
|
|
161
|
+
hasState,
|
|
162
|
+
workflow,
|
|
163
|
+
state,
|
|
164
|
+
stepOutputs
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async function detectStepOutputs(workspacePath, workflow) {
|
|
168
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
169
|
+
for (const step of workflow.steps) {
|
|
170
|
+
const outputPath = path2.join(workspacePath, step.output);
|
|
171
|
+
try {
|
|
172
|
+
const content = await fs3.readFile(outputPath, "utf-8");
|
|
173
|
+
outputs.set(step.id, content.trim().length > 0);
|
|
174
|
+
} catch {
|
|
175
|
+
outputs.set(step.id, false);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return outputs;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/core/state-machine.ts
|
|
182
|
+
function computeState(workflow, stepOutputs) {
|
|
183
|
+
if (!workflow.steps || workflow.steps.length === 0) {
|
|
184
|
+
return {
|
|
185
|
+
currentIndex: 0,
|
|
186
|
+
status: "done",
|
|
187
|
+
nextStepIndex: null,
|
|
188
|
+
completedSteps: [],
|
|
189
|
+
pendingSteps: []
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const completedSteps = [];
|
|
193
|
+
const pendingSteps = [];
|
|
194
|
+
let currentIndex = 0;
|
|
195
|
+
let status = "running";
|
|
196
|
+
for (let i = 0; i < workflow.steps.length; i++) {
|
|
197
|
+
const step = workflow.steps[i];
|
|
198
|
+
const outputExists = stepOutputs.get(step.id) ?? false;
|
|
199
|
+
if (outputExists) {
|
|
200
|
+
completedSteps.push(i);
|
|
201
|
+
} else {
|
|
202
|
+
pendingSteps.push(i);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const firstPendingIndex = pendingSteps[0];
|
|
206
|
+
if (firstPendingIndex === void 0) {
|
|
207
|
+
currentIndex = workflow.steps.length;
|
|
208
|
+
status = "done";
|
|
209
|
+
} else {
|
|
210
|
+
currentIndex = firstPendingIndex;
|
|
211
|
+
status = "running";
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
currentIndex,
|
|
215
|
+
status,
|
|
216
|
+
nextStepIndex: status === "done" ? null : currentIndex,
|
|
217
|
+
completedSteps,
|
|
218
|
+
pendingSteps
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function getCurrentStep(workflow, stepOutputs) {
|
|
222
|
+
const result = computeState(workflow, stepOutputs);
|
|
223
|
+
if (result.status === "done" || result.nextStepIndex === null) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
index: result.nextStepIndex,
|
|
228
|
+
step: workflow.steps[result.nextStepIndex]
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/cli/commands/status.ts
|
|
233
|
+
var statusCommand = new Command2("status").description("\u663E\u793A workspace \u72B6\u6001").argument("[workspace]", "workspace \u8DEF\u5F84", ".").option("-j, --json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (workspace, options) => {
|
|
234
|
+
const workspacePath = path3.resolve(workspace);
|
|
235
|
+
try {
|
|
236
|
+
const info = await loadWorkspace(workspacePath);
|
|
237
|
+
if (!info.exists) {
|
|
238
|
+
console.error(`Error: workspace not found: ${workspacePath}`);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
if (!info.hasWorkflow) {
|
|
242
|
+
console.error(`Error: workflow.yaml not found in ${workspacePath}`);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
if (!info.workflow) {
|
|
246
|
+
console.error(`Error: failed to parse workflow.yaml`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
const stateResult = computeState(info.workflow, info.stepOutputs);
|
|
250
|
+
if (options.json) {
|
|
251
|
+
const jsonOutput = {
|
|
252
|
+
name: info.workflow.name,
|
|
253
|
+
path: workspacePath,
|
|
254
|
+
status: stateResult.status,
|
|
255
|
+
currentIndex: stateResult.currentIndex,
|
|
256
|
+
totalSteps: info.workflow.steps.length,
|
|
257
|
+
completedSteps: stateResult.completedSteps.length,
|
|
258
|
+
steps: info.workflow.steps.map((step, index) => ({
|
|
259
|
+
index,
|
|
260
|
+
id: step.id,
|
|
261
|
+
executor: step.executor,
|
|
262
|
+
workItemId: step.workItemId,
|
|
263
|
+
completed: info.stepOutputs.get(step.id) ?? false
|
|
264
|
+
}))
|
|
265
|
+
};
|
|
266
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
267
|
+
} else {
|
|
268
|
+
console.log(`Workspace: ${info.workflow.name}`);
|
|
269
|
+
console.log(`Status: ${stateResult.status}`);
|
|
270
|
+
console.log("");
|
|
271
|
+
console.log("Steps:");
|
|
272
|
+
info.workflow.steps.forEach((step, index) => {
|
|
273
|
+
const completed = info.stepOutputs.get(step.id) ?? false;
|
|
274
|
+
const statusIcon = completed ? "\u2705" : "\u2B1C";
|
|
275
|
+
const stepNum = String(index + 1).padStart(2, "0");
|
|
276
|
+
let line = ` ${statusIcon} ${stepNum}-${step.id} (${step.executor})`;
|
|
277
|
+
if (step.workItemId) {
|
|
278
|
+
line += ` [${step.workItemId}]`;
|
|
279
|
+
}
|
|
280
|
+
console.log(line);
|
|
281
|
+
});
|
|
282
|
+
console.log("");
|
|
283
|
+
if (stateResult.status === "done") {
|
|
284
|
+
console.log("Current: completed");
|
|
285
|
+
} else {
|
|
286
|
+
const currentStep = info.workflow.steps[stateResult.currentIndex];
|
|
287
|
+
console.log(`Current: step ${stateResult.currentIndex + 1} (${currentStep?.id})`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error(`Error: ${error}`);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// src/cli/commands/next.ts
|
|
297
|
+
import { Command as Command3 } from "commander";
|
|
298
|
+
import path4 from "path";
|
|
299
|
+
|
|
300
|
+
// src/core/prompt-generator.ts
|
|
301
|
+
function generatePrompt(context) {
|
|
302
|
+
const { workflow, step, stepIndex } = context;
|
|
303
|
+
const totalSteps = workflow.steps.length;
|
|
304
|
+
const stepNum = stepIndex + 1;
|
|
305
|
+
let prompt = `# \u4EFB\u52A1\uFF1A${step.id}
|
|
306
|
+
|
|
307
|
+
## \u4E0A\u4E0B\u6587
|
|
308
|
+
- Workflow: ${workflow.name}
|
|
309
|
+
- Step: ${stepNum} / ${totalSteps}
|
|
310
|
+
- Executor: ${step.executor}`;
|
|
311
|
+
if (step.workItemId) {
|
|
312
|
+
prompt += `
|
|
313
|
+
- Work Item: ${step.workItemId}`;
|
|
314
|
+
}
|
|
315
|
+
prompt += `
|
|
316
|
+
|
|
317
|
+
## \u8F93\u5165\u4EA7\u7269
|
|
318
|
+
\u8BF7\u9605\u8BFB\u4EE5\u4E0B\u8F93\u5165\u4EA7\u7269\uFF1A
|
|
319
|
+
- ${step.input}
|
|
320
|
+
|
|
321
|
+
## \u8F93\u51FA\u4EA7\u7269
|
|
322
|
+
\u8BF7\u5C06\u7ED3\u679C\u5199\u5165\uFF1A
|
|
323
|
+
- ${step.output}`;
|
|
324
|
+
if (step.acceptance && step.acceptance.length > 0) {
|
|
325
|
+
prompt += `
|
|
326
|
+
|
|
327
|
+
## \u9A8C\u6536\u6807\u51C6`;
|
|
328
|
+
for (const criteria of step.acceptance) {
|
|
329
|
+
prompt += `
|
|
330
|
+
- ${criteria}`;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
prompt += `
|
|
334
|
+
|
|
335
|
+
## \u8F93\u51FA\u8981\u6C42
|
|
336
|
+
\u5B8C\u6210\u540E\u8BF7\u5728 output.md \u4E2D\u5305\u542B\u4EE5\u4E0B\u533A\u5757\uFF1A
|
|
337
|
+
- \u4EA7\u7269\u66F4\u65B0
|
|
338
|
+
- \u5173\u952E\u51B3\u7B56
|
|
339
|
+
- \u98CE\u9669\u4E0E\u5F85\u786E\u8BA4
|
|
340
|
+
- \u4E0B\u4E00\u6B65\u4EA4\u63A5
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
AgentHandoff Step Prompt`;
|
|
344
|
+
return prompt;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// src/cli/commands/next.ts
|
|
348
|
+
var nextCommand = new Command3("next").description("\u8F93\u51FA\u4E0B\u4E00\u6B65\u6267\u884C\u6307\u4EE4\u548C prompt").argument("[workspace]", "workspace \u8DEF\u5F84", ".").option("-c, --copy", "\u590D\u5236 prompt \u5230\u526A\u8D34\u677F\uFF08v0.2\uFF09").action(async (workspace, options) => {
|
|
349
|
+
const workspacePath = path4.resolve(workspace);
|
|
350
|
+
try {
|
|
351
|
+
const info = await loadWorkspace(workspacePath);
|
|
352
|
+
if (!info.exists) {
|
|
353
|
+
console.error(`Error: workspace not found: ${workspacePath}`);
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
if (!info.hasWorkflow) {
|
|
357
|
+
console.error(`Error: workflow.yaml not found in ${workspacePath}`);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
if (!info.workflow) {
|
|
361
|
+
console.error(`Error: failed to parse workflow.yaml`);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
const stateResult = computeState(info.workflow, info.stepOutputs);
|
|
365
|
+
if (stateResult.status === "done") {
|
|
366
|
+
console.log(`Workflow "${info.workflow.name}" \u5DF2\u5B8C\u6210\u6240\u6709\u6B65\u9AA4\u3002`);
|
|
367
|
+
console.log("\u65E0\u4E0B\u4E00\u6B65\u64CD\u4F5C\u3002");
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const currentStep = getCurrentStep(info.workflow, info.stepOutputs);
|
|
371
|
+
if (!currentStep) {
|
|
372
|
+
console.log(`Workflow "${info.workflow.name}" \u5DF2\u5B8C\u6210\u6240\u6709\u6B65\u9AA4\u3002`);
|
|
373
|
+
console.log("\u65E0\u4E0B\u4E00\u6B65\u64CD\u4F5C\u3002");
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const { step, index } = currentStep;
|
|
377
|
+
console.log(`Step: ${step.id}`);
|
|
378
|
+
console.log(`Executor: ${step.executor}`);
|
|
379
|
+
if (step.workItemId) {
|
|
380
|
+
console.log(`Work Item: ${step.workItemId}`);
|
|
381
|
+
}
|
|
382
|
+
console.log("");
|
|
383
|
+
console.log("Input:");
|
|
384
|
+
console.log(` - ${step.input}`);
|
|
385
|
+
console.log("");
|
|
386
|
+
console.log("Output:");
|
|
387
|
+
console.log(` - ${step.output}`);
|
|
388
|
+
console.log("");
|
|
389
|
+
console.log("Prompt:");
|
|
390
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
391
|
+
const prompt = generatePrompt({
|
|
392
|
+
workflow: info.workflow,
|
|
393
|
+
step,
|
|
394
|
+
stepIndex: index,
|
|
395
|
+
workspacePath: info.path
|
|
396
|
+
});
|
|
397
|
+
console.log(prompt);
|
|
398
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
399
|
+
console.log("");
|
|
400
|
+
console.log("\u63D0\u793A\uFF1A\u5C06\u4E0A\u8FF0 Prompt \u590D\u5236\u5230 TRAE \u65B0 Task \u4E2D\u6267\u884C");
|
|
401
|
+
if (options.copy) {
|
|
402
|
+
console.log("");
|
|
403
|
+
console.log("\u6CE8\u610F\uFF1A\u526A\u8D34\u677F\u529F\u80FD\u5C06\u5728 v0.2 \u7248\u672C\u5B9E\u73B0");
|
|
404
|
+
}
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.error(`Error: ${error}`);
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// src/index.ts
|
|
412
|
+
var program = new Command4();
|
|
413
|
+
program.name("agent-handoff").description("\u8F7B\u91CF\u7EA7\u591A Agent \u534F\u4F5C\u63A5\u529B\u5DE5\u5177").version("0.1.0");
|
|
414
|
+
program.addCommand(initCommand);
|
|
415
|
+
program.addCommand(statusCommand);
|
|
416
|
+
program.addCommand(nextCommand);
|
|
417
|
+
program.parse();
|
|
418
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/cli/commands/init.ts","../src/cli/commands/status.ts","../src/core/workspace.ts","../src/core/workflow-parser.ts","../src/core/state-machine.ts","../src/cli/commands/next.ts","../src/core/prompt-generator.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { initCommand } from './cli/commands/init.js';\nimport { statusCommand } from './cli/commands/status.js';\nimport { nextCommand } from './cli/commands/next.js';\n\nconst program = new Command();\n\nprogram\n .name('agent-handoff')\n .description('轻量级多 Agent 协作接力工具')\n .version('0.1.0');\n\nprogram.addCommand(initCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(nextCommand);\n\nprogram.parse();\n","import { Command } from 'commander';\nimport fs from 'fs/promises';\nimport path from 'path';\n\nexport const initCommand = new Command('init')\n .description('创建新的 workspace')\n .argument('<name>', 'workspace 名称')\n .option('-p, --path <path>', '父目录路径', process.cwd())\n .action(async (name: string, options: { path: string }) => {\n const workspacePath = path.resolve(options.path, name);\n\n try {\n await fs.access(workspacePath);\n console.error(`Error: workspace \"${name}\" already exists at ${workspacePath}`);\n process.exit(1);\n } catch {\n // 目录不存在,继续创建\n }\n\n try {\n await fs.mkdir(workspacePath, { recursive: true });\n await fs.mkdir(path.join(workspacePath, 'steps'), { recursive: true });\n\n const workflowTemplate = `name: ${name}\nsteps:\n - id: clarify\n executor: trae\n input: brief.md\n output: steps/01-clarify/output.md\n acceptance:\n - 澄清需求范围与非目标\n - 产出结构化需求文档\n`;\n\n const stateTemplate = JSON.stringify({\n currentIndex: 0,\n status: 'running',\n updatedAt: new Date().toISOString(),\n }, null, 2);\n\n const briefTemplate = `# brief:需求描述\n\n## 背景\n(请描述项目背景)\n\n## 目标\n(请描述本轮目标)\n\n## 非目标\n(请描述本轮不做的事情)\n\n## 验收标准\n(请描述验收标准)\n`;\n\n await fs.writeFile(path.join(workspacePath, 'workflow.yaml'), workflowTemplate);\n await fs.writeFile(path.join(workspacePath, 'state.json'), stateTemplate);\n await fs.writeFile(path.join(workspacePath, 'brief.md'), briefTemplate);\n\n console.log(`✅ Created workspace \"${name}\" at ${workspacePath}`);\n console.log(`\nNext steps:\n 1. Edit brief.md to describe your requirements\n 2. Run \"agent-handoff status ${name}\" to check workspace status\n 3. Run \"agent-handoff next ${name}\" to get the first step prompt\n`);\n } catch (error) {\n console.error(`Error creating workspace: ${error}`);\n process.exit(1);\n }\n });\n","import { Command } from 'commander';\nimport path from 'path';\nimport { loadWorkspace } from '../../core/workspace.js';\nimport { computeState } from '../../core/state-machine.js';\n\nexport const statusCommand = new Command('status')\n .description('显示 workspace 状态')\n .argument('[workspace]', 'workspace 路径', '.')\n .option('-j, --json', 'JSON 格式输出')\n .action(async (workspace: string, options: { json: boolean }) => {\n const workspacePath = path.resolve(workspace);\n\n try {\n const info = await loadWorkspace(workspacePath);\n\n if (!info.exists) {\n console.error(`Error: workspace not found: ${workspacePath}`);\n process.exit(1);\n }\n\n if (!info.hasWorkflow) {\n console.error(`Error: workflow.yaml not found in ${workspacePath}`);\n process.exit(1);\n }\n\n if (!info.workflow) {\n console.error(`Error: failed to parse workflow.yaml`);\n process.exit(1);\n }\n\n const stateResult = computeState(info.workflow, info.stepOutputs);\n\n if (options.json) {\n const jsonOutput = {\n name: info.workflow.name,\n path: workspacePath,\n status: stateResult.status,\n currentIndex: stateResult.currentIndex,\n totalSteps: info.workflow.steps.length,\n completedSteps: stateResult.completedSteps.length,\n steps: info.workflow.steps.map((step, index) => ({\n index,\n id: step.id,\n executor: step.executor,\n workItemId: step.workItemId,\n completed: info.stepOutputs.get(step.id) ?? false,\n })),\n };\n console.log(JSON.stringify(jsonOutput, null, 2));\n } else {\n console.log(`Workspace: ${info.workflow.name}`);\n console.log(`Status: ${stateResult.status}`);\n console.log('');\n console.log('Steps:');\n\n info.workflow.steps.forEach((step, index) => {\n const completed = info.stepOutputs.get(step.id) ?? false;\n const statusIcon = completed ? '✅' : '⬜';\n const stepNum = String(index + 1).padStart(2, '0');\n let line = ` ${statusIcon} ${stepNum}-${step.id} (${step.executor})`;\n if (step.workItemId) {\n line += ` [${step.workItemId}]`;\n }\n console.log(line);\n });\n\n console.log('');\n if (stateResult.status === 'done') {\n console.log('Current: completed');\n } else {\n const currentStep = info.workflow.steps[stateResult.currentIndex];\n console.log(`Current: step ${stateResult.currentIndex + 1} (${currentStep?.id})`);\n }\n }\n } catch (error) {\n console.error(`Error: ${error}`);\n process.exit(1);\n }\n });\n","import fs from 'fs/promises';\nimport path from 'path';\nimport { Workflow } from './models/workflow.js';\nimport { State } from './models/state.js';\nimport { parseWorkflow } from './workflow-parser.js';\n\nexport interface WorkspaceInfo {\n path: string;\n exists: boolean;\n hasWorkflow: boolean;\n hasState: boolean;\n workflow?: Workflow;\n state?: State;\n stepOutputs: Map<string, boolean>;\n}\n\nexport async function loadWorkspace(workspacePath: string): Promise<WorkspaceInfo> {\n const absolutePath = path.resolve(workspacePath);\n const workflowPath = path.join(absolutePath, 'workflow.yaml');\n const statePath = path.join(absolutePath, 'state.json');\n\n let exists = false;\n let hasWorkflow = false;\n let hasState = false;\n let workflow: Workflow | undefined;\n let state: State | undefined;\n let stepOutputs = new Map<string, boolean>();\n\n try {\n await fs.access(absolutePath);\n exists = true;\n } catch {\n return {\n path: absolutePath,\n exists: false,\n hasWorkflow: false,\n hasState: false,\n stepOutputs,\n };\n }\n\n try {\n await fs.access(workflowPath);\n hasWorkflow = true;\n workflow = await parseWorkflow(workflowPath);\n } catch {\n hasWorkflow = false;\n }\n\n try {\n const stateContent = await fs.readFile(statePath, 'utf-8');\n hasState = true;\n state = JSON.parse(stateContent) as State;\n } catch {\n hasState = false;\n }\n\n if (workflow) {\n stepOutputs = await detectStepOutputs(absolutePath, workflow);\n }\n\n return {\n path: absolutePath,\n exists,\n hasWorkflow,\n hasState,\n workflow,\n state,\n stepOutputs,\n };\n}\n\nexport async function detectStepOutputs(\n workspacePath: string,\n workflow: Workflow\n): Promise<Map<string, boolean>> {\n const outputs = new Map<string, boolean>();\n\n for (const step of workflow.steps) {\n const outputPath = path.join(workspacePath, step.output);\n try {\n const content = await fs.readFile(outputPath, 'utf-8');\n outputs.set(step.id, content.trim().length > 0);\n } catch {\n outputs.set(step.id, false);\n }\n }\n\n return outputs;\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function fileNotEmpty(filePath: string): Promise<boolean> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n return content.trim().length > 0;\n } catch {\n return false;\n }\n}\n","import fs from 'fs/promises';\nimport YAML from 'yaml';\nimport { Workflow, Step, Executor } from './models/workflow';\n\nexport async function parseWorkflow(filePath: string): Promise<Workflow> {\n const content = await fs.readFile(filePath, 'utf-8');\n const parsed = YAML.parse(content);\n \n if (!parsed || typeof parsed !== 'object') {\n throw new Error(`Invalid workflow.yaml: ${filePath}`);\n }\n \n if (!parsed.name || typeof parsed.name !== 'string') {\n throw new Error('workflow.yaml missing required field: name');\n }\n \n if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) {\n throw new Error('workflow.yaml missing required field: steps (non-empty array)');\n }\n \n const steps: Step[] = parsed.steps.map((step: Record<string, unknown>, index: number) => {\n if (!step.id || typeof step.id !== 'string') {\n throw new Error(`Step ${index} missing required field: id`);\n }\n if (!step.executor || typeof step.executor !== 'string') {\n throw new Error(`Step ${index} missing required field: executor`);\n }\n if (!step.input || typeof step.input !== 'string') {\n throw new Error(`Step ${index} missing required field: input`);\n }\n if (!step.output || typeof step.output !== 'string') {\n throw new Error(`Step ${index} missing required field: output`);\n }\n \n return {\n id: step.id,\n executor: step.executor as Executor,\n input: step.input,\n output: step.output,\n workItemId: step.workItemId as string | undefined,\n acceptance: step.acceptance as string[] | undefined,\n };\n });\n \n return {\n name: parsed.name,\n steps,\n };\n}\n\nexport function validateWorkflow(workflow: Workflow): string[] {\n const errors: string[] = [];\n \n if (!workflow.name || workflow.name.trim() === '') {\n errors.push('workflow.name is required');\n }\n \n if (!workflow.steps || workflow.steps.length === 0) {\n errors.push('workflow.steps is required and must be non-empty');\n return errors;\n }\n \n workflow.steps.forEach((step, index) => {\n if (!step.id || step.id.trim() === '') {\n errors.push(`steps[${index}].id is required`);\n }\n if (!step.executor) {\n errors.push(`steps[${index}].executor is required`);\n }\n if (!step.input || step.input.trim() === '') {\n errors.push(`steps[${index}].input is required`);\n }\n if (!step.output || step.output.trim() === '') {\n errors.push(`steps[${index}].output is required`);\n }\n if (!step.output.startsWith('steps/')) {\n errors.push(`steps[${index}].output should start with 'steps/'`);\n }\n });\n \n return errors;\n}\n","import { Workflow } from './models/workflow';\nimport { State, WorkflowStatus } from './models/state';\n\nexport interface StateMachineResult {\n currentIndex: number;\n status: WorkflowStatus;\n nextStepIndex: number | null;\n completedSteps: number[];\n pendingSteps: number[];\n}\n\nexport function computeState(\n workflow: Workflow,\n stepOutputs: Map<string, boolean>\n): StateMachineResult {\n if (!workflow.steps || workflow.steps.length === 0) {\n return {\n currentIndex: 0,\n status: 'done',\n nextStepIndex: null,\n completedSteps: [],\n pendingSteps: [],\n };\n }\n\n const completedSteps: number[] = [];\n const pendingSteps: number[] = [];\n let currentIndex = 0;\n let status: WorkflowStatus = 'running';\n\n for (let i = 0; i < workflow.steps.length; i++) {\n const step = workflow.steps[i];\n const outputExists = stepOutputs.get(step.id) ?? false;\n\n if (outputExists) {\n completedSteps.push(i);\n } else {\n pendingSteps.push(i);\n }\n }\n\n const firstPendingIndex = pendingSteps[0];\n\n if (firstPendingIndex === undefined) {\n currentIndex = workflow.steps.length;\n status = 'done';\n } else {\n currentIndex = firstPendingIndex;\n status = 'running';\n }\n\n return {\n currentIndex,\n status,\n nextStepIndex: status === 'done' ? null : currentIndex,\n completedSteps,\n pendingSteps,\n };\n}\n\nexport function advanceState(\n state: State,\n workflow: Workflow,\n stepOutputs: Map<string, boolean>\n): State {\n const result = computeState(workflow, stepOutputs);\n\n return {\n ...state,\n currentIndex: result.currentIndex,\n status: result.status,\n updatedAt: new Date().toISOString(),\n };\n}\n\nexport function isWorkflowComplete(\n workflow: Workflow,\n stepOutputs: Map<string, boolean>\n): boolean {\n if (!workflow.steps || workflow.steps.length === 0) {\n return true;\n }\n\n return workflow.steps.every((step) => stepOutputs.get(step.id) === true);\n}\n\nexport function getCurrentStep(\n workflow: Workflow,\n stepOutputs: Map<string, boolean>\n): { index: number; step: typeof workflow.steps[0] } | null {\n const result = computeState(workflow, stepOutputs);\n\n if (result.status === 'done' || result.nextStepIndex === null) {\n return null;\n }\n\n return {\n index: result.nextStepIndex,\n step: workflow.steps[result.nextStepIndex],\n };\n}\n","import { Command } from 'commander';\nimport path from 'path';\nimport { loadWorkspace } from '../../core/workspace.js';\nimport { computeState, getCurrentStep } from '../../core/state-machine.js';\nimport { generatePrompt } from '../../core/prompt-generator.js';\n\nexport const nextCommand = new Command('next')\n .description('输出下一步执行指令和 prompt')\n .argument('[workspace]', 'workspace 路径', '.')\n .option('-c, --copy', '复制 prompt 到剪贴板(v0.2)')\n .action(async (workspace: string, options: { copy: boolean }) => {\n const workspacePath = path.resolve(workspace);\n\n try {\n const info = await loadWorkspace(workspacePath);\n\n if (!info.exists) {\n console.error(`Error: workspace not found: ${workspacePath}`);\n process.exit(1);\n }\n\n if (!info.hasWorkflow) {\n console.error(`Error: workflow.yaml not found in ${workspacePath}`);\n process.exit(1);\n }\n\n if (!info.workflow) {\n console.error(`Error: failed to parse workflow.yaml`);\n process.exit(1);\n }\n\n const stateResult = computeState(info.workflow, info.stepOutputs);\n\n if (stateResult.status === 'done') {\n console.log(`Workflow \"${info.workflow.name}\" 已完成所有步骤。`);\n console.log('无下一步操作。');\n return;\n }\n\n const currentStep = getCurrentStep(info.workflow, info.stepOutputs);\n\n if (!currentStep) {\n console.log(`Workflow \"${info.workflow.name}\" 已完成所有步骤。`);\n console.log('无下一步操作。');\n return;\n }\n\n const { step, index } = currentStep;\n\n console.log(`Step: ${step.id}`);\n console.log(`Executor: ${step.executor}`);\n if (step.workItemId) {\n console.log(`Work Item: ${step.workItemId}`);\n }\n console.log('');\n console.log('Input:');\n console.log(` - ${step.input}`);\n console.log('');\n console.log('Output:');\n console.log(` - ${step.output}`);\n console.log('');\n console.log('Prompt:');\n console.log('────────────────────────────────────────');\n\n const prompt = generatePrompt({\n workflow: info.workflow,\n step,\n stepIndex: index,\n workspacePath: info.path,\n });\n\n console.log(prompt);\n console.log('────────────────────────────────────────');\n console.log('');\n console.log('提示:将上述 Prompt 复制到 TRAE 新 Task 中执行');\n\n if (options.copy) {\n console.log('');\n console.log('注意:剪贴板功能将在 v0.2 版本实现');\n }\n } catch (error) {\n console.error(`Error: ${error}`);\n process.exit(1);\n }\n });\n","import { Workflow, Step } from './models/workflow.js';\n\nexport interface PromptContext {\n workflow: Workflow;\n step: Step;\n stepIndex: number;\n workspacePath: string;\n}\n\nexport function generatePrompt(context: PromptContext): string {\n const { workflow, step, stepIndex } = context;\n const totalSteps = workflow.steps.length;\n const stepNum = stepIndex + 1;\n\n let prompt = `# 任务:${step.id}\n\n## 上下文\n- Workflow: ${workflow.name}\n- Step: ${stepNum} / ${totalSteps}\n- Executor: ${step.executor}`;\n\n if (step.workItemId) {\n prompt += `\\n- Work Item: ${step.workItemId}`;\n }\n\n prompt += `\n\n## 输入产物\n请阅读以下输入产物:\n- ${step.input}\n\n## 输出产物\n请将结果写入:\n- ${step.output}`;\n\n if (step.acceptance && step.acceptance.length > 0) {\n prompt += `\n\n## 验收标准`;\n for (const criteria of step.acceptance) {\n prompt += `\\n- ${criteria}`;\n }\n }\n\n prompt += `\n\n## 输出要求\n完成后请在 output.md 中包含以下区块:\n- 产物更新\n- 关键决策\n- 风险与待确认\n- 下一步交接\n\n---\nAgentHandoff Step Prompt`;\n\n return prompt;\n}\n"],"mappings":"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","names":["Command","Command","path","fs","path","fs","path","fs","Command","path","Command","path","Command","path","Command"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-handoff",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "轻量级多 Agent 协作接力工具",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": "",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/your-org/agent-handoff.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"agent",
|
|
14
|
+
"workflow",
|
|
15
|
+
"ai-coding",
|
|
16
|
+
"trae",
|
|
17
|
+
"collaboration"
|
|
18
|
+
],
|
|
19
|
+
"bin": {
|
|
20
|
+
"agent-handoff": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"CHANGELOG.md"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
|
+
"test": "vitest",
|
|
31
|
+
"test:run": "vitest run",
|
|
32
|
+
"lint": "eslint src",
|
|
33
|
+
"typecheck": "tsc --noEmit"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"commander": "^12.0.0",
|
|
37
|
+
"yaml": "^2.4.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^20.0.0",
|
|
41
|
+
"tsup": "^8.0.0",
|
|
42
|
+
"typescript": "^5.3.0",
|
|
43
|
+
"vitest": "^1.0.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
}
|
|
48
|
+
}
|