kibi-cli 0.1.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.
Files changed (75) hide show
  1. package/bin/kibi +19 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +117 -0
  5. package/dist/commands/branch.d.ts +3 -0
  6. package/dist/commands/branch.d.ts.map +1 -0
  7. package/dist/commands/branch.js +66 -0
  8. package/dist/commands/check.d.ts +12 -0
  9. package/dist/commands/check.d.ts.map +1 -0
  10. package/dist/commands/check.js +439 -0
  11. package/dist/commands/doctor.d.ts +2 -0
  12. package/dist/commands/doctor.d.ts.map +1 -0
  13. package/dist/commands/doctor.js +268 -0
  14. package/dist/commands/gc.d.ts +6 -0
  15. package/dist/commands/gc.d.ts.map +1 -0
  16. package/dist/commands/gc.js +117 -0
  17. package/dist/commands/init-helpers.d.ts +8 -0
  18. package/dist/commands/init-helpers.d.ts.map +1 -0
  19. package/dist/commands/init-helpers.js +150 -0
  20. package/dist/commands/init.d.ts +6 -0
  21. package/dist/commands/init.d.ts.map +1 -0
  22. package/dist/commands/init.js +85 -0
  23. package/dist/commands/query.d.ts +12 -0
  24. package/dist/commands/query.d.ts.map +1 -0
  25. package/dist/commands/query.js +469 -0
  26. package/dist/commands/sync.d.ts +7 -0
  27. package/dist/commands/sync.d.ts.map +1 -0
  28. package/dist/commands/sync.js +587 -0
  29. package/dist/extractors/manifest.d.ts +30 -0
  30. package/dist/extractors/manifest.d.ts.map +1 -0
  31. package/dist/extractors/manifest.js +122 -0
  32. package/dist/extractors/markdown.d.ts +39 -0
  33. package/dist/extractors/markdown.d.ts.map +1 -0
  34. package/dist/extractors/markdown.js +203 -0
  35. package/dist/extractors/symbols-coordinator.d.ts +4 -0
  36. package/dist/extractors/symbols-coordinator.d.ts.map +1 -0
  37. package/dist/extractors/symbols-coordinator.js +131 -0
  38. package/dist/extractors/symbols-ts.d.ts +21 -0
  39. package/dist/extractors/symbols-ts.d.ts.map +1 -0
  40. package/dist/extractors/symbols-ts.js +197 -0
  41. package/dist/prolog.d.ts +35 -0
  42. package/dist/prolog.d.ts.map +1 -0
  43. package/dist/prolog.js +328 -0
  44. package/dist/public/extractors/symbols-coordinator.d.ts +2 -0
  45. package/dist/public/extractors/symbols-coordinator.d.ts.map +1 -0
  46. package/dist/public/extractors/symbols-coordinator.js +46 -0
  47. package/dist/public/prolog/index.d.ts +2 -0
  48. package/dist/public/prolog/index.d.ts.map +1 -0
  49. package/dist/public/prolog/index.js +46 -0
  50. package/dist/public/schemas/entity.d.ts +58 -0
  51. package/dist/public/schemas/entity.d.ts.map +1 -0
  52. package/dist/public/schemas/entity.js +102 -0
  53. package/dist/public/schemas/relationship.d.ts +35 -0
  54. package/dist/public/schemas/relationship.d.ts.map +1 -0
  55. package/dist/public/schemas/relationship.js +81 -0
  56. package/dist/types/changeset.d.ts +22 -0
  57. package/dist/types/changeset.d.ts.map +1 -0
  58. package/dist/types/changeset.js +18 -0
  59. package/dist/types/entities.d.ts +40 -0
  60. package/dist/types/entities.d.ts.map +1 -0
  61. package/dist/types/entities.js +18 -0
  62. package/dist/types/relationships.d.ts +11 -0
  63. package/dist/types/relationships.d.ts.map +1 -0
  64. package/dist/types/relationships.js +18 -0
  65. package/package.json +57 -0
  66. package/schema/entities.pl +50 -0
  67. package/schema/relationships.pl +47 -0
  68. package/schema/validation.pl +49 -0
  69. package/src/public/extractors/symbols-coordinator.ts +50 -0
  70. package/src/public/prolog/index.ts +47 -0
  71. package/src/public/schemas/entity.ts +104 -0
  72. package/src/public/schemas/relationship.ts +83 -0
  73. package/src/schemas/changeset.schema.json +48 -0
  74. package/src/schemas/entity.schema.json +55 -0
  75. package/src/schemas/relationship.schema.json +34 -0
