kibi-cli 0.2.3 → 0.2.6
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.js +3 -28
- package/dist/commands/aggregated-checks.d.ts +4 -1
- package/dist/commands/aggregated-checks.d.ts.map +1 -1
- package/dist/commands/aggregated-checks.js +13 -3
- package/dist/commands/branch.d.ts.map +1 -1
- package/dist/commands/branch.js +3 -41
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +55 -45
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +0 -27
- package/dist/commands/gc.d.ts.map +1 -1
- package/dist/commands/gc.js +2 -51
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +23 -36
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +0 -27
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +7 -288
- package/dist/commands/sync/cache.d.ts +13 -0
- package/dist/commands/sync/cache.d.ts.map +1 -0
- package/dist/commands/sync/cache.js +76 -0
- package/dist/commands/sync/discovery.d.ts +8 -0
- package/dist/commands/sync/discovery.d.ts.map +1 -0
- package/dist/commands/sync/discovery.js +50 -0
- package/dist/commands/sync/extraction.d.ts +11 -0
- package/dist/commands/sync/extraction.d.ts.map +1 -0
- package/dist/commands/sync/extraction.js +69 -0
- package/dist/commands/sync/manifest.d.ts +5 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -0
- package/dist/commands/sync/manifest.js +118 -0
- package/dist/commands/sync/persistence.d.ts +16 -0
- package/dist/commands/sync/persistence.d.ts.map +1 -0
- package/dist/commands/sync/persistence.js +188 -0
- package/dist/commands/sync/staging.d.ts +4 -0
- package/dist/commands/sync/staging.d.ts.map +1 -0
- package/dist/commands/sync/staging.js +86 -0
- package/dist/commands/sync.d.ts +2 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +69 -501
- package/dist/extractors/manifest.d.ts +0 -1
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +41 -49
- package/dist/extractors/markdown.d.ts +0 -1
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +28 -50
- package/dist/extractors/relationships.d.ts +39 -0
- package/dist/extractors/relationships.d.ts.map +1 -0
- package/dist/extractors/relationships.js +137 -0
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +0 -27
- package/dist/extractors/symbols-ts.d.ts.map +1 -1
- package/dist/extractors/symbols-ts.js +0 -27
- package/dist/kb/target-resolver.d.ts +80 -0
- package/dist/kb/target-resolver.d.ts.map +1 -0
- package/dist/kb/target-resolver.js +313 -0
- package/dist/prolog/codec.d.ts +63 -0
- package/dist/prolog/codec.d.ts.map +1 -0
- package/dist/prolog/codec.js +434 -0
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +0 -27
- package/dist/public/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/public/extractors/symbols-coordinator.js +0 -27
- package/dist/public/prolog/index.d.ts.map +1 -1
- package/dist/public/prolog/index.js +0 -27
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +10 -27
- package/dist/public/schemas/relationship.d.ts.map +1 -1
- package/dist/public/schemas/relationship.js +0 -27
- package/dist/query/service.d.ts +35 -0
- package/dist/query/service.d.ts.map +1 -0
- package/dist/query/service.js +149 -0
- package/dist/relationships/shards.d.ts +68 -0
- package/dist/relationships/shards.d.ts.map +1 -0
- package/dist/relationships/shards.js +263 -0
- package/dist/traceability/git-staged.d.ts +4 -1
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +24 -11
- package/dist/types/changeset.d.ts.map +1 -1
- package/dist/types/entities.d.ts.map +1 -1
- package/dist/types/relationships.d.ts.map +1 -1
- package/dist/utils/branch-resolver.d.ts +4 -0
- package/dist/utils/branch-resolver.d.ts.map +1 -1
- package/dist/utils/branch-resolver.js +4 -0
- package/dist/utils/config.d.ts +10 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +27 -1
- package/dist/utils/rule-registry.d.ts +47 -0
- package/dist/utils/rule-registry.d.ts.map +1 -0
- package/dist/utils/rule-registry.js +139 -0
- package/package.json +6 -2
- package/schema/config.json +161 -0
- package/src/public/extractors/symbols-coordinator.ts +0 -27
- package/src/public/prolog/index.ts +0 -27
- package/src/public/schemas/entity.ts +10 -27
- package/src/public/schemas/relationship.ts +0 -27
- package/src/schemas/entity.schema.json +11 -1
package/dist/cli.js
CHANGED
|
@@ -15,33 +15,7 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
|
|
19
|
-
How to apply this header to source files (examples)
|
|
20
|
-
|
|
21
|
-
1) Prepend header to a single file (POSIX shells):
|
|
22
|
-
|
|
23
|
-
cat LICENSE_HEADER.txt "$FILE" > "$FILE".with-header && mv "$FILE".with-header "$FILE"
|
|
24
|
-
|
|
25
|
-
2) Apply to multiple files (example: the project's main entry files):
|
|
26
|
-
|
|
27
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp packages/cli/src/*.ts packages/mcp/src/*.ts; do
|
|
28
|
-
if [ -f "$f" ]; then
|
|
29
|
-
cp "$f" "$f".bak
|
|
30
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
3) Avoid duplicating the header: run a quick guard to only add if missing
|
|
35
|
-
|
|
36
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp; do
|
|
37
|
-
if [ -f "$f" ]; then
|
|
38
|
-
if ! head -n 5 "$f" | grep -q "Copyright (C) 2026 Piotr Franczyk"; then
|
|
39
|
-
cp "$f" "$f".bak
|
|
40
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
41
|
-
fi
|
|
42
|
-
fi
|
|
43
|
-
done
|
|
44
|
-
*/
|
|
18
|
+
import { readFileSync } from "node:fs";
|
|
45
19
|
import { Command } from "commander";
|
|
46
20
|
import { branchEnsureCommand } from "./commands/branch.js";
|
|
47
21
|
import { checkCommand } from "./commands/check.js";
|
|
@@ -50,7 +24,8 @@ import { gcCommand } from "./commands/gc.js";
|
|
|
50
24
|
import { initCommand } from "./commands/init.js";
|
|
51
25
|
import { queryCommand } from "./commands/query.js";
|
|
52
26
|
import { syncCommand } from "./commands/sync.js";
|
|
53
|
-
const
|
|
27
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
28
|
+
const VERSION = packageJson.version ?? "0.1.0";
|
|
54
29
|
const program = new Command();
|
|
55
30
|
program
|
|
56
31
|
.name("kibi")
|
|
@@ -4,6 +4,9 @@ import type { Violation } from "./check.js";
|
|
|
4
4
|
* Run all checks using the aggregated Prolog predicates.
|
|
5
5
|
* This makes a single Prolog call and parses JSON output, significantly
|
|
6
6
|
* faster than running individual checks with multiple round-trips.
|
|
7
|
+
* @param prolog - The Prolog process
|
|
8
|
+
* @param rulesAllowlist - Set of rule names to run (null = all)
|
|
9
|
+
* @param requireAdr - Whether to require ADR constraints for symbol-traceability
|
|
7
10
|
*/
|
|
8
|
-
export declare function runAggregatedChecks(prolog: PrologProcess, rulesAllowlist: Set<string> | null): Promise<Violation[]>;
|
|
11
|
+
export declare function runAggregatedChecks(prolog: PrologProcess, rulesAllowlist: Set<string> | null, requireAdr?: boolean): Promise<Violation[]>;
|
|
9
12
|
//# sourceMappingURL=aggregated-checks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregated-checks.d.ts","sourceRoot":"","sources":["../../src/commands/aggregated-checks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"aggregated-checks.d.ts","sourceRoot":"","sources":["../../src/commands/aggregated-checks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,cAAc,CAAC;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAU5C;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,EACrB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAClC,UAAU,UAAQ,GACjB,OAAO,CAAC,SAAS,EAAE,CAAC,CA2DtB"}
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { resolveKbPlPath } from "../prolog.js";
|
|
3
|
+
import { escapeAtom } from "../prolog/codec.js";
|
|
3
4
|
/**
|
|
4
5
|
* Run all checks using the aggregated Prolog predicates.
|
|
5
6
|
* This makes a single Prolog call and parses JSON output, significantly
|
|
6
7
|
* faster than running individual checks with multiple round-trips.
|
|
8
|
+
* @param prolog - The Prolog process
|
|
9
|
+
* @param rulesAllowlist - Set of rule names to run (null = all)
|
|
10
|
+
* @param requireAdr - Whether to require ADR constraints for symbol-traceability
|
|
7
11
|
*/
|
|
8
|
-
export async function runAggregatedChecks(prolog, rulesAllowlist) {
|
|
12
|
+
export async function runAggregatedChecks(prolog, rulesAllowlist, requireAdr = false) {
|
|
9
13
|
const violations = [];
|
|
10
14
|
const checksPlPath = path.join(path.dirname(resolveKbPlPath()), "checks.pl");
|
|
11
|
-
const checksPlPathEscaped = checksPlPath
|
|
12
|
-
|
|
15
|
+
const checksPlPathEscaped = escapeAtom(checksPlPath);
|
|
16
|
+
// Use check_all_json_with_options if available, otherwise fall back to check_all_json
|
|
17
|
+
const requireAdrStr = requireAdr ? "true" : "false";
|
|
18
|
+
const query = `(use_module('${checksPlPathEscaped}'),
|
|
19
|
+
( predicate_property(checks:check_all_json_with_options(_, _), _)
|
|
20
|
+
-> call(checks:check_all_json_with_options(JsonString, ${requireAdrStr}))
|
|
21
|
+
; call(checks:check_all_json(JsonString))
|
|
22
|
+
))`;
|
|
13
23
|
try {
|
|
14
24
|
const result = await prolog.query(query);
|
|
15
25
|
if (!result.success) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/commands/branch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/commands/branch.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAmDD,wBAAsB,mBAAmB,CACvC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED,eAAe,mBAAmB,CAAC"}
|
package/dist/commands/branch.js
CHANGED
|
@@ -15,37 +15,9 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
/*
|
|
19
|
-
How to apply this header to source files (examples)
|
|
20
|
-
|
|
21
|
-
1) Prepend header to a single file (POSIX shells):
|
|
22
|
-
|
|
23
|
-
cat LICENSE_HEADER.txt "$FILE" > "$FILE".with-header && mv "$FILE".with-header "$FILE"
|
|
24
|
-
|
|
25
|
-
2) Apply to multiple files (example: the project's main entry files):
|
|
26
|
-
|
|
27
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp packages/cli/src/*.ts packages/mcp/src/*.ts; do
|
|
28
|
-
if [ -f "$f" ]; then
|
|
29
|
-
cp "$f" "$f".bak
|
|
30
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
3) Avoid duplicating the header: run a quick guard to only add if missing
|
|
35
|
-
|
|
36
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp; do
|
|
37
|
-
if [ -f "$f" ]; then
|
|
38
|
-
if ! head -n 5 "$f" | grep -q "Copyright (C) 2026 Piotr Franczyk"; then
|
|
39
|
-
cp "$f" "$f".bak
|
|
40
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
41
|
-
fi
|
|
42
|
-
fi
|
|
43
|
-
done
|
|
44
|
-
*/
|
|
45
18
|
import * as fs from "node:fs";
|
|
46
19
|
import * as path from "node:path";
|
|
47
|
-
import { copyCleanSnapshot, getBranchDiagnostic, isValidBranchName, resolveActiveBranch,
|
|
48
|
-
import { loadConfig } from "../utils/config.js";
|
|
20
|
+
import { copyCleanSnapshot, getBranchDiagnostic, isValidBranchName, resolveActiveBranch, } from "../utils/branch-resolver.js";
|
|
49
21
|
function resolveExplicitFromBranch(fromBranch) {
|
|
50
22
|
if (!isValidBranchName(fromBranch)) {
|
|
51
23
|
console.warn(`Warning: invalid branch name provided via --from: '${fromBranch}'`);
|
|
@@ -59,18 +31,8 @@ function resolveExplicitFromBranch(fromBranch) {
|
|
|
59
31
|
return null;
|
|
60
32
|
}
|
|
61
33
|
function resolveDefaultSourceBranch() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if ("branch" in defaultResult) {
|
|
65
|
-
const defaultBranch = defaultResult.branch;
|
|
66
|
-
const defaultPath = path.join(process.cwd(), ".kb/branches", defaultBranch);
|
|
67
|
-
if (fs.existsSync(defaultPath)) {
|
|
68
|
-
return defaultBranch;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
console.warn(`Warning: could not resolve default branch: ${defaultResult.error}`);
|
|
73
|
-
}
|
|
34
|
+
// No default branch concept - branches are independent
|
|
35
|
+
// When --from is not specified, an empty branch KB will be created
|
|
74
36
|
return null;
|
|
75
37
|
}
|
|
76
38
|
function determineSourceBranch(explicitFromBranch) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AA2CA,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuPvE"}
|
package/dist/commands/check.js
CHANGED
|
@@ -15,40 +15,16 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
/*
|
|
19
|
-
How to apply this header to source files (examples)
|
|
20
|
-
|
|
21
|
-
1) Prepend header to a single file (POSIX shells):
|
|
22
|
-
|
|
23
|
-
cat LICENSE_HEADER.txt "$FILE" > "$FILE".with-header && mv "$FILE".with-header "$FILE"
|
|
24
|
-
|
|
25
|
-
2) Apply to multiple files (example: the project's main entry files):
|
|
26
|
-
|
|
27
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp packages/cli/src/*.ts packages/mcp/src/*.ts; do
|
|
28
|
-
if [ -f "$f" ]; then
|
|
29
|
-
cp "$f" "$f".bak
|
|
30
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
3) Avoid duplicating the header: run a quick guard to only add if missing
|
|
35
|
-
|
|
36
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp; do
|
|
37
|
-
if [ -f "$f" ]; then
|
|
38
|
-
if ! head -n 5 "$f" | grep -q "Copyright (C) 2026 Piotr Franczyk"; then
|
|
39
|
-
cp "$f" "$f".bak
|
|
40
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
41
|
-
fi
|
|
42
|
-
fi
|
|
43
|
-
done
|
|
44
|
-
*/
|
|
45
18
|
import * as path from "node:path";
|
|
46
19
|
import { PrologProcess } from "../prolog.js";
|
|
20
|
+
import { escapeAtom } from "../prolog/codec.js";
|
|
47
21
|
import { getStagedFiles } from "../traceability/git-staged.js";
|
|
48
22
|
import { validateStagedMarkdown } from "../traceability/markdown-validate.js";
|
|
49
23
|
import { extractSymbolsFromStagedFile } from "../traceability/symbol-extract.js";
|
|
50
24
|
import { cleanupTempKb, consultOverlay, createOverlayFacts, createTempKb, } from "../traceability/temp-kb.js";
|
|
51
25
|
import { formatViolations as formatStagedViolations, validateStagedSymbols, } from "../traceability/validate.js";
|
|
26
|
+
import { loadConfig } from "../utils/config.js";
|
|
27
|
+
import { RULES, getEffectiveRules, } from "../utils/rule-registry.js";
|
|
52
28
|
import { runAggregatedChecks } from "./aggregated-checks.js";
|
|
53
29
|
import { getCurrentBranch } from "./init-helpers.js";
|
|
54
30
|
export async function checkCommand(options) {
|
|
@@ -111,7 +87,7 @@ export async function checkCommand(options) {
|
|
|
111
87
|
for (const f of codeFiles) {
|
|
112
88
|
try {
|
|
113
89
|
const symbols = extractSymbolsFromStagedFile(f);
|
|
114
|
-
if (symbols
|
|
90
|
+
if (symbols?.length) {
|
|
115
91
|
allSymbols.push(...symbols);
|
|
116
92
|
}
|
|
117
93
|
}
|
|
@@ -165,7 +141,7 @@ export async function checkCommand(options) {
|
|
|
165
141
|
}
|
|
166
142
|
const prolog = new PrologProcess({ timeout: 120000 });
|
|
167
143
|
await prolog.start();
|
|
168
|
-
const kbPathEscaped = resolvedKbPath
|
|
144
|
+
const kbPathEscaped = escapeAtom(resolvedKbPath);
|
|
169
145
|
const attachResult = await prolog.query(`kb_attach('${kbPathEscaped}')`);
|
|
170
146
|
if (!attachResult.success) {
|
|
171
147
|
await prolog.terminate();
|
|
@@ -173,21 +149,20 @@ export async function checkCommand(options) {
|
|
|
173
149
|
process.exit(1);
|
|
174
150
|
}
|
|
175
151
|
const violations = [];
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
152
|
+
// Load config to get rule enablement settings
|
|
153
|
+
const config = loadConfig(process.cwd());
|
|
154
|
+
const checksConfig = config.checks ?? {
|
|
155
|
+
rules: Object.fromEntries(RULES.map((r) => [r.name, true])),
|
|
156
|
+
symbolTraceability: { requireAdr: false },
|
|
157
|
+
};
|
|
158
|
+
// Get effective rules based on config and CLI --rules filter
|
|
159
|
+
const effectiveRules = getEffectiveRules(checksConfig.rules, options.rules);
|
|
185
160
|
// Helper to conditionally run a check by name
|
|
186
161
|
async function runCheck(name, fn, ...args) {
|
|
187
|
-
if (
|
|
162
|
+
if (!effectiveRules.has(name))
|
|
188
163
|
return;
|
|
189
164
|
const res = await fn(prolog, ...args);
|
|
190
|
-
if (res
|
|
165
|
+
if (res?.length)
|
|
191
166
|
violations.push(...res);
|
|
192
167
|
}
|
|
193
168
|
// Use aggregated checks (single Prolog call) when possible for better performance
|
|
@@ -196,27 +171,32 @@ export async function checkCommand(options) {
|
|
|
196
171
|
const supportedRules = [
|
|
197
172
|
"must-priority-coverage",
|
|
198
173
|
"symbol-coverage",
|
|
174
|
+
"symbol-traceability",
|
|
199
175
|
"no-dangling-refs",
|
|
200
176
|
"no-cycles",
|
|
201
177
|
"required-fields",
|
|
202
178
|
"deprecated-adr-no-successor",
|
|
203
179
|
"domain-contradictions",
|
|
204
180
|
];
|
|
205
|
-
const canUseAggregated =
|
|
206
|
-
Array.from(rulesAllowlist).every((r) => supportedRules.includes(r));
|
|
181
|
+
const canUseAggregated = Array.from(effectiveRules).every((r) => supportedRules.includes(r));
|
|
207
182
|
if (canUseAggregated) {
|
|
208
183
|
// Fast path: single Prolog call returning all violations
|
|
209
|
-
|
|
184
|
+
// Pass the requireAdr option for symbol-traceability
|
|
185
|
+
const aggregatedViolations = await runAggregatedChecks(prolog, effectiveRules, checksConfig.symbolTraceability?.requireAdr ?? false);
|
|
210
186
|
violations.push(...aggregatedViolations);
|
|
211
187
|
}
|
|
212
188
|
else {
|
|
213
189
|
// Legacy path: individual checks for backward compatibility
|
|
214
190
|
await runCheck("must-priority-coverage", checkMustPriorityCoverage);
|
|
215
191
|
await runCheck("symbol-coverage", checkSymbolCoverage);
|
|
192
|
+
await runCheck("symbol-traceability", (p) => checkSymbolTraceability(p, checksConfig.symbolTraceability?.requireAdr ?? false));
|
|
216
193
|
await runCheck("no-dangling-refs", checkNoDanglingRefs);
|
|
217
194
|
await runCheck("no-cycles", checkNoCycles);
|
|
218
195
|
const allEntityIds = await getAllEntityIds(prolog);
|
|
219
|
-
|
|
196
|
+
if (effectiveRules.has("required-fields")) {
|
|
197
|
+
const requiredViolations = await checkRequiredFields(prolog, allEntityIds);
|
|
198
|
+
violations.push(...requiredViolations);
|
|
199
|
+
}
|
|
220
200
|
await runCheck("deprecated-adr-no-successor", checkDeprecatedAdrs);
|
|
221
201
|
await runCheck("domain-contradictions", checkDomainContradictions);
|
|
222
202
|
}
|
|
@@ -525,7 +505,7 @@ async function checkDeprecatedAdrs(prolog) {
|
|
|
525
505
|
violations.push({
|
|
526
506
|
rule: "deprecated-adr-no-successor",
|
|
527
507
|
entityId: adrId,
|
|
528
|
-
description: "
|
|
508
|
+
description: "Superseded/deprecated ADR has no successor — add a supersedes link from the replacement ADR",
|
|
529
509
|
suggestion: `Create a new ADR and add: links: [{type: supersedes, target: ${adrId}}]`,
|
|
530
510
|
source,
|
|
531
511
|
});
|
|
@@ -573,6 +553,36 @@ async function checkSymbolCoverage(prolog) {
|
|
|
573
553
|
}
|
|
574
554
|
return violations;
|
|
575
555
|
}
|
|
556
|
+
async function checkSymbolTraceability(prolog, requireAdr) {
|
|
557
|
+
const violations = [];
|
|
558
|
+
const requireAdrStr = requireAdr ? "true" : "false";
|
|
559
|
+
const result = await prolog.query(`findall(violation(Rule, EntityId, Desc, Sugg, Src),
|
|
560
|
+
checks:symbol_traceability_violation(${requireAdrStr}, violation(Rule, EntityId, Desc, Sugg, Src)),
|
|
561
|
+
Violations)`);
|
|
562
|
+
if (!result.success || !result.bindings.Violations) {
|
|
563
|
+
return violations;
|
|
564
|
+
}
|
|
565
|
+
// Parse the violations from Prolog format
|
|
566
|
+
const violationsStr = result.bindings.Violations;
|
|
567
|
+
if (violationsStr && violationsStr !== "[]") {
|
|
568
|
+
// Parse each violation term
|
|
569
|
+
const violationRegex = /violation\(([^,]+),'?([^',]+)'?,([^,]+),([^,]+),'?([^']*)'?\)/g;
|
|
570
|
+
let match;
|
|
571
|
+
do {
|
|
572
|
+
match = violationRegex.exec(violationsStr);
|
|
573
|
+
if (match) {
|
|
574
|
+
violations.push({
|
|
575
|
+
rule: match[1].trim().replace(/^'|'$/g, ""),
|
|
576
|
+
entityId: match[2].trim(),
|
|
577
|
+
description: match[3].trim().replace(/^"|"$/g, ""),
|
|
578
|
+
suggestion: match[4].trim().replace(/^"|"$/g, ""),
|
|
579
|
+
source: match[5].trim() || undefined,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
} while (match);
|
|
583
|
+
}
|
|
584
|
+
return violations;
|
|
585
|
+
}
|
|
576
586
|
function parseTripleRows(raw) {
|
|
577
587
|
const cleaned = raw.trim();
|
|
578
588
|
if (cleaned === "[]" || cleaned.length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AA2BA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DnD"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -15,33 +15,6 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
/*
|
|
19
|
-
How to apply this header to source files (examples)
|
|
20
|
-
|
|
21
|
-
1) Prepend header to a single file (POSIX shells):
|
|
22
|
-
|
|
23
|
-
cat LICENSE_HEADER.txt "$FILE" > "$FILE".with-header && mv "$FILE".with-header "$FILE"
|
|
24
|
-
|
|
25
|
-
2) Apply to multiple files (example: the project's main entry files):
|
|
26
|
-
|
|
27
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp packages/cli/src/*.ts packages/mcp/src/*.ts; do
|
|
28
|
-
if [ -f "$f" ]; then
|
|
29
|
-
cp "$f" "$f".bak
|
|
30
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
3) Avoid duplicating the header: run a quick guard to only add if missing
|
|
35
|
-
|
|
36
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp; do
|
|
37
|
-
if [ -f "$f" ]; then
|
|
38
|
-
if ! head -n 5 "$f" | grep -q "Copyright (C) 2026 Piotr Franczyk"; then
|
|
39
|
-
cp "$f" "$f".bak
|
|
40
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
41
|
-
fi
|
|
42
|
-
fi
|
|
43
|
-
done
|
|
44
|
-
*/
|
|
45
18
|
import { execSync } from "node:child_process";
|
|
46
19
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
47
20
|
import * as path from "node:path";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../src/commands/gc.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../src/commands/gc.ts"],"names":[],"mappings":"AAsBA,wBAAsB,SAAS,CAAC,OAAO,EAAE;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,iBAiFA;AAED,eAAe,SAAS,CAAC"}
|
package/dist/commands/gc.js
CHANGED
|
@@ -15,38 +15,9 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
/*
|
|
19
|
-
How to apply this header to source files (examples)
|
|
20
|
-
|
|
21
|
-
1) Prepend header to a single file (POSIX shells):
|
|
22
|
-
|
|
23
|
-
cat LICENSE_HEADER.txt "$FILE" > "$FILE".with-header && mv "$FILE".with-header "$FILE"
|
|
24
|
-
|
|
25
|
-
2) Apply to multiple files (example: the project's main entry files):
|
|
26
|
-
|
|
27
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp packages/cli/src/*.ts packages/mcp/src/*.ts; do
|
|
28
|
-
if [ -f "$f" ]; then
|
|
29
|
-
cp "$f" "$f".bak
|
|
30
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
3) Avoid duplicating the header: run a quick guard to only add if missing
|
|
35
|
-
|
|
36
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp; do
|
|
37
|
-
if [ -f "$f" ]; then
|
|
38
|
-
if ! head -n 5 "$f" | grep -q "Copyright (C) 2026 Piotr Franczyk"; then
|
|
39
|
-
cp "$f" "$f".bak
|
|
40
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
41
|
-
fi
|
|
42
|
-
fi
|
|
43
|
-
done
|
|
44
|
-
*/
|
|
45
18
|
import { execSync } from "node:child_process";
|
|
46
19
|
import * as fs from "node:fs";
|
|
47
20
|
import * as path from "node:path";
|
|
48
|
-
import { resolveDefaultBranch } from "../utils/branch-resolver.js";
|
|
49
|
-
import { loadConfig } from "../utils/config.js";
|
|
50
21
|
export async function gcCommand(options) {
|
|
51
22
|
// If force is true, perform deletion. Otherwise default to dry run.
|
|
52
23
|
const dryRun = options?.force ? false : (options?.dryRun ?? true);
|
|
@@ -87,28 +58,8 @@ export async function gcCommand(options) {
|
|
|
87
58
|
.readdirSync(kbRoot, { withFileTypes: true })
|
|
88
59
|
.filter((d) => d.isDirectory())
|
|
89
60
|
.map((d) => d.name);
|
|
90
|
-
//
|
|
91
|
-
const
|
|
92
|
-
// Prefer explicit configured defaultBranch if set
|
|
93
|
-
const configured = config?.defaultBranch;
|
|
94
|
-
let defaultBranch;
|
|
95
|
-
if (configured && typeof configured === "string" && configured.trim()) {
|
|
96
|
-
defaultBranch = configured.trim();
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
const resolved = resolveDefaultBranch(process.cwd(), config);
|
|
100
|
-
defaultBranch =
|
|
101
|
-
"branch" in resolved && typeof resolved.branch === "string"
|
|
102
|
-
? resolved.branch
|
|
103
|
-
: "main";
|
|
104
|
-
}
|
|
105
|
-
// Protect resolved branch and its 'master'->'main' normalization
|
|
106
|
-
const protectedBranches = new Set([defaultBranch]);
|
|
107
|
-
if (defaultBranch === "main")
|
|
108
|
-
protectedBranches.add("master");
|
|
109
|
-
if (defaultBranch === "master")
|
|
110
|
-
protectedBranches.add("main");
|
|
111
|
-
const staleBranches = kbBranches.filter((kb) => !protectedBranches.has(kb) && !gitBranches.has(kb));
|
|
61
|
+
// Branches are protected if they exist in git - no default branch concept
|
|
62
|
+
const staleBranches = kbBranches.filter((kb) => !gitBranches.has(kb));
|
|
112
63
|
// Perform deletion when dryRun is false (force requested)
|
|
113
64
|
const performDelete = !dryRun;
|
|
114
65
|
let deletedCount = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"AAmFA,wBAAsB,gBAAgB,CACpC,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACpB,IAAI,CAQN;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAMpD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAajD;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAYf;AASD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAmCnE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAiBpD"}
|
|
@@ -15,33 +15,6 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
/*
|
|
19
|
-
How to apply this header to source files (examples)
|
|
20
|
-
|
|
21
|
-
1) Prepend header to a single file (POSIX shells):
|
|
22
|
-
|
|
23
|
-
cat LICENSE_HEADER.txt "$FILE" > "$FILE".with-header && mv "$FILE".with-header "$FILE"
|
|
24
|
-
|
|
25
|
-
2) Apply to multiple files (example: the project's main entry files):
|
|
26
|
-
|
|
27
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp packages/cli/src/*.ts packages/mcp/src/*.ts; do
|
|
28
|
-
if [ -f "$f" ]; then
|
|
29
|
-
cp "$f" "$f".bak
|
|
30
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
3) Avoid duplicating the header: run a quick guard to only add if missing
|
|
35
|
-
|
|
36
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp; do
|
|
37
|
-
if [ -f "$f" ]; then
|
|
38
|
-
if ! head -n 5 "$f" | grep -q "Copyright (C) 2026 Piotr Franczyk"; then
|
|
39
|
-
cp "$f" "$f".bak
|
|
40
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
41
|
-
fi
|
|
42
|
-
fi
|
|
43
|
-
done
|
|
44
|
-
*/
|
|
45
18
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
|
|
46
19
|
import * as path from "node:path";
|
|
47
20
|
import fg from "fast-glob";
|
|
@@ -58,7 +31,7 @@ branch_flag=$3
|
|
|
58
31
|
|
|
59
32
|
if [ "$branch_flag" = "1" ]; then
|
|
60
33
|
# Try to resolve the branch we just left (strip decorations like ^ and ~)
|
|
61
|
-
old_branch=$(git name-rev --name-only "$old_ref" 2>/dev/null | sed 's
|
|
34
|
+
old_branch=$(git name-rev --name-only "$old_ref" 2>/dev/null | sed 's/\\^.*//')
|
|
62
35
|
|
|
63
36
|
# Basic validation: non-empty and does not contain ~ or ^
|
|
64
37
|
if [ -n "$old_branch" ] && echo "$old_branch" | grep -qv '[~^]'; then
|
|
@@ -138,21 +111,35 @@ export async function copySchemaFiles(kbDir, schemaSourceDir) {
|
|
|
138
111
|
}
|
|
139
112
|
console.log(`✓ Copied ${schemaFiles.length} schema files`);
|
|
140
113
|
}
|
|
114
|
+
const KIBI_HOOK_BEGIN = "# BEGIN kibi-managed";
|
|
115
|
+
const KIBI_HOOK_END = "# END kibi-managed";
|
|
116
|
+
function escapeRegex(s) {
|
|
117
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
118
|
+
}
|
|
141
119
|
export function installHook(hookPath, content) {
|
|
120
|
+
const kibiSection = `${KIBI_HOOK_BEGIN}\n${content}\n${KIBI_HOOK_END}`;
|
|
142
121
|
if (existsSync(hookPath)) {
|
|
143
122
|
const existing = readFileSync(hookPath, "utf8");
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
});
|
|
123
|
+
if (existing.includes(KIBI_HOOK_BEGIN) &&
|
|
124
|
+
existing.includes(KIBI_HOOK_END)) {
|
|
125
|
+
// Replace only the kibi-managed section, preserving any user-authored content
|
|
126
|
+
const updated = existing.replace(new RegExp(`${escapeRegex(KIBI_HOOK_BEGIN)}[\\s\\S]*?${escapeRegex(KIBI_HOOK_END)}`), kibiSection);
|
|
127
|
+
writeFileSync(hookPath, updated, { mode: 0o755 });
|
|
128
|
+
}
|
|
129
|
+
else if (existing.includes("kibi branch ensure")) {
|
|
130
|
+
// Legacy format: already has the complete kibi logic, skip
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Hook exists with user content (no kibi section) - append kibi section
|
|
135
|
+
const shebang = existing.startsWith("#!/") ? "" : "#!/bin/sh\n";
|
|
136
|
+
writeFileSync(hookPath, `${shebang}${existing.trimEnd()}\n${kibiSection}\n`, { mode: 0o755 });
|
|
149
137
|
}
|
|
150
138
|
}
|
|
151
139
|
else {
|
|
152
|
-
writeFileSync(hookPath, `#!/bin/sh
|
|
153
|
-
${content}`, { mode: 0o755 });
|
|
140
|
+
writeFileSync(hookPath, `#!/bin/sh\n${kibiSection}\n`, { mode: 0o755 });
|
|
154
141
|
}
|
|
155
|
-
// Explicitly ensure hook is executable
|
|
142
|
+
// Explicitly ensure hook is executable
|
|
156
143
|
chmodSync(hookPath, 0o755);
|
|
157
144
|
}
|
|
158
145
|
export function installGitHooks(gitDir) {
|
|
@@ -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":"AAiCA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DrE"}
|
package/dist/commands/init.js
CHANGED
|
@@ -15,33 +15,6 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
/*
|
|
19
|
-
How to apply this header to source files (examples)
|
|
20
|
-
|
|
21
|
-
1) Prepend header to a single file (POSIX shells):
|
|
22
|
-
|
|
23
|
-
cat LICENSE_HEADER.txt "$FILE" > "$FILE".with-header && mv "$FILE".with-header "$FILE"
|
|
24
|
-
|
|
25
|
-
2) Apply to multiple files (example: the project's main entry files):
|
|
26
|
-
|
|
27
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp packages/cli/src/*.ts packages/mcp/src/*.ts; do
|
|
28
|
-
if [ -f "$f" ]; then
|
|
29
|
-
cp "$f" "$f".bak
|
|
30
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
3) Avoid duplicating the header: run a quick guard to only add if missing
|
|
35
|
-
|
|
36
|
-
for f in packages/cli/bin/kibi packages/mcp/bin/kibi-mcp; do
|
|
37
|
-
if [ -f "$f" ]; then
|
|
38
|
-
if ! head -n 5 "$f" | grep -q "Copyright (C) 2026 Piotr Franczyk"; then
|
|
39
|
-
cp "$f" "$f".bak
|
|
40
|
-
(cat LICENSE_HEADER.txt; echo; cat "$f" ) > "$f".new && mv "$f".new "$f"
|
|
41
|
-
fi
|
|
42
|
-
fi
|
|
43
|
-
done
|
|
44
|
-
*/
|
|
45
18
|
import { existsSync } from "node:fs";
|
|
46
19
|
import * as path from "node:path";
|
|
47
20
|
import { fileURLToPath } from "node:url";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAmCA,UAAU,YAAY;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAyKf"}
|