kibi-cli 0.1.7 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/cli.js +4 -2
  2. package/dist/commands/branch.d.ts +4 -1
  3. package/dist/commands/branch.d.ts.map +1 -1
  4. package/dist/commands/branch.js +62 -12
  5. package/dist/commands/check.d.ts.map +1 -1
  6. package/dist/commands/check.js +27 -3
  7. package/dist/commands/doctor.d.ts.map +1 -1
  8. package/dist/commands/doctor.js +57 -0
  9. package/dist/commands/gc.d.ts.map +1 -1
  10. package/dist/commands/gc.js +24 -1
  11. package/dist/commands/init-helpers.d.ts.map +1 -1
  12. package/dist/commands/init-helpers.js +46 -28
  13. package/dist/commands/init.d.ts.map +1 -1
  14. package/dist/commands/init.js +23 -2
  15. package/dist/commands/query.d.ts.map +1 -1
  16. package/dist/commands/query.js +19 -10
  17. package/dist/commands/sync.d.ts +3 -1
  18. package/dist/commands/sync.d.ts.map +1 -1
  19. package/dist/commands/sync.js +348 -203
  20. package/dist/diagnostics.d.ts +61 -0
  21. package/dist/diagnostics.d.ts.map +1 -0
  22. package/dist/diagnostics.js +114 -0
  23. package/dist/extractors/markdown.d.ts +1 -0
  24. package/dist/extractors/markdown.d.ts.map +1 -1
  25. package/dist/extractors/markdown.js +47 -0
  26. package/dist/prolog.d.ts +1 -0
  27. package/dist/prolog.d.ts.map +1 -1
  28. package/dist/prolog.js +82 -67
  29. package/dist/public/branch-resolver.d.ts +2 -0
  30. package/dist/public/branch-resolver.d.ts.map +1 -0
  31. package/dist/public/branch-resolver.js +1 -0
  32. package/dist/traceability/git-staged.d.ts.map +1 -1
  33. package/dist/traceability/git-staged.js +13 -3
  34. package/dist/traceability/markdown-validate.d.ts +7 -0
  35. package/dist/traceability/markdown-validate.d.ts.map +1 -0
  36. package/dist/traceability/markdown-validate.js +35 -0
  37. package/dist/utils/branch-resolver.d.ts +79 -0
  38. package/dist/utils/branch-resolver.d.ts.map +1 -0
  39. package/dist/utils/branch-resolver.js +311 -0
  40. package/dist/utils/config.d.ts +47 -0
  41. package/dist/utils/config.d.ts.map +1 -0
  42. package/dist/utils/config.js +105 -0
  43. package/package.json +21 -3
  44. package/src/public/branch-resolver.ts +1 -0
package/dist/cli.js CHANGED
@@ -67,6 +67,7 @@ program
67
67
  .command("sync")
68
68
  .description("Sync entities from documents")
69
69
  .option("--validate-only", "Perform validation without mutations")
70
+ .option("--rebuild", "Rebuild branch snapshot from scratch (discards current KB)")
70
71
  .action(async (options) => {
71
72
  await syncCommand(options);
72
73
  });
@@ -114,9 +115,10 @@ program
114
115
  .command("branch")
115
116
  .description("Manage branch KBs")
116
117
  .argument("<action>", "Action: ensure")
