agent-guardrails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +335 -0
- package/adapters/README.md +18 -0
- package/adapters/claude-code/README.md +38 -0
- package/adapters/codex/README.md +39 -0
- package/adapters/cursor/README.md +38 -0
- package/adapters/openclaw/OPENCLAW.md +35 -0
- package/adapters/openclaw/README.md +41 -0
- package/adapters/openhands/README.md +38 -0
- package/bin/agent-guardrails.js +9 -0
- package/lib/benchmark/runner.js +272 -0
- package/lib/check/detectors/oss.js +374 -0
- package/lib/check/finding.js +30 -0
- package/lib/check/pipeline.js +25 -0
- package/lib/check/plugins.js +124 -0
- package/lib/check/policy.js +40 -0
- package/lib/check/review.js +39 -0
- package/lib/cli.js +86 -0
- package/lib/commands/check.js +506 -0
- package/lib/commands/init.js +140 -0
- package/lib/commands/plan.js +232 -0
- package/lib/i18n.js +362 -0
- package/lib/utils.js +359 -0
- package/package.json +55 -0
- package/templates/adapters/claude-code/CLAUDE.md +24 -0
- package/templates/adapters/cursor/agent-guardrails.mdc +13 -0
- package/templates/adapters/openclaw/OPENCLAW.md +34 -0
- package/templates/adapters/openhands/agent-guardrails.md +8 -0
- package/templates/base/AGENTS.md +31 -0
- package/templates/base/IMPLEMENT_PROMPT.md +21 -0
- package/templates/base/PROJECT_STATE.md +23 -0
- package/templates/base/PR_CHECKLIST.md +11 -0
- package/templates/base/TASK_TEMPLATE.md +31 -0
- package/templates/base/workflows/agent-guardrails.yml +33 -0
- package/templates/locales/zh-CN/adapters/claude-code/CLAUDE.md +24 -0
- package/templates/locales/zh-CN/adapters/cursor/agent-guardrails.mdc +13 -0
- package/templates/locales/zh-CN/adapters/openclaw/OPENCLAW.md +34 -0
- package/templates/locales/zh-CN/adapters/openhands/agent-guardrails.md +8 -0
- package/templates/locales/zh-CN/base/AGENTS.md +31 -0
- package/templates/locales/zh-CN/base/IMPLEMENT_PROMPT.md +21 -0
- package/templates/locales/zh-CN/base/PROJECT_STATE.md +23 -0
- package/templates/locales/zh-CN/base/PR_CHECKLIST.md +11 -0
- package/templates/locales/zh-CN/base/TASK_TEMPLATE.md +31 -0
- package/templates/presets/monorepo/config.json +96 -0
- package/templates/presets/nextjs/config.json +97 -0
- package/templates/presets/node-service/config.json +105 -0
- package/templates/presets/python-fastapi/config.json +96 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { createTranslator } from "../i18n.js";
|
|
3
|
+
import {
|
|
4
|
+
defaultTaskContractPath,
|
|
5
|
+
formatList,
|
|
6
|
+
parseCommaSeparatedList,
|
|
7
|
+
parseStringList,
|
|
8
|
+
readConfig,
|
|
9
|
+
normalizeChangeType,
|
|
10
|
+
writeTaskContract
|
|
11
|
+
} from "../utils.js";
|
|
12
|
+
|
|
13
|
+
function parseBooleanFlag(value) {
|
|
14
|
+
if (value === true) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (value === false || value == null) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const normalized = String(value).trim().toLowerCase();
|
|
23
|
+
return ["1", "true", "yes", "on"].includes(normalized);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderOrFallback(items, renderer, fallback) {
|
|
27
|
+
return items.length > 0 ? formatList(items.map(renderer)) : fallback;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function runPlan({ positional, flags, locale = null }) {
|
|
31
|
+
const repoRoot = process.cwd();
|
|
32
|
+
const config = readConfig(repoRoot);
|
|
33
|
+
const task = flags.task || positional.join(" ");
|
|
34
|
+
const { t } = createTranslator(flags.lang || locale);
|
|
35
|
+
|
|
36
|
+
if (!task) {
|
|
37
|
+
throw new Error(t("errors.missingTask"));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!config) {
|
|
41
|
+
throw new Error(t("errors.missingInitConfig"));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const preset = config.preset;
|
|
45
|
+
const readBeforeWrite = config.workflow?.readBeforeWrite ?? [
|
|
46
|
+
"AGENTS.md",
|
|
47
|
+
"docs/PROJECT_STATE.md",
|
|
48
|
+
"README.md"
|
|
49
|
+
];
|
|
50
|
+
const constraints = config.workflow?.constraints ?? [
|
|
51
|
+
"Prefer the existing architecture and file patterns.",
|
|
52
|
+
"Keep the change small and bounded.",
|
|
53
|
+
"Add tests for behavioral changes."
|
|
54
|
+
];
|
|
55
|
+
const doneWhen = config.workflow?.definitionOfDone ?? [
|
|
56
|
+
"Code matches existing project conventions.",
|
|
57
|
+
"Tests and checks pass.",
|
|
58
|
+
"Risks and assumptions are documented."
|
|
59
|
+
];
|
|
60
|
+
const allowedPaths = parseCommaSeparatedList(flags["allow-paths"] || flags.allow);
|
|
61
|
+
const requiredCommands = parseStringList(flags["required-commands"] || flags.commands);
|
|
62
|
+
const evidencePaths = parseCommaSeparatedList(flags["evidence-paths"] || flags.evidence);
|
|
63
|
+
const intendedFiles = parseCommaSeparatedList(flags["intended-files"]);
|
|
64
|
+
const protectedPaths = parseCommaSeparatedList(flags["protected-paths"]);
|
|
65
|
+
const allowedChangeTypes = parseStringList(flags["allowed-change-types"])
|
|
66
|
+
.map((item) => normalizeChangeType(item))
|
|
67
|
+
.filter(Boolean);
|
|
68
|
+
const riskLevel = flags["risk-level"] ? String(flags["risk-level"]).trim().toLowerCase() : "";
|
|
69
|
+
const requiresReviewNotes = parseBooleanFlag(flags["requires-review-notes"]);
|
|
70
|
+
const validationProfile = flags["validation-profile"]
|
|
71
|
+
? String(flags["validation-profile"]).trim().toLowerCase()
|
|
72
|
+
: "standard";
|
|
73
|
+
const acknowledgedSkips = parseStringList(flags["acknowledged-skips"]);
|
|
74
|
+
const patternSummary = flags["pattern-summary"] ? String(flags["pattern-summary"]).trim() : "";
|
|
75
|
+
const smallestViableChange = flags["smallest-change"] || flags["smallest-viable-change"]
|
|
76
|
+
? String(flags["smallest-change"] || flags["smallest-viable-change"]).trim()
|
|
77
|
+
: "";
|
|
78
|
+
const assumptions = parseStringList(flags.assumptions);
|
|
79
|
+
const acceptanceCriteria = parseStringList(flags["acceptance-criteria"]);
|
|
80
|
+
const nonGoals = parseStringList(flags["non-goals"]);
|
|
81
|
+
const expectedBehaviorChanges = parseStringList(flags["expected-behavior-changes"]);
|
|
82
|
+
const userVisibleEffects = parseStringList(flags["user-visible-effects"]);
|
|
83
|
+
const intendedSymbols = parseStringList(flags["intended-symbols"]);
|
|
84
|
+
const expectedPublicSurfaceChanges = parseStringList(flags["expected-public-surface-changes"]);
|
|
85
|
+
const expectedBoundaryExceptions = parseStringList(flags["expected-boundary-exceptions"]);
|
|
86
|
+
const expectedTestTargets = parseStringList(flags["expected-test-targets"]);
|
|
87
|
+
const productionProfile = flags["production-profile"] ? String(flags["production-profile"]).trim().toLowerCase() : "";
|
|
88
|
+
const nfrRequirements = parseStringList(flags["nfr-requirements"]);
|
|
89
|
+
const expectedLoadSensitivePaths = parseCommaSeparatedList(flags["expected-load-sensitive-paths"]);
|
|
90
|
+
const expectedConcurrencyImpact = flags["expected-concurrency-impact"]
|
|
91
|
+
? String(flags["expected-concurrency-impact"]).trim()
|
|
92
|
+
: "";
|
|
93
|
+
const observabilityRequirements = parseStringList(flags["observability-requirements"]);
|
|
94
|
+
const rollbackNotes = flags["rollback-notes"] ? String(flags["rollback-notes"]).trim() : "";
|
|
95
|
+
const riskJustification = flags["risk-justification"] ? String(flags["risk-justification"]).trim() : "";
|
|
96
|
+
const contractPath = String(flags["contract-path"] || defaultTaskContractPath);
|
|
97
|
+
const printOnly = Boolean(flags["print-only"]);
|
|
98
|
+
let writtenContractPath = null;
|
|
99
|
+
|
|
100
|
+
const contract = {
|
|
101
|
+
schemaVersion: 3,
|
|
102
|
+
task,
|
|
103
|
+
preset,
|
|
104
|
+
createdAt: new Date().toISOString(),
|
|
105
|
+
allowedPaths,
|
|
106
|
+
requiredCommands,
|
|
107
|
+
evidencePaths,
|
|
108
|
+
intendedFiles,
|
|
109
|
+
protectedPaths,
|
|
110
|
+
allowedChangeTypes,
|
|
111
|
+
riskLevel,
|
|
112
|
+
requiresReviewNotes,
|
|
113
|
+
validationProfile,
|
|
114
|
+
acknowledgedSkips,
|
|
115
|
+
patternSummary,
|
|
116
|
+
smallestViableChange,
|
|
117
|
+
assumptions,
|
|
118
|
+
acceptanceCriteria,
|
|
119
|
+
nonGoals,
|
|
120
|
+
expectedBehaviorChanges,
|
|
121
|
+
userVisibleEffects,
|
|
122
|
+
intendedSymbols,
|
|
123
|
+
expectedPublicSurfaceChanges,
|
|
124
|
+
expectedBoundaryExceptions,
|
|
125
|
+
expectedTestTargets,
|
|
126
|
+
productionProfile,
|
|
127
|
+
nfrRequirements,
|
|
128
|
+
expectedLoadSensitivePaths,
|
|
129
|
+
expectedConcurrencyImpact,
|
|
130
|
+
observabilityRequirements,
|
|
131
|
+
rollbackNotes,
|
|
132
|
+
riskJustification
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (!printOnly) {
|
|
136
|
+
writtenContractPath = writeTaskContract(repoRoot, contract, contractPath);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log(`# ${t("plan.title")}
|
|
140
|
+
|
|
141
|
+
${t("plan.task")}:
|
|
142
|
+
${task}
|
|
143
|
+
|
|
144
|
+
${t("plan.preset")}:
|
|
145
|
+
${preset}
|
|
146
|
+
|
|
147
|
+
${t("plan.readBeforeWriting")}:
|
|
148
|
+
${formatList(readBeforeWrite.map((item) => path.normalize(item)))}
|
|
149
|
+
|
|
150
|
+
${t("plan.constraints")}:
|
|
151
|
+
${formatList(constraints)}
|
|
152
|
+
|
|
153
|
+
${t("plan.definitionOfDone")}:
|
|
154
|
+
${formatList(doneWhen)}
|
|
155
|
+
|
|
156
|
+
${t("plan.taskContract")}:
|
|
157
|
+
- ${printOnly ? t("plan.printOnly") : t("plan.writtenTo", { contractPath: path.normalize(contractPath) })}
|
|
158
|
+
${renderOrFallback(allowedPaths, (item) => t("plan.allowedPath", { value: path.normalize(item) }), `- ${t("plan.noneDeclared", { label: t("plan.allowedPath", { value: "" }).split(":")[0] })}`)}
|
|
159
|
+
${renderOrFallback(intendedFiles, (item) => t("plan.intendedFile", { value: path.normalize(item) }), `- ${t("plan.noneDeclared", { label: t("plan.intendedFile", { value: "" }).split(":")[0] })}`)}
|
|
160
|
+
${renderOrFallback(protectedPaths, (item) => t("plan.protectedPath", { value: path.normalize(item) }), `- ${t("plan.noneDeclared", { label: t("plan.protectedPath", { value: "" }).split(":")[0] })}`)}
|
|
161
|
+
${renderOrFallback(allowedChangeTypes, (item) => t("plan.allowedChangeType", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.allowedChangeType", { value: "" }).split(":")[0] })}`)}
|
|
162
|
+
- ${t("plan.riskLevel", { value: riskLevel || "standard" })}
|
|
163
|
+
- ${requiresReviewNotes ? t("plan.reviewNotesRequiredYes") : t("plan.reviewNotesRequiredNo")}
|
|
164
|
+
- ${t("plan.validationProfile", { value: validationProfile || "standard" })}
|
|
165
|
+
${renderOrFallback(requiredCommands, (item) => t("plan.requiredCommand", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.requiredCommand", { value: "" }).split(":")[0] })}`)}
|
|
166
|
+
${renderOrFallback(evidencePaths, (item) => t("plan.evidencePath", { value: path.normalize(item) }), `- ${t("plan.noneDeclared", { label: t("plan.evidencePath", { value: "" }).split(":")[0] })}`)}
|
|
167
|
+
${renderOrFallback(acknowledgedSkips, (item) => t("plan.acknowledgedSkip", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.acknowledgedSkip", { value: "" }).split(":")[0] })}`)}
|
|
168
|
+
|
|
169
|
+
${t("plan.acceptanceAlignment")}:
|
|
170
|
+
${renderOrFallback(acceptanceCriteria, (item) => t("plan.acceptanceCriteria", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.acceptanceCriteria", { value: "" }).split(":")[0] })}`)}
|
|
171
|
+
${renderOrFallback(nonGoals, (item) => t("plan.nonGoal", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.nonGoal", { value: "" }).split(":")[0] })}`)}
|
|
172
|
+
${renderOrFallback(expectedBehaviorChanges, (item) => t("plan.behaviorChange", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.behaviorChange", { value: "" }).split(":")[0] })}`)}
|
|
173
|
+
${renderOrFallback(userVisibleEffects, (item) => t("plan.userVisibleEffect", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.userVisibleEffect", { value: "" }).split(":")[0] })}`)}
|
|
174
|
+
|
|
175
|
+
${t("plan.implementationShape")}:
|
|
176
|
+
${patternSummary ? `- ${t("plan.existingPatternSummary", { value: patternSummary })}` : `- ${t("plan.existingPatternSummaryDefault")}`}
|
|
177
|
+
${smallestViableChange ? `- ${t("plan.smallestViableChange", { value: smallestViableChange })}` : `- ${t("plan.smallestViableChangeDefault")}`}
|
|
178
|
+
${renderOrFallback(assumptions, (item) => t("plan.assumption", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.assumption", { value: "" }).split(":")[0] })}`)}
|
|
179
|
+
|
|
180
|
+
${t("plan.productionProfile", { value: productionProfile || "none declared" })}
|
|
181
|
+
${renderOrFallback(nfrRequirements, (item) => t("plan.nfrRequirement", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.nfrRequirement", { value: "" }).split(":")[0] })}`)}
|
|
182
|
+
${renderOrFallback(intendedSymbols, (item) => t("plan.intendedSymbol", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.intendedSymbol", { value: "" }).split(":")[0] })}`)}
|
|
183
|
+
${renderOrFallback(expectedPublicSurfaceChanges, (item) => t("plan.expectedPublicSurfaceChange", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.expectedPublicSurfaceChange", { value: "" }).split(":")[0] })}`)}
|
|
184
|
+
${renderOrFallback(expectedBoundaryExceptions, (item) => t("plan.expectedBoundaryException", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.expectedBoundaryException", { value: "" }).split(":")[0] })}`)}
|
|
185
|
+
${renderOrFallback(expectedTestTargets, (item) => t("plan.expectedTestTarget", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.expectedTestTarget", { value: "" }).split(":")[0] })}`)}
|
|
186
|
+
${renderOrFallback(expectedLoadSensitivePaths, (item) => t("plan.loadSensitivePath", { value: path.normalize(item) }), `- ${t("plan.noneDeclared", { label: t("plan.loadSensitivePath", { value: "" }).split(":")[0] })}`)}
|
|
187
|
+
${expectedConcurrencyImpact ? `- ${t("plan.expectedConcurrencyImpact", { value: expectedConcurrencyImpact })}` : `- ${t("plan.noneDeclared", { label: t("plan.expectedConcurrencyImpact", { value: "" }).split(":")[0] })}`}
|
|
188
|
+
${renderOrFallback(observabilityRequirements, (item) => t("plan.observabilityRequirement", { value: item }), `- ${t("plan.noneDeclared", { label: t("plan.observabilityRequirement", { value: "" }).split(":")[0] })}`)}
|
|
189
|
+
${rollbackNotes ? `- ${t("plan.rollbackNotes", { value: rollbackNotes })}` : `- ${t("plan.noneDeclared", { label: t("plan.rollbackNotes", { value: "" }).split(":")[0] })}`}
|
|
190
|
+
${riskJustification ? `- ${t("plan.riskJustification", { value: riskJustification })}` : `- ${t("plan.noneDeclared", { label: t("plan.riskJustification", { value: "" }).split(":")[0] })}`}
|
|
191
|
+
|
|
192
|
+
${t("plan.implementationNote")}:
|
|
193
|
+
- ${t("plan.implementationNote1")}
|
|
194
|
+
- ${t("plan.implementationNote2")}
|
|
195
|
+
- ${t("plan.implementationNote3")}
|
|
196
|
+
- ${t("plan.implementationNote4")}
|
|
197
|
+
`);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
task,
|
|
201
|
+
preset,
|
|
202
|
+
allowedPaths,
|
|
203
|
+
intendedFiles,
|
|
204
|
+
protectedPaths,
|
|
205
|
+
allowedChangeTypes,
|
|
206
|
+
riskLevel,
|
|
207
|
+
requiresReviewNotes,
|
|
208
|
+
validationProfile,
|
|
209
|
+
requiredCommands,
|
|
210
|
+
evidencePaths,
|
|
211
|
+
acknowledgedSkips,
|
|
212
|
+
patternSummary,
|
|
213
|
+
smallestViableChange,
|
|
214
|
+
assumptions,
|
|
215
|
+
acceptanceCriteria,
|
|
216
|
+
nonGoals,
|
|
217
|
+
expectedBehaviorChanges,
|
|
218
|
+
userVisibleEffects,
|
|
219
|
+
intendedSymbols,
|
|
220
|
+
expectedPublicSurfaceChanges,
|
|
221
|
+
expectedBoundaryExceptions,
|
|
222
|
+
expectedTestTargets,
|
|
223
|
+
productionProfile,
|
|
224
|
+
nfrRequirements,
|
|
225
|
+
expectedLoadSensitivePaths,
|
|
226
|
+
expectedConcurrencyImpact,
|
|
227
|
+
observabilityRequirements,
|
|
228
|
+
rollbackNotes,
|
|
229
|
+
riskJustification,
|
|
230
|
+
contractPath: writtenContractPath
|
|
231
|
+
};
|
|
232
|
+
}
|
package/lib/i18n.js
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
const resources = {
|
|
2
|
+
en: {
|
|
3
|
+
cli: {
|
|
4
|
+
usage: "Usage:",
|
|
5
|
+
commands: "Commands:",
|
|
6
|
+
supportedPresets: "Supported presets:",
|
|
7
|
+
supportedAdapters: "Supported adapters:",
|
|
8
|
+
supportedLocales: "Supported locales:",
|
|
9
|
+
initSummary: "Seed guardrail files into a repository",
|
|
10
|
+
planSummary: "Print a bounded implementation brief and write a richer task contract by default",
|
|
11
|
+
checkSummary: "Validate scope, consistency, correctness, risk, and production-profile rules for the current change",
|
|
12
|
+
unknownCommand: "Unknown command \"{command}\""
|
|
13
|
+
},
|
|
14
|
+
errors: {
|
|
15
|
+
missingTask: "Pass task text with --task \"...\"",
|
|
16
|
+
missingInitConfig: "Missing .agent-guardrails/config.json. Run `agent-guardrails init . --preset node-service` first.",
|
|
17
|
+
missingCheckConfig: "Missing .agent-guardrails/config.json. Run init first.",
|
|
18
|
+
unknownPreset: "Unknown preset \"{preset}\". Supported presets: {supportedPresets}",
|
|
19
|
+
unknownAdapters: "Unknown adapter(s): {adapters}. Supported adapters: {supportedAdapters}"
|
|
20
|
+
},
|
|
21
|
+
init: {
|
|
22
|
+
initialized: "Initialized guardrails for \"{projectName}\" with preset \"{preset}\".",
|
|
23
|
+
adapters: "Adapters: {adapters}",
|
|
24
|
+
created: "Created:",
|
|
25
|
+
skipped: "Skipped existing files:",
|
|
26
|
+
nextSteps: "Next steps:",
|
|
27
|
+
nextPlan: "- Run `agent-guardrails plan --task \"<task>\" --allow-paths \"src/,tests/\" --intended-files \"src/file.js,tests/file.test.js\" --allowed-change-types \"implementation-only\" --required-commands \"npm test\" --evidence \".agent-guardrails/evidence/current-task.md\"`.",
|
|
28
|
+
nextChange: "- Make the smallest change that fits the task contract.",
|
|
29
|
+
nextEvidence: "- Update `.agent-guardrails/evidence/current-task.md` with commands run, notable results, and residual risk.",
|
|
30
|
+
nextCheck: "- Run `agent-guardrails check --base-ref origin/main --commands-run \"npm test\" --review` before finishing."
|
|
31
|
+
},
|
|
32
|
+
plan: {
|
|
33
|
+
title: "Agent Guardrails Task Brief",
|
|
34
|
+
task: "Task",
|
|
35
|
+
preset: "Preset",
|
|
36
|
+
readBeforeWriting: "Read before writing",
|
|
37
|
+
constraints: "Constraints",
|
|
38
|
+
definitionOfDone: "Definition of done",
|
|
39
|
+
taskContract: "Task contract",
|
|
40
|
+
printOnly: "Print only mode; no contract written.",
|
|
41
|
+
writtenTo: "Written to {contractPath}",
|
|
42
|
+
allowedPath: "Allowed path: {value}",
|
|
43
|
+
intendedFile: "Intended file: {value}",
|
|
44
|
+
protectedPath: "Protected path: {value}",
|
|
45
|
+
allowedChangeType: "Allowed change type: {value}",
|
|
46
|
+
riskLevel: "Risk level: {value}",
|
|
47
|
+
reviewNotesRequiredYes: "Review notes required: yes",
|
|
48
|
+
reviewNotesRequiredNo: "Review notes required: no",
|
|
49
|
+
validationProfile: "Validation profile: {value}",
|
|
50
|
+
requiredCommand: "Required command: {value}",
|
|
51
|
+
evidencePath: "Evidence path: {value}",
|
|
52
|
+
acknowledgedSkip: "Acknowledged skip: {value}",
|
|
53
|
+
implementationShape: "Implementation shape",
|
|
54
|
+
existingPatternSummary: "Existing pattern summary: {value}",
|
|
55
|
+
existingPatternSummaryDefault: "Existing pattern summary: describe the current module pattern before editing",
|
|
56
|
+
smallestViableChange: "Smallest viable change: {value}",
|
|
57
|
+
smallestViableChangeDefault: "Smallest viable change: keep the implementation to the narrowest working slice",
|
|
58
|
+
assumption: "Assumption or unknown: {value}",
|
|
59
|
+
noneDeclared: "{label}: none declared",
|
|
60
|
+
implementationNote: "Implementation note",
|
|
61
|
+
implementationNote1: "List the exact files you plan to touch before editing.",
|
|
62
|
+
implementationNote2: "If the task requires new abstractions, justify why existing patterns are insufficient.",
|
|
63
|
+
implementationNote3: "Keep interface, config, and migration changes explicit instead of folding them into a generic task.",
|
|
64
|
+
implementationNote4: "Stop and surface missing context instead of inventing it.",
|
|
65
|
+
acceptanceAlignment: "Acceptance alignment",
|
|
66
|
+
acceptanceCriteria: "Acceptance criterion: {value}",
|
|
67
|
+
nonGoal: "Non-goal: {value}",
|
|
68
|
+
behaviorChange: "Expected behavior change: {value}",
|
|
69
|
+
userVisibleEffect: "User-visible effect: {value}",
|
|
70
|
+
productionProfile: "Production profile: {value}",
|
|
71
|
+
nfrRequirement: "Non-functional requirement: {value}",
|
|
72
|
+
intendedSymbol: "Intended symbol: {value}",
|
|
73
|
+
expectedPublicSurfaceChange: "Expected public surface change: {value}",
|
|
74
|
+
expectedBoundaryException: "Expected boundary exception: {value}",
|
|
75
|
+
expectedTestTarget: "Expected test target: {value}",
|
|
76
|
+
loadSensitivePath: "Load-sensitive path: {value}",
|
|
77
|
+
expectedConcurrencyImpact: "Expected concurrency impact: {value}",
|
|
78
|
+
observabilityRequirement: "Observability requirement: {value}",
|
|
79
|
+
rollbackNotes: "Rollback notes: {value}",
|
|
80
|
+
riskJustification: "Risk justification: {value}"
|
|
81
|
+
},
|
|
82
|
+
check: {
|
|
83
|
+
title: "Agent Guardrails Check",
|
|
84
|
+
preset: "Preset: {value}",
|
|
85
|
+
diffSource: "Diff source: {value}",
|
|
86
|
+
changedFiles: "Changed files: {value}",
|
|
87
|
+
sourceFiles: "Source files: {value}",
|
|
88
|
+
testFiles: "Test files: {value}",
|
|
89
|
+
topLevelEntries: "Top-level entries touched: {value}",
|
|
90
|
+
changeTypes: "Change types: {value}",
|
|
91
|
+
allowedPaths: "Allowed paths: {value}",
|
|
92
|
+
outOfScopeFiles: "Out of scope files: {value}",
|
|
93
|
+
taskContract: "Task contract: {value}",
|
|
94
|
+
taskAllowedPaths: "Task allowed paths: {value}",
|
|
95
|
+
taskOutOfScopeFiles: "Task out of scope files: {value}",
|
|
96
|
+
intendedFiles: "Intended files: {value}",
|
|
97
|
+
outOfIntendedFiles: "Out of intended files: {value}",
|
|
98
|
+
taskRequiredCommands: "Task required commands: {value}",
|
|
99
|
+
commandsReported: "Commands reported: {value}",
|
|
100
|
+
missingRequiredCommands: "Missing required commands: {value}",
|
|
101
|
+
taskEvidencePaths: "Task evidence paths: {value}",
|
|
102
|
+
missingEvidenceFiles: "Missing evidence files: {value}",
|
|
103
|
+
productionProfile: "Production profile: {value}",
|
|
104
|
+
nfrRequirements: "Non-functional requirements: {value}",
|
|
105
|
+
warnings: "Warnings:",
|
|
106
|
+
failures: "Failures:",
|
|
107
|
+
allPassed: "All baseline guardrail checks passed.",
|
|
108
|
+
reviewSummary: "Review summary:",
|
|
109
|
+
scopeIssues: "Scope issues: {value}",
|
|
110
|
+
validationIssues: "Missing validation: {value}",
|
|
111
|
+
consistencyConcerns: "Consistency concerns: {value}",
|
|
112
|
+
riskConcerns: "High-risk surface changes: {value}",
|
|
113
|
+
scopeGroup: "Scope issues",
|
|
114
|
+
validationGroup: "Missing validation",
|
|
115
|
+
consistencyGroup: "Consistency concerns",
|
|
116
|
+
riskGroup: "High-risk surface changes",
|
|
117
|
+
action: "Action: {value}",
|
|
118
|
+
findingSeverity: "[{severity}] {message}"
|
|
119
|
+
},
|
|
120
|
+
findings: {
|
|
121
|
+
missingRequiredFile: "Required file is missing: {path}",
|
|
122
|
+
diffUnavailable: "Unable to inspect repository diff. {details}",
|
|
123
|
+
noChangesBaseRef: "No git changes detected relative to base ref \"{baseRef}\".",
|
|
124
|
+
noChangesWorkingTree: "No git changes detected. Working-tree checks were skipped.",
|
|
125
|
+
changedFileBudgetExceeded: "Changed file count ({count}) exceeds configured task budget ({budget}).",
|
|
126
|
+
broadTopLevelChange: "Task spans {count} top-level areas ({areas}), which is wider than the recommended review surface ({budget}).",
|
|
127
|
+
repoAllowedPathViolation: "Changed files outside allowed paths: {files}",
|
|
128
|
+
taskPathViolation: "Changed files outside task contract paths: {files}",
|
|
129
|
+
intendedFileViolation: "Changed files outside intended files: {files}",
|
|
130
|
+
taskBreadthSuspicious: "Changed file count ({count}) is much larger than the declared intended file set ({intendedCount}).",
|
|
131
|
+
missingRequiredCommands: "Missing required commands from task contract: {commands}",
|
|
132
|
+
missingEvidenceFiles: "Missing required evidence files: {files}",
|
|
133
|
+
sourceWithoutTests: "Source files changed without any accompanying test changes.",
|
|
134
|
+
taskProtectedPathsTouched: "Task touched review-critical paths: {paths}",
|
|
135
|
+
protectedAreaRiskLevelTooLow: "Protected area touched without sufficient task risk level: {label}",
|
|
136
|
+
protectedAreaTouched: "High-risk area changed: {label}",
|
|
137
|
+
protectedAreaMissingReviewNotes: "High-risk area changed without review-oriented notes in evidence: {label}",
|
|
138
|
+
taskMissingReviewNotes: "Task contract requires review notes, but the evidence files do not include them.",
|
|
139
|
+
changeTypeViolation: "Changed files violate the declared change types ({types}): {files}",
|
|
140
|
+
interfaceChangeWithoutContract: "Interface-like files changed without an explicit allowed change type declaration: {files}",
|
|
141
|
+
configOrMigrationChange: "Config or migration files changed: {files}",
|
|
142
|
+
productionProfileMissingNfr: "Task declares a production profile without any explicit non-functional requirements.",
|
|
143
|
+
criticalPathTouchedWithoutRollback: "Critical path changed without rollback notes: {files}",
|
|
144
|
+
performanceSensitiveAreaTouched: "Performance-sensitive area changed: {files}",
|
|
145
|
+
observabilityRequirementsUnaddressed: "Observability requirements were declared, but the evidence note does not mention them.",
|
|
146
|
+
concurrencyRequirementsUnaddressed: "Concurrency or performance requirements were declared, but the evidence note does not mention validation for them."
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"zh-CN": {
|
|
150
|
+
cli: {
|
|
151
|
+
usage: "用法:",
|
|
152
|
+
commands: "命令:",
|
|
153
|
+
supportedPresets: "支持的 preset:",
|
|
154
|
+
supportedAdapters: "支持的 adapter:",
|
|
155
|
+
supportedLocales: "支持的语言:",
|
|
156
|
+
initSummary: "向仓库写入 guardrail 文件",
|
|
157
|
+
planSummary: "输出受约束的实现简报,并默认写入更丰富的任务契约",
|
|
158
|
+
checkSummary: "校验当前改动的范围、一致性、正确性、风险和生产画像规则",
|
|
159
|
+
unknownCommand: "未知命令 \"{command}\""
|
|
160
|
+
},
|
|
161
|
+
errors: {
|
|
162
|
+
missingTask: "请通过 --task \"...\" 传入任务描述",
|
|
163
|
+
missingInitConfig: "缺少 .agent-guardrails/config.json。请先运行 `agent-guardrails init . --preset node-service`。",
|
|
164
|
+
missingCheckConfig: "缺少 .agent-guardrails/config.json。请先执行 init。",
|
|
165
|
+
unknownPreset: "未知 preset \"{preset}\"。支持的 preset:{supportedPresets}",
|
|
166
|
+
unknownAdapters: "未知 adapter:{adapters}。支持的 adapter:{supportedAdapters}"
|
|
167
|
+
},
|
|
168
|
+
init: {
|
|
169
|
+
initialized: "已为 \"{projectName}\" 使用 preset \"{preset}\" 初始化 guardrails。",
|
|
170
|
+
adapters: "Adapters:{adapters}",
|
|
171
|
+
created: "已创建:",
|
|
172
|
+
skipped: "已跳过现有文件:",
|
|
173
|
+
nextSteps: "下一步:",
|
|
174
|
+
nextPlan: "- 运行 `agent-guardrails plan --task \"<task>\" --allow-paths \"src/,tests/\" --intended-files \"src/file.js,tests/file.test.js\" --allowed-change-types \"implementation-only\" --required-commands \"npm test\" --evidence \".agent-guardrails/evidence/current-task.md\"`。",
|
|
175
|
+
nextChange: "- 只做符合任务契约的最小改动。",
|
|
176
|
+
nextEvidence: "- 更新 `.agent-guardrails/evidence/current-task.md`,写明执行过的命令、关键结果和残余风险。",
|
|
177
|
+
nextCheck: "- 完成前运行 `agent-guardrails check --base-ref origin/main --commands-run \"npm test\" --review`。"
|
|
178
|
+
},
|
|
179
|
+
plan: {
|
|
180
|
+
title: "Agent Guardrails 任务简报",
|
|
181
|
+
task: "任务",
|
|
182
|
+
preset: "Preset",
|
|
183
|
+
readBeforeWriting: "编码前先阅读",
|
|
184
|
+
constraints: "约束",
|
|
185
|
+
definitionOfDone: "完成定义",
|
|
186
|
+
taskContract: "任务契约",
|
|
187
|
+
printOnly: "仅打印模式;未写入任务契约。",
|
|
188
|
+
writtenTo: "已写入 {contractPath}",
|
|
189
|
+
allowedPath: "允许路径:{value}",
|
|
190
|
+
intendedFile: "预期文件:{value}",
|
|
191
|
+
protectedPath: "保护路径:{value}",
|
|
192
|
+
allowedChangeType: "允许的改动类型:{value}",
|
|
193
|
+
riskLevel: "风险级别:{value}",
|
|
194
|
+
reviewNotesRequiredYes: "需要 review 说明:是",
|
|
195
|
+
reviewNotesRequiredNo: "需要 review 说明:否",
|
|
196
|
+
validationProfile: "验证画像:{value}",
|
|
197
|
+
requiredCommand: "必须执行的命令:{value}",
|
|
198
|
+
evidencePath: "证据文件:{value}",
|
|
199
|
+
acknowledgedSkip: "已确认跳过项:{value}",
|
|
200
|
+
implementationShape: "实现形状",
|
|
201
|
+
existingPatternSummary: "现有模式总结:{value}",
|
|
202
|
+
existingPatternSummaryDefault: "现有模式总结:在改代码前先说明当前模块的实现模式",
|
|
203
|
+
smallestViableChange: "最小可行改动:{value}",
|
|
204
|
+
smallestViableChangeDefault: "最小可行改动:把实现收敛到最小工作切片",
|
|
205
|
+
assumption: "假设或未知项:{value}",
|
|
206
|
+
noneDeclared: "{label}:未声明",
|
|
207
|
+
implementationNote: "实现说明",
|
|
208
|
+
implementationNote1: "开始编辑前列出你计划修改的准确文件。",
|
|
209
|
+
implementationNote2: "如果任务确实需要新抽象,说明为什么现有模式不够。",
|
|
210
|
+
implementationNote3: "接口、配置、迁移类改动必须显式声明,不要混进普通实现任务。",
|
|
211
|
+
implementationNote4: "如果上下文不足,要明确提出,不要自行脑补。",
|
|
212
|
+
acceptanceAlignment: "需求与验收对齐",
|
|
213
|
+
acceptanceCriteria: "验收标准:{value}",
|
|
214
|
+
nonGoal: "不在范围内:{value}",
|
|
215
|
+
behaviorChange: "预期行为变化:{value}",
|
|
216
|
+
userVisibleEffect: "用户可见影响:{value}",
|
|
217
|
+
productionProfile: "生产画像:{value}",
|
|
218
|
+
nfrRequirement: "非功能性要求:{value}",
|
|
219
|
+
intendedSymbol: "预期符号:{value}",
|
|
220
|
+
expectedPublicSurfaceChange: "预期公共接口变化:{value}",
|
|
221
|
+
expectedBoundaryException: "预期边界例外:{value}",
|
|
222
|
+
expectedTestTarget: "预期测试目标:{value}",
|
|
223
|
+
loadSensitivePath: "负载敏感路径:{value}",
|
|
224
|
+
expectedConcurrencyImpact: "预期并发影响:{value}",
|
|
225
|
+
observabilityRequirement: "可观测性要求:{value}",
|
|
226
|
+
rollbackNotes: "回滚说明:{value}",
|
|
227
|
+
riskJustification: "风险说明:{value}"
|
|
228
|
+
},
|
|
229
|
+
check: {
|
|
230
|
+
title: "Agent Guardrails 检查结果",
|
|
231
|
+
preset: "Preset:{value}",
|
|
232
|
+
diffSource: "Diff 来源:{value}",
|
|
233
|
+
changedFiles: "变更文件数:{value}",
|
|
234
|
+
sourceFiles: "源码文件数:{value}",
|
|
235
|
+
testFiles: "测试文件数:{value}",
|
|
236
|
+
topLevelEntries: "触及的顶层区域:{value}",
|
|
237
|
+
changeTypes: "改动类型:{value}",
|
|
238
|
+
allowedPaths: "允许路径数:{value}",
|
|
239
|
+
outOfScopeFiles: "越界文件数:{value}",
|
|
240
|
+
taskContract: "任务契约:{value}",
|
|
241
|
+
taskAllowedPaths: "任务允许路径数:{value}",
|
|
242
|
+
taskOutOfScopeFiles: "任务范围外文件数:{value}",
|
|
243
|
+
intendedFiles: "预期文件数:{value}",
|
|
244
|
+
outOfIntendedFiles: "预期外文件数:{value}",
|
|
245
|
+
taskRequiredCommands: "任务要求命令数:{value}",
|
|
246
|
+
commandsReported: "已上报命令数:{value}",
|
|
247
|
+
missingRequiredCommands: "缺失命令数:{value}",
|
|
248
|
+
taskEvidencePaths: "任务证据文件数:{value}",
|
|
249
|
+
missingEvidenceFiles: "缺失证据文件数:{value}",
|
|
250
|
+
productionProfile: "生产画像:{value}",
|
|
251
|
+
nfrRequirements: "非功能性要求:{value}",
|
|
252
|
+
warnings: "警告:",
|
|
253
|
+
failures: "失败项:",
|
|
254
|
+
allPassed: "所有基础 guardrail 检查均已通过。",
|
|
255
|
+
reviewSummary: "Review 摘要:",
|
|
256
|
+
scopeIssues: "范围问题:{value}",
|
|
257
|
+
validationIssues: "验证缺口:{value}",
|
|
258
|
+
consistencyConcerns: "一致性问题:{value}",
|
|
259
|
+
riskConcerns: "高风险变更:{value}",
|
|
260
|
+
scopeGroup: "范围问题",
|
|
261
|
+
validationGroup: "验证缺口",
|
|
262
|
+
consistencyGroup: "一致性问题",
|
|
263
|
+
riskGroup: "高风险变更",
|
|
264
|
+
action: "处理建议:{value}",
|
|
265
|
+
findingSeverity: "[{severity}] {message}"
|
|
266
|
+
},
|
|
267
|
+
findings: {
|
|
268
|
+
missingRequiredFile: "缺少必需文件:{path}",
|
|
269
|
+
diffUnavailable: "无法读取仓库 diff。{details}",
|
|
270
|
+
noChangesBaseRef: "相对于基线 \"{baseRef}\" 没有检测到 git 变更。",
|
|
271
|
+
noChangesWorkingTree: "未检测到 git 变更,已跳过 working-tree 检查。",
|
|
272
|
+
changedFileBudgetExceeded: "变更文件数({count})超过了任务预算({budget})。",
|
|
273
|
+
broadTopLevelChange: "本次任务跨越了 {count} 个顶层区域({areas}),已经超出推荐的 review 范围({budget})。",
|
|
274
|
+
repoAllowedPathViolation: "存在超出仓库允许路径的变更:{files}",
|
|
275
|
+
taskPathViolation: "存在超出任务契约路径的变更:{files}",
|
|
276
|
+
intendedFileViolation: "存在超出预期文件的变更:{files}",
|
|
277
|
+
taskBreadthSuspicious: "变更文件数({count})明显大于声明的预期文件数({intendedCount})。",
|
|
278
|
+
missingRequiredCommands: "缺少任务契约要求的命令:{commands}",
|
|
279
|
+
missingEvidenceFiles: "缺少要求的证据文件:{files}",
|
|
280
|
+
sourceWithoutTests: "源码发生了变更,但没有任何配套测试改动。",
|
|
281
|
+
taskProtectedPathsTouched: "任务触及了 review 关键路径:{paths}",
|
|
282
|
+
protectedAreaRiskLevelTooLow: "触及保护区域,但任务风险级别不足:{label}",
|
|
283
|
+
protectedAreaTouched: "触及高风险区域:{label}",
|
|
284
|
+
protectedAreaMissingReviewNotes: "触及高风险区域,但证据中缺少面向 review 的说明:{label}",
|
|
285
|
+
taskMissingReviewNotes: "任务契约要求提供 review 说明,但证据文件中没有体现。",
|
|
286
|
+
changeTypeViolation: "存在违反声明改动类型({types})的文件:{files}",
|
|
287
|
+
interfaceChangeWithoutContract: "存在接口类文件变更,但任务没有显式声明改动类型:{files}",
|
|
288
|
+
configOrMigrationChange: "存在配置或迁移类变更:{files}",
|
|
289
|
+
productionProfileMissingNfr: "任务声明了生产画像,但没有任何明确的非功能性要求。",
|
|
290
|
+
criticalPathTouchedWithoutRollback: "触及关键路径,但缺少回滚说明:{files}",
|
|
291
|
+
performanceSensitiveAreaTouched: "触及性能敏感区域:{files}",
|
|
292
|
+
observabilityRequirementsUnaddressed: "任务声明了可观测性要求,但证据中没有体现。",
|
|
293
|
+
concurrencyRequirementsUnaddressed: "任务声明了并发或性能要求,但证据中没有体现相关验证。"
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
export const supportedLocales = ["en", "zh-CN"];
|
|
299
|
+
|
|
300
|
+
function interpolate(template, vars) {
|
|
301
|
+
return Object.entries(vars || {}).reduce((output, [key, value]) => {
|
|
302
|
+
return output.replaceAll(`{${key}}`, String(value));
|
|
303
|
+
}, template);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function normalizeLocale(locale) {
|
|
307
|
+
if (!locale) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const normalized = String(locale).trim().replaceAll("_", "-");
|
|
312
|
+
const lower = normalized.toLowerCase();
|
|
313
|
+
|
|
314
|
+
if (lower === "zh" || lower.startsWith("zh-cn")) {
|
|
315
|
+
return "zh-CN";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (lower.startsWith("en")) {
|
|
319
|
+
return "en";
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function getSystemLocale() {
|
|
326
|
+
return (
|
|
327
|
+
process.env.LC_ALL ||
|
|
328
|
+
process.env.LC_MESSAGES ||
|
|
329
|
+
process.env.LANG ||
|
|
330
|
+
Intl.DateTimeFormat().resolvedOptions().locale ||
|
|
331
|
+
"en"
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function resolveLocale(explicitLocale = null) {
|
|
336
|
+
return normalizeLocale(
|
|
337
|
+
explicitLocale ||
|
|
338
|
+
process.env.AGENT_GUARDRAILS_LOCALE ||
|
|
339
|
+
getSystemLocale()
|
|
340
|
+
) ?? "en";
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function createTranslator(explicitLocale = null) {
|
|
344
|
+
const locale = resolveLocale(explicitLocale);
|
|
345
|
+
const resource = resources[locale] ?? resources.en;
|
|
346
|
+
|
|
347
|
+
function t(key, vars = {}) {
|
|
348
|
+
const value = key.split(".").reduce((current, segment) => current?.[segment], resource);
|
|
349
|
+
if (typeof value === "string") {
|
|
350
|
+
return interpolate(value, vars);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const fallback = key.split(".").reduce((current, segment) => current?.[segment], resources.en);
|
|
354
|
+
if (typeof fallback === "string") {
|
|
355
|
+
return interpolate(fallback, vars);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return key;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return { locale, t };
|
|
362
|
+
}
|