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
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Diagnostic categories for drift detection and sync failures
3
+ *
4
+ * These categories provide stable identifiers for common failure modes,
5
+ * making it easier for both CLI users and MCP consumers to understand
6
+ * and respond to issues programmatically.
7
+ */
8
+ export type DiagnosticCategory = "BRANCH_RESOLUTION_FAILURE" | "KB_MISSING" | "DOCS_NOT_INDEXED" | "INVALID_AUTHORING" | "SYNC_ERROR" | "EXTRACTION_ERROR" | "RELATIONSHIP_ERROR";
9
+ export interface Diagnostic {
10
+ category: DiagnosticCategory;
11
+ severity: "error" | "warning";
12
+ message: string;
13
+ file?: string;
14
+ suggestion?: string;
15
+ }
16
+ export interface SyncSummary {
17
+ branch: string;
18
+ commit?: string;
19
+ timestamp: string;
20
+ entityCounts: Record<string, number>;
21
+ relationshipCount: number;
22
+ success: boolean;
23
+ published: boolean;
24
+ failures: Diagnostic[];
25
+ durationMs?: number;
26
+ }
27
+ /**
28
+ * Branch resolution error codes mapping to diagnostic categories
29
+ */
30
+ export type BranchErrorCode = "DETACHED_HEAD" | "UNBORN_BRANCH" | "GIT_NOT_AVAILABLE" | "NOT_A_GIT_REPO" | "ENV_OVERRIDE" | "UNKNOWN_ERROR";
31
+ /**
32
+ * Convert branch error code to diagnostic category
33
+ */
34
+ export declare function branchErrorToDiagnostic(code: BranchErrorCode, message: string, branch?: string): Diagnostic;
35
+ /**
36
+ * Create KB_MISSING diagnostic
37
+ */
38
+ export declare function createKbMissingDiagnostic(branch: string, path: string): Diagnostic;
39
+ /**
40
+ * Create DOCS_NOT_INDEXED diagnostic
41
+ */
42
+ export declare function createDocsNotIndexedDiagnostic(docCount: number, entityCount: number): Diagnostic;
43
+ /**
44
+ * Create INVALID_AUTHORING diagnostic for embedded entities
45
+ */
46
+ export declare function createInvalidAuthoringDiagnostic(filePath: string, embeddedTypes: string[]): Diagnostic;
47
+ /**
48
+ * Format sync summary for CLI output
49
+ */
50
+ export declare function formatSyncSummary(summary: SyncSummary): string;
51
+ /**
52
+ * Format diagnostics for MCP structured response
53
+ */
54
+ export declare function formatDiagnosticsForMcp(diagnostics: Diagnostic[]): Array<{
55
+ category: string;
56
+ severity: string;
57
+ message: string;
58
+ file?: string;
59
+ suggestion?: string;
60
+ }>;
61
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,2BAA2B,GAC3B,YAAY,GACZ,kBAAkB,GAClB,mBAAmB,GACnB,YAAY,GACZ,kBAAkB,GAClB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,eAAe,GACf,eAAe,GACf,mBAAmB,GACnB,gBAAgB,GAChB,cAAc,GACd,eAAe,CAAC;AAEpB;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CASZ;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,UAAU,CAOZ;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,UAAU,CAQZ;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EAAE,GACtB,UAAU,CAQZ;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAmD9D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,UAAU,EAAE,GACxB,KAAK,CAAC;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC,CAQD"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Diagnostic categories for drift detection and sync failures
3
+ *
4
+ * These categories provide stable identifiers for common failure modes,
5
+ * making it easier for both CLI users and MCP consumers to understand
6
+ * and respond to issues programmatically.
7
+ */
8
+ /**
9
+ * Convert branch error code to diagnostic category
10
+ */
11
+ export function branchErrorToDiagnostic(code, message, branch) {
12
+ return {
13
+ category: "BRANCH_RESOLUTION_FAILURE",
14
+ severity: "error",
15
+ message: `Failed to resolve active branch: ${message}`,
16
+ suggestion: branch
17
+ ? `Detected branch: ${branch}. Set KIBI_BRANCH environment variable to explicitly specify the branch.`
18
+ : "Set KIBI_BRANCH environment variable or ensure you're in a git repository with a valid checked-out branch.",
19
+ };
20
+ }
21
+ /**
22
+ * Create KB_MISSING diagnostic
23
+ */
24
+ export function createKbMissingDiagnostic(branch, path) {
25
+ return {
26
+ category: "KB_MISSING",
27
+ severity: "warning",
28
+ message: `Branch KB does not exist for '${branch}'`,
29
+ suggestion: `Run 'kibi sync' to create the KB at ${path}`,
30
+ };
31
+ }
32
+ /**
33
+ * Create DOCS_NOT_INDEXED diagnostic
34
+ */
35
+ export function createDocsNotIndexedDiagnostic(docCount, entityCount) {
36
+ return {
37
+ category: "DOCS_NOT_INDEXED",
38
+ severity: "warning",
39
+ message: `${docCount} markdown files found but only ${entityCount} entities in KB`,
40
+ suggestion: "Some documents may have extraction errors. Run 'kibi sync --validate-only' to check.",
41
+ };
42
+ }
43
+ /**
44
+ * Create INVALID_AUTHORING diagnostic for embedded entities
45
+ */
46
+ export function createInvalidAuthoringDiagnostic(filePath, embeddedTypes) {
47
+ return {
48
+ category: "INVALID_AUTHORING",
49
+ severity: "error",
50
+ message: `Requirement contains embedded ${embeddedTypes.join(" and ")} fields`,
51
+ file: filePath,
52
+ suggestion: `Move ${embeddedTypes.join(" and ")} to separate entity files and link them using 'links' with relationship types like 'specified_by' or 'verified_by'.`,
53
+ };
54
+ }
55
+ /**
56
+ * Format sync summary for CLI output
57
+ */
58
+ export function formatSyncSummary(summary) {
59
+ const lines = [];
60
+ lines.push("═══ Sync Summary ═══");
61
+ lines.push(`Branch: ${summary.branch}`);
62
+ if (summary.commit) {
63
+ lines.push(`Commit: ${summary.commit.slice(0, 8)}`);
64
+ }
65
+ lines.push(`Timestamp: ${summary.timestamp}`);
66
+ lines.push("");
67
+ // Entity counts by type
68
+ lines.push("Entity Counts:");
69
+ const sortedTypes = Object.keys(summary.entityCounts).sort();
70
+ if (sortedTypes.length === 0) {
71
+ lines.push(" (none)");
72
+ }
73
+ else {
74
+ for (const type of sortedTypes) {
75
+ lines.push(` ${type}: ${summary.entityCounts[type]}`);
76
+ }
77
+ }
78
+ lines.push(`Total Relationships: ${summary.relationshipCount}`);
79
+ lines.push("");
80
+ // Status
81
+ lines.push(`Status: ${summary.success ? "✓ Success" : "✗ Failed"}`);
82
+ lines.push(`Published: ${summary.published ? "Yes" : "No"}`);
83
+ if (summary.durationMs !== undefined) {
84
+ lines.push(`Duration: ${summary.durationMs}ms`);
85
+ }
86
+ // Failures
87
+ if (summary.failures.length > 0) {
88
+ lines.push("");
89
+ lines.push(`Failures (${summary.failures.length}):`);
90
+ for (const failure of summary.failures) {
91
+ lines.push(` [${failure.category}] ${failure.severity.toUpperCase()}: ${failure.message}`);
92
+ if (failure.file) {
93
+ lines.push(` File: ${failure.file}`);
94
+ }
95
+ if (failure.suggestion) {
96
+ lines.push(` Suggestion: ${failure.suggestion}`);
97
+ }
98
+ }
99
+ }
100
+ lines.push("═══ End Summary ═══");
101
+ return lines.join("\n");
102
+ }
103
+ /**
104
+ * Format diagnostics for MCP structured response
105
+ */
106
+ export function formatDiagnosticsForMcp(diagnostics) {
107
+ return diagnostics.map((d) => ({
108
+ category: d.category,
109
+ severity: d.severity,
110
+ message: d.message,
111
+ file: d.file,
112
+ suggestion: d.suggestion,
113
+ }));
114
+ }
@@ -34,6 +34,7 @@ export declare class FrontmatterError extends Error {
34
34
  });