117
- .action(async (action) => {
118
+ .option("--from <branch>", "Source branch to copy KB from")
119
+ .action(async (action, options) => {
118
120
  if (action === "ensure") {
119
- await branchEnsureCommand();
121
+ await branchEnsureCommand(options);
120
122
  }
121
123
  });
122
124
  program.parse(process.argv);
@@ -1,3 +1,6 @@
1
- export declare function branchEnsureCommand(): Promise<void>;
1
+ export interface BranchEnsureOptions {
2
+ from?: string;
3
+ }
4
+ export declare function branchEnsureCommand(options?: BranchEnsureOptions): Promise<void>;
2
5
  export default branchEnsureCommand;
3
6
  //# sourceMappingURL=branch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/commands/branch.ts"],"names":[],"mappings":"AAiDA,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBzD;AAED,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/commands/branch.ts"],"names":[],"mappings":"AAwDA,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA+DD,wBAAsB,mBAAmB,CACvC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED,eAAe,mBAAmB,CAAC"}
@@ -42,25 +42,75 @@
42
42
  fi
43
43
  done
44
44
  */
45
- import { execSync } from "node:child_process";
46
45
  import * as fs from "node:fs";
47
46
  import * as path from "node:path";
48
- export async function branchEnsureCommand() {
49
- const branch = execSync("git branch --show-current", {
50
- encoding: "utf-8",
51
- }).trim();
47
+ import { copyCleanSnapshot, getBranchDiagnostic, isValidBranchName, resolveActiveBranch, resolveDefaultBranch, } from "../utils/branch-resolver.js";
48
+ import { loadConfig } from "../utils/config.js";
49
+ function resolveExplicitFromBranch(fromBranch) {
50
+ if (!isValidBranchName(fromBranch)) {
51
+ console.warn(`Warning: invalid branch name provided via --from: '${fromBranch}'`);
52
+ return null;
53
+ }
54
+ const fromPath = path.join(process.cwd(), ".kb/branches", fromBranch);
55
+ if (fs.existsSync(fromPath)) {
56
+ return fromBranch;
57
+ }
58
+ console.warn(`Warning: --from branch '${fromBranch}' KB does not exist`);
59
+ return null;
60
+ }
61
+ function resolveDefaultSourceBranch() {
62
+ const config = loadConfig(process.cwd());
63
+ const defaultResult = resolveDefaultBranch(process.cwd(), config);
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
+ }
74
+ return null;
75
+ }
76
+ function determineSourceBranch(explicitFromBranch) {
77
+ if (explicitFromBranch) {
78
+ const fromResult = resolveExplicitFromBranch(explicitFromBranch);
79
+ if (fromResult) {
80
+ return fromResult;
81
+ }
82
+ }
83
+ return resolveDefaultSourceBranch();
84
+ }
85
+ function createBranchKbFromSource(sourceBranch, targetBranch) {
86
+ const sourcePath = path.join(process.cwd(), ".kb/branches", sourceBranch);
87
+ const targetPath = path.join(process.cwd(), ".kb/branches", targetBranch);
88
+ copyCleanSnapshot(sourcePath, targetPath);
89
+ console.log(`Created branch KB: ${targetBranch} (from ${sourceBranch})`);
90
+ }
91
+ function createEmptyBranchKb(branch) {
52
92
  const kbPath = path.join(process.cwd(), ".kb/branches", branch);
53
- const mainPath = path.join(process.cwd(), ".kb/branches/main");
54
- if (!fs.existsSync(mainPath)) {
55
- console.warn("Warning: main branch KB does not exist, skipping branch ensure");
93
+ fs.mkdirSync(kbPath, { recursive: true });
94
+ console.log(`Created branch KB: ${branch} (empty schema)`);
95
+ }
96
+ export async function branchEnsureCommand(options) {
97
+ const branchResult = resolveActiveBranch(process.cwd());
98
+ if ("error" in branchResult) {
99
+ console.error(getBranchDiagnostic(undefined, branchResult.error));
100
+ throw new Error(`Failed to resolve active branch: ${branchResult.error}`);
101
+ }
102
+ const currentBranch = branchResult.branch;
103
+ const kbPath = path.join(process.cwd(), ".kb/branches", currentBranch);
104
+ if (fs.existsSync(kbPath)) {
105
+ console.log(`Branch KB already exists: ${currentBranch}`);
56
106
  return;
57
107
  }
58
- if (!fs.existsSync(kbPath)) {
59
- fs.cpSync(mainPath, kbPath, { recursive: true });
60
- console.log(`Created branch KB: ${branch}`);
108
+ const sourceBranch = determineSourceBranch(options?.from);
109
+ if (sourceBranch) {
110
+ createBranchKbFromSource(sourceBranch, currentBranch);
61
111
  }
62
112
  else {
63
- console.log(`Branch KB already exists: ${branch}`);
113
+ createEmptyBranchKb(currentBranch);
64
114
  }
65
115
  }
66
116
  export default branchEnsureCommand;
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AA8DA,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,CA6MvE"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AA+DA,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,CA2OvE"}
@@ -45,6 +45,7 @@
45
45
  import * as path from "node:path";
46
46
  import { PrologProcess } from "../prolog.js";
47
47
  import { getStagedFiles } from "../traceability/git-staged.js";
48
+ import { validateStagedMarkdown } from "../traceability/markdown-validate.js";
48
49
  import { extractSymbolsFromStagedFile } from "../traceability/symbol-extract.js";
49
50
  import { cleanupTempKb, consultOverlay, createOverlayFacts, createTempKb, } from "../traceability/temp-kb.js";
50
51
  import { formatViolations as formatStagedViolations, validateStagedSymbols, } from "../traceability/validate.js";
@@ -86,9 +87,28 @@ export async function checkCommand(options) {
86
87
  console.log("No staged files found.");
87
88
  process.exit(0);
88
89
  }
89
- // Extract symbols from staged files
90
+ const codeFiles = stagedFiles.filter((f) => !f.path.endsWith(".md"));
91
+ const markdownFiles = stagedFiles.filter((f) => f.path.endsWith(".md"));
92
+ const markdownErrors = [];
93
+ for (const f of markdownFiles) {
94
+ const result = validateStagedMarkdown(f.path, f.content || "");
95
+ for (const err of result.errors) {
96
+ markdownErrors.push(err.toString());
97
+ }
98
+ }
99
+ if (markdownErrors.length > 0) {
100
+ console.log("Found embedded entity violations in staged markdown files:");
101
+ for (const err of markdownErrors) {
102
+ console.log(err);
103
+ console.log();
104
+ }
105
+ if (options.dryRun) {
106
+ process.exit(0);
107
+ }
108
+ process.exit(1);
109
+ }
90
110
  const allSymbols = [];
91
- for (const f of stagedFiles) {
111
+ for (const f of codeFiles) {
92
112
  try {
93
113
  const symbols = extractSymbolsFromStagedFile(f);
94
114
  if (symbols && symbols.length) {
@@ -99,8 +119,12 @@ export async function checkCommand(options) {
99
119
  console.error(`Error extracting symbols from staged file ${f.path}: ${e instanceof Error ? e.message : String(e)}`);
100
120
  }
101
121
  }
122
+ if (allSymbols.length === 0 && markdownFiles.length === 0) {
123
+ console.log("No exported symbols or markdown entities found in staged files.");
124
+ process.exit(0);
125
+ }
102
126
  if (allSymbols.length === 0) {
103
- console.log("No exported symbols found in staged files.");
127
+ console.log("No violations found in staged files.");
104
128
  process.exit(0);
105
129
  }
106
130
  // Create temp KB
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAsDA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAsDnD"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAsDA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DnD"}
@@ -71,6 +71,10 @@ export async function doctorCommand() {
71
71
  name: "pre-commit hook",
72
72
  check: checkPreCommitHook,
73
73
  },
74
+ {
75
+ name: "post-rewrite hook",
76
+ check: checkPostRewriteHook,
77
+ },
74
78
  ];
75
79
  console.log("Kibi Environment Diagnostics\n");
76
80
  let allPassed = true;
@@ -287,3 +291,56 @@ function checkPreCommitHook() {
287
291
  };
288
292
  }
289
293
  }
294
+ function checkPostRewriteHook() {
295
+ const postCheckoutPath = path.join(process.cwd(), ".git/hooks/post-checkout");
296
+ const postMergePath = path.join(process.cwd(), ".git/hooks/post-merge");
297
+ const postRewritePath = path.join(process.cwd(), ".git/hooks/post-rewrite");
298
+ const postCheckoutExists = existsSync(postCheckoutPath);
299
+ const postMergeExists = existsSync(postMergePath);
300
+ if (!postCheckoutExists && !postMergeExists) {
301
+ return {
302
+ passed: true,
303
+ message: "Not installed (optional)",
304
+ };
305
+ }
306
+ const postRewriteExists = existsSync(postRewritePath);
307
+ if (!postRewriteExists) {
308
+ return {
309
+ passed: false,
310
+ message: "Not installed",
311
+ remediation: "Run: kibi init --hooks",
312
+ };
313
+ }
314
+ try {
315
+ const postRewriteStats = statSync(postRewritePath);
316
+ const postRewriteExecutable = (postRewriteStats.mode & 0o111) !== 0;
317
+ // Read hook content to verify it invokes kibi
318
+ const content = readFileSync(postRewritePath, "utf-8");
319
+ const usesKibi = content.includes("kibi sync");
320
+ if (!usesKibi) {
321
+ return {
322
+ passed: false,
323
+ message: "post-rewrite hook installed but does not invoke kibi",
324
+ remediation: "Run: kibi init --hooks to install recommended hooks",
325
+ };
326
+ }
327
+ if (postRewriteExecutable) {
328
+ return {
329
+ passed: true,
330
+ message: "Installed and executable",
331
+ };
332
+ }
333
+ return {
334
+ passed: false,
335
+ message: "Installed but not executable",
336
+ remediation: "Run: chmod +x .git/hooks/post-rewrite",
337
+ };
338
+ }
339
+ catch (error) {
340
+ return {
341
+ passed: false,
342
+ message: "Unable to check hook permissions or read content",
343
+ remediation: "Run: kibi init --hooks",
344
+ };
345
+ }
346
+ }
@@ -1 +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"}
1
+ {"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../src/commands/gc.ts"],"names":[],"mappings":"AAmDA,wBAAsB,SAAS,CAAC,OAAO,EAAE;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,iBAsGA;AAED,eAAe,SAAS,CAAC"}
@@ -45,6 +45,8 @@
45
45
  import { execSync } from "node:child_process";
46
46
  import * as fs from "node:fs";
47
47
  import * as path from "node:path";
48
+ import { resolveDefaultBranch } from "../utils/branch-resolver.js";
49
+ import { loadConfig } from "../utils/config.js";
48
50
  export async function gcCommand(options) {
49
51
  // If force is true, perform deletion. Otherwise default to dry run.
50
52
  const dryRun = options?.force ? false : (options?.dryRun ?? true);
@@ -85,7 +87,28 @@ export async function gcCommand(options) {
85
87
  .readdirSync(kbRoot, { withFileTypes: true })
86
88
  .filter((d) => d.isDirectory())
87
89
  .map((d) => d.name);
88
- const staleBranches = kbBranches.filter((kb) => kb !== "main" && !gitBranches.has(kb));
90
+ // Resolve configured/default branch to protect
91
+ const config = loadConfig(process.cwd());
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));
89
112
  // Perform deletion when dryRun is false (force requested)
90
113
  const performDelete = !dryRun;
91
114
  let deletedCount = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"AAkFA,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,CAuBnE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAapD"}