@@ -0,0 +1,268 @@
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
+ /*
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
+ import { execSync } from "node:child_process";
46
+ import { existsSync, readFileSync, statSync } from "node:fs";
47
+ import * as path from "node:path";
48
+ export async function doctorCommand() {
49
+ const checks = [
50
+ {
51
+ name: "SWI-Prolog",
52
+ check: checkSWIProlog,
53
+ },
54
+ {
55
+ name: ".kb/ directory",
56
+ check: checkKbDirectory,
57
+ },
58
+ {
59
+ name: "config.json",
60
+ check: checkConfigJson,
61
+ },
62
+ {
63
+ name: "Git repository",
64
+ check: checkGitRepository,
65
+ },
66
+ {
67
+ name: "Git hooks",
68
+ check: checkGitHooks,
69
+ },
70
+ {
71
+ name: "pre-commit hook",
72
+ check: checkPreCommitHook,
73
+ },
74
+ ];
75
+ console.log("Kibi Environment Diagnostics\n");
76
+ let allPassed = true;
77
+ for (const { name, check } of checks) {
78
+ const result = check();
79
+ const status = result.passed ? "✓" : "✗";
80
+ console.log(`${status} ${name}: ${result.message}`);
81
+ if (!result.passed) {
82
+ allPassed = false;
83
+ if (result.remediation) {
84
+ console.log(` → ${result.remediation}`);
85
+ }
86
+ }
87
+ }
88
+ console.log();
89
+ if (allPassed) {
90
+ console.log("All checks passed! Your environment is ready.");
91
+ process.exit(0);
92
+ }
93
+ else {
94
+ console.log("Some checks failed. Please address the issues above.");
95
+ process.exit(1);
96
+ }
97
+ }
98
+ function checkSWIProlog() {
99
+ try {
100
+ const output = execSync("swipl --version", { encoding: "utf-8" });
101
+ const versionMatch = output.match(/version\s+(\d+)\.(\d+)/i);
102
+ if (!versionMatch) {
103
+ return {
104
+ passed: false,
105
+ message: "Unable to parse version",
106
+ remediation: "Reinstall SWI-Prolog from https://www.swi-prolog.org/",
107
+ };
108
+ }
109
+ const major = Number.parseInt(versionMatch[1], 10);
110
+ if (major < 9) {
111
+ return {
112
+ passed: false,
113
+ message: `Version ${major}.x found (requires ≥9.0)`,
114
+ remediation: "Upgrade SWI-Prolog to version 9.0 or higher from https://www.swi-prolog.org/",
115
+ };
116
+ }
117
+ return {
118
+ passed: true,
119
+ message: `Version ${versionMatch[0]} installed`,
120
+ };
121
+ }
122
+ catch (error) {
123
+ return {
124
+ passed: false,
125
+ message: "Not installed or not in PATH",
126
+ remediation: "Install SWI-Prolog from https://www.swi-prolog.org/ and add to PATH",
127
+ };
128
+ }
129
+ }
130
+ function checkKbDirectory() {
131
+ const kbDir = path.join(process.cwd(), ".kb");
132
+ if (!existsSync(kbDir)) {
133
+ return {
134
+ passed: false,
135
+ message: "Not found",
136
+ remediation: "Run: kibi init",
137
+ };
138
+ }
139
+ return {
140
+ passed: true,
141
+ message: "Found",
142
+ };
143
+ }
144
+ function checkConfigJson() {
145
+ const configPath = path.join(process.cwd(), ".kb/config.json");
146
+ if (!existsSync(configPath)) {
147
+ return {
148
+ passed: false,
149
+ message: "Not found",
150
+ remediation: "Run: kibi init",
151
+ };
152
+ }
153
+ try {
154
+ const content = readFileSync(configPath, "utf-8");
155
+ JSON.parse(content);
156
+ return {
157
+ passed: true,
158
+ message: "Valid JSON",
159
+ };
160
+ }
161
+ catch (error) {
162
+ return {
163
+ passed: false,
164
+ message: "Invalid JSON",
165
+ remediation: "Fix .kb/config.json syntax or run: kibi init",
166
+ };
167
+ }
168
+ }
169
+ function checkGitRepository() {
170
+ try {
171
+ execSync("git status", { stdio: "pipe", cwd: process.cwd() });
172
+ return {
173
+ passed: true,
174
+ message: "Found",
175
+ };
176
+ }
177
+ catch (error) {
178
+ return {
179
+ passed: false,
180
+ message: "Not a git repository",
181
+ remediation: "Run: git init",
182
+ };
183
+ }
184
+ }
185
+ function checkGitHooks() {
186
+ const postCheckoutPath = path.join(process.cwd(), ".git/hooks/post-checkout");
187
+ const postMergePath = path.join(process.cwd(), ".git/hooks/post-merge");
188
+ const postCheckoutExists = existsSync(postCheckoutPath);
189
+ const postMergeExists = existsSync(postMergePath);
190
+ if (!postCheckoutExists && !postMergeExists) {
191
+ return {
192
+ passed: true,
193
+ message: "Not installed (optional)",
194
+ };
195
+ }
196
+ if (postCheckoutExists && postMergeExists) {
197
+ try {
198
+ const checkoutStats = statSync(postCheckoutPath);
199
+ const mergeStats = statSync(postMergePath);
200
+ const checkoutExecutable = (checkoutStats.mode & 0o111) !== 0;
201
+ const mergeExecutable = (mergeStats.mode & 0o111) !== 0;
202
+ if (checkoutExecutable && mergeExecutable) {
203
+ return {
204
+ passed: true,
205
+ message: "Installed and executable",
206
+ };
207
+ }
208
+ return {
209
+ passed: false,
210
+ message: "Installed but not executable",
211
+ remediation: "Run: chmod +x .git/hooks/post-checkout .git/hooks/post-merge",
212
+ };
213
+ }
214
+ catch (error) {
215
+ return {
216
+ passed: false,
217
+ message: "Unable to check hook permissions",
218
+ };
219
+ }
220
+ }
221
+ return {
222
+ passed: false,
223
+ message: "Partially installed",
224
+ remediation: "Run: kibi init --hooks",
225
+ };
226
+ }
227
+ function checkPreCommitHook() {
228
+ const postCheckoutPath = path.join(process.cwd(), ".git/hooks/post-checkout");
229
+ const postMergePath = path.join(process.cwd(), ".git/hooks/post-merge");
230
+ const preCommitPath = path.join(process.cwd(), ".git/hooks/pre-commit");
231
+ const postCheckoutExists = existsSync(postCheckoutPath);
232
+ const postMergeExists = existsSync(postMergePath);
233
+ if (!postCheckoutExists && !postMergeExists) {
234
+ return {
235
+ passed: true,
236
+ message: "Not installed (optional)",
237
+ };
238
+ }
239
+ const preCommitExists = existsSync(preCommitPath);
240
+ if (!preCommitExists) {
241
+ return {
242
+ passed: false,
243
+ message: "Not installed",
244
+ remediation: "Run: kibi init --hooks",
245
+ };
246
+ }
247
+ try {
248
+ const preCommitStats = statSync(preCommitPath);
249
+ const preCommitExecutable = (preCommitStats.mode & 0o111) !== 0;
250
+ if (preCommitExecutable) {
251
+ return {
252
+ passed: true,
253
+ message: "Installed and executable",
254
+ };
255
+ }
256
+ return {
257
+ passed: false,
258
+ message: "Installed but not executable",
259
+ remediation: "Run: chmod +x .git/hooks/pre-commit",
260
+ };
261
+ }
262
+ catch (error) {
263
+ return {
264
+ passed: false,
265
+ message: "Unable to check hook permissions",
266
+ };
267
+ }
268
+ }
@@ -0,0 +1,6 @@
1
+ export declare function gcCommand(options: {
2
+ dryRun?: boolean;
3
+ force?: boolean;
4
+ }): Promise<void>;
5
+ export default gcCommand;
6
+ //# sourceMappingURL=gc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../src/commands/gc.ts"],"names":[],"mappings":"AAiDA,wBAAsB,SAAS,CAAC,OAAO,EAAE;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,iBAkFA;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,117 @@
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
+ /*
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
+ import { execSync } from "node:child_process";
46
+ import * as fs from "node:fs";
47
+ import * as path from "node:path";
48
+ export async function gcCommand(options) {
49
+ // If force is true, perform deletion. Otherwise default to dry run.
50
+ const dryRun = options?.force ? false : (options?.dryRun ?? true);
51
+ try {
52
+ const kbRoot = path.resolve(process.cwd(), ".kb/branches");
53
+ if (!fs.existsSync(kbRoot)) {
54
+ console.error("No branch KBs found (.kb/branches does not exist)");
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ let gitBranches;
59
+ try {
60
+ execSync("git rev-parse --git-dir", {
61
+ encoding: "utf-8",
62
+ cwd: process.cwd(),
63
+ stdio: ["pipe", "pipe", "pipe"],
64
+ env: process.env,
65
+ });
66
+ const output = execSync("git branch --format='%(refname:short)'", {
67
+ encoding: "utf-8",
68
+ cwd: process.cwd(),
69
+ stdio: ["pipe", "pipe", "pipe"],
70
+ env: process.env,
71
+ });
72
+ gitBranches = new Set(output
73
+ .trim()
74
+ .split("\n")
75
+ .map((b) => b.trim().replace(/^'|'$/g, ""))
76
+ .filter((b) => b));
77
+ }
78
+ catch (error) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ console.error(`Not in a git repository or git command failed: ${message}`);
81
+ process.exitCode = 1;
82
+ return;
83
+ }
84
+ const kbBranches = fs
85
+ .readdirSync(kbRoot, { withFileTypes: true })
86
+ .filter((d) => d.isDirectory())
87
+ .map((d) => d.name);
88
+ const staleBranches = kbBranches.filter((kb) => kb !== "main" && !gitBranches.has(kb));
89
+ // Perform deletion when dryRun is false (force requested)
90
+ const performDelete = !dryRun;
91
+ let deletedCount = 0;
92
+ if (performDelete && staleBranches.length > 0) {
93
+ for (const branch of staleBranches) {
94
+ const branchPath = path.join(kbRoot, branch);
95
+ fs.rmSync(branchPath, { recursive: true, force: true });
96
+ deletedCount++;
97
+ }
98
+ }
99
+ if (dryRun) {
100
+ console.log(`Found ${staleBranches.length} stale branch KB(s) (dry run - not deleted)`);
101
+ if (staleBranches.length > 0) {
102
+ for (const b of staleBranches)
103
+ console.log(` - ${b}`);
104
+ }
105
+ }
106
+ else {
107
+ console.log(`Deleted ${deletedCount} stale branch KB(s)`);
108
+ }
109
+ process.exitCode = 0;
110
+ }
111
+ catch (error) {
112
+ const message = error instanceof Error ? error.message : String(error);
113
+ console.error(`Branch GC failed: ${message}`);
114
+ process.exitCode = 1;
115
+ }
116
+ }
117
+ export default gcCommand;
@@ -0,0 +1,8 @@
1
+ export declare function getCurrentBranch(cwd?: string): Promise<string>;
2
+ export declare function createKbDirectoryStructure(kbDir: string, currentBranch: string): void;
3
+ export declare function createConfigFile(kbDir: string): void;
4
+ export declare function updateGitIgnore(cwd: string): void;
5
+ export declare function copySchemaFiles(kbDir: string, schemaSourceDir: string): Promise<void>;
6
+ export declare function installHook(hookPath: string, content: string): void;
7
+ export declare function installGitHooks(gitDir: string): void;
8
+ //# sourceMappingURL=init-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"AAiFA,wBAAsB,gBAAgB,CACpC,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAejB;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;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAqBnE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAapD"}
@@ -0,0 +1,150 @@
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
+ /*
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
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
46
+ import * as path from "node:path";
47
+ import fg from "fast-glob";
48
+ const POST_CHECKOUT_HOOK = `#!/bin/sh
49
+ kibi sync
50
+ `;
51
+ const POST_MERGE_HOOK = `#!/bin/sh
52
+ kibi sync
53
+ `;
54
+ const PRE_COMMIT_HOOK = `#!/bin/sh
55
+ set -e
56
+ kibi check
57
+ `;
58
+ const DEFAULT_CONFIG = {
59
+ paths: {
60
+ requirements: "requirements",
61
+ scenarios: "scenarios",
62
+ tests: "tests",
63
+ adr: "adr",
64
+ flags: "flags",
65
+ events: "events",
66
+ facts: "facts",
67
+ symbols: "symbols.yaml",
68
+ },
69
+ };
70
+ export async function getCurrentBranch(cwd = process.cwd()) {
71
+ let currentBranch = "develop";
72
+ try {
73
+ const { execSync } = await import("node:child_process");
74
+ const branch = execSync("git branch --show-current", {
75
+ cwd,
76
+ encoding: "utf8",
77
+ }).trim();
78
+ if (branch && branch !== "master") {
79
+ currentBranch = branch;
80
+ }
81
+ }
82
+ catch {
83
+ currentBranch = "develop";
84
+ }
85
+ return currentBranch;
86
+ }
87
+ export function createKbDirectoryStructure(kbDir, currentBranch) {
88
+ mkdirSync(kbDir, { recursive: true });
89
+ mkdirSync(path.join(kbDir, "schema"), { recursive: true });
90
+ mkdirSync(path.join(kbDir, "branches", currentBranch), {
91
+ recursive: true,
92
+ });
93
+ console.log("✓ Created .kb/ directory structure");
94
+ console.log(`✓ Created branches/${currentBranch}/ directory`);
95
+ }
96
+ export function createConfigFile(kbDir) {
97
+ writeFileSync(path.join(kbDir, "config.json"), JSON.stringify(DEFAULT_CONFIG, null, 2));
98
+ console.log("✓ Created config.json with default paths");
99
+ }
100
+ export function updateGitIgnore(cwd) {
101
+ const gitignorePath = path.join(cwd, ".gitignore");
102
+ const gitignoreContent = existsSync(gitignorePath)
103
+ ? readFileSync(gitignorePath, "utf8")
104
+ : "";
105
+ if (!gitignoreContent.includes(".kb/")) {
106
+ const newContent = gitignoreContent
107
+ ? `${gitignoreContent.trimEnd()}\n.kb/\n`
108
+ : ".kb/\n";
109
+ writeFileSync(gitignorePath, newContent);
110
+ console.log("✓ Added .kb/ to .gitignore");
111
+ }
112
+ }
113
+ export async function copySchemaFiles(kbDir, schemaSourceDir) {
114
+ const schemaFiles = await fg("*.pl", {
115
+ cwd: schemaSourceDir,
116
+ absolute: false,
117
+ });
118
+ for (const file of schemaFiles) {
119
+ const sourcePath = path.join(schemaSourceDir, file);
120
+ const destPath = path.join(kbDir, "schema", file);
121
+ copyFileSync(sourcePath, destPath);
122
+ }
123
+ console.log(`✓ Copied ${schemaFiles.length} schema files`);
124
+ }
125
+ export function installHook(hookPath, content) {
126
+ if (existsSync(hookPath)) {
127
+ const existing = readFileSync(hookPath, "utf8");
128
+ if (!existing.includes("kibi")) {
129
+ writeFileSync(hookPath, `${existing}
130
+ ${content}`, {
131
+ mode: 0o755,
132
+ });
133
+ }
134
+ }
135
+ else {
136
+ writeFileSync(hookPath, `#!/bin/sh
137
+ ${content}`, { mode: 0o755 });
138
+ }
139
+ }
140
+ export function installGitHooks(gitDir) {
141
+ const hooksDir = path.join(gitDir, "hooks");
142
+ mkdirSync(hooksDir, { recursive: true });
143
+ const postCheckoutPath = path.join(hooksDir, "post-checkout");
144
+ const postMergePath = path.join(hooksDir, "post-merge");
145
+ const preCommitPath = path.join(hooksDir, "pre-commit");
146
+ installHook(postCheckoutPath, POST_CHECKOUT_HOOK.replace("#!/bin/sh\n", ""));
147
+ installHook(postMergePath, POST_MERGE_HOOK.replace("#!/bin/sh\n", ""));
148
+ installHook(preCommitPath, PRE_COMMIT_HOOK.replace("#!/bin/sh\n", ""));
149
+ console.log("✓ Installed git hooks (pre-commit, post-checkout, post-merge)");
150
+ }
@@ -0,0 +1,6 @@
1
+ interface InitOptions {
2
+ hooks?: boolean;
3
+ }
4
+ export declare function initCommand(options: InitOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA4DA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCrE"}
@@ -0,0 +1,85 @@
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
+ /*
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
+ import { existsSync } from "node:fs";
46
+ import * as path from "node:path";
47
+ import { fileURLToPath } from "node:url";
48
+ import { copySchemaFiles, createConfigFile, createKbDirectoryStructure, getCurrentBranch, installGitHooks, updateGitIgnore, } from "./init-helpers.js";
49
+ const __filename = fileURLToPath(import.meta.url);
50
+ const __dirname = path.dirname(__filename);
51
+ export async function initCommand(options) {
52
+ const kbDir = path.join(process.cwd(), ".kb");
53
+ const kbExists = existsSync(kbDir);
54
+ const currentBranch = await getCurrentBranch();
55
+ try {
56
+ if (!kbExists) {
57
+ createKbDirectoryStructure(kbDir, currentBranch);
58
+ createConfigFile(kbDir);
59
+ updateGitIgnore(process.cwd());
60
+ const schemaSourceDir = path.resolve(__dirname, "..", "..", "schema");
61
+ await copySchemaFiles(kbDir, schemaSourceDir);
62
+ }
63
+ else {
64
+ console.log("✓ .kb/ directory already exists, skipping creation");
65
+ }
66
+ if (options.hooks) {
67
+ const gitDir = path.join(process.cwd(), ".git");
68
+ if (!existsSync(gitDir)) {
69
+ console.error("Warning: No git repository found, skipping hooks");
70
+ }
71
+ else {
72
+ installGitHooks(gitDir);
73
+ }
74
+ }
75
+ console.log("\nKibi initialized successfully!");
76
+ console.log("Next steps:");
77
+ console.log(" 1. Run 'kibi doctor' to verify setup");
78
+ console.log(" 2. Run 'kibi sync' to extract entities from documents");
79
+ process.exit(0);
80
+ }
81
+ catch (error) {
82
+ console.error("Error during initialization:", error);
83
+ process.exit(1);
84
+ }
85
+ }
@@ -0,0 +1,12 @@
1
+ interface QueryOptions {
2
+ id?: string;
3
+ tag?: string;
4
+ source?: string;
5
+ relationships?: string;
6
+ format?: "json" | "table";
7
+ limit?: string;
8
+ offset?: string;
9
+ }
10
+ export declare function queryCommand(type: string | undefined, options: QueryOptions): Promise<void>;
11
+ export {};
12
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAiDA,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,CA2Jf"}