35
35
  toString(): string;
36
36
  }
37
+ export declare function detectEmbeddedEntities(data: Record<string, unknown>, entityType: string): string[];
37
38
  export declare function extractFromMarkdown(filePath: string): ExtractionResult;
38
39
  export declare function inferTypeFromPath(filePath: string): string | null;
39
40
  //# sourceMappingURL=markdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"AAiDA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IAOhC,QAAQ,EAAE,MAAM;IANlB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;gBAG5B,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE;QACR,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB;IASM,QAAQ;CAUlB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAqHtE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASjE"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"AAiDA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IAOhC,QAAQ,EAAE,MAAM;IANlB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;gBAG5B,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE;QACR,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB;IASM,QAAQ;CAUlB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CA8CV;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAkItE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASjE"}
@@ -69,6 +69,45 @@ export class FrontmatterError extends Error {
69
69
  return msg;
70
70
  }
71
71
  }
72
+ export function detectEmbeddedEntities(data, entityType) {
73
+ if (entityType !== "req") {
74
+ return [];
75
+ }
76
+ const detected = [];
77
+ const scenarioFields = ["scenarios", "given", "when", "then", "steps"];
78
+ for (const field of scenarioFields) {
79
+ if (field in data) {
80
+ const value = data[field];
81
+ if (value !== null &&
82
+ value !== undefined &&
83
+ (Array.isArray(value) ||
84
+ typeof value === "object" ||
85
+ typeof value === "string")) {
86
+ if (!detected.includes("scenario")) {
87
+ detected.push("scenario");
88
+ }
89
+ break;
90
+ }
91
+ }
92
+ }
93
+ const testFields = ["tests", "testCases", "assertions", "testSteps"];
94
+ for (const field of testFields) {
95
+ if (field in data) {
96
+ const value = data[field];
97
+ if (value !== null &&
98
+ value !== undefined &&
99
+ (Array.isArray(value) ||
100
+ typeof value === "object" ||
101
+ typeof value === "string")) {
102
+ if (!detected.includes("test")) {
103
+ detected.push("test");
104
+ }
105
+ break;
106
+ }
107
+ }
108
+ }
109
+ return detected;
110
+ }
72
111
  export function extractFromMarkdown(filePath) {
73
112
  let content;
74
113
  try {
@@ -101,6 +140,14 @@ export function extractFromMarkdown(filePath) {
101
140
  hint: "Add a 'title: ...' field to the YAML frontmatter.",
102
141
  });
103
142
  }