1
+ {"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"AA8GA,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;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAuBnE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAiBpD"}
@@ -45,44 +45,60 @@
45
45
  import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
46
46
  import * as path from "node:path";
47
47
  import fg from "fast-glob";
48
+ import { getBranchDiagnostic, resolveActiveBranch, } from "../utils/branch-resolver.js";
49
+ import { DEFAULT_CONFIG } from "../utils/config.js";
48
50
  const POST_CHECKOUT_HOOK = `#!/bin/sh
49
- kibi sync
51
+ # post-checkout hook for kibi
52
+ # Parameters: old_ref new_ref branch_flag
53
+ # branch_flag is 1 for branch checkout, 0 for file checkout
54
+
55
+ old_ref=$1
56
+ new_ref=$2
57
+ branch_flag=$3
58
+
59
+ if [ "$branch_flag" = "1" ]; then
60
+ # 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/\^.*//')
62
+
63
+ # Basic validation: non-empty and does not contain ~ or ^
64
+ if [ -n "$old_branch" ] && echo "$old_branch" | grep -qv '[~^]'; then
65
+ kibi branch ensure --from "$old_branch" && kibi sync
66
+ else
67
+ kibi branch ensure && kibi sync
68
+ fi
69
+ fi
50
70
  `;
51
71
  const POST_MERGE_HOOK = `#!/bin/sh
72
+ # post-merge hook for kibi
73
+ # Parameter: squash_flag (not used)
74
+
52
75
  kibi sync
53
76
  `;
77
+ const POST_REWRITE_HOOK = `#!/bin/sh
78
+ # post-rewrite hook for kibi
79
+ # Triggered after git rebase, git commit --amend, etc.
80
+ # Parameter: rewrite_type (rebase or amend)
81
+
82
+ rewrite_type=$1
83
+
84
+ if [ "$rewrite_type" = "rebase" ]; then
85
+ kibi sync
86
+ fi
87
+ `;
54
88
  const PRE_COMMIT_HOOK = `#!/bin/sh
89
+ # pre-commit hook for kibi
90
+ # Blocks commits if kibi check finds violations
91
+
55
92
  set -e
56
93
  kibi check --staged
57
94
  `;
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
95
  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";
96
+ const result = resolveActiveBranch(cwd);
97
+ if ("error" in result) {
98
+ console.error(getBranchDiagnostic(undefined, result.error));
99
+ throw new Error(`Failed to resolve active branch: ${result.error}`);
84
100
  }
85
- return currentBranch;
101
+ return result.branch;
86
102
  }
