kibi-cli 2.35.1 → 2.38.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/dist/cli-operation-metadata.d.ts +1 -1
- package/dist/cli-operation-metadata.js +1 -1
- package/dist/cli-register-foundation.d.ts.map +1 -1
- package/dist/cli-register-foundation.js +2 -1
- package/dist/cli-register-maintenance.d.ts.map +1 -1
- package/dist/cli-register-maintenance.js +5 -0
- package/dist/cli-validate.d.ts.map +1 -1
- package/dist/cli-validate.js +21 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +16 -1
- package/dist/commands/aggregated-checks.d.ts.map +1 -1
- package/dist/commands/aggregated-checks.js +89 -16
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +56 -35
- package/dist/commands/github-init.d.ts.map +1 -1
- package/dist/commands/github-init.js +4 -2
- package/dist/commands/hook-managers.d.ts +43 -0
- package/dist/commands/hook-managers.d.ts.map +1 -0
- package/dist/commands/hook-managers.js +426 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +40 -5
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +43 -1
- package/dist/commands/telemetry.d.ts +7 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/commands/telemetry.js +23 -0
- package/dist/decision-firmness.d.ts.map +1 -1
- package/dist/decision-firmness.js +11 -14
- package/dist/engine-limits.d.ts.map +1 -1
- package/dist/engine-limits.js +8 -10
- package/dist/entity-body-context.d.ts.map +1 -1
- package/dist/entity-body-context.js +39 -36
- package/dist/extractors/symbol-coordinates.d.ts.map +1 -1
- package/dist/extractors/symbol-coordinates.js +23 -4
- package/dist/firm-decision-edit.d.ts.map +1 -1
- package/dist/firm-decision-edit.js +1 -3
- package/dist/intent-search.d.ts +5 -1
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +118 -33
- package/dist/logic/ir.d.ts.map +1 -1
- package/dist/logic/ir.js +82 -108
- package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance-actions.js +2 -1
- package/dist/operations/bootstrap/plugin-offers.d.ts +11 -2
- package/dist/operations/bootstrap/plugin-offers.d.ts.map +1 -1
- package/dist/operations/bootstrap/plugin-offers.js +48 -9
- package/dist/operations/proof/claim-bindings.d.ts.map +1 -1
- package/dist/operations/proof/claim-bindings.js +1 -0
- package/dist/operations/proof/code-certificates.d.ts +2 -0
- package/dist/operations/proof/code-certificates.d.ts.map +1 -1
- package/dist/operations/proof/code-certificates.js +34 -14
- package/dist/operations/proof/code-models.d.ts.map +1 -1
- package/dist/operations/proof/code-models.js +19 -5
- package/dist/operations/proof/code-runner.d.ts +16 -0
- package/dist/operations/proof/code-runner.d.ts.map +1 -1
- package/dist/operations/proof/code-runner.js +42 -6
- package/dist/plugins/compose-search-hints.d.ts +29 -0
- package/dist/plugins/compose-search-hints.d.ts.map +1 -0
- package/dist/plugins/compose-search-hints.js +94 -0
- package/dist/plugins/external-allowlist.d.ts +7 -0
- package/dist/plugins/external-allowlist.d.ts.map +1 -1
- package/dist/plugins/external-allowlist.js +11 -0
- package/dist/plugins/registry.d.ts +8 -1
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +22 -3
- package/dist/prolog/codec.d.ts.map +1 -1
- package/dist/prolog/codec.js +10 -20
- package/dist/public/impact/coverage-depth-quality.d.ts.map +1 -1
- package/dist/public/impact/coverage-depth-quality.js +32 -16
- package/dist/public/impact/full-kb-quality.d.ts +6 -0
- package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
- package/dist/public/impact/full-kb-quality.js +8 -3
- package/dist/public/impact/requirement-quality.d.ts.map +1 -1
- package/dist/public/impact/requirement-quality.js +56 -29
- package/dist/public/operations/catalog.d.ts +13 -3
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.d.ts +6 -0
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +34 -2
- package/dist/public/operations/discovery-executors.d.ts +5 -1
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +65 -4
- package/dist/public/operations/evidence-floor.d.ts +8 -1
- package/dist/public/operations/evidence-floor.d.ts.map +1 -1
- package/dist/public/operations/evidence-floor.js +3 -4
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +41 -10
- package/dist/public/operations/node-ports.d.ts.map +1 -1
- package/dist/public/operations/node-ports.js +3 -1
- package/dist/public/operations/source-analysis-cache.d.ts +22 -0
- package/dist/public/operations/source-analysis-cache.d.ts.map +1 -0
- package/dist/public/operations/source-analysis-cache.js +157 -0
- package/dist/public/operations/specs/discovery.d.ts +7 -2
- package/dist/public/operations/specs/discovery.d.ts.map +1 -1
- package/dist/public/operations/specs/discovery.js +7 -2
- package/dist/public/operations/specs/mutation-schemas.d.ts +6 -1
- package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
- package/dist/public/operations/specs/upsert.d.ts +6 -1
- package/dist/public/operations/specs/upsert.d.ts.map +1 -1
- package/dist/public/operations/symbol-granularity-check.d.ts +3 -1
- package/dist/public/operations/symbol-granularity-check.d.ts.map +1 -1
- package/dist/public/operations/symbol-granularity-check.js +21 -7
- package/dist/public/proof-protocol.d.ts +6 -1
- package/dist/public/proof-protocol.d.ts.map +1 -1
- package/dist/public/proof-protocol.js +12 -3
- package/dist/public/proof-sources.d.ts +2 -0
- package/dist/public/proof-sources.d.ts.map +1 -1
- package/dist/public/proof-sources.js +32 -6
- package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/dist/public/skills/kibi-usage/SKILL.md +42 -3
- package/dist/public/skills/kibi-usage/resources/fact-lanes.md +34 -0
- package/dist/public/skills/kibi-usage/resources/proof.md +13 -5
- package/dist/public/skills/kibi-usage/resources/workflows.md +10 -0
- package/dist/relationships/shards.d.ts +2 -0
- package/dist/relationships/shards.d.ts.map +1 -1
- package/dist/relationships/shards.js +25 -1
- package/dist/schemas/entity.schema.json +6 -1
- package/dist/search-answer.js +1 -1
- package/dist/search-hints.d.ts +58 -0
- package/dist/search-hints.d.ts.map +1 -0
- package/dist/search-hints.js +187 -0
- package/dist/search-ranking.d.ts +7 -0
- package/dist/search-ranking.d.ts.map +1 -1
- package/dist/search-ranking.js +54 -56
- package/dist/search-token-cache.d.ts +30 -0
- package/dist/search-token-cache.d.ts.map +1 -0
- package/dist/search-token-cache.js +86 -0
- package/dist/usage-telemetry.d.ts +11 -0
- package/dist/usage-telemetry.d.ts.map +1 -0
- package/dist/usage-telemetry.js +47 -0
- package/dist/utils/branch-resolver.d.ts.map +1 -1
- package/dist/utils/branch-resolver.js +1 -0
- package/package.json +5 -4
- package/src/public/impact/coverage-depth-quality.ts +59 -21
- package/src/public/impact/full-kb-quality.ts +15 -1
- package/src/public/impact/requirement-quality.ts +95 -38
- package/src/public/operations/check-executor.ts +41 -1
- package/src/public/operations/discovery-executors.ts +92 -4
- package/src/public/operations/evidence-floor.ts +15 -3
- package/src/public/operations/migration-plan.ts +41 -9
- package/src/public/operations/node-ports.ts +3 -1
- package/src/public/operations/source-analysis-cache.ts +196 -0
- package/src/public/operations/specs/discovery.ts +8 -2
- package/src/public/operations/symbol-granularity-check.ts +25 -5
- package/src/public/proof-protocol-types.d.ts +1 -1
- package/src/public/proof-protocol.ts +18 -4
- package/src/public/proof-sources.ts +37 -6
- package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/src/public/skills/kibi-usage/SKILL.md +42 -3
- package/src/public/skills/kibi-usage/resources/fact-lanes.md +34 -0
- package/src/public/skills/kibi-usage/resources/proof.md +13 -5
- package/src/public/skills/kibi-usage/resources/workflows.md +10 -0
- package/src/schemas/entity.schema.json +6 -1
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync, statSync } from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { parse as parseYaml } from "yaml";
|
|
21
|
+
import { MANAGED_HOOK_NAMES, hookDelegationSnippet, } from "./init-helpers.js";
|
|
22
|
+
/** Git hook managers whose configuration Kibi can print and read. */
|
|
23
|
+
export const HOOK_MANAGER_NAMES = ["lefthook", "husky", "pre-commit"];
|
|
24
|
+
/** Formats `kibi init --print-hook` can emit. */
|
|
25
|
+
export const HOOK_SNIPPET_FORMATS = ["shell", ...HOOK_MANAGER_NAMES];
|
|
26
|
+
// lefthook reads the first of these it finds; the -local variants are merged
|
|
27
|
+
// on top of the main file.
|
|
28
|
+
const LEFTHOOK_CONFIG_FILES = [
|
|
29
|
+
"lefthook.yml",
|
|
30
|
+
".lefthook.yml",
|
|
31
|
+
"lefthook.yaml",
|
|
32
|
+
".lefthook.yaml",
|
|
33
|
+
"lefthook.json",
|
|
34
|
+
".lefthook.json",
|
|
35
|
+
"lefthook.jsonc",
|
|
36
|
+
".lefthook.jsonc",
|
|
37
|
+
"lefthook.toml",
|
|
38
|
+
".lefthook.toml",
|
|
39
|
+
".config/lefthook.yml",
|
|
40
|
+
".config/lefthook.yaml",
|
|
41
|
+
];
|
|
42
|
+
const LEFTHOOK_LOCAL_CONFIG_FILES = [
|
|
43
|
+
"lefthook-local.yml",
|
|
44
|
+
".lefthook-local.yml",
|
|
45
|
+
"lefthook-local.yaml",
|
|
46
|
+
".lefthook-local.yaml",
|
|
47
|
+
"lefthook-local.json",
|
|
48
|
+
".lefthook-local.json",
|
|
49
|
+
];
|
|
50
|
+
const PRE_COMMIT_CONFIG_FILE = ".pre-commit-config.yaml";
|
|
51
|
+
const HUSKY_DIR = ".husky";
|
|
52
|
+
/** Hooks in the order a reader expects them: the commit gate first. */
|
|
53
|
+
const PRINT_ORDER = [
|
|
54
|
+
"pre-commit",
|
|
55
|
+
"post-checkout",
|
|
56
|
+
"post-merge",
|
|
57
|
+
"post-rewrite",
|
|
58
|
+
];
|
|
59
|
+
// The local runner every Node.js install ships with. `--no-install` never
|
|
60
|
+
// downloads a kibi outside the project lockfile.
|
|
61
|
+
const KIBI_COMMAND = "npx --no-install kibi";
|
|
62
|
+
function isFile(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
return statSync(filePath).isFile();
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function isDirectory(filePath) {
|
|
71
|
+
try {
|
|
72
|
+
return statSync(filePath).isDirectory();
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function readText(filePath) {
|
|
79
|
+
try {
|
|
80
|
+
return readFileSync(filePath, "utf8");
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function firstExisting(root, candidates) {
|
|
87
|
+
return candidates.find((file) => isFile(path.join(root, file))) ?? null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Which manager generated a hook file, from the marker text each one emits.
|
|
91
|
+
* husky is recognized by core.hooksPath instead: its hook files are the
|
|
92
|
+
* project's own scripts, which may mention husky without being generated.
|
|
93
|
+
*/
|
|
94
|
+
function hookFileManager(content) {
|
|
95
|
+
if (content.includes("File generated by pre-commit"))
|
|
96
|
+
return "pre-commit";
|
|
97
|
+
if (/\blefthook\b/.test(content))
|
|
98
|
+
return "lefthook";
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
function managerConfigPath(worktreeRoot, name) {
|
|
102
|
+
switch (name) {
|
|
103
|
+
case "lefthook":
|
|
104
|
+
return (firstExisting(worktreeRoot, LEFTHOOK_CONFIG_FILES) ?? "lefthook.yml");
|
|
105
|
+
case "husky":
|
|
106
|
+
return HUSKY_DIR;
|
|
107
|
+
case "pre-commit":
|
|
108
|
+
return PRE_COMMIT_CONFIG_FILE;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The hook manager that owns this repository's Git hooks, if any. Installed
|
|
113
|
+
* hook files are the strongest evidence (they show what Git runs today), then
|
|
114
|
+
* a core.hooksPath pointing into `.husky`, then a manager configuration file
|
|
115
|
+
* that has not been installed yet.
|
|
116
|
+
*/
|
|
117
|
+
// implements REQ-git-hook-manager-integration
|
|
118
|
+
export function detectHookManager(worktreeRoot, effectiveHooksDir, hooksPathConfig = null) {
|
|
119
|
+
const found = (name) => ({
|
|
120
|
+
name,
|
|
121
|
+
configPath: managerConfigPath(worktreeRoot, name),
|
|
122
|
+
});
|
|
123
|
+
for (const hook of MANAGED_HOOK_NAMES) {
|
|
124
|
+
const content = readText(path.join(effectiveHooksDir, hook));
|
|
125
|
+
const manager = content === null ? null : hookFileManager(content);
|
|
126
|
+
if (manager)
|
|
127
|
+
return found(manager);
|
|
128
|
+
}
|
|
129
|
+
if (hooksPathConfig !== null &&
|
|
130
|
+
/(?:^|[\\/])\.husky(?:[\\/]_)?[\\/]?$/.test(hooksPathConfig.trim())) {
|
|
131
|
+
return found("husky");
|
|
132
|
+
}
|
|
133
|
+
if (firstExisting(worktreeRoot, LEFTHOOK_CONFIG_FILES)) {
|
|
134
|
+
return found("lefthook");
|
|
135
|
+
}
|
|
136
|
+
if (isDirectory(path.join(worktreeRoot, HUSKY_DIR)))
|
|
137
|
+
return found("husky");
|
|
138
|
+
if (isFile(path.join(worktreeRoot, PRE_COMMIT_CONFIG_FILE))) {
|
|
139
|
+
return found("pre-commit");
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
function parseConfig(filePath) {
|
|
144
|
+
const text = readText(filePath);
|
|
145
|
+
if (text === null)
|
|
146
|
+
return undefined;
|
|
147
|
+
try {
|
|
148
|
+
return parseYaml(text);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function asRecord(value) {
|
|
155
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
156
|
+
? value
|
|
157
|
+
: null;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Every shell command lefthook runs for one hook: `run` strings of commands
|
|
161
|
+
* and jobs (including nested groups), plus the contents of the scripts it
|
|
162
|
+
* executes from `.lefthook/<hook>/`.
|
|
163
|
+
*/
|
|
164
|
+
function lefthookRuns(node, scriptDir, commands) {
|
|
165
|
+
if (Array.isArray(node)) {
|
|
166
|
+
for (const item of node)
|
|
167
|
+
lefthookRuns(item, scriptDir, commands);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const record = asRecord(node);
|
|
171
|
+
if (!record)
|
|
172
|
+
return;
|
|
173
|
+
for (const [key, value] of Object.entries(record)) {
|
|
174
|
+
if (key === "run" && typeof value === "string") {
|
|
175
|
+
commands.push(value);
|
|
176
|
+
}
|
|
177
|
+
else if (key === "script" && typeof value === "string") {
|
|
178
|
+
const script = readText(path.join(scriptDir, value));
|
|
179
|
+
if (script !== null)
|
|
180
|
+
commands.push(script);
|
|
181
|
+
}
|
|
182
|
+
else if (key === "scripts" && asRecord(value)) {
|
|
183
|
+
for (const name of Object.keys(value)) {
|
|
184
|
+
const script = readText(path.join(scriptDir, name));
|
|
185
|
+
if (script !== null)
|
|
186
|
+
commands.push(script);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
lefthookRuns(value, scriptDir, commands);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function lefthookCommands(worktreeRoot, hook) {
|
|
195
|
+
const main = firstExisting(worktreeRoot, LEFTHOOK_CONFIG_FILES);
|
|
196
|
+
const files = [
|
|
197
|
+
...(main ? [main] : []),
|
|
198
|
+
...LEFTHOOK_LOCAL_CONFIG_FILES.filter((file) => isFile(path.join(worktreeRoot, file))),
|
|
199
|
+
];
|
|
200
|
+
let configured = false;
|
|
201
|
+
const commands = [];
|
|
202
|
+
for (const file of files) {
|
|
203
|
+
// TOML configs are not read; their hooks count as not visible.
|
|
204
|
+
const hookNode = asRecord(parseConfig(path.join(worktreeRoot, file)))?.[hook];
|
|
205
|
+
if (hookNode === undefined || hookNode === null)
|
|
206
|
+
continue;
|
|
207
|
+
configured = true;
|
|
208
|
+
lefthookRuns(hookNode, path.join(worktreeRoot, ".lefthook", hook), commands);
|
|
209
|
+
}
|
|
210
|
+
return configured ? commands : null;
|
|
211
|
+
}
|
|
212
|
+
// pre-commit stage names before 3.2 dropped the `pre-` prefix.
|
|
213
|
+
const LEGACY_PRE_COMMIT_STAGES = {
|
|
214
|
+
commit: "pre-commit",
|
|
215
|
+
push: "pre-push",
|
|
216
|
+
"merge-commit": "pre-merge-commit",
|
|
217
|
+
};
|
|
218
|
+
function preCommitFrameworkCommands(worktreeRoot, hook) {
|
|
219
|
+
const config = asRecord(parseConfig(path.join(worktreeRoot, PRE_COMMIT_CONFIG_FILE)));
|
|
220
|
+
if (!config || !Array.isArray(config.repos))
|
|
221
|
+
return null;
|
|
222
|
+
const defaultStages = Array.isArray(config.default_stages)
|
|
223
|
+
? config.default_stages
|
|
224
|
+
: null;
|
|
225
|
+
let configured = false;
|
|
226
|
+
const commands = [];
|
|
227
|
+
for (const repo of config.repos) {
|
|
228
|
+
const hooks = asRecord(repo)?.hooks;
|
|
229
|
+
if (!Array.isArray(hooks))
|
|
230
|
+
continue;
|
|
231
|
+
for (const entry of hooks) {
|
|
232
|
+
const definition = asRecord(entry);
|
|
233
|
+
if (!definition)
|
|
234
|
+
continue;
|
|
235
|
+
const stages = Array.isArray(definition.stages)
|
|
236
|
+
? definition.stages
|
|
237
|
+
: defaultStages;
|
|
238
|
+
const runsHere = stages === null
|
|
239
|
+
? hook === "pre-commit"
|
|
240
|
+
: stages.some((stage) => typeof stage === "string" &&
|
|
241
|
+
(LEGACY_PRE_COMMIT_STAGES[stage] ?? stage) === hook);
|
|
242
|
+
if (!runsHere)
|
|
243
|
+
continue;
|
|
244
|
+
configured = true;
|
|
245
|
+
// Only local hooks carry a shell entry; remote hooks are opaque here.
|
|
246
|
+
if (typeof definition.entry === "string") {
|
|
247
|
+
const args = Array.isArray(definition.args)
|
|
248
|
+
? definition.args.filter((arg) => typeof arg === "string")
|
|
249
|
+
: [];
|
|
250
|
+
commands.push([definition.entry, ...args].join(" "));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return configured ? commands : null;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* The commands a hook manager runs for one Git hook, read from its
|
|
258
|
+
* configuration, or null when the manager does not define that hook (or its
|
|
259
|
+
* configuration cannot be read).
|
|
260
|
+
*/
|
|
261
|
+
// implements REQ-git-hook-manager-integration
|
|
262
|
+
export function hookManagerCommands(manager, worktreeRoot, hook) {
|
|
263
|
+
switch (manager) {
|
|
264
|
+
case "lefthook":
|
|
265
|
+
return lefthookCommands(worktreeRoot, hook);
|
|
266
|
+
case "husky": {
|
|
267
|
+
const script = readText(path.join(worktreeRoot, HUSKY_DIR, hook));
|
|
268
|
+
return script === null ? null : [script];
|
|
269
|
+
}
|
|
270
|
+
case "pre-commit":
|
|
271
|
+
return preCommitFrameworkCommands(worktreeRoot, hook);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Individual shell commands of a hook script, ignoring comments. Lines are
|
|
276
|
+
* split on command separators so `a && b` counts as two commands.
|
|
277
|
+
*/
|
|
278
|
+
function splitShellCommands(content) {
|
|
279
|
+
return content
|
|
280
|
+
.split("\n")
|
|
281
|
+
.filter((line) => !line.trimStart().startsWith("#"))
|
|
282
|
+
.flatMap((line) => line.split(/&&|\|\||;|\|/))
|
|
283
|
+
.map((command) => command.trim())
|
|
284
|
+
.filter((command) => command.length > 0);
|
|
285
|
+
}
|
|
286
|
+
// Matches `kibi <sub>`, `npx kibi <sub>`, `node_modules/.bin/kibi <sub>` and
|
|
287
|
+
// the template's `"$KIBI_BIN" <sub>`.
|
|
288
|
+
function invokesKibi(command, subcommand) {
|
|
289
|
+
return new RegExp(`(?:\\bkibi|\\bKIBI_BIN)["'}]*\\s+${subcommand}(?=[\\s"']|$)`).test(command);
|
|
290
|
+
}
|
|
291
|
+
// A flag ends at whitespace, a closing quote (`sh -c '... --staged'`) or the
|
|
292
|
+
// end of the command.
|
|
293
|
+
function hasFlag(command, flag) {
|
|
294
|
+
return new RegExp(`\\s${flag}(?=[\\s"']|$)`).test(command);
|
|
295
|
+
}
|
|
296
|
+
/** The commands each Kibi-managed hook template runs. */
|
|
297
|
+
const KIBI_HOOK_DELEGATIONS = (() => {
|
|
298
|
+
const sync = [
|
|
299
|
+
{
|
|
300
|
+
label: "kibi sync",
|
|
301
|
+
matches: (command) => invokesKibi(command, "sync"),
|
|
302
|
+
},
|
|
303
|
+
];
|
|
304
|
+
return {
|
|
305
|
+
"pre-commit": [
|
|
306
|
+
{
|
|
307
|
+
label: "kibi check-generated --staged --changed-only",
|
|
308
|
+
matches: (command) => invokesKibi(command, "check-generated") &&
|
|
309
|
+
hasFlag(command, "--staged") &&
|
|
310
|
+
hasFlag(command, "--changed-only"),
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
label: "kibi check --staged",
|
|
314
|
+
matches: (command) => invokesKibi(command, "check") && hasFlag(command, "--staged"),
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
"post-checkout": sync,
|
|
318
|
+
"post-merge": sync,
|
|
319
|
+
"post-rewrite": sync,
|
|
320
|
+
};
|
|
321
|
+
})();
|
|
322
|
+
/** Labels of the Kibi commands a hook definition never visibly runs. */
|
|
323
|
+
// implements REQ-git-hook-manager-integration
|
|
324
|
+
export function missingKibiHookCommands(hook, scripts) {
|
|
325
|
+
const commands = scripts.flatMap(splitShellCommands);
|
|
326
|
+
return KIBI_HOOK_DELEGATIONS[hook]
|
|
327
|
+
.filter((delegation) => !commands.some(delegation.matches))
|
|
328
|
+
.map((delegation) => delegation.label);
|
|
329
|
+
}
|
|
330
|
+
function ordered(hooks) {
|
|
331
|
+
return PRINT_ORDER.filter((hook) => hooks.includes(hook));
|
|
332
|
+
}
|
|
333
|
+
// Runs in lefthook's shell, where {N} is the Nth Git hook argument.
|
|
334
|
+
const LEFTHOOK_RUNS = {
|
|
335
|
+
"pre-commit": `${KIBI_COMMAND} check-generated --staged --changed-only && ${KIBI_COMMAND} check --staged`,
|
|
336
|
+
"post-checkout": `if [ "{3}" = "1" ]; then ${KIBI_COMMAND} sync; fi`,
|
|
337
|
+
"post-merge": `${KIBI_COMMAND} sync`,
|
|
338
|
+
"post-rewrite": `if [ "{1}" = "rebase" ]; then ${KIBI_COMMAND} sync; fi`,
|
|
339
|
+
};
|
|
340
|
+
function lefthookSnippet(hooks) {
|
|
341
|
+
const lines = [
|
|
342
|
+
"# Kibi git hook commands for lefthook. Merge these entries into your",
|
|
343
|
+
"# lefthook.yml (keep your existing commands), then run 'lefthook install'.",
|
|
344
|
+
];
|
|
345
|
+
for (const hook of hooks) {
|
|
346
|
+
lines.push(`${hook}:`, " commands:");
|
|
347
|
+
if (hook === "post-checkout") {
|
|
348
|
+
lines.push(" # {3} is 1 for a branch checkout, 0 for a file checkout.");
|
|
349
|
+
}
|
|
350
|
+
lines.push(` ${hook === "pre-commit" ? "kibi-check" : "kibi-sync"}:`, ` run: ${LEFTHOOK_RUNS[hook]}`);
|
|
351
|
+
}
|
|
352
|
+
return `${lines.join("\n")}\n`;
|
|
353
|
+
}
|
|
354
|
+
// pre-commit exposes post-checkout and post-rewrite arguments as variables.
|
|
355
|
+
const PRE_COMMIT_FRAMEWORK_HOOKS = {
|
|
356
|
+
"pre-commit": {
|
|
357
|
+
id: "kibi-check",
|
|
358
|
+
name: "kibi check --staged",
|
|
359
|
+
entry: `sh -c '${KIBI_COMMAND} check-generated --staged --changed-only && ${KIBI_COMMAND} check --staged'`,
|
|
360
|
+
},
|
|
361
|
+
"post-checkout": {
|
|
362
|
+
id: "kibi-sync-checkout",
|
|
363
|
+
name: "kibi sync (branch checkout)",
|
|
364
|
+
entry: `sh -c 'if [ "$PRE_COMMIT_CHECKOUT_TYPE" = "1" ]; then ${KIBI_COMMAND} sync; fi'`,
|
|
365
|
+
},
|
|
366
|
+
"post-merge": {
|
|
367
|
+
id: "kibi-sync-merge",
|
|
368
|
+
name: "kibi sync (merge)",
|
|
369
|
+
entry: `${KIBI_COMMAND} sync`,
|
|
370
|
+
},
|
|
371
|
+
"post-rewrite": {
|
|
372
|
+
id: "kibi-sync-rebase",
|
|
373
|
+
name: "kibi sync (rebase)",
|
|
374
|
+
entry: `sh -c 'if [ "$PRE_COMMIT_REWRITE_COMMAND" = "rebase" ]; then ${KIBI_COMMAND} sync; fi'`,
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
function preCommitFrameworkSnippet(hooks) {
|
|
378
|
+
const lines = [
|
|
379
|
+
"# Kibi git hooks for the pre-commit framework. Merge into your",
|
|
380
|
+
"# .pre-commit-config.yaml (add the hook types to any existing",
|
|
381
|
+
"# default_install_hook_types and the hooks to a local repo), then run",
|
|
382
|
+
`# 'pre-commit install'.`,
|
|
383
|
+
`default_install_hook_types: [${hooks.join(", ")}]`,
|
|
384
|
+
"repos:",
|
|
385
|
+
" - repo: local",
|
|
386
|
+
" hooks:",
|
|
387
|
+
];
|
|
388
|
+
for (const hook of hooks) {
|
|
389
|
+
const definition = PRE_COMMIT_FRAMEWORK_HOOKS[hook];
|
|
390
|
+
lines.push(` - id: ${definition.id}`, ` name: ${definition.name}`, ` entry: ${definition.entry}`, " language: system", " pass_filenames: false", " always_run: true", ` stages: [${hook}]`);
|
|
391
|
+
}
|
|
392
|
+
return `${lines.join("\n")}\n`;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Configuration that wires the given hooks to the commands Kibi's managed
|
|
396
|
+
* templates run, in a hook manager's own format. `shell` and `husky` print
|
|
397
|
+
* the hook-script snippets of `kibi init --print-hook`.
|
|
398
|
+
*/
|
|
399
|
+
// implements REQ-git-hook-manager-integration
|
|
400
|
+
export function hookManagerSnippet(format, hooks) {
|
|
401
|
+
const selected = ordered(hooks);
|
|
402
|
+
switch (format) {
|
|
403
|
+
case "shell":
|
|
404
|
+
return selected.map(hookDelegationSnippet).join("\n");
|
|
405
|
+
case "husky":
|
|
406
|
+
return selected
|
|
407
|
+
.map((hook) => `# --- ${HUSKY_DIR}/${hook} ---\n${hookDelegationSnippet(hook)}`)
|
|
408
|
+
.join("\n");
|
|
409
|
+
case "lefthook":
|
|
410
|
+
return lefthookSnippet(selected);
|
|
411
|
+
case "pre-commit":
|
|
412
|
+
return preCommitFrameworkSnippet(selected);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/** What each Kibi-managed hook looks like in the manager's configuration. */
|
|
416
|
+
// implements REQ-git-hook-manager-integration
|
|
417
|
+
export function hookManagerCoverage(manager, worktreeRoot) {
|
|
418
|
+
return ordered(MANAGED_HOOK_NAMES).map((hook) => {
|
|
419
|
+
const commands = hookManagerCommands(manager, worktreeRoot, hook);
|
|
420
|
+
return {
|
|
421
|
+
hook,
|
|
422
|
+
configured: commands !== null,
|
|
423
|
+
missing: missingKibiHookCommands(hook, commands ?? []),
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA8GA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAkFD,wBAAsB,WAAW,CAC/B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0L/B"}
|
package/dist/commands/init.js
CHANGED
|
@@ -24,7 +24,8 @@ import { resolveBranchAttachment } from "../utils/branch-resolver.js";
|
|
|
24
24
|
import { readLegacyKbConfig } from "../utils/config.js";
|
|
25
25
|
import { resolveGitRepository, } from "../utils/git-repository-context.js";
|
|
26
26
|
import { scaffoldGitHubIntegration } from "./github-init.js";
|
|
27
|
-
import {
|
|
27
|
+
import { HOOK_SNIPPET_FORMATS, detectHookManager, hookManagerCoverage, hookManagerSnippet, } from "./hook-managers.js";
|
|
28
|
+
import { MANAGED_HOOK_NAMES, copySchemaFiles, createKbDirectoryStructure, createManifestFile, ensureSymbolsManifestFile, installGitHooks, isManagedHookName, updateGitIgnore, } from "./init-helpers.js";
|
|
28
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
29
30
|
const __dirname = path.dirname(__filename);
|
|
30
31
|
/**
|
|
@@ -83,6 +84,24 @@ function isRepositoryManagedHooksDir(context) {
|
|
|
83
84
|
// directory, which can sit outside the current worktree root.
|
|
84
85
|
return isWithin(hooksDir, commonGitDir) || isWithin(hooksDir, worktreeRoot);
|
|
85
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Hooks owned by a hook manager are left to it: Kibi installs nothing into
|
|
89
|
+
* the hooks directory and prints the manager configuration that runs Kibi's
|
|
90
|
+
* commands for every hook the manager does not visibly cover yet.
|
|
91
|
+
*/
|
|
92
|
+
// implements REQ-git-hook-manager-integration
|
|
93
|
+
function reportHookManager(manager, worktreeRoot) {
|
|
94
|
+
const gaps = hookManagerCoverage(manager.name, worktreeRoot).filter((entry) => entry.missing.length > 0);
|
|
95
|
+
const owner = `Git hooks are managed by ${manager.name} (${manager.configPath})`;
|
|
96
|
+
if (gaps.length === 0) {
|
|
97
|
+
console.log(`✓ ${owner}, which already runs Kibi's hook commands; Kibi installed no hooks of its own.`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const hooks = gaps.map((entry) => entry.hook);
|
|
101
|
+
console.log(`! ${owner}; Kibi installed no hooks of its own. ${manager.configPath} does not visibly run Kibi's commands for: ${hooks.join(", ")}. Add this (running the same commands through a task runner or script works too), or Kibi enforcement stays OFF for those hooks:\n`);
|
|
102
|
+
console.log(hookManagerSnippet(manager.name, hooks));
|
|
103
|
+
console.log(` Print it again with: kibi init --print-hook ${hooks.length === 1 ? hooks[0] : "all"} --format ${manager.name}`);
|
|
104
|
+
}
|
|
86
105
|
async function initNextAction(projectRoot) {
|
|
87
106
|
const sourceFiles = await nodeFilesystem.glob([
|
|
88
107
|
".kb/requirements/**/*.md",
|
|
@@ -121,15 +140,25 @@ async function initNextAction(projectRoot) {
|
|
|
121
140
|
// implements REQ-cli-init
|
|
122
141
|
// implements REQ-kibi-github-report-integration
|
|
123
142
|
export async function initCommand(options) {
|
|
143
|
+
if (options.format !== undefined && options.printHook === undefined) {
|
|
144
|
+
console.error("Error: --format applies only to --print-hook.");
|
|
145
|
+
return { exitCode: 1 };
|
|
146
|
+
}
|
|
124
147
|
if (options.printHook !== undefined) {
|
|
125
148
|
// Print-only mode: emit a delegation snippet for a hook Kibi does not
|
|
126
149
|
// own and touch nothing, not even the repository context.
|
|
127
|
-
// implements REQ-git-hook-effective-install
|
|
128
|
-
|
|
129
|
-
|
|
150
|
+
// implements REQ-git-hook-effective-install, REQ-git-hook-manager-integration
|
|
151
|
+
const printHook = options.printHook;
|
|
152
|
+
if (printHook !== "all" && !isManagedHookName(printHook)) {
|
|
153
|
+
console.error(`Error: unknown hook '${printHook}' for --print-hook. Expected one of: ${MANAGED_HOOK_NAMES.join(", ")}, all.`);
|
|
154
|
+
return { exitCode: 1 };
|
|
155
|
+
}
|
|
156
|
+
const format = options.format ?? "shell";
|
|
157
|
+
if (!HOOK_SNIPPET_FORMATS.includes(format)) {
|
|
158
|
+
console.error(`Error: unknown --format '${format}' for --print-hook. Expected one of: ${HOOK_SNIPPET_FORMATS.join(", ")}.`);
|
|
130
159
|
return { exitCode: 1 };
|
|
131
160
|
}
|
|
132
|
-
process.stdout.write(
|
|
161
|
+
process.stdout.write(hookManagerSnippet(format, printHook === "all" ? MANAGED_HOOK_NAMES : [printHook]));
|
|
133
162
|
return { exitCode: 0 };
|
|
134
163
|
}
|
|
135
164
|
if (options.badgeOnly === true && options.github !== true) {
|
|
@@ -202,9 +231,15 @@ export async function initCommand(options) {
|
|
|
202
231
|
ensureSymbolsManifestFile(projectRoot);
|
|
203
232
|
}
|
|
204
233
|
if (options.hooks) {
|
|
234
|
+
const manager = repoContext
|
|
235
|
+
? detectHookManager(repoContext.worktreeRoot, repoContext.effectiveHooksDir, repoContext.hooksPathConfig)
|
|
236
|
+
: null;
|
|
205
237
|
if (!repoContext) {
|
|
206
238
|
console.error("Warning: No git repository found, skipping hooks");
|
|
207
239
|
}
|
|
240
|
+
else if (manager) {
|
|
241
|
+
reportHookManager(manager, repoContext.worktreeRoot);
|
|
242
|
+
}
|
|
208
243
|
else if (!isRepositoryManagedHooksDir(repoContext)) {
|
|
209
244
|
// core.hooksPath is only the culprit when it is configured; the
|
|
210
245
|
// default hooks directory can also escape the repository through a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/persistence.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAEV,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA4HrD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/persistence.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAEV,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA4HrD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AA0HD,iFAAiF;AACjF,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,MAAM,CAAC,CA+BjB;AAED,0EAA0E;AAC1E,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,8EAA8E;AAC9E,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,SAAS,qBAAqB,EAAE,GAC9C,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAmCD,6EAA6E;AAE7E,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC,CAAC;AAEH;;;GAGG;AAEH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAmBlC;AAED,wBAAsB,eAAe,CAEnC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EACtB,OAAO,GAAE;IAAE,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAAO,GAChD,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAyRvD;AA4BD,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,MAAM,CAER;AAGD,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,kBAAkB,EAAE,qBAAqB,EAAE,EAC3C,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACvC,OAAO,CAAC;IAAE,iBAAiB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CA4P7D"}
|
|
@@ -151,6 +151,30 @@ function parsePrologList(value) {
|
|
|
151
151
|
}
|
|
152
152
|
return values;
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Assert a batch of relationships, keeping each one that succeeds, and return
|
|
156
|
+
* the 1-based positions of those that failed. Null when the store could not
|
|
157
|
+
* run the batch at all, so the caller retries every relationship.
|
|
158
|
+
*/
|
|
159
|
+
async function assertRelationshipsReporting(prolog, chunk) {
|
|
160
|
+
try {
|
|
161
|
+
const rels = chunk.map(({ rel, fromId, toId }) => `rel(${toPrologAtom(rel.type)}, ${toPrologAtom(fromId)}, ${toPrologAtom(toId)})`);
|
|
162
|
+
const result = await prolog.query(`kb_assert_relationships_reporting([${rels.join(", ")}], Failed)`);
|
|
163
|
+
// rationale: a non-string answer fails to parse below and the catch
|
|
164
|
+
// returns null too; the type test only narrows for the parser.
|
|
165
|
+
// Stryker disable next-line ConditionalExpression
|
|
166
|
+
if (!result.success || typeof result.bindings.Failed !== "string") {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const failed = parsePrologList(result.bindings.Failed).map(Number);
|
|
170
|
+
if (failed.some((position) => !Number.isInteger(position)))
|
|
171
|
+
return null;
|
|
172
|
+
return new Set(failed);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
154
178
|
function sourceLookupCandidates(sourceFile) {
|
|
155
179
|
return [
|
|
156
180
|
...new Set([
|
|
@@ -618,7 +642,25 @@ export async function persistRelationships(prolog, results, shardRelationships,
|
|
|
618
642
|
kbModified = true;
|
|
619
643
|
continue;
|
|
620
644
|
}
|
|
621
|
-
|
|
645
|
+
// One failing relationship (a dangling link is enough) rolls the whole
|
|
646
|
+
// batch back. Keep the ones that do assert in one more round trip and
|
|
647
|
+
// retry only the failures one by one for their exact errors, instead of
|
|
648
|
+
// one round trip per relationship of the batch.
|
|
649
|
+
const failedPositions = relationshipBatchRunner
|
|
650
|
+
? await assertRelationshipsReporting(prolog, chunk)
|
|
651
|
+
: null;
|
|
652
|
+
const retried = failedPositions === null
|
|
653
|
+
? chunk
|
|
654
|
+
: chunk.filter((_, index) => failedPositions.has(index + 1));
|
|
655
|
+
if (failedPositions !== null) {
|
|
656
|
+
chunk.forEach((item, index) => {
|
|
657
|
+
if (failedPositions.has(index + 1))
|
|
658
|
+
return;
|
|
659
|
+
countEdge(item.rel.type, item.fromId, item.toId);
|
|
660
|
+
kbModified = true;
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
for (const item of retried) {
|
|
622
664
|
try {
|
|
623
665
|
const single = await prolog.query(item.goal);
|
|
624
666
|
if (single.success) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CommandResult } from "../cli-command.js";
|
|
2
|
+
export type TelemetryAction = "status" | "enable" | "disable";
|
|
3
|
+
/** `kibi telemetry [status|enable|disable]`: show or persist the choice. */
|
|
4
|
+
export declare function telemetryCommand(action: string | undefined, options?: {
|
|
5
|
+
format?: "json" | "text";
|
|
6
|
+
}, env?: NodeJS.ProcessEnv): Promise<CommandResult>;
|
|
7
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/commands/telemetry.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAG9D,4EAA4E;AAC5E,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAO,EAC1C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,aAAa,CAAC,CAsBxB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// implements REQ-kibi-anonymous-usage-telemetry
|
|
2
|
+
import { TELEMETRY_DOCS_URL, setTelemetryPreference, telemetryStatus, } from "kibi-agent-core/usage-telemetry";
|
|
3
|
+
// implements REQ-kibi-anonymous-usage-telemetry
|
|
4
|
+
/** `kibi telemetry [status|enable|disable]`: show or persist the choice. */
|
|
5
|
+
export async function telemetryCommand(action, options = {}, env = process.env) {
|
|
6
|
+
const chosen = action ?? "status";
|
|
7
|
+
if (chosen !== "status" && chosen !== "enable" && chosen !== "disable") {
|
|
8
|
+
console.error(`Error: unknown telemetry action "${chosen}"; use status, enable or disable`);
|
|
9
|
+
return { exitCode: 1 };
|
|
10
|
+
}
|
|
11
|
+
if (chosen !== "status") {
|
|
12
|
+
setTelemetryPreference(chosen === "enable" ? "enabled" : "disabled", env);
|
|
13
|
+
}
|
|
14
|
+
const status = telemetryStatus(env);
|
|
15
|
+
if (options.format === "json") {
|
|
16
|
+
console.log(JSON.stringify({ ...status, docs: TELEMETRY_DOCS_URL }));
|
|
17
|
+
return { exitCode: 0 };
|
|
18
|
+
}
|
|
19
|
+
console.log(`Anonymous usage telemetry is ${status.enabled ? "on" : "off"} (${status.reason}).`);
|
|
20
|
+
console.log(`Settings and queue: ${status.directory}`);
|
|
21
|
+
console.log(`What is collected: ${TELEMETRY_DOCS_URL}`);
|
|
22
|
+
return { exitCode: 0 };
|
|
23
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-firmness.d.ts","sourceRoot":"","sources":["../src/decision-firmness.ts"],"names":[],"mappings":"AA4BA,sCAAsC;AAEtC,eAAO,MAAM,eAAe,6CAA8C,CAAC;AAG3E,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,yEAAyE;AAEzE,eAAO,MAAM,gBAAgB,EAAE,QAAoB,CAAC;AAEpD,wCAAwC;AAExC,eAAO,MAAM,cAAc,yBAA0B,CAAC;AAEtD,0EAA0E;AAE1E,eAAO,MAAM,wBAAwB,qBAAqB,CAAC;AAE3D,+DAA+D;AAE/D,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAIxC;;;;GAIG;AAEH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzE;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,
|
|
1
|
+
{"version":3,"file":"decision-firmness.d.ts","sourceRoot":"","sources":["../src/decision-firmness.ts"],"names":[],"mappings":"AA4BA,sCAAsC;AAEtC,eAAO,MAAM,eAAe,6CAA8C,CAAC;AAG3E,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,yEAAyE;AAEzE,eAAO,MAAM,gBAAgB,EAAE,QAAoB,CAAC;AAEpD,wCAAwC;AAExC,eAAO,MAAM,cAAc,yBAA0B,CAAC;AAEtD,0EAA0E;AAE1E,eAAO,MAAM,wBAAwB,qBAAqB,CAAC;AAE3D,+DAA+D;AAE/D,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAIxC;;;;GAIG;AAEH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzE;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAEtD;AAED;;;;;;;GAOG;AAEH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,OAAO,GACf,QAAQ,GAAG,SAAS,CAItB;AAED;;;;GAIG;AAEH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAEzE;AAED,uEAAuE;AAEvE,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI9D;AAED;;;GAGG;AAEH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,MAAM,GAAG,SAAS,CAcrB;AAMD;;;GAGG;AAEH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBxD"}
|