kibi-cli 0.1.7 → 0.2.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.js +4 -2
- package/dist/commands/branch.d.ts +4 -1
- package/dist/commands/branch.d.ts.map +1 -1
- package/dist/commands/branch.js +62 -12
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +27 -3
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +57 -0
- package/dist/commands/gc.d.ts.map +1 -1
- package/dist/commands/gc.js +24 -1
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +46 -28
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +23 -2
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +19 -10
- package/dist/commands/sync.d.ts +3 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +348 -203
- package/dist/diagnostics.d.ts +61 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +114 -0
- package/dist/extractors/markdown.d.ts +1 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +47 -0
- package/dist/public/branch-resolver.d.ts +2 -0
- package/dist/public/branch-resolver.d.ts.map +1 -0
- package/dist/public/branch-resolver.js +1 -0
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +13 -3
- package/dist/traceability/markdown-validate.d.ts +7 -0
- package/dist/traceability/markdown-validate.d.ts.map +1 -0
- package/dist/traceability/markdown-validate.js +35 -0
- package/dist/utils/branch-resolver.d.ts +79 -0
- package/dist/utils/branch-resolver.d.ts.map +1 -0
- package/dist/utils/branch-resolver.js +311 -0
- package/dist/utils/config.d.ts +47 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +105 -0
- package/package.json +13 -1
- package/src/public/branch-resolver.ts +1 -0
|
@@ -0,0 +1,311 @@
|
|
|
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 { execSync } from "node:child_process";
|
|
19
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, } from "node:fs";
|
|
20
|
+
import * as path from "node:path";
|
|
21
|
+
// Files to exclude when copying branch snapshots (volatile artifacts)
|
|
22
|
+
const VOLATILE_ARTIFACTS = new Set([
|
|
23
|
+
"sync-cache.json",
|
|
24
|
+
"journal.log",
|
|
25
|
+
"audit.log",
|
|
26
|
+
"lock",
|
|
27
|
+
"lockfile",
|
|
28
|
+
".lock",
|
|
29
|
+
]);
|
|
30
|
+
// File extensions to exclude
|
|
31
|
+
const VOLATILE_EXTENSIONS = new Set([".lock", ".tmp", ".temp", ".pid"]);
|
|
32
|
+
/**
|
|
33
|
+
* Check if a file should be excluded from clean snapshot copy.
|
|
34
|
+
*/
|
|
35
|
+
function isVolatileArtifact(fileName) {
|
|
36
|
+
if (VOLATILE_ARTIFACTS.has(fileName))
|
|
37
|
+
return true;
|
|
38
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
39
|
+
if (VOLATILE_EXTENSIONS.has(ext))
|
|
40
|
+
return true;
|
|
41
|
+
// Journal files with timestamps: journal-*.log
|
|
42
|
+
if (fileName.startsWith("journal-") && fileName.endsWith(".log"))
|
|
43
|
+
return true;
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the active branch according to ADR-012 precedence:
|
|
48
|
+
* 1. KIBI_BRANCH env var (if set)
|
|
49
|
+
* 2. Git active branch (from git branch --show-current)
|
|
50
|
+
* 3. Diagnostic failure (no silent fallback)
|
|
51
|
+
*
|
|
52
|
+
* @param workspaceRoot - The workspace root directory
|
|
53
|
+
* @returns BranchResolutionResult with either the branch name or an error
|
|
54
|
+
*/
|
|
55
|
+
export function resolveActiveBranch(workspaceRoot = process.cwd()) {
|
|
56
|
+
// 1. Check KIBI_BRANCH env var first (highest precedence)
|
|
57
|
+
const envBranch = process.env.KIBI_BRANCH?.trim();
|
|
58
|
+
if (envBranch) {
|
|
59
|
+
// Validate the env branch name
|
|
60
|
+
if (!isValidBranchName(envBranch)) {
|
|
61
|
+
return {
|
|
62
|
+
error: `Invalid branch name from KIBI_BRANCH environment variable: '${envBranch}'`,
|
|
63
|
+
code: "ENV_OVERRIDE",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { branch: envBranch };
|
|
67
|
+
}
|
|
68
|
+
// 2. Try to get the current git branch
|
|
69
|
+
try {
|
|
70
|
+
const branch = execSync("git branch --show-current", {
|
|
71
|
+
cwd: workspaceRoot,
|
|
72
|
+
encoding: "utf8",
|
|
73
|
+
timeout: 5000,
|
|
74
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
75
|
+
}).trim();
|
|
76
|
+
if (!branch) {
|
|
77
|
+
// Empty result means detached HEAD
|
|
78
|
+
return {
|
|
79
|
+
error: getBranchDiagnostic(undefined, "Git is in detached HEAD state"),
|
|
80
|
+
code: "DETACHED_HEAD",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Validate the branch name
|
|
84
|
+
if (!isValidBranchName(branch)) {
|
|
85
|
+
return {
|
|
86
|
+
error: `Invalid branch name detected: '${branch}'`,
|
|
87
|
+
code: "UNKNOWN_ERROR",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Normalize 'master' to 'main' for consistency
|
|
91
|
+
const normalizedBranch = branch === "master" ? "main" : branch;
|
|
92
|
+
return { branch: normalizedBranch };
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
// Try alternative: git rev-parse --abbrev-ref HEAD
|
|
96
|
+
try {
|
|
97
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
98
|
+
cwd: workspaceRoot,
|
|
99
|
+
encoding: "utf8",
|
|
100
|
+
timeout: 5000,
|
|
101
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
102
|
+
}).trim();
|
|
103
|
+
if (branch === "HEAD") {
|
|
104
|
+
return {
|
|
105
|
+
error: getBranchDiagnostic(undefined, "Git is in detached HEAD state"),
|
|
106
|
+
code: "DETACHED_HEAD",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (!branch || branch === "") {
|
|
110
|
+
return {
|
|
111
|
+
error: getBranchDiagnostic(undefined, "Unable to determine git branch"),
|
|
112
|
+
code: "UNBORN_BRANCH",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (!isValidBranchName(branch)) {
|
|
116
|
+
return {
|
|
117
|
+
error: `Invalid branch name detected: '${branch}'`,
|
|
118
|
+
code: "UNKNOWN_ERROR",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// Normalize 'master' to 'main' for consistency
|
|
122
|
+
const normalizedBranch = branch === "master" ? "main" : branch;
|
|
123
|
+
return { branch: normalizedBranch };
|
|
124
|
+
}
|
|
125
|
+
catch (fallbackError) {
|
|
126
|
+
// Determine specific error type
|
|
127
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
128
|
+
if (errorMessage.includes("not a git repository")) {
|
|
129
|
+
return {
|
|
130
|
+
error: getBranchDiagnostic(undefined, "Not a git repository"),
|
|
131
|
+
code: "NOT_A_GIT_REPO",
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (errorMessage.includes("command not found") ||
|
|
135
|
+
errorMessage.includes("ENOENT")) {
|
|
136
|
+
return {
|
|
137
|
+
error: getBranchDiagnostic(undefined, "Git is not installed or not available in PATH"),
|
|
138
|
+
code: "GIT_NOT_AVAILABLE",
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
error: getBranchDiagnostic(undefined, errorMessage),
|
|
143
|
+
code: "UNKNOWN_ERROR",
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Check if the repository is in detached HEAD state.
|
|
150
|
+
*
|
|
151
|
+
* @param workspaceRoot - The workspace root directory
|
|
152
|
+
* @returns true if in detached HEAD, false otherwise
|
|
153
|
+
*/
|
|
154
|
+
export function isDetachedHead(workspaceRoot = process.cwd()) {
|
|
155
|
+
try {
|
|
156
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
157
|
+
cwd: workspaceRoot,
|
|
158
|
+
encoding: "utf8",
|
|
159
|
+
timeout: 5000,
|
|
160
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
161
|
+
}).trim();
|
|
162
|
+
return branch === "HEAD";
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get a detailed diagnostic message for branch resolution failures.
|
|
170
|
+
*
|
|
171
|
+
* @param branch - The branch that was detected (if any)
|
|
172
|
+
* @param error - The error message or context
|
|
173
|
+
* @returns A formatted diagnostic message
|
|
174
|
+
*/
|
|
175
|
+
export function getBranchDiagnostic(branch, error) {
|
|
176
|
+
const lines = ["Branch Resolution Failed", "", `Reason: ${error}`];
|
|
177
|
+
if (branch) {
|
|
178
|
+
lines.push(`Detected branch: ${branch}`);
|
|
179
|
+
}
|
|
180
|
+
lines.push("", "Resolution options:", "1. Set KIBI_BRANCH environment variable to explicitly specify the branch:", " export KIBI_BRANCH=main", "", "2. Ensure you are in a git repository with a valid checked-out branch", "", "3. If in detached HEAD state, create or checkout a branch:", " git checkout -b my-branch", "", "4. For non-git workspaces, always use KIBI_BRANCH:", " KIBI_BRANCH=feature-branch kibi sync");
|
|
181
|
+
return lines.join("\n");
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Validate a branch name for safety and correctness.
|
|
185
|
+
*
|
|
186
|
+
* @param name - The branch name to validate
|
|
187
|
+
* @returns true if valid, false otherwise
|
|
188
|
+
*/
|
|
189
|
+
export function isValidBranchName(name) {
|
|
190
|
+
if (!name || name.length === 0 || name.length > 255)
|
|
191
|
+
return false;
|
|
192
|
+
// Reject path traversal attempts
|
|
193
|
+
if (name.includes("..") || path.isAbsolute(name) || name.startsWith("/")) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
// Only allow safe characters
|
|
197
|
+
if (!/^[a-zA-Z0-9._\-/+]+$/.test(name))
|
|
198
|
+
return false;
|
|
199
|
+
// Reject problematic patterns
|
|
200
|
+
if (name.includes("//") ||
|
|
201
|
+
name.endsWith("/") ||
|
|
202
|
+
name.endsWith(".") ||
|
|
203
|
+
name.includes("\\") ||
|
|
204
|
+
name.startsWith("-")) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Copy a clean snapshot from source branch to target branch, excluding
|
|
211
|
+
* volatile artifacts like sync-cache.json, lock files, and journal files.
|
|
212
|
+
*
|
|
213
|
+
* @param sourcePath - Path to the source branch KB directory
|
|
214
|
+
* @param targetPath - Path to the target branch KB directory
|
|
215
|
+
* @throws Error if the copy fails
|
|
216
|
+
*/
|
|
217
|
+
export function copyCleanSnapshot(sourcePath, targetPath) {
|
|
218
|
+
if (!existsSync(sourcePath)) {
|
|
219
|
+
throw new Error(`Source branch KB does not exist: ${sourcePath}`);
|
|
220
|
+
}
|
|
221
|
+
// Create target directory
|
|
222
|
+
mkdirSync(targetPath, { recursive: true });
|
|
223
|
+
// Recursively copy, excluding volatile artifacts
|
|
224
|
+
copyDirectoryClean(sourcePath, targetPath);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Recursively copy a directory, excluding volatile artifacts.
|
|
228
|
+
*/
|
|
229
|
+
function copyDirectoryClean(sourceDir, targetDir) {
|
|
230
|
+
const entries = readdirSync(sourceDir);
|
|
231
|
+
for (const entry of entries) {
|
|
232
|
+
// Skip volatile artifacts
|
|
233
|
+
if (isVolatileArtifact(entry)) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const sourcePath = path.join(sourceDir, entry);
|
|
237
|
+
const targetPath = path.join(targetDir, entry);
|
|
238
|
+
const stat = statSync(sourcePath);
|
|
239
|
+
if (stat.isDirectory()) {
|
|
240
|
+
// Recursively copy subdirectory
|
|
241
|
+
mkdirSync(targetPath, { recursive: true });
|
|
242
|
+
copyDirectoryClean(sourcePath, targetPath);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Copy file
|
|
246
|
+
copyFileSync(sourcePath, targetPath);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get the list of files that would be excluded from a clean snapshot copy.
|
|
252
|
+
* Useful for debugging and testing.
|
|
253
|
+
*
|
|
254
|
+
* @returns Array of volatile artifact patterns
|
|
255
|
+
*/
|
|
256
|
+
export function getVolatileArtifactPatterns() {
|
|
257
|
+
return [
|
|
258
|
+
...Array.from(VOLATILE_ARTIFACTS),
|
|
259
|
+
...Array.from(VOLATILE_EXTENSIONS).map((ext) => `*${ext}`),
|
|
260
|
+
"journal-*.log",
|
|
261
|
+
];
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Resolve the default branch using the following precedence:
|
|
265
|
+
* 1. Configured defaultBranch from config (if set and valid)
|
|
266
|
+
* 2. Git remote HEAD (refs/remotes/origin/HEAD)
|
|
267
|
+
* 3. Fallback to "main"
|
|
268
|
+
*
|
|
269
|
+
* Unlike resolveActiveBranch, this does NOT normalize branch names.
|
|
270
|
+
* Configured names are returned verbatim.
|
|
271
|
+
*
|
|
272
|
+
* @param cwd - The working directory to resolve the default branch
|
|
273
|
+
* @param config - Optional config with defaultBranch
|
|
274
|
+
* @returns BranchResolutionResult with either the branch name or an error
|
|
275
|
+
*/
|
|
276
|
+
export function resolveDefaultBranch(cwd = process.cwd(), config) {
|
|
277
|
+
// 1. Check config.defaultBranch first (highest precedence)
|
|
278
|
+
const configuredBranch = config?.defaultBranch?.trim();
|
|
279
|
+
if (configuredBranch) {
|
|
280
|
+
if (!isValidBranchName(configuredBranch)) {
|
|
281
|
+
return {
|
|
282
|
+
error: `Invalid defaultBranch configured in .kb/config.json: '${configuredBranch}'`,
|
|
283
|
+
code: "INVALID_CONFIG",
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// Return configured branch verbatim (no normalization)
|
|
287
|
+
return { branch: configuredBranch };
|
|
288
|
+
}
|
|
289
|
+
// 2. Try to get the remote default branch from origin/HEAD
|
|
290
|
+
try {
|
|
291
|
+
const remoteHead = execSync("git symbolic-ref refs/remotes/origin/HEAD", {
|
|
292
|
+
cwd,
|
|
293
|
+
encoding: "utf8",
|
|
294
|
+
timeout: 5000,
|
|
295
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
296
|
+
}).trim();
|
|
297
|
+
// Parse refs/remotes/origin/BRANCH_NAME -> BRANCH_NAME
|
|
298
|
+
const match = remoteHead.match(/^refs\/remotes\/origin\/(.+)$/);
|
|
299
|
+
if (match) {
|
|
300
|
+
const branch = match[1];
|
|
301
|
+
if (isValidBranchName(branch)) {
|
|
302
|
+
return { branch };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// origin/HEAD doesn't exist or command failed, fall through to fallback
|
|
308
|
+
}
|
|
309
|
+
// 3. Final fallback to "main"
|
|
310
|
+
return { branch: "main" };
|
|
311
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration paths for entity documentation directories.
|
|
3
|
+
*/
|
|
4
|
+
export interface KbConfigPaths {
|
|
5
|
+
requirements?: string;
|
|
6
|
+
scenarios?: string;
|
|
7
|
+
tests?: string;
|
|
8
|
+
adr?: string;
|
|
9
|
+
flags?: string;
|
|
10
|
+
events?: string;
|
|
11
|
+
facts?: string;
|
|
12
|
+
symbols?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Shared configuration for Kibi.
|
|
16
|
+
* Stored in .kb/config.json
|
|
17
|
+
*/
|
|
18
|
+
export interface KbConfig {
|
|
19
|
+
paths: KbConfigPaths;
|
|
20
|
+
defaultBranch?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Default configuration values for new repositories.
|
|
24
|
+
*/
|
|
25
|
+
export declare const DEFAULT_CONFIG: KbConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Default paths used by sync command (backward compatible glob patterns).
|
|
28
|
+
*/
|
|
29
|
+
export declare const DEFAULT_SYNC_PATHS: KbConfigPaths;
|
|
30
|
+
/**
|
|
31
|
+
* Load and parse the Kibi configuration from .kb/config.json.
|
|
32
|
+
* Falls back to DEFAULT_CONFIG if the file doesn't exist or is invalid.
|
|
33
|
+
*
|
|
34
|
+
* @param cwd - The working directory to look for .kb/config.json
|
|
35
|
+
* @returns The merged configuration (defaults + user config)
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadConfig(cwd?: string): KbConfig;
|
|
38
|
+
/**
|
|
39
|
+
* Load sync configuration with fallback to glob patterns.
|
|
40
|
+
* This is used by sync.ts to maintain backward compatibility with
|
|
41
|
+
* older config files that may use glob patterns.
|
|
42
|
+
*
|
|
43
|
+
* @param cwd - The working directory to look for .kb/config.json
|
|
44
|
+
* @returns The merged configuration with sync-compatible paths
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadSyncConfig(cwd?: string): KbConfig;
|
|
47
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAqBA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,QAY5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,aAShC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAqBhE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAqBpE"}
|
|
@@ -0,0 +1,105 @@
|
|
|
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 { existsSync, readFileSync } from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
/**
|
|
21
|
+
* Default configuration values for new repositories.
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_CONFIG = {
|
|
24
|
+
paths: {
|
|
25
|
+
requirements: "documentation/requirements",
|
|
26
|
+
scenarios: "documentation/scenarios",
|
|
27
|
+
tests: "documentation/tests",
|
|
28
|
+
adr: "documentation/adr",
|
|
29
|
+
flags: "documentation/flags",
|
|
30
|
+
events: "documentation/events",
|
|
31
|
+
facts: "documentation/facts",
|
|
32
|
+
symbols: "documentation/symbols.yaml",
|
|
33
|
+
},
|
|
34
|
+
defaultBranch: undefined,
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Default paths used by sync command (backward compatible glob patterns).
|
|
38
|
+
*/
|
|
39
|
+
export const DEFAULT_SYNC_PATHS = {
|
|
40
|
+
requirements: "requirements/**/*.md",
|
|
41
|
+
scenarios: "scenarios/**/*.md",
|
|
42
|
+
tests: "tests/**/*.md",
|
|
43
|
+
adr: "adr/**/*.md",
|
|
44
|
+
flags: "flags/**/*.md",
|
|
45
|
+
events: "events/**/*.md",
|
|
46
|
+
facts: "facts/**/*.md",
|
|
47
|
+
symbols: "symbols.yaml",
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Load and parse the Kibi configuration from .kb/config.json.
|
|
51
|
+
* Falls back to DEFAULT_CONFIG if the file doesn't exist or is invalid.
|
|
52
|
+
*
|
|
53
|
+
* @param cwd - The working directory to look for .kb/config.json
|
|
54
|
+
* @returns The merged configuration (defaults + user config)
|
|
55
|
+
*/
|
|
56
|
+
export function loadConfig(cwd = process.cwd()) {
|
|
57
|
+
const configPath = path.join(cwd, ".kb/config.json");
|
|
58
|
+
let userConfig = {};
|
|
59
|
+
if (existsSync(configPath)) {
|
|
60
|
+
try {
|
|
61
|
+
const content = readFileSync(configPath, "utf8");
|
|
62
|
+
userConfig = JSON.parse(content);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Invalid config, use defaults
|
|
66
|
+
userConfig = {};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
paths: {
|
|
71
|
+
...DEFAULT_CONFIG.paths,
|
|
72
|
+
...userConfig.paths,
|
|
73
|
+
},
|
|
74
|
+
defaultBranch: userConfig.defaultBranch,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Load sync configuration with fallback to glob patterns.
|
|
79
|
+
* This is used by sync.ts to maintain backward compatibility with
|
|
80
|
+
* older config files that may use glob patterns.
|
|
81
|
+
*
|
|
82
|
+
* @param cwd - The working directory to look for .kb/config.json
|
|
83
|
+
* @returns The merged configuration with sync-compatible paths
|
|
84
|
+
*/
|
|
85
|
+
export function loadSyncConfig(cwd = process.cwd()) {
|
|
86
|
+
const configPath = path.join(cwd, ".kb/config.json");
|
|
87
|
+
let userConfig = {};
|
|
88
|
+
if (existsSync(configPath)) {
|
|
89
|
+
try {
|
|
90
|
+
const content = readFileSync(configPath, "utf8");
|
|
91
|
+
userConfig = JSON.parse(content);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Invalid config, use defaults
|
|
95
|
+
userConfig = {};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
paths: {
|
|
100
|
+
...DEFAULT_SYNC_PATHS,
|
|
101
|
+
...userConfig.paths,
|
|
102
|
+
},
|
|
103
|
+
defaultBranch: userConfig.defaultBranch,
|
|
104
|
+
};
|
|
105
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -36,6 +36,18 @@
|
|
|
36
36
|
"./schemas/relationship": {
|
|
37
37
|
"types": "./dist/public/schemas/relationship.d.ts",
|
|
38
38
|
"default": "./dist/public/schemas/relationship.js"
|
|
39
|
+
},
|
|
40
|
+
"./utils/branch-resolver": {
|
|
41
|
+
"types": "./dist/public/branch-resolver.d.ts",
|
|
42
|
+
"default": "./dist/public/branch-resolver.js"
|
|
43
|
+
},
|
|
44
|
+
"./public/branch-resolver": {
|
|
45
|
+
"types": "./dist/public/branch-resolver.d.ts",
|
|
46
|
+
"default": "./dist/public/branch-resolver.js"
|
|
47
|
+
},
|
|
48
|
+
"./diagnostics": {
|
|
49
|
+
"types": "./dist/diagnostics.d.ts",
|
|
50
|
+
"default": "./dist/diagnostics.js"
|
|
39
51
|
}
|
|
40
52
|
},
|
|
41
53
|
"types": "./dist/cli.d.ts",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../utils/branch-resolver.js";
|