agent-handoff 0.1.1 → 0.3.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 +33 -0
- package/README.md +35 -1
- package/dist/index.js +847 -7
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.3.0] - 2026-03-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- TRAE 自动化能力(可选):视觉识别、自动输入、任务等待(Nut.js 路线)
|
|
9
|
+
- 自动化操作日志:会话级 JSONL 落盘(`workspace/operations/`)
|
|
10
|
+
- 自动化诊断落盘:失败时输出 diagnostics JSON(`workspace/diagnostics/`)
|
|
11
|
+
- 自动化降级:失败时输出可继续手工执行的辅助模式提示,并可复制 prompt 到剪贴板
|
|
12
|
+
- `agent-handoff report` 命令:从 operations JSONL 生成 json/markdown/html 报告
|
|
13
|
+
- 新事件类型:`automation.session`(events.jsonl,支持 data 字段承载统计信息)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `TraeAdapter.execute()` 支持恢复重试与失败降级输出
|
|
17
|
+
|
|
18
|
+
## [0.2.0] - 2026-03-02
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `agent-handoff validate` 命令 - 校验产物结构完整性
|
|
22
|
+
- `agent-handoff advance` 命令 - 推进 workspace 状态
|
|
23
|
+
- `agent-handoff config` 命令 - 查看和管理配置
|
|
24
|
+
- `--copy` 选项 - 自动复制 prompt 到剪贴板(跨平台支持)
|
|
25
|
+
- `--no-event` 选项 - 跳过事件日志写入
|
|
26
|
+
- 产物校验模块 - 检查 output.md 必要区块
|
|
27
|
+
- 剪贴板模块 - 跨平台剪贴板操作
|
|
28
|
+
- 事件写入模块 - events.jsonl 追加式日志
|
|
29
|
+
- 配置文件支持 - `.agenthandoffrc` / `.agenthandoffrc.yaml` / `package.json#agenthandoff`
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- `next` 命令支持 `--no-event` 选项跳过事件写入
|
|
33
|
+
- 改进 prompt 输出格式
|
|
34
|
+
|
|
35
|
+
### Dependencies
|
|
36
|
+
- 新增 `clipboardy` 依赖(跨平台剪贴板)
|
|
37
|
+
|
|
5
38
|
## [0.1.1] - 2026-03-02
|
|
6
39
|
|
|
7
40
|
### Changed
|
package/README.md
CHANGED
|
@@ -65,11 +65,45 @@ agent-handoff status [workspace] [--json]
|
|
|
65
65
|
输出下一步执行指令和 prompt。
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
agent-handoff next [workspace]
|
|
68
|
+
agent-handoff next [workspace] [--copy] [--no-event]
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
参数:
|
|
72
72
|
- `[workspace]` - workspace 路径(默认当前目录)
|
|
73
|
+
- `--copy, -c` - 复制 prompt 到剪贴板
|
|
74
|
+
- `--no-event` - 不写入 events.jsonl
|
|
75
|
+
|
|
76
|
+
### validate
|
|
77
|
+
|
|
78
|
+
校验 workflow 与产物结构。
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
agent-handoff validate [workspace]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### advance
|
|
85
|
+
|
|
86
|
+
手动推进 workflow 状态,并可写入事件。
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
agent-handoff advance [workspace]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### config
|
|
93
|
+
|
|
94
|
+
查看或生成配置文件。
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
agent-handoff config [workspace]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### report
|
|
101
|
+
|
|
102
|
+
读取 workspace 下的自动化操作日志并生成报告(json/markdown/html)。
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
agent-handoff report [workspace] [--format markdown|json|html] [--session <id>] [--screenshots] [--output <path>]
|
|
106
|
+
```
|
|
73
107
|
|
|
74
108
|
## Workspace 结构
|
|
75
109
|
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
-
import { Command as
|
|
5
|
+
import { Command as Command8 } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/cli/commands/init.ts
|
|
8
8
|
import { Command } from "commander";
|
|
@@ -296,7 +296,7 @@ var statusCommand = new Command2("status").description("\u663E\u793A workspace \
|
|
|
296
296
|
|
|
297
297
|
// src/cli/commands/next.ts
|
|
298
298
|
import { Command as Command3 } from "commander";
|
|
299
|
-
import
|
|
299
|
+
import path5 from "path";
|
|
300
300
|
|
|
301
301
|
// src/core/prompt-generator.ts
|
|
302
302
|
function generatePrompt(context) {
|
|
@@ -345,9 +345,69 @@ AgentHandoff Step Prompt`;
|
|
|
345
345
|
return prompt;
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
// src/core/clipboard.ts
|
|
349
|
+
import clipboard from "clipboardy";
|
|
350
|
+
var clipboardSupported = null;
|
|
351
|
+
function isClipboardSupported() {
|
|
352
|
+
if (clipboardSupported !== null) {
|
|
353
|
+
return clipboardSupported;
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
clipboardSupported = true;
|
|
357
|
+
return true;
|
|
358
|
+
} catch {
|
|
359
|
+
clipboardSupported = false;
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async function copyToClipboard(text) {
|
|
364
|
+
try {
|
|
365
|
+
await clipboard.write(text);
|
|
366
|
+
return { success: true };
|
|
367
|
+
} catch (error) {
|
|
368
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
369
|
+
return {
|
|
370
|
+
success: false,
|
|
371
|
+
error: errorMessage
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/core/events-writer.ts
|
|
377
|
+
import fs4 from "fs/promises";
|
|
378
|
+
import path4 from "path";
|
|
379
|
+
async function writeEvent(options) {
|
|
380
|
+
const { workspacePath, step, type, summary, workItemId, links, data } = options;
|
|
381
|
+
const event = {
|
|
382
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
383
|
+
step,
|
|
384
|
+
type,
|
|
385
|
+
summary,
|
|
386
|
+
...workItemId && { workItemId },
|
|
387
|
+
...links && links.length > 0 && { links },
|
|
388
|
+
...data && { data }
|
|
389
|
+
};
|
|
390
|
+
try {
|
|
391
|
+
const eventsPath = path4.join(workspacePath, "events.jsonl");
|
|
392
|
+
const line = JSON.stringify(event) + "\n";
|
|
393
|
+
await fs4.appendFile(eventsPath, line, "utf-8");
|
|
394
|
+
return {
|
|
395
|
+
success: true,
|
|
396
|
+
event
|
|
397
|
+
};
|
|
398
|
+
} catch (error) {
|
|
399
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
400
|
+
return {
|
|
401
|
+
success: false,
|
|
402
|
+
event,
|
|
403
|
+
error: errorMessage
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
348
408
|
// src/cli/commands/next.ts
|
|
349
|
-
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
|
|
350
|
-
const workspacePath =
|
|
409
|
+
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").option("--no-event", "\u4E0D\u5199\u5165\u4E8B\u4EF6\u65E5\u5FD7").action(async (workspace, options) => {
|
|
410
|
+
const workspacePath = path5.resolve(workspace);
|
|
351
411
|
try {
|
|
352
412
|
const info = await loadWorkspace(workspacePath);
|
|
353
413
|
if (!info.exists) {
|
|
@@ -398,24 +458,804 @@ var nextCommand = new Command3("next").description("\u8F93\u51FA\u4E0B\u4E00\u6B
|
|
|
398
458
|
console.log(prompt);
|
|
399
459
|
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");
|
|
400
460
|
console.log("");
|
|
401
|
-
|
|
461
|
+
if (options.event) {
|
|
462
|
+
await writeEvent({
|
|
463
|
+
workspacePath,
|
|
464
|
+
step: { index: index + 1, id: step.id },
|
|
465
|
+
type: "step.started",
|
|
466
|
+
summary: `\u5F00\u59CB\u6267\u884C\u6B65\u9AA4: ${step.id}`,
|
|
467
|
+
workItemId: step.workItemId,
|
|
468
|
+
links: [step.input]
|
|
469
|
+
});
|
|
470
|
+
}
|
|
402
471
|
if (options.copy) {
|
|
472
|
+
if (!isClipboardSupported()) {
|
|
473
|
+
console.log("\u26A0\uFE0F \u526A\u8D34\u677F\u529F\u80FD\u5728\u5F53\u524D\u73AF\u5883\u4E0D\u53EF\u7528");
|
|
474
|
+
console.log("\u8BF7\u624B\u52A8\u590D\u5236\u4E0A\u9762\u7684 Prompt");
|
|
475
|
+
} else {
|
|
476
|
+
const result = await copyToClipboard(prompt);
|
|
477
|
+
if (result.success) {
|
|
478
|
+
console.log("\u2705 Prompt \u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F");
|
|
479
|
+
} else {
|
|
480
|
+
console.log(`\u274C \u590D\u5236\u5931\u8D25: ${result.error}`);
|
|
481
|
+
console.log("\u8BF7\u624B\u52A8\u590D\u5236\u4E0A\u9762\u7684 Prompt");
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
console.log("\u63D0\u793A\uFF1A\u5C06\u4E0A\u8FF0 Prompt \u590D\u5236\u5230 TRAE \u65B0 Task \u4E2D\u6267\u884C");
|
|
486
|
+
console.log(" \u4F7F\u7528 --copy \u9009\u9879\u53EF\u81EA\u52A8\u590D\u5236\u5230\u526A\u8D34\u677F");
|
|
487
|
+
}
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error(`Error: ${error}`);
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// src/cli/commands/validate.ts
|
|
495
|
+
import { Command as Command4 } from "commander";
|
|
496
|
+
import path7 from "path";
|
|
497
|
+
|
|
498
|
+
// src/core/artifact-validator.ts
|
|
499
|
+
import fs5 from "fs/promises";
|
|
500
|
+
import path6 from "path";
|
|
501
|
+
var REQUIRED_SECTIONS = [
|
|
502
|
+
"\u4EA7\u7269\u66F4\u65B0",
|
|
503
|
+
"\u5173\u952E\u51B3\u7B56",
|
|
504
|
+
"\u98CE\u9669\u4E0E\u5F85\u786E\u8BA4",
|
|
505
|
+
"\u4E0B\u4E00\u6B65\u4EA4\u63A5"
|
|
506
|
+
];
|
|
507
|
+
var MIN_CONTENT_LENGTH = 10;
|
|
508
|
+
function validateArtifact(content) {
|
|
509
|
+
const errors = [];
|
|
510
|
+
const warnings = [];
|
|
511
|
+
for (const section of REQUIRED_SECTIONS) {
|
|
512
|
+
const sectionResult = findSection(content, section);
|
|
513
|
+
if (!sectionResult.found) {
|
|
514
|
+
errors.push({
|
|
515
|
+
type: "missing_section",
|
|
516
|
+
section,
|
|
517
|
+
message: `\u7F3A\u5C11\u5FC5\u8981\u533A\u5757: ${section}`
|
|
518
|
+
});
|
|
519
|
+
} else if (!sectionResult.hasContent) {
|
|
520
|
+
errors.push({
|
|
521
|
+
type: "empty_section",
|
|
522
|
+
section,
|
|
523
|
+
message: `\u533A\u5757\u5185\u5BB9\u4E3A\u7A7A: ${section}`
|
|
524
|
+
});
|
|
525
|
+
} else if (sectionResult.contentLength < MIN_CONTENT_LENGTH) {
|
|
526
|
+
warnings.push({
|
|
527
|
+
type: "short_content",
|
|
528
|
+
section,
|
|
529
|
+
message: `\u533A\u5757\u5185\u5BB9\u8FC7\u77ED (${sectionResult.contentLength} \u5B57\u7B26): ${section}`
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return {
|
|
534
|
+
valid: errors.length === 0,
|
|
535
|
+
errors,
|
|
536
|
+
warnings
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function findSection(content, sectionName) {
|
|
540
|
+
const patterns = [
|
|
541
|
+
new RegExp(`^##\\s+${escapeRegex(sectionName)}[^\\n]*$`, "m"),
|
|
542
|
+
new RegExp(`^###\\s+${escapeRegex(sectionName)}[^\\n]*$`, "m")
|
|
543
|
+
];
|
|
544
|
+
let matchIndex = -1;
|
|
545
|
+
let matchLength = 0;
|
|
546
|
+
for (const pattern of patterns) {
|
|
547
|
+
const match = content.match(pattern);
|
|
548
|
+
if (match && match.index !== void 0) {
|
|
549
|
+
matchIndex = match.index;
|
|
550
|
+
matchLength = match[0].length;
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (matchIndex === -1) {
|
|
555
|
+
return { found: false, hasContent: false, contentLength: 0 };
|
|
556
|
+
}
|
|
557
|
+
const contentStart = matchIndex + matchLength;
|
|
558
|
+
const remainingContent = content.slice(contentStart);
|
|
559
|
+
const nextSectionMatch = remainingContent.match(/^#{1,3}\s+/m);
|
|
560
|
+
const sectionContent = nextSectionMatch ? remainingContent.slice(0, nextSectionMatch.index) : remainingContent;
|
|
561
|
+
const trimmedContent = sectionContent.trim();
|
|
562
|
+
return {
|
|
563
|
+
found: true,
|
|
564
|
+
hasContent: trimmedContent.length > 0,
|
|
565
|
+
contentLength: trimmedContent.length
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
function escapeRegex(str) {
|
|
569
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
570
|
+
}
|
|
571
|
+
async function validateArtifactFile(filePath) {
|
|
572
|
+
try {
|
|
573
|
+
const content = await fs5.readFile(filePath, "utf-8");
|
|
574
|
+
return validateArtifact(content);
|
|
575
|
+
} catch {
|
|
576
|
+
return {
|
|
577
|
+
valid: false,
|
|
578
|
+
errors: [
|
|
579
|
+
{
|
|
580
|
+
type: "invalid_format",
|
|
581
|
+
section: "",
|
|
582
|
+
message: `\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6: ${filePath}`
|
|
583
|
+
}
|
|
584
|
+
],
|
|
585
|
+
warnings: []
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async function validateWorkspaceArtifacts(workspacePath) {
|
|
590
|
+
const workspace = await loadWorkspace(workspacePath);
|
|
591
|
+
const stepResults = /* @__PURE__ */ new Map();
|
|
592
|
+
let validSteps = 0;
|
|
593
|
+
let errorCount = 0;
|
|
594
|
+
let warningCount = 0;
|
|
595
|
+
if (!workspace.workflow) {
|
|
596
|
+
return {
|
|
597
|
+
valid: false,
|
|
598
|
+
stepResults,
|
|
599
|
+
totalSteps: 0,
|
|
600
|
+
validSteps: 0,
|
|
601
|
+
errorCount: 1,
|
|
602
|
+
warningCount: 0
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
for (const step of workspace.workflow.steps) {
|
|
606
|
+
const outputPath = path6.join(workspace.path, step.output);
|
|
607
|
+
const result = await validateArtifactFile(outputPath);
|
|
608
|
+
stepResults.set(step.id, result);
|
|
609
|
+
if (result.valid) {
|
|
610
|
+
validSteps++;
|
|
611
|
+
}
|
|
612
|
+
errorCount += result.errors.length;
|
|
613
|
+
warningCount += result.warnings.length;
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
valid: errorCount === 0,
|
|
617
|
+
stepResults,
|
|
618
|
+
totalSteps: workspace.workflow.steps.length,
|
|
619
|
+
validSteps,
|
|
620
|
+
errorCount,
|
|
621
|
+
warningCount
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// src/cli/commands/validate.ts
|
|
626
|
+
var validateCommand = new Command4("validate").description("\u6821\u9A8C workspace \u4EA7\u7269\u7ED3\u6784").argument("[workspace]", "workspace \u8DEF\u5F84", ".").option("--strict", "\u4E25\u683C\u6A21\u5F0F\uFF08\u5C06\u8B66\u544A\u89C6\u4E3A\u9519\u8BEF\uFF09").option("-j, --json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (workspace, options) => {
|
|
627
|
+
const workspacePath = path7.resolve(workspace);
|
|
628
|
+
try {
|
|
629
|
+
const info = await loadWorkspace(workspacePath);
|
|
630
|
+
if (!info.exists) {
|
|
631
|
+
console.error(`Error: workspace not found: ${workspacePath}`);
|
|
632
|
+
process.exit(1);
|
|
633
|
+
}
|
|
634
|
+
if (!info.hasWorkflow) {
|
|
635
|
+
console.error(`Error: workflow.yaml not found in ${workspacePath}`);
|
|
636
|
+
process.exit(1);
|
|
637
|
+
}
|
|
638
|
+
if (!info.workflow) {
|
|
639
|
+
console.error(`Error: failed to parse workflow.yaml`);
|
|
640
|
+
process.exit(1);
|
|
641
|
+
}
|
|
642
|
+
const result = await validateWorkspaceArtifacts(workspacePath);
|
|
643
|
+
if (options.json) {
|
|
644
|
+
const jsonOutput = {
|
|
645
|
+
valid: options.strict ? result.valid && result.warningCount === 0 : result.valid,
|
|
646
|
+
workspace: workspacePath,
|
|
647
|
+
totalSteps: result.totalSteps,
|
|
648
|
+
validSteps: result.validSteps,
|
|
649
|
+
errorCount: result.errorCount,
|
|
650
|
+
warningCount: result.warningCount,
|
|
651
|
+
steps: Array.from(result.stepResults.entries()).map(([stepId, stepResult]) => ({
|
|
652
|
+
stepId,
|
|
653
|
+
valid: options.strict ? stepResult.valid && stepResult.warnings.length === 0 : stepResult.valid,
|
|
654
|
+
errors: stepResult.errors,
|
|
655
|
+
warnings: stepResult.warnings
|
|
656
|
+
}))
|
|
657
|
+
};
|
|
658
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
659
|
+
} else {
|
|
660
|
+
console.log(`Workspace: ${info.workflow.name}`);
|
|
661
|
+
console.log("");
|
|
662
|
+
for (const step of info.workflow.steps) {
|
|
663
|
+
const stepResult = result.stepResults.get(step.id);
|
|
664
|
+
if (!stepResult) continue;
|
|
665
|
+
const icon = stepResult.valid ? "\u2705" : "\u274C";
|
|
666
|
+
console.log(`${icon} ${step.output} - ${stepResult.valid ? "Valid" : "Invalid"}`);
|
|
667
|
+
if (!stepResult.valid) {
|
|
668
|
+
for (const error of stepResult.errors) {
|
|
669
|
+
console.log(` - ${error.message}`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (stepResult.warnings.length > 0) {
|
|
673
|
+
for (const warning of stepResult.warnings) {
|
|
674
|
+
console.log(` \u26A0\uFE0F ${warning.message}`);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
console.log("");
|
|
679
|
+
const hasErrors = result.errorCount > 0;
|
|
680
|
+
const hasWarnings = result.warningCount > 0;
|
|
681
|
+
const strictFail = options.strict && hasWarnings;
|
|
682
|
+
if (!hasErrors && !strictFail) {
|
|
683
|
+
console.log("All artifacts validated successfully.");
|
|
684
|
+
if (hasWarnings) {
|
|
685
|
+
console.log(`\u26A0\uFE0F ${result.warningCount} warning(s) found.`);
|
|
686
|
+
}
|
|
687
|
+
} else {
|
|
688
|
+
const totalErrors = result.errorCount + (strictFail ? result.warningCount : 0);
|
|
689
|
+
console.log(`Validation failed with ${totalErrors} error(s).`);
|
|
690
|
+
}
|
|
691
|
+
if (hasErrors || strictFail) {
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
} catch (error) {
|
|
696
|
+
console.error(`Error: ${error}`);
|
|
697
|
+
process.exit(1);
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
// src/cli/commands/advance.ts
|
|
702
|
+
import { Command as Command5 } from "commander";
|
|
703
|
+
import path8 from "path";
|
|
704
|
+
import fs6 from "fs/promises";
|
|
705
|
+
var advanceCommand = new Command5("advance").description("\u63A8\u8FDB workspace \u72B6\u6001").argument("[workspace]", "workspace \u8DEF\u5F84", ".").option("-e, --event <type>", "\u4E8B\u4EF6\u7C7B\u578B", "step.done").option("-s, --summary <text>", "\u4E8B\u4EF6\u6458\u8981").option("--no-state", "\u4E0D\u66F4\u65B0 state.json").option("--skip-event", "\u4E0D\u5199\u5165\u4E8B\u4EF6\u65E5\u5FD7").action(
|
|
706
|
+
async (workspace, options) => {
|
|
707
|
+
const workspacePath = path8.resolve(workspace);
|
|
708
|
+
try {
|
|
709
|
+
const info = await loadWorkspace(workspacePath);
|
|
710
|
+
if (!info.exists) {
|
|
711
|
+
console.error(`Error: workspace not found: ${workspacePath}`);
|
|
712
|
+
process.exit(1);
|
|
713
|
+
}
|
|
714
|
+
if (!info.hasWorkflow) {
|
|
715
|
+
console.error(`Error: workflow.yaml not found in ${workspacePath}`);
|
|
716
|
+
process.exit(1);
|
|
717
|
+
}
|
|
718
|
+
if (!info.workflow) {
|
|
719
|
+
console.error(`Error: failed to parse workflow.yaml`);
|
|
720
|
+
process.exit(1);
|
|
721
|
+
}
|
|
722
|
+
const stateResult = computeState(info.workflow, info.stepOutputs);
|
|
723
|
+
console.log(`Workspace: ${info.workflow.name}`);
|
|
724
|
+
if (stateResult.status === "done") {
|
|
725
|
+
console.log("Status: done");
|
|
726
|
+
console.log("");
|
|
727
|
+
console.log("\u26A0\uFE0F Workflow already completed. No steps to advance.");
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const currentStep = getCurrentStep(info.workflow, info.stepOutputs);
|
|
731
|
+
if (!currentStep) {
|
|
732
|
+
console.log("Status: done");
|
|
733
|
+
console.log("");
|
|
734
|
+
console.log("\u26A0\uFE0F Workflow already completed. No steps to advance.");
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const { step, index } = currentStep;
|
|
738
|
+
console.log(`Current step: ${step.id} (index: ${index})`);
|
|
403
739
|
console.log("");
|
|
404
|
-
|
|
740
|
+
const eventType = options.event;
|
|
741
|
+
const eventSummary = options.summary || getDefaultSummary(eventType, step.id);
|
|
742
|
+
if (!options.skipEvent) {
|
|
743
|
+
const result = await writeEvent({
|
|
744
|
+
workspacePath,
|
|
745
|
+
step: { index: index + 1, id: step.id },
|
|
746
|
+
type: eventType,
|
|
747
|
+
summary: eventSummary,
|
|
748
|
+
workItemId: step.workItemId,
|
|
749
|
+
links: [step.output]
|
|
750
|
+
});
|
|
751
|
+
if (result.success) {
|
|
752
|
+
console.log(`\u2705 Event written: ${eventType}`);
|
|
753
|
+
console.log(` Summary: ${eventSummary}`);
|
|
754
|
+
if (step.workItemId) {
|
|
755
|
+
console.log(` Work Item: ${step.workItemId}`);
|
|
756
|
+
}
|
|
757
|
+
if (step.output) {
|
|
758
|
+
console.log(` Links: ${step.output}`);
|
|
759
|
+
}
|
|
760
|
+
} else {
|
|
761
|
+
console.log(`\u274C Failed to write event: ${result.error}`);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (options.state) {
|
|
765
|
+
const statePath = path8.join(workspacePath, "state.json");
|
|
766
|
+
const newIndex = index + 1;
|
|
767
|
+
const isDone = newIndex >= info.workflow.steps.length;
|
|
768
|
+
const newState = {
|
|
769
|
+
currentIndex: newIndex,
|
|
770
|
+
status: isDone ? "done" : "running",
|
|
771
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
772
|
+
};
|
|
773
|
+
await fs6.writeFile(statePath, JSON.stringify(newState, null, 2));
|
|
774
|
+
console.log("");
|
|
775
|
+
console.log(`\u2705 State updated: ${statePath}`);
|
|
776
|
+
console.log(` Current index: ${newState.currentIndex}`);
|
|
777
|
+
console.log(` Status: ${newState.status}`);
|
|
778
|
+
if (isDone) {
|
|
779
|
+
console.log("");
|
|
780
|
+
console.log("\u{1F389} Workflow completed!");
|
|
781
|
+
} else {
|
|
782
|
+
const nextStep = info.workflow.steps[newIndex];
|
|
783
|
+
console.log(` Next step: ${nextStep.id}`);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
} catch (error) {
|
|
787
|
+
console.error(`Error: ${error}`);
|
|
788
|
+
process.exit(1);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
);
|
|
792
|
+
function getDefaultSummary(eventType, stepId) {
|
|
793
|
+
const summaries = {
|
|
794
|
+
"step.started": `\u5F00\u59CB\u6267\u884C\u6B65\u9AA4: ${stepId}`,
|
|
795
|
+
"step.done": `\u6B65\u9AA4\u5B8C\u6210: ${stepId}`,
|
|
796
|
+
"artifact.updated": "\u4EA7\u7269\u5DF2\u66F4\u65B0",
|
|
797
|
+
"workflow.updated": "\u5DE5\u4F5C\u6D41\u5DF2\u66F4\u65B0",
|
|
798
|
+
"verify.passed": "\u9A8C\u8BC1\u901A\u8FC7",
|
|
799
|
+
"verify.failed": "\u9A8C\u8BC1\u5931\u8D25",
|
|
800
|
+
"accept.passed": "\u9A8C\u6536\u901A\u8FC7",
|
|
801
|
+
"accept.failed": "\u9A8C\u6536\u5931\u8D25",
|
|
802
|
+
"issue.raised": "\u53D1\u73B0\u95EE\u9898",
|
|
803
|
+
"handoff.sent": "\u5DF2\u4EA4\u63A5\u7ED9\u4E0B\u4E00\u6B65",
|
|
804
|
+
"automation.session": "\u81EA\u52A8\u5316\u4F1A\u8BDD\u5DF2\u8BB0\u5F55"
|
|
805
|
+
};
|
|
806
|
+
return summaries[eventType] || `\u4E8B\u4EF6: ${eventType}`;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// src/cli/commands/config.ts
|
|
810
|
+
import { Command as Command6 } from "commander";
|
|
811
|
+
import os2 from "os";
|
|
812
|
+
|
|
813
|
+
// src/core/config.ts
|
|
814
|
+
import fs7 from "fs/promises";
|
|
815
|
+
import path9 from "path";
|
|
816
|
+
import os from "os";
|
|
817
|
+
var DEFAULT_CONFIG = {
|
|
818
|
+
events: {
|
|
819
|
+
enabled: true,
|
|
820
|
+
logStepStarted: true,
|
|
821
|
+
logStepDone: true
|
|
822
|
+
},
|
|
823
|
+
automation: {
|
|
824
|
+
enabled: false,
|
|
825
|
+
provider: "nutjs",
|
|
826
|
+
screenshot: false,
|
|
827
|
+
timeout: 3e4,
|
|
828
|
+
retries: 3,
|
|
829
|
+
confidence: 0.8
|
|
830
|
+
},
|
|
831
|
+
clipboard: {
|
|
832
|
+
autoCopy: false
|
|
833
|
+
},
|
|
834
|
+
prompt: {
|
|
835
|
+
language: "zh"
|
|
836
|
+
},
|
|
837
|
+
validation: {
|
|
838
|
+
strict: false,
|
|
839
|
+
warnOnShortContent: true
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
var CONFIG_FILES = [
|
|
843
|
+
".agenthandoffrc",
|
|
844
|
+
".agenthandoffrc.json",
|
|
845
|
+
".agenthandoffrc.yaml",
|
|
846
|
+
".agenthandoffrc.yml"
|
|
847
|
+
];
|
|
848
|
+
var configCache = /* @__PURE__ */ new Map();
|
|
849
|
+
async function findConfigFile(startPath) {
|
|
850
|
+
let currentPath = path9.resolve(startPath);
|
|
851
|
+
const homePath = os.homedir();
|
|
852
|
+
while (true) {
|
|
853
|
+
for (const configFile of CONFIG_FILES) {
|
|
854
|
+
const filePath = path9.join(currentPath, configFile);
|
|
855
|
+
try {
|
|
856
|
+
await fs7.access(filePath);
|
|
857
|
+
return filePath;
|
|
858
|
+
} catch {
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
const packageJsonPath = path9.join(currentPath, "package.json");
|
|
862
|
+
try {
|
|
863
|
+
const content = await fs7.readFile(packageJsonPath, "utf-8");
|
|
864
|
+
const pkg = JSON.parse(content);
|
|
865
|
+
if (pkg.agenthandoff) {
|
|
866
|
+
return packageJsonPath;
|
|
867
|
+
}
|
|
868
|
+
} catch {
|
|
869
|
+
}
|
|
870
|
+
if (currentPath === homePath || currentPath === path9.dirname(currentPath)) {
|
|
871
|
+
break;
|
|
872
|
+
}
|
|
873
|
+
currentPath = path9.dirname(currentPath);
|
|
874
|
+
}
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
async function loadConfigFile(filePath) {
|
|
878
|
+
try {
|
|
879
|
+
const content = await fs7.readFile(filePath, "utf-8");
|
|
880
|
+
const ext = path9.extname(filePath);
|
|
881
|
+
if (ext === ".yaml" || ext === ".yml") {
|
|
882
|
+
return parseYaml(content);
|
|
883
|
+
}
|
|
884
|
+
if (filePath.endsWith("package.json")) {
|
|
885
|
+
const pkg = JSON.parse(content);
|
|
886
|
+
return pkg.agenthandoff || {};
|
|
887
|
+
}
|
|
888
|
+
return JSON.parse(content);
|
|
889
|
+
} catch {
|
|
890
|
+
return {};
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
function parseYaml(content) {
|
|
894
|
+
const result = {};
|
|
895
|
+
const lines = content.split("\n");
|
|
896
|
+
let currentObj = result;
|
|
897
|
+
const stack = [];
|
|
898
|
+
for (const line of lines) {
|
|
899
|
+
if (!line.trim() || line.trim().startsWith("#")) {
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
const indent = line.search(/\S/);
|
|
903
|
+
const trimmed = line.trim();
|
|
904
|
+
if (trimmed.includes(":")) {
|
|
905
|
+
const colonIndex = trimmed.indexOf(":");
|
|
906
|
+
const key = trimmed.slice(0, colonIndex).trim();
|
|
907
|
+
const value = trimmed.slice(colonIndex + 1).trim();
|
|
908
|
+
if (value === "") {
|
|
909
|
+
if (stack.length > 0) {
|
|
910
|
+
const expectedIndent = stack.length * 2;
|
|
911
|
+
if (indent < expectedIndent) {
|
|
912
|
+
while (stack.length > 0 && indent < stack.length * 2) {
|
|
913
|
+
stack.pop();
|
|
914
|
+
}
|
|
915
|
+
if (stack.length > 0) {
|
|
916
|
+
currentObj = stack[stack.length - 1].obj;
|
|
917
|
+
} else {
|
|
918
|
+
currentObj = result;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const newObj = {};
|
|
923
|
+
currentObj[key] = newObj;
|
|
924
|
+
stack.push({ obj: currentObj });
|
|
925
|
+
currentObj = newObj;
|
|
926
|
+
} else {
|
|
927
|
+
let parsedValue = value;
|
|
928
|
+
if (value === "true") parsedValue = true;
|
|
929
|
+
else if (value === "false") parsedValue = false;
|
|
930
|
+
else if (value === "null") parsedValue = null;
|
|
931
|
+
else if (/^\d+$/.test(value)) parsedValue = parseInt(value, 10);
|
|
932
|
+
else if (/^\d+\.\d+$/.test(value)) parsedValue = parseFloat(value);
|
|
933
|
+
else if (value.startsWith('"') && value.endsWith('"')) {
|
|
934
|
+
parsedValue = value.slice(1, -1);
|
|
935
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
936
|
+
parsedValue = value.slice(1, -1);
|
|
937
|
+
}
|
|
938
|
+
currentObj[key] = parsedValue;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return result;
|
|
943
|
+
}
|
|
944
|
+
function mergeConfig(base, override) {
|
|
945
|
+
const result = JSON.parse(JSON.stringify(base));
|
|
946
|
+
for (const key of Object.keys(override)) {
|
|
947
|
+
const overrideValue = override[key];
|
|
948
|
+
if (overrideValue === void 0) continue;
|
|
949
|
+
if (typeof overrideValue === "object" && overrideValue !== null && !Array.isArray(overrideValue)) {
|
|
950
|
+
const baseValue = result[key];
|
|
951
|
+
if (typeof baseValue === "object" && baseValue !== null && !Array.isArray(baseValue)) {
|
|
952
|
+
result[key] = mergeConfig(
|
|
953
|
+
baseValue,
|
|
954
|
+
overrideValue
|
|
955
|
+
);
|
|
956
|
+
} else {
|
|
957
|
+
result[key] = overrideValue;
|
|
958
|
+
}
|
|
959
|
+
} else {
|
|
960
|
+
result[key] = overrideValue;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return result;
|
|
964
|
+
}
|
|
965
|
+
async function loadConfig(workspacePath) {
|
|
966
|
+
const startPath = workspacePath ? path9.resolve(workspacePath) : process.cwd();
|
|
967
|
+
const cacheKey = startPath;
|
|
968
|
+
if (configCache.has(cacheKey)) {
|
|
969
|
+
return configCache.get(cacheKey);
|
|
970
|
+
}
|
|
971
|
+
let config = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
|
|
972
|
+
const globalConfigPath = path9.join(os.homedir(), ".agenthandoffrc");
|
|
973
|
+
try {
|
|
974
|
+
await fs7.access(globalConfigPath);
|
|
975
|
+
const globalConfig = await loadConfigFile(globalConfigPath);
|
|
976
|
+
config = mergeConfig(config, globalConfig);
|
|
977
|
+
} catch {
|
|
978
|
+
}
|
|
979
|
+
const localConfigFile = await findConfigFile(startPath);
|
|
980
|
+
if (localConfigFile) {
|
|
981
|
+
const localConfig = await loadConfigFile(localConfigFile);
|
|
982
|
+
config = mergeConfig(config, localConfig);
|
|
983
|
+
}
|
|
984
|
+
configCache.set(cacheKey, config);
|
|
985
|
+
return config;
|
|
986
|
+
}
|
|
987
|
+
async function initConfigFile(targetPath, global = false) {
|
|
988
|
+
const configPath = global ? path9.join(os.homedir(), ".agenthandoffrc") : path9.join(targetPath, ".agenthandoffrc");
|
|
989
|
+
const content = JSON.stringify(DEFAULT_CONFIG, null, 2);
|
|
990
|
+
await fs7.writeFile(configPath, content, "utf-8");
|
|
991
|
+
return configPath;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/cli/commands/config.ts
|
|
995
|
+
var configCommand = new Command6("config").description("\u67E5\u770B\u6216\u7BA1\u7406\u914D\u7F6E").argument("[action]", "\u64CD\u4F5C: show, init", "show").option("-g, --global", "\u64CD\u4F5C\u5168\u5C40\u914D\u7F6E").option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u4FE1\u606F").action(async (action, options) => {
|
|
996
|
+
try {
|
|
997
|
+
if (action === "show") {
|
|
998
|
+
await showConfig(options);
|
|
999
|
+
} else if (action === "init") {
|
|
1000
|
+
await initConfig(options);
|
|
1001
|
+
} else if (action === "list") {
|
|
1002
|
+
await listConfig();
|
|
1003
|
+
} else {
|
|
1004
|
+
console.error(`Unknown action: ${action}`);
|
|
1005
|
+
console.log("Available actions: show, init, list");
|
|
1006
|
+
process.exit(1);
|
|
405
1007
|
}
|
|
406
1008
|
} catch (error) {
|
|
407
1009
|
console.error(`Error: ${error}`);
|
|
408
1010
|
process.exit(1);
|
|
409
1011
|
}
|
|
410
1012
|
});
|
|
1013
|
+
async function showConfig(options) {
|
|
1014
|
+
const targetPath = options.global ? os2.homedir() : process.cwd();
|
|
1015
|
+
const config = await loadConfig(targetPath);
|
|
1016
|
+
if (options.verbose) {
|
|
1017
|
+
const configFile = await findConfigFile(targetPath);
|
|
1018
|
+
if (configFile) {
|
|
1019
|
+
console.log(`Config file: ${configFile}`);
|
|
1020
|
+
console.log("");
|
|
1021
|
+
} else {
|
|
1022
|
+
console.log("No config file found, using defaults");
|
|
1023
|
+
console.log("");
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
console.log(JSON.stringify(config, null, 2));
|
|
1027
|
+
}
|
|
1028
|
+
async function initConfig(options) {
|
|
1029
|
+
const targetPath = options.global ? os2.homedir() : process.cwd();
|
|
1030
|
+
const configPath = await initConfigFile(targetPath, options.global);
|
|
1031
|
+
console.log(`\u2705 Created config file: ${configPath}`);
|
|
1032
|
+
console.log("");
|
|
1033
|
+
console.log("Default configuration:");
|
|
1034
|
+
console.log(JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
1035
|
+
}
|
|
1036
|
+
async function listConfig() {
|
|
1037
|
+
console.log("Configuration options:");
|
|
1038
|
+
console.log("");
|
|
1039
|
+
console.log(" defaultWorkspace - \u9ED8\u8BA4 workspace \u8DEF\u5F84");
|
|
1040
|
+
console.log(" events.enabled - \u662F\u5426\u542F\u7528\u4E8B\u4EF6\u65E5\u5FD7 (default: true)");
|
|
1041
|
+
console.log(" events.logStepStarted - \u662F\u5426\u8BB0\u5F55 step.started (default: true)");
|
|
1042
|
+
console.log(" events.logStepDone - \u662F\u5426\u8BB0\u5F55 step.done (default: true)");
|
|
1043
|
+
console.log(" clipboard.autoCopy - \u662F\u5426\u81EA\u52A8\u590D\u5236 prompt (default: false)");
|
|
1044
|
+
console.log(" prompt.templatePath - \u81EA\u5B9A\u4E49 prompt \u6A21\u677F\u8DEF\u5F84");
|
|
1045
|
+
console.log(" prompt.language - prompt \u8BED\u8A00 (default: zh)");
|
|
1046
|
+
console.log(" validation.strict - \u4E25\u683C\u6821\u9A8C\u6A21\u5F0F (default: false)");
|
|
1047
|
+
console.log(" validation.warnOnShortContent - \u5185\u5BB9\u8FC7\u77ED\u65F6\u8B66\u544A (default: true)");
|
|
1048
|
+
console.log("");
|
|
1049
|
+
console.log("Config file locations (in order of priority):");
|
|
1050
|
+
console.log(" .agenthandoffrc");
|
|
1051
|
+
console.log(" .agenthandoffrc.json");
|
|
1052
|
+
console.log(" .agenthandoffrc.yaml");
|
|
1053
|
+
console.log(" package.json#agenthandoff");
|
|
1054
|
+
console.log(` ~/.agenthandoffrc (global)`);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// src/cli/commands/report.ts
|
|
1058
|
+
import { Command as Command7 } from "commander";
|
|
1059
|
+
import fs9 from "fs/promises";
|
|
1060
|
+
import path10 from "path";
|
|
1061
|
+
|
|
1062
|
+
// src/adapters/trae/operation-reporter.ts
|
|
1063
|
+
import fs8 from "fs/promises";
|
|
1064
|
+
var OperationReporter = class {
|
|
1065
|
+
session;
|
|
1066
|
+
constructor(session) {
|
|
1067
|
+
this.session = session;
|
|
1068
|
+
}
|
|
1069
|
+
generate(options) {
|
|
1070
|
+
switch (options.format) {
|
|
1071
|
+
case "json":
|
|
1072
|
+
return this.generateJson();
|
|
1073
|
+
case "markdown":
|
|
1074
|
+
return this.generateMarkdown(options.includeScreenshots);
|
|
1075
|
+
case "html":
|
|
1076
|
+
return this.generateHtml(options.includeScreenshots);
|
|
1077
|
+
default:
|
|
1078
|
+
return this.generateMarkdown(options.includeScreenshots);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
generateJson() {
|
|
1082
|
+
return JSON.stringify(this.session, null, 2);
|
|
1083
|
+
}
|
|
1084
|
+
generateMarkdown(includeScreenshots) {
|
|
1085
|
+
const lines = [
|
|
1086
|
+
"# Automation Session Report",
|
|
1087
|
+
"",
|
|
1088
|
+
`**Session ID:** ${this.session.id}`,
|
|
1089
|
+
`**Started At:** ${this.session.startedAt}`,
|
|
1090
|
+
`**Workspace:** ${this.session.workspacePath}`,
|
|
1091
|
+
`**Step ID:** ${this.session.stepId}`,
|
|
1092
|
+
...this.session.status ? [`**Status:** ${this.session.status}`] : [],
|
|
1093
|
+
...this.session.error ? [`**Error:** ${this.session.error}`] : [],
|
|
1094
|
+
"",
|
|
1095
|
+
`## Operations (${this.session.operations.length})`,
|
|
1096
|
+
""
|
|
1097
|
+
];
|
|
1098
|
+
this.session.operations.forEach((op, index) => {
|
|
1099
|
+
lines.push(`### ${index + 1}. ${op.type}`);
|
|
1100
|
+
if (op.target) {
|
|
1101
|
+
lines.push(`- **Target:** ${op.target}`);
|
|
1102
|
+
}
|
|
1103
|
+
if (op.value) {
|
|
1104
|
+
lines.push(
|
|
1105
|
+
`- **Value:** ${op.value.substring(0, 100)}${op.value.length > 100 ? "..." : ""}`
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
lines.push(`- **Timestamp:** ${new Date(op.timestamp).toISOString()}`);
|
|
1109
|
+
lines.push("");
|
|
1110
|
+
});
|
|
1111
|
+
if (includeScreenshots && this.session.screenshots.length > 0) {
|
|
1112
|
+
lines.push(`## Screenshots (${this.session.screenshots.length})`);
|
|
1113
|
+
lines.push("");
|
|
1114
|
+
this.session.screenshots.forEach((screenshot, index) => {
|
|
1115
|
+
lines.push(`### Screenshot ${index + 1}`);
|
|
1116
|
+
lines.push(``);
|
|
1117
|
+
lines.push("");
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
return lines.join("\n");
|
|
1121
|
+
}
|
|
1122
|
+
generateHtml(includeScreenshots) {
|
|
1123
|
+
const operationsHtml = this.session.operations.map(
|
|
1124
|
+
(op, index) => `
|
|
1125
|
+
<div class="operation">
|
|
1126
|
+
<h3>${index + 1}. ${op.type}</h3>
|
|
1127
|
+
${op.target ? `<p><strong>Target:</strong> ${op.target}</p>` : ""}
|
|
1128
|
+
${op.value ? `<p><strong>Value:</strong> ${op.value.substring(0, 100)}${op.value.length > 100 ? "..." : ""}</p>` : ""}
|
|
1129
|
+
<p><strong>Timestamp:</strong> ${new Date(op.timestamp).toISOString()}</p>
|
|
1130
|
+
</div>
|
|
1131
|
+
`
|
|
1132
|
+
).join("");
|
|
1133
|
+
const screenshotsHtml = includeScreenshots && this.session.screenshots.length > 0 ? `
|
|
1134
|
+
<h2>Screenshots (${this.session.screenshots.length})</h2>
|
|
1135
|
+
${this.session.screenshots.map(
|
|
1136
|
+
(s, i) => `
|
|
1137
|
+
<div class="screenshot">
|
|
1138
|
+
<h3>Screenshot ${i + 1}</h3>
|
|
1139
|
+
<img src="${s}" alt="Screenshot ${i + 1}" />
|
|
1140
|
+
</div>
|
|
1141
|
+
`
|
|
1142
|
+
).join("")}
|
|
1143
|
+
` : "";
|
|
1144
|
+
const statusLine = this.session.status ? `<p><strong>Status:</strong> ${this.session.status}</p>` : "";
|
|
1145
|
+
const errorLine = this.session.error ? `<p><strong>Error:</strong> ${this.session.error}</p>` : "";
|
|
1146
|
+
return `
|
|
1147
|
+
<!DOCTYPE html>
|
|
1148
|
+
<html lang="zh-CN">
|
|
1149
|
+
<head>
|
|
1150
|
+
<meta charset="UTF-8">
|
|
1151
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1152
|
+
<title>Automation Session Report</title>
|
|
1153
|
+
<style>
|
|
1154
|
+
body {
|
|
1155
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1156
|
+
max-width: 800px;
|
|
1157
|
+
margin: 0 auto;
|
|
1158
|
+
padding: 20px;
|
|
1159
|
+
background: #f5f5f5;
|
|
1160
|
+
}
|
|
1161
|
+
.header {
|
|
1162
|
+
background: white;
|
|
1163
|
+
padding: 20px;
|
|
1164
|
+
border-radius: 8px;
|
|
1165
|
+
margin-bottom: 20px;
|
|
1166
|
+
}
|
|
1167
|
+
.operation {
|
|
1168
|
+
background: white;
|
|
1169
|
+
padding: 15px;
|
|
1170
|
+
border-radius: 8px;
|
|
1171
|
+
margin-bottom: 10px;
|
|
1172
|
+
}
|
|
1173
|
+
.screenshot img {
|
|
1174
|
+
max-width: 100%;
|
|
1175
|
+
border-radius: 8px;
|
|
1176
|
+
}
|
|
1177
|
+
h1 { color: #333; }
|
|
1178
|
+
h2 { color: #555; border-bottom: 1px solid #eee; padding-bottom: 10px; }
|
|
1179
|
+
h3 { color: #666; margin: 0 0 10px 0; }
|
|
1180
|
+
p { margin: 5px 0; color: #666; }
|
|
1181
|
+
</style>
|
|
1182
|
+
</head>
|
|
1183
|
+
<body>
|
|
1184
|
+
<div class="header">
|
|
1185
|
+
<h1>Automation Session Report</h1>
|
|
1186
|
+
<p><strong>Session ID:</strong> ${this.session.id}</p>
|
|
1187
|
+
<p><strong>Started At:</strong> ${this.session.startedAt}</p>
|
|
1188
|
+
<p><strong>Workspace:</strong> ${this.session.workspacePath}</p>
|
|
1189
|
+
<p><strong>Step ID:</strong> ${this.session.stepId}</p>
|
|
1190
|
+
${statusLine}
|
|
1191
|
+
${errorLine}
|
|
1192
|
+
</div>
|
|
1193
|
+
|
|
1194
|
+
<h2>Operations (${this.session.operations.length})</h2>
|
|
1195
|
+
${operationsHtml}
|
|
1196
|
+
|
|
1197
|
+
${screenshotsHtml}
|
|
1198
|
+
</body>
|
|
1199
|
+
</html>
|
|
1200
|
+
`.trim();
|
|
1201
|
+
}
|
|
1202
|
+
async saveToFile(outputPath, options) {
|
|
1203
|
+
const content = this.generate(options);
|
|
1204
|
+
await fs8.writeFile(outputPath, content, "utf-8");
|
|
1205
|
+
return outputPath;
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
// src/cli/commands/report.ts
|
|
1210
|
+
var reportCommand = new Command7("report").description("\u751F\u6210\u81EA\u52A8\u5316\u64CD\u4F5C\u62A5\u544A").argument("[workspace]", "workspace \u8DEF\u5F84", ".").option("-f, --format <format>", "\u62A5\u544A\u683C\u5F0F: json, markdown, html", "markdown").option("-s, --session <id>", "\u6307\u5B9A session ID").option("--screenshots", "\u5305\u542B\u622A\u56FE", false).option("-o, --output <path>", "\u8F93\u51FA\u8DEF\u5F84").action(
|
|
1211
|
+
async (workspace, options) => {
|
|
1212
|
+
const workspacePath = path10.resolve(workspace);
|
|
1213
|
+
const logDir = path10.join(workspacePath, "operations");
|
|
1214
|
+
try {
|
|
1215
|
+
let sessionFile;
|
|
1216
|
+
if (options.session) {
|
|
1217
|
+
sessionFile = path10.join(logDir, `operations-${options.session}.jsonl`);
|
|
1218
|
+
} else {
|
|
1219
|
+
const files = await fs9.readdir(logDir);
|
|
1220
|
+
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl")).sort().reverse();
|
|
1221
|
+
if (jsonlFiles.length === 0) {
|
|
1222
|
+
console.error("Error: no operation logs found");
|
|
1223
|
+
process.exit(1);
|
|
1224
|
+
}
|
|
1225
|
+
sessionFile = path10.join(logDir, jsonlFiles[0]);
|
|
1226
|
+
}
|
|
1227
|
+
const content = await fs9.readFile(sessionFile, "utf-8");
|
|
1228
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
1229
|
+
const session = JSON.parse(lines[lines.length - 1]);
|
|
1230
|
+
const reporter = new OperationReporter(session);
|
|
1231
|
+
const report = reporter.generate({
|
|
1232
|
+
format: options.format,
|
|
1233
|
+
includeScreenshots: options.screenshots
|
|
1234
|
+
});
|
|
1235
|
+
if (options.output) {
|
|
1236
|
+
await fs9.writeFile(path10.resolve(options.output), report, "utf-8");
|
|
1237
|
+
console.log(`\u2705 Report saved to: ${options.output}`);
|
|
1238
|
+
} else {
|
|
1239
|
+
console.log(report);
|
|
1240
|
+
}
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
console.error(`Error: ${error}`);
|
|
1243
|
+
process.exit(1);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
);
|
|
411
1247
|
|
|
412
1248
|
// src/index.ts
|
|
413
1249
|
var require2 = createRequire(import.meta.url);
|
|
414
1250
|
var { version } = require2("../package.json");
|
|
415
|
-
var program = new
|
|
1251
|
+
var program = new Command8();
|
|
416
1252
|
program.name("agent-handoff").description("\u8F7B\u91CF\u7EA7\u591A Agent \u534F\u4F5C\u63A5\u529B\u5DE5\u5177").version(version);
|
|
417
1253
|
program.addCommand(initCommand);
|
|
418
1254
|
program.addCommand(statusCommand);
|
|
419
1255
|
program.addCommand(nextCommand);
|
|
1256
|
+
program.addCommand(validateCommand);
|
|
1257
|
+
program.addCommand(advanceCommand);
|
|
1258
|
+
program.addCommand(configCommand);
|
|
1259
|
+
program.addCommand(reportCommand);
|
|
420
1260
|
program.parse();
|
|
421
1261
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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 { createRequire } from 'module';\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 require = createRequire(import.meta.url);\nconst { version } = require('../package.json');\n\nconst program = new Command();\n\nprogram\n .name('agent-handoff')\n .description('轻量级多 Agent 协作接力工具')\n .version(version);\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":";;;AACA,SAAS,qBAAqB;AAC9B,SAAS,WAAAA,gBAAe;;;ACFxB,SAAS,eAAe;AACxB,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,IAAM,cAAc,IAAI,QAAQ,MAAM,EAC1C,YAAY,oCAAgB,EAC5B,SAAS,UAAU,wBAAc,EACjC,OAAO,qBAAqB,kCAAS,QAAQ,IAAI,CAAC,EAClD,OAAO,OAAO,MAAc,YAA8B;AACzD,QAAM,gBAAgB,KAAK,QAAQ,QAAQ,MAAM,IAAI;AAErD,MAAI;AACF,UAAM,GAAG,OAAO,aAAa;AAC7B,YAAQ,MAAM,qBAAqB,IAAI,uBAAuB,aAAa,EAAE;AAC7E,YAAQ,KAAK,CAAC;AAAA,EAChB,QAAQ;AAAA,EAER;AAEA,MAAI;AACF,UAAM,GAAG,MAAM,eAAe,EAAE,WAAW,KAAK,CAAC;AACjD,UAAM,GAAG,MAAM,KAAK,KAAK,eAAe,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAErE,UAAM,mBAAmB,SAAS,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWtC,UAAM,gBAAgB,KAAK,UAAU;AAAA,MACnC,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,GAAG,MAAM,CAAC;AAEV,UAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAetB,UAAM,GAAG,UAAU,KAAK,KAAK,eAAe,eAAe,GAAG,gBAAgB;AAC9E,UAAM,GAAG,UAAU,KAAK,KAAK,eAAe,YAAY,GAAG,aAAa;AACxE,UAAM,GAAG,UAAU,KAAK,KAAK,eAAe,UAAU,GAAG,aAAa;AAEtE,YAAQ,IAAI,6BAAwB,IAAI,QAAQ,aAAa,EAAE;AAC/D,YAAQ,IAAI;AAAA;AAAA;AAAA,iCAGe,IAAI;AAAA,+BACN,IAAI;AAAA,CAClC;AAAA,EACG,SAAS,OAAO;AACd,YAAQ,MAAM,6BAA6B,KAAK,EAAE;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;;;ACtEH,SAAS,WAAAC,gBAAe;AACxB,OAAOC,WAAU;;;ACDjB,OAAOC,SAAQ;AACf,OAAOC,WAAU;;;ACDjB,OAAOC,SAAQ;AACf,OAAO,UAAU;AAGjB,eAAsB,cAAc,UAAqC;AACvE,QAAM,UAAU,MAAMA,IAAG,SAAS,UAAU,OAAO;AACnD,QAAM,SAAS,KAAK,MAAM,OAAO;AAEjC,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,MAAM,0BAA0B,QAAQ,EAAE;AAAA,EACtD;AAEA,MAAI,CAAC,OAAO,QAAQ,OAAO,OAAO,SAAS,UAAU;AACnD,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AAEA,MAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,KAAK,OAAO,MAAM,WAAW,GAAG;AAC7D,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AAEA,QAAM,QAAgB,OAAO,MAAM,IAAI,CAAC,MAA+B,UAAkB;AACvF,QAAI,CAAC,KAAK,MAAM,OAAO,KAAK,OAAO,UAAU;AAC3C,YAAM,IAAI,MAAM,QAAQ,KAAK,6BAA6B;AAAA,IAC5D;AACA,QAAI,CAAC,KAAK,YAAY,OAAO,KAAK,aAAa,UAAU;AACvD,YAAM,IAAI,MAAM,QAAQ,KAAK,mCAAmC;AAAA,IAClE;AACA,QAAI,CAAC,KAAK,SAAS,OAAO,KAAK,UAAU,UAAU;AACjD,YAAM,IAAI,MAAM,QAAQ,KAAK,gCAAgC;AAAA,IAC/D;AACA,QAAI,CAAC,KAAK,UAAU,OAAO,KAAK,WAAW,UAAU;AACnD,YAAM,IAAI,MAAM,QAAQ,KAAK,iCAAiC;AAAA,IAChE;AAEA,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,UAAU,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,MACb,YAAY,KAAK;AAAA,MACjB,YAAY,KAAK;AAAA,IACnB;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb;AAAA,EACF;AACF;;;ADhCA,eAAsB,cAAc,eAA+C;AACjF,QAAM,eAAeC,MAAK,QAAQ,aAAa;AAC/C,QAAM,eAAeA,MAAK,KAAK,cAAc,eAAe;AAC5D,QAAM,YAAYA,MAAK,KAAK,cAAc,YAAY;AAEtD,MAAI,SAAS;AACb,MAAI,cAAc;AAClB,MAAI,WAAW;AACf,MAAI;AACJ,MAAI;AACJ,MAAI,cAAc,oBAAI,IAAqB;AAE3C,MAAI;AACF,UAAMC,IAAG,OAAO,YAAY;AAC5B,aAAS;AAAA,EACX,QAAQ;AACN,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,UAAU;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACF,UAAMA,IAAG,OAAO,YAAY;AAC5B,kBAAc;AACd,eAAW,MAAM,cAAc,YAAY;AAAA,EAC7C,QAAQ;AACN,kBAAc;AAAA,EAChB;AAEA,MAAI;AACF,UAAM,eAAe,MAAMA,IAAG,SAAS,WAAW,OAAO;AACzD,eAAW;AACX,YAAQ,KAAK,MAAM,YAAY;AAAA,EACjC,QAAQ;AACN,eAAW;AAAA,EACb;AAEA,MAAI,UAAU;AACZ,kBAAc,MAAM,kBAAkB,cAAc,QAAQ;AAAA,EAC9D;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,kBACpB,eACA,UAC+B;AAC/B,QAAM,UAAU,oBAAI,IAAqB;AAEzC,aAAW,QAAQ,SAAS,OAAO;AACjC,UAAM,aAAaD,MAAK,KAAK,eAAe,KAAK,MAAM;AACvD,QAAI;AACF,YAAM,UAAU,MAAMC,IAAG,SAAS,YAAY,OAAO;AACrD,cAAQ,IAAI,KAAK,IAAI,QAAQ,KAAK,EAAE,SAAS,CAAC;AAAA,IAChD,QAAQ;AACN,cAAQ,IAAI,KAAK,IAAI,KAAK;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO;AACT;;;AE9EO,SAAS,aACd,UACA,aACoB;AACpB,MAAI,CAAC,SAAS,SAAS,SAAS,MAAM,WAAW,GAAG;AAClD,WAAO;AAAA,MACL,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,gBAAgB,CAAC;AAAA,MACjB,cAAc,CAAC;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,iBAA2B,CAAC;AAClC,QAAM,eAAyB,CAAC;AAChC,MAAI,eAAe;AACnB,MAAI,SAAyB;AAE7B,WAAS,IAAI,GAAG,IAAI,SAAS,MAAM,QAAQ,KAAK;AAC9C,UAAM,OAAO,SAAS,MAAM,CAAC;AAC7B,UAAM,eAAe,YAAY,IAAI,KAAK,EAAE,KAAK;AAEjD,QAAI,cAAc;AAChB,qBAAe,KAAK,CAAC;AAAA,IACvB,OAAO;AACL,mBAAa,KAAK,CAAC;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,oBAAoB,aAAa,CAAC;AAExC,MAAI,sBAAsB,QAAW;AACnC,mBAAe,SAAS,MAAM;AAC9B,aAAS;AAAA,EACX,OAAO;AACL,mBAAe;AACf,aAAS;AAAA,EACX;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe,WAAW,SAAS,OAAO;AAAA,IAC1C;AAAA,IACA;AAAA,EACF;AACF;AA4BO,SAAS,eACd,UACA,aAC0D;AAC1D,QAAM,SAAS,aAAa,UAAU,WAAW;AAEjD,MAAI,OAAO,WAAW,UAAU,OAAO,kBAAkB,MAAM;AAC7D,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,MAAM,SAAS,MAAM,OAAO,aAAa;AAAA,EAC3C;AACF;;;AH/FO,IAAM,gBAAgB,IAAIC,SAAQ,QAAQ,EAC9C,YAAY,qCAAiB,EAC7B,SAAS,eAAe,0BAAgB,GAAG,EAC3C,OAAO,cAAc,+BAAW,EAChC,OAAO,OAAO,WAAmB,YAA+B;AAC/D,QAAM,gBAAgBC,MAAK,QAAQ,SAAS;AAE5C,MAAI;AACF,UAAM,OAAO,MAAM,cAAc,aAAa;AAE9C,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,MAAM,+BAA+B,aAAa,EAAE;AAC5D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,CAAC,KAAK,aAAa;AACrB,cAAQ,MAAM,qCAAqC,aAAa,EAAE;AAClE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,CAAC,KAAK,UAAU;AAClB,cAAQ,MAAM,sCAAsC;AACpD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,cAAc,aAAa,KAAK,UAAU,KAAK,WAAW;AAEhE,QAAI,QAAQ,MAAM;AAChB,YAAM,aAAa;AAAA,QACjB,MAAM,KAAK,SAAS;AAAA,QACpB,MAAM;AAAA,QACN,QAAQ,YAAY;AAAA,QACpB,cAAc,YAAY;AAAA,QAC1B,YAAY,KAAK,SAAS,MAAM;AAAA,QAChC,gBAAgB,YAAY,eAAe;AAAA,QAC3C,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,UAC/C;AAAA,UACA,IAAI,KAAK;AAAA,UACT,UAAU,KAAK;AAAA,UACf,YAAY,KAAK;AAAA,UACjB,WAAW,KAAK,YAAY,IAAI,KAAK,EAAE,KAAK;AAAA,QAC9C,EAAE;AAAA,MACJ;AACA,cAAQ,IAAI,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC;AAAA,IACjD,OAAO;AACL,cAAQ,IAAI,cAAc,KAAK,SAAS,IAAI,EAAE;AAC9C,cAAQ,IAAI,WAAW,YAAY,MAAM,EAAE;AAC3C,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,QAAQ;AAEpB,WAAK,SAAS,MAAM,QAAQ,CAAC,MAAM,UAAU;AAC3C,cAAM,YAAY,KAAK,YAAY,IAAI,KAAK,EAAE,KAAK;AACnD,cAAM,aAAa,YAAY,WAAM;AACrC,cAAM,UAAU,OAAO,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AACjD,YAAI,OAAO,KAAK,UAAU,IAAI,OAAO,IAAI,KAAK,EAAE,KAAK,KAAK,QAAQ;AAClE,YAAI,KAAK,YAAY;AACnB,kBAAQ,KAAK,KAAK,UAAU;AAAA,QAC9B;AACA,gBAAQ,IAAI,IAAI;AAAA,MAClB,CAAC;AAED,cAAQ,IAAI,EAAE;AACd,UAAI,YAAY,WAAW,QAAQ;AACjC,gBAAQ,IAAI,oBAAoB;AAAA,MAClC,OAAO;AACL,cAAM,cAAc,KAAK,SAAS,MAAM,YAAY,YAAY;AAChE,gBAAQ,IAAI,iBAAiB,YAAY,eAAe,CAAC,KAAK,aAAa,EAAE,GAAG;AAAA,MAClF;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,UAAU,KAAK,EAAE;AAC/B,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;;;AI9EH,SAAS,WAAAC,gBAAe;AACxB,OAAOC,WAAU;;;ACQV,SAAS,eAAe,SAAgC;AAC7D,QAAM,EAAE,UAAU,MAAM,UAAU,IAAI;AACtC,QAAM,aAAa,SAAS,MAAM;AAClC,QAAM,UAAU,YAAY;AAE5B,MAAI,SAAS,uBAAQ,KAAK,EAAE;AAAA;AAAA;AAAA,cAGhB,SAAS,IAAI;AAAA,UACjB,OAAO,MAAM,UAAU;AAAA,cACnB,KAAK,QAAQ;AAEzB,MAAI,KAAK,YAAY;AACnB,cAAU;AAAA,eAAkB,KAAK,UAAU;AAAA,EAC7C;AAEA,YAAU;AAAA;AAAA;AAAA;AAAA,IAIR,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA,IAIV,KAAK,MAAM;AAEb,MAAI,KAAK,cAAc,KAAK,WAAW,SAAS,GAAG;AACjD,cAAU;AAAA;AAAA;AAGV,eAAW,YAAY,KAAK,YAAY;AACtC,gBAAU;AAAA,IAAO,QAAQ;AAAA,IAC3B;AAAA,EACF;AAEA,YAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYV,SAAO;AACT;;;ADnDO,IAAM,cAAc,IAAIC,SAAQ,MAAM,EAC1C,YAAY,qEAAmB,EAC/B,SAAS,eAAe,0BAAgB,GAAG,EAC3C,OAAO,cAAc,8DAAsB,EAC3C,OAAO,OAAO,WAAmB,YAA+B;AAC/D,QAAM,gBAAgBC,MAAK,QAAQ,SAAS;AAE5C,MAAI;AACF,UAAM,OAAO,MAAM,cAAc,aAAa;AAE9C,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,MAAM,+BAA+B,aAAa,EAAE;AAC5D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,CAAC,KAAK,aAAa;AACrB,cAAQ,MAAM,qCAAqC,aAAa,EAAE;AAClE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,CAAC,KAAK,UAAU;AAClB,cAAQ,MAAM,sCAAsC;AACpD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,cAAc,aAAa,KAAK,UAAU,KAAK,WAAW;AAEhE,QAAI,YAAY,WAAW,QAAQ;AACjC,cAAQ,IAAI,aAAa,KAAK,SAAS,IAAI,oDAAY;AACvD,cAAQ,IAAI,4CAAS;AACrB;AAAA,IACF;AAEA,UAAM,cAAc,eAAe,KAAK,UAAU,KAAK,WAAW;AAElE,QAAI,CAAC,aAAa;AAChB,cAAQ,IAAI,aAAa,KAAK,SAAS,IAAI,oDAAY;AACvD,cAAQ,IAAI,4CAAS;AACrB;AAAA,IACF;AAEA,UAAM,EAAE,MAAM,MAAM,IAAI;AAExB,YAAQ,IAAI,SAAS,KAAK,EAAE,EAAE;AAC9B,YAAQ,IAAI,aAAa,KAAK,QAAQ,EAAE;AACxC,QAAI,KAAK,YAAY;AACnB,cAAQ,IAAI,cAAc,KAAK,UAAU,EAAE;AAAA,IAC7C;AACA,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,QAAQ;AACpB,YAAQ,IAAI,OAAO,KAAK,KAAK,EAAE;AAC/B,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,SAAS;AACrB,YAAQ,IAAI,OAAO,KAAK,MAAM,EAAE;AAChC,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,SAAS;AACrB,YAAQ,IAAI,kPAA0C;AAEtD,UAAM,SAAS,eAAe;AAAA,MAC5B,UAAU,KAAK;AAAA,MACf;AAAA,MACA,WAAW;AAAA,MACX,eAAe,KAAK;AAAA,IACtB,CAAC;AAED,YAAQ,IAAI,MAAM;AAClB,YAAQ,IAAI,kPAA0C;AACtD,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,oGAAmC;AAE/C,QAAI,QAAQ,MAAM;AAChB,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,4FAAsB;AAAA,IACpC;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,UAAU,KAAK,EAAE;AAC/B,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;;;AN7EH,IAAMC,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,EAAE,QAAQ,IAAIA,SAAQ,iBAAiB;AAE7C,IAAM,UAAU,IAAIC,SAAQ;AAE5B,QACG,KAAK,eAAe,EACpB,YAAY,qEAAmB,EAC/B,QAAQ,OAAO;AAElB,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,aAAa;AAChC,QAAQ,WAAW,WAAW;AAE9B,QAAQ,MAAM;","names":["Command","Command","path","fs","path","fs","path","fs","Command","path","Command","path","Command","path","require","Command"]}
|
|
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","../src/core/clipboard.ts","../src/core/events-writer.ts","../src/cli/commands/validate.ts","../src/core/artifact-validator.ts","../src/cli/commands/advance.ts","../src/cli/commands/config.ts","../src/core/config.ts","../src/cli/commands/report.ts","../src/adapters/trae/operation-reporter.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { createRequire } from 'module';\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';\nimport { validateCommand } from './cli/commands/validate.js';\nimport { advanceCommand } from './cli/commands/advance.js';\nimport { configCommand } from './cli/commands/config.js';\nimport { reportCommand } from './cli/commands/report.js';\n\nconst require = createRequire(import.meta.url);\nconst { version } = require('../package.json');\n\nconst program = new Command();\n\nprogram\n .name('agent-handoff')\n .description('轻量级多 Agent 协作接力工具')\n .version(version);\n\nprogram.addCommand(initCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(nextCommand);\nprogram.addCommand(validateCommand);\nprogram.addCommand(advanceCommand);\nprogram.addCommand(configCommand);\nprogram.addCommand(reportCommand);\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';\nimport { copyToClipboard, isClipboardSupported } from '../../core/clipboard.js';\nimport { writeEvent } from '../../core/events-writer.js';\n\nexport const nextCommand = new Command('next')\n .description('输出下一步执行指令和 prompt')\n .argument('[workspace]', 'workspace 路径', '.')\n .option('-c, --copy', '复制 prompt 到剪贴板')\n .option('--no-event', '不写入事件日志')\n .action(async (workspace: string, options: { copy: boolean; event: 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\n if (options.event) {\n await writeEvent({\n workspacePath,\n step: { index: index + 1, id: step.id },\n type: 'step.started',\n summary: `开始执行步骤: ${step.id}`,\n workItemId: step.workItemId,\n links: [step.input],\n });\n }\n\n if (options.copy) {\n if (!isClipboardSupported()) {\n console.log('⚠️ 剪贴板功能在当前环境不可用');\n console.log('请手动复制上面的 Prompt');\n } else {\n const result = await copyToClipboard(prompt);\n if (result.success) {\n console.log('✅ Prompt 已复制到剪贴板');\n } else {\n console.log(`❌ 复制失败: ${result.error}`);\n console.log('请手动复制上面的 Prompt');\n }\n }\n } else {\n console.log('提示:将上述 Prompt 复制到 TRAE 新 Task 中执行');\n console.log(' 使用 --copy 选项可自动复制到剪贴板');\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","import clipboard from 'clipboardy';\n\nexport interface ClipboardResult {\n success: boolean;\n error?: string;\n}\n\nlet clipboardSupported: boolean | null = null;\n\nexport function isClipboardSupported(): boolean {\n if (clipboardSupported !== null) {\n return clipboardSupported;\n }\n\n try {\n clipboardSupported = true;\n return true;\n } catch {\n clipboardSupported = false;\n return false;\n }\n}\n\nexport async function copyToClipboard(text: string): Promise<ClipboardResult> {\n try {\n await clipboard.write(text);\n return { success: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: errorMessage,\n };\n }\n}\n\nexport async function readFromClipboard(): Promise<string> {\n try {\n return await clipboard.read();\n } catch {\n return '';\n }\n}\n","import fs from 'fs/promises';\nimport path from 'path';\nimport { Event, EventType, EventStep } from './models/event.js';\n\nexport interface WriteEventOptions {\n workspacePath: string;\n step: EventStep;\n type: EventType;\n summary: string;\n workItemId?: string;\n links?: string[];\n data?: Record<string, unknown>;\n}\n\nexport interface EventsWriterResult {\n success: boolean;\n event: Event;\n error?: string;\n}\n\nexport async function writeEvent(options: WriteEventOptions): Promise<EventsWriterResult> {\n const { workspacePath, step, type, summary, workItemId, links, data } = options;\n\n const event: Event = {\n ts: new Date().toISOString(),\n step,\n type,\n summary,\n ...(workItemId && { workItemId }),\n ...(links && links.length > 0 && { links }),\n ...(data && { data }),\n };\n\n try {\n const eventsPath = path.join(workspacePath, 'events.jsonl');\n const line = JSON.stringify(event) + '\\n';\n await fs.appendFile(eventsPath, line, 'utf-8');\n\n return {\n success: true,\n event,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n event,\n error: errorMessage,\n };\n }\n}\n\nexport async function readEvents(workspacePath: string): Promise<Event[]> {\n const eventsPath = path.join(workspacePath, 'events.jsonl');\n\n try {\n const content = await fs.readFile(eventsPath, 'utf-8');\n const lines = content.split('\\n').filter((line) => line.trim());\n\n const events: Event[] = [];\n for (const line of lines) {\n try {\n const event = JSON.parse(line) as Event;\n events.push(event);\n } catch {\n // Skip invalid lines\n }\n }\n\n return events;\n } catch {\n return [];\n }\n}\n\nexport async function getLatestEvent(workspacePath: string): Promise<Event | null> {\n const events = await readEvents(workspacePath);\n if (events.length === 0) {\n return null;\n }\n return events[events.length - 1];\n}\n\nexport async function getEventsByStep(workspacePath: string, stepId: string): Promise<Event[]> {\n const events = await readEvents(workspacePath);\n return events.filter((event) => event.step.id === stepId);\n}\n\nexport async function getEventsByType(workspacePath: string, type: EventType): Promise<Event[]> {\n const events = await readEvents(workspacePath);\n return events.filter((event) => event.type === type);\n}\n\nexport async function getEventsByWorkItem(\n workspacePath: string,\n workItemId: string\n): Promise<Event[]> {\n const events = await readEvents(workspacePath);\n return events.filter((event) => event.workItemId === workItemId);\n}\n","import { Command } from 'commander';\nimport path from 'path';\nimport { loadWorkspace } from '../../core/workspace.js';\nimport { validateWorkspaceArtifacts } from '../../core/artifact-validator.js';\n\nexport const validateCommand = new Command('validate')\n .description('校验 workspace 产物结构')\n .argument('[workspace]', 'workspace 路径', '.')\n .option('--strict', '严格模式(将警告视为错误)')\n .option('-j, --json', 'JSON 格式输出')\n .action(async (workspace: string, options: { strict: boolean; 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 result = await validateWorkspaceArtifacts(workspacePath);\n\n if (options.json) {\n const jsonOutput = {\n valid: options.strict ? result.valid && result.warningCount === 0 : result.valid,\n workspace: workspacePath,\n totalSteps: result.totalSteps,\n validSteps: result.validSteps,\n errorCount: result.errorCount,\n warningCount: result.warningCount,\n steps: Array.from(result.stepResults.entries()).map(([stepId, stepResult]) => ({\n stepId,\n valid: options.strict ? stepResult.valid && stepResult.warnings.length === 0 : stepResult.valid,\n errors: stepResult.errors,\n warnings: stepResult.warnings,\n })),\n };\n console.log(JSON.stringify(jsonOutput, null, 2));\n } else {\n console.log(`Workspace: ${info.workflow.name}`);\n console.log('');\n\n for (const step of info.workflow.steps) {\n const stepResult = result.stepResults.get(step.id);\n if (!stepResult) continue;\n\n const icon = stepResult.valid ? '✅' : '❌';\n console.log(`${icon} ${step.output} - ${stepResult.valid ? 'Valid' : 'Invalid'}`);\n\n if (!stepResult.valid) {\n for (const error of stepResult.errors) {\n console.log(` - ${error.message}`);\n }\n }\n\n if (stepResult.warnings.length > 0) {\n for (const warning of stepResult.warnings) {\n console.log(` ⚠️ ${warning.message}`);\n }\n }\n }\n\n console.log('');\n\n const hasErrors = result.errorCount > 0;\n const hasWarnings = result.warningCount > 0;\n const strictFail = options.strict && hasWarnings;\n\n if (!hasErrors && !strictFail) {\n console.log('All artifacts validated successfully.');\n if (hasWarnings) {\n console.log(`⚠️ ${result.warningCount} warning(s) found.`);\n }\n } else {\n const totalErrors = result.errorCount + (strictFail ? result.warningCount : 0);\n console.log(`Validation failed with ${totalErrors} error(s).`);\n }\n\n if (hasErrors || strictFail) {\n process.exit(1);\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 { loadWorkspace } from './workspace.js';\n\nexport interface ValidationResult {\n valid: boolean;\n errors: ValidationError[];\n warnings: ValidationWarning[];\n}\n\nexport interface ValidationError {\n type: 'missing_section' | 'empty_section' | 'invalid_format';\n section: string;\n message: string;\n}\n\nexport interface ValidationWarning {\n type: 'short_content' | 'missing_detail';\n section: string;\n message: string;\n}\n\nexport const REQUIRED_SECTIONS = [\n '产物更新',\n '关键决策',\n '风险与待确认',\n '下一步交接',\n] as const;\n\nexport type RequiredSection = (typeof REQUIRED_SECTIONS)[number];\n\nexport interface WorkspaceValidationResult {\n valid: boolean;\n stepResults: Map<string, ValidationResult>;\n totalSteps: number;\n validSteps: number;\n errorCount: number;\n warningCount: number;\n}\n\nconst MIN_CONTENT_LENGTH = 10;\n\nexport function validateArtifact(content: string): ValidationResult {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n\n for (const section of REQUIRED_SECTIONS) {\n const sectionResult = findSection(content, section);\n\n if (!sectionResult.found) {\n errors.push({\n type: 'missing_section',\n section,\n message: `缺少必要区块: ${section}`,\n });\n } else if (!sectionResult.hasContent) {\n errors.push({\n type: 'empty_section',\n section,\n message: `区块内容为空: ${section}`,\n });\n } else if (sectionResult.contentLength < MIN_CONTENT_LENGTH) {\n warnings.push({\n type: 'short_content',\n section,\n message: `区块内容过短 (${sectionResult.contentLength} 字符): ${section}`,\n });\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\ninterface SectionResult {\n found: boolean;\n hasContent: boolean;\n contentLength: number;\n}\n\nfunction findSection(content: string, sectionName: string): SectionResult {\n const patterns = [\n new RegExp(`^##\\\\s+${escapeRegex(sectionName)}[^\\\\n]*$`, 'm'),\n new RegExp(`^###\\\\s+${escapeRegex(sectionName)}[^\\\\n]*$`, 'm'),\n ];\n\n let matchIndex = -1;\n let matchLength = 0;\n\n for (const pattern of patterns) {\n const match = content.match(pattern);\n if (match && match.index !== undefined) {\n matchIndex = match.index;\n matchLength = match[0].length;\n break;\n }\n }\n\n if (matchIndex === -1) {\n return { found: false, hasContent: false, contentLength: 0 };\n }\n\n const contentStart = matchIndex + matchLength;\n const remainingContent = content.slice(contentStart);\n\n const nextSectionMatch = remainingContent.match(/^#{1,3}\\s+/m);\n const sectionContent = nextSectionMatch\n ? remainingContent.slice(0, nextSectionMatch.index)\n : remainingContent;\n\n const trimmedContent = sectionContent.trim();\n\n return {\n found: true,\n hasContent: trimmedContent.length > 0,\n contentLength: trimmedContent.length,\n };\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport async function validateArtifactFile(\n filePath: string\n): Promise<ValidationResult> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n return validateArtifact(content);\n } catch {\n return {\n valid: false,\n errors: [\n {\n type: 'invalid_format',\n section: '',\n message: `无法读取文件: ${filePath}`,\n },\n ],\n warnings: [],\n };\n }\n}\n\nexport async function validateWorkspaceArtifacts(\n workspacePath: string\n): Promise<WorkspaceValidationResult> {\n const workspace = await loadWorkspace(workspacePath);\n const stepResults = new Map<string, ValidationResult>();\n let validSteps = 0;\n let errorCount = 0;\n let warningCount = 0;\n\n if (!workspace.workflow) {\n return {\n valid: false,\n stepResults,\n totalSteps: 0,\n validSteps: 0,\n errorCount: 1,\n warningCount: 0,\n };\n }\n\n for (const step of workspace.workflow.steps) {\n const outputPath = path.join(workspace.path, step.output);\n const result = await validateArtifactFile(outputPath);\n stepResults.set(step.id, result);\n\n if (result.valid) {\n validSteps++;\n }\n errorCount += result.errors.length;\n warningCount += result.warnings.length;\n }\n\n return {\n valid: errorCount === 0,\n stepResults,\n totalSteps: workspace.workflow.steps.length,\n validSteps,\n errorCount,\n warningCount,\n };\n}\n","import { Command } from 'commander';\nimport path from 'path';\nimport fs from 'fs/promises';\nimport { loadWorkspace } from '../../core/workspace.js';\nimport { computeState, getCurrentStep } from '../../core/state-machine.js';\nimport { writeEvent } from '../../core/events-writer.js';\nimport { EventType } from '../../core/models/event.js';\n\nexport const advanceCommand = new Command('advance')\n .description('推进 workspace 状态')\n .argument('[workspace]', 'workspace 路径', '.')\n .option('-e, --event <type>', '事件类型', 'step.done')\n .option('-s, --summary <text>', '事件摘要')\n .option('--no-state', '不更新 state.json')\n .option('--skip-event', '不写入事件日志')\n .action(\n async (\n workspace: string,\n options: { event: string; summary?: string; state: boolean; skipEvent: boolean }\n ) => {\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 console.log(`Workspace: ${info.workflow.name}`);\n\n if (stateResult.status === 'done') {\n console.log('Status: done');\n console.log('');\n console.log('⚠️ Workflow already completed. No steps to advance.');\n return;\n }\n\n const currentStep = getCurrentStep(info.workflow, info.stepOutputs);\n\n if (!currentStep) {\n console.log('Status: done');\n console.log('');\n console.log('⚠️ Workflow already completed. No steps to advance.');\n return;\n }\n\n const { step, index } = currentStep;\n\n console.log(`Current step: ${step.id} (index: ${index})`);\n console.log('');\n\n const eventType = options.event as EventType;\n const eventSummary = options.summary || getDefaultSummary(eventType, step.id);\n\n if (!options.skipEvent) {\n const result = await writeEvent({\n workspacePath,\n step: { index: index + 1, id: step.id },\n type: eventType,\n summary: eventSummary,\n workItemId: step.workItemId,\n links: [step.output],\n });\n\n if (result.success) {\n console.log(`✅ Event written: ${eventType}`);\n console.log(` Summary: ${eventSummary}`);\n if (step.workItemId) {\n console.log(` Work Item: ${step.workItemId}`);\n }\n if (step.output) {\n console.log(` Links: ${step.output}`);\n }\n } else {\n console.log(`❌ Failed to write event: ${result.error}`);\n }\n }\n\n if (options.state) {\n const statePath = path.join(workspacePath, 'state.json');\n const newIndex = index + 1;\n const isDone = newIndex >= info.workflow.steps.length;\n\n const newState = {\n currentIndex: newIndex,\n status: isDone ? 'done' : 'running',\n updatedAt: new Date().toISOString(),\n };\n\n await fs.writeFile(statePath, JSON.stringify(newState, null, 2));\n console.log('');\n console.log(`✅ State updated: ${statePath}`);\n console.log(` Current index: ${newState.currentIndex}`);\n console.log(` Status: ${newState.status}`);\n\n if (isDone) {\n console.log('');\n console.log('🎉 Workflow completed!');\n } else {\n const nextStep = info.workflow.steps[newIndex];\n console.log(` Next step: ${nextStep.id}`);\n }\n }\n } catch (error) {\n console.error(`Error: ${error}`);\n process.exit(1);\n }\n }\n );\n\nfunction getDefaultSummary(eventType: EventType, stepId: string): string {\n const summaries: Record<EventType, string> = {\n 'step.started': `开始执行步骤: ${stepId}`,\n 'step.done': `步骤完成: ${stepId}`,\n 'artifact.updated': '产物已更新',\n 'workflow.updated': '工作流已更新',\n 'verify.passed': '验证通过',\n 'verify.failed': '验证失败',\n 'accept.passed': '验收通过',\n 'accept.failed': '验收失败',\n 'issue.raised': '发现问题',\n 'handoff.sent': '已交接给下一步',\n 'automation.session': '自动化会话已记录',\n };\n return summaries[eventType] || `事件: ${eventType}`;\n}\n","import { Command } from 'commander';\nimport os from 'os';\nimport {\n loadConfig,\n findConfigFile,\n initConfigFile,\n DEFAULT_CONFIG,\n} from '../../core/config.js';\n\nexport const configCommand = new Command('config')\n .description('查看或管理配置')\n .argument('[action]', '操作: show, init', 'show')\n .option('-g, --global', '操作全局配置')\n .option('--verbose', '显示详细信息')\n .action(async (action: string, options: { global: boolean; verbose: boolean }) => {\n try {\n if (action === 'show') {\n await showConfig(options);\n } else if (action === 'init') {\n await initConfig(options);\n } else if (action === 'list') {\n await listConfig();\n } else {\n console.error(`Unknown action: ${action}`);\n console.log('Available actions: show, init, list');\n process.exit(1);\n }\n } catch (error) {\n console.error(`Error: ${error}`);\n process.exit(1);\n }\n });\n\nasync function showConfig(options: { global: boolean; verbose: boolean }): Promise<void> {\n const targetPath = options.global ? os.homedir() : process.cwd();\n const config = await loadConfig(targetPath);\n\n if (options.verbose) {\n const configFile = await findConfigFile(targetPath);\n if (configFile) {\n console.log(`Config file: ${configFile}`);\n console.log('');\n } else {\n console.log('No config file found, using defaults');\n console.log('');\n }\n }\n\n console.log(JSON.stringify(config, null, 2));\n}\n\nasync function initConfig(options: { global: boolean; verbose: boolean }): Promise<void> {\n const targetPath = options.global ? os.homedir() : process.cwd();\n const configPath = await initConfigFile(targetPath, options.global);\n\n console.log(`✅ Created config file: ${configPath}`);\n console.log('');\n console.log('Default configuration:');\n console.log(JSON.stringify(DEFAULT_CONFIG, null, 2));\n}\n\nasync function listConfig(): Promise<void> {\n console.log('Configuration options:');\n console.log('');\n console.log(' defaultWorkspace - 默认 workspace 路径');\n console.log(' events.enabled - 是否启用事件日志 (default: true)');\n console.log(' events.logStepStarted - 是否记录 step.started (default: true)');\n console.log(' events.logStepDone - 是否记录 step.done (default: true)');\n console.log(' clipboard.autoCopy - 是否自动复制 prompt (default: false)');\n console.log(' prompt.templatePath - 自定义 prompt 模板路径');\n console.log(' prompt.language - prompt 语言 (default: zh)');\n console.log(' validation.strict - 严格校验模式 (default: false)');\n console.log(' validation.warnOnShortContent - 内容过短时警告 (default: true)');\n console.log('');\n console.log('Config file locations (in order of priority):');\n console.log(' .agenthandoffrc');\n console.log(' .agenthandoffrc.json');\n console.log(' .agenthandoffrc.yaml');\n console.log(' package.json#agenthandoff');\n console.log(` ~/.agenthandoffrc (global)`);\n}\n","import fs from 'fs/promises';\nimport path from 'path';\nimport os from 'os';\n\nexport interface AgentHandoffConfig {\n defaultWorkspace?: string;\n events?: {\n enabled: boolean;\n logStepStarted: boolean;\n logStepDone: boolean;\n };\n automation?: {\n enabled: boolean;\n provider: 'nutjs';\n screenshot: boolean;\n timeout: number;\n retries: number;\n confidence?: number;\n };\n clipboard?: {\n autoCopy: boolean;\n };\n prompt?: {\n templatePath?: string;\n language: 'zh' | 'en';\n };\n validation?: {\n strict: boolean;\n warnOnShortContent: boolean;\n };\n}\n\nexport const DEFAULT_CONFIG: AgentHandoffConfig = {\n events: {\n enabled: true,\n logStepStarted: true,\n logStepDone: true,\n },\n automation: {\n enabled: false,\n provider: 'nutjs',\n screenshot: false,\n timeout: 30000,\n retries: 3,\n confidence: 0.8,\n },\n clipboard: {\n autoCopy: false,\n },\n prompt: {\n language: 'zh',\n },\n validation: {\n strict: false,\n warnOnShortContent: true,\n },\n};\n\nconst CONFIG_FILES = [\n '.agenthandoffrc',\n '.agenthandoffrc.json',\n '.agenthandoffrc.yaml',\n '.agenthandoffrc.yml',\n];\n\nconst configCache = new Map<string, AgentHandoffConfig>();\n\nexport async function findConfigFile(startPath: string): Promise<string | null> {\n let currentPath = path.resolve(startPath);\n const homePath = os.homedir();\n\n while (true) {\n for (const configFile of CONFIG_FILES) {\n const filePath = path.join(currentPath, configFile);\n try {\n await fs.access(filePath);\n return filePath;\n } catch {\n // File doesn't exist, continue\n }\n }\n\n const packageJsonPath = path.join(currentPath, 'package.json');\n try {\n const content = await fs.readFile(packageJsonPath, 'utf-8');\n const pkg = JSON.parse(content);\n if (pkg.agenthandoff) {\n return packageJsonPath;\n }\n } catch {\n // File doesn't exist or invalid, continue\n }\n\n if (currentPath === homePath || currentPath === path.dirname(currentPath)) {\n break;\n }\n currentPath = path.dirname(currentPath);\n }\n\n return null;\n}\n\nexport async function loadConfigFile(filePath: string): Promise<Partial<AgentHandoffConfig>> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n const ext = path.extname(filePath);\n\n if (ext === '.yaml' || ext === '.yml') {\n return parseYaml(content);\n }\n\n if (filePath.endsWith('package.json')) {\n const pkg = JSON.parse(content);\n return pkg.agenthandoff || {};\n }\n\n return JSON.parse(content);\n } catch {\n return {};\n }\n}\n\nfunction parseYaml(content: string): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n const lines = content.split('\\n');\n let currentObj: Record<string, unknown> = result;\n const stack: Array<{ obj: Record<string, unknown> }> = [];\n\n for (const line of lines) {\n if (!line.trim() || line.trim().startsWith('#')) {\n continue;\n }\n\n const indent = line.search(/\\S/);\n const trimmed = line.trim();\n\n if (trimmed.includes(':')) {\n const colonIndex = trimmed.indexOf(':');\n const key = trimmed.slice(0, colonIndex).trim();\n const value = trimmed.slice(colonIndex + 1).trim();\n\n if (value === '') {\n if (stack.length > 0) {\n const expectedIndent = stack.length * 2;\n if (indent < expectedIndent) {\n while (stack.length > 0 && indent < stack.length * 2) {\n stack.pop();\n }\n if (stack.length > 0) {\n currentObj = stack[stack.length - 1].obj as Record<string, unknown>;\n } else {\n currentObj = result;\n }\n }\n }\n\n const newObj: Record<string, unknown> = {};\n currentObj[key] = newObj;\n stack.push({ obj: currentObj });\n currentObj = newObj;\n } else {\n let parsedValue: unknown = value;\n if (value === 'true') parsedValue = true;\n else if (value === 'false') parsedValue = false;\n else if (value === 'null') parsedValue = null;\n else if (/^\\d+$/.test(value)) parsedValue = parseInt(value, 10);\n else if (/^\\d+\\.\\d+$/.test(value)) parsedValue = parseFloat(value);\n else if (value.startsWith('\"') && value.endsWith('\"')) {\n parsedValue = value.slice(1, -1);\n } else if (value.startsWith(\"'\") && value.endsWith(\"'\")) {\n parsedValue = value.slice(1, -1);\n }\n\n currentObj[key] = parsedValue;\n }\n }\n }\n\n return result;\n}\n\nexport function mergeConfig(\n base: AgentHandoffConfig,\n override: Partial<AgentHandoffConfig>\n): AgentHandoffConfig {\n const result: AgentHandoffConfig = JSON.parse(JSON.stringify(base));\n\n for (const key of Object.keys(override) as Array<keyof AgentHandoffConfig>) {\n const overrideValue = override[key];\n if (overrideValue === undefined) continue;\n\n if (\n typeof overrideValue === 'object' &&\n overrideValue !== null &&\n !Array.isArray(overrideValue)\n ) {\n const baseValue = result[key];\n if (\n typeof baseValue === 'object' &&\n baseValue !== null &&\n !Array.isArray(baseValue)\n ) {\n // Deep merge for nested objects\n (result as Record<string, unknown>)[key] = mergeConfig(\n baseValue as AgentHandoffConfig,\n overrideValue as Partial<AgentHandoffConfig>\n );\n } else {\n (result as Record<string, unknown>)[key] = overrideValue;\n }\n } else {\n (result as Record<string, unknown>)[key] = overrideValue;\n }\n }\n\n return result;\n}\n\nexport async function loadConfig(workspacePath?: string): Promise<AgentHandoffConfig> {\n const startPath = workspacePath ? path.resolve(workspacePath) : process.cwd();\n const cacheKey = startPath;\n\n if (configCache.has(cacheKey)) {\n return configCache.get(cacheKey)!;\n }\n\n let config = JSON.parse(JSON.stringify(DEFAULT_CONFIG)) as AgentHandoffConfig;\n\n const globalConfigPath = path.join(os.homedir(), '.agenthandoffrc');\n try {\n await fs.access(globalConfigPath);\n const globalConfig = await loadConfigFile(globalConfigPath);\n config = mergeConfig(config, globalConfig);\n } catch {\n // Global config doesn't exist\n }\n\n const localConfigFile = await findConfigFile(startPath);\n if (localConfigFile) {\n const localConfig = await loadConfigFile(localConfigFile);\n config = mergeConfig(config, localConfig);\n }\n\n configCache.set(cacheKey, config);\n return config;\n}\n\nexport function clearConfigCache(): void {\n configCache.clear();\n}\n\nexport async function initConfigFile(targetPath: string, global = false): Promise<string> {\n const configPath = global\n ? path.join(os.homedir(), '.agenthandoffrc')\n : path.join(targetPath, '.agenthandoffrc');\n\n const content = JSON.stringify(DEFAULT_CONFIG, null, 2);\n await fs.writeFile(configPath, content, 'utf-8');\n\n return configPath;\n}\n","import { Command } from 'commander';\nimport fs from 'fs/promises';\nimport path from 'path';\nimport { OperationReporter } from '../../adapters/trae/operation-reporter.js';\n\ntype ReportFormat = 'json' | 'markdown' | 'html';\n\nexport const reportCommand = new Command('report')\n .description('生成自动化操作报告')\n .argument('[workspace]', 'workspace 路径', '.')\n .option('-f, --format <format>', '报告格式: json, markdown, html', 'markdown')\n .option('-s, --session <id>', '指定 session ID')\n .option('--screenshots', '包含截图', false)\n .option('-o, --output <path>', '输出路径')\n .action(\n async (\n workspace: string,\n options: { format: ReportFormat; session?: string; screenshots: boolean; output?: string }\n ) => {\n const workspacePath = path.resolve(workspace);\n const logDir = path.join(workspacePath, 'operations');\n\n try {\n let sessionFile: string;\n if (options.session) {\n sessionFile = path.join(logDir, `operations-${options.session}.jsonl`);\n } else {\n const files = await fs.readdir(logDir);\n const jsonlFiles = files\n .filter((f) => f.endsWith('.jsonl'))\n .sort()\n .reverse();\n if (jsonlFiles.length === 0) {\n console.error('Error: no operation logs found');\n process.exit(1);\n }\n sessionFile = path.join(logDir, jsonlFiles[0]);\n }\n\n const content = await fs.readFile(sessionFile, 'utf-8');\n const lines = content.split('\\n').filter((l) => l.trim());\n const session = JSON.parse(lines[lines.length - 1]);\n\n const reporter = new OperationReporter(session);\n const report = reporter.generate({\n format: options.format,\n includeScreenshots: options.screenshots,\n });\n\n if (options.output) {\n await fs.writeFile(path.resolve(options.output), report, 'utf-8');\n console.log(`✅ Report saved to: ${options.output}`);\n } else {\n console.log(report);\n }\n } catch (error) {\n console.error(`Error: ${error}`);\n process.exit(1);\n }\n }\n );\n\n","import fs from 'fs/promises';\nimport { TraeSession } from './types';\n\nexport interface ReportOptions {\n format: 'json' | 'markdown' | 'html';\n includeScreenshots: boolean;\n}\n\nexport class OperationReporter {\n private session: TraeSession;\n\n constructor(session: TraeSession) {\n this.session = session;\n }\n\n generate(options: ReportOptions): string {\n switch (options.format) {\n case 'json':\n return this.generateJson();\n case 'markdown':\n return this.generateMarkdown(options.includeScreenshots);\n case 'html':\n return this.generateHtml(options.includeScreenshots);\n default:\n return this.generateMarkdown(options.includeScreenshots);\n }\n }\n\n private generateJson(): string {\n return JSON.stringify(this.session, null, 2);\n }\n\n private generateMarkdown(includeScreenshots: boolean): string {\n const lines: string[] = [\n '# Automation Session Report',\n '',\n `**Session ID:** ${this.session.id}`,\n `**Started At:** ${this.session.startedAt}`,\n `**Workspace:** ${this.session.workspacePath}`,\n `**Step ID:** ${this.session.stepId}`,\n ...(this.session.status ? [`**Status:** ${this.session.status}`] : []),\n ...(this.session.error ? [`**Error:** ${this.session.error}`] : []),\n '',\n `## Operations (${this.session.operations.length})`,\n '',\n ];\n\n this.session.operations.forEach((op, index) => {\n lines.push(`### ${index + 1}. ${op.type}`);\n if (op.target) {\n lines.push(`- **Target:** ${op.target}`);\n }\n if (op.value) {\n lines.push(\n `- **Value:** ${op.value.substring(0, 100)}${op.value.length > 100 ? '...' : ''}`\n );\n }\n lines.push(`- **Timestamp:** ${new Date(op.timestamp).toISOString()}`);\n lines.push('');\n });\n\n if (includeScreenshots && this.session.screenshots.length > 0) {\n lines.push(`## Screenshots (${this.session.screenshots.length})`);\n lines.push('');\n this.session.screenshots.forEach((screenshot, index) => {\n lines.push(`### Screenshot ${index + 1}`);\n lines.push(``);\n lines.push('');\n });\n }\n\n return lines.join('\\n');\n }\n\n private generateHtml(includeScreenshots: boolean): string {\n const operationsHtml = this.session.operations\n .map(\n (op, index) => `\n <div class=\"operation\">\n <h3>${index + 1}. ${op.type}</h3>\n ${op.target ? `<p><strong>Target:</strong> ${op.target}</p>` : ''}\n ${\n op.value\n ? `<p><strong>Value:</strong> ${op.value.substring(0, 100)}${\n op.value.length > 100 ? '...' : ''\n }</p>`\n : ''\n }\n <p><strong>Timestamp:</strong> ${new Date(op.timestamp).toISOString()}</p>\n </div>\n `\n )\n .join('');\n\n const screenshotsHtml =\n includeScreenshots && this.session.screenshots.length > 0\n ? `\n <h2>Screenshots (${this.session.screenshots.length})</h2>\n ${this.session.screenshots\n .map(\n (s, i) => `\n <div class=\"screenshot\">\n <h3>Screenshot ${i + 1}</h3>\n <img src=\"${s}\" alt=\"Screenshot ${i + 1}\" />\n </div>\n `\n )\n .join('')}\n `\n : '';\n\n const statusLine = this.session.status\n ? `<p><strong>Status:</strong> ${this.session.status}</p>`\n : '';\n const errorLine = this.session.error ? `<p><strong>Error:</strong> ${this.session.error}</p>` : '';\n\n return `\n<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Automation Session Report</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n max-width: 800px;\n margin: 0 auto;\n padding: 20px;\n background: #f5f5f5;\n }\n .header {\n background: white;\n padding: 20px;\n border-radius: 8px;\n margin-bottom: 20px;\n }\n .operation {\n background: white;\n padding: 15px;\n border-radius: 8px;\n margin-bottom: 10px;\n }\n .screenshot img {\n max-width: 100%;\n border-radius: 8px;\n }\n h1 { color: #333; }\n h2 { color: #555; border-bottom: 1px solid #eee; padding-bottom: 10px; }\n h3 { color: #666; margin: 0 0 10px 0; }\n p { margin: 5px 0; color: #666; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <h1>Automation Session Report</h1>\n <p><strong>Session ID:</strong> ${this.session.id}</p>\n <p><strong>Started At:</strong> ${this.session.startedAt}</p>\n <p><strong>Workspace:</strong> ${this.session.workspacePath}</p>\n <p><strong>Step ID:</strong> ${this.session.stepId}</p>\n ${statusLine}\n ${errorLine}\n </div>\n\n <h2>Operations (${this.session.operations.length})</h2>\n ${operationsHtml}\n\n ${screenshotsHtml}\n</body>\n</html>\n `.trim();\n }\n\n async saveToFile(outputPath: string, options: ReportOptions): Promise<string> {\n const content = this.generate(options);\n await fs.writeFile(outputPath, content, 'utf-8');\n return outputPath;\n }\n}\n\n"],"mappings":"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","names":["Command","Command","path","fs","path","fs","path","fs","Command","path","Command","path","fs","path","Command","path","Command","path","fs","path","fs","path","Command","path","Command","path","fs","Command","path","fs","Command","os","fs","path","path","fs","Command","os","Command","fs","path","fs","Command","path","fs","require","Command"]}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-handoff",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "轻量级多 Agent 协作接力工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "helinjiang",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/helinjiang/agent-handoff.git"
|
|
10
|
+
"url": "git+https://github.com/helinjiang/agent-handoff.git"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
13
|
"agent",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"collaboration"
|
|
18
18
|
],
|
|
19
19
|
"bin": {
|
|
20
|
-
"agent-handoff": "
|
|
20
|
+
"agent-handoff": "dist/index.js"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"dist",
|
|
@@ -33,13 +33,17 @@
|
|
|
33
33
|
"typecheck": "tsc --noEmit"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"clipboardy": "^5.3.1",
|
|
36
37
|
"commander": "^12.0.0",
|
|
37
38
|
"yaml": "^2.4.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
41
|
+
"@eslint/js": "^9.30.0",
|
|
40
42
|
"@types/node": "^20.0.0",
|
|
43
|
+
"eslint": "^9.30.0",
|
|
41
44
|
"tsup": "^8.0.0",
|
|
42
45
|
"typescript": "^5.3.0",
|
|
46
|
+
"typescript-eslint": "^8.35.0",
|
|
43
47
|
"vitest": "^1.0.0"
|
|
44
48
|
},
|
|
45
49
|
"engines": {
|