agent-quality-kit 0.2.5 → 0.4.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/README.md +72 -0
- package/README.ru.md +71 -0
- package/kit/gates/_native.sh +35 -0
- package/kit/gates/_skip.sh +34 -3
- package/kit/gates/commit-explains-itself/gate.yml +1 -0
- package/kit/gates/complexity-limit/gate.yml +1 -0
- package/kit/gates/dead-code/gate.yml +1 -0
- package/kit/gates/deps-are-pinned/gate.yml +1 -0
- package/kit/gates/duplicate-code/gate.yml +1 -0
- package/kit/gates/entry-links-exist/gate.yml +1 -0
- package/kit/gates/file-size-limit/gate.yml +1 -0
- package/kit/gates/gate-has-samples/gate.yml +1 -0
- package/kit/gates/gates-are-runnable/gate.yml +1 -0
- package/kit/gates/gates-run-in-ci/gate.yml +1 -0
- package/kit/gates/lesson-has-outcome/gate.yml +1 -0
- package/kit/gates/no-print-in-prod/check.sh +3 -1
- package/kit/gates/no-print-in-prod/gate.yml +1 -0
- package/kit/gates/secrets-not-in-code/gate.yml +1 -0
- package/kit/gates/swallowed-error/gate.yml +1 -0
- package/kit/gates/todo-without-task/gate.yml +1 -0
- package/package.json +1 -1
- package/tool/commands/badge.mjs +79 -0
- package/tool/commands/doctor.mjs +38 -41
- package/tool/commands/gates.mjs +121 -112
- package/tool/commands/project.mjs +84 -85
- package/tool/commands/report.mjs +194 -0
- package/tool/i18n/en.mjs +436 -0
- package/tool/i18n/index.mjs +33 -0
- package/tool/i18n/ru.mjs +437 -0
- package/tool/i18n/templates-en.mjs +164 -0
- package/tool/i18n/templates-ru.mjs +170 -0
- package/tool/lib/core.mjs +5 -1
- package/tool/lib/manifest.mjs +12 -31
- package/tool/lib/repo.mjs +45 -21
- package/tool/lib/templates.mjs +38 -182
- package/tool/program.mjs +36 -9
- package/tool/selfcheck/gates.sh +7 -1
- package/tool/selfcheck/smoke.sh +169 -0
- package/tool/selfcheck/units.mjs +101 -5
package/tool/i18n/en.mjs
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// tool/i18n/en.mjs — английский каталог строк вывода.
|
|
2
|
+
//
|
|
3
|
+
// Правка здесь обязана иметь пару в ru.mjs с тем же ключом: расхождение ловит модульная
|
|
4
|
+
// проверка «оба каталога несут одни и те же ключи».
|
|
5
|
+
|
|
6
|
+
import { templates } from "./templates-en.mjs";
|
|
7
|
+
|
|
8
|
+
export const en = {
|
|
9
|
+
templates,
|
|
10
|
+
help: {
|
|
11
|
+
tagline: "tooling for building software with agents",
|
|
12
|
+
name: "<name>",
|
|
13
|
+
init: "lay the rules and guides into the current project",
|
|
14
|
+
initForce: "overwrite files that already exist",
|
|
15
|
+
start: "no code yet: day-zero guards and the order of work",
|
|
16
|
+
doctor: "check what is laid out and what is missing",
|
|
17
|
+
doctorRun: "and also run the declared gates",
|
|
18
|
+
add: "install a gate from the catalogue into the project",
|
|
19
|
+
find: "is there already such a gate — matched by intent",
|
|
20
|
+
why: "a bug slipped through — why did no guard catch it",
|
|
21
|
+
ratchet: "ratchet: existing violations become debt, new ones are blocked",
|
|
22
|
+
new: "scaffold your own gate for the catalogue",
|
|
23
|
+
note: "record a lesson in the shared bruise journal",
|
|
24
|
+
blob: "assemble the guides into a single GOD_AI.md",
|
|
25
|
+
report: "the mandatory report form: what is in place, what is not, what was not read",
|
|
26
|
+
badge: "a level badge for your README — and a check that it does not lie",
|
|
27
|
+
noInstall: "Without installing: npx agent-quality-kit init",
|
|
28
|
+
language: "Output language: AQK_LANG=ru (or en), otherwise your system locale",
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
doctor: {
|
|
32
|
+
docsKit: "guides — the originals live here, not a copy",
|
|
33
|
+
docs: "guides",
|
|
34
|
+
rulesKit: "standards — the originals live here, not a copy",
|
|
35
|
+
rules: "standards",
|
|
36
|
+
agents: "entry point for agents",
|
|
37
|
+
gitignore: "repository hygiene",
|
|
38
|
+
git: "project under version control",
|
|
39
|
+
|
|
40
|
+
emptyCommands: (n) => `AGENTS.md has ${n} unfilled commands.`,
|
|
41
|
+
emptyCommandsWhy: "An agent cannot execute an empty line.",
|
|
42
|
+
|
|
43
|
+
levelHeading: "AQK compliance level",
|
|
44
|
+
levelNone: "none",
|
|
45
|
+
levelNotSet: "Level: the standard is not set up in this repository.",
|
|
46
|
+
levelNotSetWhy: [
|
|
47
|
+
"This is not a verdict on the project. The level measures how machine-readable",
|
|
48
|
+
"your practice is, not how mature it is. Checks may exist and work — but until",
|
|
49
|
+
"they are declared in .aqk.yml, no agent, no pipeline and no newcomer knows.",
|
|
50
|
+
],
|
|
51
|
+
levelManifestNoZero: "Level: a manifest exists, but AQK-0 is not reached.",
|
|
52
|
+
level: (n) => `Level: AQK-${n}.`,
|
|
53
|
+
toReach: (n) => `To reach AQK-${n}:`,
|
|
54
|
+
gives: (what) => `What it buys you: ${what}`,
|
|
55
|
+
allDone: "All levels reached.",
|
|
56
|
+
|
|
57
|
+
gatesHeading: "Gates",
|
|
58
|
+
langs: "languages",
|
|
59
|
+
langsUnknown: "not detected",
|
|
60
|
+
files: "files",
|
|
61
|
+
hasThings: "has",
|
|
62
|
+
install: (cmd) => `install: ${cmd}`,
|
|
63
|
+
notApplicable: (n) => `Not applicable to this repository (${n}):`,
|
|
64
|
+
total: "Total:",
|
|
65
|
+
totalHeld: (n) => `held by a machine ${n}`,
|
|
66
|
+
totalTodo: (n) => `applicable but not installed ${n}`,
|
|
67
|
+
totalSkip: (n) => `hidden ${n}`,
|
|
68
|
+
|
|
69
|
+
runHeading: "Running the declared gates",
|
|
70
|
+
timeout: "did not finish within 5 minutes",
|
|
71
|
+
exitCode: (code) => `exit ${code}`,
|
|
72
|
+
moreLines: (n) => `… and ${n} more lines`,
|
|
73
|
+
declaredNotRun: (n) => `${n} gates declared, but never run.`,
|
|
74
|
+
declaredNotRunWhy: (cmd) => ` "declared" and "works" are different claims: ${cmd}`,
|
|
75
|
+
|
|
76
|
+
thresholdPass: (min) => `Threshold AQK-${min} passed.`,
|
|
77
|
+
thresholdFail: (min, now) => `Threshold AQK-${min} NOT passed: currently AQK-${now}.`,
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
trigger: {
|
|
81
|
+
noLangs: (langs) => `none of these languages: ${langs}`,
|
|
82
|
+
tooFewFiles: (n) => `fewer than ${n} files — too early`,
|
|
83
|
+
tooManyFiles: (n) => `more than ${n} files`,
|
|
84
|
+
notSet: "no trigger declared",
|
|
85
|
+
unknown: (key) => `the program cannot evaluate the condition "${key}"`,
|
|
86
|
+
flags: {
|
|
87
|
+
has_gates: ["no gates declared in the manifest", "gates are already declared"],
|
|
88
|
+
has_ci: ["no pipeline in this repository", "a pipeline already exists"],
|
|
89
|
+
has_db: ["no database in sight: no migrations, no sql", "a database exists"],
|
|
90
|
+
has_docker: ["no Dockerfile or compose", "docker is already here"],
|
|
91
|
+
has_deps: ["no dependency file in sight", "dependencies are declared"],
|
|
92
|
+
has_tests: ["no tests in sight", "tests exist"],
|
|
93
|
+
has_env: ["no environment file", "an environment file exists"],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
recipe: {
|
|
98
|
+
skipped: (lang, prog) => `skipped the ${lang} recipe: "${prog}" is not installed`,
|
|
99
|
+
none: "no recipe described",
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
manifest: {
|
|
103
|
+
noGatesBlock: "no gates: block in .aqk.yml",
|
|
104
|
+
alreadyDeclared: "already declared",
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
add: {
|
|
108
|
+
noSuchGate: (slug, cmd) => `No such gate: ${slug}\nThe applicable ones — ${cmd}`,
|
|
109
|
+
noRecipe: (slug, stack) => `Entry ${slug} has no command for ${stack} and no portable one.`,
|
|
110
|
+
thisStack: "this stack",
|
|
111
|
+
needName: (cmd, doctor) => `Name the gate: ${cmd}. The list — ${doctor}`,
|
|
112
|
+
noManifest: (cmd) => `No .aqk.yml — run ${cmd} first`,
|
|
113
|
+
notApplicable: (why) => `This gate does not apply to the repository: ${why}`,
|
|
114
|
+
installAnyway: "Installing anyway — your call, but it has nothing to guard here.",
|
|
115
|
+
copied: (n) => `${n} files: the check and its samples`,
|
|
116
|
+
declared: (cmd) => `gate declared: ${cmd}`,
|
|
117
|
+
notDeclared: (why, slug, cmd) => `untouched (${why}). Add it yourself: ${slug}: "${cmd}"`,
|
|
118
|
+
nextTitle: "Next:",
|
|
119
|
+
next1: "Check that it goes red and stays quiet where it should:",
|
|
120
|
+
expectFail: "→ a failure is expected",
|
|
121
|
+
expectSilence: "→ silence is expected",
|
|
122
|
+
next2: "Put the command into your commit hook and your pipeline.",
|
|
123
|
+
next2Why: "A gate nobody runs is not a gate.",
|
|
124
|
+
next3: (cmd) => `Run everything declared: ${cmd}`,
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
gnew: {
|
|
128
|
+
needName: (cmd) => `Name it: ${cmd}`,
|
|
129
|
+
badName: (slug) => `The name "${slug}" will not do: lowercase latin with dashes, e.g. secrets-not-in-code.\nThe name is read in other people's projects — it is part of a shared vocabulary.`,
|
|
130
|
+
looksExisting: (slug) => `Looks like this already exists: ${slug}`,
|
|
131
|
+
recipeNotGate: "A recipe for another stack is a line in the recipes of an existing entry.",
|
|
132
|
+
forceHint: (cmd) => `Create a new one anyway: ${cmd}`,
|
|
133
|
+
exists: (path) => `${path} already exists.`,
|
|
134
|
+
nextTitle: "Next, in order:",
|
|
135
|
+
n1: "Check whether an off-the-shelf rule exists",
|
|
136
|
+
n1Where: "in ruff, eslint, semgrep.",
|
|
137
|
+
n1Why: "Off-the-shelf is more precise, better documented and maintained without you. Your own check is the fallback.",
|
|
138
|
+
n2: "Add the samples.",
|
|
139
|
+
n2Red: "— code the check must fire on.",
|
|
140
|
+
n2Green: "— the same code, done right.",
|
|
141
|
+
n2Why: "The green one matters more: it catches a check that goes red on correct code.",
|
|
142
|
+
n3: "Write the check",
|
|
143
|
+
n3Where: "in check.sh. The failure text must say what exactly to do.",
|
|
144
|
+
n4: "Fill in gate.yml:",
|
|
145
|
+
n4What: "the intent, the trigger, the failure it proved itself on.",
|
|
146
|
+
n5: "Run it:",
|
|
147
|
+
n5Why: "The arbiter must go red on red/ and stay quiet on green/. It did not — it is not an entry.",
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
ratchet: {
|
|
151
|
+
needName: (cmd) => `Name the gate: ${cmd}. It must already be declared in .aqk.yml`,
|
|
152
|
+
noManifest: (cmd) => `No .aqk.yml — run ${cmd} first`,
|
|
153
|
+
notDeclared: (slug, cmd) => `Gate "${slug}" is not declared in .aqk.yml. First: ${cmd}`,
|
|
154
|
+
already: (slug) => `Gate "${slug}" already has a ratchet.`,
|
|
155
|
+
notRunnable: (slug, cmd) =>
|
|
156
|
+
`Gate "${slug}" does not run: ${cmd}\n` +
|
|
157
|
+
`You cannot capture debt from a guard that does not exist — its own error messages\n` +
|
|
158
|
+
`would land in the registry and become a permission. Fix the command first.`,
|
|
159
|
+
registryHead: (slug, stamp) =>
|
|
160
|
+
`# Debt registry: ${slug}\n` +
|
|
161
|
+
`# Captured ${stamp}. This list may ONLY get shorter.\n` +
|
|
162
|
+
`# A new violation turns the gate red; a fixed one is struck out automatically.\n`,
|
|
163
|
+
recorded: (n) => `${n} violations recorded as debt`,
|
|
164
|
+
libCopied: "wrapper copied into the project",
|
|
165
|
+
wrapped: "command wrapped in the ratchet",
|
|
166
|
+
changesTitle: "What this changes:",
|
|
167
|
+
fromToday: "from the day it is installed",
|
|
168
|
+
changes1: (fromToday) => `The rule applies ${fromToday}. The old code stays untouched, but a new`,
|
|
169
|
+
changes2: "violation of the same class will not get through.",
|
|
170
|
+
test1: 'The test for a ratchet rather than an advisor: "can new code add a violation',
|
|
171
|
+
test2: 'and still pass?" If it can, there is no gate.',
|
|
172
|
+
run: (cmd) => `Run it: ${cmd}`,
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
find: {
|
|
176
|
+
needQuery: (cmd) => `Describe the intent in words: ${cmd}`,
|
|
177
|
+
example: 'find "debug printing reaches production"',
|
|
178
|
+
exists: "This already exists — no new entry needed:",
|
|
179
|
+
match: (pct) => `${pct}% match`,
|
|
180
|
+
recipes: (langs) => `recipes: ${langs}portable`,
|
|
181
|
+
existsWhy1: "If you have a recipe for another stack, that is a line in the recipes of the",
|
|
182
|
+
existsWhy2: "existing entry, not a new gate. One intent, many possible executors.",
|
|
183
|
+
near: "No exact match, but these are close:",
|
|
184
|
+
nearWhy: "Read their README. If the intent is the same — extend it, do not start a new one.",
|
|
185
|
+
none: "No such intent in the catalogue.",
|
|
186
|
+
journal: "The journal has a bruise on this topic:",
|
|
187
|
+
journalWhy: "A recorded bruise means the proof for a new entry already exists.",
|
|
188
|
+
howTitle: "How to add your own gate:",
|
|
189
|
+
how1: "Name the failure.",
|
|
190
|
+
how1What: "What concrete defect it caught in a live project, and what that cost.",
|
|
191
|
+
how1Why: '"It is a good practice" is not accepted: that is how a catalogue collects hundreds of entries and dies.',
|
|
192
|
+
how2: "Create the folder",
|
|
193
|
+
how2What: "— gate.yml, red/, green/, README.md.",
|
|
194
|
+
how2Why: "The full entry format is in kit/gates/README.md",
|
|
195
|
+
how3: "Check it by machine:",
|
|
196
|
+
how3Why: "The arbiter must go red on red/ and stay quiet on green/. It did not — it is not an entry.",
|
|
197
|
+
how4: "Send it as a change",
|
|
198
|
+
how4What: "to the kit repository.",
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
why: {
|
|
202
|
+
needQuery: (cmd) => `Describe what slipped through: ${cmd}`,
|
|
203
|
+
example: 'why "a file grew to nine thousand lines"',
|
|
204
|
+
ciAtOnce: "all at once: doctor --run",
|
|
205
|
+
ciOwnStep: "as its own step",
|
|
206
|
+
unsure: "No confident match. These entries look close:",
|
|
207
|
+
unsureByName: (cmd) => `Name the entry directly: ${cmd}`,
|
|
208
|
+
unsureNone: (cmd) => `None of them fits — then there was no guard: ${cmd}`,
|
|
209
|
+
decideTitle: "The rest is your call:",
|
|
210
|
+
decideQ: "is this specific to you, or a general case?",
|
|
211
|
+
decideWhy: "General goes into the catalogue and reaches everyone. Specific stays with you.",
|
|
212
|
+
decideNote: (cmd) => `Either way, the lesson goes into the shared journal: ${cmd}`,
|
|
213
|
+
fix: "Fix it like this:",
|
|
214
|
+
noGuard: "There was no guard.",
|
|
215
|
+
noGuardWhy: "No entry in the catalogue carries this intent.",
|
|
216
|
+
noGuardFix: (cmd) => `create an entry — ${cmd}`,
|
|
217
|
+
noGuardHint: "Take the red sample straight from this failure: it already happened, nothing to invent.",
|
|
218
|
+
closest: (slug, pct) => `Closest catalogue entry: ${slug} ${pct}% match`,
|
|
219
|
+
notInstalled: "The guard exists in the catalogue but is not installed in this project.",
|
|
220
|
+
notInstalledHint1: "It will go red on old code — that is normal: the old is covered by a ratchet,",
|
|
221
|
+
notInstalledHint2: (cmd) => `the new is caught from day one. ${cmd}`,
|
|
222
|
+
declaredAs: (cmd) => `Declared as: ${cmd}`,
|
|
223
|
+
notRunning: "The guard is declared but does not run.",
|
|
224
|
+
notRunningWhy: "The worst case: silence reads as success.",
|
|
225
|
+
notRunningFix: "the path or the program in the command does not exist — check them.",
|
|
226
|
+
notRunningHint: 'A missing signal is indistinguishable from success, so this is not "a small config detail".',
|
|
227
|
+
bypassed: "The guard exists and catches this failure — so it was bypassed.",
|
|
228
|
+
noCiFix: "there is no pipeline. A check only a human runs",
|
|
229
|
+
noCiHint: 'works right up until the first "forgot".',
|
|
230
|
+
notInCiFix: "a pipeline exists, but this gate does not run in it.",
|
|
231
|
+
notInCiHint: (cmd) => `The cheapest way is one step: ${cmd} — it runs everything declared.`,
|
|
232
|
+
inCiFix: (how) => `the pipeline does run it (${how}) — so a red run`,
|
|
233
|
+
inCiHint1: "was skipped or overridden. Move the rule out of prose and into mechanics:",
|
|
234
|
+
inCiHint2: "a blocking step, not an optional one; no merging while red.",
|
|
235
|
+
blind: "The guard exists, is installed and runs — but does not see this failure.",
|
|
236
|
+
blindFix: (dir) => `put a piece of the broken code into ${dir}`,
|
|
237
|
+
blindHint1: "and drive the check to red on it. The order is the reverse of the habit: the sample",
|
|
238
|
+
blindHint2: "first, the fix second — otherwise it is unclear what exactly got fixed.",
|
|
239
|
+
blindCheck: "Verify after the fix: bash tool/selfcheck/gates.sh",
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
init: {
|
|
243
|
+
noDocs: (dir) => `Guides not found: ${dir}\nLooks like the package is not fully installed.`,
|
|
244
|
+
created: (n) => `created (${n}):`,
|
|
245
|
+
andMore: (n) => `… and ${n} more`,
|
|
246
|
+
kept: (n) => `already there, left untouched (${n}):`,
|
|
247
|
+
overwrite: (cmd) => `overwrite: ${cmd}`,
|
|
248
|
+
nextTitle: "What to do next, in order:",
|
|
249
|
+
n1a: "Open",
|
|
250
|
+
n1b: 'and fill in the "Commands" section. A command you cannot',
|
|
251
|
+
n1c: "copy and run is not a command, it is a wish.",
|
|
252
|
+
n2a: "Read",
|
|
253
|
+
n2b: "— it is the minimum a project needs,",
|
|
254
|
+
n2c: "independent of language. Go through it top to bottom and mark what is missing.",
|
|
255
|
+
n3a: "Fill in",
|
|
256
|
+
n3b: "— gates, samples, journal. Your AQK level",
|
|
257
|
+
n3c: (cmd) => `is computed from it: ${cmd}.`,
|
|
258
|
+
n4a: "Climb the levels",
|
|
259
|
+
n4b: "one at a time",
|
|
260
|
+
n4c: ". A gate guards an artefact that exists:",
|
|
261
|
+
n4d: "a check for code that is not written yet is a dead rule.",
|
|
262
|
+
burned: (cmd) => `Got burned by something — write it down: ${cmd}`,
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
feedback: {
|
|
266
|
+
title: "If this was useful:",
|
|
267
|
+
star: (url) => `Star it — ${url}`,
|
|
268
|
+
issue: "Found a bug or it did not fit — open an issue; both are the most useful feedback there is.",
|
|
269
|
+
once: "This message is shown once: it will not appear again on this machine.",
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
note: {
|
|
273
|
+
journalTitle: "Bruise journal",
|
|
274
|
+
needTitle: (cmd) => `A title is required: ${cmd}`,
|
|
275
|
+
noJournal: (url) => `No journal clone found.\nDo this once:\n git clone ${url}.git ~/projects/aqk\nor point at it: export AQK_HOME=/path/to/aqk`,
|
|
276
|
+
journalMissing: (path) => `Journal not found: ${path}`,
|
|
277
|
+
emptyBody: `The entry body is empty. Pass it on standard input, for example:\n\n aqk note "title" <<'EOF'\n **What happened.** ...\n **What it cost.** ...\n **Conclusion.** 🔧 ...\n EOF`,
|
|
278
|
+
noOutcome:
|
|
279
|
+
"The entry carries no decision mark. A lesson without a conclusion is a story, not a lesson.\n" +
|
|
280
|
+
"Add one of the three:\n" +
|
|
281
|
+
" ✅ **Became a gate:** <catalogue entry name>\n" +
|
|
282
|
+
" 🔧 **Became a change to the tooling:** <what exactly changed>\n" +
|
|
283
|
+
" 👤 **Will not become a gate:** <why>",
|
|
284
|
+
unknownProject: "unknown",
|
|
285
|
+
projectField: "Project",
|
|
286
|
+
pushed: (title) => `Recorded and pushed: ${title}`,
|
|
287
|
+
localOnly: (cmd) => `Recorded locally, the push failed. Push it: ${cmd}`,
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
blob: {
|
|
291
|
+
noDocs: (dir) => `Guides not found: ${dir}`,
|
|
292
|
+
header: (stamp) =>
|
|
293
|
+
`<!-- ASSEMBLED BY aqk blob ${stamp} from kit/docs. Do not edit by hand:\n` +
|
|
294
|
+
` the next assembly overwrites it. The source is the separate files. -->\n\n` +
|
|
295
|
+
`# AQK — the guides in one file\n`,
|
|
296
|
+
source: (path) => `source: ${path}`,
|
|
297
|
+
done: (n, kb) => `GOD_AI.md — ${n} files, ${kb} KB`,
|
|
298
|
+
rebuilt: "Reassembled by every run. Edit the originals in kit/docs.",
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
start: {
|
|
302
|
+
initFailed: (cmd) => `Could not lay out the kit. Start with ${cmd}`,
|
|
303
|
+
tooManyFiles: (n) => `This repository already has ${n} code files — that is a different scenario.`,
|
|
304
|
+
useDoctor: (cmd) => `${cmd} will inspect what is here and split the entries into three lists:`,
|
|
305
|
+
threeLists: "held by a machine · applicable but not installed · not applicable, and why.",
|
|
306
|
+
anyway: (cmd) => `Install the day-zero guards anyway: ${cmd}`,
|
|
307
|
+
expectRed: "Expect red: a guard installed onto living code goes red across all of it.",
|
|
308
|
+
expectRedFix: (cmd) => `That is cured by a ratchet — ${cmd} — not by switching it off.`,
|
|
309
|
+
installed: (n) => `Day-zero guards installed: ${n}`,
|
|
310
|
+
noDebt1: "There is no debt: on an empty project they have nothing to let through. The same guard,",
|
|
311
|
+
noDebt2: "installed six months later, would go red on all the old code — and be switched off.",
|
|
312
|
+
allDeclared: "Every applicable entry is already declared.",
|
|
313
|
+
notYet: "Not applicable yet:",
|
|
314
|
+
notYetWhy: "Once the sign appears, the entry shows up on its own.",
|
|
315
|
+
orderTitle: "The order people actually do this in:",
|
|
316
|
+
o1: "The task, in words.",
|
|
317
|
+
o1What: "What, and for whom, without a single technical term.",
|
|
318
|
+
o1Why: "Until the task is described in words, any architecture protects who knows what.",
|
|
319
|
+
o2: "Constraints.",
|
|
320
|
+
o2What: "Deadlines, money, load, what you are not allowed to use.",
|
|
321
|
+
o2Why: "Constraints pick the solution far more often than taste does: without them, taste picks.",
|
|
322
|
+
o3: "Sizing.",
|
|
323
|
+
o3What: "How much data, how many requests, how many people — in numbers, at least an order of magnitude.",
|
|
324
|
+
o3Why: 'A number separates "we need a queue" from "a table is enough". Without it, people argue in words.',
|
|
325
|
+
o4: "Architecture.",
|
|
326
|
+
o4What: "And only now — out of the first three, not before them.",
|
|
327
|
+
softNote1: "The program does not check this order: it lives in .aqk/docs/, and an agent can",
|
|
328
|
+
softNote2: "ignore it. The machine holds something else — the guards above. The difference between",
|
|
329
|
+
softNote3: "soft and hard is exactly this: text is asked for, a command is executed.",
|
|
330
|
+
next: "Next:",
|
|
331
|
+
nextWhy: "— run everything that is declared",
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
manifestDoc: {
|
|
335
|
+
head: [
|
|
336
|
+
"# .aqk.yml — the Agent Quality Kit manifest",
|
|
337
|
+
"# What this is: a machine-readable description of how agents live in this repository.",
|
|
338
|
+
"# `aqk doctor` computes the compliance level. An empty field = the level is not reached,",
|
|
339
|
+
"# and that is honest: filling it with placeholders is pointless, files are checked, not words.",
|
|
340
|
+
],
|
|
341
|
+
entry: "# AQK-0 — what the agent reads first.",
|
|
342
|
+
rules: "# AQK-1 — where the standards are and which checks are mandatory.",
|
|
343
|
+
gates: [
|
|
344
|
+
" # name: a command returning 0 or non-zero. An empty declaration protects nothing and is",
|
|
345
|
+
' # rejected by the "a declared gate runs" check — hence examples here, not placeholders.',
|
|
346
|
+
' # lint: "ruff check ."',
|
|
347
|
+
' # test: "pytest -q"',
|
|
348
|
+
" # To install a ready entry from the catalogue together with its samples: aqk add <name>",
|
|
349
|
+
],
|
|
350
|
+
samples: [
|
|
351
|
+
"# AQK-2 — what proves the gates work, and where the debt registries are.",
|
|
352
|
+
"# samples: the directory with red and green samples (a gate must go red on the first and",
|
|
353
|
+
"# stay quiet on the second). ratchets: lists of known violations that may only get",
|
|
354
|
+
"# shorter.",
|
|
355
|
+
],
|
|
356
|
+
lessons: "# AQK-3 — where lessons accumulate. A path or an address.",
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
badge: {
|
|
360
|
+
noManifest: (cmd) => `No .aqk.yml — there is no level yet. Start with ${cmd}`,
|
|
361
|
+
notReached: (cmd) => `AQK-0 is not reached — there is nothing to put on a badge. What is missing: ${cmd}`,
|
|
362
|
+
redGates: (n, names) =>
|
|
363
|
+
`Red gates: ${n} (${names}). A badge issued over a red gate is the author's claim, not a machine's fact.`,
|
|
364
|
+
hint: (n) => `Proven by a run: ${n} gates, all green. Paste the line above into your README.`,
|
|
365
|
+
keepTrue: (cmd) => `To keep the badge from turning into a lie, put this in your pipeline: ${cmd}`,
|
|
366
|
+
checkMissing: (places) => `No AQK badge in any of: ${places}. This is the line to paste:`,
|
|
367
|
+
checkMismatch: (where, level) => `The badge lies: ${where}, while the run says AQK-${level}. Replace it with:`,
|
|
368
|
+
checkOk: (level, where) => `Badge matches the run: AQK-${level} — ${where}`,
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
report2: {
|
|
372
|
+
title: "AQK report",
|
|
373
|
+
noManifest: (cmd) => `No .aqk.yml — nothing to report on. Start with ${cmd}`,
|
|
374
|
+
level: "Level",
|
|
375
|
+
holdsTitle: "What a machine holds (from a run, not from the manifest)",
|
|
376
|
+
nothingRuns: "⬜ no gate is declared",
|
|
377
|
+
native: (prog) => `native recipe: ${prog}`,
|
|
378
|
+
portable: "portable check",
|
|
379
|
+
weakerTitle: "Weaker than it could be",
|
|
380
|
+
weaker: (progs) => `${progs} is available on this system, but the gate uses the portable check — it catches less`,
|
|
381
|
+
missingTitle: "What is missing",
|
|
382
|
+
nothingMissing: "✅ every applicable entry is installed",
|
|
383
|
+
needsTool: (prog) => `needs ${prog} — not on this system`,
|
|
384
|
+
notInstalled: "applicable, but not installed",
|
|
385
|
+
hiddenTitle: "Not applicable to this repository",
|
|
386
|
+
readTitle: "What the kit told you to read",
|
|
387
|
+
readWarn:
|
|
388
|
+
"The mark only means the file is on disk. Whether it was read, the machine does not know " +
|
|
389
|
+
"and does not pretend to: that is answered by whoever is reporting.",
|
|
390
|
+
ignoreTitle: "What .aqkignore hides",
|
|
391
|
+
ignoreNone: "no .aqkignore file — nothing is hidden",
|
|
392
|
+
ignoreWarn:
|
|
393
|
+
"Hiding things silently is the same class as a silent gate: the gates do not look at these " +
|
|
394
|
+
"paths at all. A line here means there is no protection there, and will not be.",
|
|
395
|
+
whyTitle: "Why this matters — briefly",
|
|
396
|
+
whyNothing: "nothing to add: everything applicable is in place",
|
|
397
|
+
saved: (path) => `Saved: ${path}`,
|
|
398
|
+
docs: {
|
|
399
|
+
baseline: "the minimum a project needs, independent of language",
|
|
400
|
+
readyMade: "the map of off-the-shelf rules: look for a ready one before writing your own",
|
|
401
|
+
rulesGeneral: "general working rules",
|
|
402
|
+
rulesTesting: "rules about tests",
|
|
403
|
+
rulesSecurity: "rules about security",
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
levels: [
|
|
408
|
+
{
|
|
409
|
+
title: "a manifest and an entry point",
|
|
410
|
+
need: "create .aqk.yml and point entry at the file an agent reads first (AGENTS.md)",
|
|
411
|
+
gives: "any tool understands what to read in this repository",
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
title: "rules and working gates",
|
|
415
|
+
need: "set rules (the standards directory) and fill at least one gate in gates with a real command",
|
|
416
|
+
gives: "checks are declared as commands, not described in prose",
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
title: "gates are proven, debt is under a ratchet",
|
|
420
|
+
need: "set samples (red and green gate samples) and ratchets (debt registries)",
|
|
421
|
+
gives: "the gate has proven it catches defects and stays quiet on correct code",
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
title: "lessons come back into the work",
|
|
425
|
+
need: "set lessons — the path or address of a journal where every incident yields a conclusion",
|
|
426
|
+
gives: "the project learns: the same bruise is not collected twice",
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
|
|
430
|
+
report: {
|
|
431
|
+
title: "aqk doctor --run",
|
|
432
|
+
version: "version",
|
|
433
|
+
level: "level",
|
|
434
|
+
summary: (ok, all) => `total: ${ok} of ${all} green`,
|
|
435
|
+
},
|
|
436
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// tool/i18n/index.mjs — выбор языка вывода.
|
|
2
|
+
//
|
|
3
|
+
// ЗАЧЕМ. Комплект ставят из npm, площадка англоязычная. Русский вывод отсекает тех, кто мог бы
|
|
4
|
+
// им пользоваться; английский по умолчанию отсекал бы автора. Поэтому язык выбирается, а не
|
|
5
|
+
// назначается.
|
|
6
|
+
//
|
|
7
|
+
// ЧТО ПЕРЕВОДИТСЯ, А ЧТО НЕТ. Переводится то, что печатается человеку. Комментарии в коде
|
|
8
|
+
// остаются русскими: они для того, кто правит программу, а не для того, кто ею пользуется.
|
|
9
|
+
//
|
|
10
|
+
// ПОЧЕМУ КАТАЛОГ, А НЕ СТРОКИ ПО МЕСТУ. Два языка вперемешку в коде команд читаются вдвое
|
|
11
|
+
// хуже и расходятся молча. Каталоги лежат отдельно, и модульная проверка сверяет, что в них
|
|
12
|
+
// одни и те же ключи: «поддерживаем два языка» — утверждение, которое обязана держать машина.
|
|
13
|
+
|
|
14
|
+
import { ru } from "./ru.mjs";
|
|
15
|
+
import { en } from "./en.mjs";
|
|
16
|
+
|
|
17
|
+
// Порядок: явная переменная окружения → системная локаль → английский. Английский последним
|
|
18
|
+
// потому, что незнакомый язык интерфейса — это препятствие, а незнакомый английский в 2026-м
|
|
19
|
+
// препятствие меньшее, чем незнакомая кириллица.
|
|
20
|
+
function pickLang(env = process.env) {
|
|
21
|
+
const forced = String(env.AQK_LANG || "").toLowerCase();
|
|
22
|
+
if (forced.startsWith("ru")) return "ru";
|
|
23
|
+
if (forced.startsWith("en")) return "en";
|
|
24
|
+
const locale = String(env.LC_ALL || env.LC_MESSAGES || env.LANG || "").toLowerCase();
|
|
25
|
+
if (locale.startsWith("ru")) return "ru";
|
|
26
|
+
return "en";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const CATALOGS = { ru, en };
|
|
30
|
+
const LANG = pickLang();
|
|
31
|
+
const L = CATALOGS[LANG];
|
|
32
|
+
|
|
33
|
+
export { L, LANG, pickLang, CATALOGS };
|