87
103
  export function createKbDirectoryStructure(kbDir, currentBranch) {
88
104
  mkdirSync(kbDir, { recursive: true });
@@ -144,9 +160,11 @@ export function installGitHooks(gitDir) {
144
160
  mkdirSync(hooksDir, { recursive: true });
145
161
  const postCheckoutPath = path.join(hooksDir, "post-checkout");
146
162
  const postMergePath = path.join(hooksDir, "post-merge");
163
+ const postRewritePath = path.join(hooksDir, "post-rewrite");
147
164
  const preCommitPath = path.join(hooksDir, "pre-commit");
148
165
  installHook(postCheckoutPath, POST_CHECKOUT_HOOK.replace("#!/bin/sh\n", ""));
149
166
  installHook(postMergePath, POST_MERGE_HOOK.replace("#!/bin/sh\n", ""));
167
+ installHook(postRewritePath, POST_REWRITE_HOOK.replace("#!/bin/sh\n", ""));
150
168
  installHook(preCommitPath, PRE_COMMIT_HOOK.replace("#!/bin/sh\n", ""));
151
- console.log("✓ Installed git hooks (pre-commit, post-checkout, post-merge)");
169
+ console.log("✓ Installed git hooks (pre-commit, post-checkout, post-merge, post-rewrite)");
152
170
  }
@@ -1 +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"}
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,CA6DrE"}
@@ -45,13 +45,34 @@
45
45
  import { existsSync } from "node:fs";
46
46
  import * as path from "node:path";
47
47
  import { fileURLToPath } from "node:url";
48
- import { copySchemaFiles, createConfigFile, createKbDirectoryStructure, getCurrentBranch, installGitHooks, updateGitIgnore, } from "./init-helpers.js";
48
+ import { resolveActiveBranch } from "../utils/branch-resolver.js";
49
+ import { copySchemaFiles, createConfigFile, createKbDirectoryStructure, installGitHooks, updateGitIgnore, } from "./init-helpers.js";
49
50
  const __filename = fileURLToPath(import.meta.url);
50
51
  const __dirname = path.dirname(__filename);
51
52
  export async function initCommand(options) {
52
53
  const kbDir = path.join(process.cwd(), ".kb");
53
54
  const kbExists = existsSync(kbDir);
54
- const currentBranch = await getCurrentBranch();
55
+ // Resolve branch: allow non-git repos to use default "main" for init
56
+ let currentBranch;
57
+ const result = resolveActiveBranch();
58
+ if ("error" in result) {
59
+ const isNonGitError = result.code === "NOT_A_GIT_REPO" || result.code === "GIT_NOT_AVAILABLE";
60
+ if (isNonGitError) {
61
+ // For init command, use "main" as default branch when not in a git repo.
62
+ // This allows initialization before git init, which is useful for first-time setup.
63
+ console.warn("Warning: Not in a git repository");
64
+ console.warn("Using 'main' as default branch. Run 'kibi sync' in a git repo for proper branch-aware behavior.");
65
+ currentBranch = "main";
66
+ }
67
+ else {
68
+ console.error("Error: Failed to resolve the active git branch.");
69
+ console.error(result.error);
70
+ process.exit(1);
71
+ }
72
+ }
73
+ else {
74
+ currentBranch = result.branch;
75
+ }
55
76
  try {
56
77
  if (!kbExists) {
57
78
  createKbDirectoryStructure(kbDir, currentBranch);
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAoDA,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,CAwKf"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAqDA,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,CAmLf"}
@@ -46,24 +46,33 @@ import * as path from "node:path";
46
46
  import Table from "cli-table3";
47
47
  import { PrologProcess } from "../prolog.js";
48
48
  import relationshipSchema from "../public/schemas/relationship.js";
49
+ import { resolveActiveBranch } from "../utils/branch-resolver.js";
49
50
  const REL_TYPES = relationshipSchema.properties.type.enum;
50
51
  export async function queryCommand(type, options) {
51
52
  try {
52
53
  const prolog = new PrologProcess({ timeout: 120000 });
53
54
  await prolog.start();
54
55
  await prolog.query("set_prolog_flag(answer_write_options, [max_depth(0), spacing(next_argument)])");
55
- let currentBranch = "main";
56
- try {
57
- const { execSync } = await import("node:child_process");
58
- currentBranch = execSync("git branch --show-current", {
59
- cwd: process.cwd(),
60
- encoding: "utf8",
61
- }).trim();
62
- if (!currentBranch || currentBranch === "master")
56
+ // Resolve branch: allow non-git repos to use default "main" for query
57
+ let currentBranch;
58
+ const branchResult = resolveActiveBranch();
59
+ if ("error" in branchResult) {
60
+ const isNonGitError = branchResult.code === "NOT_A_GIT_REPO" ||
61
+ branchResult.code === "GIT_NOT_AVAILABLE";
62
+ if (isNonGitError) {
63
+ // For query command, use "main" as default branch when git is not available
64
+ // or the current directory is not a git repository. This allows querying
65
+ // after init in a non-git directory.
63
66
  currentBranch = "main";
67
+ }
68
+ else {
69
+ console.error(`Error: Failed to resolve active branch:\n${branchResult.error}`);
70
+ await prolog.terminate();
71
+ process.exit(1);
72
+ }
64
73
  }
65
- catch {
66
- currentBranch = "main";
74
+ else {
75
+ currentBranch = branchResult.branch;
67
76
  }
68
77
  const kbPath = path.join(process.cwd(), `.kb/branches/${currentBranch}`);
69
78
  const attachResult = await prolog.query(`kb_attach('${kbPath}')`);
@@ -1,7 +1,9 @@
1
+ import { type SyncSummary } from "../diagnostics.js";
1
2
  export declare class SyncError extends Error {
2
3
  constructor(message: string);
3
4
  }
4
5
  export declare function syncCommand(options?: {
5
6
  validateOnly?: boolean;
6
- }): Promise<void>;
7
+ rebuild?: boolean;
8
+ }): Promise<SyncSummary>;
7
9
  //# sourceMappingURL=sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAgEA,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AA6FD,wBAAsB,WAAW,CAC/B,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,OAAO,CAAC;CACnB,GACL,OAAO,CAAC,IAAI,CAAC,CAkaf"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAmEA,OAAO,EAEL,KAAK,WAAW,EAMjB,MAAM,mBAAmB,CAAC;AAc3B,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAwLD,wBAAsB,WAAW,CAC/B,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,GACL,OAAO,CAAC,WAAW,CAAC,CAyhBtB"}