agent-quality-kit 0.8.0 → 0.10.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 +154 -12
- package/README.ru.md +185 -27
- package/kit/docs/ai/index.md +1 -0
- package/kit/docs/ai/project-baseline.md +14 -0
- package/kit/docs/api-e2e.md +214 -0
- package/kit/docs/ready-made-rules.md +188 -0
- package/kit/gates/README.md +22 -0
- package/kit/gates/api-contract-has-arbiter/README.md +63 -0
- package/kit/gates/api-contract-has-arbiter/check.sh +117 -0
- package/kit/gates/api-contract-has-arbiter/gate.yml +15 -0
- package/kit/gates/api-contract-has-arbiter/green/.github/workflows/ci.yml +12 -0
- package/kit/gates/api-contract-has-arbiter/green/openapi.yaml +18 -0
- package/kit/gates/api-contract-has-arbiter/red/.github/workflows/ci.yml +11 -0
- package/kit/gates/api-contract-has-arbiter/red/openapi.yaml +18 -0
- package/kit/gates/ci-actually-fails/check.sh +9 -1
- package/kit/gates/color-from-token/check.sh +5 -1
- package/kit/gates/commit-explains-itself/check.sh +15 -0
- package/kit/gates/complexity-limit/red/deep.go +17 -0
- package/kit/gates/complexity-limit/red/deep.rs +17 -0
- package/kit/gates/gate-not-weakened/red/suppress.go +5 -0
- package/kit/gates/gate-not-weakened/red/suppress.rs +3 -0
- package/kit/gates/lesson-has-outcome/check.sh +5 -1
- package/kit/gates/mcp-server-resolves/README.md +62 -0
- package/kit/gates/mcp-server-resolves/check.sh +110 -0
- package/kit/gates/mcp-server-resolves/gate.yml +18 -0
- package/kit/gates/mcp-server-resolves/green/.mcp.json +20 -0
- package/kit/gates/mcp-server-resolves/red/.mcp.json +16 -0
- package/kit/gates/protection-not-removed/README.md +67 -0
- package/kit/gates/protection-not-removed/check.sh +92 -0
- package/kit/gates/protection-not-removed/gate.yml +10 -0
- package/kit/gates/protection-not-removed/green/.aqk.yml +7 -0
- package/kit/gates/protection-not-removed/green/gates-declared.txt +4 -0
- package/kit/gates/protection-not-removed/red/.aqk.yml +7 -0
- package/kit/gates/protection-not-removed/red/gates-declared.txt +4 -0
- package/kit/gates/secrets-not-in-code/red/leak.go +9 -0
- package/kit/gates/secrets-not-in-code/red/leak.rs +5 -0
- package/kit/gates/todo-without-task/red/later.go +6 -0
- package/kit/gates/todo-without-task/red/later.rs +4 -0
- package/llms.txt +25 -4
- package/package.json +3 -6
- package/tool/commands/context.mjs +37 -6
- package/tool/commands/doctor.mjs +139 -16
- package/tool/commands/probe.mjs +228 -0
- package/tool/commands/project.mjs +18 -2
- package/tool/commands/prove.mjs +1 -0
- package/tool/commands/vitals.mjs +167 -0
- package/tool/i18n/en-docs.mjs +48 -0
- package/tool/i18n/en-gates.mjs +309 -0
- package/tool/i18n/en.mjs +26 -279
- package/tool/i18n/index.mjs +36 -3
- package/tool/i18n/ru-docs.mjs +48 -0
- package/tool/i18n/ru-gates.mjs +311 -0
- package/tool/i18n/ru.mjs +26 -278
- package/tool/lib/banner.mjs +59 -0
- package/tool/lib/brief.mjs +192 -0
- package/tool/lib/cadence.mjs +57 -0
- package/tool/lib/core.mjs +3 -0
- package/tool/lib/history.mjs +82 -0
- package/tool/lib/manifest.mjs +146 -15
- package/tool/lib/prove.mjs +11 -1
- package/tool/lib/repo.mjs +43 -3
- package/tool/program.mjs +33 -0
- package/tool/selfcheck/smoke/_fixture.mjs +89 -0
- package/tool/selfcheck/smoke/api-contract.test.mjs +79 -0
- package/tool/selfcheck/smoke/commit-report.test.mjs +47 -0
- package/tool/selfcheck/smoke/verdict.test.mjs +40 -0
- package/tool/selfcheck/smoke.sh +528 -6
- package/tool/selfcheck/units-banner.mjs +65 -0
- package/tool/selfcheck/units-brief.mjs +97 -0
- package/tool/selfcheck/units-cadence.mjs +69 -0
- package/tool/selfcheck/units-context.mjs +3 -1
- package/tool/selfcheck/units-level.mjs +147 -1
- package/tool/selfcheck/units-probe.mjs +100 -0
- package/tool/selfcheck/units-repo.mjs +164 -0
- package/tool/selfcheck/units-vitals.mjs +81 -0
- package/tool/selfcheck/units.mjs +3 -75
package/tool/i18n/en.mjs
CHANGED
|
@@ -7,6 +7,8 @@ import { enDocs } from "./en-docs.mjs";
|
|
|
7
7
|
|
|
8
8
|
import { templates } from "./templates-en.mjs";
|
|
9
9
|
|
|
10
|
+
import { enGates } from "./en-gates.mjs";
|
|
11
|
+
|
|
10
12
|
export const en = {
|
|
11
13
|
learn: {
|
|
12
14
|
title: "Said out loud, never written down",
|
|
@@ -28,6 +30,7 @@ export const en = {
|
|
|
28
30
|
init: "lay the rules and guides into the current project",
|
|
29
31
|
initForce: "overwrite files that already exist",
|
|
30
32
|
start: "no code yet: day-zero guards and the order of work",
|
|
33
|
+
probe: "what the declared checks cannot see: plant a defect into files history calls hot",
|
|
31
34
|
prove: "prove the gates catch a defect: each against its own red and green sample",
|
|
32
35
|
doctor: "check what is laid out and what is missing",
|
|
33
36
|
doctorRun: "and also run the declared gates",
|
|
@@ -44,6 +47,7 @@ export const en = {
|
|
|
44
47
|
contextInstall: "the same in full — the map and the rulebook — installed as a hook",
|
|
45
48
|
report: "the mandatory report form: what is in place, what is not, what was not read; --since <ref> adds what proves the diff",
|
|
46
49
|
badge: "a level badge for your README — and a check that it does not lie",
|
|
50
|
+
version: "the banner and the version number — same as --version and -v",
|
|
47
51
|
noInstall: "Without installing: npx agent-quality-kit init",
|
|
48
52
|
language: "Output language: AQK_LANG=ru (or en), otherwise your system locale",
|
|
49
53
|
},
|
|
@@ -87,6 +91,16 @@ export const en = {
|
|
|
87
91
|
hasThings: "has",
|
|
88
92
|
install: (cmd) => `install: ${cmd}`,
|
|
89
93
|
notApplicable: (n) => `Not applicable to this repository (${n}):`,
|
|
94
|
+
noBrowserServer:
|
|
95
|
+
"this project has a UI, and the agent has no browser: it cannot look at its own change\n and judges the work by what compiled. Those are different claims.",
|
|
96
|
+
noBrowserServerHow: (l) => `add an MCP server — works with any agent: ${l}`,
|
|
97
|
+
coveredBy: (n) => `held by another arbiter: ${n} — the portable entry is not needed`,
|
|
98
|
+
coveredByGate: (g) => `held by "${g}", declared in the manifest`,
|
|
99
|
+
coversUnknown: (l) => `covers names a gate absent from gates: ${l} — those entries are held by nothing`,
|
|
100
|
+
coversUnproven: (e, g, codes) =>
|
|
101
|
+
`claim unverified: "${e}" is declared held by gate "${g}", but neither its command nor the\n linter config names rules ${codes} — the entry may be held by nothing`,
|
|
102
|
+
coversUnprovenHow: (cmd) => `settle it: add those rules to the linter, or install the entry — ${cmd}`,
|
|
103
|
+
totalCovered: (n) => `held by another arbiter ${n}`,
|
|
90
104
|
total: "Total:",
|
|
91
105
|
totalHeld: (n) => `held by a machine ${n}`,
|
|
92
106
|
totalTodo: (n) => `applicable but not installed ${n}`,
|
|
@@ -96,6 +110,7 @@ export const en = {
|
|
|
96
110
|
sinceBadRef: (ref) => `cannot compare against "${ref}": no such ref, or this is not a git repository`,
|
|
97
111
|
notScopable: "output carries no paths — cannot be narrowed by diff, left red",
|
|
98
112
|
outsideDiff: (n) => `findings exist, but outside the diff (${n})`,
|
|
113
|
+
advisoryQuiet: "(advisory — cannot fail the run)",
|
|
99
114
|
advisoryMark: "advisory — shown, the run was not failed",
|
|
100
115
|
advisorySummary: (names) =>
|
|
101
116
|
`advisory and red: ${names.join(", ")}. These are switched-off checks: ` +
|
|
@@ -110,7 +125,15 @@ export const en = {
|
|
|
110
125
|
manifestUnknown: (keys) =>
|
|
111
126
|
`The manifest has fields the standard does not know: ${keys.join(", ")}. Looks like a typo — ` +
|
|
112
127
|
`such a field is silently read as absent, and the verdict comes out wrong.`,
|
|
128
|
+
manifestUnparsed: (n, t) => `manifest line ${n} was not parsed and HAS NO EFFECT: ${t}`,
|
|
129
|
+
manifestUnparsedWhy:
|
|
130
|
+
"field and gate names use latin letters, digits, dash and underscore. What is declared here\n does not run at all — while looking as if it does.",
|
|
113
131
|
manifestKnown: (keys) => `Manifest fields: ${keys.join(", ")}`,
|
|
132
|
+
runVerdictOk: "Everything declared is green.",
|
|
133
|
+
runVerdictFail: (why) => `The run is red: ${why}.`,
|
|
134
|
+
whyMissing: "files from the header are missing",
|
|
135
|
+
whyLevel: "the level was not reached",
|
|
136
|
+
whyGates: (n, names) => `${n} red gate(s)${names ? ` (${names})` : ""}`,
|
|
114
137
|
thresholdPass: (min) => `Threshold AQK-${min} passed.`,
|
|
115
138
|
thresholdFail: (min, now) => `Threshold AQK-${min} NOT passed: currently AQK-${now}.`,
|
|
116
139
|
thresholdGateFail: (min, now, names) =>
|
|
@@ -164,6 +187,8 @@ export const en = {
|
|
|
164
187
|
has_agent_config: ["the agent was never configured here", "agent settings exist"],
|
|
165
188
|
has_agent_entry: ["no entry point for an agent here", "an entry point for an agent exists"],
|
|
166
189
|
has_ui: ["no stylesheets or UI components in sight", "a UI exists: stylesheets or components"],
|
|
190
|
+
has_mcp: ["no MCP tools are wired up for the agent here", "MCP servers are declared"],
|
|
191
|
+
has_api_spec: ["no API specification in sight", "an API specification exists"],
|
|
167
192
|
},
|
|
168
193
|
},
|
|
169
194
|
|
|
@@ -189,283 +214,5 @@ export const en = {
|
|
|
189
214
|
alreadyDeclared: "already declared",
|
|
190
215
|
},
|
|
191
216
|
|
|
192
|
-
|
|
193
|
-
noSuchGate: (slug, cmd) => `No such gate: ${slug}\nThe applicable ones — ${cmd}`,
|
|
194
|
-
toolMissing: (slug, missing) =>
|
|
195
|
-
`Entry ${slug} needs ${missing.join(" and ")}, which is not on this machine.\n fix: install ${missing.join(" and ")} and try again — this entry delegates to a ready-made tool by design and has no check of its own.`,
|
|
196
|
-
noRecipe: (slug, stack, missing) =>
|
|
197
|
-
`Entry ${slug} has no command for ${stack} and no portable one.` +
|
|
198
|
-
(Array.isArray(missing) && missing.length
|
|
199
|
-
? `\n fix: install ${missing.join(" or ")} and try again — this entry delegates to a ready-made tool by design and has no check of its own.`
|
|
200
|
-
: `\n fix: add a recipe for your stack to this entry's gate.yml, or pick another entry.`),
|
|
201
|
-
thisStack: "this stack",
|
|
202
|
-
needName: (cmd, doctor) => `Name the gate: ${cmd}. The list — ${doctor}`,
|
|
203
|
-
noManifest: (cmd) => `No .aqk.yml — run ${cmd} first`,
|
|
204
|
-
notApplicable: (why) => `This gate does not apply to the repository: ${why}`,
|
|
205
|
-
installAnyway: "Installing anyway — your call, but it has nothing to guard here.",
|
|
206
|
-
copied: (n) => `${n} files: the check and its samples`,
|
|
207
|
-
declared: (cmd) => `gate declared: ${cmd}`,
|
|
208
|
-
notDeclared: (why, slug, cmd) => `untouched (${why}). Add it yourself: ${slug}: "${cmd}"`,
|
|
209
|
-
nextTitle: "Next:",
|
|
210
|
-
next1: "Check that it goes red and stays quiet where it should:",
|
|
211
|
-
expectFail: "→ a failure is expected",
|
|
212
|
-
expectSilence: "→ silence is expected",
|
|
213
|
-
next2: "Put the command into your commit hook and your pipeline.",
|
|
214
|
-
next2Why: "A gate nobody runs is not a gate.",
|
|
215
|
-
next3: (cmd) => `Run everything declared: ${cmd}`,
|
|
216
|
-
},
|
|
217
|
-
|
|
218
|
-
gnew: {
|
|
219
|
-
needName: (cmd) => `Name it: ${cmd}`,
|
|
220
|
-
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.`,
|
|
221
|
-
looksExisting: (slug) => `Looks like this already exists: ${slug}`,
|
|
222
|
-
recipeNotGate: "A recipe for another stack is a line in the recipes of an existing entry.",
|
|
223
|
-
forceHint: (cmd) => `Create a new one anyway: ${cmd}`,
|
|
224
|
-
exists: (path) => `${path} already exists.`,
|
|
225
|
-
nextTitle: "Next, in order:",
|
|
226
|
-
n1: "Check whether an off-the-shelf rule exists",
|
|
227
|
-
n1Where: "in ruff, eslint, semgrep.",
|
|
228
|
-
n1Why: "Off-the-shelf is more precise, better documented and maintained without you. Your own check is the fallback.",
|
|
229
|
-
n2: "Add the samples.",
|
|
230
|
-
n2Red: "— code the check must fire on.",
|
|
231
|
-
n2Green: "— the same code, done right.",
|
|
232
|
-
n2Why: "The green one matters more: it catches a check that goes red on correct code.",
|
|
233
|
-
n3: "Write the check",
|
|
234
|
-
n3Where: "in check.sh. The failure text must say what exactly to do.",
|
|
235
|
-
n4: "Fill in gate.yml:",
|
|
236
|
-
n4What: "the intent, the trigger, the failure it proved itself on.",
|
|
237
|
-
n5: "Run it:",
|
|
238
|
-
n5Why: "The arbiter must go red on red/ and stay quiet on green/. It did not — it is not an entry.",
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
ratchet: {
|
|
242
|
-
needName: (cmd) => `Name the gate: ${cmd}. It must already be declared in .aqk.yml`,
|
|
243
|
-
noManifest: (cmd) => `No .aqk.yml — run ${cmd} first`,
|
|
244
|
-
notDeclared: (slug, cmd) => `Gate "${slug}" is not declared in .aqk.yml. First: ${cmd}`,
|
|
245
|
-
already: (slug) => `Gate "${slug}" already has a ratchet.`,
|
|
246
|
-
notRunnable: (slug, cmd) =>
|
|
247
|
-
`Gate "${slug}" does not run: ${cmd}\n` +
|
|
248
|
-
`You cannot capture debt from a guard that does not exist — its own error messages\n` +
|
|
249
|
-
`would land in the registry and become a permission. Fix the command first.`,
|
|
250
|
-
registryHead: (slug, stamp) =>
|
|
251
|
-
`# Debt registry: ${slug}\n` +
|
|
252
|
-
`# Captured ${stamp}. This list may ONLY get shorter.\n` +
|
|
253
|
-
`# A new violation turns the gate red; a fixed one is struck out automatically.\n` +
|
|
254
|
-
`#\n` +
|
|
255
|
-
`# Debt with no goal and no deadline never ends. The goal is how many violations count as\n` +
|
|
256
|
-
`# paid off; on reaching it the ratchet tells you to remove the wrapper. The deadline is\n` +
|
|
257
|
-
`# optional, but if set, debt still open past that date turns the gate red — a deadline\n` +
|
|
258
|
-
`# without a consequence is not a deadline.\n` +
|
|
259
|
-
`# aqk-goal: 0\n` +
|
|
260
|
-
`# aqk-deadline:\n`,
|
|
261
|
-
recorded: (n) => `${n} violations recorded as debt`,
|
|
262
|
-
libCopied: "wrapper copied into the project",
|
|
263
|
-
wrapped: "command wrapped in the ratchet",
|
|
264
|
-
changesTitle: "What this changes:",
|
|
265
|
-
fromToday: "from the day it is installed",
|
|
266
|
-
changes1: (fromToday) => `The rule applies ${fromToday}. The old code stays untouched, but a new`,
|
|
267
|
-
changes2: "violation of the same class will not get through.",
|
|
268
|
-
test1: 'The test for a ratchet rather than an advisor: "can new code add a violation',
|
|
269
|
-
test2: 'and still pass?" If it can, there is no gate.',
|
|
270
|
-
run: (cmd) => `Run it: ${cmd}`,
|
|
271
|
-
},
|
|
272
|
-
|
|
273
|
-
find: {
|
|
274
|
-
needQuery: (cmd) => `Describe the intent in words: ${cmd}`,
|
|
275
|
-
example: 'find "debug printing reaches production"',
|
|
276
|
-
exists: "This already exists — no new entry needed:",
|
|
277
|
-
match: (pct) => `${pct}% match`,
|
|
278
|
-
recipes: (langs) => `recipes: ${langs}portable`,
|
|
279
|
-
existsWhy1: "If you have a recipe for another stack, that is a line in the recipes of the",
|
|
280
|
-
existsWhy2: "existing entry, not a new gate. One intent, many possible executors.",
|
|
281
|
-
near: "No exact match, but these are close:",
|
|
282
|
-
nearWhy: "Read their README. If the intent is the same — extend it, do not start a new one.",
|
|
283
|
-
none: "No such intent in the catalogue.",
|
|
284
|
-
journal: "The journal has a bruise on this topic:",
|
|
285
|
-
journalWhy: "A recorded bruise means the proof for a new entry already exists.",
|
|
286
|
-
howTitle: "How to add your own gate:",
|
|
287
|
-
how1: "Name the failure.",
|
|
288
|
-
how1What: "What concrete defect it caught in a live project, and what that cost.",
|
|
289
|
-
how1Why: '"It is a good practice" is not accepted: that is how a catalogue collects hundreds of entries and dies.',
|
|
290
|
-
how2: "Create the folder",
|
|
291
|
-
how2What: "— gate.yml, red/, green/, README.md.",
|
|
292
|
-
how2Why: "The full entry format is in kit/gates/README.md",
|
|
293
|
-
how3: "Check it by machine:",
|
|
294
|
-
how3Why: "The arbiter must go red on red/ and stay quiet on green/. It did not — it is not an entry.",
|
|
295
|
-
how4: "Send it as a change",
|
|
296
|
-
how4What: "to the kit repository.",
|
|
297
|
-
},
|
|
298
|
-
|
|
299
|
-
why: {
|
|
300
|
-
needQuery: (cmd) => `Describe what slipped through: ${cmd}`,
|
|
301
|
-
example: 'why "a file grew to nine thousand lines"',
|
|
302
|
-
ciAtOnce: "all at once: doctor --run",
|
|
303
|
-
ciOwnStep: "as its own step",
|
|
304
|
-
unsure: "No confident match. These entries look close:",
|
|
305
|
-
unsureByName: (cmd) => `Name the entry directly: ${cmd}`,
|
|
306
|
-
unsureNone: (cmd) => `None of them fits — then there was no guard: ${cmd}`,
|
|
307
|
-
decideTitle: "The rest is your call:",
|
|
308
|
-
decideQ: "is this specific to you, or a general case?",
|
|
309
|
-
decideWhy: "General goes into the catalogue and reaches everyone. Specific stays with you.",
|
|
310
|
-
decideNote: (cmd) => `Either way, the lesson goes into the shared journal: ${cmd}`,
|
|
311
|
-
fix: "Fix it like this:",
|
|
312
|
-
noGuard: "There was no guard.",
|
|
313
|
-
noGuardWhy: "No entry in the catalogue carries this intent.",
|
|
314
|
-
noGuardFix: (cmd) => `create an entry — ${cmd}`,
|
|
315
|
-
noGuardHint: "Take the red sample straight from this failure: it already happened, nothing to invent.",
|
|
316
|
-
closest: (slug, pct) => `Closest catalogue entry: ${slug} ${pct}% match`,
|
|
317
|
-
notInstalled: "The guard exists in the catalogue but is not installed in this project.",
|
|
318
|
-
notInstalledHint1: "It will go red on old code — that is normal: the old is covered by a ratchet,",
|
|
319
|
-
notInstalledHint2: (cmd) => `the new is caught from day one. ${cmd}`,
|
|
320
|
-
declaredAs: (cmd) => `Declared as: ${cmd}`,
|
|
321
|
-
notRunning: "The guard is declared but does not run.",
|
|
322
|
-
notRunningWhy: "The worst case: silence reads as success.",
|
|
323
|
-
notRunningFix: "the path or the program in the command does not exist — check them.",
|
|
324
|
-
notRunningHint: 'A missing signal is indistinguishable from success, so this is not "a small config detail".',
|
|
325
|
-
bypassed: "The guard exists and catches this failure — so it was bypassed.",
|
|
326
|
-
noCiFix: "there is no pipeline. A check only a human runs",
|
|
327
|
-
noCiHint: 'works right up until the first "forgot".',
|
|
328
|
-
notInCiFix: "a pipeline exists, but this gate does not run in it.",
|
|
329
|
-
notInCiHint: (cmd) => `The cheapest way is one step: ${cmd} — it runs everything declared.`,
|
|
330
|
-
inCiFix: (how) => `the pipeline does run it (${how}) — so a red run`,
|
|
331
|
-
inCiHint1: "was skipped or overridden. Move the rule out of prose and into mechanics:",
|
|
332
|
-
inCiHint2: "a blocking step, not an optional one; no merging while red.",
|
|
333
|
-
blind: "The guard exists, is installed and runs — but does not see this failure.",
|
|
334
|
-
blindFix: (dir) => `put a piece of the broken code into ${dir}`,
|
|
335
|
-
blindHint1: "and drive the check to red on it. The order is the reverse of the habit: the sample",
|
|
336
|
-
blindHint2: "first, the fix second — otherwise it is unclear what exactly got fixed.",
|
|
337
|
-
blindCheck: "Verify after the fix: bash tool/selfcheck/gates.sh",
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
init: {
|
|
341
|
-
noDocs: (dir) => `Guides not found: ${dir}\nLooks like the package is not fully installed.`,
|
|
342
|
-
docsRu:
|
|
343
|
-
"the guides in .aqk/docs/ are in Russian — a deliberate decision, not a broken install.\n The rules in .aqk/rules/ are in English; the guides are prose an agent may ignore anyway,\n and what a machine holds lives in .aqk.yml and the gates. Translation waits for someone who needs it.",
|
|
344
|
-
created: (n) => `created (${n}):`,
|
|
345
|
-
andMore: (n) => `… and ${n} more`,
|
|
346
|
-
kept: (n) => `already there, left untouched (${n}):`,
|
|
347
|
-
overwrite: (cmd) => `overwrite: ${cmd}`,
|
|
348
|
-
nextTitle: "What to do next, in order:",
|
|
349
|
-
n1a: "Open",
|
|
350
|
-
n1b: 'and fill in the "Commands" section. A command you cannot',
|
|
351
|
-
n1c: "copy and run is not a command, it is a wish.",
|
|
352
|
-
n2a: "Read",
|
|
353
|
-
n2b: "— it is the minimum a project needs,",
|
|
354
|
-
n2c: "independent of language. Go through it top to bottom and mark what is missing.",
|
|
355
|
-
n3a: "Fill in",
|
|
356
|
-
n3b: "— gates, samples, journal. Your AQK level",
|
|
357
|
-
n3c: (cmd) => `is computed from it: ${cmd}.`,
|
|
358
|
-
n4a: "Climb the levels",
|
|
359
|
-
n4b: "one at a time",
|
|
360
|
-
n4c: ". A gate guards an artefact that exists:",
|
|
361
|
-
n4d: "a check for code that is not written yet is a dead rule.",
|
|
362
|
-
burned: (cmd) => `Got burned by something — write it down: ${cmd}`,
|
|
363
|
-
hookHint: (cmd) => `Claude Code: ${cmd} — the project state will reach the agent's context\n by itself, before its first action. Other agents have no hook, and that is not an omission.`,
|
|
364
|
-
},
|
|
365
|
-
|
|
366
|
-
feedback: {
|
|
367
|
-
title: "If this was useful:",
|
|
368
|
-
star: (url) => `Star it — ${url}`,
|
|
369
|
-
issue: "Found a bug or it did not fit — open an issue; both are the most useful feedback there is.",
|
|
370
|
-
once: "This message is shown once: it will not appear again on this machine.",
|
|
371
|
-
},
|
|
372
|
-
|
|
373
|
-
note: {
|
|
374
|
-
journalTitle: "Bruise journal",
|
|
375
|
-
needTitle: (cmd) => `A title is required: ${cmd}`,
|
|
376
|
-
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`,
|
|
377
|
-
journalMissing: (path) => `Journal not found: ${path}`,
|
|
378
|
-
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`,
|
|
379
|
-
noOutcome:
|
|
380
|
-
"The entry carries no decision mark. A lesson without a conclusion is a story, not a lesson.\n" +
|
|
381
|
-
"Add one of the three:\n" +
|
|
382
|
-
" ✅ **Became a gate:** <catalogue entry name>\n" +
|
|
383
|
-
" 🔧 **Became a change to the tooling:** <what exactly changed>\n" +
|
|
384
|
-
" 👤 **Will not become a gate:** <why>",
|
|
385
|
-
unknownProject: "unknown",
|
|
386
|
-
projectField: "Project",
|
|
387
|
-
pushed: (title) => `Recorded and pushed: ${title}`,
|
|
388
|
-
localOnly: (cmd) => `Recorded locally, the push failed. Push it: ${cmd}`,
|
|
389
|
-
},
|
|
390
|
-
|
|
391
|
-
blob: {
|
|
392
|
-
noDocs: (dir) => `Guides not found: ${dir}`,
|
|
393
|
-
header: (stamp) =>
|
|
394
|
-
`<!-- ASSEMBLED BY aqk blob ${stamp} from kit/docs. Do not edit by hand:\n` +
|
|
395
|
-
` the next assembly overwrites it. The source is the separate files. -->\n\n` +
|
|
396
|
-
`# AQK — the guides in one file\n`,
|
|
397
|
-
source: (path) => `source: ${path}`,
|
|
398
|
-
done: (n, kb) => `GOD_AI.md — ${n} files, ${kb} KB`,
|
|
399
|
-
rebuilt: "Reassembled by every run. Edit the originals in kit/docs.",
|
|
400
|
-
},
|
|
401
|
-
|
|
402
|
-
start: {
|
|
403
|
-
noRecipeHere: "needs a tool that is not on this machine",
|
|
404
|
-
initFailed: (cmd) => `Could not lay out the kit. Start with ${cmd}`,
|
|
405
|
-
tooManyFiles: (n) => `This repository already has ${n} code files — that is a different scenario.`,
|
|
406
|
-
useDoctor: (cmd) => `${cmd} will inspect what is here and split the entries into three lists:`,
|
|
407
|
-
threeLists: "held by a machine · applicable but not installed · not applicable, and why.",
|
|
408
|
-
anyway: (cmd) => `Install the day-zero guards anyway: ${cmd}`,
|
|
409
|
-
expectRed: "Expect red: a guard installed onto living code goes red across all of it.",
|
|
410
|
-
expectRedFix: (cmd) => `That is cured by a ratchet — ${cmd} — not by switching it off.`,
|
|
411
|
-
installed: (n) => `Day-zero guards installed: ${n}`,
|
|
412
|
-
noDebt1: "There is no debt: on an empty project they have nothing to let through. The same guard,",
|
|
413
|
-
noDebt2: "installed six months later, would go red on all the old code — and be switched off.",
|
|
414
|
-
allDeclared: "Every applicable entry is already declared.",
|
|
415
|
-
notYet: "Not applicable yet:",
|
|
416
|
-
notYetWhy: "Once the sign appears, the entry shows up on its own.",
|
|
417
|
-
orderTitle: "The order people actually do this in:",
|
|
418
|
-
o1: "The task, in words.",
|
|
419
|
-
o1What: "What, and for whom, without a single technical term.",
|
|
420
|
-
o1Why: "Until the task is described in words, any architecture protects who knows what.",
|
|
421
|
-
o2: "Constraints.",
|
|
422
|
-
o2What: "Deadlines, money, load, what you are not allowed to use.",
|
|
423
|
-
o2Why: "Constraints pick the solution far more often than taste does: without them, taste picks.",
|
|
424
|
-
o3: "Sizing.",
|
|
425
|
-
o3What: "How much data, how many requests, how many people — in numbers, at least an order of magnitude.",
|
|
426
|
-
o3Why: 'A number separates "we need a queue" from "a table is enough". Without it, people argue in words.',
|
|
427
|
-
o4: "Architecture.",
|
|
428
|
-
o4What: "And only now — out of the first three, not before them.",
|
|
429
|
-
softNote1: "The program does not check this order: it lives in .aqk/docs/, and an agent can",
|
|
430
|
-
softNote2: "ignore it. The machine holds something else — the guards above. The difference between",
|
|
431
|
-
softNote3: "soft and hard is exactly this: text is asked for, a command is executed.",
|
|
432
|
-
next: "Next:",
|
|
433
|
-
nextWhy: "— run everything that is declared",
|
|
434
|
-
},
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
prove: {
|
|
438
|
-
title: "aqk prove — proving the gates",
|
|
439
|
-
running: "proving the gates against their samples…",
|
|
440
|
-
proven: (n) => `proven: ${n}`,
|
|
441
|
-
broken: (n) => `do not catch: ${n}`,
|
|
442
|
-
unprovable: (n) => `nothing to prove with: ${n}`,
|
|
443
|
-
okRed: "red on the red sample, silent on the green one",
|
|
444
|
-
redPassed: "stayed silent on the RED sample — the gate does not catch the defect",
|
|
445
|
-
greenFailed: "went red on the GREEN sample — the gate complains about working code",
|
|
446
|
-
empty: "the command is empty — a declaration without a command protects nothing",
|
|
447
|
-
noSamples: "no samples — nothing to prove with",
|
|
448
|
-
noTarget: "no place to substitute the sample directory — the command was written by hand",
|
|
449
|
-
otherRecipe: (lang) => `the samples are written for the "${lang}" recipe, another one is installed — nothing to prove with`,
|
|
450
|
-
noGates: "no gates declared — nothing to prove",
|
|
451
|
-
noSamplesDir: "the samples field in .aqk.yml is empty — nowhere to look for samples",
|
|
452
|
-
nothingProven:
|
|
453
|
-
"not a single gate is proven. A level above AQK-1 would mean trust in the author, not a fact:\n a project whose gate is `true` would pass it exactly like a project with real protection.",
|
|
454
|
-
fix: (cmd) => `fix: install a catalogue entry together with its samples — ${cmd}`,
|
|
455
|
-
heading: "Proving the gates",
|
|
456
|
-
},
|
|
457
|
-
badge: {
|
|
458
|
-
noManifest: (cmd) => `No .aqk.yml — there is no level yet. Start with ${cmd}`,
|
|
459
|
-
notReached: (cmd) => `AQK-0 is not reached — there is nothing to put on a badge. What is missing: ${cmd}`,
|
|
460
|
-
redGates: (n, names) =>
|
|
461
|
-
`Red gates: ${n} (${names}). A badge issued over a red gate is the author's claim, not a machine's fact.`,
|
|
462
|
-
hint: (n) => `Proven by a run. Green gates: ${n}. Paste the line above into your README.`,
|
|
463
|
-
keepTrue: (cmd) => `To keep the badge from turning into a lie, put this in your pipeline: ${cmd}`,
|
|
464
|
-
checkMissing: (places) => `No AQK badge in any of: ${places}. This is the line to paste:`,
|
|
465
|
-
checkMismatch: (where, level) => `The badge lies: ${where}, while the run says AQK-${level}. Replace it with:`,
|
|
466
|
-
checkOk: (level, where) => `Badge matches the run: AQK-${level} — ${where}`,
|
|
467
|
-
},
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
217
|
+
...enGates,
|
|
471
218
|
};
|
package/tool/i18n/index.mjs
CHANGED
|
@@ -11,23 +11,56 @@
|
|
|
11
11
|
// хуже и расходятся молча. Каталоги лежат отдельно, и модульная проверка сверяет, что в них
|
|
12
12
|
// одни и те же ключи: «поддерживаем два языка» — утверждение, которое обязана держать машина.
|
|
13
13
|
|
|
14
|
+
import { readFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
14
16
|
import { ru } from "./ru.mjs";
|
|
15
17
|
import { en } from "./en.mjs";
|
|
16
18
|
|
|
17
19
|
// Порядок: явная переменная окружения → системная локаль → английский. Английский последним
|
|
18
20
|
// потому, что незнакомый язык интерфейса — это препятствие, а незнакомый английский в 2026-м
|
|
19
21
|
// препятствие меньшее, чем незнакомая кириллица.
|
|
20
|
-
|
|
22
|
+
// Порядок: переменная окружения → ПОЛЕ МАНИФЕСТА → системная локаль → английский.
|
|
23
|
+
//
|
|
24
|
+
// Поле манифеста добавлено 2026-09-08 по просьбе первого чужого пользователя: «язык берётся из
|
|
25
|
+
// LC_ALL/LANG, а на Windows их просто нет: русский проект получает английский вывод. AQK_LANG=ru
|
|
26
|
+
// чинит, но у следующего человека будет своё». Он прав: язык репозитория — свойство репозитория,
|
|
27
|
+
// а локаль — свойство машины, на которой его сегодня открыли.
|
|
28
|
+
//
|
|
29
|
+
// Переменная окружения оставлена ВЫШЕ манифеста намеренно: человек, набравший AQK_LANG=en
|
|
30
|
+
// руками, хочет английский именно сейчас, и спорить с ним манифестом значит отнять у него
|
|
31
|
+
// последнее средство. Манифест выше локали — он про проект, локаль про машину.
|
|
32
|
+
function pickLang(env = process.env, man = null) {
|
|
21
33
|
const forced = String(env.AQK_LANG || "").toLowerCase();
|
|
22
34
|
if (forced.startsWith("ru")) return "ru";
|
|
23
35
|
if (forced.startsWith("en")) return "en";
|
|
36
|
+
const declared = String(man?.lang || "").toLowerCase();
|
|
37
|
+
if (declared.startsWith("ru")) return "ru";
|
|
38
|
+
if (declared.startsWith("en")) return "en";
|
|
24
39
|
const locale = String(env.LC_ALL || env.LC_MESSAGES || env.LANG || "").toLowerCase();
|
|
25
40
|
if (locale.startsWith("ru")) return "ru";
|
|
26
41
|
return "en";
|
|
27
42
|
}
|
|
28
43
|
|
|
44
|
+
// Язык читается СИНХРОННО и одним полем: каталог строк нужен раньше, чем что-либо успеет
|
|
45
|
+
// прочитать манифест целиком, а полный разбор отсюда звать нельзя — manifest.mjs сам берёт
|
|
46
|
+
// строки здесь, и вышло бы кольцо. Чтобы этот сокращённый разбор не разошёлся с настоящим,
|
|
47
|
+
// модульная проверка сверяет их ответы на одном и том же тексте.
|
|
48
|
+
function langFromText(text) {
|
|
49
|
+
const m = String(text).match(/^lang:[ \t]*["']?([A-Za-z][A-Za-z-]*)/m);
|
|
50
|
+
return m ? m[1] : "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function manifestLang(cwd = process.cwd()) {
|
|
54
|
+
try {
|
|
55
|
+
return { lang: langFromText(readFileSync(join(cwd, ".aqk.yml"), "utf8")) };
|
|
56
|
+
} catch {
|
|
57
|
+
// Манифеста нет или он нечитаем — не повод падать: язык просто выберется дальше по порядку.
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
29
62
|
const CATALOGS = { ru, en };
|
|
30
|
-
const LANG = pickLang();
|
|
63
|
+
const LANG = pickLang(process.env, manifestLang());
|
|
31
64
|
const L = CATALOGS[LANG];
|
|
32
65
|
|
|
33
|
-
export { L, LANG, pickLang, CATALOGS };
|
|
66
|
+
export { L, LANG, pickLang, langFromText, CATALOGS };
|
package/tool/i18n/ru-docs.mjs
CHANGED
|
@@ -7,6 +7,47 @@
|
|
|
7
7
|
// вторые — он же в терминале, одну секунду.
|
|
8
8
|
|
|
9
9
|
const ruDocs = {
|
|
10
|
+
// Блок vitals переехал сюда 2026-09-09 по той же причине, что и brief: терминальный
|
|
11
|
+
// каталог снова перерос 500 строк, и поймал это наш же file-size-limit.
|
|
12
|
+
vitals: {
|
|
13
|
+
title: "Подключено ли то, чем комплект работает",
|
|
14
|
+
names: {
|
|
15
|
+
tools: "инструменты гейтов",
|
|
16
|
+
manifest: "манифест разобран",
|
|
17
|
+
preCommit: "хук pre-commit",
|
|
18
|
+
sessionHook: "состояние агенту",
|
|
19
|
+
version: "версия",
|
|
20
|
+
vitals: "подключено ли то, чем комплект работает: инструменты, хуки, свежесть",
|
|
21
|
+
},
|
|
22
|
+
toolsOk: (n) => `все ${n} на месте`,
|
|
23
|
+
toolsMissing: (l) => `НЕ НАЙДЕНЫ: ${l} — эти гейты не состоятся`,
|
|
24
|
+
manifestOk: "разобран целиком",
|
|
25
|
+
manifestBad: (n) => `${n} строк не разобраны и НЕ ДЕЙСТВУЮТ — подробности в doctor`,
|
|
26
|
+
unknownHook: "посмотреть не смогли — неизвестно, а не «нет»",
|
|
27
|
+
preCommitOk: "прописан в .git/hooks",
|
|
28
|
+
preCommitNo: "в .git/hooks его нет: запись в конфиге — намерение, а не защита",
|
|
29
|
+
sessionOk: "хук SessionStart отдаёт состояние агенту",
|
|
30
|
+
sessionNo: (cmd) => `агент не получает состояние: ${cmd}`,
|
|
31
|
+
versionOk: (v) => `${v}, свежая`,
|
|
32
|
+
versionUnknown: (v) => `${v}, до реестра не дозвонились — свежая ли, неизвестно`,
|
|
33
|
+
versionOld: (l, cur) => `есть ${l}, у вас ${cur}`,
|
|
34
|
+
},
|
|
35
|
+
// Блок краткого вывода переехал сюда 2026-09-09: терминальный каталог снова перерос
|
|
36
|
+
// 500 строк, и поймал это наш же file-size-limit. Шов по смыслу условен — это всё-таки
|
|
37
|
+
// терминал, — но предел настоящий, а делить пополам хуже, чем делить по соседству.
|
|
38
|
+
brief: {
|
|
39
|
+
name: "AQK",
|
|
40
|
+
held: (n) => `держит ${n}`,
|
|
41
|
+
todo: (n) => `не поставлено ${n}`,
|
|
42
|
+
levelUnknown: "уровень неизвестен",
|
|
43
|
+
red: (l) => ` КРАСНЫЕ: ${l}`,
|
|
44
|
+
advise: (slug, why) => ` ↑ поставить: ${slug} — ${why}`,
|
|
45
|
+
adviseOff: (cmd, env) => ` не нужно: ${cmd}, либо ${env}`,
|
|
46
|
+
update: (l, c, how) => `новая версия ${l}, у вас ${c} — обновить: ${how}`,
|
|
47
|
+
updateHow: "npx agent-quality-kit@latest, либо npm i -g agent-quality-kit",
|
|
48
|
+
updateHookHow: "pre-commit autoupdate",
|
|
49
|
+
updateOff: (env) => ` не нужно: ${env}`,
|
|
50
|
+
},
|
|
10
51
|
// БЛОК СОСТОЯНИЯ — единственный наш текст, у которого читатель машина, а не человек.
|
|
11
52
|
// Уходит в контекст агента хуком SessionStart, поэтому пишется как утверждения о факте:
|
|
12
53
|
// без вежливости, без вводных, каждая строка либо факт, либо честное «неизвестно».
|
|
@@ -28,6 +69,13 @@ const ruDocs = {
|
|
|
28
69
|
runRed: (when, names) => `Последний прогон ${when} — КРАСНЫЕ: ${names}.`,
|
|
29
70
|
andMore: (n) => `и ещё ${n}`,
|
|
30
71
|
skipped: (n) => `Не запускались: ${n} — инструмента нет на этой машине, их состояние неизвестно.`,
|
|
72
|
+
probeNever: "Проба покрытия не делалась — что здесь не прикрыто ничем, НЕИЗВЕСТНО. Это не «прикрыто»: `aqk probe`.",
|
|
73
|
+
probeBlind: (n, behind) =>
|
|
74
|
+
`Не прикрыто ничем: ${n} классов брака в местах, куда чаще всего возвращаются с починкой` +
|
|
75
|
+
(behind ? ` (проба отстала на ${behind} коммитов)` : "") + ". Подробно: `aqk probe`.",
|
|
76
|
+
probeClean: (behind) =>
|
|
77
|
+
"Проба покрытия: в проверенных местах каждый применимый класс кто-то ловит" +
|
|
78
|
+
(behind ? ` (отстала на ${behind} коммитов)` : "") + ".",
|
|
31
79
|
ratchets: (list) => `Храповики: ${list}. Список может только укорачиваться, увеличивать нельзя.`,
|
|
32
80
|
where: (entry) => `Свод правил: ${entry}. Чем доказан диф: \`aqk report --since main\`.`,
|
|
33
81
|
mapTitle: "ЧТО УМЕЕТ ЭТОТ ИНСТРУМЕНТ. Полный список команд — не пересказ, а тот же список,\nиз которого собрана справка:",
|