agent-guardrails 0.3.0 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -6
- package/lib/check/detectors/oss.js +59 -0
- package/lib/cli.js +48 -6
- package/lib/commands/agents-md.js +281 -0
- package/lib/commands/check.js +5 -0
- package/lib/commands/daemon.js +323 -0
- package/lib/commands/plan.js +221 -0
- package/lib/daemon/worker.js +266 -0
- package/lib/i18n.js +184 -6
- package/lib/mcp/server.js +128 -0
- package/lib/rough-intent/index.js +43 -0
- package/lib/rough-intent/modes.js +304 -0
- package/lib/rough-intent/parser.js +473 -0
- package/lib/runtime/service.js +94 -0
- package/lib/utils.js +13 -0
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -1,13 +1,165 @@
|
|
|
1
1
|
# Agent Guardrails
|
|
2
2
|
|
|
3
|
-
**3
|
|
3
|
+
**3 秒判断:这次 AI 改动可以安全 merge 吗?**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
|
|
5
|
+
[中文简介](#中文简介) | [Quick Start](#快速开始--先看这里) | [Docs](#文档--documentation)
|
|
6
|
+
|
|
7
|
+
`agent-guardrails` 是 **AI 代码合并门** - 在 merge 前检查 AI 改动是否符合预期。
|
|
8
|
+
|
|
9
|
+
- 🎯 **范围验证** - AI 只改了允许的文件
|
|
10
|
+
- ✅ **测试验证** - 测试必须运行
|
|
11
|
+
- 🔍 **漂移检测** - 检测并行抽象、接口变更
|
|
12
|
+
- 🛡 **保护路径** - 关键文件不被触碰
|
|
13
|
+
|
|
14
|
+
## How it works
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Before vs After
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Rough-Intent Mode
|
|
27
|
+
|
|
28
|
+
Don't have a precise task? Just say what you want in natural language:
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# No detailed flags needed - just describe your task
|
|
34
|
+
agent-guardrails plan "加个登录功能" --lang zh-CN --yes
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 快速开始 / 先看这里
|
|
40
|
+
|
|
41
|
+
**30 秒快速体验**(推荐)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 1. 安装
|
|
45
|
+
npm install -g agent-guardrails
|
|
46
|
+
|
|
47
|
+
# 2. 在项目中设置
|
|
48
|
+
cd your-repo
|
|
49
|
+
agent-guardrails setup --agent claude-code
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
你可以用其他 Agent:
|
|
53
|
+
- `claude-code` - Claude Code CLI
|
|
54
|
+
- `cursor` - Cursor Editor
|
|
55
|
+
- `codex` - OpenAI Codex CLI
|
|
56
|
+
- `openhands` - OpenHands CLI
|
|
57
|
+
- `openclaw` - OpenClaw CLI
|
|
58
|
+
|
|
59
|
+
**注意**: `setup` 会在项目根目录生成配置文件,输出使用指南。
|
|
60
|
+
|
|
61
|
+
请复制输出的配置片段到 粘贴到你的 AI 工具中。
|
|
62
|
+
|
|
63
|
+
**3. 开始使用**
|
|
64
|
+
让你的 AI 按照指南操作代码
|
|
65
|
+
- 输入 `/init` 开始新任务
|
|
66
|
+
- 输入 `/plan` 创建任务计划
|
|
67
|
+
- 输入 `/check` 在完成后检查
|
|
68
|
+
|
|
69
|
+
- 使用 `/review` 获取合并建议
|
|
70
|
+
|
|
71
|
+
**4. 在 merge 前运行检查**
|
|
72
|
+
```bash
|
|
73
|
+
agent-guardrails check --review
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 核心价值 / Core Value
|
|
79
|
+
|
|
80
|
+
### 与 传统 AI 编码 | 使用 agent-guardrails |
|
|
81
|
+
|---------------|---------------------------|
|
|
82
|
+
| "AI 改了 47 个文件,不知道为什么" | "AI 改了 3 个文件,都在范围内" |
|
|
83
|
+
| "应该测试过了?" | "测试运行完成,12 通过,0 失败" |
|
|
84
|
+
| "这看起来像是个新模式" | "⚠️ 检测到并行抽象" |
|
|
85
|
+
| "希望不会出问题" | "✓ 可以安全 merge,剩余风险:低" |
|
|
86
|
+
| 5 files, clear scope, clear validation | 12 passed, 0 failed | 0 files, clear scope, clear validation | safe to merge, remaining risk: low |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 适用场景 / When to Use
|
|
91
|
+
|
|
92
|
+
| 场景 | 推荐度 |
|
|
93
|
+
|------|--------|
|
|
94
|
+
| 在真实仓库中使用 AI Agent 的开发者 | ⭐⭐⭐⭐⭐ |
|
|
95
|
+
| 被越界改动、漏测试坑过的团队 | ⭐⭐⭐⭐⭐ |
|
|
96
|
+
| 希望在 merge 前看到清晰验证结果的人 | ⭐⭐⭐⭐ |
|
|
97
|
+
|
|
98
|
+
**不适用场景**:
|
|
99
|
+
- 只想做一次性 prototype 的用户
|
|
100
|
+
- 不关心代码质量和维护性的团队
|
|
101
|
+
- 想找通用静态分析工具的用户
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 与竞品对比 / vs. Competitors
|
|
106
|
+
|
|
107
|
+
| 功能 | CodeRabbit | Sonar | agent-guardrails |
|
|
108
|
+
|------|-----------|-------|------------------|
|
|
109
|
+
| 事前约束 | ❌ 事后评论 | ❌ 事后检查 | ✅ |
|
|
110
|
+
| 范围控制 | ❌ | ❌ | ✅ |
|
|
111
|
+
| 任务上下文 | ❌ | ❌ | ✅ |
|
|
112
|
+
| 测试相关性检查 | ❌ | ❌ | ✅ |
|
|
113
|
+
|
|
114
|
+
**我们的优势**: 在代码生成**之前**定义边界,而不是生成**之后**发现问题
|
|
115
|
+
- 主动而非被动
|
|
116
|
+
- 与 AI Agent 巷度集成
|
|
117
|
+
- 支持多种编程语言
|
|
118
|
+
|
|
119
|
+
- 完全开源免费
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 安装 / Installation
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm install -g agent-guardrails
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
在项目目录运行:
|
|
130
|
+
```bash
|
|
131
|
+
npx agent-guardrails setup --agent <your-agent>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
支持的 Agent:
|
|
135
|
+
- `claude-code` (推荐)
|
|
136
|
+
- `cursor`
|
|
137
|
+
- `codex`
|
|
138
|
+
- `openhands`
|
|
139
|
+
- `openclaw`
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 文档 / Documentation
|
|
144
|
+
|
|
145
|
+
- [README.md](./README.md) - 你文档
|
|
146
|
+
- [FAILURE Cases](./docs/FAILURE_Cases.md) - 真实失败案例
|
|
147
|
+
- [Rough-Intent Mode](./docs/Rrough_intent.md) - 模糊请求处理
|
|
148
|
+
- [OSS/Pro 边界](./docs/oss_pro_boundary.md) - 功能对比
|
|
149
|
+
|
|
150
|
+
- [Roadmap](./docs/roadmap.md) - 发展规划
|
|
151
|
+
|
|
152
|
+
- [Proof](./docs/proof.md) - 效果证明
|
|
153
|
+
|
|
154
|
+
- [Pilots](./docs/pilots/README.md) - 试点记录
|
|
155
|
+
|
|
156
|
+
- [Python FastAPI Demo](./examples/python-fastapi-demo/README.md) - Python 示例
|
|
157
|
+
- [TypeScript Demo](./examples/pattern-drift-demo/README.md) - TypeScript 示例
|
|
158
|
+
- [Bounded Scope Demo](./examples/bounded-scope-demo/README.md) - 范围控制示例
|
|
159
|
+
- [Boundary Demo](./examples/boundary-violation-demo/README.md) - 边界检查示例
|
|
160
|
+
- [Interface Drift Demo](./examples/interface-drift-demo/README.md) - 接口变更示例
|
|
161
|
+
- [Source-Test Relevance Demo](./examples/source-test-relevance-demo/README.md) - 测试相关性示例
|
|
9
162
|
|
|
10
|
-
For real repos, not one-off prototypes.
|
|
11
163
|
|
|
12
164
|
---
|
|
13
165
|
|
|
@@ -371,6 +523,58 @@ The first recommended MCP flow is:
|
|
|
371
523
|
|
|
372
524
|
`suggest_task_contract` and `run_guardrail_check` still exist as lower-level MCP tools, but they are not the preferred first-run chat flow.
|
|
373
525
|
|
|
526
|
+
## Daemon Mode / 守护进程模式
|
|
527
|
+
|
|
528
|
+
Run guardrails automatically in the background while you code:
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
# Start the daemon (background mode)
|
|
532
|
+
agent-guardrails start
|
|
533
|
+
|
|
534
|
+
# Check daemon status
|
|
535
|
+
agent-guardrails status
|
|
536
|
+
|
|
537
|
+
# Stop the daemon
|
|
538
|
+
agent-guardrails stop
|
|
539
|
+
|
|
540
|
+
# Run in foreground (useful for debugging or Docker)
|
|
541
|
+
agent-guardrails start --foreground
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### How It Works
|
|
545
|
+
|
|
546
|
+
The daemon monitors file changes and automatically runs guardrail checks:
|
|
547
|
+
- Watches `src/`, `lib/`, `tests/` by default
|
|
548
|
+
- Debounces checks (5 second interval)
|
|
549
|
+
- Logs to `.agent-guardrails/daemon.log`
|
|
550
|
+
|
|
551
|
+
### Configuration (`.agent-guardrails/daemon.json`)
|
|
552
|
+
|
|
553
|
+
| Option | Default | Description |
|
|
554
|
+
|--------|---------|-------------|
|
|
555
|
+
| `watchPaths` | `["src/", "lib/", "tests/"]` | Paths to monitor |
|
|
556
|
+
| `ignorePatterns` | `["node_modules", ".git", ...]` | Patterns to ignore |
|
|
557
|
+
| `checkInterval` | `5000` | Debounce interval (ms) |
|
|
558
|
+
| `blockOnHighRisk` | `true` | Block on high-risk findings |
|
|
559
|
+
| `autoFix` | `false` | Auto-fix issues when possible |
|
|
560
|
+
|
|
561
|
+
### Use Cases
|
|
562
|
+
|
|
563
|
+
- **Local development**: Get instant feedback while coding
|
|
564
|
+
- **CI/CD integration**: Run in containers with `--foreground`
|
|
565
|
+
- **Team guardrails**: Shared daemon config in repo
|
|
566
|
+
|
|
567
|
+
### Daemon vs Manual Check
|
|
568
|
+
|
|
569
|
+
| Daemon Mode | Manual Check |
|
|
570
|
+
|-------------|--------------|
|
|
571
|
+
| Continuous monitoring | One-time check |
|
|
572
|
+
| Automatic on file change | Run when you want |
|
|
573
|
+
| Background process | Foreground process |
|
|
574
|
+
| Best for active development | Best for pre-commit/CI |
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
374
578
|
## CLI Fallback Quick Start
|
|
375
579
|
|
|
376
580
|
If you want the shortest manual path, copy this:
|
|
@@ -639,3 +843,13 @@ See [CHANGELOG.md](./CHANGELOG.md).
|
|
|
639
843
|
## License
|
|
640
844
|
|
|
641
845
|
MIT
|
|
846
|
+
|
|
847
|
+
---
|
|
848
|
+
|
|
849
|
+
## Contributing / 贡献
|
|
850
|
+
|
|
851
|
+
- 🐛 **Bug Reports**: [Open an Issue](https://github.com/logi-cmd/agent-guardrails/issues/new)
|
|
852
|
+
- 💡 **Feature Requests**: [Start a Discussion](https://github.com/logi-cmd/agent-guardrails/discussions)
|
|
853
|
+
- ❓ **Questions**: [Ask in Discussions](https://github.com/logi-cmd/agent-guardrails/discussions)
|
|
854
|
+
|
|
855
|
+
If you find this project helpful, please consider giving it a ⭐️!
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createFinding } from "../finding.js";
|
|
2
2
|
import { normalizeChangeType } from "../../utils.js";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
3
4
|
|
|
4
5
|
function toBoolean(value) {
|
|
5
6
|
if (value === true || value === false) {
|
|
@@ -461,5 +462,63 @@ export const ossDetectors = [
|
|
|
461
462
|
}));
|
|
462
463
|
}
|
|
463
464
|
}
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: "secrets-safety",
|
|
468
|
+
run({ context, addFinding, t }) {
|
|
469
|
+
const { repoRoot, changedFiles } = context;
|
|
470
|
+
if (!changedFiles || changedFiles.length === 0) return;
|
|
471
|
+
|
|
472
|
+
const patterns = [
|
|
473
|
+
{ regex: /(?:api[_-]?key|apikey)\s*[=:]\s*["'][^"']{8,}["']/i, label: "API key" },
|
|
474
|
+
{ regex: /(?:password|passwd|pwd)\s*[=:]\s*["'][^"']{6,}["']/i, label: "Password" },
|
|
475
|
+
{ regex: /Bearer\s+[A-Za-z0-9\-._~+/]+=*/i, label: "Bearer token" },
|
|
476
|
+
{ regex: /-----BEGIN(?:\s+(?:RSA|EC|DSA|OPENSSH|PRIVATE))?[\s-]*PRIVATE KEY-----/, label: "Private key" },
|
|
477
|
+
{ regex: /(?:secret|token)\s*[=:]\s*["'][A-Za-z0-9\-._~+/]{16,}["']/i, label: "Secret/token" }
|
|
478
|
+
];
|
|
479
|
+
|
|
480
|
+
const testLike = /\.(test|spec)\.(js|ts|mjs|cjs|jsx|tsx)$|__tests__|fixtures|mock/i;
|
|
481
|
+
let matched = false;
|
|
482
|
+
|
|
483
|
+
for (const filePath of changedFiles) {
|
|
484
|
+
if (testLike.test(filePath)) continue;
|
|
485
|
+
let content;
|
|
486
|
+
try {
|
|
487
|
+
content = execFileSync(
|
|
488
|
+
"git",
|
|
489
|
+
["diff", "--cached", "--", filePath],
|
|
490
|
+
{ cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
491
|
+
);
|
|
492
|
+
if (!content.trim()) {
|
|
493
|
+
content = execFileSync(
|
|
494
|
+
"git",
|
|
495
|
+
["diff", "--", filePath],
|
|
496
|
+
{ cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
} catch {
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const addedLines = content.split("\n").filter((line) => /^\+/.test(line) && !/^\+\+\+/.test(line));
|
|
503
|
+
for (const { regex, label } of patterns) {
|
|
504
|
+
for (const line of addedLines) {
|
|
505
|
+
if (regex.test(line)) {
|
|
506
|
+
matched = true;
|
|
507
|
+
addFinding(createFinding({
|
|
508
|
+
severity: "warning",
|
|
509
|
+
category: "risk",
|
|
510
|
+
code: "secrets-safety",
|
|
511
|
+
message: t("findings.secretsSafetyDetected", { file: filePath, type: label }),
|
|
512
|
+
action: t("actions.moveSecretToEnv"),
|
|
513
|
+
files: [filePath]
|
|
514
|
+
}));
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (matched) break;
|
|
519
|
+
}
|
|
520
|
+
if (matched) break;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
464
523
|
}
|
|
465
524
|
];
|
package/lib/cli.js
CHANGED
|
@@ -4,7 +4,9 @@ import { runMcp } from "./commands/mcp.js";
|
|
|
4
4
|
import { runSetup } from "./commands/setup.js";
|
|
5
5
|
import { createTranslator, supportedLocales } from "./i18n.js";
|
|
6
6
|
import { runPlan } from "./commands/plan.js";
|
|
7
|
-
import {
|
|
7
|
+
import { runGenerateAgentsMd } from "./commands/agents-md.js";
|
|
8
|
+
import { startDaemon, stopDaemon, showDaemonStatus } from "./commands/daemon.js";
|
|
9
|
+
import { supportedAdapters, supportedPresets, readOwnPackageJson } from "./utils.js";
|
|
8
10
|
|
|
9
11
|
function parseArgs(argv) {
|
|
10
12
|
const positional = [];
|
|
@@ -42,14 +44,28 @@ ${t("cli.usage")}
|
|
|
42
44
|
agent-guardrails setup [targetDir] --agent <name> [--preset <name>] [--lang <locale>] [--json] [--write-repo-config]
|
|
43
45
|
agent-guardrails plan --task "<task description>" [--intended-files "src/service.js,tests/service.test.js"] [--allowed-change-types "implementation-only"] [--allow-paths "src/,tests/"] [--required-commands "npm test"] [--evidence ".agent-guardrails/evidence/current-task.md"] [--risk-level high] [--lang <locale>] [--print-only]
|
|
44
46
|
agent-guardrails check [--contract-path <path>] [--base-ref <ref>] [--commands-run "npm test"] [--review] [--lang <locale>] [--json]
|
|
47
|
+
agent-guardrails start [--foreground] - ${t("cli.startSummary")}
|
|
48
|
+
agent-guardrails stop - ${t("cli.stopSummary")}
|
|
49
|
+
agent-guardrails status - ${t("cli.statusSummary")}
|
|
50
|
+
agent-guardrails generate-agents [targetDir] [--preset <name>] [--lang <locale>]
|
|
45
51
|
agent-guardrails mcp
|
|
52
|
+
agent-guardrails --version
|
|
53
|
+
|
|
54
|
+
${t("cli.globalOptions")}
|
|
55
|
+
--version, -v ${t("cli.versionSummary") ?? "Show version number"}
|
|
56
|
+
--help, -h ${t("cli.helpSummary") ?? "Show this help"}
|
|
57
|
+
--lang <locale> ${t("cli.langSummary") ?? "Language for output (en, zh-CN)"}
|
|
46
58
|
|
|
47
59
|
${t("cli.commands")}
|
|
48
|
-
init
|
|
49
|
-
setup
|
|
50
|
-
plan
|
|
51
|
-
check
|
|
52
|
-
|
|
60
|
+
init ${t("cli.initSummary")}
|
|
61
|
+
setup ${t("cli.setupSummary")}
|
|
62
|
+
plan ${t("cli.planSummary")}
|
|
63
|
+
check ${t("cli.checkSummary")}
|
|
64
|
+
start ${t("cli.startSummary")}
|
|
65
|
+
stop ${t("cli.stopSummary")}
|
|
66
|
+
status ${t("cli.statusSummary")}
|
|
67
|
+
generate-agents Generate AGENTS.md for Harness Engineering
|
|
68
|
+
mcp ${t("cli.mcpSummary")}
|
|
53
69
|
|
|
54
70
|
${t("cli.supportedPresets")}
|
|
55
71
|
${supportedPresets.join(", ")}
|
|
@@ -67,6 +83,12 @@ export async function runCli(argv) {
|
|
|
67
83
|
const [command, ...rest] = positional;
|
|
68
84
|
const locale = flags.lang ?? null;
|
|
69
85
|
|
|
86
|
+
if (flags.version || flags.v) {
|
|
87
|
+
const pkg = readOwnPackageJson();
|
|
88
|
+
console.log(`agent-guardrails v${pkg.version}`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
if (!command || command === "help" || flags.help) {
|
|
71
93
|
printHelp(locale);
|
|
72
94
|
return;
|
|
@@ -97,6 +119,26 @@ export async function runCli(argv) {
|
|
|
97
119
|
return;
|
|
98
120
|
}
|
|
99
121
|
|
|
122
|
+
if (command === "start") {
|
|
123
|
+
await startDaemon(process.cwd(), { locale, foreground: flags.foreground || false });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (command === "stop") {
|
|
128
|
+
stopDaemon(process.cwd(), { locale });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (command === "status") {
|
|
133
|
+
showDaemonStatus(process.cwd(), { locale });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (command === "generate-agents" || command === "gen-agents") {
|
|
138
|
+
await runGenerateAgentsMd({ positional: rest, flags, locale });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
100
142
|
const { t } = createTranslator(locale);
|
|
101
143
|
throw new Error(t("cli.unknownCommand", { command }));
|
|
102
144
|
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AGENTS.md Generator
|
|
3
|
+
*
|
|
4
|
+
* 基于 Harness Engineering 范式,生成符合 OpenAI 推荐的渐进式文档结构
|
|
5
|
+
*
|
|
6
|
+
* OpenAI 团队的 AGENTS.md 结构:
|
|
7
|
+
* - 精简目录(~100行)
|
|
8
|
+
* - 渐进式披露
|
|
9
|
+
* - 指向 docs/ 目录
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { createTranslator } from "../i18n.js";
|
|
15
|
+
import { ensureDirectory, readConfig } from "../utils.js";
|
|
16
|
+
|
|
17
|
+
// AGENTS.md 模板
|
|
18
|
+
const AGENTS_MD_TEMPLATE = `# AGENTS.md
|
|
19
|
+
|
|
20
|
+
> 此文件由 agent-guardrails 自动生成
|
|
21
|
+
> 基于 Harness Engineering 范式,帮助 AI Agent 更好地理解项目
|
|
22
|
+
|
|
23
|
+
## 项目概述
|
|
24
|
+
|
|
25
|
+
<!-- 简短描述项目是做什么的 -->
|
|
26
|
+
|
|
27
|
+
## 快速开始
|
|
28
|
+
|
|
29
|
+
\`\`\`bash
|
|
30
|
+
# 安装依赖
|
|
31
|
+
npm install
|
|
32
|
+
|
|
33
|
+
# 运行测试
|
|
34
|
+
npm test
|
|
35
|
+
|
|
36
|
+
# 启动开发服务器
|
|
37
|
+
npm run dev
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
## 架构约束
|
|
41
|
+
|
|
42
|
+
### 分层架构
|
|
43
|
+
|
|
44
|
+
\`\`\`
|
|
45
|
+
┌─────────────────────────────────────────┐
|
|
46
|
+
│ 表现层 (UI) │
|
|
47
|
+
├─────────────────────────────────────────┤
|
|
48
|
+
│ 应用层 (Services) │
|
|
49
|
+
├─────────────────────────────────────────┤
|
|
50
|
+
│ 领域层 (Domain) │
|
|
51
|
+
├─────────────────────────────────────────┤
|
|
52
|
+
│ 基础设施层 (Infra) │
|
|
53
|
+
└─────────────────────────────────────────┘
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
### 依赖规则
|
|
57
|
+
|
|
58
|
+
- 上层可以依赖下层
|
|
59
|
+
- 下层不能依赖上层
|
|
60
|
+
- 同层之间尽量独立
|
|
61
|
+
|
|
62
|
+
## 代码风格
|
|
63
|
+
|
|
64
|
+
### 命名约定
|
|
65
|
+
|
|
66
|
+
- 文件名: kebab-case (例: user-service.ts)
|
|
67
|
+
- 类名: PascalCase (例: UserService)
|
|
68
|
+
- 函数名: camelCase (例: getUserById)
|
|
69
|
+
- 常量: UPPER_SNAKE_CASE (例: MAX_RETRY_COUNT)
|
|
70
|
+
|
|
71
|
+
### 目录结构
|
|
72
|
+
|
|
73
|
+
\`\`\`
|
|
74
|
+
src/
|
|
75
|
+
├── modules/ # 功能模块
|
|
76
|
+
│ ├── user/
|
|
77
|
+
│ │ ├── user.service.ts
|
|
78
|
+
│ │ ├── user.controller.ts
|
|
79
|
+
│ │ └── user.types.ts
|
|
80
|
+
│ └── ...
|
|
81
|
+
├── shared/ # 共享代码
|
|
82
|
+
│ ├── utils/
|
|
83
|
+
│ └── types/
|
|
84
|
+
└── config/ # 配置文件
|
|
85
|
+
\`\`\`
|
|
86
|
+
|
|
87
|
+
## 禁止事项
|
|
88
|
+
|
|
89
|
+
❌ 不要直接修改数据库迁移文件
|
|
90
|
+
❌ 不要跳过测试提交代码
|
|
91
|
+
❌ 不要在 src 外创建新的源代码目录
|
|
92
|
+
❌ 不要使用 any 类型(除非有充分理由)
|
|
93
|
+
|
|
94
|
+
## 详细文档
|
|
95
|
+
|
|
96
|
+
更多信息请查看 docs/ 目录:
|
|
97
|
+
|
|
98
|
+
- [架构设计](./docs/ARCHITECTURE.md)
|
|
99
|
+
- [API 文档](./docs/API.md)
|
|
100
|
+
- [测试指南](./docs/TESTING.md)
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
*此文件由 agent-guardrails 生成 | Harness Engineering Ready*
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
// docs/ARCHITECTURE.md 模板
|
|
107
|
+
const ARCHITECTURE_MD_TEMPLATE = `# 架构设计
|
|
108
|
+
|
|
109
|
+
## 系统架构
|
|
110
|
+
|
|
111
|
+
<!-- 描述系统的整体架构 -->
|
|
112
|
+
|
|
113
|
+
## 模块划分
|
|
114
|
+
|
|
115
|
+
<!-- 描述主要模块及其职责 -->
|
|
116
|
+
|
|
117
|
+
## 数据流
|
|
118
|
+
|
|
119
|
+
<!-- 描述数据如何在系统中流动 -->
|
|
120
|
+
|
|
121
|
+
## 关键决策
|
|
122
|
+
|
|
123
|
+
<!-- 记录重要的架构决策及其原因 -->
|
|
124
|
+
|
|
125
|
+
## 待办事项
|
|
126
|
+
|
|
127
|
+
<!-- 需要改进的地方 -->
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
// docs/TESTING.md 模板
|
|
131
|
+
const TESTING_MD_TEMPLATE = `# 测试指南
|
|
132
|
+
|
|
133
|
+
## 测试策略
|
|
134
|
+
|
|
135
|
+
### 单元测试
|
|
136
|
+
|
|
137
|
+
- 每个模块都应该有对应的单元测试
|
|
138
|
+
- 测试文件放在 \\\`__tests__\\\` 目录或与源文件同级
|
|
139
|
+
- 使用 jest 作为测试框架
|
|
140
|
+
|
|
141
|
+
\`\`\`bash
|
|
142
|
+
# 运行单元测试
|
|
143
|
+
npm test
|
|
144
|
+
|
|
145
|
+
# 运行特定测试
|
|
146
|
+
npm test -- user.service.test.ts
|
|
147
|
+
\`\`\`
|
|
148
|
+
|
|
149
|
+
### 集成测试
|
|
150
|
+
|
|
151
|
+
- 测试模块之间的交互
|
|
152
|
+
- 使用测试数据库
|
|
153
|
+
|
|
154
|
+
### E2E 测试
|
|
155
|
+
|
|
156
|
+
- 测试完整的用户流程
|
|
157
|
+
- 使用 Cypress 或 Playwright
|
|
158
|
+
|
|
159
|
+
## 测试覆盖率
|
|
160
|
+
|
|
161
|
+
目标:80% 以上的代码覆盖率
|
|
162
|
+
|
|
163
|
+
\`\`\`bash
|
|
164
|
+
# 查看覆盖率报告
|
|
165
|
+
npm run test:coverage
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
## Mock 规则
|
|
169
|
+
|
|
170
|
+
- 外部服务必须 mock
|
|
171
|
+
- 数据库操作使用测试数据库
|
|
172
|
+
- 时间相关的测试使用固定时间
|
|
173
|
+
`;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 生成 AGENTS.md 文件
|
|
177
|
+
*/
|
|
178
|
+
export function generateAgentsMd(options) {
|
|
179
|
+
const { repoRoot, preset, locale } = options;
|
|
180
|
+
const t = createTranslator(locale);
|
|
181
|
+
|
|
182
|
+
const agentsMdPath = path.join(repoRoot, "AGENTS.md");
|
|
183
|
+
const docsDir = path.join(repoRoot, "docs");
|
|
184
|
+
|
|
185
|
+
// 检查是否已存在
|
|
186
|
+
if (fs.existsSync(agentsMdPath)) {
|
|
187
|
+
return {
|
|
188
|
+
ok: false,
|
|
189
|
+
reason: "AGENTS.md already exists",
|
|
190
|
+
path: agentsMdPath
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 创建 docs 目录
|
|
195
|
+
ensureDirectory(docsDir);
|
|
196
|
+
|
|
197
|
+
// 生成 AGENTS.md
|
|
198
|
+
fs.writeFileSync(agentsMdPath, AGENTS_MD_TEMPLATE, "utf8");
|
|
199
|
+
|
|
200
|
+
// 生成 docs/ARCHITECTURE.md
|
|
201
|
+
const architecturePath = path.join(docsDir, "ARCHITECTURE.md");
|
|
202
|
+
if (!fs.existsSync(architecturePath)) {
|
|
203
|
+
fs.writeFileSync(architecturePath, ARCHITECTURE_MD_TEMPLATE, "utf8");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 生成 docs/TESTING.md
|
|
207
|
+
const testingPath = path.join(docsDir, "TESTING.md");
|
|
208
|
+
if (!fs.existsSync(testingPath)) {
|
|
209
|
+
fs.writeFileSync(testingPath, TESTING_MD_TEMPLATE, "utf8");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
ok: true,
|
|
214
|
+
files: [
|
|
215
|
+
agentsMdPath,
|
|
216
|
+
architecturePath,
|
|
217
|
+
testingPath
|
|
218
|
+
],
|
|
219
|
+
message: "AGENTS.md generated successfully"
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 更新 AGENTS.md
|
|
225
|
+
*/
|
|
226
|
+
export function updateAgentsMd(options) {
|
|
227
|
+
const { repoRoot, section, content, locale } = options;
|
|
228
|
+
|
|
229
|
+
const agentsMdPath = path.join(repoRoot, "AGENTS.md");
|
|
230
|
+
|
|
231
|
+
if (!fs.existsSync(agentsMdPath)) {
|
|
232
|
+
return {
|
|
233
|
+
ok: false,
|
|
234
|
+
reason: "AGENTS.md not found. Run 'agent-guardrails generate-agents' first."
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const currentContent = fs.readFileSync(agentsMdPath, "utf8");
|
|
239
|
+
|
|
240
|
+
// 根据 section 更新对应部分
|
|
241
|
+
// 这里可以扩展更多 section 的更新逻辑
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
ok: true,
|
|
245
|
+
message: "AGENTS.md updated"
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* CLI 命令入口
|
|
251
|
+
*/
|
|
252
|
+
export async function runGenerateAgentsMd({ positional, flags, locale = null }) {
|
|
253
|
+
const repoRoot = positional[0] ? path.resolve(positional[0]) : process.cwd();
|
|
254
|
+
const preset = flags.preset || "node-service";
|
|
255
|
+
|
|
256
|
+
console.log("🔧 Generating AGENTS.md...\n");
|
|
257
|
+
|
|
258
|
+
const result = generateAgentsMd({
|
|
259
|
+
repoRoot,
|
|
260
|
+
preset,
|
|
261
|
+
locale: flags.lang || locale
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
if (result.ok) {
|
|
265
|
+
console.log("✅ AGENTS.md generated successfully!\n");
|
|
266
|
+
console.log("Files created:");
|
|
267
|
+
result.files.forEach(file => {
|
|
268
|
+
console.log(` - ${path.relative(repoRoot, file)}`);
|
|
269
|
+
});
|
|
270
|
+
console.log("\n📖 What's next:");
|
|
271
|
+
console.log(" 1. Review and customize AGENTS.md for your project");
|
|
272
|
+
console.log(" 2. Update docs/ARCHITECTURE.md with your architecture");
|
|
273
|
+
console.log(" 3. Update docs/TESTING.md with your testing strategy");
|
|
274
|
+
console.log(" 4. Point your AI agent to AGENTS.md");
|
|
275
|
+
} else {
|
|
276
|
+
console.log(`⚠️ ${result.reason}`);
|
|
277
|
+
console.log(` Path: ${result.path}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return result;
|
|
281
|
+
}
|
package/lib/commands/check.js
CHANGED
|
@@ -475,6 +475,11 @@ function printTextResult(result, t, { reviewMode = false } = {}) {
|
|
|
475
475
|
if (result.failures.length === 0) {
|
|
476
476
|
console.log(`\n${t("check.allPassed")}`);
|
|
477
477
|
}
|
|
478
|
+
|
|
479
|
+
if (result.runtime?.costHints?.entries?.length > 0) {
|
|
480
|
+
console.log(`\n${t("check.costAwareness")}`);
|
|
481
|
+
console.log(result.runtime.costHints.entries.map((entry) => `- ${t(entry.key, entry.vars)}`).join("\n"));
|
|
482
|
+
}
|
|
478
483
|
}
|
|
479
484
|
|
|
480
485
|
function printJsonResult(result) {
|