agent-quality-kit 0.2.2
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 +155 -0
- package/kit/docs/ai/agent-harness-playbook.md +596 -0
- package/kit/docs/ai/ai-native-development.md +371 -0
- package/kit/docs/ai/ai-sdlc.md +221 -0
- package/kit/docs/ai/anthropic-ai-native-sdlc-2026-08.md +294 -0
- package/kit/docs/ai/app-owner-strategy.md +921 -0
- package/kit/docs/ai/deep-research-2026-07.md +161 -0
- package/kit/docs/ai/harness-best-practices.md +385 -0
- package/kit/docs/ai/index.md +64 -0
- package/kit/docs/ai/project-baseline.md +261 -0
- package/kit/docs/ai/quality-gates-checklist.md +322 -0
- package/kit/docs/ai/sources-building-with-agents.md +111 -0
- package/kit/docs/ai/stream-2026-08-ai-coding-panel.md +304 -0
- package/kit/docs/ready-made-rules.md +170 -0
- package/kit/gates/README.md +231 -0
- package/kit/gates/_skip.sh +75 -0
- package/kit/gates/commit-explains-itself/README.md +45 -0
- package/kit/gates/commit-explains-itself/check.sh +63 -0
- package/kit/gates/commit-explains-itself/gate.yml +10 -0
- package/kit/gates/commit-explains-itself/green/COMMIT_MSG +6 -0
- package/kit/gates/commit-explains-itself/red/COMMIT_MSG +3 -0
- package/kit/gates/complexity-limit/README.md +37 -0
- package/kit/gates/complexity-limit/check.sh +44 -0
- package/kit/gates/complexity-limit/gate.yml +13 -0
- package/kit/gates/complexity-limit/green/flat.py +10 -0
- package/kit/gates/complexity-limit/red/deep.py +9 -0
- package/kit/gates/dead-code/README.md +30 -0
- package/kit/gates/dead-code/gate.yml +23 -0
- package/kit/gates/dead-code/green/mod.py +9 -0
- package/kit/gates/dead-code/red/mod.py +9 -0
- package/kit/gates/deps-are-pinned/README.md +29 -0
- package/kit/gates/deps-are-pinned/check.sh +49 -0
- package/kit/gates/deps-are-pinned/gate.yml +9 -0
- package/kit/gates/deps-are-pinned/green/nodep-go/go.mod +3 -0
- package/kit/gates/deps-are-pinned/green/package-lock.json +3 -0
- package/kit/gates/deps-are-pinned/green/package.json +4 -0
- package/kit/gates/deps-are-pinned/green/requirements.txt +2 -0
- package/kit/gates/deps-are-pinned/red/package.json +4 -0
- package/kit/gates/deps-are-pinned/red/requirements.txt +2 -0
- package/kit/gates/deps-are-pinned/red/withdep-go/go.mod +5 -0
- package/kit/gates/duplicate-code/README.md +40 -0
- package/kit/gates/duplicate-code/check.sh +58 -0
- package/kit/gates/duplicate-code/gate.yml +12 -0
- package/kit/gates/duplicate-code/green/common.py +9 -0
- package/kit/gates/duplicate-code/green/use.py +9 -0
- package/kit/gates/duplicate-code/red/a.py +12 -0
- package/kit/gates/duplicate-code/red/b.py +12 -0
- package/kit/gates/entry-links-exist/README.md +22 -0
- package/kit/gates/entry-links-exist/check.sh +24 -0
- package/kit/gates/entry-links-exist/gate.yml +16 -0
- package/kit/gates/entry-links-exist/green/AGENTS.md +5 -0
- package/kit/gates/entry-links-exist/green/rules/general.md +3 -0
- package/kit/gates/entry-links-exist/red/AGENTS.md +3 -0
- package/kit/gates/file-size-limit/README.md +22 -0
- package/kit/gates/file-size-limit/check.sh +34 -0
- package/kit/gates/file-size-limit/gate.yml +9 -0
- package/kit/gates/file-size-limit/green/a.py +251 -0
- package/kit/gates/file-size-limit/green/b.py +251 -0
- package/kit/gates/file-size-limit/red/big.py +601 -0
- package/kit/gates/gate-has-samples/README.md +29 -0
- package/kit/gates/gate-has-samples/check.sh +48 -0
- package/kit/gates/gate-has-samples/gate.yml +9 -0
- package/kit/gates/gate-has-samples/green/.aqk.yml +10 -0
- package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/check.sh +2 -0
- package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/green/good.py +2 -0
- package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/red/bad.py +1 -0
- package/kit/gates/gate-has-samples/red/.aqk.yml +10 -0
- package/kit/gates/gate-has-samples/red/gates/no-print-in-prod/check.sh +2 -0
- package/kit/gates/gates-are-runnable/README.md +23 -0
- package/kit/gates/gates-are-runnable/check.sh +35 -0
- package/kit/gates/gates-are-runnable/gate.yml +9 -0
- package/kit/gates/gates-are-runnable/green/.aqk.yml +9 -0
- package/kit/gates/gates-are-runnable/green/checks/lint.sh +2 -0
- package/kit/gates/gates-are-runnable/red/.aqk.yml +6 -0
- package/kit/gates/gates-run-in-ci/README.md +29 -0
- package/kit/gates/gates-run-in-ci/check.sh +42 -0
- package/kit/gates/gates-run-in-ci/gate.yml +12 -0
- package/kit/gates/gates-run-in-ci/green/.aqk.yml +6 -0
- package/kit/gates/gates-run-in-ci/green/.github/workflows/ci.yml +7 -0
- package/kit/gates/gates-run-in-ci/green/checks/lint.sh +2 -0
- package/kit/gates/gates-run-in-ci/red/.aqk.yml +6 -0
- package/kit/gates/gates-run-in-ci/red/.github/workflows/ci.yml +7 -0
- package/kit/gates/gates-run-in-ci/red/checks/lint.sh +2 -0
- package/kit/gates/lesson-has-outcome/README.md +37 -0
- package/kit/gates/lesson-has-outcome/check.sh +50 -0
- package/kit/gates/lesson-has-outcome/gate.yml +11 -0
- package/kit/gates/lesson-has-outcome/green/.aqk.yml +2 -0
- package/kit/gates/lesson-has-outcome/green/incidents/README.md +32 -0
- package/kit/gates/lesson-has-outcome/red/.aqk.yml +2 -0
- package/kit/gates/lesson-has-outcome/red/incidents/README.md +13 -0
- package/kit/gates/no-print-in-prod/README.md +44 -0
- package/kit/gates/no-print-in-prod/check.sh +36 -0
- package/kit/gates/no-print-in-prod/gate.yml +15 -0
- package/kit/gates/no-print-in-prod/green/docs.ts +15 -0
- package/kit/gates/no-print-in-prod/green/legacy.py +9 -0
- package/kit/gates/no-print-in-prod/green/main.go +8 -0
- package/kit/gates/no-print-in-prod/green/main.rs +4 -0
- package/kit/gates/no-print-in-prod/green/service.py +8 -0
- package/kit/gates/no-print-in-prod/red/main.go +8 -0
- package/kit/gates/no-print-in-prod/red/main.rs +4 -0
- package/kit/gates/no-print-in-prod/red/service.py +3 -0
- package/kit/gates/secrets-not-in-code/README.md +29 -0
- package/kit/gates/secrets-not-in-code/check.sh +18 -0
- package/kit/gates/secrets-not-in-code/gate.yml +9 -0
- package/kit/gates/secrets-not-in-code/green/settings.py +4 -0
- package/kit/gates/secrets-not-in-code/red/settings.py +2 -0
- package/kit/gates/swallowed-error/README.md +26 -0
- package/kit/gates/swallowed-error/check.sh +54 -0
- package/kit/gates/swallowed-error/gate.yml +12 -0
- package/kit/gates/swallowed-error/green/loader.py +11 -0
- package/kit/gates/swallowed-error/green/run.js +8 -0
- package/kit/gates/swallowed-error/red/loader.py +5 -0
- package/kit/gates/swallowed-error/red/run.js +3 -0
- package/kit/gates/todo-without-task/README.md +26 -0
- package/kit/gates/todo-without-task/check.sh +19 -0
- package/kit/gates/todo-without-task/gate.yml +12 -0
- package/kit/gates/todo-without-task/green/order.py +9 -0
- package/kit/gates/todo-without-task/red/order.py +8 -0
- package/kit/ratchet/ratchet.sh +62 -0
- package/kit/rules/general.md +55 -0
- package/kit/rules/security.md +33 -0
- package/kit/rules/testing.md +46 -0
- package/package.json +41 -0
- package/tool/commands/doctor.mjs +230 -0
- package/tool/commands/gates.mjs +445 -0
- package/tool/commands/project.mjs +316 -0
- package/tool/lib/core.mjs +98 -0
- package/tool/lib/manifest.mjs +140 -0
- package/tool/lib/repo.mjs +270 -0
- package/tool/lib/templates.mjs +187 -0
- package/tool/program.mjs +81 -0
- package/tool/selfcheck/conditional.sh +24 -0
- package/tool/selfcheck/gates.sh +127 -0
- package/tool/selfcheck/smoke.sh +539 -0
- package/tool/selfcheck/syntax.sh +23 -0
- package/tool/selfcheck/units.mjs +105 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// tool/selfcheck/units.mjs — проверки отдельных функций программы.
|
|
2
|
+
//
|
|
3
|
+
// ЗАЧЕМ ОТДЕЛЬНО ОТ smoke.sh. Прогон на чистой папке проверяет программу целиком и снаружи:
|
|
4
|
+
// он ловит «init не разложил файлы», но не ловит «условие триггера считается неверно» — такой
|
|
5
|
+
// дефект прячется за общим зелёным итогом. Оба дефекта, ради которых эти проверки написаны,
|
|
6
|
+
// именно так и жили: `.mjs` не считался JavaScript, а подсказка печаталась куском исходника.
|
|
7
|
+
//
|
|
8
|
+
// Зависимостей нет: `node:test` встроен начиная с Node 18.
|
|
9
|
+
//
|
|
10
|
+
// node --test tool/selfcheck/units.mjs
|
|
11
|
+
|
|
12
|
+
import test from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import { parseManifest, manifestWithGate } from "../lib/manifest.mjs";
|
|
15
|
+
import { triggerVerdict, recipeFor, stems, overlap, EXT_LANG } from "../lib/repo.mjs";
|
|
16
|
+
|
|
17
|
+
const facts = (over = {}) => ({ langs: new Set(), files: 0, ...over });
|
|
18
|
+
|
|
19
|
+
// --- опознание языка по расширению -------------------------------------------
|
|
20
|
+
// Найдено на самом aqk: вся программа лежит в .mjs, и запись про отладочную печать
|
|
21
|
+
// пряталась с пояснением «нет языков: javascript» — в проекте, целиком на JavaScript.
|
|
22
|
+
test("расширение .mjs — это JavaScript", () => {
|
|
23
|
+
assert.equal(EXT_LANG[".mjs"], "javascript");
|
|
24
|
+
assert.equal(EXT_LANG[".cjs"], "javascript");
|
|
25
|
+
assert.equal(EXT_LANG[".mts"], "typescript");
|
|
26
|
+
assert.equal(EXT_LANG[".py"], "python");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// --- разбор манифеста ---------------------------------------------------------
|
|
30
|
+
test("список читается и строкой в скобках, и пунктами", () => {
|
|
31
|
+
assert.deepEqual(parseManifest("entry: [AGENTS.md, CLAUDE.md]").entry, ["AGENTS.md", "CLAUDE.md"]);
|
|
32
|
+
assert.deepEqual(parseManifest("entry:\n - AGENTS.md\n - CLAUDE.md").entry, ["AGENTS.md", "CLAUDE.md"]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("вложенный блок читается словарём, комментарий отбрасывается", () => {
|
|
36
|
+
const m = parseManifest('gates:\n smoke: "bash x.sh" # пояснение\nrules: kit/rules\n');
|
|
37
|
+
assert.deepEqual(m.gates, { smoke: "bash x.sh" });
|
|
38
|
+
assert.equal(m.rules, "kit/rules");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// --- триггер ------------------------------------------------------------------
|
|
42
|
+
test("без триггера запись не показывается", () => {
|
|
43
|
+
assert.equal(triggerVerdict({}, facts()).applies, false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("условия складываются по И: одно ложное скрывает запись", () => {
|
|
47
|
+
const rec = { trigger: { langs: "python", files_gt: "10" } };
|
|
48
|
+
assert.equal(triggerVerdict(rec, facts({ langs: new Set(["python"]), files: 50 })).applies, true);
|
|
49
|
+
assert.equal(triggerVerdict(rec, facts({ langs: new Set(["python"]), files: 3 })).applies, false);
|
|
50
|
+
assert.equal(triggerVerdict(rec, facts({ langs: new Set(["go"]), files: 50 })).applies, false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("причина, по которой запись скрыта, называется словами", () => {
|
|
54
|
+
const v = triggerVerdict({ trigger: { langs: "python, typescript" } }, facts({ langs: new Set(["go"]) }));
|
|
55
|
+
assert.match(v.why, /нет языков: python, typescript/);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("always: false значит «никогда не применимо», а не «условие пропущено»", () => {
|
|
59
|
+
const v = triggerVerdict({ trigger: { always: "false" } }, facts());
|
|
60
|
+
assert.equal(v.applies, false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("неизвестное условие скрывает запись, а не пропускает её", () => {
|
|
64
|
+
// Молча пропустить незнакомое условие значит показать запись всем подряд.
|
|
65
|
+
const v = triggerVerdict({ trigger: { has_kubernetes: "true" } }, facts());
|
|
66
|
+
assert.equal(v.applies, false);
|
|
67
|
+
assert.match(v.why, /не умеет считать/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// --- выбор рецепта ------------------------------------------------------------
|
|
71
|
+
test("без родного языка берётся переносимый рецепт, {dir} подставляется", () => {
|
|
72
|
+
const cmd = recipeFor({ slug: "x", recipes: { any: "bash {gate}/check.sh {dir}" } }, facts());
|
|
73
|
+
assert.match(cmd, /check\.sh \.$/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("рецепта нет — так и сказано, а не пустая строка", () => {
|
|
77
|
+
assert.equal(recipeFor({ slug: "x", recipes: {} }, facts()), "рецепт не описан");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// --- дедупликация по намерению ------------------------------------------------
|
|
81
|
+
test("разные намерения не путаются служебными словами", () => {
|
|
82
|
+
// Найдено на `aqk new dead-code-not-shipped`: слова not/in/code давали ложное совпадение
|
|
83
|
+
// с записью secrets-not-in-code, и заготовка не создавалась.
|
|
84
|
+
const q = stems("dead code not shipped");
|
|
85
|
+
assert.ok(overlap(q, stems("ключи, пароли и приватные ключи не попадают в код")) < 0.5);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("то же намерение другими словами совпадает", () => {
|
|
89
|
+
const q = stems("отладочная печать не доезжает до продакшена");
|
|
90
|
+
assert.ok(overlap(q, stems("отладочная печать не доезжает до прод-кода")) >= 0.5);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// --- дописывание гейта в манифест ---------------------------------------------
|
|
94
|
+
test("гейт дописывается в блок gates и не дублируется", () => {
|
|
95
|
+
const src = "aqk: 1\ngates:\n smoke: \"bash x.sh\"\n";
|
|
96
|
+
const a = manifestWithGate(src, "no-print-in-prod", "bash y.sh");
|
|
97
|
+
assert.match(a.text, /no-print-in-prod: "bash y\.sh"/);
|
|
98
|
+
assert.equal(manifestWithGate(a.text, "no-print-in-prod", "bash y.sh").text, null);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("без блока gates программа объясняет, чего не хватает", () => {
|
|
102
|
+
const r = manifestWithGate("aqk: 1\n", "x", "bash y.sh");
|
|
103
|
+
assert.equal(r.text, null);
|
|
104
|
+
assert.match(r.why, /нет блока gates/);
|
|
105
|
+
});
|