143
+ const embeddedEntities = detectEmbeddedEntities(data, type);
144
+ if (embeddedEntities.length > 0) {
145
+ const entityTypes = embeddedEntities.join(" and ");
146
+ throw new FrontmatterError(`Invalid embedded entity: requirement contains ${entityTypes} fields`, filePath, {
147
+ classification: "Embedded Entity Violation",
148
+ hint: `Move ${entityTypes} to separate entity files and link them using 'links' with relationship types like 'specified_by' or 'verified_by'.`,
149
+ });
150
+ }
104
151
  const id = data.id || generateId(filePath, data.title);
105
152
  const relationships = extractRelationships(data.links || [], id);
106
153
  return {
package/dist/prolog.d.ts CHANGED
@@ -15,6 +15,7 @@ export declare class PrologProcess {
15
15
  private outputBuffer;
16
16
  private errorBuffer;
17
17
  private cache;
18
+ private interactiveQueryTail;
18
19
  private useOneShotMode;
19
20
  private attachedKbPath;
20
21
  private onProcessExit;
@@ -1 +1 @@
1
- {"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AAuDA,wBAAgB,eAAe,IAAI,MAAM,CAsCxC;AACD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,cAAc,CACyC;IAC/D,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,GAAE,aAAkB;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAoCd,YAAY;IAyCpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAsH1D,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;YAYT,YAAY;IA0B1B,OAAO,CAAC,WAAW;IA8EnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,cAAc;IA8BtB,SAAS,IAAI,OAAO;IAIpB,MAAM,IAAI,MAAM;IAIV,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAyBjC"}
1
+ {"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AAuDA,wBAAgB,eAAe,IAAI,MAAM,CAsCxC;AACD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,cAAc,CACyC;IAC/D,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,GAAE,aAAkB;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAoCd,YAAY;IAyCpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IA8I1D,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;YAYT,YAAY;IA0B1B,OAAO,CAAC,WAAW;IA8EnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,cAAc;IA8BtB,SAAS,IAAI,OAAO;IAIpB,MAAM,IAAI,MAAM;IAIV,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAyBjC"}
package/dist/prolog.js CHANGED
@@ -85,6 +85,7 @@ export class PrologProcess {
85
85
  outputBuffer = "";
86
86
  errorBuffer = "";
87
87
  cache = new Map();
88
+ interactiveQueryTail = Promise.resolve();
88
89
  useOneShotMode = typeof globalThis.Bun !== "undefined";
89
90
  attachedKbPath = null;
90
91
  onProcessExit = null;
@@ -174,78 +175,92 @@ export class PrologProcess {
174
175
  if (!this.process || !this.process.stdin) {
175
176
  throw new Error("Prolog process not started");
176
177
  }
177
- this.outputBuffer = "";
178
- this.errorBuffer = "";
179
- this.process.stdin.write(`${goal}.
180
- `);
181
- const debug = !!process.env.KIBI_PROLOG_DEBUG;
182
- const normalizedGoal = isSingleGoal
183
- ? this.normalizeGoal(goal)
184
- : undefined;
185
- const start = Date.now();
186
- if (debug && normalizedGoal)
187
- console.error(`[prolog debug] start query: ${normalizedGoal}`);
188
- return new Promise((resolve, reject) => {
189
- const timeoutId = setTimeout(() => {
190
- const msg = `Query timeout after ${this.timeout / 1000}s`;
191
- if (debug) {
192
- const tailOut = this.outputBuffer.slice(-2048);
193
- const tailErr = this.errorBuffer.slice(-2048);
194
- console.error(`[prolog debug] timeout: ${msg}`);
195
- console.error(`[prolog debug] last stdout: ---\n${tailOut}\n---`);
196
- console.error(`[prolog debug] last stderr: ---\n${tailErr}\n---`);
197
- }
198
- reject(new Error(msg));
199
- }, this.timeout);
200
- const checkResult = () => {
201
- if (this.errorBuffer.length > 0 && this.errorBuffer.includes("ERROR")) {
202
- clearTimeout(timeoutId);
203
- if (debug && normalizedGoal)
204
- console.error(`[prolog debug] query error: ${normalizedGoal} error=${this.errorBuffer.split("\n")[0]}`);
205
- resolve({
206
- success: false,
207
- bindings: {},
208
- error: this.translateError(this.errorBuffer),
209
- });
210
- }
211
- else if (this.outputBuffer.includes("true.") ||
212
- this.outputBuffer.match(/^[A-Z_][A-Za-z0-9_]*\s*=\s*.+\./m) ||
213
- // Match multi-line output ending with ] (Prolog list/term output without trailing period)
214
- this.outputBuffer.match(/\]\s*$/m)) {
215
- clearTimeout(timeoutId);
216
- const result = {
217
- success: true,
218
- bindings: this.extractBindings(this.outputBuffer),
219
- };
220
- if (cacheable) {
221
- this.cache.set(goalKey, result);
178
+ const runInteractiveQuery = async () => {
179
+ this.outputBuffer = "";
180
+ this.errorBuffer = "";
181
+ const debug = !!process.env.KIBI_PROLOG_DEBUG;
182
+ const normalizedGoal = this.normalizeGoal(goal);
183
+ const wrappedGoal = /^once\s*\(/.test(normalizedGoal)
184
+ ? normalizedGoal
185
+ : `once((${normalizedGoal}))`;
186
+ const start = Date.now();
187
+ if (debug) {
188
+ console.error(`[prolog debug] start query: ${normalizedGoal}`);
189
+ }
190
+ this.process?.stdin?.write(`${wrappedGoal}.\n`);
191
+ return new Promise((resolve, reject) => {
192
+ const timeoutId = setTimeout(() => {
193
+ const msg = `Query timeout after ${this.timeout / 1000}s (pid=${this.process?.pid ?? 0}, killed=${this.process?.killed ? "yes" : "no"}, exitCode=${this.process?.exitCode ?? "null"}, goal=${JSON.stringify(normalizedGoal.slice(0, 120))})`;
194
+ if (debug) {
195
+ const tailOut = this.outputBuffer.slice(-2048);
196
+ const tailErr = this.errorBuffer.slice(-2048);
197
+ console.error(`[prolog debug] timeout: ${msg}`);
198
+ console.error(`[prolog debug] last stdout: ---\n${tailOut}\n---`);
199
+ console.error(`[prolog debug] last stderr: ---\n${tailErr}\n---`);
200
+ }
201
+ reject(new Error(msg));
202
+ }, this.timeout);
203
+ const checkResult = () => {
204
+ if (this.errorBuffer.length > 0 &&
205
+ this.errorBuffer.includes("ERROR")) {
206
+ clearTimeout(timeoutId);
207
+ if (debug) {
208
+ console.error(`[prolog debug] query error: ${normalizedGoal} error=${this.errorBuffer.split("\n")[0]}`);
209
+ }
210
+ resolve({
211
+ success: false,
212
+ bindings: {},
213
+ error: this.translateError(this.errorBuffer),
214
+ });
215
+ return;
222
216
  }
223
- if (debug && normalizedGoal) {
224
- console.error(`[prolog debug] query success: ${normalizedGoal} elapsed=${(Date.now() - start) / 1000}s`);
217
+ if (this.outputBuffer.includes("true.") ||
218
+ this.outputBuffer.match(/^[A-Z_][A-Za-z0-9_]*\s*=\s*.+\./m) ||
219
+ this.outputBuffer.match(/\]\s*$/m)) {
220
+ clearTimeout(timeoutId);
221
+ const result = {
222
+ success: true,
223
+ bindings: this.extractBindings(this.outputBuffer),
224
+ };
225
+ if (cacheable) {
226
+ this.cache.set(goalKey, result);
227
+ }
228
+ if (debug) {
229
+ console.error(`[prolog debug] query success: ${normalizedGoal} elapsed=${(Date.now() - start) / 1000}s`);
230
+ }
231
+ resolve(result);
232
+ return;
225
233
  }
226
- resolve(result);
227
- // Send newline to exit Prolog's interactive prompt
228
- if (this.process?.stdin) {
229
- this.process.stdin.write("\n");
234
+ if (this.outputBuffer.includes("false.") ||
235
+ this.outputBuffer.includes("fail.")) {
236
+ clearTimeout(timeoutId);
237
+ if (debug) {
238
+ console.error(`[prolog debug] query failed (false): ${normalizedGoal}`);
239
+ }
240
+ resolve({
241
+ success: false,
242
+ bindings: {},
243
+ error: "Query failed",
244
+ });
245
+ return;
230
246
  }
231
- }
232
- else if (this.outputBuffer.includes("false.") ||
233
- this.outputBuffer.includes("fail.")) {
234
- clearTimeout(timeoutId);
235
- if (debug && normalizedGoal)
236
- console.error(`[prolog debug] query failed (false): ${normalizedGoal}`);
237
- resolve({
238
- success: false,
239
- bindings: {},
240
- error: "Query failed",
241
- });
242
- }
243
- else {
244
247
  setTimeout(checkResult, 50);
245
- }
246
- };
247
- checkResult();
248
+ };
249
+ checkResult();
250
+ });
251
+ };
252
+ const previousQuery = this.interactiveQueryTail;
253
+ let releaseQuery;
254
+ this.interactiveQueryTail = new Promise((resolve) => {
255
+ releaseQuery = resolve;
248
256
  });
257
+ await previousQuery;
258
+ try {
259
+ return await runInteractiveQuery();
260
+ }
261
+ finally {
262
+ releaseQuery();
263
+ }
249
264
  }
250
265
  invalidateCache() {
251
266
  this.cache.clear();
@@ -0,0 +1,2 @@
1
+ export * from "../utils/branch-resolver.js";
2
+ //# sourceMappingURL=branch-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-resolver.d.ts","sourceRoot":"","sources":["../../src/public/branch-resolver.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC"}
@@ -0,0 +1 @@
1
+ export * from "../utils/branch-resolver.js";
@@ -1 +1 @@
1
- {"version":3,"file":"git-staged.d.ts","sourceRoot":"","sources":["../../src/traceability/git-staged.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAaD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAS5C;AAoBD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,UAAQ,GAChB,SAAS,EAAE,CAoBb;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAmF7C;AAED,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"git-staged.d.ts","sourceRoot":"","sources":["../../src/traceability/git-staged.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAaD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAS5C;AA8BD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,UAAQ,GAChB,SAAS,EAAE,CAoBb;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAmF7C;AAED,eAAe,cAAc,CAAC"}
@@ -6,7 +6,7 @@ function runGit(cmd) {
6
6
  catch (err) {
7
7
  // wrap common errors
8
8
  const e = err;
9
- const message = e && e.message ? String(e.message) : String(err);
9
+ const message = e?.message ? String(e.message) : String(err);
10
10
  throw new Error(`git command failed: ${cmd} -> ${message}`);
11
11
  }
12
12
  }
@@ -34,6 +34,7 @@ const SUPPORTED_EXT = new Set([
34
34
  ".mjs",
35
35
  ".cjs",
36
36
  ]);
37
+ const ENTITY_MARKDOWN_DIRS = ["/requirements/", "/scenarios/", "/tests/"];
37
38
  function hasSupportedExt(p) {
38
39
  for (const ext of SUPPORTED_EXT) {
39
40
  if (p.endsWith(ext))
@@ -41,6 +42,15 @@ function hasSupportedExt(p) {
41
42
  }
42
43
  return false;
43
44
  }
45
+ function isEntityMarkdown(p) {
46
+ if (!p.endsWith(".md"))
47
+ return false;
48
+ for (const dir of ENTITY_MARKDOWN_DIRS) {
49
+ if (p.includes(dir))
50
+ return true;
51
+ }
52
+ return false;
53
+ }
44
54
  /**
45
55
  * Parse unified diff hunks (new-file coordinates) from git diff output
46
56
  */
@@ -98,7 +108,7 @@ export function getStagedFiles() {
98
108
  path = entry.parts[1];
99
109
  }
100
110
  }
101
- if (!hasSupportedExt(path)) {
111
+ if (!hasSupportedExt(path) && !isEntityMarkdown(path)) {
102
112
  console.debug(`Skipping unsupported extension: ${path}`);
103
113
  continue;
104
114
  }
@@ -123,7 +133,7 @@ export function getStagedFiles() {
123
133
  catch (err) {
124
134
  // binary or deleted in index
125
135
  const e = err;
126
- const em = e && e.message ? String(e.message) : String(err);
136
+ const em = e?.message ? String(e.message) : String(err);
127
137
  console.debug(`Skipping binary/deleted or unreadable staged file ${path}: ${em}`);
128
138
  continue;
129
139
  }
@@ -0,0 +1,7 @@
1
+ import { FrontmatterError } from "../extractors/markdown.js";
2
+ export interface MarkdownValidationResult {
3
+ filePath: string;
4
+ errors: FrontmatterError[];
5
+ }
6
+ export declare function validateStagedMarkdown(filePath: string, content: string): MarkdownValidationResult;
7
+ //# sourceMappingURL=markdown-validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-validate.d.ts","sourceRoot":"","sources":["../../src/traceability/markdown-validate.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAEjB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,wBAAwB,CAgC1B"}
@@ -0,0 +1,35 @@
1
+ import matter from "gray-matter";
2
+ import { FrontmatterError, detectEmbeddedEntities, } from "../extractors/markdown.js";
3
+ export function validateStagedMarkdown(filePath, content) {
4
+ const errors = [];
5
+ try {
6
+ const { data } = matter(content);
7
+ const type = data.type || inferTypeFromPath(filePath);
8
+ if (!type) {
9
+ return { filePath, errors };
10
+ }
11
+ const embeddedEntities = detectEmbeddedEntities(data, type);
12
+ if (embeddedEntities.length > 0) {
13
+ const entityTypes = embeddedEntities.join(" and ");
14
+ errors.push(new FrontmatterError(`Invalid embedded entity: requirement contains ${entityTypes} fields`, filePath, {
15
+ classification: "Embedded Entity Violation",
16
+ hint: `Move ${entityTypes} to separate entity files and link them using 'links' with relationship types like 'specified_by' or 'verified_by'.`,
17
+ }));
18
+ }
19
+ }
20
+ catch (error) {
21
+ if (error instanceof FrontmatterError) {
22
+ errors.push(error);
23
+ }
24
+ }
25
+ return { filePath, errors };
26
+ }
27
+ function inferTypeFromPath(filePath) {
28
+ if (filePath.includes("/requirements/"))
29
+ return "req";
30
+ if (filePath.includes("/scenarios/"))
31
+ return "scenario";
32
+ if (filePath.includes("/tests/"))
33
+ return "test";
34
+ return null;
35
+ }
@@ -0,0 +1,79 @@
1
+ export type BranchResolutionSuccess = {
2
+ branch: string;
3
+ };
4
+ export type BranchResolutionError = {
5
+ error: string;
6
+ code: BranchErrorCode;
7
+ };
8
+ export type BranchResolutionResult = BranchResolutionSuccess | BranchResolutionError;
9
+ export type BranchErrorCode = "ENV_OVERRIDE" | "DETACHED_HEAD" | "UNBORN_BRANCH" | "GIT_NOT_AVAILABLE" | "NOT_A_GIT_REPO" | "UNKNOWN_ERROR";
10
+ /**
11
+ * Resolve the active branch according to ADR-012 precedence:
12
+ * 1. KIBI_BRANCH env var (if set)
13
+ * 2. Git active branch (from git branch --show-current)
14
+ * 3. Diagnostic failure (no silent fallback)
15
+ *
16
+ * @param workspaceRoot - The workspace root directory
17
+ * @returns BranchResolutionResult with either the branch name or an error
18
+ */
19
+ export declare function resolveActiveBranch(workspaceRoot?: string): BranchResolutionResult;
20
+ /**
21
+ * Check if the repository is in detached HEAD state.
22
+ *
23
+ * @param workspaceRoot - The workspace root directory
24
+ * @returns true if in detached HEAD, false otherwise
25
+ */
26
+ export declare function isDetachedHead(workspaceRoot?: string): boolean;
27
+ /**
28
+ * Get a detailed diagnostic message for branch resolution failures.
29
+ *
30
+ * @param branch - The branch that was detected (if any)
31
+ * @param error - The error message or context
32
+ * @returns A formatted diagnostic message
33
+ */
34
+ export declare function getBranchDiagnostic(branch: string | undefined, error: string): string;
35
+ /**
36
+ * Validate a branch name for safety and correctness.
37
+ *
38
+ * @param name - The branch name to validate
39
+ * @returns true if valid, false otherwise
40
+ */
41
+ export declare function isValidBranchName(name: string): boolean;
42
+ /**
43
+ * Copy a clean snapshot from source branch to target branch, excluding
44
+ * volatile artifacts like sync-cache.json, lock files, and journal files.
45
+ *
46
+ * @param sourcePath - Path to the source branch KB directory
47
+ * @param targetPath - Path to the target branch KB directory
48
+ * @throws Error if the copy fails
49
+ */
50
+ export declare function copyCleanSnapshot(sourcePath: string, targetPath: string): void;
51
+ /**
52
+ * Get the list of files that would be excluded from a clean snapshot copy.
53
+ * Useful for debugging and testing.
54
+ *
55
+ * @returns Array of volatile artifact patterns
56
+ */
57
+ export declare function getVolatileArtifactPatterns(): string[];
58
+ /**
59
+ * Resolve the default branch using the following precedence:
60
+ * 1. Configured defaultBranch from config (if set and valid)
61
+ * 2. Git remote HEAD (refs/remotes/origin/HEAD)
62
+ * 3. Fallback to "main"
63
+ *
64
+ * Unlike resolveActiveBranch, this does NOT normalize branch names.
65
+ * Configured names are returned verbatim.
66
+ *
67
+ * @param cwd - The working directory to resolve the default branch
68
+ * @param config - Optional config with defaultBranch
69
+ * @returns BranchResolutionResult with either the branch name or an error
70
+ */
71
+ export declare function resolveDefaultBranch(cwd?: string, config?: {
72
+ defaultBranch?: string;
73
+ }): {
74
+ branch: string;
75
+ } | {
76
+ error: string;
77
+ code: string;
78
+ };
79
+ //# sourceMappingURL=branch-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/branch-resolver.ts"],"names":[],"mappings":"AA4BA,MAAM,MAAM,uBAAuB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACzD,MAAM,MAAM,qBAAqB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAC7E,MAAM,MAAM,sBAAsB,GAC9B,uBAAuB,GACvB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,eAAe,GACf,eAAe,GACf,mBAAmB,GACnB,gBAAgB,GAChB,eAAe,CAAC;AA8BpB;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,GAAE,MAAsB,GACpC,sBAAsB,CAmHxB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,aAAa,GAAE,MAAsB,GAAG,OAAO,CAa7E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,MAAM,GACZ,MAAM,CAuBR;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAuBvD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,IAAI,CAUN;AA6BD;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,EAAE,CAMtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAsB,EAC3B,MAAM,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAqCtD"}